1 /************************************************************************
2 * Copyright 2010-2015 Brian McKelvey.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 ***********************************************************************/
17 var extend = require('./utils').extend;
18 var util = require('util');
19 var EventEmitter = require('events').EventEmitter;
20 var WebSocketRouterRequest = require('./WebSocketRouterRequest');
22 function WebSocketRouter(config) {
23 // Superclass Constructor
24 EventEmitter.call(this);
27 // The WebSocketServer instance to attach to.
31 extend(this.config, config);
35 this._requestHandler = this.handleRequest.bind(this);
36 if (this.config.server) {
37 this.attachServer(this.config.server);
41 util.inherits(WebSocketRouter, EventEmitter);
43 WebSocketRouter.prototype.attachServer = function(server) {
46 this.server.on('request', this._requestHandler);
49 throw new Error('You must specify a WebSocketServer instance to attach to.');
53 WebSocketRouter.prototype.detachServer = function() {
55 this.server.removeListener('request', this._requestHandler);
59 throw new Error('Cannot detach from server: not attached.');
63 WebSocketRouter.prototype.mount = function(path, protocol, callback) {
65 throw new Error('You must specify a path for this handler.');
68 protocol = '____no_protocol____';
71 throw new Error('You must specify a callback for this handler.');
74 path = this.pathToRegExp(path);
75 if (!(path instanceof RegExp)) {
76 throw new Error('Path must be specified as either a string or a RegExp.');
78 var pathString = path.toString();
80 // normalize protocol to lower-case
81 protocol = protocol.toLocaleLowerCase();
83 if (this.findHandlerIndex(pathString, protocol) !== -1) {
84 throw new Error('You may only mount one handler per path/protocol combination.');
89 'pathString': pathString,
94 WebSocketRouter.prototype.unmount = function(path, protocol) {
95 var index = this.findHandlerIndex(this.pathToRegExp(path).toString(), protocol);
97 this.handlers.splice(index, 1);
100 throw new Error('Unable to find a route matching the specified path and protocol.');
104 WebSocketRouter.prototype.findHandlerIndex = function(pathString, protocol) {
105 protocol = protocol.toLocaleLowerCase();
106 for (var i=0, len=this.handlers.length; i < len; i++) {
107 var handler = this.handlers[i];
108 if (handler.pathString === pathString && handler.protocol === protocol) {
115 WebSocketRouter.prototype.pathToRegExp = function(path) {
116 if (typeof(path) === 'string') {
121 path = path.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
122 path = new RegExp('^' + path + '$');
128 WebSocketRouter.prototype.handleRequest = function(request) {
129 var requestedProtocols = request.requestedProtocols;
130 if (requestedProtocols.length === 0) {
131 requestedProtocols = ['____no_protocol____'];
134 // Find a handler with the first requested protocol first
135 for (var i=0; i < requestedProtocols.length; i++) {
136 var requestedProtocol = requestedProtocols[i].toLocaleLowerCase();
138 // find the first handler that can process this request
139 for (var j=0, len=this.handlers.length; j < len; j++) {
140 var handler = this.handlers[j];
141 if (handler.path.test(request.resourceURL.pathname)) {
142 if (requestedProtocol === handler.protocol ||
143 handler.protocol === '*')
145 var routerRequest = new WebSocketRouterRequest(request, requestedProtocol);
146 handler.callback(routerRequest);
153 // If we get here we were unable to find a suitable handler.
154 request.reject(404, 'No handler is available for the given request.');
157 module.exports = WebSocketRouter;