/
listener.js
135 lines (115 loc) · 3.54 KB
/
listener.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
"use strict";
/**
* @module listener
*/
var util = require("util"),
url = require("url");
/**
* A **HubListener** augments an existing `http.Server`,
* adding Hub handling to a given route.
*
* The default route is `/yui`, which can be changed by
* passing an options object with a route property.
*
* @class HubListener
* @extends process.EventEmitter
* @constructor
* @param {Hub} hub An instance of Hub.
* @param {Server} server The `http.Server` to augment.
* @param {Object} options Options.
*/
function HubListener(hub, server, options) {
process.EventEmitter.call(this);
this.options = options || {};
this.route = this.options.route || "/yeti";
this.server = server;
this.hub = hub;
this._setupServer();
}
util.inherits(HubListener, process.EventEmitter);
var proto = HubListener.prototype;
/**
* Inject our handler into the request listener.
* If the request didn't start with `this.route`,
* call the existing listeners.
*
* @method _setupServer
* @private
*/
proto._setupServer = function () {
var self = this,
upgradeListeners = this.server.listeners("upgrade"),
requestListeners = this.server.listeners("request");
this.server.removeAllListeners("upgrade");
this.server.removeAllListeners("request");
this.server.on("upgrade", function (req, socket, head) {
var server = this,
parsedUrl = url.parse(req.url),
resolvedUrl,
yetiUpgrade = false;
// Check if the request is either for:
// - Socket.io
// - Blizzard-Yeti
if (parsedUrl.pathname.indexOf(self.route) === 0) {
parsedUrl.pathname = parsedUrl.pathname.substr(self.route.length);
resolvedUrl = url.format(parsedUrl);
if (resolvedUrl.indexOf("/socket.io") === 0) {
yetiUpgrade = true;
}
}
if (req.headers.upgrade === "Blizzard-Yeti") {
yetiUpgrade = true;
}
if (yetiUpgrade) {
self.hub.server.emit("upgrade", req, socket, head);
return;
}
upgradeListeners.forEach(function (listener) {
listener.call(server, req, socket, head);
});
});
this.server.on("request", function (req, res) {
if (self.match(req, res)) {
return;
}
var server = this;
requestListeners.forEach(function (listener) {
listener.call(server, req, res);
});
});
};
/**
* Check if this request begins with `this.route`.
* If it does, handle it with Hub.
*
* @method match
* @param {Object} req An HTTP Request.
* @param {Object} res An HTTP Response.
* @return {Boolean} True if matched, false otherwise.
*/
proto.match = function (req, res) {
var parsedUrl = url.parse(req.url),
resolvedUrl;
if (parsedUrl.pathname.indexOf(this.route) === 0) {
parsedUrl.pathname = parsedUrl.pathname.substr(this.route.length);
resolvedUrl = url.format(parsedUrl);
if (resolvedUrl.indexOf("/socket.io") !== 0) {
req.url = resolvedUrl;
}
if (!req.url) {
// There was no trailing slash,
// which means relative paths will not work.
// For example, /yeti instead of /yeti/.
res.writeHead(302, {
"Location": this.route + "/"
});
res.end();
return true;
}
this.hub.server.emit("request", req, res);
return true;
}
return false;
};
// Export HubListener.
module.exports = HubListener;