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,520 +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 PerMessageDeflate = require('./PerMessageDeflate');
10
- const isValidUTF8 = require('./Validation');
11
- const bufferUtil = require('./BufferUtil');
12
- const ErrorCodes = require('./ErrorCodes');
13
-
14
- const EMPTY_BUFFER = Buffer.alloc(0);
15
-
16
- const GET_INFO = 0;
17
- const GET_PAYLOAD_LENGTH_16 = 1;
18
- const GET_PAYLOAD_LENGTH_64 = 2;
19
- const HAVE_LENGTH = 3;
20
- const GET_MASK = 4;
21
- const GET_DATA = 5;
22
- const HANDLE_DATA = 6;
23
- const INFLATING = 7;
24
-
25
- /**
26
- * HyBi Receiver implementation.
27
- */
28
- class Receiver {
29
- /**
30
- * Creates a Receiver instance.
31
- *
32
- * @param {Object} extensions An object containing the negotiated extensions
33
- * @param {Number} maxPayload The maximum allowed message length
34
- */
35
- constructor (extensions, maxPayload) {
36
- this.extensions = extensions || {};
37
- this.maxPayload = maxPayload | 0;
38
-
39
- this.bufferedBytes = 0;
40
- this.buffers = [];
41
-
42
- this.compressed = false;
43
- this.payloadLength = 0;
44
- this.fragmented = 0;
45
- this.masked = false;
46
- this.fin = false;
47
- this.mask = null;
48
- this.opcode = 0;
49
-
50
- this.totalPayloadLength = 0;
51
- this.messageLength = 0;
52
- this.fragments = [];
53
-
54
- this.cleanupCallback = null;
55
- this.hadError = false;
56
- this.dead = false;
57
-
58
- this.onmessage = null;
59
- this.onclose = null;
60
- this.onerror = null;
61
- this.onping = null;
62
- this.onpong = null;
63
-
64
- this.state = GET_INFO;
65
- }
66
-
67
- /**
68
- * Consumes bytes from the available buffered data.
69
- *
70
- * @param {Number} bytes The number of bytes to consume
71
- * @return {Buffer} Consumed bytes
72
- * @private
73
- */
74
- readBuffer (bytes) {
75
- var offset = 0;
76
- var dst;
77
- var l;
78
-
79
- this.bufferedBytes -= bytes;
80
-
81
- if (bytes === this.buffers[0].length) return this.buffers.shift();
82
-
83
- if (bytes < this.buffers[0].length) {
84
- dst = this.buffers[0].slice(0, bytes);
85
- this.buffers[0] = this.buffers[0].slice(bytes);
86
- return dst;
87
- }
88
-
89
- dst = new Buffer(bytes);
90
-
91
- while (bytes > 0) {
92
- l = this.buffers[0].length;
93
-
94
- if (bytes >= l) {
95
- this.buffers[0].copy(dst, offset);
96
- offset += l;
97
- this.buffers.shift();
98
- } else {
99
- this.buffers[0].copy(dst, offset, 0, bytes);
100
- this.buffers[0] = this.buffers[0].slice(bytes);
101
- }
102
-
103
- bytes -= l;
104
- }
105
-
106
- return dst;
107
- }
108
-
109
- /**
110
- * Checks if the number of buffered bytes is bigger or equal than `n` and
111
- * calls `cleanup` if necessary.
112
- *
113
- * @param {Number} n The number of bytes to check against
114
- * @return {Boolean} `true` if `bufferedBytes >= n`, else `false`
115
- * @private
116
- */
117
- hasBufferedBytes (n) {
118
- if (this.bufferedBytes >= n) return true;
119
-
120
- if (this.dead) this.cleanup(this.cleanupCallback);
121
- return false;
122
- }
123
-
124
- /**
125
- * Adds new data to the parser.
126
- *
127
- * @public
128
- */
129
- add (data) {
130
- if (this.dead) return;
131
-
132
- this.bufferedBytes += data.length;
133
- this.buffers.push(data);
134
- this.startLoop();
135
- }
136
-
137
- /**
138
- * Starts the parsing loop.
139
- *
140
- * @private
141
- */
142
- startLoop () {
143
- while (true) {
144
- if (this.state === GET_INFO) {
145
- if (!this.getInfo()) break;
146
- } else if (this.state === GET_PAYLOAD_LENGTH_16) {
147
- if (!this.getPayloadLength16()) break;
148
- } else if (this.state === GET_PAYLOAD_LENGTH_64) {
149
- if (!this.getPayloadLength64()) break;
150
- } else if (this.state === HAVE_LENGTH) {
151
- if (!this.haveLength()) break;
152
- } else if (this.state === GET_MASK) {
153
- if (!this.getMask()) break;
154
- } else if (this.state === GET_DATA) {
155
- if (!this.getData()) break;
156
- } else { // `HANDLE_DATA` or `INFLATING`
157
- break;
158
- }
159
- }
160
- }
161
-
162
- /**
163
- * Reads the first two bytes of a frame.
164
- *
165
- * @return {Boolean} `true` if the operation is successful, else `false`
166
- * @private
167
- */
168
- getInfo () {
169
- if (!this.hasBufferedBytes(2)) return false;
170
-
171
- const buf = this.readBuffer(2);
172
-
173
- if ((buf[0] & 0x30) !== 0x00) {
174
- this.error(new Error('RSV2 and RSV3 must be clear'), 1002);
175
- return false;
176
- }
177
-
178
- const compressed = (buf[0] & 0x40) === 0x40;
179
-
180
- if (compressed && !this.extensions[PerMessageDeflate.extensionName]) {
181
- this.error(new Error('RSV1 must be clear'), 1002);
182
- return false;
183
- }
184
-
185
- this.fin = (buf[0] & 0x80) === 0x80;
186
- this.opcode = buf[0] & 0x0f;
187
- this.payloadLength = buf[1] & 0x7f;
188
-
189
- if (this.opcode === 0x00) {
190
- if (compressed) {
191
- this.error(new Error('RSV1 must be clear'), 1002);
192
- return false;
193
- }
194
-
195
- if (!this.fragmented) {
196
- this.error(new Error(`invalid opcode: ${this.opcode}`), 1002);
197
- return false;
198
- } else {
199
- this.opcode = this.fragmented;
200
- }
201
- } else if (this.opcode === 0x01 || this.opcode === 0x02) {
202
- if (this.fragmented) {
203
- this.error(new Error(`invalid opcode: ${this.opcode}`), 1002);
204
- return false;
205
- }
206
-
207
- this.compressed = compressed;
208
- } else if (this.opcode > 0x07 && this.opcode < 0x0b) {
209
- if (!this.fin) {
210
- this.error(new Error('FIN must be set'), 1002);
211
- return false;
212
- }
213
-
214
- if (compressed) {
215
- this.error(new Error('RSV1 must be clear'), 1002);
216
- return false;
217
- }
218
-
219
- if (this.payloadLength > 0x7d) {
220
- this.error(new Error('invalid payload length'), 1002);
221
- return false;
222
- }
223
- } else {
224
- this.error(new Error(`invalid opcode: ${this.opcode}`), 1002);
225
- return false;
226
- }
227
-
228
- if (!this.fin && !this.fragmented) this.fragmented = this.opcode;
229
-
230
- this.masked = (buf[1] & 0x80) === 0x80;
231
-
232
- if (this.payloadLength === 126) this.state = GET_PAYLOAD_LENGTH_16;
233
- else if (this.payloadLength === 127) this.state = GET_PAYLOAD_LENGTH_64;
234
- else this.state = HAVE_LENGTH;
235
-
236
- return true;
237
- }
238
-
239
- /**
240
- * Gets extended payload length (7+16).
241
- *
242
- * @return {Boolean} `true` if payload length has been read, else `false`
243
- * @private
244
- */
245
- getPayloadLength16 () {
246
- if (!this.hasBufferedBytes(2)) return false;
247
-
248
- this.payloadLength = this.readBuffer(2).readUInt16BE(0, true);
249
- this.state = HAVE_LENGTH;
250
- return true;
251
- }
252
-
253
- /**
254
- * Gets extended payload length (7+64).
255
- *
256
- * @return {Boolean} `true` if payload length has been read, else `false`
257
- * @private
258
- */
259
- getPayloadLength64 () {
260
- if (!this.hasBufferedBytes(8)) return false;
261
-
262
- const buf = this.readBuffer(8);
263
- const num = buf.readUInt32BE(0, true);
264
-
265
- //
266
- // The maximum safe integer in JavaScript is 2^53 - 1. An error is returned
267
- // if payload length is greater than this number.
268
- //
269
- if (num > Math.pow(2, 53 - 32) - 1) {
270
- this.error(new Error('max payload size exceeded'), 1009);
271
- return false;
272
- }
273
-
274
- this.payloadLength = num * Math.pow(2, 32) + buf.readUInt32BE(4, true);
275
- this.state = HAVE_LENGTH;
276
- return true;
277
- }
278
-
279
- /**
280
- * Payload length has been read.
281
- *
282
- * @return {Boolean} `false` if payload length exceeds `maxPayload`, else `true`
283
- * @private
284
- */
285
- haveLength () {
286
- if (this.opcode < 0x08 && this.maxPayloadExceeded(this.payloadLength)) {
287
- return false;
288
- }
289
-
290
- if (this.masked) this.state = GET_MASK;
291
- else this.state = GET_DATA;
292
- return true;
293
- }
294
-
295
- /**
296
- * Reads mask bytes.
297
- *
298
- * @return {Boolean} `true` if the mask has been read, else `false`
299
- * @private
300
- */
301
- getMask () {
302
- if (!this.hasBufferedBytes(4)) return false;
303
-
304
- this.mask = this.readBuffer(4);
305
- this.state = GET_DATA;
306
- return true;
307
- }
308
-
309
- /**
310
- * Reads data bytes.
311
- *
312
- * @return {Boolean} `true` if the data bytes have been read, else `false`
313
- * @private
314
- */
315
- getData () {
316
- var data = EMPTY_BUFFER;
317
-
318
- if (this.payloadLength) {
319
- if (!this.hasBufferedBytes(this.payloadLength)) return false;
320
-
321
- data = this.readBuffer(this.payloadLength);
322
- if (this.masked) bufferUtil.unmask(data, this.mask);
323
- }
324
-
325
- this.state = HANDLE_DATA;
326
-
327
- if (this.opcode > 0x07) {
328
- this.controlMessage(data);
329
- } else if (this.compressed) {
330
- this.state = INFLATING;
331
- this.decompress(data);
332
- } else if (this.pushFragment(data)) {
333
- this.dataMessage();
334
- }
335
-
336
- return true;
337
- }
338
-
339
- /**
340
- * Decompresses data.
341
- *
342
- * @param {Buffer} data Compressed data
343
- * @private
344
- */
345
- decompress (data) {
346
- const extension = this.extensions[PerMessageDeflate.extensionName];
347
-
348
- extension.decompress(data, this.fin, (err, buf) => {
349
- if (err) {
350
- this.error(err, err.closeCode === 1009 ? 1009 : 1007);
351
- return;
352
- }
353
-
354
- if (this.pushFragment(buf)) this.dataMessage();
355
- if (this.state === GET_INFO) this.startLoop();
356
- });
357
- }
358
-
359
- /**
360
- * Handles a data message.
361
- *
362
- * @private
363
- */
364
- dataMessage () {
365
- if (this.fin) {
366
- const buf = this.fragments.length > 1
367
- ? Buffer.concat(this.fragments, this.messageLength)
368
- : this.fragments.length === 1
369
- ? this.fragments[0]
370
- : EMPTY_BUFFER;
371
-
372
- this.totalPayloadLength = 0;
373
- this.fragments.length = 0;
374
- this.messageLength = 0;
375
- this.fragmented = 0;
376
-
377
- if (this.opcode === 2) {
378
- this.onmessage(buf, { masked: this.masked, binary: true });
379
- } else {
380
- if (!isValidUTF8(buf)) {
381
- this.error(new Error('invalid utf8 sequence'), 1007);
382
- return;
383
- }
384
-
385
- this.onmessage(buf.toString(), { masked: this.masked });
386
- }
387
- }
388
-
389
- this.state = GET_INFO;
390
- }
391
-
392
- /**
393
- * Handles a control message.
394
- *
395
- * @param {Buffer} data Data to handle
396
- * @private
397
- */
398
- controlMessage (data) {
399
- if (this.opcode === 0x08) {
400
- if (data.length === 0) {
401
- this.onclose(1000, '', { masked: this.masked });
402
- this.cleanup(this.cleanupCallback);
403
- } else if (data.length === 1) {
404
- this.error(new Error('invalid payload length'), 1002);
405
- } else {
406
- const code = data.readUInt16BE(0, true);
407
-
408
- if (!ErrorCodes.isValidErrorCode(code)) {
409
- this.error(new Error(`invalid status code: ${code}`), 1002);
410
- return;
411
- }
412
-
413
- const buf = data.slice(2);
414
-
415
- if (!isValidUTF8(buf)) {
416
- this.error(new Error('invalid utf8 sequence'), 1007);
417
- return;
418
- }
419
-
420
- this.onclose(code, buf.toString(), { masked: this.masked });
421
- this.cleanup(this.cleanupCallback);
422
- }
423
-
424
- return;
425
- }
426
-
427
- const flags = { masked: this.masked, binary: true };
428
-
429
- if (this.opcode === 0x09) this.onping(data, flags);
430
- else this.onpong(data, flags);
431
-
432
- this.state = GET_INFO;
433
- }
434
-
435
- /**
436
- * Handles an error.
437
- *
438
- * @param {Error} err The error
439
- * @param {Number} code Close code
440
- * @private
441
- */
442
- error (err, code) {
443
- this.onerror(err, code);
444
- this.hadError = true;
445
- this.cleanup(this.cleanupCallback);
446
- }
447
-
448
- /**
449
- * Checks payload size, disconnects socket when it exceeds `maxPayload`.
450
- *
451
- * @param {Number} length Payload length
452
- * @private
453
- */
454
- maxPayloadExceeded (length) {
455
- if (length === 0 || this.maxPayload < 1) return false;
456
-
457
- const fullLength = this.totalPayloadLength + length;
458
-
459
- if (fullLength <= this.maxPayload) {
460
- this.totalPayloadLength = fullLength;
461
- return false;
462
- }
463
-
464
- this.error(new Error('max payload size exceeded'), 1009);
465
- return true;
466
- }
467
-
468
- /**
469
- * Appends a fragment in the fragments array after checking that the sum of
470
- * fragment lengths does not exceed `maxPayload`.
471
- *
472
- * @param {Buffer} fragment The fragment to add
473
- * @return {Boolean} `true` if `maxPayload` is not exceeded, else `false`
474
- * @private
475
- */
476
- pushFragment (fragment) {
477
- if (fragment.length === 0) return true;
478
-
479
- const totalLength = this.messageLength + fragment.length;
480
-
481
- if (this.maxPayload < 1 || totalLength <= this.maxPayload) {
482
- this.messageLength = totalLength;
483
- this.fragments.push(fragment);
484
- return true;
485
- }
486
-
487
- this.error(new Error('max payload size exceeded'), 1009);
488
- return false;
489
- }
490
-
491
- /**
492
- * Releases resources used by the receiver.
493
- *
494
- * @param {Function} cb Callback
495
- * @public
496
- */
497
- cleanup (cb) {
498
- this.dead = true;
499
-
500
- if (!this.hadError && this.state === INFLATING) {
501
- this.cleanupCallback = cb;
502
- } else {
503
- this.extensions = null;
504
- this.fragments = null;
505
- this.buffers = null;
506
- this.mask = null;
507
-
508
- this.cleanupCallback = null;
509
- this.onmessage = null;
510
- this.onclose = null;
511
- this.onerror = null;
512
- this.onping = null;
513
- this.onpong = null;
514
-
515
- if (cb) cb();
516
- }
517
- }
518
- }
519
-
520
- module.exports = Receiver;