latte_web2
Version:
256 lines (238 loc) • 7.56 kB
JavaScript
(function(define) { "use strict";
define("latte_web/io", ["require", "exports", "module", "window"],
function(require, exports, module, window) {
var latte_lib = require("latte_lib")
, events = latte_lib.events
, WebsocketServer = require("ws").Server
, transports = require("./transports")
, Socket = require("./socket");
var Io = function(path, opts) {
if(!this instanceof Io) {
return new Io(opts);
}
this.clients = {};
this.clientsCount = 0;
opts = opts || {};
this.pingTimeout = opts.pingTimeout || 60000;
this.pingInterval = opts.pingInterval || 25000;
this.upgradeTimeout = opts.upgradeTimeout || 10000;
this.maxHttpBufferSize = opts.maxHttpBufferSize || 10E7;
this.transports = opts.transports || Object.keys(transports);
this.allowUpgrades = false !== opts.allowUpgrades;
this.allowRequest = opts.allowRequest;
this.cookie = false !== opts.cookie ?(opts.cookie || "io"): false;
if(~this.transports.indexOf("websocket")) {
this.ws = new WebsocketServer({noServer: true, clientTracking: false});
}
this.path = path;
this.auth = opts.auth;
};
latte_lib.inherits(Io, events);
(function() {
this.errors = {
UNKNOWN_TRANSPORT: 0,
UNKNOWN_SID: 1,
BAD_HANDSHAKE_METHOD: 2,
BAD_REQUEST: 3,
AUTH_FAILED: 4
};
this.errorMessages = {
0: 'Transport unknown',
1: 'Session ID unknown',
2: 'Bad handshake method',
3: 'Bad request',
4: 'Auth failed'
}
}).call(Io);
(function() {
this.handleUpgrade = function(req, socket, head) {
var self = this;
this.verify(req, true, function(err, success) {
if(err) {
socket.end();
return;
}
self.ws.handleUpgrade(req, socket, head, function(conn) {
self.onWebSocket(req, conn);
});
});
}
this.onWebSocket = function(req, socket) {
if(!transports[req.gets.transport].prototype.handlesUpgrades) {
socket.close();
return;
}
var id = req.gets.sid;
req.websocket = socket;
if(id) {
if(!this.clients[id]) {
socket.close();
} else if(this.clients[id].upgraded) {
socket.close();
} else {
var transport = new transports[req.gets.transport](req);
if(req.gets.b64) {
transport.supportsBinary = false;
}else{
transport.supportsBinary = true;
}
this.clients[id].maybeUpgrade(transport);
}
} else {
this.handshake(req.gets.transport, req);
}
}
this.upgrades = function(transport) {
if(!this.allowUpgrades) return [];
return transports[transport].upgradesTo || [];
}
var utils = require("../utils");
function sendErrorMessage(req, res, code) {
var headers = { 'Content-Type': 'application/json' };
if(res) {
utils.Origin(req, res);
/*if (req.headers.origin) {
headers['Access-Control-Allow-Credentials'] = 'true';
headers['Access-Control-Allow-Origin'] = req.headers.origin;
} else {
headers['Access-Control-Allow-Origin'] = '*';
}*/
res.writeHead(400, headers);
res.end(JSON.stringify({
code: code,
message: Io.errorMessages[code]
}));
}else{
var parser = require("./parser");
parser.encodePacket({
type: "message",
data: JSON.stringify({
code: code,
message: Io.errorMessages[code]
})
},0, function(data) {
req.websocket.send(data);
req.websocket.close();
});
}
}
//初始化
this.handleRequest = function(req, res) {
var self = this;
req.res = res;
this.verify(req, false, function(err) {
//console.log(req.method, err);
if(err) {
return sendErrorMessage(req, res, err);
}
if(req.gets.sid) {
self.clients[req.gets.sid].transport.onRequest(req);
}else{
self.handshake(req.gets.transport, req);
}
});
}
this.verify = function(req, upgrade, fn) {
var transportName = req.gets.transport;
if(!~this.transports.indexOf(transportName)) {
return fn(Io.errors.UNKNOWN_TRANSPORT);
}
var sid = req.gets.sid;
if(sid) {
if(!this.clients.hasOwnProperty(sid)) {
return fn(Io.errors.UNKNOWN_SID);
}
if(!upgrade && this.clients[sid].transport.name !== transportName) {
return fn(Io.errors.BAD_REQUEST);
}
} else {
if("GET" != req.method ) return fn(Io.errors.BAD_HANDSHAKE_METHOD);
//if(this.auth && !this.auth(req)) { return fn(Io.errors.AUTH_FAILED); }
if(!this.allowRequest) return fn(null);
return this.allowRequest(req, fn);
}
fn(null);
}
var randomId = function(num) {
var str = Math.abs(Math.random() * Math.random() * Date.now() | 0).toString(32)
+ Math.abs(Math.random() * Math.random() * Date.now() | 0).toString(32)
return str.slice(-num);
}
this.handshake = function(transportName, req) {
var id ;
if(this.auth) {
id = this.auth(req);
if(id === false || id== undefined) { return sendErrorMessage(req, req.res, Io.errors.AUTH_FAILED); }
if(id === true) {
id = randomId(15);
};
}else{
id = randomId(15);
}
//base64id.generateId();
try {
var transport = new transports[transportName](req);
if("polling" == transportName) {
transport.maxHttpBufferSize = this.maxHttpBufferSize;
}
if( req.gets.b64 ) {
transport.supportsBinary = false;
} else {
transport.supportsBinary = true;
}
}catch(e) {
return sendErrorMessage(req, req.res, Io.errors.BAD_REQUEST);
}
var socket = new Socket(id, this, transport, req);
var self = this;
if(false !== this.cookie) {
transport.on("headers", function(headers) {
headers["Set-Cookie"] = self.cookie + "=" + id;
});
}
/*process.send({
type: "bind",
data: {
address: req.socket.address()
}
});*/
transport.onRequest(req);
this.clients[id] = socket;
this.clientsCount++;
socket.once("close", function() {
if(self.clients[id] == socket) {
delete self.clients[id];
self.clientsCount--;
}
});
this.emit("connection", socket);
}
}).call(Io.prototype);
//module.exports = Io;
function Ios() {
this.all = {};
};
latte_lib.inherits(Ios, events);
(function() {
this.add = function(path, opts, fn) {
if(latte_lib.isFunction(opts)) {
fn = opts;
opts = {};
}
var io = this.all[path] = new Io(path, opts);
fn && io.on("connection", fn);
return io;
}
}).call(Ios.prototype);
module.exports = Ios;
/*} else {
var Engines = function() {
this.all = {};
};
latte_lib.inherits(Engines, events);
(function() {
}).call(Engines.prototype);
module.exports = Engines;
}*/
});
})(typeof define === "function" ? define : function(name, reqs, factory){factory(require, exports, module); });