Node.js: introducing websocket support.
This commit is contained in:
157
src/nodejs/unit-http/websocket_router.js
Normal file
157
src/nodejs/unit-http/websocket_router.js
Normal file
@@ -0,0 +1,157 @@
|
||||
/************************************************************************
|
||||
* Copyright 2010-2015 Brian McKelvey.
|
||||
*
|
||||
* 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.
|
||||
***********************************************************************/
|
||||
|
||||
var extend = require('./utils').extend;
|
||||
var util = require('util');
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var WebSocketRouterRequest = require('./websocket_router_request');
|
||||
|
||||
function WebSocketRouter(config) {
|
||||
// Superclass Constructor
|
||||
EventEmitter.call(this);
|
||||
|
||||
this.config = {
|
||||
// The WebSocketServer instance to attach to.
|
||||
server: null
|
||||
};
|
||||
if (config) {
|
||||
extend(this.config, config);
|
||||
}
|
||||
this.handlers = [];
|
||||
|
||||
this._requestHandler = this.handleRequest.bind(this);
|
||||
if (this.config.server) {
|
||||
this.attachServer(this.config.server);
|
||||
}
|
||||
}
|
||||
|
||||
util.inherits(WebSocketRouter, EventEmitter);
|
||||
|
||||
WebSocketRouter.prototype.attachServer = function(server) {
|
||||
if (server) {
|
||||
this.server = server;
|
||||
this.server.on('request', this._requestHandler);
|
||||
}
|
||||
else {
|
||||
throw new Error('You must specify a WebSocketServer instance to attach to.');
|
||||
}
|
||||
};
|
||||
|
||||
WebSocketRouter.prototype.detachServer = function() {
|
||||
if (this.server) {
|
||||
this.server.removeListener('request', this._requestHandler);
|
||||
this.server = null;
|
||||
}
|
||||
else {
|
||||
throw new Error('Cannot detach from server: not attached.');
|
||||
}
|
||||
};
|
||||
|
||||
WebSocketRouter.prototype.mount = function(path, protocol, callback) {
|
||||
if (!path) {
|
||||
throw new Error('You must specify a path for this handler.');
|
||||
}
|
||||
if (!protocol) {
|
||||
protocol = '____no_protocol____';
|
||||
}
|
||||
if (!callback) {
|
||||
throw new Error('You must specify a callback for this handler.');
|
||||
}
|
||||
|
||||
path = this.pathToRegExp(path);
|
||||
if (!(path instanceof RegExp)) {
|
||||
throw new Error('Path must be specified as either a string or a RegExp.');
|
||||
}
|
||||
var pathString = path.toString();
|
||||
|
||||
// normalize protocol to lower-case
|
||||
protocol = protocol.toLocaleLowerCase();
|
||||
|
||||
if (this.findHandlerIndex(pathString, protocol) !== -1) {
|
||||
throw new Error('You may only mount one handler per path/protocol combination.');
|
||||
}
|
||||
|
||||
this.handlers.push({
|
||||
'path': path,
|
||||
'pathString': pathString,
|
||||
'protocol': protocol,
|
||||
'callback': callback
|
||||
});
|
||||
};
|
||||
WebSocketRouter.prototype.unmount = function(path, protocol) {
|
||||
var index = this.findHandlerIndex(this.pathToRegExp(path).toString(), protocol);
|
||||
if (index !== -1) {
|
||||
this.handlers.splice(index, 1);
|
||||
}
|
||||
else {
|
||||
throw new Error('Unable to find a route matching the specified path and protocol.');
|
||||
}
|
||||
};
|
||||
|
||||
WebSocketRouter.prototype.findHandlerIndex = function(pathString, protocol) {
|
||||
protocol = protocol.toLocaleLowerCase();
|
||||
for (var i=0, len=this.handlers.length; i < len; i++) {
|
||||
var handler = this.handlers[i];
|
||||
if (handler.pathString === pathString && handler.protocol === protocol) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
|
||||
WebSocketRouter.prototype.pathToRegExp = function(path) {
|
||||
if (typeof(path) === 'string') {
|
||||
if (path === '*') {
|
||||
path = /^.*$/;
|
||||
}
|
||||
else {
|
||||
path = path.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
|
||||
path = new RegExp('^' + path + '$');
|
||||
}
|
||||
}
|
||||
return path;
|
||||
};
|
||||
|
||||
WebSocketRouter.prototype.handleRequest = function(request) {
|
||||
var requestedProtocols = request.requestedProtocols;
|
||||
if (requestedProtocols.length === 0) {
|
||||
requestedProtocols = ['____no_protocol____'];
|
||||
}
|
||||
|
||||
// Find a handler with the first requested protocol first
|
||||
for (var i=0; i < requestedProtocols.length; i++) {
|
||||
var requestedProtocol = requestedProtocols[i].toLocaleLowerCase();
|
||||
|
||||
// find the first handler that can process this request
|
||||
for (var j=0, len=this.handlers.length; j < len; j++) {
|
||||
var handler = this.handlers[j];
|
||||
if (handler.path.test(request.resourceURL.pathname)) {
|
||||
if (requestedProtocol === handler.protocol ||
|
||||
handler.protocol === '*')
|
||||
{
|
||||
var routerRequest = new WebSocketRouterRequest(request, requestedProtocol);
|
||||
handler.callback(routerRequest);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we get here we were unable to find a suitable handler.
|
||||
request.reject(404, 'No handler is available for the given request.');
|
||||
};
|
||||
|
||||
module.exports = WebSocketRouter;
|
||||
Reference in New Issue
Block a user