/
OrionSocketWSClient.js
152 lines (123 loc) · 4.04 KB
/
OrionSocketWSClient.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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
var url = require('url'),
Buffer = require('buffer').Buffer,
sys = require('sys'),
crypto = require('crypto');
global.OrionSocketWSClient = OrionSocketClient.extend({
httpUpgrade: true,
connectionHeartbeat: false,
_onConnect: function(req, socket){
var self = this, headers = [];
sys.puts('OrionSocketWSClient request: ' + req);
this.request = req;
this.connection = socket;
this.data = '';
if (this.request.headers.upgrade !== 'WebSocket' || !this._verifyOrigin(this.request.headers.origin)){
this.listener.options.log('WebSocket connection invalid');
this.connection.end();
}
this.connection.setTimeout(0);
this.connection.setEncoding('utf8');
this.connection.setNoDelay(true);
if ('sec-websocket-key1' in this.request.headers) {
this.draft = 76;
}
if (this.draft == 76) {
var origin = this.request.headers.origin;
headers = [
'HTTP/1.1 101 WebSocket Protocol Handshake',
'Upgrade: WebSocket',
'Connection: Upgrade',
'Sec-WebSocket-Origin: ' + (origin || 'null'),
'Sec-WebSocket-Location: ws://' + this.request.headers.host + this.request.url
];
if ('sec-websocket-protocol' in this.request.headers) {
headers.push('Sec-WebSocket-Protocol: ' + this.request.headers['sec-websocket-protocol']);
}
}
else {
headers = [
'HTTP/1.1 101 Web Socket Protocol Handshake',
'Upgrade: WebSocket',
'Connection: Upgrade',
'WebSocket-Origin: ' + this.request.headers.origin,
'WebSocket-Location: ws://' + this.request.headers.host + this.request.url
];
try {
this.connection.write(headers.concat('', '').join('\r\n'));
} catch(e){
this._onClose();
}
}
this.connection.addListener('end', function(){self._onClose();});
this.connection.addListener('data', function(data){self._handle(data);});
if (this._proveReception(headers)){
// now we know that the connection is proper...
// the function below sets the session key and sends it to the client...
// this is not the way I want to have things work.
// a client needs to be authenticated before anything can be sent
// the payload function sets a few useful settings
this._payload();
}
// I don't know why this is necessary at all...
if(this.connectionHeartbeat){
setInterval(function(){
self._write(JSON.stringify({heartbeat: '1'}));
}, 10000);
}
},
_handle: function(data){
var chunk, chunks, chunk_count;
this.data += data;
chunks = this.data.split('\ufffd');
chunk_count = chunks.length - 1;
for (var i = 0; i < chunk_count; i++) {
chunk = chunks[i];
if (chunk[0] !== '\u0000') {
this.listener.options.log('Data incorrectly framed by UA. Dropping connection');
this.connection.destroy();
return false;
}
this._onMessage(chunk.slice(1));
}
this.data = chunks[chunks.length - 1];
},
// http://www.whatwg.org/specs/web-apps/current-work/complete/network.html#opening-handshake
_proveReception: function(headers){
var k1 = this.request.headers['sec-websocket-key1'],
k2 = this.request.headers['sec-websocket-key2'];
if (k1 && k2) {
var md5 = crypto.createHash('md5');
[k1, k2].forEach(function(k) {
var n = parseInt(k.replace(/[^\d]/g, '')),
spaces = k.replace(/[^ ]/g, '').length;
if (spaces === 0 || n % spaces !== 0) {
this.listener.options.log('Invalid WebSocket key: "' + k + '". Dropping connection');
this.connection.destroy();
return false;
}
n /= spaces;
md5.update(String.fromCharCode(
n >> 24 & 0xFF,
n >> 16 & 0xFF,
n >> 8 & 0xFF,
n & 0xFF));
});
md5.update(this.upgradeHead.toString('binary'));
try {
this.connection.write(headers.concat('', '').join('\r\n') + md5.digest('binary'), 'binary');
} catch(e){
this._onClose();
}
}
return true;
},
_write: function(message){
try {
this.connection.write('\u0000', 'binary');
this.connection.write(message, 'utf8');
this.connection.write('\uffff', 'binary');
} catch(e){
this._onClose();
}
}
});