opal 1.6.1 → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/build.yml +17 -0
- data/CHANGELOG.md +35 -1
- data/Gemfile +1 -0
- data/HACKING.md +47 -26
- data/benchmark/benchmarks +415 -103
- data/benchmark/bm_call_overhead.yml +28 -0
- data/benchmark/run.rb +61 -40
- data/docs/cdp_common.json +3364 -0
- data/docs/cdp_common.md +18 -0
- data/docs/{headless_chrome.md → headless_browsers.md} +31 -12
- data/lib/opal/ast/builder.rb +1 -1
- data/lib/opal/builder.rb +6 -1
- data/lib/opal/builder_processors.rb +5 -3
- data/lib/opal/cache.rb +1 -7
- data/lib/opal/cli_options.rb +72 -58
- data/lib/opal/cli_runners/chrome.rb +47 -9
- data/lib/opal/cli_runners/chrome_cdp_interface.rb +238 -112
- data/lib/opal/cli_runners/compiler.rb +146 -13
- data/lib/opal/cli_runners/deno.rb +32 -0
- data/lib/opal/cli_runners/firefox.rb +350 -0
- data/lib/opal/cli_runners/firefox_cdp_interface.rb +212 -0
- data/lib/opal/cli_runners/node_modules/.bin/chrome-remote-interface.cmd +17 -0
- data/lib/opal/cli_runners/node_modules/.bin/chrome-remote-interface.ps1 +28 -0
- data/lib/opal/cli_runners/node_modules/.package-lock.json +41 -0
- data/lib/opal/cli_runners/node_modules/chrome-remote-interface/LICENSE +1 -1
- data/lib/opal/cli_runners/node_modules/chrome-remote-interface/README.md +322 -182
- data/lib/opal/cli_runners/node_modules/chrome-remote-interface/bin/client.js +99 -114
- data/lib/opal/cli_runners/node_modules/chrome-remote-interface/chrome-remote-interface.js +1 -11
- data/lib/opal/cli_runners/node_modules/chrome-remote-interface/index.js +16 -11
- data/lib/opal/cli_runners/node_modules/chrome-remote-interface/lib/api.js +41 -33
- data/lib/opal/cli_runners/node_modules/chrome-remote-interface/lib/chrome.js +224 -214
- data/lib/opal/cli_runners/node_modules/chrome-remote-interface/lib/devtools.js +71 -191
- data/lib/opal/cli_runners/node_modules/chrome-remote-interface/lib/external-request.js +26 -6
- data/lib/opal/cli_runners/node_modules/chrome-remote-interface/lib/protocol.json +20788 -9049
- data/lib/opal/cli_runners/node_modules/chrome-remote-interface/lib/websocket-wrapper.js +10 -3
- data/lib/opal/cli_runners/node_modules/chrome-remote-interface/package.json +59 -123
- data/lib/opal/cli_runners/node_modules/chrome-remote-interface/webpack.config.js +25 -32
- data/lib/opal/cli_runners/node_modules/commander/History.md +298 -0
- data/lib/opal/cli_runners/node_modules/commander/LICENSE +22 -0
- data/lib/opal/cli_runners/node_modules/commander/Readme.md +217 -61
- data/lib/opal/cli_runners/node_modules/commander/index.js +431 -145
- data/lib/opal/cli_runners/node_modules/commander/package.json +16 -79
- data/lib/opal/cli_runners/node_modules/ws/README.md +334 -98
- data/lib/opal/cli_runners/node_modules/ws/browser.js +8 -0
- data/lib/opal/cli_runners/node_modules/ws/index.js +5 -10
- data/lib/opal/cli_runners/node_modules/ws/lib/buffer-util.js +129 -0
- data/lib/opal/cli_runners/node_modules/ws/lib/constants.js +10 -0
- data/lib/opal/cli_runners/node_modules/ws/lib/event-target.js +184 -0
- data/lib/opal/cli_runners/node_modules/ws/lib/extension.js +223 -0
- data/lib/opal/cli_runners/node_modules/ws/lib/limiter.js +55 -0
- data/lib/opal/cli_runners/node_modules/ws/lib/permessage-deflate.js +518 -0
- data/lib/opal/cli_runners/node_modules/ws/lib/receiver.js +607 -0
- data/lib/opal/cli_runners/node_modules/ws/lib/sender.js +409 -0
- data/lib/opal/cli_runners/node_modules/ws/lib/stream.js +180 -0
- data/lib/opal/cli_runners/node_modules/ws/lib/validation.js +104 -0
- data/lib/opal/cli_runners/node_modules/ws/lib/websocket-server.js +447 -0
- data/lib/opal/cli_runners/node_modules/ws/lib/websocket.js +1195 -0
- data/lib/opal/cli_runners/node_modules/ws/package.json +40 -106
- data/lib/opal/cli_runners/package-lock.json +62 -0
- data/lib/opal/cli_runners/package.json +1 -1
- data/lib/opal/cli_runners.rb +26 -4
- data/lib/opal/nodes/args/prepare_post_args.rb +2 -2
- data/lib/opal/nodes/def.rb +8 -8
- data/lib/opal/nodes/iter.rb +12 -12
- data/lib/opal/nodes/logic.rb +1 -1
- data/lib/opal/nodes/masgn.rb +2 -2
- data/lib/opal/parser/with_ruby_lexer.rb +1 -1
- data/lib/opal/paths.rb +14 -0
- data/lib/opal/rewriter.rb +2 -0
- data/lib/opal/rewriters/forward_args.rb +52 -4
- data/lib/opal/rewriters/targeted_patches.rb +94 -0
- data/lib/opal/version.rb +1 -1
- data/opal/corelib/basic_object.rb +1 -1
- data/opal/corelib/boolean.rb +2 -2
- data/opal/corelib/class.rb +11 -0
- data/opal/corelib/constants.rb +3 -3
- data/opal/corelib/enumerable.rb +4 -0
- data/opal/corelib/enumerator.rb +1 -1
- data/opal/corelib/hash.rb +2 -2
- data/opal/corelib/helpers.rb +1 -1
- data/opal/corelib/kernel.rb +3 -3
- data/opal/corelib/method.rb +1 -1
- data/opal/corelib/module.rb +29 -8
- data/opal/corelib/proc.rb +7 -5
- data/opal/corelib/runtime.js +141 -78
- data/opal/corelib/set.rb +252 -0
- data/opal/corelib/string.rb +2 -1
- data/opal/corelib/time.rb +2 -2
- data/opal/opal.rb +1 -0
- data/opal.gemspec +1 -0
- data/spec/filters/bugs/array.rb +22 -13
- data/spec/filters/bugs/base64.rb +5 -5
- data/spec/filters/bugs/basicobject.rb +16 -8
- data/spec/filters/bugs/bigdecimal.rb +161 -160
- data/spec/filters/bugs/binding.rb +10 -10
- data/spec/filters/bugs/class.rb +8 -8
- data/spec/filters/bugs/complex.rb +2 -1
- data/spec/filters/bugs/date.rb +79 -81
- data/spec/filters/bugs/datetime.rb +29 -29
- data/spec/filters/bugs/delegate.rb +1 -3
- data/spec/filters/bugs/encoding.rb +69 -69
- data/spec/filters/bugs/enumerable.rb +22 -20
- data/spec/filters/bugs/enumerator.rb +88 -85
- data/spec/filters/bugs/exception.rb +46 -40
- data/spec/filters/bugs/file.rb +32 -32
- data/spec/filters/bugs/float.rb +26 -21
- data/spec/filters/bugs/freeze.rb +88 -0
- data/spec/filters/bugs/hash.rb +39 -38
- data/spec/filters/bugs/integer.rb +57 -44
- data/spec/filters/bugs/io.rb +1 -1
- data/spec/filters/bugs/kernel.rb +349 -269
- data/spec/filters/bugs/language.rb +220 -188
- data/spec/filters/bugs/main.rb +5 -3
- data/spec/filters/bugs/marshal.rb +38 -38
- data/spec/filters/bugs/math.rb +2 -1
- data/spec/filters/bugs/method.rb +73 -62
- data/spec/filters/bugs/module.rb +163 -143
- data/spec/filters/bugs/numeric.rb +6 -6
- data/spec/filters/bugs/objectspace.rb +16 -16
- data/spec/filters/bugs/openstruct.rb +1 -1
- data/spec/filters/bugs/pack_unpack.rb +51 -51
- data/spec/filters/bugs/pathname.rb +7 -7
- data/spec/filters/bugs/proc.rb +63 -63
- data/spec/filters/bugs/random.rb +7 -6
- data/spec/filters/bugs/range.rb +12 -9
- data/spec/filters/bugs/rational.rb +8 -7
- data/spec/filters/bugs/regexp.rb +49 -48
- data/spec/filters/bugs/ruby-32.rb +56 -0
- data/spec/filters/bugs/set.rb +30 -30
- data/spec/filters/bugs/singleton.rb +4 -4
- data/spec/filters/bugs/string.rb +187 -99
- data/spec/filters/bugs/stringio.rb +7 -0
- data/spec/filters/bugs/stringscanner.rb +68 -68
- data/spec/filters/bugs/struct.rb +11 -9
- data/spec/filters/bugs/symbol.rb +1 -1
- data/spec/filters/bugs/time.rb +78 -63
- data/spec/filters/bugs/trace_point.rb +4 -4
- data/spec/filters/bugs/unboundmethod.rb +32 -17
- data/spec/filters/bugs/warnings.rb +8 -12
- data/spec/filters/unsupported/array.rb +24 -107
- data/spec/filters/unsupported/basicobject.rb +12 -12
- data/spec/filters/unsupported/bignum.rb +27 -52
- data/spec/filters/unsupported/class.rb +1 -2
- data/spec/filters/unsupported/delegator.rb +3 -3
- data/spec/filters/unsupported/enumerable.rb +2 -9
- data/spec/filters/unsupported/enumerator.rb +2 -11
- data/spec/filters/unsupported/file.rb +1 -1
- data/spec/filters/unsupported/float.rb +28 -47
- data/spec/filters/unsupported/hash.rb +8 -14
- data/spec/filters/unsupported/integer.rb +75 -91
- data/spec/filters/unsupported/kernel.rb +17 -35
- data/spec/filters/unsupported/language.rb +11 -19
- data/spec/filters/unsupported/marshal.rb +22 -41
- data/spec/filters/unsupported/matchdata.rb +28 -52
- data/spec/filters/unsupported/math.rb +1 -1
- data/spec/filters/unsupported/privacy.rb +229 -285
- data/spec/filters/unsupported/range.rb +1 -5
- data/spec/filters/unsupported/regexp.rb +40 -66
- data/spec/filters/unsupported/set.rb +2 -2
- data/spec/filters/unsupported/singleton.rb +4 -4
- data/spec/filters/unsupported/string.rb +305 -508
- data/spec/filters/unsupported/struct.rb +3 -4
- data/spec/filters/unsupported/symbol.rb +15 -18
- data/spec/filters/unsupported/thread.rb +1 -7
- data/spec/filters/unsupported/time.rb +159 -202
- data/spec/filters/unsupported/usage_of_files.rb +170 -259
- data/spec/lib/builder_spec.rb +4 -4
- data/spec/lib/rewriters/forward_args_spec.rb +32 -12
- data/spec/mspec-opal/runner.rb +2 -0
- data/spec/ruby_specs +4 -0
- data/stdlib/deno/base.rb +28 -0
- data/stdlib/deno/file.rb +340 -0
- data/stdlib/{headless_chrome.rb → headless_browser/base.rb} +1 -1
- data/stdlib/headless_browser/file.rb +15 -0
- data/stdlib/headless_browser.rb +4 -0
- data/stdlib/native.rb +1 -1
- data/stdlib/nodejs/file.rb +5 -0
- data/stdlib/opal/platform.rb +8 -6
- data/stdlib/opal-platform.rb +14 -8
- data/stdlib/set.rb +1 -258
- data/tasks/benchmarking.rake +62 -19
- data/tasks/performance.rake +1 -1
- data/tasks/testing.rake +5 -3
- data/test/nodejs/test_file.rb +29 -10
- data/test/opal/http_server.rb +28 -11
- data/test/opal/unsupported_and_bugs.rb +2 -1
- metadata +89 -50
- data/lib/opal/cli_runners/node_modules/ultron/LICENSE +0 -22
- data/lib/opal/cli_runners/node_modules/ultron/index.js +0 -138
- data/lib/opal/cli_runners/node_modules/ultron/package.json +0 -112
- data/lib/opal/cli_runners/node_modules/ws/SECURITY.md +0 -33
- data/lib/opal/cli_runners/node_modules/ws/lib/BufferUtil.fallback.js +0 -56
- data/lib/opal/cli_runners/node_modules/ws/lib/BufferUtil.js +0 -15
- data/lib/opal/cli_runners/node_modules/ws/lib/ErrorCodes.js +0 -28
- data/lib/opal/cli_runners/node_modules/ws/lib/EventTarget.js +0 -158
- data/lib/opal/cli_runners/node_modules/ws/lib/Extensions.js +0 -69
- data/lib/opal/cli_runners/node_modules/ws/lib/PerMessageDeflate.js +0 -339
- data/lib/opal/cli_runners/node_modules/ws/lib/Receiver.js +0 -520
- data/lib/opal/cli_runners/node_modules/ws/lib/Sender.js +0 -438
- data/lib/opal/cli_runners/node_modules/ws/lib/Validation.fallback.js +0 -9
- data/lib/opal/cli_runners/node_modules/ws/lib/Validation.js +0 -17
- data/lib/opal/cli_runners/node_modules/ws/lib/WebSocket.js +0 -705
- data/lib/opal/cli_runners/node_modules/ws/lib/WebSocketServer.js +0 -336
- data/spec/filters/bugs/boolean.rb +0 -3
- data/spec/filters/bugs/matrix.rb +0 -3
- data/spec/filters/unsupported/fixnum.rb +0 -15
- data/spec/filters/unsupported/freeze.rb +0 -102
- data/spec/filters/unsupported/pathname.rb +0 -4
- data/spec/filters/unsupported/proc.rb +0 -4
- data/spec/filters/unsupported/random.rb +0 -5
- data/spec/filters/unsupported/taint.rb +0 -162
@@ -1,705 +0,0 @@
|
|
1
|
-
/*!
|
2
|
-
* ws: a node.js websocket client
|
3
|
-
* Copyright(c) 2011 Einar Otto Stangvik <einaros@gmail.com>
|
4
|
-
* MIT Licensed
|
5
|
-
*/
|
6
|
-
|
7
|
-
'use strict';
|
8
|
-
|
9
|
-
const EventEmitter = require('events');
|
10
|
-
const crypto = require('crypto');
|
11
|
-
const Ultron = require('ultron');
|
12
|
-
const https = require('https');
|
13
|
-
const http = require('http');
|
14
|
-
const url = require('url');
|
15
|
-
|
16
|
-
const PerMessageDeflate = require('./PerMessageDeflate');
|
17
|
-
const EventTarget = require('./EventTarget');
|
18
|
-
const Extensions = require('./Extensions');
|
19
|
-
const Receiver = require('./Receiver');
|
20
|
-
const Sender = require('./Sender');
|
21
|
-
|
22
|
-
const GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
|
23
|
-
const closeTimeout = 30 * 1000; // Allow 30 seconds to terminate the connection cleanly.
|
24
|
-
const protocolVersion = 13;
|
25
|
-
const noop = () => {};
|
26
|
-
|
27
|
-
/**
|
28
|
-
* Class representing a WebSocket.
|
29
|
-
*
|
30
|
-
* @extends EventEmitter
|
31
|
-
*/
|
32
|
-
class WebSocket extends EventEmitter {
|
33
|
-
/**
|
34
|
-
* Create a new `WebSocket`.
|
35
|
-
*
|
36
|
-
* @param {String} address The URL to which to connect
|
37
|
-
* @param {(String|String[])} protocols The subprotocols
|
38
|
-
* @param {Object} options Connection options
|
39
|
-
*/
|
40
|
-
constructor (address, protocols, options) {
|
41
|
-
super();
|
42
|
-
|
43
|
-
if (typeof protocols === 'object' && !Array.isArray(protocols)) {
|
44
|
-
options = protocols;
|
45
|
-
protocols = null;
|
46
|
-
}
|
47
|
-
|
48
|
-
if (typeof protocols === 'string') protocols = [protocols];
|
49
|
-
if (!Array.isArray(protocols)) protocols = [];
|
50
|
-
|
51
|
-
this.readyState = WebSocket.CONNECTING;
|
52
|
-
this.bytesReceived = 0;
|
53
|
-
this.extensions = {};
|
54
|
-
this.protocol = '';
|
55
|
-
|
56
|
-
this._finalize = this.finalize.bind(this);
|
57
|
-
this._binaryType = 'nodebuffer';
|
58
|
-
this._finalizeCalled = false;
|
59
|
-
this._closeMessage = null;
|
60
|
-
this._closeTimer = null;
|
61
|
-
this._closeCode = null;
|
62
|
-
this._receiver = null;
|
63
|
-
this._sender = null;
|
64
|
-
this._socket = null;
|
65
|
-
this._ultron = null;
|
66
|
-
|
67
|
-
if (Array.isArray(address)) {
|
68
|
-
initAsServerClient.call(this, address[0], address[1], address[2], options);
|
69
|
-
} else {
|
70
|
-
initAsClient.call(this, address, protocols, options);
|
71
|
-
}
|
72
|
-
}
|
73
|
-
|
74
|
-
get CONNECTING () { return WebSocket.CONNECTING; }
|
75
|
-
get CLOSING () { return WebSocket.CLOSING; }
|
76
|
-
get CLOSED () { return WebSocket.CLOSED; }
|
77
|
-
get OPEN () { return WebSocket.OPEN; }
|
78
|
-
|
79
|
-
/**
|
80
|
-
* @type {Number}
|
81
|
-
*/
|
82
|
-
get bufferedAmount () {
|
83
|
-
var amount = 0;
|
84
|
-
|
85
|
-
if (this._socket) {
|
86
|
-
amount = this._socket.bufferSize + this._sender.bufferedBytes;
|
87
|
-
}
|
88
|
-
return amount;
|
89
|
-
}
|
90
|
-
|
91
|
-
/**
|
92
|
-
* This deviates from the WHATWG interface since ws doesn't support the required
|
93
|
-
* default "blob" type (instead we define a custom "nodebuffer" type).
|
94
|
-
*
|
95
|
-
* @type {String}
|
96
|
-
*/
|
97
|
-
get binaryType () {
|
98
|
-
return this._binaryType;
|
99
|
-
}
|
100
|
-
|
101
|
-
set binaryType (type) {
|
102
|
-
if (type === 'arraybuffer' || type === 'nodebuffer') {
|
103
|
-
this._binaryType = type;
|
104
|
-
} else {
|
105
|
-
throw new SyntaxError('unsupported binaryType: must be either "nodebuffer" or "arraybuffer"');
|
106
|
-
}
|
107
|
-
}
|
108
|
-
|
109
|
-
/**
|
110
|
-
* Set up the socket and the internal resources.
|
111
|
-
*
|
112
|
-
* @param {net.Socket} socket The network socket between the server and client
|
113
|
-
* @param {Buffer} head The first packet of the upgraded stream
|
114
|
-
* @private
|
115
|
-
*/
|
116
|
-
setSocket (socket, head) {
|
117
|
-
socket.setTimeout(0);
|
118
|
-
socket.setNoDelay();
|
119
|
-
|
120
|
-
this._receiver = new Receiver(this.extensions, this.maxPayload);
|
121
|
-
this._sender = new Sender(socket, this.extensions);
|
122
|
-
this._ultron = new Ultron(socket);
|
123
|
-
this._socket = socket;
|
124
|
-
|
125
|
-
// socket cleanup handlers
|
126
|
-
this._ultron.on('close', this._finalize);
|
127
|
-
this._ultron.on('error', this._finalize);
|
128
|
-
this._ultron.on('end', this._finalize);
|
129
|
-
|
130
|
-
// ensure that the head is added to the receiver
|
131
|
-
if (head && head.length > 0) {
|
132
|
-
socket.unshift(head);
|
133
|
-
head = null;
|
134
|
-
}
|
135
|
-
|
136
|
-
// subsequent packets are pushed to the receiver
|
137
|
-
this._ultron.on('data', (data) => {
|
138
|
-
this.bytesReceived += data.length;
|
139
|
-
this._receiver.add(data);
|
140
|
-
});
|
141
|
-
|
142
|
-
// receiver event handlers
|
143
|
-
this._receiver.onmessage = (data, flags) => this.emit('message', data, flags);
|
144
|
-
this._receiver.onping = (data, flags) => {
|
145
|
-
this.pong(data, !this._isServer, true);
|
146
|
-
this.emit('ping', data, flags);
|
147
|
-
};
|
148
|
-
this._receiver.onpong = (data, flags) => this.emit('pong', data, flags);
|
149
|
-
this._receiver.onclose = (code, reason) => {
|
150
|
-
this._closeMessage = reason;
|
151
|
-
this._closeCode = code;
|
152
|
-
this.close(code, reason);
|
153
|
-
};
|
154
|
-
this._receiver.onerror = (error, code) => {
|
155
|
-
// close the connection when the receiver reports a HyBi error code
|
156
|
-
this.close(code, '');
|
157
|
-
this.emit('error', error);
|
158
|
-
};
|
159
|
-
|
160
|
-
// sender event handlers
|
161
|
-
this._sender.onerror = (error) => {
|
162
|
-
this.close(1002, '');
|
163
|
-
this.emit('error', error);
|
164
|
-
};
|
165
|
-
|
166
|
-
this.readyState = WebSocket.OPEN;
|
167
|
-
this.emit('open');
|
168
|
-
}
|
169
|
-
|
170
|
-
/**
|
171
|
-
* Clean up and release internal resources.
|
172
|
-
*
|
173
|
-
* @param {(Boolean|Error)} Indicates whether or not an error occurred
|
174
|
-
* @private
|
175
|
-
*/
|
176
|
-
finalize (error) {
|
177
|
-
if (this._finalizeCalled) return;
|
178
|
-
|
179
|
-
this.readyState = WebSocket.CLOSING;
|
180
|
-
this._finalizeCalled = true;
|
181
|
-
|
182
|
-
clearTimeout(this._closeTimer);
|
183
|
-
this._closeTimer = null;
|
184
|
-
|
185
|
-
//
|
186
|
-
// If the connection was closed abnormally (with an error), or if the close
|
187
|
-
// control frame was malformed or not received then the close code must be
|
188
|
-
// 1006.
|
189
|
-
//
|
190
|
-
if (error) this._closeCode = 1006;
|
191
|
-
|
192
|
-
if (this._socket) {
|
193
|
-
this._ultron.destroy();
|
194
|
-
this._socket.on('error', function onerror () {
|
195
|
-
this.destroy();
|
196
|
-
});
|
197
|
-
|
198
|
-
if (!error) this._socket.end();
|
199
|
-
else this._socket.destroy();
|
200
|
-
|
201
|
-
this._socket = null;
|
202
|
-
this._ultron = null;
|
203
|
-
}
|
204
|
-
|
205
|
-
if (this._sender) {
|
206
|
-
this._sender = this._sender.onerror = null;
|
207
|
-
}
|
208
|
-
|
209
|
-
if (this._receiver) {
|
210
|
-
this._receiver.cleanup(() => this.emitClose());
|
211
|
-
this._receiver = null;
|
212
|
-
} else {
|
213
|
-
this.emitClose();
|
214
|
-
}
|
215
|
-
}
|
216
|
-
|
217
|
-
/**
|
218
|
-
* Emit the `close` event.
|
219
|
-
*
|
220
|
-
* @private
|
221
|
-
*/
|
222
|
-
emitClose () {
|
223
|
-
this.readyState = WebSocket.CLOSED;
|
224
|
-
this.emit('close', this._closeCode || 1006, this._closeMessage || '');
|
225
|
-
|
226
|
-
if (this.extensions[PerMessageDeflate.extensionName]) {
|
227
|
-
this.extensions[PerMessageDeflate.extensionName].cleanup();
|
228
|
-
}
|
229
|
-
|
230
|
-
this.extensions = null;
|
231
|
-
|
232
|
-
this.removeAllListeners();
|
233
|
-
this.on('error', noop); // Catch all errors after this.
|
234
|
-
}
|
235
|
-
|
236
|
-
/**
|
237
|
-
* Pause the socket stream.
|
238
|
-
*
|
239
|
-
* @public
|
240
|
-
*/
|
241
|
-
pause () {
|
242
|
-
if (this.readyState !== WebSocket.OPEN) throw new Error('not opened');
|
243
|
-
|
244
|
-
this._socket.pause();
|
245
|
-
}
|
246
|
-
|
247
|
-
/**
|
248
|
-
* Resume the socket stream
|
249
|
-
*
|
250
|
-
* @public
|
251
|
-
*/
|
252
|
-
resume () {
|
253
|
-
if (this.readyState !== WebSocket.OPEN) throw new Error('not opened');
|
254
|
-
|
255
|
-
this._socket.resume();
|
256
|
-
}
|
257
|
-
|
258
|
-
/**
|
259
|
-
* Start a closing handshake.
|
260
|
-
*
|
261
|
-
* @param {Number} code Status code explaining why the connection is closing
|
262
|
-
* @param {String} data A string explaining why the connection is closing
|
263
|
-
* @public
|
264
|
-
*/
|
265
|
-
close (code, data) {
|
266
|
-
if (this.readyState === WebSocket.CLOSED) return;
|
267
|
-
if (this.readyState === WebSocket.CONNECTING) {
|
268
|
-
if (this._req && !this._req.aborted) {
|
269
|
-
this._req.abort();
|
270
|
-
this.emit('error', new Error('closed before the connection is established'));
|
271
|
-
this.finalize(true);
|
272
|
-
}
|
273
|
-
return;
|
274
|
-
}
|
275
|
-
|
276
|
-
if (this.readyState === WebSocket.CLOSING) {
|
277
|
-
if (this._closeCode) this.terminate();
|
278
|
-
return;
|
279
|
-
}
|
280
|
-
|
281
|
-
this.readyState = WebSocket.CLOSING;
|
282
|
-
this._sender.close(code, data, !this._isServer, (err) => {
|
283
|
-
if (err) this.emit('error', err);
|
284
|
-
|
285
|
-
if (this._closeCode) {
|
286
|
-
this.terminate();
|
287
|
-
} else {
|
288
|
-
//
|
289
|
-
// Ensure that the connection is cleaned up even when the closing
|
290
|
-
// handshake fails.
|
291
|
-
//
|
292
|
-
clearTimeout(this._closeTimer);
|
293
|
-
this._closeTimer = setTimeout(this._finalize, closeTimeout, true);
|
294
|
-
}
|
295
|
-
});
|
296
|
-
}
|
297
|
-
|
298
|
-
/**
|
299
|
-
* Send a ping message.
|
300
|
-
*
|
301
|
-
* @param {*} data The message to send
|
302
|
-
* @param {Boolean} mask Indicates whether or not to mask `data`
|
303
|
-
* @param {Boolean} failSilently Indicates whether or not to throw if `readyState` isn't `OPEN`
|
304
|
-
* @public
|
305
|
-
*/
|
306
|
-
ping (data, mask, failSilently) {
|
307
|
-
if (this.readyState !== WebSocket.OPEN) {
|
308
|
-
if (failSilently) return;
|
309
|
-
throw new Error('not opened');
|
310
|
-
}
|
311
|
-
|
312
|
-
if (typeof data === 'number') data = data.toString();
|
313
|
-
if (mask === undefined) mask = !this._isServer;
|
314
|
-
this._sender.ping(data, mask);
|
315
|
-
}
|
316
|
-
|
317
|
-
/**
|
318
|
-
* Send a pong message.
|
319
|
-
*
|
320
|
-
* @param {*} data The message to send
|
321
|
-
* @param {Boolean} mask Indicates whether or not to mask `data`
|
322
|
-
* @param {Boolean} failSilently Indicates whether or not to throw if `readyState` isn't `OPEN`
|
323
|
-
* @public
|
324
|
-
*/
|
325
|
-
pong (data, mask, failSilently) {
|
326
|
-
if (this.readyState !== WebSocket.OPEN) {
|
327
|
-
if (failSilently) return;
|
328
|
-
throw new Error('not opened');
|
329
|
-
}
|
330
|
-
|
331
|
-
if (typeof data === 'number') data = data.toString();
|
332
|
-
if (mask === undefined) mask = !this._isServer;
|
333
|
-
this._sender.pong(data, mask);
|
334
|
-
}
|
335
|
-
|
336
|
-
/**
|
337
|
-
* Send a data message.
|
338
|
-
*
|
339
|
-
* @param {*} data The message to send
|
340
|
-
* @param {Object} options Options object
|
341
|
-
* @param {Boolean} options.compress Specifies whether or not to compress `data`
|
342
|
-
* @param {Boolean} options.binary Specifies whether `data` is binary or text
|
343
|
-
* @param {Boolean} options.fin Specifies whether the fragment is the last one
|
344
|
-
* @param {Boolean} options.mask Specifies whether or not to mask `data`
|
345
|
-
* @param {Function} cb Callback which is executed when data is written out
|
346
|
-
* @public
|
347
|
-
*/
|
348
|
-
send (data, options, cb) {
|
349
|
-
if (typeof options === 'function') {
|
350
|
-
cb = options;
|
351
|
-
options = {};
|
352
|
-
}
|
353
|
-
|
354
|
-
if (this.readyState !== WebSocket.OPEN) {
|
355
|
-
if (cb) cb(new Error('not opened'));
|
356
|
-
else throw new Error('not opened');
|
357
|
-
return;
|
358
|
-
}
|
359
|
-
|
360
|
-
if (typeof data === 'number') data = data.toString();
|
361
|
-
else if (!data) data = '';
|
362
|
-
|
363
|
-
const opts = Object.assign({
|
364
|
-
fin: true,
|
365
|
-
binary: typeof data !== 'string',
|
366
|
-
mask: !this._isServer,
|
367
|
-
compress: true
|
368
|
-
}, options);
|
369
|
-
|
370
|
-
if (!this.extensions[PerMessageDeflate.extensionName]) {
|
371
|
-
opts.compress = false;
|
372
|
-
}
|
373
|
-
|
374
|
-
this._sender.send(data, opts, cb);
|
375
|
-
}
|
376
|
-
|
377
|
-
/**
|
378
|
-
* Half-close the socket sending a FIN packet.
|
379
|
-
*
|
380
|
-
* @public
|
381
|
-
*/
|
382
|
-
terminate () {
|
383
|
-
if (this.readyState === WebSocket.CLOSED) return;
|
384
|
-
if (this.readyState === WebSocket.CONNECTING) {
|
385
|
-
if (this._req && !this._req.aborted) {
|
386
|
-
this._req.abort();
|
387
|
-
this.emit('error', new Error('closed before the connection is established'));
|
388
|
-
this.finalize(true);
|
389
|
-
}
|
390
|
-
return;
|
391
|
-
}
|
392
|
-
|
393
|
-
if (this._socket) {
|
394
|
-
this.readyState = WebSocket.CLOSING;
|
395
|
-
this._socket.end();
|
396
|
-
|
397
|
-
//
|
398
|
-
// Add a timeout to ensure that the connection is completely cleaned up
|
399
|
-
// within 30 seconds, even if the other peer does not send a FIN packet.
|
400
|
-
//
|
401
|
-
clearTimeout(this._closeTimer);
|
402
|
-
this._closeTimer = setTimeout(this._finalize, closeTimeout, true);
|
403
|
-
}
|
404
|
-
}
|
405
|
-
}
|
406
|
-
|
407
|
-
WebSocket.CONNECTING = 0;
|
408
|
-
WebSocket.OPEN = 1;
|
409
|
-
WebSocket.CLOSING = 2;
|
410
|
-
WebSocket.CLOSED = 3;
|
411
|
-
|
412
|
-
//
|
413
|
-
// Add the `onopen`, `onerror`, `onclose`, and `onmessage` attributes.
|
414
|
-
// See https://html.spec.whatwg.org/multipage/comms.html#the-websocket-interface
|
415
|
-
//
|
416
|
-
['open', 'error', 'close', 'message'].forEach((method) => {
|
417
|
-
Object.defineProperty(WebSocket.prototype, `on${method}`, {
|
418
|
-
/**
|
419
|
-
* Return the listener of the event.
|
420
|
-
*
|
421
|
-
* @return {(Function|undefined)} The event listener or `undefined`
|
422
|
-
* @public
|
423
|
-
*/
|
424
|
-
get () {
|
425
|
-
const listeners = this.listeners(method);
|
426
|
-
for (var i = 0; i < listeners.length; i++) {
|
427
|
-
if (listeners[i]._listener) return listeners[i]._listener;
|
428
|
-
}
|
429
|
-
},
|
430
|
-
/**
|
431
|
-
* Add a listener for the event.
|
432
|
-
*
|
433
|
-
* @param {Function} listener The listener to add
|
434
|
-
* @public
|
435
|
-
*/
|
436
|
-
set (listener) {
|
437
|
-
const listeners = this.listeners(method);
|
438
|
-
for (var i = 0; i < listeners.length; i++) {
|
439
|
-
//
|
440
|
-
// Remove only the listeners added via `addEventListener`.
|
441
|
-
//
|
442
|
-
if (listeners[i]._listener) this.removeListener(method, listeners[i]);
|
443
|
-
}
|
444
|
-
this.addEventListener(method, listener);
|
445
|
-
}
|
446
|
-
});
|
447
|
-
});
|
448
|
-
|
449
|
-
WebSocket.prototype.addEventListener = EventTarget.addEventListener;
|
450
|
-
WebSocket.prototype.removeEventListener = EventTarget.removeEventListener;
|
451
|
-
|
452
|
-
module.exports = WebSocket;
|
453
|
-
|
454
|
-
/**
|
455
|
-
* Initialize a WebSocket server client.
|
456
|
-
*
|
457
|
-
* @param {http.IncomingMessage} req The request object
|
458
|
-
* @param {net.Socket} socket The network socket between the server and client
|
459
|
-
* @param {Buffer} head The first packet of the upgraded stream
|
460
|
-
* @param {Object} options WebSocket attributes
|
461
|
-
* @param {Number} options.protocolVersion The WebSocket protocol version
|
462
|
-
* @param {Object} options.extensions The negotiated extensions
|
463
|
-
* @param {Number} options.maxPayload The maximum allowed message size
|
464
|
-
* @param {String} options.protocol The chosen subprotocol
|
465
|
-
* @private
|
466
|
-
*/
|
467
|
-
function initAsServerClient (req, socket, head, options) {
|
468
|
-
this.protocolVersion = options.protocolVersion;
|
469
|
-
this.extensions = options.extensions;
|
470
|
-
this.maxPayload = options.maxPayload;
|
471
|
-
this.protocol = options.protocol;
|
472
|
-
|
473
|
-
this.upgradeReq = req;
|
474
|
-
this._isServer = true;
|
475
|
-
|
476
|
-
this.setSocket(socket, head);
|
477
|
-
}
|
478
|
-
|
479
|
-
/**
|
480
|
-
* Initialize a WebSocket client.
|
481
|
-
*
|
482
|
-
* @param {String} address The URL to which to connect
|
483
|
-
* @param {String[]} protocols The list of subprotocols
|
484
|
-
* @param {Object} options Connection options
|
485
|
-
* @param {String} options.protocol Value of the `Sec-WebSocket-Protocol` header
|
486
|
-
* @param {(Boolean|Object)} options.perMessageDeflate Enable/disable permessage-deflate
|
487
|
-
* @param {String} options.localAddress Local interface to bind for network connections
|
488
|
-
* @param {Number} options.protocolVersion Value of the `Sec-WebSocket-Version` header
|
489
|
-
* @param {Object} options.headers An object containing request headers
|
490
|
-
* @param {String} options.origin Value of the `Origin` or `Sec-WebSocket-Origin` header
|
491
|
-
* @param {http.Agent} options.agent Use the specified Agent
|
492
|
-
* @param {String} options.host Value of the `Host` header
|
493
|
-
* @param {Number} options.family IP address family to use during hostname lookup (4 or 6).
|
494
|
-
* @param {Function} options.checkServerIdentity A function to validate the server hostname
|
495
|
-
* @param {Boolean} options.rejectUnauthorized Verify or not the server certificate
|
496
|
-
* @param {String} options.passphrase The passphrase for the private key or pfx
|
497
|
-
* @param {String} options.ciphers The ciphers to use or exclude
|
498
|
-
* @param {(String|String[]|Buffer|Buffer[])} options.cert The certificate key
|
499
|
-
* @param {(String|String[]|Buffer|Buffer[])} options.key The private key
|
500
|
-
* @param {(String|Buffer)} options.pfx The private key, certificate, and CA certs
|
501
|
-
* @param {(String|String[]|Buffer|Buffer[])} options.ca Trusted certificates
|
502
|
-
* @private
|
503
|
-
*/
|
504
|
-
function initAsClient (address, protocols, options) {
|
505
|
-
options = Object.assign({
|
506
|
-
protocol: protocols.join(','),
|
507
|
-
perMessageDeflate: true,
|
508
|
-
localAddress: null,
|
509
|
-
protocolVersion,
|
510
|
-
headers: null,
|
511
|
-
origin: null,
|
512
|
-
agent: null,
|
513
|
-
host: null,
|
514
|
-
family: null,
|
515
|
-
|
516
|
-
//
|
517
|
-
// SSL options.
|
518
|
-
//
|
519
|
-
checkServerIdentity: null,
|
520
|
-
rejectUnauthorized: null,
|
521
|
-
passphrase: null,
|
522
|
-
ciphers: null,
|
523
|
-
cert: null,
|
524
|
-
key: null,
|
525
|
-
pfx: null,
|
526
|
-
ca: null
|
527
|
-
}, options);
|
528
|
-
|
529
|
-
if (options.protocolVersion !== 8 && options.protocolVersion !== 13) {
|
530
|
-
throw new Error('unsupported protocol version');
|
531
|
-
}
|
532
|
-
|
533
|
-
this.protocolVersion = options.protocolVersion;
|
534
|
-
this._isServer = false;
|
535
|
-
this.url = address;
|
536
|
-
|
537
|
-
const serverUrl = url.parse(address);
|
538
|
-
const isUnixSocket = serverUrl.protocol === 'ws+unix:';
|
539
|
-
|
540
|
-
if (!serverUrl.host && !isUnixSocket) throw new Error('invalid url');
|
541
|
-
|
542
|
-
const isSecure = serverUrl.protocol === 'wss:' || serverUrl.protocol === 'https:';
|
543
|
-
const key = crypto.randomBytes(16).toString('base64');
|
544
|
-
const port = serverUrl.port || (isSecure ? 443 : 80);
|
545
|
-
const httpObj = isSecure ? https : http;
|
546
|
-
|
547
|
-
//
|
548
|
-
// Prepare extensions.
|
549
|
-
//
|
550
|
-
const extensionsOffer = {};
|
551
|
-
var perMessageDeflate;
|
552
|
-
|
553
|
-
if (options.perMessageDeflate) {
|
554
|
-
perMessageDeflate = new PerMessageDeflate(
|
555
|
-
options.perMessageDeflate !== true ? options.perMessageDeflate : {},
|
556
|
-
false
|
557
|
-
);
|
558
|
-
extensionsOffer[PerMessageDeflate.extensionName] = perMessageDeflate.offer();
|
559
|
-
}
|
560
|
-
|
561
|
-
const requestOptions = {
|
562
|
-
host: serverUrl.hostname,
|
563
|
-
port,
|
564
|
-
path: '/',
|
565
|
-
headers: {
|
566
|
-
'Sec-WebSocket-Version': options.protocolVersion,
|
567
|
-
'Sec-WebSocket-Key': key,
|
568
|
-
'Connection': 'Upgrade',
|
569
|
-
'Upgrade': 'websocket'
|
570
|
-
}
|
571
|
-
};
|
572
|
-
|
573
|
-
if (options.headers) Object.assign(requestOptions.headers, options.headers);
|
574
|
-
if (Object.keys(extensionsOffer).length) {
|
575
|
-
requestOptions.headers['Sec-WebSocket-Extensions'] = Extensions.format(extensionsOffer);
|
576
|
-
}
|
577
|
-
if (options.protocol) {
|
578
|
-
requestOptions.headers['Sec-WebSocket-Protocol'] = options.protocol;
|
579
|
-
}
|
580
|
-
if (options.origin) {
|
581
|
-
if (options.protocolVersion < 13) {
|
582
|
-
requestOptions.headers['Sec-WebSocket-Origin'] = options.origin;
|
583
|
-
} else {
|
584
|
-
requestOptions.headers.Origin = options.origin;
|
585
|
-
}
|
586
|
-
}
|
587
|
-
if (options.host) requestOptions.headers.Host = options.host;
|
588
|
-
if (options.family) requestOptions.family = options.family;
|
589
|
-
|
590
|
-
if (options.localAddress) requestOptions.localAddress = options.localAddress;
|
591
|
-
if (isUnixSocket) requestOptions.socketPath = serverUrl.pathname;
|
592
|
-
if (serverUrl.auth) requestOptions.auth = serverUrl.auth;
|
593
|
-
|
594
|
-
//
|
595
|
-
// Make sure that path starts with `/`.
|
596
|
-
//
|
597
|
-
if (serverUrl.path) {
|
598
|
-
if (serverUrl.path.charAt(0) !== '/') {
|
599
|
-
requestOptions.path = `/${serverUrl.path}`;
|
600
|
-
} else {
|
601
|
-
requestOptions.path = serverUrl.path;
|
602
|
-
}
|
603
|
-
}
|
604
|
-
|
605
|
-
var agent = options.agent;
|
606
|
-
|
607
|
-
//
|
608
|
-
// A custom agent is required for these options.
|
609
|
-
//
|
610
|
-
if (
|
611
|
-
options.rejectUnauthorized != null ||
|
612
|
-
options.checkServerIdentity ||
|
613
|
-
options.passphrase ||
|
614
|
-
options.ciphers ||
|
615
|
-
options.cert ||
|
616
|
-
options.key ||
|
617
|
-
options.pfx ||
|
618
|
-
options.ca
|
619
|
-
) {
|
620
|
-
if (options.passphrase) requestOptions.passphrase = options.passphrase;
|
621
|
-
if (options.ciphers) requestOptions.ciphers = options.ciphers;
|
622
|
-
if (options.cert) requestOptions.cert = options.cert;
|
623
|
-
if (options.key) requestOptions.key = options.key;
|
624
|
-
if (options.pfx) requestOptions.pfx = options.pfx;
|
625
|
-
if (options.ca) requestOptions.ca = options.ca;
|
626
|
-
if (options.checkServerIdentity) {
|
627
|
-
requestOptions.checkServerIdentity = options.checkServerIdentity;
|
628
|
-
}
|
629
|
-
if (options.rejectUnauthorized != null) {
|
630
|
-
requestOptions.rejectUnauthorized = options.rejectUnauthorized;
|
631
|
-
}
|
632
|
-
|
633
|
-
if (!agent) agent = new httpObj.Agent(requestOptions);
|
634
|
-
}
|
635
|
-
|
636
|
-
if (agent) requestOptions.agent = agent;
|
637
|
-
|
638
|
-
this._req = httpObj.get(requestOptions);
|
639
|
-
|
640
|
-
this._req.on('error', (error) => {
|
641
|
-
if (this._req.aborted) return;
|
642
|
-
|
643
|
-
this._req = null;
|
644
|
-
this.emit('error', error);
|
645
|
-
this.finalize(true);
|
646
|
-
});
|
647
|
-
|
648
|
-
this._req.on('response', (res) => {
|
649
|
-
if (!this.emit('unexpected-response', this._req, res)) {
|
650
|
-
this._req.abort();
|
651
|
-
this.emit('error', new Error(`unexpected server response (${res.statusCode})`));
|
652
|
-
this.finalize(true);
|
653
|
-
}
|
654
|
-
});
|
655
|
-
|
656
|
-
this._req.on('upgrade', (res, socket, head) => {
|
657
|
-
this._req = null;
|
658
|
-
|
659
|
-
const digest = crypto.createHash('sha1')
|
660
|
-
.update(key + GUID, 'binary')
|
661
|
-
.digest('base64');
|
662
|
-
|
663
|
-
if (res.headers['sec-websocket-accept'] !== digest) {
|
664
|
-
socket.destroy();
|
665
|
-
this.emit('error', new Error('invalid server key'));
|
666
|
-
return this.finalize(true);
|
667
|
-
}
|
668
|
-
|
669
|
-
const serverProt = res.headers['sec-websocket-protocol'];
|
670
|
-
const protList = (options.protocol || '').split(/, */);
|
671
|
-
var protError;
|
672
|
-
|
673
|
-
if (!options.protocol && serverProt) {
|
674
|
-
protError = 'server sent a subprotocol even though none requested';
|
675
|
-
} else if (options.protocol && !serverProt) {
|
676
|
-
protError = 'server sent no subprotocol even though requested';
|
677
|
-
} else if (serverProt && protList.indexOf(serverProt) === -1) {
|
678
|
-
protError = 'server responded with an invalid protocol';
|
679
|
-
}
|
680
|
-
|
681
|
-
if (protError) {
|
682
|
-
socket.destroy();
|
683
|
-
this.emit('error', new Error(protError));
|
684
|
-
return this.finalize(true);
|
685
|
-
}
|
686
|
-
|
687
|
-
if (serverProt) this.protocol = serverProt;
|
688
|
-
|
689
|
-
const serverExtensions = Extensions.parse(res.headers['sec-websocket-extensions']);
|
690
|
-
|
691
|
-
if (perMessageDeflate && serverExtensions[PerMessageDeflate.extensionName]) {
|
692
|
-
try {
|
693
|
-
perMessageDeflate.accept(serverExtensions[PerMessageDeflate.extensionName]);
|
694
|
-
} catch (err) {
|
695
|
-
socket.destroy();
|
696
|
-
this.emit('error', new Error('invalid extension parameter'));
|
697
|
-
return this.finalize(true);
|
698
|
-
}
|
699
|
-
|
700
|
-
this.extensions[PerMessageDeflate.extensionName] = perMessageDeflate;
|
701
|
-
}
|
702
|
-
|
703
|
-
this.setSocket(socket, head);
|
704
|
-
});
|
705
|
-
}
|