web.io

Deploy and run node apps seamlessly, with auto-reloading and multiprocess clustering

Web.IO

Alpha project, use at your own risk

Deploy and run node apps seamlessly, with auto-reloading and multiprocess clustering.

Web.IO is a very primitive server bootstrapper for node.js applications. It only implemented the following features:

  1. Auto-reload: Automatic reload node app js files on changes, no reboot needed.

  2. Multiprocess: Server process is clustered in a master-worker structure.

The Web.IO does NOT include the following features:

  1. Auto-parsing the COOKIE/GET/POST/FILE request data.

  2. Helpers for sending response data back more conveniently.

These features can be done with other more popular npm packages.

For we want to keep the Web.IO to be enough primitive and customizable.

npm install web.io

Edit server.js for bootstrapping:

// Create and start server 
require('web.io').startServer({
    // Name of this server node 
    name: 'demo',
    // Number of workers 
    workers: require('os').cpus().length,
    // Listening port/unix socket/socket object 
    listen: 8080,
    // Path to the web root folder 
    root: __dirname + '/webroot',
    // Filename of the index page 
    index: 'index.js'
});

Create the web root folder IMPORTANT:

mkdir ./webroot

Edit webroot/hello.js as a js web app page:

// Just export a function to receive web requests 
// `req` and `rsp` are provided by the node 
module.exports = function ( reqrsp ) {
    rsp.writeHead( 200 );
    rsp.end('Hello World!');
};

Start the server up as:

node ./server.js

Visit http://127.0.0.1:8080/hello.js, should get:

Hello World!

KEEP RUNNING the server, and modify the webroot/hello.js as:

module.exports = function ( reqrsp ) {
    rsp.writeHead( 200 );
    rsp.end('Hello Web!');
};

Revisit http://127.0.0.1:8080/hello.js, should get:

Hello Web!

That's all.

The .startServer() function only returns http.Server instance in the worker processes.

var server = require('web.io').startServer({
        // Name of this server node 
        name: 'demo',
        // Running user 
        user: 'nobody',
        // Running group 
        group: 'nobody',
        // Number of workers 
        workers: require('os').cpus().length,
        // Listening port/unix socket/socket object 
        listen: 8080,
        // Path to the web root folder 
        root: __dirname + '/webroot',
        // Filename of the index page 
        index: 'index.js'
    })
;
 
// `server` object only valid in workers 
if ( server && server instanceof require('http').Server ) {
    require('socket.io').listen( server );
}

Server bootstrap file app.js:

var express = require('express'),
    app = express(),  // express 3.x 
    server = require('http').createServer( app )
;
 
app.all('*', function ( reqrsp ) {
    // Always use `require()` to retrieve the updated module 
    require( './app' + req.path.replace( /\.\./g, '' ) )( req, rsp );
});
 
server = require('web.io').startServer({
    // Name of this server node 
    name: 'demo',
    // Number of workers 
    workers: require('os').cpus().length,
    // Server instance 
    server: app,
    // Listening port/unix socket/socket object 
    listen: 8080,
    // Path to the web root folder 
    root: __dirname + '/app'
    // 
});

Application module file hello.js:

module.exports = function ( reqrsp ) {
    rsp.send('Hello World!');
};

NOTICE: The codes above is for the illustration only, Do not copy it completely into your production projects. Because the routing policy in this example is a bad practice.

For the keypoints of the usage please forward to next section.

Use the server config entry flexibly, and just remember 3 things:

  1. Do not put the bootstrap file itself into the web root folder. Besides that you have a strong strategy to prevent recursive listening.

  2. Whenever a node.js file changed, all the cached modules in the web root folder will be reloaded at reloadDelay milliseconds after recevied the last file change event. During the reload procedure, frontend access to these node.js files may suffer a 404 error in a very short period. (Currently no better solutions for loading only the changed files with updating the module dependencies)

  3. The .startServer() function only returns http.Server instance in the worker processes. Although master process always returns nothing, you should not depend on this, please use instanceof require('http').Server to verify the return value restrictively.

All options with default values:

require('web.io').startServer({
    // Name of this server node 
    name: 'webio',
    // Running user 
    user: 'nobody',
    // Running group 
    group: 'nobody',
    // Number of workers 
    workers: require('os').cpus().length,
    // Server instance 
    server: null,
    // Listening port/unix socket/socket object 
    listen: 8080,
    // Backlog size (when listen on a port number) 
    backlog: 511,
    // Sockets timeout (ms) 
    timeout: 120000,
    // Path to the web root folder 
    root: __dirname + '/webroot',
    // Filename of the index page 
    index: 'index.js',
    // Paths forbidden from access 
    forbids: [],
    // Serve static files 
    servo: false,
    // Cache static files 
    cache: false,
    // Paths contain non-node js files 
    excludes: [],
    // Auto-reload files on change 
    reload: true,
    // Auto-reload delay (ms) 
    reloadDelay: 100,
    // Heartbeat pattern 
    heartbeat: false,
    // Heartbeat tempo 
    tempo: 15,
    // Modules to be run on each heartbeat 
    actives: [],
    // Log level 
    logLevel: 'INFO'
});

Explanations:

  • name string Name of this server node, affects the process title. e.g. ps command.

  • user string|number Running user, name or uid, used for process.setuid.

  • group string|number Running group, name or gid, used for process.setgid.

  • workers number Number of workers to fork to. Generally, the default value is the best value.

  • server object Defaults to null, then it will use http.createServer() along with the built-in handler.

  • listen number Listening port/unix socket/socket object, identical to the http.Server.listen function.

  • backlog number (refer to node's http.Server.listen function)

  • timeout number Socket timeout value, in ms.

  • root string Path to the web root folder, in which the internal files will be watched and auto-reloaded.

  • index string Filename of the index page.

  • forbids Array Paths forbidden from access, absolute/relative paths/prefixes of files/folders are acceptable.

  • servo boolean Whether to serve static files (must set excludes to avoid non-node js files from being loaded).

  • cache boolean Whether to cache static files (cache stores in Buffer, be careful of your memory).

  • excludes Array Paths contain non-node js files, absolute/relative paths/prefixes of files/folders are acceptable.

  • reload boolean Whether to reload files on change automatically.

  • reloadDelay number The delay time in ms to reload all cached modules after the last file change event receives.

  • heartbeat string|boolean EXPERIMENTAL, currently only one option 'timer' is supported, set to false to disable the heartbeat function.

  • tempo number EXPERIMENTAL, the tempo factor of the heartbeat function.

  • actives Array EXPERIMENTAL, An array containing module paths, which will be invoked on each heartbeat pulses.

  • logLevel string EXPERIMENTAL, Log level.

Please forward to the demo/ inside the project folder.

Start demo server by running: node demo/server.js.

And visit the files inside demo/webroot/ folder via browsers, on port 8080.

  • 127.0.0.1:8080

  • 127.0.0.1:8080/index.js

  • 127.0.0.1:8080/index.html

  • 127.0.0.1:8080/js/jquery.min.js

  • 127.0.0.1:8080/folder/

  • 127.0.0.1:8080/folder/index.js

  • 127.0.0.1:8080/lib/

  • 127.0.0.1:8080/lib/hello.js

(The Apache License 2.0)

Copyright (c) 2014-2015, DJ-NotYet dj.notyet@gmail.com

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.