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,92 +1,29 @@
1
1
  {
2
- "_args": [
3
- [
4
- {
5
- "raw": "commander@2.1.x",
6
- "scope": null,
7
- "escapedName": "commander",
8
- "name": "commander",
9
- "rawSpec": "2.1.x",
10
- "spec": ">=2.1.0 <2.2.0",
11
- "type": "range"
12
- },
13
- "/Users/ilya/Work/opal/lib/opal/cli_runners/node_modules/chrome-remote-interface"
14
- ]
15
- ],
16
- "_from": "commander@>=2.1.0 <2.2.0",
17
- "_id": "commander@2.1.0",
18
- "_inCache": true,
19
- "_location": "/commander",
20
- "_npmUser": {
21
- "name": "tjholowaychuk",
22
- "email": "tj@vision-media.ca"
23
- },
24
- "_npmVersion": "1.3.14",
25
- "_phantomChildren": {},
26
- "_requested": {
27
- "raw": "commander@2.1.x",
28
- "scope": null,
29
- "escapedName": "commander",
30
- "name": "commander",
31
- "rawSpec": "2.1.x",
32
- "spec": ">=2.1.0 <2.2.0",
33
- "type": "range"
34
- },
35
- "_requiredBy": [
36
- "/chrome-remote-interface"
37
- ],
38
- "_resolved": "https://registry.npmjs.org/commander/-/commander-2.1.0.tgz",
39
- "_shasum": "d121bbae860d9992a3d517ba96f56588e47c6781",
40
- "_shrinkwrap": null,
41
- "_spec": "commander@2.1.x",
42
- "_where": "/Users/ilya/Work/opal/lib/opal/cli_runners/node_modules/chrome-remote-interface",
43
- "author": {
44
- "name": "TJ Holowaychuk",
45
- "email": "tj@vision-media.ca"
46
- },
47
- "bugs": {
48
- "url": "https://github.com/visionmedia/commander.js/issues"
49
- },
50
- "dependencies": {},
2
+ "name": "commander",
3
+ "version": "2.11.0",
51
4
  "description": "the complete solution for node.js command-line programs",
52
- "devDependencies": {
53
- "should": ">= 0.0.1"
54
- },
55
- "directories": {},
56
- "dist": {
57
- "shasum": "d121bbae860d9992a3d517ba96f56588e47c6781",
58
- "tarball": "https://registry.npmjs.org/commander/-/commander-2.1.0.tgz"
59
- },
60
- "engines": {
61
- "node": ">= 0.6.x"
62
- },
63
- "files": [
64
- "index.js"
65
- ],
66
- "homepage": "https://github.com/visionmedia/commander.js",
67
5
  "keywords": [
6
+ "commander",
68
7
  "command",
69
8
  "option",
70
- "parser",
71
- "prompt",
72
- "stdin"
73
- ],
74
- "main": "index",
75
- "maintainers": [
76
- {
77
- "name": "tjholowaychuk",
78
- "email": "tj@vision-media.ca"
79
- }
9
+ "parser"
80
10
  ],
81
- "name": "commander",
82
- "optionalDependencies": {},
83
- "readme": "ERROR: No README data found!",
11
+ "author": "TJ Holowaychuk <tj@vision-media.ca>",
12
+ "license": "MIT",
84
13
  "repository": {
85
14
  "type": "git",
86
- "url": "git+https://github.com/visionmedia/commander.js.git"
15
+ "url": "https://github.com/tj/commander.js.git"
16
+ },
17
+ "devDependencies": {
18
+ "should": "^11.2.1",
19
+ "sinon": "^2.3.5"
87
20
  },
88
21
  "scripts": {
89
22
  "test": "make test"
90
23
  },
91
- "version": "2.1.0"
24
+ "main": "index",
25
+ "files": [
26
+ "index.js"
27
+ ],
28
+ "dependencies": {}
92
29
  }
@@ -1,73 +1,131 @@
1
1
  # ws: a Node.js WebSocket library
