Subversion Repositories qbpwcf-lib(archive)

Rev

Rev 915 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 liveuser 1
/************************************************************************
2
 *  Copyright 2010-2015 Brian McKelvey.
3
 *
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
7
 *
8
 *      http://www.apache.org/licenses/LICENSE-2.0
9
 *
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
 ***********************************************************************/
16
 
17
var extend = require('./utils').extend;
18
var util = require('util');
19
var EventEmitter = require('events').EventEmitter;
20
var WebSocketRouterRequest = require('./WebSocketRouterRequest');
21
 
22
function WebSocketRouter(config) {
23
    // Superclass Constructor
24
    EventEmitter.call(this);
25
 
26
    this.config = {
27
        // The WebSocketServer instance to attach to.
28
        server: null
29
    };
30
    if (config) {
31
        extend(this.config, config);
32
    }
33
    this.handlers = [];
34
 
35
    this._requestHandler = this.handleRequest.bind(this);
36
    if (this.config.server) {
37
        this.attachServer(this.config.server);
38
    }
39
}
40
 
41
util.inherits(WebSocketRouter, EventEmitter);
42
 
43
WebSocketRouter.prototype.attachServer = function(server) {
44
    if (server) {
45
        this.server = server;
46
        this.server.on('request', this._requestHandler);
47
    }
48
    else {
49
        throw new Error('You must specify a WebSocketServer instance to attach to.');
50
    }
51
};
52
 
53
WebSocketRouter.prototype.detachServer = function() {
54
    if (this.server) {
55
        this.server.removeListener('request', this._requestHandler);
56
        this.server = null;
57
    }
58
    else {
59
        throw new Error('Cannot detach from server: not attached.');
60
    }
61
};
62
 
63
WebSocketRouter.prototype.mount = function(path, protocol, callback) {
64
    if (!path) {
65
        throw new Error('You must specify a path for this handler.');
66
    }
67
    if (!protocol) {
68
        protocol = '____no_protocol____';
69
    }
70
    if (!callback) {
71
        throw new Error('You must specify a callback for this handler.');
72
    }
73
 
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.');
77
    }
78
    var pathString = path.toString();
79
 
80
    // normalize protocol to lower-case
81
    protocol = protocol.toLocaleLowerCase();
82
 
83
    if (this.findHandlerIndex(pathString, protocol) !== -1) {
84
        throw new Error('You may only mount one handler per path/protocol combination.');
85
    }
86
 
87
    this.handlers.push({
88
        'path': path,
89
        'pathString': pathString,
90
        'protocol': protocol,
91
        'callback': callback
92
    });
93
};
94
WebSocketRouter.prototype.unmount = function(path, protocol) {
95
    var index = this.findHandlerIndex(this.pathToRegExp(path).toString(), protocol);
96
    if (index !== -1) {
97
        this.handlers.splice(index, 1);
98
    }
99
    else {
100
        throw new Error('Unable to find a route matching the specified path and protocol.');
101
    }
102
};
103
 
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) {
109
            return i;
110
        }
111
    }
112
    return -1;
113
};
114
 
115
WebSocketRouter.prototype.pathToRegExp = function(path) {
116
    if (typeof(path) === 'string') {
117
        if (path === '*') {
118
            path = /^.*$/;
119
        }
120
        else {
121
            path = path.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
122
            path = new RegExp('^' + path + '$');
123
        }
124
    }
125
    return path;
126
};
127
 
128
WebSocketRouter.prototype.handleRequest = function(request) {
129
    var requestedProtocols = request.requestedProtocols;
130
    if (requestedProtocols.length === 0) {
131
        requestedProtocols = ['____no_protocol____'];
132
    }
133
 
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();
137
 
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 === '*')
144
                {
145
                    var routerRequest = new WebSocketRouterRequest(request, requestedProtocol);
146
                    handler.callback(routerRequest);
147
                    return;
148
                }
149
            }
150
        }
151
    }
152
 
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.');
155
};
156
 
157
module.exports = WebSocketRouter;