qws
Version:
An HTML5 Web Sockets Server Module
124 lines (110 loc) • 4.1 kB
JavaScript
// Generated by CoffeeScript 1.9.2
var EventEmitter, Message, Server, crypto, os, urlParse,
extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
hasProp = {}.hasOwnProperty;
Message = require('./message').Message;
EventEmitter = require('events').EventEmitter;
urlParse = require('url').parse;
crypto = require('crypto');
os = require('options-stream');
Server = (function(superClass) {
extend(Server, superClass);
function Server(server1, options, cb) {
var server;
this.server = server1;
this.serverConfigList = [];
this.addServer(options, cb);
server = this.server;
server.on('upgrade', (function(_this) {
return function(req, socket) {
var config, j, len, msg, path, ref, result;
if (result = _this._handShake(req, socket)) {
socket.end("HTTP/1.1 400 Bad Request\r\n\r\n" + result + "\r\n");
return;
}
msg = new Message(socket, {
deflate: false
});
msg.request = req;
path = urlParse(req.url).path;
ref = _this.serverConfigList;
for (j = 0, len = ref.length; j < len; j++) {
config = ref[j];
if (path === config.url) {
if (result = _this._domainCheck(req, config)) {
msg.errorHandle("HTTP/1.1 403 Forbidden\r\n\r\n" + result + "\r\n");
return;
}
msg.reset(config);
return _this.emit('connect', msg);
}
}
msg.errorHandle('HTTP/1.1 400 Bad Request\r\n\r\nurl not matched\r\n');
};
})(this));
}
Server.prototype.addServer = function(options, cb) {
var i, j, len, origin, ref, serverConfig;
serverConfig = os({
url: '/ws',
deflate: false,
min_deflate_length: 32,
origins: [],
cb: cb
}, options);
if (serverConfig.origins.length) {
ref = serverConfig.origins;
for (i = j = 0, len = ref.length; j < len; i = ++j) {
origin = ref[i];
serverConfig.origins[i] = origin.replace(/\/$/g, '');
}
}
return this.serverConfigList.push(serverConfig);
};
Server.prototype._handShake = function(req, socket) {
var head, key, protocol, sha1, sign, uinfo;
uinfo = urlParse(req.url);
if (uinfo.protocol && uinfo.protocol !== 'ws:') {
return 'protocol not match';
}
if ('websocket' !== req.headers.upgrade) {
return 'upgrade not match';
}
if ('13' !== req.headers['sec-websocket-version']) {
return 'version not match';
}
if (!req.headers['sec-websocket-key']) {
return 'key missed';
}
protocol = req.headers['sec-websocket-protocol'];
key = req.headers['sec-websocket-key'];
sha1 = crypto.createHash('sha1');
sha1.update(key + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11');
sign = sha1.digest('base64');
head = "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: " + sign + "\r\nSec-WebSocket-Origin: " + req.headers.origin + "\r\n";
if (protocol) {
head += "Sec-WebSocket-Protocol: " + protocol + "\r\n";
}
head += "Sec-WebSocket-Location: ws://" + (req.headers.host + req.url) + "\r\n\r\n";
socket.write(head);
return false;
};
Server.prototype._domainCheck = function(req, config) {
var host, j, len, origin, origins, reqOrigin;
origins = config.origins;
host = urlParse(req.headers.origin).host;
reqOrigin = req.headers.origin.replace(/\/$/g, '');
if (host !== req.headers.host) {
for (j = 0, len = origins.length; j < len; j++) {
origin = origins[j];
if (origin === '*' || origin === reqOrigin) {
return false;
}
}
return "Origin " + req.headers.origin + " is not allowed";
}
return false;
};
return Server;
})(EventEmitter);
exports.Server = Server;