2
2
 
3
- [![Version npm](https://img.shields.io/npm/v/ws.svg)](https://www.npmjs.com/package/ws)
4
- [![Linux Build](https://img.shields.io/travis/websockets/ws/master.svg)](https://travis-ci.org/websockets/ws)
5
- [![Windows Build](https://ci.appveyor.com/api/projects/status/github/websockets/ws?branch=master&svg=true)](https://ci.appveyor.com/project/lpinca/ws)
6
- [![Coverage Status](https://img.shields.io/coveralls/websockets/ws/master.svg)](https://coveralls.io/r/websockets/ws?branch=master)
7
-
8
- `ws` is a simple to use, blazing fast, and thoroughly tested WebSocket client
9
- and server implementation.
10
-
11
- Passes the quite extensive Autobahn test suite. See http://websockets.github.io/ws/
12
- for the full reports.
3
+ [![Version npm](https://img.shields.io/npm/v/ws.svg?logo=npm)](https://www.npmjs.com/package/ws)
4
+ [![CI](https://img.shields.io/github/workflow/status/websockets/ws/CI/master?label=CI&logo=github)](https://github.com/websockets/ws/actions?query=workflow%3ACI+branch%3Amaster)
5
+ [![Coverage Status](https://img.shields.io/coveralls/websockets/ws/master.svg?logo=coveralls)](https://coveralls.io/github/websockets/ws)
6
+
7
+ ws is a simple to use, blazing fast, and thoroughly tested WebSocket client and
8
+ server implementation.
9
+
10
+ Passes the quite extensive Autobahn test suite: [server][server-report],
11
+ [client][client-report].
12
+
13
+ **Note**: This module does not work in the browser. The client in the docs is a
14
+ reference to a back end with the role of a client in the WebSocket
15
+ communication. Browser clients must use the native
16
+ [`WebSocket`](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket)
17
+ object. To make the same code work seamlessly on Node.js and the browser, you
18
+ can use one of the many wrappers available on npm, like
19
+ [isomorphic-ws](https://github.com/heineiuo/isomorphic-ws).
20
+
21
+ ## Table of Contents
22
+
23
+ - [Protocol support](#protocol-support)
24
+ - [Installing](#installing)
25
+ - [Opt-in for performance](#opt-in-for-performance)
26
+ - [API docs](#api-docs)
27
+ - [WebSocket compression](#websocket-compression)
28
+ - [Usage examples](#usage-examples)
29
+ - [Sending and receiving text data](#sending-and-receiving-text-data)
30
+ - [Sending binary data](#sending-binary-data)
31
+ - [Simple server](#simple-server)
32
+ - [External HTTP/S server](#external-https-server)
33
+ - [Multiple servers sharing a single HTTP/S server](#multiple-servers-sharing-a-single-https-server)
34
+ - [Client authentication](#client-authentication)
35
+ - [Server broadcast](#server-broadcast)
36
+ - [echo.websocket.org demo](#echowebsocketorg-demo)
37
+ - [Use the Node.js streams API](#use-the-nodejs-streams-api)
38
+ - [Other examples](#other-examples)
39
+ - [FAQ](#faq)
40
+ - [How to get the IP address of the client?](#how-to-get-the-ip-address-of-the-client)
41
+ - [How to detect and close broken connections?](#how-to-detect-and-close-broken-connections)
42
+ - [How to connect via a proxy?](#how-to-connect-via-a-proxy)
43
+ - [Changelog](#changelog)
44
+ - [License](#license)
13
45
 
14
46
  ## Protocol support
15
47
 
16
- * **HyBi drafts 07-12** (Use the option `protocolVersion: 8`)
17
- * **HyBi drafts 13-17** (Current default, alternatively option `protocolVersion: 13`)
48
+ - **HyBi drafts 07-12** (Use the option `protocolVersion: 8`)
49
+ - **HyBi drafts 13-17** (Current default, alternatively option
50
+ `protocolVersion: 13`)
18
51
 
19
52
  ## Installing
20
53
 
21
54
  ```
22
- npm install --save ws
55
+ npm install ws
23
56
  ```
24
57
 
25
58
  ### Opt-in for performance
26
59
 
27
- There are 2 optional modules that can be installed along side with the `ws`
28
- module. These modules are binary addons which improve certain operations, but as
29
- they are binary addons they require compilation which can fail if no c++
30
- compiler is installed on the host system.
60
+ There are 2 optional modules that can be installed along side with the ws
61
+ module. These modules are binary addons which improve certain operations.
62
+ Prebuilt binaries are available for the most popular platforms so you don't
63
+ necessarily need to have a C++ compiler installed on your machine.
31
64
 
32
- - `npm install --save bufferutil`: Improves internal buffer operations which
33
- allows for faster processing of masked WebSocket frames and general buffer
34
- operations.
35
- - `npm install --save utf-8-validate`: The specification requires validation of
36
- invalid UTF-8 chars, some of these validations could not be done in JavaScript
37
- hence the need for a binary addon. In most cases you will already be
38
- validating the input that you receive for security purposes leading to double
39
- validation. But if you want to be 100% spec-conforming and have fast
40
- validation of UTF-8 then this module is a must.
65
+ - `npm install --save-optional bufferutil`: Allows to efficiently perform
66
+ operations such as masking and unmasking the data payload of the WebSocket
67
+ frames.
68
+ - `npm install --save-optional utf-8-validate`: Allows to efficiently check if a
69
+ message contains valid UTF-8.
41
70
 
42
- ## API Docs
71
+ ## API docs
43
72
 
44
- See [`/doc/ws.md`](https://github.com/websockets/ws/blob/master/doc/ws.md)
45
- for Node.js-like docs for the ws classes.
73
+ See [`/doc/ws.md`](./doc/ws.md) for Node.js-like documentation of ws classes and
74
+ utility functions.
46
75
 
47
76
  ## WebSocket compression
48
77
 
49
- `ws` supports the [permessage-deflate extension][permessage-deflate] extension
50
- which enables the client and server to negotiate a compression algorithm and
51
- its parameters, and then selectively apply it to the data payloads of each
52
- WebSocket message.
78
+ ws supports the [permessage-deflate extension][permessage-deflate] which enables
79
+ the client and server to negotiate a compression algorithm and its parameters,
80
+ and then selectively apply it to the data payloads of each WebSocket message.
81
+
82
+ The extension is disabled by default on the server and enabled by default on the
83
+ client. It adds a significant overhead in terms of performance and memory
84
+ consumption so we suggest to enable it only if it is really needed.
53
85
 
54
- The extension is enabled by default but adds a significant overhead in terms of
55
- performance and memory comsumption. We suggest to use WebSocket compression
56
- only if it is really needed.
86
+ Note that Node.js has a variety of issues with high-performance compression,
87
+ where increased concurrency, especially on Linux, can lead to [catastrophic
88
+ memory fragmentation][node-zlib-bug] and slow performance. If you intend to use
89
+ permessage-deflate in production, it is worthwhile to set up a test
90
+ representative of your workload and ensure Node.js/zlib will handle it with
91
+ acceptable performance and memory usage.
57
92
 
58
- To disable the extension you can set the `perMessageDeflate` option to `false`.
59
- On the server:
93
+ Tuning of permessage-deflate can be done via the options defined below. You can
94
+ also use `zlibDeflateOptions` and `zlibInflateOptions`, which is passed directly
95
+ into the creation of [raw deflate/inflate streams][node-zlib-deflaterawdocs].
96
+
97
+ See [the docs][ws-server-options] for more options.
60
98
 
61
99
  ```js
62
100
  const WebSocket = require('ws');
63
101
 
64
102
  const wss = new WebSocket.Server({
65
- perMessageDeflate: false,
66
- port: 8080
103
+ port: 8080,
104
+ perMessageDeflate: {
105
+ zlibDeflateOptions: {
106
+ // See zlib defaults.
107
+ chunkSize: 1024,
108
+ memLevel: 7,
109
+ level: 3
110
+ },
111
+ zlibInflateOptions: {
112
+ chunkSize: 10 * 1024
113
+ },
114
+ // Other options settable:
115
+ clientNoContextTakeover: true, // Defaults to negotiated value.
116
+ serverNoContextTakeover: true, // Defaults to negotiated value.
117
+ serverMaxWindowBits: 10, // Defaults to negotiated value.
118
+ // Below options specified as default values.
119
+ concurrencyLimit: 10, // Limits zlib concurrency for perf.
120
+ threshold: 1024 // Size (in bytes) below which messages
121
+ // should not be compressed.
122
+ }
67
123
  });
68
124
  ```
69
125
 
70
- On the client:
126
+ The client will only use the extension if it is supported and enabled on the
127
+ server. To always disable the extension on the client set the
128
+ `perMessageDeflate` option to `false`.
71
129
 
72
130
  ```js
73
131
  const WebSocket = require('ws');
@@ -90,9 +148,8 @@ ws.on('open', function open() {
90
148
  ws.send('something');
91
149
  });
92
150
 
93
- ws.on('message', function incoming(data, flags) {
94
- // flags.binary will be set if a binary data is received.
95
- // flags.masked will be set if the data was masked.
151
+ ws.on('message', function incoming(data) {
152
+ console.log(data);
96
153
  });
97
154
  ```
98
155
 
@@ -114,7 +171,7 @@ ws.on('open', function open() {
114
171
  });
115
172
  ```
116
173
 
117
- ### Server example
174
+ ### Simple server
118
175
 
119
176
  ```js
120
177
  const WebSocket = require('ws');
@@ -130,27 +187,117 @@ wss.on('connection', function connection(ws) {
130
187
  });
131
188
  ```
132
189
 
133
- ### Broadcast example
190
+ ### External HTTP/S server
134
191
 
135
192
  ```js
193
+ const fs = require('fs');
194
+ const https = require('https');
136
195
  const WebSocket = require('ws');
137
196
 
138
- const wss = new WebSocket.Server({ port: 8080 });
197
+ const server = https.createServer({
198
+ cert: fs.readFileSync('/path/to/cert.pem'),
199
+ key: fs.readFileSync('/path/to/key.pem')
200
+ });
201
+ const wss = new WebSocket.Server({ server });
202
+
203
+ wss.on('connection', function connection(ws) {
204
+ ws.on('message', function incoming(message) {
205
+ console.log('received: %s', message);
206
+ });
207
+
208
+ ws.send('something');
209
+ });
210
+
211
+ server.listen(8080);
212
+ ```
213
+
214
+ ### Multiple servers sharing a single HTTP/S server
139
215
 
140
- // Broadcast to all.
141
- wss.broadcast = function broadcast(data) {
142
- wss.clients.forEach(function each(client) {
143
- if (client.readyState === WebSocket.OPEN) {
144
- client.send(data);
216
+ ```js
217
+ const http = require('http');
218
+ const WebSocket = require('ws');
219
+ const url = require('url');
220
+
221
+ const server = http.createServer();
222
+ const wss1 = new WebSocket.Server({ noServer: true });
223
+ const wss2 = new WebSocket.Server({ noServer: true });
224
+
225
+ wss1.on('connection', function connection(ws) {
226
+ // ...
227
+ });
228
+
229
+ wss2.on('connection', function connection(ws) {
230
+ // ...
231
+ });
232
+
233
+ server.on('upgrade', function upgrade(request, socket, head) {
234
+ const pathname = url.parse(request.url).pathname;
235
+
236
+ if (pathname === '/foo') {
237
+ wss1.handleUpgrade(request, socket, head, function done(ws) {
238
+ wss1.emit('connection', ws, request);
239
+ });
240
+ } else if (pathname === '/bar') {
241
+ wss2.handleUpgrade(request, socket, head, function done(ws) {
242
+ wss2.emit('connection', ws, request);
243
+ });
244
+ } else {
245
+ socket.destroy();
246
+ }
247
+ });
248
+
249
+ server.listen(8080);
250
+ ```
251
+
252
+ ### Client authentication
253
+
254
+ ```js
255
+ const http = require('http');
256
+ const WebSocket = require('ws');
257
+
258
+ const server = http.createServer();
259
+ const wss = new WebSocket.Server({ noServer: true });
260
+
261
+ wss.on('connection', function connection(ws, request, client) {
262
+ ws.on('message', function message(msg) {
263
+ console.log(`Received message ${msg} from user ${client}`);
264
+ });
265
+ });
266
+
267
+ server.on('upgrade', function upgrade(request, socket, head) {
268
+ // This function is not defined on purpose. Implement it with your own logic.
269
+ authenticate(request, (err, client) => {
270
+ if (err || !client) {
271
+ socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n');
272
+ socket.destroy();
273
+ return;
145
274
  }
275
+
276
+ wss.handleUpgrade(request, socket, head, function done(ws) {
277
+ wss.emit('connection', ws, request, client);
278
+ });
146
279
  });
147
- };
280
+ });
281
+
282
+ server.listen(8080);
283
+ ```
284
+
285
+ Also see the provided [example][session-parse-example] using `express-session`.
286
+
287
+ ### Server broadcast
288
+
289
+ A client WebSocket broadcasting to all connected WebSocket clients, including
290
+ itself.
291
+
292
+ ```js
293
+ const WebSocket = require('ws');
294
+
295
+ const wss = new WebSocket.Server({ port: 8080 });
148
296
 
149
297
  wss.on('connection', function connection(ws) {
150
298
  ws.on('message', function incoming(data) {
151
- // Broadcast to everyone else.
152
299
  wss.clients.forEach(function each(client) {
153
- if (client !== ws && client.readyState === WebSocket.OPEN) {
300
+ if (client.readyState === WebSocket.OPEN) {
154
301
  client.send(data);
155
302
  }
156
303
  });
@@ -158,37 +305,22 @@ wss.on('connection', function connection(ws) {
158
305
  });
159
306
  ```
160
307
 
161
- ### ExpressJS example
308
+ A client WebSocket broadcasting to every other connected WebSocket clients,
309
+ excluding itself.
162
310
 
163
311
  ```js
164
- const express = require('express');
165
- const http = require('http');
166
- const url = require('url');
167
312
  const WebSocket = require('ws');
168
313
 
169
- const app = express();
170
-
171
- app.use(function (req, res) {
172
- res.send({ msg: "hello" });
173
- });
174
-
175
- const server = http.createServer(app);
176
- const wss = new WebSocket.Server({ server });
314
+ const wss = new WebSocket.Server({ port: 8080 });
177
315
 
178
316
  wss.on('connection', function connection(ws) {
179
- const location = url.parse(ws.upgradeReq.url, true);
180
- // You might use location.query.access_token to authenticate or share sessions
181
- // or ws.upgradeReq.headers.cookie (see http://stackoverflow.com/a/16395220/151312)
182
-
183
- ws.on('message', function incoming(message) {
184
- console.log('received: %s', message);
317
+ ws.on('message', function incoming(data) {
318
+ wss.clients.forEach(function each(client) {
319
+ if (client !== ws && client.readyState === WebSocket.OPEN) {
320
+ client.send(data);
321
+ }
322
+ });
185
323
  });
186
-
187
- ws.send('something');
188
- });
189
-
190
- server.listen(8080, function listening() {
191
- console.log('Listening on %d', server.address().port);
192
324
  });
193
325
  ```
194
326
 
@@ -210,8 +342,8 @@ ws.on('close', function close() {
210
342
  console.log('disconnected');
211
343
  });
212
344
 
213
- ws.on('message', function incoming(data, flags) {
214
- console.log(`Roundtrip time: ${Date.now() - data} ms`, flags);
345
+ ws.on('message', function incoming(data) {
346
+ console.log(`Roundtrip time: ${Date.now() - data} ms`);
215
347
 
216
348
  setTimeout(function timeout() {
217
349
  ws.send(Date.now());
@@ -219,6 +351,21 @@ ws.on('message', function incoming(data, flags) {
219
351
  });
220
352
  ```
221
353
 
354
+ ### Use the Node.js streams API
355
+
356
+ ```js
357
+ const WebSocket = require('ws');
358
+
359
+ const ws = new WebSocket('wss://echo.websocket.org/', {
360
+ origin: 'https://websocket.org'
361
+ });
362
+
363
+ const duplex = WebSocket.createWebSocketStream(ws, { encoding: 'utf8' });
364
+
365
+ duplex.pipe(process.stdout);
366
+ process.stdin.pipe(duplex);
367
+ ```
368
+
222
369
  ### Other examples
223
370
 
224
371
  For a full example with a browser client communicating with a ws server, see the
@@ -226,34 +373,123 @@ examples folder.
226
373
 
227
374
  Otherwise, see the test cases.
228
375
 
229
- ## Error handling best practices
376
+ ## FAQ
377
+
378
+ ### How to get the IP address of the client?
379
+
380
+ The remote IP address can be obtained from the raw socket.
381
+
382
+ ```js
383
+ const WebSocket = require('ws');
384
+
385
+ const wss = new WebSocket.Server({ port: 8080 });
386
+
387
+ wss.on('connection', function connection(ws, req) {
388
+ const ip = req.socket.remoteAddress;
389
+ });
390
+ ```
391
+
392
+ When the server runs behind a proxy like NGINX, the de-facto standard is to use
393
+ the `X-Forwarded-For` header.
230
394
 
231
395
  ```js
232
- // If the WebSocket is closed before the following send is attempted
233
- ws.send('something');
234
-
235
- // Errors (both immediate and async write errors) can be detected in an optional
236
- // callback. The callback is also the only way of being notified that data has
237
- // actually been sent.
238
- ws.send('something', function ack(error) {
239
- // If error is not defined, the send has been completed, otherwise the error
240
- // object will indicate what failed.
396
+ wss.on('connection', function connection(ws, req) {
397
+ const ip = req.headers['x-forwarded-for'].split(',')[0].trim();
241
398
  });
399
+ ```
400
+
401
+ ### How to detect and close broken connections?
402
+
403
+ Sometimes the link between the server and the client can be interrupted in a way
404
+ that keeps both the server and the client unaware of the broken state of the
405
+ connection (e.g. when pulling the cord).
406
+
407
+ In these cases ping messages can be used as a means to verify that the remote
408
+ endpoint is still responsive.
409
+
410
+ ```js
411
+ const WebSocket = require('ws');
412
+
413
+ function noop() {}
414
+
415
+ function heartbeat() {
416
+ this.isAlive = true;
417
+ }
418
+
419
+ const wss = new WebSocket.Server({ port: 8080 });
420
+
421
+ wss.on('connection', function connection(ws) {
422
+ ws.isAlive = true;
423
+ ws.on('pong', heartbeat);
424
+ });
425
+
426
+ const interval = setInterval(function ping() {
427
+ wss.clients.forEach(function each(ws) {
428
+ if (ws.isAlive === false) return ws.terminate();
242
429
 
243
- // Immediate errors can also be handled with `try...catch`, but **note** that
244
- // since sends are inherently asynchronous, socket write failures will *not* be
245
- // captured when this technique is used.
246
- try { ws.send('something'); }
247
- catch (e) { /* handle error */ }
430
+ ws.isAlive = false;
431
+ ws.ping(noop);
432
+ });
433
+ }, 30000);
434
+
435
+ wss.on('close', function close() {
436
+ clearInterval(interval);
437
+ });
438
+ ```
439
+
440
+ Pong messages are automatically sent in response to ping messages as required by
441
+ the spec.
442
+
443
+ Just like the server example above your clients might as well lose connection
444
+ without knowing it. You might want to add a ping listener on your clients to
445
+ prevent that. A simple implementation would be:
446
+
447
+ ```js
448
+ const WebSocket = require('ws');
449
+
450
+ function heartbeat() {
451
+ clearTimeout(this.pingTimeout);
452
+
453
+ // Use `WebSocket#terminate()`, which immediately destroys the connection,
454
+ // instead of `WebSocket#close()`, which waits for the close timer.
455
+ // Delay should be equal to the interval at which your server
456
+ // sends out pings plus a conservative assumption of the latency.
457
+ this.pingTimeout = setTimeout(() => {
458
+ this.terminate();
459
+ }, 30000 + 1000);
460
+ }
461
+
462
+ const client = new WebSocket('wss://echo.websocket.org/');
463
+
464
+ client.on('open', heartbeat);
465
+ client.on('ping', heartbeat);
466
+ client.on('close', function clear() {
467
+ clearTimeout(this.pingTimeout);
468
+ });
248
469
  ```
249
470
 
471
+ ### How to connect via a proxy?
472
+
473
+ Use a custom `http.Agent` implementation like [https-proxy-agent][] or
474
+ [socks-proxy-agent][].
475
+
250
476
  ## Changelog
251
477
 
252
- We're using the GitHub [`releases`](https://github.com/websockets/ws/releases)
253
- for changelog entries.
478
+ We're using the GitHub [releases][changelog] for changelog entries.
254
479
 
255
480
  ## License
256
481
 
257
482
  [MIT](LICENSE)
258
483
 
484
+ [changelog]: https://github.com/websockets/ws/releases
485
+ [client-report]: http://websockets.github.io/ws/autobahn/clients/
486
+ [https-proxy-agent]: https://github.com/TooTallNate/node-https-proxy-agent
487
+ [node-zlib-bug]: https://github.com/nodejs/node/issues/8871
488
+ [node-zlib-deflaterawdocs]:
489
+ https://nodejs.org/api/zlib.html#zlib_zlib_createdeflateraw_options
259
490
  [permessage-deflate]: https://tools.ietf.org/html/rfc7692
491
+ [server-report]: http://websockets.github.io/ws/autobahn/servers/
492
+ [session-parse-example]: ./examples/express-session-parse
493
+ [socks-proxy-agent]: https://github.com/TooTallNate/node-socks-proxy-agent
494
+ [ws-server-options]:
495
+ https://github.com/websockets/ws/blob/master/doc/ws.md#new-websocketserveroptions-callback
@@ -0,0 +1,8 @@
1
+ 'use strict';
2
+
3
+ module.exports = function () {
4
+ throw new Error(
5
+ 'ws does not work in the browser. Browser clients must use the native ' +
6
+ 'WebSocket object'
7
+ );
8
+ };
@@ -1,15 +1,10 @@
1
- /*!
2
- * ws: a node.js websocket client
3
- * Copyright(c) 2011 Einar Otto Stangvik <einaros@gmail.com>
4
- * MIT Licensed
5
- */
6
-
7
1
  'use strict';
8
2
 
9
- const WebSocket = require('./lib/WebSocket');
3
+ const WebSocket = require('./lib/websocket');
10
4
 
11
- WebSocket.Server = require('./lib/WebSocketServer');
12
- WebSocket.Receiver = require('./lib/Receiver');
13
- WebSocket.Sender = require('./lib/Sender');
5
+ WebSocket.createWebSocketStream = require('./lib/stream');
6
+ WebSocket.Server = require('./lib/websocket-server');
7
+ WebSocket.Receiver = require('./lib/receiver');
8
+ WebSocket.Sender = require('./lib/sender');
14
9
 
15
10
  module.exports = WebSocket;