opal 1.6.1 → 1.7.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (212) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/build.yml +17 -0
  3. data/Gemfile +1 -0
  4. data/HACKING.md +47 -26
  5. data/UNRELEASED.md +28 -0
  6. data/benchmark/benchmarks +415 -103
  7. data/benchmark/bm_call_overhead.yml +28 -0
  8. data/benchmark/run.rb +61 -40
  9. data/docs/cdp_common.json +3364 -0
  10. data/docs/cdp_common.md +18 -0
  11. data/docs/{headless_chrome.md → headless_browsers.md} +31 -12
  12. data/lib/opal/ast/builder.rb +1 -1
  13. data/lib/opal/builder.rb +6 -1
  14. data/lib/opal/builder_processors.rb +5 -3
  15. data/lib/opal/cache.rb +1 -7
  16. data/lib/opal/cli_options.rb +72 -58
  17. data/lib/opal/cli_runners/chrome.rb +47 -9
  18. data/lib/opal/cli_runners/chrome_cdp_interface.rb +238 -112
  19. data/lib/opal/cli_runners/compiler.rb +146 -13
  20. data/lib/opal/cli_runners/deno.rb +32 -0
  21. data/lib/opal/cli_runners/firefox.rb +350 -0
  22. data/lib/opal/cli_runners/firefox_cdp_interface.rb +212 -0
  23. data/lib/opal/cli_runners/node_modules/.bin/chrome-remote-interface.cmd +17 -0
  24. data/lib/opal/cli_runners/node_modules/.bin/chrome-remote-interface.ps1 +28 -0
  25. data/lib/opal/cli_runners/node_modules/.package-lock.json +41 -0
  26. data/lib/opal/cli_runners/node_modules/chrome-remote-interface/LICENSE +1 -1
  27. data/lib/opal/cli_runners/node_modules/chrome-remote-interface/README.md +322 -182
  28. data/lib/opal/cli_runners/node_modules/chrome-remote-interface/bin/client.js +99 -114
  29. data/lib/opal/cli_runners/node_modules/chrome-remote-interface/chrome-remote-interface.js +1 -11
  30. data/lib/opal/cli_runners/node_modules/chrome-remote-interface/index.js +16 -11
  31. data/lib/opal/cli_runners/node_modules/chrome-remote-interface/lib/api.js +41 -33
  32. data/lib/opal/cli_runners/node_modules/chrome-remote-interface/lib/chrome.js +224 -214
  33. data/lib/opal/cli_runners/node_modules/chrome-remote-interface/lib/devtools.js +71 -191
  34. data/lib/opal/cli_runners/node_modules/chrome-remote-interface/lib/external-request.js +26 -6
  35. data/lib/opal/cli_runners/node_modules/chrome-remote-interface/lib/protocol.json +20788 -9049
  36. data/lib/opal/cli_runners/node_modules/chrome-remote-interface/lib/websocket-wrapper.js +10 -3
  37. data/lib/opal/cli_runners/node_modules/chrome-remote-interface/package.json +59 -123
  38. data/lib/opal/cli_runners/node_modules/chrome-remote-interface/webpack.config.js +25 -32
  39. data/lib/opal/cli_runners/node_modules/commander/History.md +298 -0
  40. data/lib/opal/cli_runners/node_modules/commander/LICENSE +22 -0
  41. data/lib/opal/cli_runners/node_modules/commander/Readme.md +217 -61
  42. data/lib/opal/cli_runners/node_modules/commander/index.js +431 -145
  43. data/lib/opal/cli_runners/node_modules/commander/package.json +16 -79
  44. data/lib/opal/cli_runners/node_modules/ws/README.md +334 -98
  45. data/lib/opal/cli_runners/node_modules/ws/browser.js +8 -0
  46. data/lib/opal/cli_runners/node_modules/ws/index.js +5 -10
  47. data/lib/opal/cli_runners/node_modules/ws/lib/buffer-util.js +129 -0
  48. data/lib/opal/cli_runners/node_modules/ws/lib/constants.js +10 -0
  49. data/lib/opal/cli_runners/node_modules/ws/lib/event-target.js +184 -0
  50. data/lib/opal/cli_runners/node_modules/ws/lib/extension.js +223 -0
  51. data/lib/opal/cli_runners/node_modules/ws/lib/limiter.js +55 -0
  52. data/lib/opal/cli_runners/node_modules/ws/lib/permessage-deflate.js +518 -0
  53. data/lib/opal/cli_runners/node_modules/ws/lib/receiver.js +607 -0
  54. data/lib/opal/cli_runners/node_modules/ws/lib/sender.js +409 -0
  55. data/lib/opal/cli_runners/node_modules/ws/lib/stream.js +180 -0
  56. data/lib/opal/cli_runners/node_modules/ws/lib/validation.js +104 -0
  57. data/lib/opal/cli_runners/node_modules/ws/lib/websocket-server.js +447 -0
  58. data/lib/opal/cli_runners/node_modules/ws/lib/websocket.js +1195 -0
  59. data/lib/opal/cli_runners/node_modules/ws/package.json +40 -106
  60. data/lib/opal/cli_runners/package-lock.json +62 -0
  61. data/lib/opal/cli_runners/package.json +1 -1
  62. data/lib/opal/cli_runners.rb +26 -4
  63. data/lib/opal/nodes/args/prepare_post_args.rb +2 -2
  64. data/lib/opal/nodes/def.rb +8 -8
  65. data/lib/opal/nodes/iter.rb +12 -12
  66. data/lib/opal/nodes/logic.rb +1 -1
  67. data/lib/opal/nodes/masgn.rb +2 -2
  68. data/lib/opal/parser/with_ruby_lexer.rb +1 -1
  69. data/lib/opal/paths.rb +14 -0
  70. data/lib/opal/rewriter.rb +2 -0
  71. data/lib/opal/rewriters/forward_args.rb +52 -4
  72. data/lib/opal/rewriters/targeted_patches.rb +94 -0
  73. data/lib/opal/version.rb +1 -1
  74. data/opal/corelib/basic_object.rb +1 -1
  75. data/opal/corelib/boolean.rb +2 -2
  76. data/opal/corelib/class.rb +11 -0
  77. data/opal/corelib/constants.rb +3 -3
  78. data/opal/corelib/enumerable.rb +4 -0
  79. data/opal/corelib/enumerator.rb +1 -1
  80. data/opal/corelib/hash.rb +2 -2
  81. data/opal/corelib/helpers.rb +1 -1
  82. data/opal/corelib/kernel.rb +3 -3
  83. data/opal/corelib/method.rb +1 -1
  84. data/opal/corelib/module.rb +29 -8
  85. data/opal/corelib/proc.rb +7 -5
  86. data/opal/corelib/runtime.js +141 -78
  87. data/opal/corelib/set.rb +252 -0
  88. data/opal/corelib/string.rb +2 -1
  89. data/opal/corelib/time.rb +2 -2
  90. data/opal/opal.rb +1 -0
  91. data/opal.gemspec +1 -0
  92. data/spec/filters/bugs/array.rb +22 -13
  93. data/spec/filters/bugs/base64.rb +5 -5
  94. data/spec/filters/bugs/basicobject.rb +16 -8
  95. data/spec/filters/bugs/bigdecimal.rb +161 -160
  96. data/spec/filters/bugs/binding.rb +10 -10
  97. data/spec/filters/bugs/class.rb +8 -8
  98. data/spec/filters/bugs/complex.rb +2 -1
  99. data/spec/filters/bugs/date.rb +79 -81
  100. data/spec/filters/bugs/datetime.rb +29 -29
  101. data/spec/filters/bugs/delegate.rb +1 -3
  102. data/spec/filters/bugs/encoding.rb +69 -69
  103. data/spec/filters/bugs/enumerable.rb +22 -20
  104. data/spec/filters/bugs/enumerator.rb +88 -85
  105. data/spec/filters/bugs/exception.rb +46 -40
  106. data/spec/filters/bugs/file.rb +32 -32
  107. data/spec/filters/bugs/float.rb +26 -21
  108. data/spec/filters/bugs/freeze.rb +88 -0
  109. data/spec/filters/bugs/hash.rb +39 -38
  110. data/spec/filters/bugs/integer.rb +57 -44
  111. data/spec/filters/bugs/io.rb +1 -1
  112. data/spec/filters/bugs/kernel.rb +349 -269
  113. data/spec/filters/bugs/language.rb +220 -188
  114. data/spec/filters/bugs/main.rb +5 -3
  115. data/spec/filters/bugs/marshal.rb +38 -38
  116. data/spec/filters/bugs/math.rb +2 -1
  117. data/spec/filters/bugs/method.rb +73 -62
  118. data/spec/filters/bugs/module.rb +163 -143
  119. data/spec/filters/bugs/numeric.rb +6 -6
  120. data/spec/filters/bugs/objectspace.rb +16 -16
  121. data/spec/filters/bugs/openstruct.rb +1 -1
  122. data/spec/filters/bugs/pack_unpack.rb +51 -51
  123. data/spec/filters/bugs/pathname.rb +7 -7
  124. data/spec/filters/bugs/proc.rb +63 -63
  125. data/spec/filters/bugs/random.rb +7 -6
  126. data/spec/filters/bugs/range.rb +12 -9
  127. data/spec/filters/bugs/rational.rb +8 -7
  128. data/spec/filters/bugs/regexp.rb +49 -48
  129. data/spec/filters/bugs/ruby-32.rb +56 -0
  130. data/spec/filters/bugs/set.rb +30 -30
  131. data/spec/filters/bugs/singleton.rb +4 -4
  132. data/spec/filters/bugs/string.rb +187 -99
  133. data/spec/filters/bugs/stringio.rb +7 -0
  134. data/spec/filters/bugs/stringscanner.rb +68 -68
  135. data/spec/filters/bugs/struct.rb +11 -9
  136. data/spec/filters/bugs/symbol.rb +1 -1
  137. data/spec/filters/bugs/time.rb +78 -63
  138. data/spec/filters/bugs/trace_point.rb +4 -4
  139. data/spec/filters/bugs/unboundmethod.rb +32 -17
  140. data/spec/filters/bugs/warnings.rb +8 -12
  141. data/spec/filters/unsupported/array.rb +24 -107
  142. data/spec/filters/unsupported/basicobject.rb +12 -12
  143. data/spec/filters/unsupported/bignum.rb +27 -52
  144. data/spec/filters/unsupported/class.rb +1 -2
  145. data/spec/filters/unsupported/delegator.rb +3 -3
  146. data/spec/filters/unsupported/enumerable.rb +2 -9
  147. data/spec/filters/unsupported/enumerator.rb +2 -11
  148. data/spec/filters/unsupported/file.rb +1 -1
  149. data/spec/filters/unsupported/float.rb +28 -47
  150. data/spec/filters/unsupported/hash.rb +8 -14
  151. data/spec/filters/unsupported/integer.rb +75 -91
  152. data/spec/filters/unsupported/kernel.rb +17 -35
  153. data/spec/filters/unsupported/language.rb +11 -19
  154. data/spec/filters/unsupported/marshal.rb +22 -41
  155. data/spec/filters/unsupported/matchdata.rb +28 -52
  156. data/spec/filters/unsupported/math.rb +1 -1
  157. data/spec/filters/unsupported/privacy.rb +229 -285
  158. data/spec/filters/unsupported/range.rb +1 -5
  159. data/spec/filters/unsupported/regexp.rb +40 -66
  160. data/spec/filters/unsupported/set.rb +2 -2
  161. data/spec/filters/unsupported/singleton.rb +4 -4
  162. data/spec/filters/unsupported/string.rb +305 -508
  163. data/spec/filters/unsupported/struct.rb +3 -4
  164. data/spec/filters/unsupported/symbol.rb +15 -18
  165. data/spec/filters/unsupported/thread.rb +1 -7
  166. data/spec/filters/unsupported/time.rb +159 -202
  167. data/spec/filters/unsupported/usage_of_files.rb +170 -259
  168. data/spec/lib/builder_spec.rb +4 -4
  169. data/spec/lib/rewriters/forward_args_spec.rb +32 -12
  170. data/spec/mspec-opal/runner.rb +2 -0
  171. data/spec/ruby_specs +4 -0
  172. data/stdlib/deno/base.rb +28 -0
  173. data/stdlib/deno/file.rb +340 -0
  174. data/stdlib/{headless_chrome.rb → headless_browser/base.rb} +1 -1
  175. data/stdlib/headless_browser/file.rb +15 -0
  176. data/stdlib/headless_browser.rb +4 -0
  177. data/stdlib/native.rb +1 -1
  178. data/stdlib/nodejs/file.rb +5 -0
  179. data/stdlib/opal/platform.rb +8 -6
  180. data/stdlib/opal-platform.rb +14 -8
  181. data/stdlib/set.rb +1 -258
  182. data/tasks/benchmarking.rake +62 -19
  183. data/tasks/performance.rake +1 -1
  184. data/tasks/testing.rake +5 -3
  185. data/test/nodejs/test_file.rb +29 -10
  186. data/test/opal/http_server.rb +28 -11
  187. data/test/opal/unsupported_and_bugs.rb +2 -1
  188. metadata +91 -52
  189. data/lib/opal/cli_runners/node_modules/ultron/LICENSE +0 -22
  190. data/lib/opal/cli_runners/node_modules/ultron/index.js +0 -138
  191. data/lib/opal/cli_runners/node_modules/ultron/package.json +0 -112
  192. data/lib/opal/cli_runners/node_modules/ws/SECURITY.md +0 -33
  193. data/lib/opal/cli_runners/node_modules/ws/lib/BufferUtil.fallback.js +0 -56
  194. data/lib/opal/cli_runners/node_modules/ws/lib/BufferUtil.js +0 -15
  195. data/lib/opal/cli_runners/node_modules/ws/lib/ErrorCodes.js +0 -28
  196. data/lib/opal/cli_runners/node_modules/ws/lib/EventTarget.js +0 -158
  197. data/lib/opal/cli_runners/node_modules/ws/lib/Extensions.js +0 -69
  198. data/lib/opal/cli_runners/node_modules/ws/lib/PerMessageDeflate.js +0 -339
  199. data/lib/opal/cli_runners/node_modules/ws/lib/Receiver.js +0 -520
  200. data/lib/opal/cli_runners/node_modules/ws/lib/Sender.js +0 -438
  201. data/lib/opal/cli_runners/node_modules/ws/lib/Validation.fallback.js +0 -9
  202. data/lib/opal/cli_runners/node_modules/ws/lib/Validation.js +0 -17
  203. data/lib/opal/cli_runners/node_modules/ws/lib/WebSocket.js +0 -705
  204. data/lib/opal/cli_runners/node_modules/ws/lib/WebSocketServer.js +0 -336
  205. data/spec/filters/bugs/boolean.rb +0 -3
  206. data/spec/filters/bugs/matrix.rb +0 -3
  207. data/spec/filters/unsupported/fixnum.rb +0 -15
  208. data/spec/filters/unsupported/freeze.rb +0 -102
  209. data/spec/filters/unsupported/pathname.rb +0 -4
  210. data/spec/filters/unsupported/proc.rb +0 -4
  211. data/spec/filters/unsupported/random.rb +0 -5
  212. 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
- }