UNPKG

2.89 kBJavaScriptView Raw
1var crypto = require('crypto');
2var EventEmitter = require('events').EventEmitter;
3var http = require('http');
4var https = require('https');
5var url = require('url');
6var util = require('util');
7var revolt = require('revolt');
8
9var WebSocket = module.exports = function(address, httpOptions) {
10 EventEmitter.call(this);
11
12 this.options = {
13 method: 'GET',
14 headers: {
15 'Connection': 'Upgrade',
16 'Upgrade': 'websocket',
17 'Sec-WebSocket-Version': '13',
18 }
19 };
20
21 this.isClosed = false;
22 this._requestExtensions = [];
23 this._responseExtensions = [];
24
25 var self = this;
26 if (httpOptions) {
27 for (k in httpOptions) {
28 self.options[k] = httpOptions[k];
29 }
30 }
31
32 this.setAddress(address);
33
34 this.request = revolt();
35};
36
37util.inherits(WebSocket, EventEmitter);
38
39WebSocket.prototype.setAddress = function(address) {
40 if (address.substr(0, 2) === 'ws') {
41 address = 'http' + address.substr(2);
42 }
43
44 this.options.uri = address;
45 var parsed = url.parse(address);
46 this.options.headers['Host'] = parsed.host;
47};
48
49WebSocket.prototype.extendRequest = function(extensions) {
50 this._requestExtensions = extensions;
51};
52
53WebSocket.prototype.extendResponse = function(extensions) {
54 this._responseExtensions = extensions;
55};
56
57WebSocket.prototype.close = function() {
58 if (this.isClosed) {
59 return;
60 }
61
62 this.isClosed = true;
63 if(this.socket) {
64 this.socket.end();
65 } else {
66 this.emit('close', null, null, true);
67 }
68};
69
70WebSocket.prototype.start = function() {
71 var key = new Buffer('13' + '-' + Date.now()).toString('base64');
72 var shasum = crypto.createHash('sha1');
73 shasum.update(key + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11');
74 var expectedServerKey = shasum.digest('base64');
75
76 this.options.headers['Sec-WebSocket-Key'] = key;
77
78 var self = this;
79 var req = this.request;
80
81 this._requestExtensions.forEach(function(ext) {
82 ext(req);
83 });
84
85 req = req.request(self.options);
86
87 this._responseExtensions.forEach(function(ext) {
88 req = ext(req);
89 });
90
91 var subscription = req.subscribe(function(env) {
92 // Handle non 101 response codes with clearer message
93 if (env.response.statusCode !== 101) {
94 self.emit('error', 'server returned ' + env.response.statusCode);
95 return;
96 }
97
98 var serverKey = env.response.headers['sec-websocket-accept'];
99 if (typeof serverKey == 'undefined' || serverKey !== expectedServerKey) {
100 self.emit('error', 'invalid server key');
101 return;
102 }
103
104 if (env.response.head.length > 0) {
105 env.request.connection.unshift(env.response.head);
106 }
107
108 self.isClosed = false;
109 self.socket = env.request.connection;
110 self.socket.on('close', function() {
111 self.emit('close');
112 env.request.abort();
113 subscription.dispose();
114 });
115 self.emit('open', env.request.connection);
116 }, function(err) {
117 self.emit('error', err);
118 });
119};