Bug:Fix file validation issue
[vnfsdk/refrepo.git] / vnfmarket / src / main / webapp / vnfmarket / node_modules / socket.io / lib / transports / websocket / default.js
1 /*!
2  * socket.io-node
3  * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
4  * MIT Licensed
5  */
6
7 /**
8  * Module requirements.
9  */
10
11 var Transport = require('../../transport')
12   , EventEmitter = process.EventEmitter
13   , crypto = require('crypto')
14   , parser = require('../../parser');
15
16 /**
17  * Export the constructor.
18  */
19
20 exports = module.exports = WebSocket;
21
22 /**
23  * HTTP interface constructor. Interface compatible with all transports that
24  * depend on request-response cycles.
25  *
26  * @api public
27  */
28
29 function WebSocket (mng, data, req) {
30   // parser
31   var self = this;
32
33   this.parser = new Parser();
34   this.parser.on('data', function (packet) {
35     self.log.debug(self.name + ' received data packet', packet);
36     self.onMessage(parser.decodePacket(packet));
37   });
38   this.parser.on('close', function () {
39     self.end();
40   });
41   this.parser.on('error', function () {
42     self.end();
43   });
44
45   Transport.call(this, mng, data, req);
46 };
47
48 /**
49  * Inherits from Transport.
50  */
51
52 WebSocket.prototype.__proto__ = Transport.prototype;
53
54 /**
55  * Transport name
56  *
57  * @api public
58  */
59
60 WebSocket.prototype.name = 'websocket';
61
62 /**
63  * Websocket draft version
64  *
65  * @api public
66  */
67
68 WebSocket.prototype.protocolVersion = 'hixie-76';
69
70 /**
71  * Called when the socket connects.
72  *
73  * @api private
74  */
75
76 WebSocket.prototype.onSocketConnect = function () {
77   var self = this;
78
79   this.socket.setNoDelay(true);
80
81   this.buffer = true;
82   this.buffered = [];
83
84   if (this.req.headers.upgrade !== 'WebSocket') {
85     this.log.warn(this.name + ' connection invalid');
86     this.end();
87     return;
88   }
89
90   var origin = this.req.headers['origin']
91   , waitingForNonce = false;
92   if(this.manager.settings['match origin protocol']){
93     location = (origin.indexOf('https')>-1 ? 'wss' : 'ws') + '://' + this.req.headers.host + this.req.url;
94   }else if(this.socket.encrypted){
95     location = 'wss://' + this.req.headers.host + this.req.url;
96   }else{
97     location = 'ws://' + this.req.headers.host + this.req.url;
98   }
99
100   if (this.req.headers['sec-websocket-key1']) {
101     // If we don't have the nonce yet, wait for it (HAProxy compatibility).
102     if (! (this.req.head && this.req.head.length >= 8)) {
103       waitingForNonce = true;
104     }
105
106     var headers = [
107         'HTTP/1.1 101 WebSocket Protocol Handshake'
108       , 'Upgrade: WebSocket'
109       , 'Connection: Upgrade'
110       , 'Sec-WebSocket-Origin: ' + origin
111       , 'Sec-WebSocket-Location: ' + location
112     ];
113
114     if (this.req.headers['sec-websocket-protocol']){
115       headers.push('Sec-WebSocket-Protocol: '
116           + this.req.headers['sec-websocket-protocol']);
117     }
118   } else {
119     var headers = [
120         'HTTP/1.1 101 Web Socket Protocol Handshake'
121       , 'Upgrade: WebSocket'
122       , 'Connection: Upgrade'
123       , 'WebSocket-Origin: ' + origin
124       , 'WebSocket-Location: ' + location
125     ];
126   }
127
128   try {
129     this.socket.write(headers.concat('', '').join('\r\n'));
130     this.socket.setTimeout(0);
131     this.socket.setNoDelay(true);
132     this.socket.setEncoding('utf8');
133   } catch (e) {
134     this.end();
135     return;
136   }
137
138   if (waitingForNonce) {
139     this.socket.setEncoding('binary');
140   } else if (this.proveReception(headers)) {
141     self.flush();
142   }
143
144   var headBuffer = '';
145
146   this.socket.on('data', function (data) {
147     if (waitingForNonce) {
148       headBuffer += data;
149
150       if (headBuffer.length < 8) {
151         return;
152       }
153
154       // Restore the connection to utf8 encoding after receiving the nonce
155       self.socket.setEncoding('utf8');
156       waitingForNonce = false;
157
158       // Stuff the nonce into the location where it's expected to be
159       self.req.head = headBuffer.substr(0, 8);
160       headBuffer = '';
161
162       if (self.proveReception(headers)) {
163         self.flush();
164       }
165
166       return;
167     }
168
169     self.parser.add(data);
170   });
171 };
172
173 /**
174  * Writes to the socket.
175  *
176  * @api private
177  */
178
179 WebSocket.prototype.write = function (data) {
180   if (this.open) {
181     this.drained = false;
182
183     if (this.buffer) {
184       this.buffered.push(data);
185       return this;
186     }
187
188     var length = Buffer.byteLength(data)
189       , buffer = new Buffer(2 + length);
190
191     buffer.write('\x00', 'binary');
192     buffer.write(data, 1, 'utf8');
193     buffer.write('\xff', 1 + length, 'binary');
194
195     try {
196       if (this.socket.write(buffer)) {
197         this.drained = true;
198       }
199     } catch (e) {
200       this.end();
201     }
202
203     this.log.debug(this.name + ' writing', data);
204   }
205 };
206
207 /**
208  * Flushes the internal buffer
209  *
210  * @api private
211  */
212
213 WebSocket.prototype.flush = function () {
214   this.buffer = false;
215
216   for (var i = 0, l = this.buffered.length; i < l; i++) {
217     this.write(this.buffered.splice(0, 1)[0]);
218   }
219 };
220
221 /**
222  * Finishes the handshake.
223  *
224  * @api private
225  */
226
227 WebSocket.prototype.proveReception = function (headers) {
228   var self = this
229     , k1 = this.req.headers['sec-websocket-key1']
230     , k2 = this.req.headers['sec-websocket-key2'];
231
232   if (k1 && k2){
233     var md5 = crypto.createHash('md5');
234
235     [k1, k2].forEach(function (k) {
236       var n = parseInt(k.replace(/[^\d]/g, ''))
237         , spaces = k.replace(/[^ ]/g, '').length;
238
239       if (spaces === 0 || n % spaces !== 0){
240         self.log.warn('Invalid ' + self.name + ' key: "' + k + '".');
241         self.end();
242         return false;
243       }
244
245       n /= spaces;
246
247       md5.update(String.fromCharCode(
248         n >> 24 & 0xFF,
249         n >> 16 & 0xFF,
250         n >> 8  & 0xFF,
251         n       & 0xFF));
252     });
253
254     md5.update(this.req.head.toString('binary'));
255
256     try {
257       this.socket.write(md5.digest('binary'), 'binary');
258     } catch (e) {
259       this.end();
260     }
261   }
262
263   return true;
264 };
265
266 /**
267  * Writes a payload.
268  *
269  * @api private
270  */
271
272 WebSocket.prototype.payload = function (msgs) {
273   for (var i = 0, l = msgs.length; i < l; i++) {
274     this.write(msgs[i]);
275   }
276
277   return this;
278 };
279
280 /**
281  * Closes the connection.
282  *
283  * @api private
284  */
285
286 WebSocket.prototype.doClose = function () {
287   this.socket.end();
288 };
289
290 /**
291  * WebSocket parser
292  *
293  * @api public
294  */
295
296 function Parser () {
297   this.buffer = '';
298   this.i = 0;
299 };
300
301 /**
302  * Inherits from EventEmitter.
303  */
304
305 Parser.prototype.__proto__ = EventEmitter.prototype;
306
307 /**
308  * Adds data to the buffer.
309  *
310  * @api public
311  */
312
313 Parser.prototype.add = function (data) {
314   this.buffer += data;
315   this.parse();
316 };
317
318 /**
319  * Parses the buffer.
320  *
321  * @api private
322  */
323
324 Parser.prototype.parse = function () {
325   for (var i = this.i, chr, l = this.buffer.length; i < l; i++){
326     chr = this.buffer[i];
327
328     if (this.buffer.length == 2 && this.buffer[1] == '\u0000') {
329       this.emit('close');
330       this.buffer = '';
331       this.i = 0;
332       return;
333     }
334
335     if (i === 0){
336       if (chr != '\u0000')
337         this.error('Bad framing. Expected null byte as first frame');
338       else
339         continue;
340     }
341
342     if (chr == '\ufffd'){
343       this.emit('data', this.buffer.substr(1, i - 1));
344       this.buffer = this.buffer.substr(i + 1);
345       this.i = 0;
346       return this.parse();
347     }
348   }
349 };
350
351 /**
352  * Handles an error
353  *
354  * @api private
355  */
356
357 Parser.prototype.error = function (reason) {
358   this.buffer = '';
359   this.i = 0;
360   this.emit('error', reason);
361   return this;
362 };