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.
Files changed (212) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/build.yml +17 -0
  3. data/CHANGELOG.md +35 -1
  4. data/Gemfile +1 -0
  5. data/HACKING.md +47 -26
  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 +89 -50
  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
- }