opal 1.6.1 → 1.7.0.rc1

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