summaryrefslogtreecommitdiffstats
path: root/third_party/rust/ws
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--third_party/rust/ws/.cargo-checksum.json1
-rw-r--r--third_party/rust/ws/CHANGELOG.md265
-rw-r--r--third_party/rust/ws/Cargo.lock1033
-rw-r--r--third_party/rust/ws/Cargo.toml84
-rw-r--r--third_party/rust/ws/LICENSE19
-rw-r--r--third_party/rust/ws/README.md55
-rw-r--r--third_party/rust/ws/examples/autobahn-client.rs76
-rw-r--r--third_party/rust/ws/examples/autobahn-server.rs25
-rw-r--r--third_party/rust/ws/examples/bench-server.rs16
-rw-r--r--third_party/rust/ws/examples/bench.rs79
-rw-r--r--third_party/rust/ws/examples/channel.rs156
-rw-r--r--third_party/rust/ws/examples/cli.rs180
-rw-r--r--third_party/rust/ws/examples/client.rs33
-rw-r--r--third_party/rust/ws/examples/external_shutdown.rs40
-rw-r--r--third_party/rust/ws/examples/html_chat.rs66
-rw-r--r--third_party/rust/ws/examples/peer2peer.rs96
-rw-r--r--third_party/rust/ws/examples/pong.rs130
-rw-r--r--third_party/rust/ws/examples/remote_addr.rs25
-rw-r--r--third_party/rust/ws/examples/router.rs141
-rw-r--r--third_party/rust/ws/examples/server.rs26
-rw-r--r--third_party/rust/ws/examples/shared.rs58
-rw-r--r--third_party/rust/ws/examples/ssl-server.rs122
-rw-r--r--third_party/rust/ws/examples/threaded.rs56
-rw-r--r--third_party/rust/ws/examples/unsafe-ssl-client.rs69
-rw-r--r--third_party/rust/ws/src/communication.rs249
-rw-r--r--third_party/rust/ws/src/connection.rs1230
-rw-r--r--third_party/rust/ws/src/deflate/context.rs268
-rw-r--r--third_party/rust/ws/src/deflate/extension.rs565
-rw-r--r--third_party/rust/ws/src/deflate/mod.rs9
-rw-r--r--third_party/rust/ws/src/factory.rs188
-rw-r--r--third_party/rust/ws/src/frame.rs495
-rw-r--r--third_party/rust/ws/src/handler.rs423
-rw-r--r--third_party/rust/ws/src/handshake.rs740
-rw-r--r--third_party/rust/ws/src/io.rs985
-rw-r--r--third_party/rust/ws/src/lib.rs391
-rw-r--r--third_party/rust/ws/src/message.rs173
-rw-r--r--third_party/rust/ws/src/protocol.rs227
-rw-r--r--third_party/rust/ws/src/result.rs204
-rw-r--r--third_party/rust/ws/src/stream.rs358
-rw-r--r--third_party/rust/ws/src/util.rs9
-rw-r--r--third_party/rust/ws/tests/bind.rs31
-rw-r--r--third_party/rust/ws/tests/deflate.rs78
-rw-r--r--third_party/rust/ws/tests/fuzzingclient.json11
-rw-r--r--third_party/rust/ws/tests/fuzzingserver.json8
-rw-r--r--third_party/rust/ws/tests/shutdown.rs41
-rw-r--r--third_party/rust/ws2_32-sys/.cargo-checksum.json1
-rw-r--r--third_party/rust/ws2_32-sys/Cargo.toml17
-rw-r--r--third_party/rust/ws2_32-sys/README.md13
-rw-r--r--third_party/rust/ws2_32-sys/build.rs6
-rw-r--r--third_party/rust/ws2_32-sys/src/lib.rs483
50 files changed, 10054 insertions, 0 deletions
diff --git a/third_party/rust/ws/.cargo-checksum.json b/third_party/rust/ws/.cargo-checksum.json
new file mode 100644
index 0000000000..900b1139f8
--- /dev/null
+++ b/third_party/rust/ws/.cargo-checksum.json
@@ -0,0 +1 @@
+{"files":{"CHANGELOG.md":"28a9bf7420eb71562ab146188cbc7cf9f299c90a3a46d92f32eb8ea48d70c77f","Cargo.lock":"c5261b6ab83a4ed7b8f0701048778b39585a0ad979aeed63d9d079e9c56d5c3b","Cargo.toml":"b3883e5cbe16ea8cebf5e553f1f8607b17f67c848c3a2734548b56d4e652929c","LICENSE":"87bf8937aeabfcf53266f58d5c336e80b0be36f3005af4af6ee1bcd6e5a51b0e","README.md":"b68b23e68eb79683ba4de6797427a466812deaa7daa1c6bacbf9bce40bbf90b6","examples/autobahn-client.rs":"adca1e305f45277326c5cc11df1ae214258e6412b9aa0c9094144d0d6d95aaca","examples/autobahn-server.rs":"c7dd24bddbabd141746f57ca74d72cbdffd8a8d280e1bcf03f37ea663ab89ba0","examples/bench-server.rs":"1d9759223718a9677f308435a39de83841552fc0664e542c434a5af741f0b282","examples/bench.rs":"6b12d7edbe7dbf3917ab2f6a0c6867bb426dbf278bba98c65cdc458b0dbaa302","examples/channel.rs":"00ec08f63064688b61c807fad51edf25ed3a1afba0c7b5ebe4099bdaaa0438f8","examples/cli.rs":"e9fdb6b47dc67a9f896f56e152f85ead4497ef994c9f3617efac7bf8a974db44","examples/client.rs":"b9ca005dcdc637a1ae6efdd1b3c984c249be5fe718981d8073899357aff559eb","examples/external_shutdown.rs":"125ca6605f5540d30f347365fee8ee0e71bac2d3afc7a34ddc4ee03e453e5637","examples/html_chat.rs":"4347f092659bf402f29c5d874f85e2add96817d1e8a42c413b3e69dec5e98fdd","examples/peer2peer.rs":"6202a73534f38e8ddb1a8aad2507cbca39a2a75238e3243af30640a2dc38218a","examples/pong.rs":"64e7b1355759433327282209d2549bd0134a5d85c28bb6e691ebc04f2a8e9c5b","examples/remote_addr.rs":"59e0365093cfac1cb357c3a48caa6fe9d03d75cc7e61763b6b20ac9b011cdf78","examples/router.rs":"62b184b8e12315c7cb2064e490b491c75fe93de11fd315f42b79a0688d086580","examples/server.rs":"4bbc26ef67f7033692df7bfca1a80315ad6aaafe564a15deb9d6341421a1d7fd","examples/shared.rs":"5ad4866b2f187906d81b95c92afb2f2b8fe27c0703e37c865f026bcdbd9dcb9f","examples/ssl-server.rs":"58746d094393bc1ee14011abd41a438c86137ffdbf0c45a08f7415d10881c751","examples/threaded.rs":"c55cd396b23ece7270c7a779e1c39eafe50c9860dac805c5a2c67bda1d83d395","examples/unsafe-ssl-client.rs":"d703abe42ae2ba28a3e419ac6efba0f8b02cc0c61a8772688100cacfaa522a8d","src/communication.rs":"bb86c10ca0cdecef6eb5aa94b9c78857ca011036e3e78b70d9fb747e11439446","src/connection.rs":"2e9fcdb5b3577de0380bea41658c44a314b0e43146fe55d25533a891435924c4","src/deflate/context.rs":"dc9e678c143db4c8c78214fb9fdb7b3d9544fc8fbb48ce285b7e7aa3d00fce9b","src/deflate/extension.rs":"131a9525b8234f686e59bb7ac848d1daf2b30d1a8a9729a82b84b678936f82bb","src/deflate/mod.rs":"fc927311d2d1ecee787bda54524681ead33b3d62154a90fab458fca4d00ccc04","src/factory.rs":"bff726225c632e2ee94984820cc7aa647c234ab69b75c5cb9cfff8d97eceadb6","src/frame.rs":"c97015c2be9ccfb3f5dbfbe6eeebee526a9bc9cbd7f65e8f5d9423ff6d96b284","src/handler.rs":"e69a8d8d64987c4ed6d2429db8c5408688f1fe4dbe1369dee290bbcc13eb5c42","src/handshake.rs":"33b64ad48fa11ebd14f02c5e386cc9c50c9149405aaab53918a0ccdaff65e4c4","src/io.rs":"0b8389e0b42cb4edbea2529bf983262db3c7bf8fc0a02d214689ecc00feba3e7","src/lib.rs":"96437b3777df6453e81989c62fcff343751ef247ac213b50f496e8832ad286c8","src/message.rs":"b08cd9b7426b8fb2c13bfed45ba81e59fc262988c2a46cdbe77c5161fc08d9b0","src/protocol.rs":"5096c4a96ac0c4296909bdc0d30a808da6dee44ff5550ac345f4ef4020cbe173","src/result.rs":"102deb3d3f173f2d73a460217afb85253ee5efd9289a83b6d3fd1d5754f45bce","src/stream.rs":"e648eaeec372adb1fa3e441695248af44456d781c3ef50845b4b40d185f72a37","src/util.rs":"428a5d4465aebf29e1af940a06b0f82e758e7aa9953169b9d5c61858f57ded7b","tests/bind.rs":"ceea3d8da1f216e5c919220a8f3b360d8e752c7c32f045d4bcf638b0f86ae434","tests/deflate.rs":"dcf9f5139d1a5d63cf1c75f450781933d6e0730dc710ac8a01ad6ff2ec2774a0","tests/fuzzingclient.json":"524dfc4da9385ccab2ecac941114ca2bd6be3bd553d06f637ad26d3243407261","tests/fuzzingserver.json":"0574cb0b8deb9a99b70e4d300f4877905b69d59a093a6937e4cbdac4cd2123af","tests/shutdown.rs":"7338f9feec52c405ab17c1f00a8ce4494c89f50043bd7423b470dd2a26f97b3b"},"package":"c51a2c47b5798ccc774ffb93ff536aec7c4275d722fd9c740c83cdd1af1f2d94"} \ No newline at end of file
diff --git a/third_party/rust/ws/CHANGELOG.md b/third_party/rust/ws/CHANGELOG.md
new file mode 100644
index 0000000000..019c2ca6f4
--- /dev/null
+++ b/third_party/rust/ws/CHANGELOG.md
@@ -0,0 +1,265 @@
+<a name="v0.7.9"></a>
+### v0.8.0 (2018-10-15)
+
+#### Features
+* Update rand to 0.6
+* Upgrade native-tls to 0.2
+* Add a maximal size for fragments exposed via the `max_fragment_size` setting
+
+#### Bug fixes
+* Don't try to parse response when the socket not ready
+
+<a name="v0.7.9"></a>
+### v0.7.9 (2018-10-15)
+
+
+#### Features
+* Update openssl to 0.10
+* Implement `Debug` for `Sender`
+
+<a name="v0.7.8"></a>
+### v0.7.8 (2018-08-15)
+
+
+#### Bug fixes
+
+* Fixed an infinite loop when connections were closed during a handshake
+
+
+#### Features
+* `Websocket::from_url` will now add an `Authorization` header if necessary
+* Added support for native-tls via the `nativetls` feature
+
+
+<a name="v0.7.3"></a>
+### v0.7.3 (2017-06-07)
+
+
+#### Bug Fixes
+
+* Issue with `on_close` called twice
+
+
+<a name="v0.7.2"></a>
+### v0.7.2 (2017-06-04)
+
+
+#### Bug Fixes
+
+* Issue with `on_close` called in infinite loop
+* Token aliasing resolved
+
+
+
+<a name="v0.7.1"></a>
+### v0.7.1 (2017-04-08)
+
+
+#### Bug Fixes
+
+* Issue with openssl buffering causing autobahn test failure with ssl ([d3d6b8be](d3d6b8be))
+
+
+
+<a name="v0.7.0"></a>
+### v0.7.0 (2017-03-30)
+
+#### Features
+
+* Upgrade to rust-openssl 0.9 ([d8bfdfa7](d8bfdfa7))
+
+
+<a name="v0.6.1"></a>
+### v0.6.2 (2017-03-23)
+
+#### Features
+
+* Call connection_lost() on url failure to obtain address from URL
+
+#### Bug Fixes
+
+* Fix issue with errors not being logged due to change Mio readiness semantics
+
+
+<a name="v0.6.0"></a>
+### v0.6.0 (2017-02-17)
+* Update dependencies and track latest rust
+
+
+<a name="v0.5.3"></a>
+### v0.5.3 (2016-09-05)
+
+#### Bug Fixes
+
+* Prevent one connection from hogging the server, close #65 ([4a591a49](4a591a49))
+
+<a name="v0.5.2"></a>
+### v0.5.2 (2016-08-07)
+
+#### Features
+
+* Implement From<Vec<u8>> for Message, #63([1a9d020](1a9d020))
+
+
+<a name="v0.5.1"></a>
+### v0.5.1 (2016-07-21)
+
+#### Features
+
+* Add queue_size setting and handle queue errors better ([26bcef09](26bcef09))
+
+#### Bug Fixes
+
+* Build error with ssl-server example ([1b8e5c96](1b8e5c96))
+* Links on readme ([38657a0a](38657a0a))
+
+
+<a name="v0.5.0"></a>
+### v0.5.0 (2016-06-16)
+
+* Added support for permessage-deflate as a feature
+
+<a name="v0.4.9"></a>
+### v0.4.9 (2016-05-28)
+
+* Updated dependencies, maintenance release
+
+<a name="v0.4.6"></a>
+### v0.4.6 (2016-03-17)
+
+#### Bug Fixes
+
+* Debug check failing when http handshake fails ([fb84fb32](fb84fb32))
+
+#### Features
+* **ssl:** Re-enable support for SSL in Windows ([536779e9](536779e9))
+
+
+
+<a name="v0.4.5"></a>
+### v0.4.5 (2016-02-18)
+
+
+#### Bug Fixes
+
+* Handle http parse errors gracefully, closes #28 ([fd40ab7c](fd40ab7c))
+* Failure to reply to http connection, close #29 ([d3ada6ad](d3ada6ad))
+* **io:** Type mismatch when not building will +ssl ([258754bd](258754bd))
+
+#### Features
+
+* Add connection_lost method to factory ([9d057dc5](9d057dc5))
+* **ssl:** Try multiple addresses for ssl connections too ([e5db833b](e5db833b))
+
+
+
+<a name="v0.4.4"></a>
+### v0.4.4 (2016-02-10)
+
+
+#### Bug Fixes
+
+* Trigger on_close when connection hangs up. Fix #25 ([2568e148](2568e148))
+
+
+<a name="0.4.3"></a>
+### v0.4.3 (2015-12-20)
+
+
+#### Bug Fixes
+
+* **frame:** Fail to compile on 32bit, close #20 ([c78197d0](c78197d0))
+
+#### Features
+
+* **util:**
+ * Add rexports of mio utilities to facilitate timeouts ([75a4baa4](75a4baa4))
+ * Support custom timeout events. ([8d463c9e](8d463c9e))
+
+
+
+<a name="0.4.2"></a>
+### v0.4.2 (2015-12-14)
+
+
+#### Features
+
+* **protocol:** Publicize OpCode for easier frame management ([c6ef6e7f](c6ef6e7f))
+* **ssl:**
+ * Make ssl support optional ([82c99e64](82c99e64))
+ * Don't support ssl on Windows #12 ([0047ce8e](0047ce8e))
+
+#### Documentation
+
+* Add ping/pong low-level frame access example ([3d4d994](3d4d994)
+
+
+<a name="0.4.1"></a>
+### v0.4.1 (2015-12-08)
+
+
+#### Features
+
+* **frame:** publicize Frame struct ([2ba15de2](2ba15de2))
+
+
+<a name="0.4.0"></a>
+### v0.4.0 (2015-12-02)
+
+
+#### Features
+
+* Add support for SSL (wss) connections ([c4947a5](c4947a5))
+
+<a name="0.3.1"></a>
+## 0.3.1 (2015-11-10)
+
+
+#### Bug Fixes
+
+* **io:** check remote_addr after socket is readable ([b6cccbb3](b6cccbb3))
+
+#### Features
+
+* **handshake:** Add remote_addr method on Handshake ([23b83d2f](23b83d2f))
+
+
+<a name="0.3.0"></a>
+### v0.3.0 (2015-11-02)
+
+
+#### Documentation
+
+* Add bench server example using settings ([7a0de0a](7a0de0a))
+
+#### Features
+
+* Try multiple addresses in clients ([ec99a7a](ec99a7a))
+* Improved Settings ([a67951f](a67951f))
+* Add broadcast sender to WebSocket struct ([a0af1df](a0af1df))
+* Add is_empty method to message. ([6c99167f](6c99167f))
+
+<a name="0.2.1"></a>
+### v0.2.1 (2015-09-26)
+
+
+#### Documentation
+
+* Add a [command line example](https://github.com/housleyjk/ws-rs/blob/9fcafa19f974cf72581460a1e9f3b27e7201cd24/examples/cli.rs)
+
+#### Bug Fixes
+
+* Don't use absolute URI in client handshake request. Fixes [#1](https://github.com/housleyjk/ws-rs/issues/1)
+
+<a name="0.2.0"></a>
+### v0.2.0 (2015-09-16)
+
+
+#### Features
+
+* Add settings ([366e2e0](366e2e0))
+
+
+<a name="0.1.0"></a>
+### v0.1.0 (2015-08-28)
+Initial Release
diff --git a/third_party/rust/ws/Cargo.lock b/third_party/rust/ws/Cargo.lock
new file mode 100644
index 0000000000..d1cbdf8f20
--- /dev/null
+++ b/third_party/rust/ws/Cargo.lock
@@ -0,0 +1,1033 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+[[package]]
+name = "aho-corasick"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "ansi_term"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "arrayref"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "arrayvec"
+version = "0.4.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "nodrop 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "atty"
+version = "0.2.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "autocfg"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "backtrace"
+version = "0.3.38"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "backtrace-sys 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "backtrace-sys"
+version = "0.1.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "base64"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "bitflags"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "blake2b_simd"
+version = "0.5.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "arrayvec 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)",
+ "constant_time_eq 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "block-buffer"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "block-padding 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "block-padding"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "byte-tools"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "byteorder"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "bytes"
+version = "0.4.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "c2-chacha"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "ppv-lite86 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "cc"
+version = "1.0.45"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "cfg-if"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "clap"
+version = "2.33.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)",
+ "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "cloudabi"
+version = "0.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "constant_time_eq"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "core-foundation"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "core-foundation-sys"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.6.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "digest"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "dirs"
+version = "1.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
+ "redox_users 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "env_logger"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)",
+ "humantime 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "failure"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "backtrace 0.3.38 (registry+https://github.com/rust-lang/crates.io-index)",
+ "failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "failure_derive"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "synstructure 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "fake-simd"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "foreign-types"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "foreign-types-shared"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "fuchsia-cprng"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "fuchsia-zircon"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "fuchsia-zircon-sys"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "generic-array"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.1.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
+ "wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "httparse"
+version = "1.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "humantime"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "idna"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicode-normalization 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "iovec"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "kernel32-sys"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "lazy_static"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "lazycell"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "libc"
+version = "0.2.62"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "libz-sys"
+version = "1.0.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
+ "pkg-config 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
+ "vcpkg 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "log"
+version = "0.4.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "matches"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "memchr"
+version = "2.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "mio"
+version = "0.6.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)",
+ "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "mio-extras"
+version = "2.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)",
+ "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "miow"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "native-tls"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "openssl 0.10.25 (registry+https://github.com/rust-lang/crates.io-index)",
+ "openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "openssl-sys 0.9.51 (registry+https://github.com/rust-lang/crates.io-index)",
+ "schannel 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
+ "security-framework 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "security-framework-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "net2"
+version = "0.2.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "nodrop"
+version = "0.1.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "opaque-debug"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "openssl"
+version = "0.10.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
+ "openssl-sys 0.9.51 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "openssl-probe"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "openssl-sys"
+version = "0.9.51"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
+ "pkg-config 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
+ "vcpkg 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "percent-encoding"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "pkg-config"
+version = "0.3.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "ppv-lite86"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "quick-error"
+version = "1.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "quote"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "rand"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "getrandom 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "c2-chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "rand_core"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "getrandom 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "rand_hc"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "rand_os"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "rdrand"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.1.56"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "redox_users"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rust-argon2 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "regex"
+version = "1.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "regex-syntax 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)",
+ "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.6.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "remove_dir_all"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "rust-argon2"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "blake2b_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "rustc-demangle"
+version = "0.1.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "schannel"
+version = "0.1.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "security-framework"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
+ "security-framework-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "security-framework-sys"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "sha-1"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "slab"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "smallvec"
+version = "0.6.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "strsim"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "syn"
+version = "1.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "synstructure"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "tempfile"
+version = "3.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)",
+ "remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "term"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dirs 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "termcolor"
+version = "1.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "wincolor 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "textwrap"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "thread_local"
+version = "0.3.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "time"
+version = "0.1.42"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
+ "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "typenum"
+version = "1.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "unicode-bidi"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "unicode-normalization"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "unicode-width"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "unicode-xid"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "url"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "vcpkg"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "vec_map"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "wasi"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "winapi"
+version = "0.2.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "winapi"
+version = "0.3.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "winapi-build"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "winapi-util"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "wincolor"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "ws"
+version = "0.9.1"
+dependencies = [
+ "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)",
+ "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "httparse 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libz-sys 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)",
+ "mio-extras 2.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "native-tls 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "openssl 0.10.25 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "sha-1 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "term 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
+ "url 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "ws2_32-sys"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[metadata]
+"checksum aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d"
+"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
+"checksum arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0d382e583f07208808f6b1249e60848879ba3543f57c32277bf52d69c2f0f0ee"
+"checksum arrayvec 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9"
+"checksum atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1803c647a3ec87095e7ae7acfca019e98de5ec9a7d01343f611cf3152ed71a90"
+"checksum autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b671c8fb71b457dd4ae18c4ba1e59aa81793daacc361d82fcd410cef0d491875"
+"checksum backtrace 0.3.38 (registry+https://github.com/rust-lang/crates.io-index)" = "690a62be8920ccf773ee00ef0968649b0e724cda8bd5b12286302b4ae955fdf5"
+"checksum backtrace-sys 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)" = "82a830b4ef2d1124a711c71d263c5abdc710ef8e907bd508c88be475cebc422b"
+"checksum base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e"
+"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
+"checksum blake2b_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)" = "5850aeee1552f495dd0250014cf64b82b7c8879a89d83b33bbdace2cc4f63182"
+"checksum block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b"
+"checksum block-padding 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "6d4dc3af3ee2e12f3e5d224e5e1e3d73668abbeb69e566d361f7d5563a4fdf09"
+"checksum byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7"
+"checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5"
+"checksum bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c"
+"checksum c2-chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7d64d04786e0f528460fc884753cf8dddcc466be308f6026f8e355c41a0e4101"
+"checksum cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)" = "4fc9a35e1f4290eb9e5fc54ba6cf40671ed2a2514c3eeb2b2a908dda2ea5a1be"
+"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
+"checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9"
+"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
+"checksum constant_time_eq 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "995a44c877f9212528ccc74b21a232f66ad69001e40ede5bcee2ac9ef2657120"
+"checksum core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "25b9e03f145fd4f2bf705e07b900cd41fc636598fe5dc452fd0db1441c3f496d"
+"checksum core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e7ca8a5221364ef15ce201e8ed2f609fc312682a8f4e0e3d4aa5879764e0fa3b"
+"checksum crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6"
+"checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5"
+"checksum dirs 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3fd78930633bd1c6e35c4b42b1df7b0cbc6bc191146e512bb3bedf243fcc3901"
+"checksum env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "aafcde04e90a5226a6443b7aabdb016ba2f8307c847d524724bd9b346dd1a2d3"
+"checksum failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f8273f13c977665c5db7eb2b99ae520952fe5ac831ae4cd09d80c4c7042b5ed9"
+"checksum failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0bc225b78e0391e4b8683440bf2e63c2deeeb2ce5189eab46e2b68c6d3725d08"
+"checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
+"checksum foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
+"checksum foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
+"checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
+"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
+"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
+"checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec"
+"checksum getrandom 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "473a1265acc8ff1e808cd0a1af8cee3c2ee5200916058a2ca113c29f2d903571"
+"checksum httparse 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9"
+"checksum humantime 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f"
+"checksum idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9"
+"checksum iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e"
+"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
+"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+"checksum lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f"
+"checksum libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)" = "34fcd2c08d2f832f376f4173a231990fa5aef4e99fb569867318a227ef4c06ba"
+"checksum libz-sys 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)" = "2eb5e43362e38e2bca2fd5f5134c4d4564a23a5c28e9b95411652021a8675ebe"
+"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7"
+"checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08"
+"checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e"
+"checksum mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)" = "83f51996a3ed004ef184e16818edc51fadffe8e7ca68be67f9dee67d84d0ff23"
+"checksum mio-extras 2.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "46e73a04c2fa6250b8d802134d56d554a9ec2922bf977777c805ea5def61ce40"
+"checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919"
+"checksum native-tls 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4b2df1a4c22fd44a62147fd8f13dd0f95c9d8ca7b2610299b2a2f9cf8964274e"
+"checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88"
+"checksum nodrop 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb"
+"checksum opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
+"checksum openssl 0.10.25 (registry+https://github.com/rust-lang/crates.io-index)" = "2f372b2b53ce10fb823a337aaa674e3a7d072b957c6264d0f4ff0bd86e657449"
+"checksum openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de"
+"checksum openssl-sys 0.9.51 (registry+https://github.com/rust-lang/crates.io-index)" = "ba24190c8f0805d3bd2ce028f439fe5af1d55882bbe6261bed1dbc93b50dd6b1"
+"checksum percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
+"checksum pkg-config 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)" = "72d5370d90f49f70bd033c3d75e87fc529fbfff9d6f7cccef07d6170079d91ea"
+"checksum ppv-lite86 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e3cbf9f658cdb5000fcf6f362b8ea2ba154b9f146a61c7a20d647034c6b6561b"
+"checksum proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "90cf5f418035b98e655e9cdb225047638296b862b42411c4e45bb88d700f7fc0"
+"checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0"
+"checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe"
+"checksum rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3ae1b169243eaf61759b8475a998f0a385e42042370f3a7dbaf35246eacc8412"
+"checksum rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853"
+"checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
+"checksum rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
+"checksum rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
+"checksum rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
+"checksum rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071"
+"checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
+"checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84"
+"checksum redox_users 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4ecedbca3bf205f8d8f5c2b44d83cd0690e39ee84b951ed649e9f1841132b66d"
+"checksum regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dc220bd33bdce8f093101afe22a037b8eb0e5af33592e6a9caafff0d4cb81cbd"
+"checksum regex-syntax 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "11a7e20d1cce64ef2fed88b66d347f88bd9babb82845b2b858f3edbf59a4f716"
+"checksum remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e"
+"checksum rust-argon2 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4ca4eaef519b494d1f2848fc602d18816fed808a981aedf4f1f00ceb7c9d32cf"
+"checksum rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783"
+"checksum schannel 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "87f550b06b6cba9c8b8be3ee73f391990116bf527450d2556e9b9ce263b9a021"
+"checksum security-framework 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eee63d0f4a9ec776eeb30e220f0bc1e092c3ad744b2a379e3993070364d3adc2"
+"checksum security-framework-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9636f8989cbf61385ae4824b98c1aaa54c994d7d8b41f11c601ed799f0549a56"
+"checksum sha-1 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "23962131a91661d643c98940b20fcaffe62d776a823247be80a48fcb8b6fce68"
+"checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
+"checksum smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "ab606a9c5e214920bb66c458cd7be8ef094f813f20fe77a54cc7dbfff220d4b7"
+"checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
+"checksum syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "66850e97125af79138385e9b88339cbcd037e3f28ceab8c5ad98e64f0f1f80bf"
+"checksum synstructure 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3f085a5855930c0441ca1288cf044ea4aecf4f43a91668abdb870b4ba546a203"
+"checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9"
+"checksum term 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "edd106a334b7657c10b7c540a0106114feadeb4dc314513e97df481d5d966f42"
+"checksum termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "96d6098003bde162e4277c70665bd87c326f5a0c3f3fbfb285787fa482d54e6e"
+"checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
+"checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b"
+"checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f"
+"checksum typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9"
+"checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5"
+"checksum unicode-normalization 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "141339a08b982d942be2ca06ff8b076563cbe223d1befd5450716790d44e2426"
+"checksum unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7007dbd421b92cc6e28410fe7362e2e0a2503394908f417b68ec8d1c364c4e20"
+"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
+"checksum url 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "75b414f6c464c879d7f9babf951f23bc3743fb7313c081b2e6ca719067ea9d61"
+"checksum vcpkg 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "33dd455d0f96e90a75803cfeb7f948768c08d70a6de9a8d2362461935698bf95"
+"checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a"
+"checksum wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b89c3ce4ce14bdc6fb6beaf9ec7928ca331de5df7e5ea278375642a2f478570d"
+"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
+"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6"
+"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
+"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+"checksum winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7168bab6e1daee33b4557efd0e95d5ca70a03706d39fa5f3fe7a236f584b03c9"
+"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+"checksum wincolor 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "96f5016b18804d24db43cebf3c77269e7569b8954a8464501c216cc5e070eaa9"
+"checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e"
diff --git a/third_party/rust/ws/Cargo.toml b/third_party/rust/ws/Cargo.toml
new file mode 100644
index 0000000000..d0ffd58660
--- /dev/null
+++ b/third_party/rust/ws/Cargo.toml
@@ -0,0 +1,84 @@
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# When uploading crates to the registry Cargo will automatically
+# "normalize" Cargo.toml files for maximal compatibility
+# with all versions of Cargo and also rewrite `path` dependencies
+# to registry (e.g., crates.io) dependencies
+#
+# If you believe there's an error in this file please file an
+# issue against the rust-lang/cargo repository. If you're
+# editing this file be aware that the upstream Cargo.toml
+# will likely look very different (and much more reasonable)
+
+[package]
+name = "ws"
+version = "0.9.1"
+authors = ["Jason Housley <HousleyJK@gmail.com>"]
+description = "Lightweight, event-driven WebSockets for Rust."
+documentation = "https://ws-rs.org/docs"
+readme = "README.md"
+keywords = ["websocket", "mio", "event-driven", "io", "web"]
+license = "MIT"
+repository = "https://github.com/housleyjk/ws-rs"
+[dependencies.byteorder]
+version = "1.2.1"
+
+[dependencies.bytes]
+version = "0.4.6"
+
+[dependencies.httparse]
+version = "1.2.4"
+
+[dependencies.libc]
+version = "0.2.40"
+optional = true
+
+[dependencies.libz-sys]
+version = "1.0.18"
+optional = true
+
+[dependencies.log]
+version = "0.4.1"
+
+[dependencies.mio]
+version = "0.6.14"
+
+[dependencies.mio-extras]
+version = "2.0"
+
+[dependencies.native-tls]
+version = "0.2"
+optional = true
+
+[dependencies.openssl]
+version = "0.10"
+optional = true
+
+[dependencies.rand]
+version = "0.7"
+
+[dependencies.sha-1]
+version = "0.8.0"
+
+[dependencies.slab]
+version = "0.4"
+
+[dependencies.url]
+version = "2.0.0"
+[dev-dependencies.clap]
+version = "2.31.2"
+
+[dev-dependencies.env_logger]
+version = "0.6"
+
+[dev-dependencies.term]
+version = "0.5.1"
+
+[dev-dependencies.time]
+version = "0.1.39"
+
+[features]
+default = []
+nativetls = ["native-tls"]
+permessage-deflate = ["libz-sys", "libc"]
+ssl = ["openssl"]
diff --git a/third_party/rust/ws/LICENSE b/third_party/rust/ws/LICENSE
new file mode 100644
index 0000000000..031a702dd5
--- /dev/null
+++ b/third_party/rust/ws/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2016 Jason Housley
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/third_party/rust/ws/README.md b/third_party/rust/ws/README.md
new file mode 100644
index 0000000000..4e1a403421
--- /dev/null
+++ b/third_party/rust/ws/README.md
@@ -0,0 +1,55 @@
+# WS-RS
+
+Lightweight, event-driven WebSockets for [Rust](https://www.rust-lang.org).
+```rust
+
+/// A WebSocket echo server
+listen("127.0.0.1:3012", |out| {
+ move |msg| {
+ out.send(msg)
+ }
+})
+```
+
+Introduction
+------------
+[![Build Status](https://travis-ci.org/housleyjk/ws-rs.svg?branch=stable)](https://travis-ci.org/housleyjk/ws-rs)
+[![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE)
+[![Crate](http://meritbadge.herokuapp.com/ws)](https://crates.io/crates/ws)
+
+**[Homepage](https://ws-rs.org)**
+
+**[API Documentation](https://ws-rs.org/docs)**
+
+This library provides an implementation of WebSockets,
+[RFC6455](https://tools.ietf.org/html/rfc6455) using [MIO](https://github.com/carllerche/mio). It
+allows for handling multiple connections on a single thread, and even spawning new client
+connections on the same thread. This makes for very fast and resource efficient WebSockets. The API
+design abstracts away the menial parts of the WebSocket protocol and allows you to focus on
+application code without worrying about protocol conformance. However, it is also possible to get
+low-level access to individual WebSocket frames if you need to write extensions or want to optimize
+around the WebSocket protocol.
+
+Getting Started
+---------------
+
+For detailed installation and usage instructions, check out the [guide](https://ws-rs.org/guide).
+
+Features
+--------
+
+WS-RS provides a complete implementation of the WebSocket specification. There is also support for
+[ssl](https://ws-rs.org/guide/ssl) and
+[permessage-deflate](https://ws-rs.org/guide/deflate).
+
+Testing
+-------
+
+WS-RS is thoroughly tested and passes the [Autobahn Test Suite](https://crossbar.io/autobahn/) for
+WebSockets, including the tests for `permessage-deflate`. Visit
+[ws-rs.org](https://ws-rs.org/testing/autobahn/results) to view the results of the latest test run.
+
+Contributing
+------------
+
+Please report bugs and make feature requests [here](https://github.com/housleyjk/ws-rs/issues).
diff --git a/third_party/rust/ws/examples/autobahn-client.rs b/third_party/rust/ws/examples/autobahn-client.rs
new file mode 100644
index 0000000000..110592d7a6
--- /dev/null
+++ b/third_party/rust/ws/examples/autobahn-client.rs
@@ -0,0 +1,76 @@
+/// WebSocket client used for testing against the Autobahn Test Suite
+extern crate ws;
+
+use std::cell::Cell;
+use std::rc::Rc;
+use ws::{connect, CloseCode, Message, Result};
+
+#[cfg(feature = "permessage-deflate")]
+use ws::deflate::DeflateHandler;
+
+const AGENT: &str = "WS-RS";
+
+#[cfg(not(feature = "permessage-deflate"))]
+fn main() {
+ let total = get_case_count().unwrap();
+ let mut case_id = 1;
+
+ while case_id <= total {
+ let case_url = format!(
+ "ws://127.0.0.1:9001/runCase?case={}&agent={}",
+ case_id, AGENT
+ );
+
+ connect(case_url, |out| move |msg| out.send(msg)).unwrap();
+
+ case_id += 1
+ }
+
+ update_reports().unwrap();
+}
+
+#[cfg(feature = "permessage-deflate")]
+fn main() {
+ let total = get_case_count().unwrap();
+ let mut case_id = 1;
+
+ while case_id <= total {
+ let case_url = format!(
+ "ws://127.0.0.1:9001/runCase?case={}&agent={}",
+ case_id, AGENT
+ );
+
+ connect(case_url, |out| {
+ DeflateHandler::new(move |msg| out.send(msg))
+ }).unwrap();
+
+ case_id += 1
+ }
+
+ update_reports().unwrap();
+}
+
+fn get_case_count() -> Result<u32> {
+ // sadly we need to use a Cell because we need to set the total, and RC is immutable
+ let total = Rc::new(Cell::new(0));
+
+ connect("ws://127.0.0.1:9001/getCaseCount", |out| {
+ let my_total = total.clone();
+
+ move |msg: Message| {
+ let count = msg.as_text()?;
+
+ my_total.set(count.parse::<u32>().unwrap());
+
+ out.close(CloseCode::Normal)
+ }
+ })?;
+
+ Ok(total.get())
+}
+
+fn update_reports() -> Result<()> {
+ let report_url = format!("ws://127.0.0.1:9001/updateReports?agent={}", AGENT);
+
+ connect(report_url, |out| move |_| out.close(CloseCode::Normal))
+}
diff --git a/third_party/rust/ws/examples/autobahn-server.rs b/third_party/rust/ws/examples/autobahn-server.rs
new file mode 100644
index 0000000000..c0a9a0009b
--- /dev/null
+++ b/third_party/rust/ws/examples/autobahn-server.rs
@@ -0,0 +1,25 @@
+extern crate env_logger;
+/// WebSocket server used for testing against the Autobahn Test Suite. This is basically the server
+/// example without printing output or comments.
+extern crate ws;
+
+#[cfg(feature = "permessage-deflate")]
+use ws::deflate::DeflateHandler;
+
+#[cfg(not(feature = "permessage-deflate"))]
+fn main() {
+ env_logger::init();
+
+ ws::listen("127.0.0.1:3012", |out| {
+ move |msg| out.send(msg)
+ }).unwrap()
+}
+
+#[cfg(feature = "permessage-deflate")]
+fn main() {
+ env_logger::init();
+
+ ws::listen("127.0.0.1:3012", |out| {
+ DeflateHandler::new(move |msg| out.send(msg))
+ }).unwrap();
+}
diff --git a/third_party/rust/ws/examples/bench-server.rs b/third_party/rust/ws/examples/bench-server.rs
new file mode 100644
index 0000000000..bccfe66fb6
--- /dev/null
+++ b/third_party/rust/ws/examples/bench-server.rs
@@ -0,0 +1,16 @@
+/// WebSocket server used for testing the bench example.
+extern crate ws;
+
+use ws::{Builder, Sender, Settings};
+
+fn main() {
+ Builder::new()
+ .with_settings(Settings {
+ max_connections: 10_000,
+ ..Settings::default()
+ })
+ .build(|out: Sender| move |msg| out.send(msg))
+ .unwrap()
+ .listen("127.0.0.1:3012")
+ .unwrap();
+}
diff --git a/third_party/rust/ws/examples/bench.rs b/third_party/rust/ws/examples/bench.rs
new file mode 100644
index 0000000000..427f84147f
--- /dev/null
+++ b/third_party/rust/ws/examples/bench.rs
@@ -0,0 +1,79 @@
+extern crate env_logger;
+extern crate time;
+extern crate url;
+/// A simple, but immature, benchmark client for destroying other WebSocket frameworks and proving
+/// WS-RS's performance excellence. ;)
+/// Make sure you allow for enough connections in your OS (e.g. ulimit -Sn 10000).
+extern crate ws;
+
+// Try this against node for some fun
+
+// TODO: Separate this example into a separate lib
+// TODO: num threads, num connections per thread, num concurrent connections per thread, num
+// messages per connection, length of message, text or binary
+
+use ws::{Builder, CloseCode, Handler, Handshake, Message, Result, Sender, Settings};
+
+const CONNECTIONS: usize = 10_000; // simultaneous
+const MESSAGES: u32 = 10;
+static MESSAGE: &'static str = "TEST TEST TEST TEST TEST TEST TEST TEST";
+
+fn main() {
+ env_logger::init();
+
+ let url = url::Url::parse("ws://127.0.0.1:3012").unwrap();
+
+ struct Connection {
+ out: Sender,
+ count: u32,
+ time: u64,
+ total: u64,
+ }
+
+ impl Handler for Connection {
+ fn on_open(&mut self, _: Handshake) -> Result<()> {
+ self.out.send(MESSAGE)?;
+ self.count += 1;
+ self.time = time::precise_time_ns();
+ Ok(())
+ }
+
+ fn on_message(&mut self, msg: Message) -> Result<()> {
+ assert_eq!(msg.as_text().unwrap(), MESSAGE);
+ if self.count > MESSAGES {
+ self.out.close(CloseCode::Normal)?;
+ } else {
+ self.out.send(MESSAGE)?;
+ let time = time::precise_time_ns();
+ // println!("time {}", time -self.time);
+ self.total += time - self.time;
+ self.count += 1;
+ self.time = time;
+ }
+ Ok(())
+ }
+ }
+
+ let mut ws = Builder::new()
+ .with_settings(Settings {
+ max_connections: CONNECTIONS,
+ ..Settings::default()
+ })
+ .build(|out| Connection {
+ out,
+ count: 0,
+ time: 0,
+ total: 0,
+ })
+ .unwrap();
+
+ for _ in 0..CONNECTIONS {
+ ws.connect(url.clone()).unwrap();
+ }
+ let start = time::precise_time_ns();
+ ws.run().unwrap();
+ println!(
+ "Total time. {}",
+ (time::precise_time_ns() - start) / 1_000_000
+ )
+}
diff --git a/third_party/rust/ws/examples/channel.rs b/third_party/rust/ws/examples/channel.rs
new file mode 100644
index 0000000000..fb21901297
--- /dev/null
+++ b/third_party/rust/ws/examples/channel.rs
@@ -0,0 +1,156 @@
+extern crate env_logger;
+/// An example of using channels to transfer data between three parts of some system.
+///
+/// A WebSocket server echoes data back to a client and tees that data to a logging system.
+/// A WebSocket client sends some data do the server.
+/// A worker thread stores data as a log and sends that data back to the main program when the
+/// WebSocket server has finished receiving data.
+///
+/// This example demonstrates how to use threads, channels, and WebSocket handlers to create a
+/// complex system from simple, composable parts.
+extern crate ws;
+
+use std::sync::mpsc::Sender as ThreadOut;
+use std::sync::mpsc::channel;
+use std::thread;
+use std::thread::sleep;
+use std::time::Duration;
+
+use ws::{connect, listen, CloseCode, Handler, Handshake, Message, Result, Sender};
+
+fn main() {
+ // Setup logging
+ env_logger::init();
+
+ // Data to be sent across WebSockets and channels
+ let data = vec![1, 2, 3, 4, 5];
+ let (final_in, final_out) = channel();
+ let (log_in, log_out) = channel();
+
+ // WebSocket connection handler for the server connection
+ struct Server {
+ ws: Sender,
+ log: ThreadOut<String>,
+ }
+
+ impl Handler for Server {
+ fn on_message(&mut self, msg: Message) -> Result<()> {
+ println!("Server got message '{}'. ", msg);
+
+ // log it
+ self.log.send(msg.to_string()).unwrap();
+
+ // echo it back
+ self.ws.send(msg)
+ }
+
+ fn on_close(&mut self, _: CloseCode, _: &str) {
+ self.ws.shutdown().unwrap()
+ }
+ }
+
+ // Server thread
+ let server = thread::Builder::new()
+ .name("server".to_owned())
+ .spawn(move || {
+ listen("127.0.0.1:3012", |out| {
+ Server {
+ ws: out,
+ // we need to clone the channel because
+ // in theory, there could be many active connections
+ log: log_in.clone(),
+ }
+ }).unwrap()
+ })
+ .unwrap();
+
+ // Give the server a little time to get going
+ sleep(Duration::from_millis(10));
+
+ // WebSocket connection handler for the client connection
+ struct Client {
+ out: Sender,
+ ind: usize,
+ data: Vec<u32>,
+ }
+
+ impl Client {
+ // Core business logic for client, keeping it DRY
+ fn increment(&mut self) -> Result<()> {
+ if let Some(num) = self.data.get(self.ind) {
+ // Advance the index
+ self.ind += 1;
+
+ // Send the number to the server
+ self.out.send(num.to_string())
+ } else {
+ // All of the data has been sent, let's close
+ self.out.close(CloseCode::Normal)
+ }
+ }
+ }
+
+ impl Handler for Client {
+ fn on_open(&mut self, _: Handshake) -> Result<()> {
+ self.increment()
+ }
+
+ fn on_message(&mut self, msg: Message) -> Result<()> {
+ println!("Client got message '{}'. ", msg);
+ self.increment()
+ }
+ }
+
+ // We need to clone the data into the client, making two versions we will compare for
+ // consistency later
+ let client_data = data.clone();
+
+ // Client thread
+ let client = thread::Builder::new()
+ .name("client".to_owned())
+ .spawn(move || {
+ connect("ws://127.0.0.1:3012", |out| {
+ Client {
+ out,
+ ind: 0,
+ // we need to clone again because
+ // in theory, there could be many client connections sending off the data
+ data: client_data.clone(),
+ }
+ }).unwrap()
+ })
+ .unwrap();
+
+ // Logger thread
+ let logger = thread::Builder::new()
+ .name("logger".to_owned())
+ .spawn(move || {
+ // Make a new vector to store the numbers
+ let mut log: Vec<u32> = Vec::new();
+
+ // Receive data and push it to the log, this only works if we have one WebSocket
+ // connection, otherwise the log would have data from all connections. But for our example,
+ // we know we only have one :)
+ while let Ok(string) = log_out.recv() {
+ println!("Logger is storing {}", string);
+ log.push(string.parse().unwrap());
+ }
+
+ println!("Logger sending final log result.");
+ final_in.send(log).unwrap();
+ })
+ .unwrap();
+
+ // Wait for the worker threads to finish what they are doing
+ let _ = server.join();
+ let _ = client.join();
+ let _ = logger.join();
+
+ // Get the result from the logger and check that it is correct
+ let final_data = final_out.recv().unwrap();
+ println!("In: {:?}", data);
+ println!("Out: {:?}", final_data);
+ assert_eq!(final_data, data);
+
+ println!("All done.")
+}
diff --git a/third_party/rust/ws/examples/cli.rs b/third_party/rust/ws/examples/cli.rs
new file mode 100644
index 0000000000..80ebf8cc6b
--- /dev/null
+++ b/third_party/rust/ws/examples/cli.rs
@@ -0,0 +1,180 @@
+extern crate clap;
+extern crate env_logger;
+extern crate term;
+/// Run this cli like this:
+/// cargo run --example server
+/// cargo run --example cli -- ws://127.0.0.1:3012
+extern crate ws;
+
+use std::io;
+use std::io::prelude::*;
+use std::sync::mpsc::Sender as TSender;
+use std::sync::mpsc::channel;
+use std::thread;
+
+use clap::{App, Arg};
+use ws::{connect, CloseCode, Error, ErrorKind, Handler, Handshake, Message, Result, Sender};
+
+fn main() {
+ // Setup logging
+ env_logger::init();
+
+ // setup command line arguments
+ let matches = App::new("WS Command Line Client")
+ .version("1.1")
+ .author("Jason Housley <housleyjk@gmail.com>")
+ .about("Connect to a WebSocket and send messages from the command line.")
+ .arg(
+ Arg::with_name("URL")
+ .help("The URL of the WebSocket server.")
+ .required(true)
+ .index(1),
+ )
+ .get_matches();
+
+ let url = matches.value_of("URL").unwrap().to_string();
+
+ let (tx, rx) = channel();
+
+ // Run client thread with channel to give it's WebSocket message sender back to us
+ let client = thread::spawn(move || {
+ println!("Connecting to {}", url);
+ connect(url, |sender| Client {
+ ws_out: sender,
+ thread_out: tx.clone(),
+ }).unwrap();
+ });
+
+ if let Ok(Event::Connect(sender)) = rx.recv() {
+ // If we were able to connect, print the instructions
+ instructions();
+
+ // Main loop
+ loop {
+ // Get user input
+ let mut input = String::new();
+ io::stdin().read_line(&mut input).unwrap();
+
+ if let Ok(Event::Disconnect) = rx.try_recv() {
+ break;
+ }
+
+ if input.starts_with("/h") {
+ // Show help
+ instructions()
+ } else if input.starts_with("/c") {
+ // If the close arguments are good, close the connection
+ let args: Vec<&str> = input.split(' ').collect();
+ if args.len() == 1 {
+ // Simple close
+ println!("Closing normally, please wait...");
+ sender.close(CloseCode::Normal).unwrap();
+ } else if args.len() == 2 {
+ // Close with a specific code
+ if let Ok(code) = args[1].trim().parse::<u16>() {
+ let code = CloseCode::from(code);
+ println!("Closing with code: {:?}, please wait...", code);
+ sender.close(code).unwrap();
+ } else {
+ display(&format!("Unable to parse {} as close code.", args[1]));
+ // Keep accepting input if the close arguments are invalid
+ continue;
+ }
+ } else {
+ // Close with a code and a reason
+ if let Ok(code) = args[1].trim().parse::<u16>() {
+ let code = CloseCode::from(code);
+ let reason = args[2..].join(" ");
+ println!(
+ "Closing with code: {:?} and reason: {}, please wait...",
+ code,
+ reason.trim()
+ );
+ sender
+ .close_with_reason(code, reason.trim().to_string())
+ .unwrap();
+ } else {
+ display(&format!("Unable to parse {} as close code.", args[1]));
+ // Keep accepting input if the close arguments are invalid
+ continue;
+ }
+ }
+ break;
+ } else {
+ // Send the message
+ display(&format!(">>> {}", input.trim()));
+ sender.send(input.trim()).unwrap();
+ }
+ }
+ }
+
+ // Ensure the client has a chance to finish up
+ client.join().unwrap();
+}
+
+fn display(string: &str) {
+ let mut view = term::stdout().unwrap();
+ view.carriage_return().unwrap();
+ view.delete_line().unwrap();
+ println!("{}", string);
+ print!("?> ");
+ io::stdout().flush().unwrap();
+}
+
+fn instructions() {
+ println!("Type /close [code] [reason] to close the connection.");
+ println!("Type /help to show these instructions.");
+ println!("Other input will be sent as messages.\n");
+ print!("?> ");
+ io::stdout().flush().unwrap();
+}
+
+struct Client {
+ ws_out: Sender,
+ thread_out: TSender<Event>,
+}
+
+impl Handler for Client {
+ fn on_open(&mut self, _: Handshake) -> Result<()> {
+ self.thread_out
+ .send(Event::Connect(self.ws_out.clone()))
+ .map_err(|err| {
+ Error::new(
+ ErrorKind::Internal,
+ format!("Unable to communicate between threads: {:?}.", err),
+ )
+ })
+ }
+
+ fn on_message(&mut self, msg: Message) -> Result<()> {
+ display(&format!("<<< {}", msg));
+ Ok(())
+ }
+
+ fn on_close(&mut self, code: CloseCode, reason: &str) {
+ if reason.is_empty() {
+ display(&format!(
+ "<<< Closing<({:?})>\nHit any key to end session.",
+ code
+ ));
+ } else {
+ display(&format!(
+ "<<< Closing<({:?}) {}>\nHit any key to end session.",
+ code, reason
+ ));
+ }
+
+ if let Err(err) = self.thread_out.send(Event::Disconnect) {
+ display(&format!("{:?}", err))
+ }
+ }
+
+ fn on_error(&mut self, err: Error) {
+ display(&format!("<<< Error<{:?}>", err))
+ }
+}
+
+enum Event {
+ Connect(Sender),
+ Disconnect,
+}
diff --git a/third_party/rust/ws/examples/client.rs b/third_party/rust/ws/examples/client.rs
new file mode 100644
index 0000000000..5390af3a23
--- /dev/null
+++ b/third_party/rust/ws/examples/client.rs
@@ -0,0 +1,33 @@
+extern crate env_logger;
+/// Simple WebSocket client with error handling. It is not necessary to setup logging, but doing
+/// so will allow you to see more details about the connection by using the RUST_LOG env variable.
+extern crate ws;
+
+use ws::{connect, CloseCode};
+
+fn main() {
+ // Setup logging
+ env_logger::init();
+
+ // Connect to the url and call the closure
+ if let Err(error) = connect("ws://127.0.0.1:3012", |out| {
+ // Queue a message to be sent when the WebSocket is open
+ if out.send("Hello WebSocket").is_err() {
+ println!("Websocket couldn't queue an initial message.")
+ } else {
+ println!("Client sent message 'Hello WebSocket'. ")
+ }
+
+ // The handler needs to take ownership of out, so we use move
+ move |msg| {
+ // Handle messages received on this connection
+ println!("Client got message '{}'. ", msg);
+
+ // Close the connection
+ out.close(CloseCode::Normal)
+ }
+ }) {
+ // Inform the user of failure
+ println!("Failed to create WebSocket due to: {:?}", error);
+ }
+}
diff --git a/third_party/rust/ws/examples/external_shutdown.rs b/third_party/rust/ws/examples/external_shutdown.rs
new file mode 100644
index 0000000000..9e6f009ecd
--- /dev/null
+++ b/third_party/rust/ws/examples/external_shutdown.rs
@@ -0,0 +1,40 @@
+extern crate ws;
+
+use std::sync::mpsc::channel;
+use std::thread;
+use std::time::Duration;
+
+fn main() {
+ let (tx, rx) = channel();
+
+ let socket = ws::Builder::new()
+ .build(move |out: ws::Sender| {
+ // When we get a connection, send a handle to the parent thread
+ tx.send(out).unwrap();
+
+ // Dummy message handler
+ move |_| {
+ println!("Message handler called.");
+ Ok(())
+ }
+ })
+ .unwrap();
+
+ let handle = socket.broadcaster();
+
+ let t = thread::spawn(move || {
+ socket.listen("127.0.0.1:3012").unwrap();
+ });
+
+ // Wait for 5 seconds only for incoming connections;
+ thread::sleep(Duration::from_millis(5000));
+
+ if rx.try_recv().is_err() {
+ // shutdown the server from the outside
+ handle.shutdown().unwrap();
+ println!("Shutting down server because no connections were established.");
+ }
+
+ // Let the server finish up (whether it's waiting for new connections or going down)
+ t.join().unwrap();
+}
diff --git a/third_party/rust/ws/examples/html_chat.rs b/third_party/rust/ws/examples/html_chat.rs
new file mode 100644
index 0000000000..7b7059b83a
--- /dev/null
+++ b/third_party/rust/ws/examples/html_chat.rs
@@ -0,0 +1,66 @@
+/// An example of a chat web application server
+extern crate ws;
+use ws::{listen, Handler, Message, Request, Response, Result, Sender};
+
+// This can be read from a file
+static INDEX_HTML: &'static [u8] = br#"
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ </head>
+ <body>
+ <pre id="messages"></pre>
+ <form id="form">
+ <input type="text" id="msg">
+ <input type="submit" value="Send">
+ </form>
+ <script>
+ var socket = new WebSocket("ws://" + window.location.host + "/ws");
+ socket.onmessage = function (event) {
+ var messages = document.getElementById("messages");
+ messages.append(event.data + "\n");
+ };
+ var form = document.getElementById("form");
+ form.addEventListener('submit', function (event) {
+ event.preventDefault();
+ var input = document.getElementById("msg");
+ socket.send(input.value);
+ input.value = "";
+ });
+ </script>
+ </body>
+</html>
+ "#;
+
+// Server web application handler
+struct Server {
+ out: Sender,
+}
+
+impl Handler for Server {
+ //
+ fn on_request(&mut self, req: &Request) -> Result<(Response)> {
+ // Using multiple handlers is better (see router example)
+ match req.resource() {
+ // The default trait implementation
+ "/ws" => Response::from_request(req),
+
+ // Create a custom response
+ "/" => Ok(Response::new(200, "OK", INDEX_HTML.to_vec())),
+
+ _ => Ok(Response::new(404, "Not Found", b"404 - Not Found".to_vec())),
+ }
+ }
+
+ // Handle messages recieved in the websocket (in this case, only on /ws)
+ fn on_message(&mut self, msg: Message) -> Result<()> {
+ // Broadcast to all connections
+ self.out.broadcast(msg)
+ }
+}
+
+fn main() {
+ // Listen on an address and call the closure for each connection
+ listen("127.0.0.1:8000", |out| Server { out }).unwrap()
+}
diff --git a/third_party/rust/ws/examples/peer2peer.rs b/third_party/rust/ws/examples/peer2peer.rs
new file mode 100644
index 0000000000..076c3ac757
--- /dev/null
+++ b/third_party/rust/ws/examples/peer2peer.rs
@@ -0,0 +1,96 @@
+extern crate clap;
+extern crate env_logger;
+extern crate url;
+/// An example of a client-server-agnostic WebSocket that takes input from stdin and sends that
+/// input to all other peers.
+///
+/// For example, to create a network like this:
+///
+/// 3013 ---- 3012 ---- 3014
+/// \ |
+/// \ |
+/// \ |
+/// \ |
+/// \ |
+/// \ |
+/// \ |
+/// 3015
+///
+/// Run these commands in separate processes
+/// ./peer2peer
+/// ./peer2peer --server localhost:3013 ws://localhost:3012
+/// ./peer2peer --server localhost:3014 ws://localhost:3012
+/// ./peer2peer --server localhost:3015 ws://localhost:3012 ws://localhost:3013
+///
+/// Stdin on 3012 will be sent to all other peers
+/// Stdin on 3013 will be sent to 3012 and 3015
+/// Stdin on 3014 will be sent to 3012 only
+/// Stdin on 3015 will be sent to 3012 and 2013
+extern crate ws;
+#[macro_use]
+extern crate log;
+
+use std::io;
+use std::io::prelude::*;
+use std::thread;
+
+use clap::{App, Arg};
+
+fn main() {
+ // Setup logging
+ env_logger::init();
+
+ // Parse command line arguments
+ let matches = App::new("Simple Peer 2 Peer")
+ .version("1.0")
+ .author("Jason Housley <housleyjk@gmail.com>")
+ .about("Connect to other peers and listen for incoming connections.")
+ .arg(
+ Arg::with_name("server")
+ .short("s")
+ .long("server")
+ .value_name("SERVER")
+ .help("Set the address to listen for new connections."),
+ )
+ .arg(
+ Arg::with_name("PEER")
+ .help("A WebSocket URL to attempt to connect to at start.")
+ .multiple(true),
+ )
+ .get_matches();
+
+ // Get address of this peer
+ let my_addr = matches.value_of("server").unwrap_or("localhost:3012");
+
+ // Create simple websocket that just prints out messages
+ let mut me = ws::WebSocket::new(|_| {
+ move |msg| {
+ info!("Peer {} got message: {}", my_addr, msg);
+ Ok(())
+ }
+ }).unwrap();
+
+ // Get a sender for ALL connections to the websocket
+ let broacaster = me.broadcaster();
+
+ // Setup thread for listening to stdin and sending messages to connections
+ let input = thread::spawn(move || {
+ let stdin = io::stdin();
+ for line in stdin.lock().lines() {
+ // Send a message to all connections regardless of
+ // how those connections were established
+ broacaster.send(line.unwrap()).unwrap();
+ }
+ });
+
+ // Connect to any existing peers specified on the cli
+ if let Some(peers) = matches.values_of("PEER") {
+ for peer in peers {
+ me.connect(url::Url::parse(peer).unwrap()).unwrap();
+ }
+ }
+
+ // Run the websocket
+ me.listen(my_addr).unwrap();
+ input.join().unwrap();
+}
diff --git a/third_party/rust/ws/examples/pong.rs b/third_party/rust/ws/examples/pong.rs
new file mode 100644
index 0000000000..5c9c62c2e0
--- /dev/null
+++ b/third_party/rust/ws/examples/pong.rs
@@ -0,0 +1,130 @@
+extern crate env_logger;
+extern crate mio_extras;
+extern crate time;
+/// An example demonstrating how to send and recieve a custom ping/pong frame.
+extern crate ws;
+
+use std::str::from_utf8;
+
+use mio_extras::timer::Timeout;
+
+use ws::util::Token;
+use ws::{listen, CloseCode, Error, ErrorKind, Frame, Handler, Handshake, Message, OpCode, Result,
+ Sender};
+
+const PING: Token = Token(1);
+const EXPIRE: Token = Token(2);
+
+fn main() {
+ // Setup logging
+ env_logger::init();
+
+ // Run the WebSocket
+ listen("127.0.0.1:3012", |out| Server {
+ out,
+ ping_timeout: None,
+ expire_timeout: None,
+ }).unwrap();
+}
+
+// Server WebSocket handler
+struct Server {
+ out: Sender,
+ ping_timeout: Option<Timeout>,
+ expire_timeout: Option<Timeout>,
+}
+
+impl Handler for Server {
+ fn on_open(&mut self, _: Handshake) -> Result<()> {
+ // schedule a timeout to send a ping every 5 seconds
+ self.out.timeout(5_000, PING)?;
+ // schedule a timeout to close the connection if there is no activity for 30 seconds
+ self.out.timeout(30_000, EXPIRE)
+ }
+
+ fn on_message(&mut self, msg: Message) -> Result<()> {
+ println!("Server got message '{}'. ", msg);
+ self.out.send(msg)
+ }
+
+ fn on_close(&mut self, code: CloseCode, reason: &str) {
+ println!("WebSocket closing for ({:?}) {}", code, reason);
+
+ // NOTE: This code demonstrates cleaning up timeouts
+ if let Some(t) = self.ping_timeout.take() {
+ self.out.cancel(t).unwrap();
+ }
+ if let Some(t) = self.expire_timeout.take() {
+ self.out.cancel(t).unwrap();
+ }
+
+ println!("Shutting down server after first connection closes.");
+ self.out.shutdown().unwrap();
+ }
+
+ fn on_error(&mut self, err: Error) {
+ // Shutdown on any error
+ println!("Shutting down server for error: {}", err);
+ self.out.shutdown().unwrap();
+ }
+
+ fn on_timeout(&mut self, event: Token) -> Result<()> {
+ match event {
+ // PING timeout has occured, send a ping and reschedule
+ PING => {
+ self.out.ping(time::precise_time_ns().to_string().into())?;
+ self.ping_timeout.take();
+ self.out.timeout(5_000, PING)
+ }
+ // EXPIRE timeout has occured, this means that the connection is inactive, let's close
+ EXPIRE => self.out.close(CloseCode::Away),
+ // No other timeouts are possible
+ _ => Err(Error::new(
+ ErrorKind::Internal,
+ "Invalid timeout token encountered!",
+ )),
+ }
+ }
+
+ fn on_new_timeout(&mut self, event: Token, timeout: Timeout) -> Result<()> {
+ // Cancel the old timeout and replace.
+ if event == EXPIRE {
+ if let Some(t) = self.expire_timeout.take() {
+ self.out.cancel(t)?
+ }
+ self.expire_timeout = Some(timeout)
+ } else {
+ // This ensures there is only one ping timeout at a time
+ if let Some(t) = self.ping_timeout.take() {
+ self.out.cancel(t)?
+ }
+ self.ping_timeout = Some(timeout)
+ }
+
+ Ok(())
+ }
+
+ fn on_frame(&mut self, frame: Frame) -> Result<Option<Frame>> {
+ // If the frame is a pong, print the round-trip time.
+ // The pong should contain data from out ping, but it isn't guaranteed to.
+ if frame.opcode() == OpCode::Pong {
+ if let Ok(pong) = from_utf8(frame.payload())?.parse::<u64>() {
+ let now = time::precise_time_ns();
+ println!("RTT is {:.3}ms.", (now - pong) as f64 / 1_000_000f64);
+ } else {
+ println!("Received bad pong.");
+ }
+ }
+
+ // Some activity has occured, so reset the expiration
+ self.out.timeout(30_000, EXPIRE)?;
+
+ // Run default frame validation
+ DefaultHandler.on_frame(frame)
+ }
+}
+
+// For accessing the default handler implementation
+struct DefaultHandler;
+
+impl Handler for DefaultHandler {}
diff --git a/third_party/rust/ws/examples/remote_addr.rs b/third_party/rust/ws/examples/remote_addr.rs
new file mode 100644
index 0000000000..d32e17af25
--- /dev/null
+++ b/third_party/rust/ws/examples/remote_addr.rs
@@ -0,0 +1,25 @@
+/// Example showing how to obtain the ip address of the client, where possible.
+extern crate ws;
+
+struct Server {
+ ws: ws::Sender,
+}
+
+impl ws::Handler for Server {
+ fn on_open(&mut self, shake: ws::Handshake) -> ws::Result<()> {
+ if let Some(ip_addr) = shake.remote_addr()? {
+ println!("Connection opened from {}.", ip_addr)
+ } else {
+ println!("Unable to obtain client's IP address.")
+ }
+ Ok(())
+ }
+
+ fn on_message(&mut self, _: ws::Message) -> ws::Result<()> {
+ self.ws.close(ws::CloseCode::Normal)
+ }
+}
+
+fn main() {
+ ws::listen("127.0.0.1:3012", |out| Server { ws: out }).unwrap()
+}
diff --git a/third_party/rust/ws/examples/router.rs b/third_party/rust/ws/examples/router.rs
new file mode 100644
index 0000000000..8e699298e9
--- /dev/null
+++ b/third_party/rust/ws/examples/router.rs
@@ -0,0 +1,141 @@
+extern crate env_logger;
+/// WebSocket server using trait objects to route
+/// to an infinitely extensible number of handlers
+extern crate ws;
+
+// A WebSocket handler that routes connections to different boxed handlers by resource
+struct Router {
+ sender: ws::Sender,
+ inner: Box<ws::Handler>,
+}
+
+impl ws::Handler for Router {
+ fn on_request(&mut self, req: &ws::Request) -> ws::Result<(ws::Response)> {
+ // Clone the sender so that we can move it into the child handler
+ let out = self.sender.clone();
+
+ match req.resource() {
+ "/echo" => self.inner = Box::new(Echo { ws: out }),
+
+ // Route to a data handler
+ "/data/one" => {
+ self.inner = Box::new(Data {
+ ws: out,
+ data: vec!["one", "two", "three", "four", "five"],
+ })
+ }
+
+ // Route to another data handler
+ "/data/two" => {
+ self.inner = Box::new(Data {
+ ws: out,
+ data: vec!["いち", "二", "さん", "四", "ご"],
+ })
+ }
+
+ // Use a closure as the child handler
+ "/closure" => {
+ self.inner = Box::new(move |msg: ws::Message| {
+ println!("Got a message on a closure handler: {}", msg);
+ out.close_with_reason(ws::CloseCode::Error, "Not Implemented.")
+ })
+ }
+
+ // Use the default child handler, NotFound
+ _ => (),
+ }
+
+ // Delegate to the child handler
+ self.inner.on_request(req)
+ }
+
+ // Pass through any other methods that should be delegated to the child.
+ //
+ // You could probably use a macro for this if you have many different
+ // routers or were building some sort of routing framework.
+
+ fn on_shutdown(&mut self) {
+ self.inner.on_shutdown()
+ }
+
+ fn on_open(&mut self, shake: ws::Handshake) -> ws::Result<()> {
+ self.inner.on_open(shake)
+ }
+
+ fn on_message(&mut self, msg: ws::Message) -> ws::Result<()> {
+ self.inner.on_message(msg)
+ }
+
+ fn on_close(&mut self, code: ws::CloseCode, reason: &str) {
+ self.inner.on_close(code, reason)
+ }
+
+ fn on_error(&mut self, err: ws::Error) {
+ self.inner.on_error(err);
+ }
+}
+
+// This handler returns a 404 response to all handshake requests
+struct NotFound;
+
+impl ws::Handler for NotFound {
+ fn on_request(&mut self, req: &ws::Request) -> ws::Result<(ws::Response)> {
+ // This handler responds to all requests with a 404
+ let mut res = ws::Response::from_request(req)?;
+ res.set_status(404);
+ res.set_reason("Not Found");
+ Ok(res)
+ }
+}
+
+// This handler simply echoes all messages back to the client
+struct Echo {
+ ws: ws::Sender,
+}
+
+impl ws::Handler for Echo {
+ fn on_message(&mut self, msg: ws::Message) -> ws::Result<()> {
+ println!("Echo handler received a message: {}", msg);
+ self.ws.send(msg)
+ }
+}
+
+// This handler sends some data to the client and then terminates the connection on the first
+// message received, presumably confirming receipt of the data
+struct Data {
+ ws: ws::Sender,
+ data: Vec<&'static str>,
+}
+
+impl ws::Handler for Data {
+ fn on_open(&mut self, _: ws::Handshake) -> ws::Result<()> {
+ for msg in &self.data {
+ self.ws.send(*msg)?
+ }
+ Ok(())
+ }
+
+ fn on_message(&mut self, msg: ws::Message) -> ws::Result<()> {
+ println!("Data handler received a message: {}", msg);
+ println!("Data handler going down.");
+ self.ws.close(ws::CloseCode::Normal)
+ }
+}
+
+fn main() {
+ env_logger::init();
+
+ // Listen on an address and call the closure for each connection
+ if let Err(error) = ws::listen("127.0.0.1:3012", |out| {
+ // Use our router as the handler to route the new connection
+ Router {
+ sender: out,
+ // Default to returning a 404 when the route doesn't match.
+ // You could default to any handler here.
+ inner: Box::new(NotFound),
+ }
+ }) {
+ // Inform the user of failure
+ println!("Failed to create WebSocket due to {:?}", error);
+ }
+}
diff --git a/third_party/rust/ws/examples/server.rs b/third_party/rust/ws/examples/server.rs
new file mode 100644
index 0000000000..fd9f5ae671
--- /dev/null
+++ b/third_party/rust/ws/examples/server.rs
@@ -0,0 +1,26 @@
+extern crate env_logger;
+/// Simple WebSocket server with error handling. It is not necessary to setup logging, but doing
+/// so will allow you to see more details about the connection by using the RUST_LOG env variable.
+extern crate ws;
+
+use ws::listen;
+
+fn main() {
+ // Setup logging
+ env_logger::init();
+
+ // Listen on an address and call the closure for each connection
+ if let Err(error) = listen("127.0.0.1:3012", |out| {
+ // The handler needs to take ownership of out, so we use move
+ move |msg| {
+ // Handle messages received on this connection
+ println!("Server got message '{}'. ", msg);
+
+ // Use the out channel to send messages back
+ out.send(msg)
+ }
+ }) {
+ // Inform the user of failure
+ println!("Failed to create WebSocket due to {:?}", error);
+ }
+}
diff --git a/third_party/rust/ws/examples/shared.rs b/third_party/rust/ws/examples/shared.rs
new file mode 100644
index 0000000000..e466f5ed46
--- /dev/null
+++ b/third_party/rust/ws/examples/shared.rs
@@ -0,0 +1,58 @@
+extern crate env_logger;
+extern crate url;
+/// A single-threaded client + server example showing how flexible closure handlers can be for
+/// trivial applications.
+extern crate ws;
+
+use ws::{Sender, WebSocket};
+
+fn main() {
+ // Setup logging
+ env_logger::init();
+
+ // A variable to distinguish the two halves
+ let mut name = "Client";
+
+ // Create a WebSocket with a closure as the factory
+ let mut ws = WebSocket::new(|output: Sender| {
+ // The first connection is named Client
+ if name == "Client" {
+ println!("{} sending 'Hello Websocket' ", name);
+ output.send("Hello Websocket").unwrap();
+ }
+
+ // The closure handler needs to take ownership of output
+ let handler = move |msg| {
+ println!("{} got '{}' ", name, msg);
+
+ // If we are the server,
+ if name == "Server" {
+ println!("{} sending 'How are you?' ", name);
+
+ // send the message back
+ output.send("How are you?")
+ } else {
+ // otherwise, we are the client and will shutdown the WebSocket
+ output.shutdown()
+ }
+ };
+
+ // The next connection this factory makes will be named Server
+ name = "Server";
+
+ // We must return the handler
+ handler
+ }).unwrap();
+
+ // Url for the client
+ let url = url::Url::parse("ws://127.0.0.1:3012").unwrap();
+
+ // Queue a WebSocket connection to the url
+ ws.connect(url).unwrap();
+
+ // Start listening for incoming conections
+ ws.listen("127.0.0.1:3012").unwrap();
+
+ // The WebSocket has shutdown
+ println!("All done.")
+}
diff --git a/third_party/rust/ws/examples/ssl-server.rs b/third_party/rust/ws/examples/ssl-server.rs
new file mode 100644
index 0000000000..ca788513e7
--- /dev/null
+++ b/third_party/rust/ws/examples/ssl-server.rs
@@ -0,0 +1,122 @@
+extern crate clap;
+extern crate env_logger;
+#[cfg(feature = "ssl")]
+extern crate openssl;
+/// WebSocket server to demonstrate ssl encryption within an a websocket server.
+///
+/// The resulting executable takes three arguments:
+/// ADDR - The address to listen for incoming connections (e.g. 127.0.0:3012)
+/// CERT - The path to the cert PEM (e.g. snakeoil.crt)
+/// KEY - The path to the key PEM (e.g. snakeoil.key)
+///
+/// For more details concerning setting up the SSL context, see rust-openssl docs.
+extern crate ws;
+
+#[cfg(feature = "ssl")]
+use std::fs::File;
+#[cfg(feature = "ssl")]
+use std::io::Read;
+#[cfg(feature = "ssl")]
+use std::rc::Rc;
+
+#[cfg(feature = "ssl")]
+use openssl::pkey::PKey;
+#[cfg(feature = "ssl")]
+use openssl::ssl::{SslAcceptor, SslMethod, SslStream};
+#[cfg(feature = "ssl")]
+use openssl::x509::X509;
+
+#[cfg(feature = "ssl")]
+use ws::util::TcpStream;
+
+#[cfg(feature = "ssl")]
+struct Server {
+ out: ws::Sender,
+ ssl: Rc<SslAcceptor>,
+}
+
+#[cfg(feature = "ssl")]
+impl ws::Handler for Server {
+ fn on_message(&mut self, msg: ws::Message) -> ws::Result<()> {
+ self.out.send(msg) // simple echo
+ }
+
+ fn upgrade_ssl_server(&mut self, sock: TcpStream) -> ws::Result<SslStream<TcpStream>> {
+ self.ssl.accept(sock).map_err(From::from)
+ }
+}
+
+#[cfg(feature = "ssl")]
+fn main() {
+ // Setup logging
+ env_logger::init();
+
+ // setup command line arguments
+ let matches = clap::App::new("WS-RS SSL Server Configuration")
+ .version("1.0")
+ .author("Jason Housley <housleyjk@gmail.com>")
+ .about("Establish a WebSocket server that encrypts and decrypts messages.")
+ .arg(
+ clap::Arg::with_name("ADDR")
+ .help("Address on which to bind the server.")
+ .required(true)
+ .index(1),
+ )
+ .arg(
+ clap::Arg::with_name("CERT")
+ .help("Path to the SSL certificate.")
+ .required(true)
+ .index(2),
+ )
+ .arg(
+ clap::Arg::with_name("KEY")
+ .help("Path to the SSL certificate key.")
+ .required(true)
+ .index(3),
+ )
+ .get_matches();
+
+ let cert = {
+ let data = read_file(matches.value_of("CERT").unwrap()).unwrap();
+ X509::from_pem(data.as_ref()).unwrap()
+ };
+
+ let pkey = {
+ let data = read_file(matches.value_of("KEY").unwrap()).unwrap();
+ PKey::private_key_from_pem(data.as_ref()).unwrap()
+ };
+
+ let acceptor = Rc::new({
+ let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap();
+ builder.set_private_key(&pkey).unwrap();
+ builder.set_certificate(&cert).unwrap();
+
+ builder.build()
+ });
+
+ ws::Builder::new()
+ .with_settings(ws::Settings {
+ encrypt_server: true,
+ ..ws::Settings::default()
+ })
+ .build(|out: ws::Sender| Server {
+ out: out,
+ ssl: acceptor.clone(),
+ })
+ .unwrap()
+ .listen(matches.value_of("ADDR").unwrap())
+ .unwrap();
+}
+
+#[cfg(feature = "ssl")]
+fn read_file(name: &str) -> std::io::Result<Vec<u8>> {
+ let mut file = File::open(name)?;
+ let mut buf = Vec::new();
+ file.read_to_end(&mut buf)?;
+ Ok(buf)
+}
+
+#[cfg(not(feature = "ssl"))]
+fn main() {
+ println!("SSL feature is not enabled.")
+}
diff --git a/third_party/rust/ws/examples/threaded.rs b/third_party/rust/ws/examples/threaded.rs
new file mode 100644
index 0000000000..35cf0002fc
--- /dev/null
+++ b/third_party/rust/ws/examples/threaded.rs
@@ -0,0 +1,56 @@
+extern crate env_logger;
+/// A thread-based client + server example. It also demonstrates using a struct as a WebSocket
+/// handler to implement more handler methods than a closure handler allows.
+extern crate ws;
+
+use std::thread;
+use std::thread::sleep;
+use std::time::Duration;
+
+use ws::{connect, listen, CloseCode, Handler, Message, Result, Sender};
+
+fn main() {
+ // Setup logging
+ env_logger::init();
+
+ // Server WebSocket handler
+ struct Server {
+ out: Sender,
+ }
+
+ impl Handler for Server {
+ fn on_message(&mut self, msg: Message) -> Result<()> {
+ println!("Server got message '{}'. ", msg);
+ self.out.send(msg)
+ }
+
+ fn on_close(&mut self, code: CloseCode, reason: &str) {
+ println!("WebSocket closing for ({:?}) {}", code, reason);
+ println!("Shutting down server after first connection closes.");
+ self.out.shutdown().unwrap();
+ }
+ }
+
+ // Server thread
+ let server = thread::spawn(move || listen("127.0.0.1:3012", |out| Server { out }).unwrap());
+
+ // Give the server a little time to get going
+ sleep(Duration::from_millis(10));
+
+ // Client thread
+ let client = thread::spawn(move || {
+ connect("ws://127.0.0.1:3012", |out| {
+ out.send("Hello WebSocket").unwrap();
+
+ move |msg| {
+ println!("Client got message '{}'. ", msg);
+ out.close(CloseCode::Normal)
+ }
+ }).unwrap()
+ });
+
+ let _ = server.join();
+ let _ = client.join();
+
+ println!("All done.")
+}
diff --git a/third_party/rust/ws/examples/unsafe-ssl-client.rs b/third_party/rust/ws/examples/unsafe-ssl-client.rs
new file mode 100644
index 0000000000..c055cac546
--- /dev/null
+++ b/third_party/rust/ws/examples/unsafe-ssl-client.rs
@@ -0,0 +1,69 @@
+extern crate env_logger;
+#[cfg(feature = "ssl")]
+extern crate openssl;
+extern crate url;
+extern crate ws;
+
+#[cfg(feature = "ssl")]
+use openssl::ssl::{SslConnector, SslMethod, SslStream, SslVerifyMode};
+#[cfg(feature = "ssl")]
+use ws::util::TcpStream;
+
+#[cfg(feature = "ssl")]
+struct Client {
+ out: ws::Sender,
+}
+
+#[cfg(feature = "ssl")]
+impl ws::Handler for Client {
+ fn on_message(&mut self, msg: ws::Message) -> ws::Result<()> {
+ println!("msg = {}", msg);
+ self.out.close(ws::CloseCode::Normal)
+ }
+
+ fn upgrade_ssl_client(
+ &mut self,
+ sock: TcpStream,
+ _: &url::Url,
+ ) -> ws::Result<SslStream<TcpStream>> {
+ let mut builder = SslConnector::builder(SslMethod::tls()).map_err(|e| {
+ ws::Error::new(
+ ws::ErrorKind::Internal,
+ format!("Failed to upgrade client to SSL: {}", e),
+ )
+ })?;
+ builder.set_verify(SslVerifyMode::empty());
+
+ let connector = builder.build();
+ connector
+ .configure()
+ .unwrap()
+ .use_server_name_indication(false)
+ .verify_hostname(false)
+ .connect("", sock)
+ .map_err(From::from)
+ }
+}
+
+#[cfg(feature = "ssl")]
+fn main() {
+ // Setup logging
+ env_logger::init();
+
+ if let Err(error) = ws::connect("wss://localhost:3443/api/websocket", |out| {
+ if let Err(_) = out.send("Hello WebSocket") {
+ println!("Websocket couldn't queue an initial message.")
+ } else {
+ println!("Client sent message 'Hello WebSocket'. ")
+ }
+
+ Client { out }
+ }) {
+ println!("Failed to create WebSocket due to: {:?}", error);
+ }
+}
+
+#[cfg(not(feature = "ssl"))]
+fn main() {
+ println!("SSL feature is not enabled.")
+}
diff --git a/third_party/rust/ws/src/communication.rs b/third_party/rust/ws/src/communication.rs
new file mode 100644
index 0000000000..2b2822f03c
--- /dev/null
+++ b/third_party/rust/ws/src/communication.rs
@@ -0,0 +1,249 @@
+use std::borrow::Cow;
+use std::convert::Into;
+
+use mio;
+use mio::Token;
+use mio_extras::timer::Timeout;
+use url;
+
+use io::ALL;
+use message;
+use protocol::CloseCode;
+use result::{Error, Result};
+use std::cmp::PartialEq;
+use std::hash::{Hash, Hasher};
+use std::fmt;
+
+#[derive(Debug, Clone)]
+pub enum Signal {
+ Message(message::Message),
+ Close(CloseCode, Cow<'static, str>),
+ Ping(Vec<u8>),
+ Pong(Vec<u8>),
+ Connect(url::Url),
+ Shutdown,
+ Timeout { delay: u64, token: Token },
+ Cancel(Timeout),
+}
+
+#[derive(Debug, Clone)]
+pub struct Command {
+ token: Token,
+ signal: Signal,
+ connection_id: u32,
+}
+
+impl Command {
+ pub fn token(&self) -> Token {
+ self.token
+ }
+
+ pub fn into_signal(self) -> Signal {
+ self.signal
+ }
+
+ pub fn connection_id(&self) -> u32 {
+ self.connection_id
+ }
+}
+
+/// A representation of the output of the WebSocket connection. Use this to send messages to the
+/// other endpoint.
+#[derive(Clone)]
+pub struct Sender {
+ token: Token,
+ channel: mio::channel::SyncSender<Command>,
+ connection_id: u32,
+}
+
+impl fmt::Debug for Sender {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f,
+ "Sender {{ token: {:?}, channel: mio::channel::SyncSender<Command>, connection_id: {:?} }}",
+ self.token, self.connection_id)
+ }
+}
+
+impl PartialEq for Sender {
+ fn eq(&self, other: &Sender) -> bool {
+ self.token == other.token && self.connection_id == other.connection_id
+ }
+}
+
+impl Eq for Sender { }
+
+impl Hash for Sender {
+ fn hash<H: Hasher>(&self, state: &mut H) {
+ self.connection_id.hash(state);
+ self.token.hash(state);
+ }
+}
+
+
+impl Sender {
+ #[doc(hidden)]
+ #[inline]
+ pub fn new(
+ token: Token,
+ channel: mio::channel::SyncSender<Command>,
+ connection_id: u32,
+ ) -> Sender {
+ Sender {
+ token,
+ channel,
+ connection_id,
+ }
+ }
+
+ /// A Token identifying this sender within the WebSocket.
+ #[inline]
+ pub fn token(&self) -> Token {
+ self.token
+ }
+
+ /// A connection_id identifying this sender within the WebSocket.
+ #[inline]
+ pub fn connection_id(&self) -> u32 {
+ self.connection_id
+ }
+
+ /// Send a message over the connection.
+ #[inline]
+ pub fn send<M>(&self, msg: M) -> Result<()>
+ where
+ M: Into<message::Message>,
+ {
+ self.channel
+ .send(Command {
+ token: self.token,
+ signal: Signal::Message(msg.into()),
+ connection_id: self.connection_id,
+ })
+ .map_err(Error::from)
+ }
+
+ /// Send a message to the endpoints of all connections.
+ ///
+ /// Be careful with this method. It does not discriminate between client and server connections.
+ /// If your WebSocket is only functioning as a server, then usage is simple, this method will
+ /// send a copy of the message to each connected client. However, if you have a WebSocket that
+ /// is listening for connections and is also connected to another WebSocket, this method will
+ /// broadcast a copy of the message to all the clients connected and to that WebSocket server.
+ #[inline]
+ pub fn broadcast<M>(&self, msg: M) -> Result<()>
+ where
+ M: Into<message::Message>,
+ {
+ self.channel
+ .send(Command {
+ token: ALL,
+ signal: Signal::Message(msg.into()),
+ connection_id: self.connection_id,
+ })
+ .map_err(Error::from)
+ }
+
+ /// Send a close code to the other endpoint.
+ #[inline]
+ pub fn close(&self, code: CloseCode) -> Result<()> {
+ self.channel
+ .send(Command {
+ token: self.token,
+ signal: Signal::Close(code, "".into()),
+ connection_id: self.connection_id,
+ })
+ .map_err(Error::from)
+ }
+
+ /// Send a close code and provide a descriptive reason for closing.
+ #[inline]
+ pub fn close_with_reason<S>(&self, code: CloseCode, reason: S) -> Result<()>
+ where
+ S: Into<Cow<'static, str>>,
+ {
+ self.channel
+ .send(Command {
+ token: self.token,
+ signal: Signal::Close(code, reason.into()),
+ connection_id: self.connection_id,
+ })
+ .map_err(Error::from)
+ }
+
+ /// Send a ping to the other endpoint with the given test data.
+ #[inline]
+ pub fn ping(&self, data: Vec<u8>) -> Result<()> {
+ self.channel
+ .send(Command {
+ token: self.token,
+ signal: Signal::Ping(data),
+ connection_id: self.connection_id,
+ })
+ .map_err(Error::from)
+ }
+
+ /// Send a pong to the other endpoint responding with the given test data.
+ #[inline]
+ pub fn pong(&self, data: Vec<u8>) -> Result<()> {
+ self.channel
+ .send(Command {
+ token: self.token,
+ signal: Signal::Pong(data),
+ connection_id: self.connection_id,
+ })
+ .map_err(Error::from)
+ }
+
+ /// Queue a new connection on this WebSocket to the specified URL.
+ #[inline]
+ pub fn connect(&self, url: url::Url) -> Result<()> {
+ self.channel
+ .send(Command {
+ token: self.token,
+ signal: Signal::Connect(url),
+ connection_id: self.connection_id,
+ })
+ .map_err(Error::from)
+ }
+
+ /// Request that all connections terminate and that the WebSocket stop running.
+ #[inline]
+ pub fn shutdown(&self) -> Result<()> {
+ self.channel
+ .send(Command {
+ token: self.token,
+ signal: Signal::Shutdown,
+ connection_id: self.connection_id,
+ })
+ .map_err(Error::from)
+ }
+
+ /// Schedule a `token` to be sent to the WebSocket Handler's `on_timeout` method
+ /// after `ms` milliseconds
+ #[inline]
+ pub fn timeout(&self, ms: u64, token: Token) -> Result<()> {
+ self.channel
+ .send(Command {
+ token: self.token,
+ signal: Signal::Timeout { delay: ms, token },
+ connection_id: self.connection_id,
+ })
+ .map_err(Error::from)
+ }
+
+ /// Queue the cancellation of a previously scheduled timeout.
+ ///
+ /// This method is not guaranteed to prevent the timeout from occurring, because it is
+ /// possible to call this method after a timeout has already occurred. It is still necessary to
+ /// handle spurious timeouts.
+ #[inline]
+ pub fn cancel(&self, timeout: Timeout) -> Result<()> {
+ self.channel
+ .send(Command {
+ token: self.token,
+ signal: Signal::Cancel(timeout),
+ connection_id: self.connection_id,
+ })
+ .map_err(Error::from)
+ }
+}
diff --git a/third_party/rust/ws/src/connection.rs b/third_party/rust/ws/src/connection.rs
new file mode 100644
index 0000000000..b639695e5c
--- /dev/null
+++ b/third_party/rust/ws/src/connection.rs
@@ -0,0 +1,1230 @@
+use std::borrow::Borrow;
+use std::collections::VecDeque;
+use std::io::{Cursor, Read, Seek, SeekFrom, Write};
+use std::mem::replace;
+use std::net::SocketAddr;
+use std::str::from_utf8;
+
+use mio::tcp::TcpStream;
+use mio::{Ready, Token};
+use mio_extras::timer::Timeout;
+use url;
+
+#[cfg(feature = "nativetls")]
+use native_tls::HandshakeError;
+#[cfg(feature = "ssl")]
+use openssl::ssl::HandshakeError;
+
+use frame::Frame;
+use handler::Handler;
+use handshake::{Handshake, Request, Response};
+use message::Message;
+use protocol::{CloseCode, OpCode};
+use result::{Error, Kind, Result};
+use stream::{Stream, TryReadBuf, TryWriteBuf};
+
+use self::Endpoint::*;
+use self::State::*;
+
+use super::Settings;
+
+#[derive(Debug)]
+pub enum State {
+ // Tcp connection accepted, waiting for handshake to complete
+ Connecting(Cursor<Vec<u8>>, Cursor<Vec<u8>>),
+ // Ready to send/receive messages
+ Open,
+ AwaitingClose,
+ RespondingClose,
+ FinishedClose,
+}
+
+/// A little more semantic than a boolean
+#[derive(Debug, Eq, PartialEq, Clone)]
+pub enum Endpoint {
+ /// Will mask outgoing frames
+ Client(url::Url),
+ /// Won't mask outgoing frames
+ Server,
+}
+
+impl State {
+ #[inline]
+ pub fn is_connecting(&self) -> bool {
+ match *self {
+ State::Connecting(..) => true,
+ _ => false,
+ }
+ }
+
+ #[allow(dead_code)]
+ #[inline]
+ pub fn is_open(&self) -> bool {
+ match *self {
+ State::Open => true,
+ _ => false,
+ }
+ }
+
+ #[inline]
+ pub fn is_closing(&self) -> bool {
+ match *self {
+ State::AwaitingClose | State::FinishedClose => true,
+ _ => false,
+ }
+ }
+}
+
+pub struct Connection<H>
+where
+ H: Handler,
+{
+ token: Token,
+ socket: Stream,
+ state: State,
+ endpoint: Endpoint,
+ events: Ready,
+
+ fragments: VecDeque<Frame>,
+
+ in_buffer: Cursor<Vec<u8>>,
+ out_buffer: Cursor<Vec<u8>>,
+
+ handler: H,
+
+ addresses: Vec<SocketAddr>,
+
+ settings: Settings,
+ connection_id: u32,
+}
+
+impl<H> Connection<H>
+where
+ H: Handler,
+{
+ pub fn new(
+ tok: Token,
+ sock: TcpStream,
+ handler: H,
+ settings: Settings,
+ connection_id: u32,
+ ) -> Connection<H> {
+ Connection {
+ token: tok,
+ socket: Stream::tcp(sock),
+ state: Connecting(
+ Cursor::new(Vec::with_capacity(2048)),
+ Cursor::new(Vec::with_capacity(2048)),
+ ),
+ endpoint: Endpoint::Server,
+ events: Ready::empty(),
+ fragments: VecDeque::with_capacity(settings.fragments_capacity),
+ in_buffer: Cursor::new(Vec::with_capacity(settings.in_buffer_capacity)),
+ out_buffer: Cursor::new(Vec::with_capacity(settings.out_buffer_capacity)),
+ handler,
+ addresses: Vec::new(),
+ settings,
+ connection_id,
+ }
+ }
+
+ pub fn as_server(&mut self) -> Result<()> {
+ self.events.insert(Ready::readable());
+ Ok(())
+ }
+
+ pub fn as_client(&mut self, url: url::Url, addrs: Vec<SocketAddr>) -> Result<()> {
+ if let Connecting(ref mut req_buf, _) = self.state {
+ let req = self.handler.build_request(&url)?;
+ self.addresses = addrs;
+ self.events.insert(Ready::writable());
+ self.endpoint = Endpoint::Client(url);
+ req.format(req_buf.get_mut())
+ } else {
+ Err(Error::new(
+ Kind::Internal,
+ "Tried to set connection to client while not connecting.",
+ ))
+ }
+ }
+
+ #[cfg(any(feature = "ssl", feature = "nativetls"))]
+ pub fn encrypt(&mut self) -> Result<()> {
+ let sock = self.socket().try_clone()?;
+ let ssl_stream = match self.endpoint {
+ Server => self.handler.upgrade_ssl_server(sock),
+ Client(ref url) => self.handler.upgrade_ssl_client(sock, url),
+ };
+
+ match ssl_stream {
+ Ok(stream) => {
+ self.socket = Stream::tls_live(stream);
+ Ok(())
+ }
+ #[cfg(feature = "ssl")]
+ Err(Error {
+ kind: Kind::SslHandshake(handshake_err),
+ details,
+ }) => match handshake_err {
+ HandshakeError::SetupFailure(_) => {
+ Err(Error::new(Kind::SslHandshake(handshake_err), details))
+ }
+ HandshakeError::Failure(mid) | HandshakeError::WouldBlock(mid) => {
+ self.socket = Stream::tls(mid);
+ Ok(())
+ }
+ },
+ #[cfg(feature = "nativetls")]
+ Err(Error {
+ kind: Kind::SslHandshake(handshake_err),
+ details,
+ }) => match handshake_err {
+ HandshakeError::Failure(_) => {
+ Err(Error::new(Kind::SslHandshake(handshake_err), details))
+ }
+ HandshakeError::WouldBlock(mid) => {
+ self.socket = Stream::tls(mid);
+ Ok(())
+ }
+ },
+ Err(e) => Err(e),
+ }
+ }
+
+ pub fn token(&self) -> Token {
+ self.token
+ }
+
+ pub fn socket(&self) -> &TcpStream {
+ self.socket.evented()
+ }
+
+ pub fn connection_id(&self) -> u32 {
+ self.connection_id
+ }
+
+ fn peer_addr(&self) -> String {
+ if let Ok(addr) = self.socket.peer_addr() {
+ addr.to_string()
+ } else {
+ "UNKNOWN".into()
+ }
+ }
+
+ // Resetting may be necessary in order to try all possible addresses for a server
+ #[cfg(any(feature = "ssl", feature = "nativetls"))]
+ pub fn reset(&mut self) -> Result<()> {
+ // if self.is_client() {
+ if let Client(ref url) = self.endpoint {
+ if let Connecting(ref mut req, ref mut res) = self.state {
+ req.set_position(0);
+ res.set_position(0);
+ self.events.remove(Ready::readable());
+ self.events.insert(Ready::writable());
+
+ if let Some(ref addr) = self.addresses.pop() {
+ let sock = TcpStream::connect(addr)?;
+ if self.socket.is_tls() {
+ let ssl_stream = self.handler.upgrade_ssl_client(sock, url);
+ match ssl_stream {
+ Ok(stream) => {
+ self.socket = Stream::tls_live(stream);
+ Ok(())
+ }
+ #[cfg(feature = "ssl")]
+ Err(Error {
+ kind: Kind::SslHandshake(handshake_err),
+ details,
+ }) => match handshake_err {
+ HandshakeError::SetupFailure(_) => {
+ Err(Error::new(Kind::SslHandshake(handshake_err), details))
+ }
+ HandshakeError::Failure(mid) | HandshakeError::WouldBlock(mid) => {
+ self.socket = Stream::tls(mid);
+ Ok(())
+ }
+ },
+ #[cfg(feature = "nativetls")]
+ Err(Error {
+ kind: Kind::SslHandshake(handshake_err),
+ details,
+ }) => match handshake_err {
+ HandshakeError::Failure(_) => {
+ Err(Error::new(Kind::SslHandshake(handshake_err), details))
+ }
+ HandshakeError::WouldBlock(mid) => {
+ self.socket = Stream::tls(mid);
+ Ok(())
+ }
+ },
+ Err(e) => Err(e),
+ }
+ } else {
+ self.socket = Stream::tcp(sock);
+ Ok(())
+ }
+ } else {
+ if self.settings.panic_on_new_connection {
+ panic!("Unable to connect to server.");
+ }
+ Err(Error::new(Kind::Internal, "Exhausted possible addresses."))
+ }
+ } else {
+ Err(Error::new(
+ Kind::Internal,
+ "Unable to reset client connection because it is active.",
+ ))
+ }
+ } else {
+ Err(Error::new(
+ Kind::Internal,
+ "Server connections cannot be reset.",
+ ))
+ }
+ }
+
+ #[cfg(not(any(feature = "ssl", feature = "nativetls")))]
+ pub fn reset(&mut self) -> Result<()> {
+ if self.is_client() {
+ if let Connecting(ref mut req, ref mut res) = self.state {
+ req.set_position(0);
+ res.set_position(0);
+ self.events.remove(Ready::readable());
+ self.events.insert(Ready::writable());
+
+ if let Some(ref addr) = self.addresses.pop() {
+ let sock = TcpStream::connect(addr)?;
+ self.socket = Stream::tcp(sock);
+ Ok(())
+ } else {
+ if self.settings.panic_on_new_connection {
+ panic!("Unable to connect to server.");
+ }
+ Err(Error::new(Kind::Internal, "Exhausted possible addresses."))
+ }
+ } else {
+ Err(Error::new(
+ Kind::Internal,
+ "Unable to reset client connection because it is active.",
+ ))
+ }
+ } else {
+ Err(Error::new(
+ Kind::Internal,
+ "Server connections cannot be reset.",
+ ))
+ }
+ }
+
+ pub fn events(&self) -> Ready {
+ self.events
+ }
+
+ pub fn is_client(&self) -> bool {
+ match self.endpoint {
+ Client(_) => true,
+ Server => false,
+ }
+ }
+
+ pub fn is_server(&self) -> bool {
+ match self.endpoint {
+ Client(_) => false,
+ Server => true,
+ }
+ }
+
+ pub fn shutdown(&mut self) {
+ self.handler.on_shutdown();
+ if let Err(err) = self.send_close(CloseCode::Away, "Shutting down.") {
+ self.handler.on_error(err);
+ self.disconnect()
+ }
+ }
+
+ #[inline]
+ pub fn new_timeout(&mut self, event: Token, timeout: Timeout) -> Result<()> {
+ self.handler.on_new_timeout(event, timeout)
+ }
+
+ #[inline]
+ pub fn timeout_triggered(&mut self, event: Token) -> Result<()> {
+ self.handler.on_timeout(event)
+ }
+
+ pub fn error(&mut self, err: Error) {
+ match self.state {
+ Connecting(_, ref mut res) => match err.kind {
+ #[cfg(feature = "ssl")]
+ Kind::Ssl(_) => {
+ self.handler.on_error(err);
+ self.events = Ready::empty();
+ }
+ Kind::Io(_) => {
+ self.handler.on_error(err);
+ self.events = Ready::empty();
+ }
+ Kind::Protocol => {
+ let msg = err.to_string();
+ self.handler.on_error(err);
+ if let Server = self.endpoint {
+ res.get_mut().clear();
+ if let Err(err) =
+ write!(res.get_mut(), "HTTP/1.1 400 Bad Request\r\n\r\n{}", msg)
+ {
+ self.handler.on_error(Error::from(err));
+ self.events = Ready::empty();
+ } else {
+ self.events.remove(Ready::readable());
+ self.events.insert(Ready::writable());
+ }
+ } else {
+ self.events = Ready::empty();
+ }
+ }
+ _ => {
+ let msg = err.to_string();
+ self.handler.on_error(err);
+ if let Server = self.endpoint {
+ res.get_mut().clear();
+ if let Err(err) = write!(
+ res.get_mut(),
+ "HTTP/1.1 500 Internal Server Error\r\n\r\n{}",
+ msg
+ ) {
+ self.handler.on_error(Error::from(err));
+ self.events = Ready::empty();
+ } else {
+ self.events.remove(Ready::readable());
+ self.events.insert(Ready::writable());
+ }
+ } else {
+ self.events = Ready::empty();
+ }
+ }
+ },
+ _ => {
+ match err.kind {
+ Kind::Internal => {
+ if self.settings.panic_on_internal {
+ panic!("Panicking on internal error -- {}", err);
+ }
+ let reason = format!("{}", err);
+
+ self.handler.on_error(err);
+ if let Err(err) = self.send_close(CloseCode::Error, reason) {
+ self.handler.on_error(err);
+ self.disconnect()
+ }
+ }
+ Kind::Capacity => {
+ if self.settings.panic_on_capacity {
+ panic!("Panicking on capacity error -- {}", err);
+ }
+ let reason = format!("{}", err);
+
+ self.handler.on_error(err);
+ if let Err(err) = self.send_close(CloseCode::Size, reason) {
+ self.handler.on_error(err);
+ self.disconnect()
+ }
+ }
+ Kind::Protocol => {
+ if self.settings.panic_on_protocol {
+ panic!("Panicking on protocol error -- {}", err);
+ }
+ let reason = format!("{}", err);
+
+ self.handler.on_error(err);
+ if let Err(err) = self.send_close(CloseCode::Protocol, reason) {
+ self.handler.on_error(err);
+ self.disconnect()
+ }
+ }
+ Kind::Encoding(_) => {
+ if self.settings.panic_on_encoding {
+ panic!("Panicking on encoding error -- {}", err);
+ }
+ let reason = format!("{}", err);
+
+ self.handler.on_error(err);
+ if let Err(err) = self.send_close(CloseCode::Invalid, reason) {
+ self.handler.on_error(err);
+ self.disconnect()
+ }
+ }
+ Kind::Http(_) => {
+ // This may happen if some handler writes a bad response
+ self.handler.on_error(err);
+ error!("Disconnecting WebSocket.");
+ self.disconnect()
+ }
+ Kind::Custom(_) => {
+ self.handler.on_error(err);
+ }
+ Kind::Queue(_) => {
+ if self.settings.panic_on_queue {
+ panic!("Panicking on queue error -- {}", err);
+ }
+ self.handler.on_error(err);
+ }
+ _ => {
+ if self.settings.panic_on_io {
+ panic!("Panicking on io error -- {}", err);
+ }
+ self.handler.on_error(err);
+ self.disconnect()
+ }
+ }
+ }
+ }
+ }
+
+ pub fn disconnect(&mut self) {
+ match self.state {
+ RespondingClose | FinishedClose | Connecting(_, _) => (),
+ _ => {
+ self.handler.on_close(CloseCode::Abnormal, "");
+ }
+ }
+ self.events = Ready::empty()
+ }
+
+ pub fn consume(self) -> H {
+ self.handler
+ }
+
+ fn write_handshake(&mut self) -> Result<()> {
+ if let Connecting(ref mut req, ref mut res) = self.state {
+ match self.endpoint {
+ Server => {
+ let mut done = false;
+ if self.socket.try_write_buf(res)?.is_some() {
+ if res.position() as usize == res.get_ref().len() {
+ done = true
+ }
+ }
+ if !done {
+ return Ok(());
+ }
+ }
+ Client(_) => {
+ if self.socket.try_write_buf(req)?.is_some() {
+ if req.position() as usize == req.get_ref().len() {
+ trace!(
+ "Finished writing handshake request to {}",
+ self.socket
+ .peer_addr()
+ .map(|addr| addr.to_string())
+ .unwrap_or_else(|_| "UNKNOWN".into())
+ );
+ self.events.insert(Ready::readable());
+ self.events.remove(Ready::writable());
+ }
+ }
+ return Ok(());
+ }
+ }
+ }
+
+ if let Connecting(ref req, ref res) = replace(&mut self.state, Open) {
+ trace!(
+ "Finished writing handshake response to {}",
+ self.peer_addr()
+ );
+
+ let request = match Request::parse(req.get_ref()) {
+ Ok(Some(req)) => req,
+ _ => {
+ // An error should already have been sent for the first time it failed to
+ // parse. We don't call disconnect here because `on_open` hasn't been called yet.
+ self.state = FinishedClose;
+ self.events = Ready::empty();
+ return Ok(());
+ }
+ };
+
+ let response = Response::parse(res.get_ref())?.ok_or_else(|| {
+ Error::new(
+ Kind::Internal,
+ "Failed to parse response after handshake is complete.",
+ )
+ })?;
+
+ if response.status() != 101 {
+ self.events = Ready::empty();
+ return Ok(());
+ } else {
+ self.handler.on_open(Handshake {
+ request,
+ response,
+ peer_addr: self.socket.peer_addr().ok(),
+ local_addr: self.socket.local_addr().ok(),
+ })?;
+ debug!("Connection to {} is now open.", self.peer_addr());
+ self.events.insert(Ready::readable());
+ self.check_events();
+ return Ok(());
+ }
+ } else {
+ Err(Error::new(
+ Kind::Internal,
+ "Tried to write WebSocket handshake while not in connecting state!",
+ ))
+ }
+ }
+
+ fn read_handshake(&mut self) -> Result<()> {
+ if let Connecting(ref mut req, ref mut res) = self.state {
+ match self.endpoint {
+ Server => {
+ if let Some(read) = self.socket.try_read_buf(req.get_mut())? {
+ if read == 0 {
+ self.events = Ready::empty();
+ return Ok(());
+ }
+ if let Some(ref request) = Request::parse(req.get_ref())? {
+ trace!("Handshake request received: \n{}", request);
+ let response = self.handler.on_request(request)?;
+ response.format(res.get_mut())?;
+ self.events.remove(Ready::readable());
+ self.events.insert(Ready::writable());
+ }
+ }
+ return Ok(());
+ }
+ Client(_) => {
+ if self.socket.try_read_buf(res.get_mut())?.is_some() {
+ // TODO: see if this can be optimized with drain
+ let end = {
+ let data = res.get_ref();
+ let end = data.iter()
+ .enumerate()
+ .take_while(|&(ind, _)| !data[..ind].ends_with(b"\r\n\r\n"))
+ .count();
+ if !data[..end].ends_with(b"\r\n\r\n") {
+ return Ok(());
+ }
+ self.in_buffer.get_mut().extend(&data[end..]);
+ end
+ };
+ res.get_mut().truncate(end);
+ } else {
+ // NOTE: wait to be polled again; response not ready.
+ return Ok(());
+ }
+ }
+ }
+ }
+
+ if let Connecting(ref req, ref res) = replace(&mut self.state, Open) {
+ trace!(
+ "Finished reading handshake response from {}",
+ self.peer_addr()
+ );
+
+ let request = Request::parse(req.get_ref())?.ok_or_else(|| {
+ Error::new(
+ Kind::Internal,
+ "Failed to parse request after handshake is complete.",
+ )
+ })?;
+
+ let response = Response::parse(res.get_ref())?.ok_or_else(|| {
+ Error::new(
+ Kind::Internal,
+ "Failed to parse response after handshake is complete.",
+ )
+ })?;
+
+ trace!("Handshake response received: \n{}", response);
+
+ if response.status() != 101 {
+ if response.status() != 301 && response.status() != 302 {
+ return Err(Error::new(Kind::Protocol, "Handshake failed."));
+ } else {
+ return Ok(());
+ }
+ }
+
+ if self.settings.key_strict {
+ let req_key = request.hashed_key()?;
+ let res_key = from_utf8(response.key()?)?;
+ if req_key != res_key {
+ return Err(Error::new(
+ Kind::Protocol,
+ format!(
+ "Received incorrect WebSocket Accept key: {} vs {}",
+ req_key, res_key
+ ),
+ ));
+ }
+ }
+
+ self.handler.on_response(&response)?;
+ self.handler.on_open(Handshake {
+ request,
+ response,
+ peer_addr: self.socket.peer_addr().ok(),
+ local_addr: self.socket.local_addr().ok(),
+ })?;
+
+ // check to see if there is anything to read already
+ if !self.in_buffer.get_ref().is_empty() {
+ self.read_frames()?;
+ }
+
+ self.check_events();
+ return Ok(());
+ }
+ Err(Error::new(
+ Kind::Internal,
+ "Tried to read WebSocket handshake while not in connecting state!",
+ ))
+ }
+
+ pub fn read(&mut self) -> Result<()> {
+ if self.socket.is_negotiating() {
+ trace!("Performing TLS negotiation on {}.", self.peer_addr());
+ self.socket.clear_negotiating()?;
+ self.write()
+ } else {
+ let res = if self.state.is_connecting() {
+ trace!("Ready to read handshake from {}.", self.peer_addr());
+ self.read_handshake()
+ } else {
+ trace!("Ready to read messages from {}.", self.peer_addr());
+ while let Some(len) = self.buffer_in()? {
+ self.read_frames()?;
+ if len == 0 {
+ if self.events.is_writable() {
+ self.events.remove(Ready::readable());
+ } else {
+ self.disconnect()
+ }
+ break;
+ }
+ }
+ Ok(())
+ };
+
+ if self.socket.is_negotiating() && res.is_ok() {
+ self.events.remove(Ready::readable());
+ self.events.insert(Ready::writable());
+ }
+ res
+ }
+ }
+
+ fn read_frames(&mut self) -> Result<()> {
+ let max_size = self.settings.max_fragment_size as u64;
+ while let Some(mut frame) = Frame::parse(&mut self.in_buffer, max_size)? {
+ match self.state {
+ // Ignore data received after receiving close frame
+ RespondingClose | FinishedClose => continue,
+ _ => (),
+ }
+
+ if self.settings.masking_strict {
+ if frame.is_masked() {
+ if self.is_client() {
+ return Err(Error::new(
+ Kind::Protocol,
+ "Received masked frame from a server endpoint.",
+ ));
+ }
+ } else {
+ if self.is_server() {
+ return Err(Error::new(
+ Kind::Protocol,
+ "Received unmasked frame from a client endpoint.",
+ ));
+ }
+ }
+ }
+
+ // This is safe whether or not a frame is masked.
+ frame.remove_mask();
+
+ if let Some(frame) = self.handler.on_frame(frame)? {
+ if frame.is_final() {
+ match frame.opcode() {
+ // singleton data frames
+ OpCode::Text => {
+ trace!("Received text frame {:?}", frame);
+ // since we are going to handle this, there can't be an ongoing
+ // message
+ if !self.fragments.is_empty() {
+ return Err(Error::new(Kind::Protocol, "Received unfragmented text frame while processing fragmented message."));
+ }
+ let msg = Message::text(String::from_utf8(frame.into_data())
+ .map_err(|err| err.utf8_error())?);
+ self.handler.on_message(msg)?;
+ }
+ OpCode::Binary => {
+ trace!("Received binary frame {:?}", frame);
+ // since we are going to handle this, there can't be an ongoing
+ // message
+ if !self.fragments.is_empty() {
+ return Err(Error::new(Kind::Protocol, "Received unfragmented binary frame while processing fragmented message."));
+ }
+ let data = frame.into_data();
+ self.handler.on_message(Message::binary(data))?;
+ }
+ // control frames
+ OpCode::Close => {
+ trace!("Received close frame {:?}", frame);
+ // Closing handshake
+ if self.state.is_closing() {
+ if self.is_server() {
+ // Finished handshake, disconnect server side
+ self.events = Ready::empty()
+ } else {
+ // We are a client, so we wait for the server to close the
+ // connection
+ }
+ } else {
+ // Starting handshake, will send the responding close frame
+ self.state = RespondingClose;
+ }
+
+ let mut close_code = [0u8; 2];
+ let mut data = Cursor::new(frame.into_data());
+ if let 2 = data.read(&mut close_code)? {
+ let raw_code: u16 =
+ (u16::from(close_code[0]) << 8) | (u16::from(close_code[1]));
+ trace!(
+ "Connection to {} received raw close code: {:?}, {:?}",
+ self.peer_addr(),
+ raw_code,
+ close_code
+ );
+ let named = CloseCode::from(raw_code);
+ if let CloseCode::Other(code) = named {
+ if code < 1000 ||
+ code >= 5000 ||
+ code == 1004 ||
+ code == 1014 ||
+ code == 1016 || // these below are here to pass the autobahn test suite
+ code == 1100 || // we shouldn't need them later
+ code == 2000
+ || code == 2999
+ {
+ return Err(Error::new(
+ Kind::Protocol,
+ format!(
+ "Received invalid close code from endpoint: {}",
+ code
+ ),
+ ));
+ }
+ }
+ let has_reason = {
+ if let Ok(reason) = from_utf8(&data.get_ref()[2..]) {
+ self.handler.on_close(named, reason); // note reason may be an empty string
+ true
+ } else {
+ self.handler.on_close(named, "");
+ false
+ }
+ };
+
+ if let CloseCode::Abnormal = named {
+ return Err(Error::new(
+ Kind::Protocol,
+ "Received abnormal close code from endpoint.",
+ ));
+ } else if let CloseCode::Status = named {
+ return Err(Error::new(
+ Kind::Protocol,
+ "Received no status close code from endpoint.",
+ ));
+ } else if let CloseCode::Restart = named {
+ return Err(Error::new(
+ Kind::Protocol,
+ "Restart close code is not supported.",
+ ));
+ } else if let CloseCode::Again = named {
+ return Err(Error::new(
+ Kind::Protocol,
+ "Try again later close code is not supported.",
+ ));
+ } else if let CloseCode::Tls = named {
+ return Err(Error::new(
+ Kind::Protocol,
+ "Received TLS close code outside of TLS handshake.",
+ ));
+ } else {
+ if !self.state.is_closing() {
+ if has_reason {
+ self.send_close(named, "")?; // note this drops any extra close data
+ } else {
+ self.send_close(CloseCode::Invalid, "")?;
+ }
+ } else {
+ self.state = FinishedClose;
+ }
+ }
+ } else {
+ // This is not an error. It is allowed behavior in the
+ // protocol, so we don't trigger an error.
+ // "If there is no such data in the Close control frame,
+ // _The WebSocket Connection Close Reason_ is the empty string."
+ self.handler.on_close(CloseCode::Status, "");
+ if !self.state.is_closing() {
+ self.send_close(CloseCode::Empty, "")?;
+ } else {
+ self.state = FinishedClose;
+ }
+ }
+ }
+ OpCode::Ping => {
+ trace!("Received ping frame {:?}", frame);
+ self.send_pong(frame.into_data())?;
+ }
+ OpCode::Pong => {
+ trace!("Received pong frame {:?}", frame);
+ // no ping validation for now
+ }
+ // last fragment
+ OpCode::Continue => {
+ trace!("Received final fragment {:?}", frame);
+ if let Some(first) = self.fragments.pop_front() {
+ let size = self.fragments.iter().fold(
+ first.payload().len() + frame.payload().len(),
+ |len, frame| len + frame.payload().len(),
+ );
+ match first.opcode() {
+ OpCode::Text => {
+ trace!("Constructing text message from fragments: {:?} -> {:?} -> {:?}", first, self.fragments.iter().collect::<Vec<&Frame>>(), frame);
+ let mut data = Vec::with_capacity(size);
+ data.extend(first.into_data());
+ while let Some(frame) = self.fragments.pop_front() {
+ data.extend(frame.into_data());
+ }
+ data.extend(frame.into_data());
+
+ let string = String::from_utf8(data)
+ .map_err(|err| err.utf8_error())?;
+
+ trace!(
+ "Calling handler with constructed message: {:?}",
+ string
+ );
+ self.handler.on_message(Message::text(string))?;
+ }
+ OpCode::Binary => {
+ trace!("Constructing binary message from fragments: {:?} -> {:?} -> {:?}", first, self.fragments.iter().collect::<Vec<&Frame>>(), frame);
+ let mut data = Vec::with_capacity(size);
+ data.extend(first.into_data());
+
+ while let Some(frame) = self.fragments.pop_front() {
+ data.extend(frame.into_data());
+ }
+
+ data.extend(frame.into_data());
+
+ trace!(
+ "Calling handler with constructed message: {:?}",
+ data
+ );
+ self.handler.on_message(Message::binary(data))?;
+ }
+ _ => {
+ return Err(Error::new(
+ Kind::Protocol,
+ "Encounted fragmented control frame.",
+ ))
+ }
+ }
+ } else {
+ return Err(Error::new(
+ Kind::Protocol,
+ "Unable to reconstruct fragmented message. No first frame.",
+ ));
+ }
+ }
+ _ => return Err(Error::new(Kind::Protocol, "Encountered invalid opcode.")),
+ }
+ } else {
+ if frame.is_control() {
+ return Err(Error::new(
+ Kind::Protocol,
+ "Encounted fragmented control frame.",
+ ));
+ } else {
+ trace!("Received non-final fragment frame {:?}", frame);
+ if !self.settings.fragments_grow
+ && self.settings.fragments_capacity == self.fragments.len()
+ {
+ return Err(Error::new(Kind::Capacity, "Exceeded max fragments."));
+ } else {
+ self.fragments.push_back(frame)
+ }
+ }
+ }
+ }
+ }
+ Ok(())
+ }
+
+ pub fn write(&mut self) -> Result<()> {
+ if self.socket.is_negotiating() {
+ trace!("Performing TLS negotiation on {}.", self.peer_addr());
+ self.socket.clear_negotiating()?;
+ self.read()
+ } else {
+ let res = if self.state.is_connecting() {
+ trace!("Ready to write handshake to {}.", self.peer_addr());
+ self.write_handshake()
+ } else {
+ trace!("Ready to write messages to {}.", self.peer_addr());
+
+ // Start out assuming that this write will clear the whole buffer
+ self.events.remove(Ready::writable());
+
+ if let Some(len) = self.socket.try_write_buf(&mut self.out_buffer)? {
+ trace!("Wrote {} bytes to {}", len, self.peer_addr());
+ let finished = len == 0
+ || self.out_buffer.position() == self.out_buffer.get_ref().len() as u64;
+ if finished {
+ match self.state {
+ // we are are a server that is closing and just wrote out our confirming
+ // close frame, let's disconnect
+ FinishedClose if self.is_server() => {
+ self.events = Ready::empty();
+ return Ok(());
+ }
+ _ => (),
+ }
+ }
+ }
+
+ // Check if there is more to write so that the connection will be rescheduled
+ self.check_events();
+ Ok(())
+ };
+
+ if self.socket.is_negotiating() && res.is_ok() {
+ self.events.remove(Ready::writable());
+ self.events.insert(Ready::readable());
+ }
+ res
+ }
+ }
+
+ pub fn send_message(&mut self, msg: Message) -> Result<()> {
+ if self.state.is_closing() {
+ trace!(
+ "Connection is closing. Ignoring request to send message {:?} to {}.",
+ msg,
+ self.peer_addr()
+ );
+ return Ok(());
+ }
+
+ let opcode = msg.opcode();
+ trace!("Message opcode {:?}", opcode);
+ let data = msg.into_data();
+
+ if let Some(frame) = self.handler
+ .on_send_frame(Frame::message(data, opcode, true))?
+ {
+ if frame.payload().len() > self.settings.fragment_size {
+ trace!("Chunking at {:?}.", self.settings.fragment_size);
+ // note this copies the data, so it's actually somewhat expensive to fragment
+ let mut chunks = frame
+ .payload()
+ .chunks(self.settings.fragment_size)
+ .peekable();
+ let chunk = chunks.next().expect("Unable to get initial chunk!");
+
+ let mut first = Frame::message(Vec::from(chunk), opcode, false);
+
+ // Match reserved bits from original to keep extension status intact
+ first.set_rsv1(frame.has_rsv1());
+ first.set_rsv2(frame.has_rsv2());
+ first.set_rsv3(frame.has_rsv3());
+
+ self.buffer_frame(first)?;
+
+ while let Some(chunk) = chunks.next() {
+ if chunks.peek().is_some() {
+ self.buffer_frame(Frame::message(
+ Vec::from(chunk),
+ OpCode::Continue,
+ false,
+ ))?;
+ } else {
+ self.buffer_frame(Frame::message(
+ Vec::from(chunk),
+ OpCode::Continue,
+ true,
+ ))?;
+ }
+ }
+ } else {
+ trace!("Sending unfragmented message frame.");
+ // true means that the message is done
+ self.buffer_frame(frame)?;
+ }
+ }
+ self.check_events();
+ Ok(())
+ }
+
+ #[inline]
+ pub fn send_ping(&mut self, data: Vec<u8>) -> Result<()> {
+ if self.state.is_closing() {
+ trace!(
+ "Connection is closing. Ignoring request to send ping {:?} to {}.",
+ data,
+ self.peer_addr()
+ );
+ return Ok(());
+ }
+ trace!("Sending ping to {}.", self.peer_addr());
+
+ if let Some(frame) = self.handler.on_send_frame(Frame::ping(data))? {
+ self.buffer_frame(frame)?;
+ }
+ self.check_events();
+ Ok(())
+ }
+
+ #[inline]
+ pub fn send_pong(&mut self, data: Vec<u8>) -> Result<()> {
+ if self.state.is_closing() {
+ trace!(
+ "Connection is closing. Ignoring request to send pong {:?} to {}.",
+ data,
+ self.peer_addr()
+ );
+ return Ok(());
+ }
+ trace!("Sending pong to {}.", self.peer_addr());
+
+ if let Some(frame) = self.handler.on_send_frame(Frame::pong(data))? {
+ self.buffer_frame(frame)?;
+ }
+ self.check_events();
+ Ok(())
+ }
+
+ #[inline]
+ pub fn send_close<R>(&mut self, code: CloseCode, reason: R) -> Result<()>
+ where
+ R: Borrow<str>,
+ {
+ match self.state {
+ // We are responding to a close frame the other endpoint, when this frame goes out, we
+ // are done.
+ RespondingClose => self.state = FinishedClose,
+ // Multiple close frames are being sent from our end, ignore the later frames
+ AwaitingClose | FinishedClose => {
+ trace!(
+ "Connection is already closing. Ignoring close {:?} -- {:?} to {}.",
+ code,
+ reason.borrow(),
+ self.peer_addr()
+ );
+ self.check_events();
+ return Ok(());
+ }
+ // We are initiating a closing handshake.
+ Open => self.state = AwaitingClose,
+ Connecting(_, _) => {
+ debug_assert!(false, "Attempted to close connection while not yet open.")
+ }
+ }
+
+ trace!(
+ "Sending close {:?} -- {:?} to {}.",
+ code,
+ reason.borrow(),
+ self.peer_addr()
+ );
+
+ if let Some(frame) = self.handler
+ .on_send_frame(Frame::close(code, reason.borrow()))?
+ {
+ self.buffer_frame(frame)?;
+ }
+
+ trace!("Connection to {} is now closing.", self.peer_addr());
+
+ self.check_events();
+ Ok(())
+ }
+
+ fn check_events(&mut self) {
+ if !self.state.is_connecting() {
+ self.events.insert(Ready::readable());
+ if self.out_buffer.position() < self.out_buffer.get_ref().len() as u64 {
+ self.events.insert(Ready::writable());
+ }
+ }
+ }
+
+ fn buffer_frame(&mut self, mut frame: Frame) -> Result<()> {
+ self.check_buffer_out(&frame)?;
+
+ if self.is_client() {
+ frame.set_mask();
+ }
+
+ trace!("Buffering frame to {}:\n{}", self.peer_addr(), frame);
+
+ let pos = self.out_buffer.position();
+ self.out_buffer.seek(SeekFrom::End(0))?;
+ frame.format(&mut self.out_buffer)?;
+ self.out_buffer.seek(SeekFrom::Start(pos))?;
+ Ok(())
+ }
+
+ fn check_buffer_out(&mut self, frame: &Frame) -> Result<()> {
+ if self.out_buffer.get_ref().capacity() <= self.out_buffer.get_ref().len() + frame.len() {
+ // extend
+ let mut new = Vec::with_capacity(self.out_buffer.get_ref().capacity());
+ new.extend(&self.out_buffer.get_ref()[self.out_buffer.position() as usize..]);
+ if new.len() == new.capacity() {
+ if self.settings.out_buffer_grow {
+ new.reserve(self.settings.out_buffer_capacity)
+ } else {
+ return Err(Error::new(
+ Kind::Capacity,
+ "Maxed out output buffer for connection.",
+ ));
+ }
+ }
+ self.out_buffer = Cursor::new(new);
+ }
+ Ok(())
+ }
+
+ fn buffer_in(&mut self) -> Result<Option<usize>> {
+ trace!("Reading buffer for connection to {}.", self.peer_addr());
+ if let Some(len) = self.socket.try_read_buf(self.in_buffer.get_mut())? {
+ trace!("Buffered {}.", len);
+ if self.in_buffer.get_ref().len() == self.in_buffer.get_ref().capacity() {
+ // extend
+ let mut new = Vec::with_capacity(self.in_buffer.get_ref().capacity());
+ new.extend(&self.in_buffer.get_ref()[self.in_buffer.position() as usize..]);
+ if new.len() == new.capacity() {
+ if self.settings.in_buffer_grow {
+ new.reserve(self.settings.in_buffer_capacity);
+ } else {
+ return Err(Error::new(
+ Kind::Capacity,
+ "Maxed out input buffer for connection.",
+ ));
+ }
+ }
+ self.in_buffer = Cursor::new(new);
+ }
+ Ok(Some(len))
+ } else {
+ Ok(None)
+ }
+ }
+}
diff --git a/third_party/rust/ws/src/deflate/context.rs b/third_party/rust/ws/src/deflate/context.rs
new file mode 100644
index 0000000000..2fa2e23056
--- /dev/null
+++ b/third_party/rust/ws/src/deflate/context.rs
@@ -0,0 +1,268 @@
+use std::mem;
+use std::slice;
+
+use super::ffi;
+use super::libc::{c_char, c_int, c_uint};
+
+use result::{Error, Kind, Result};
+
+const ZLIB_VERSION: &'static str = "1.2.8\0";
+
+trait Context {
+ fn stream(&mut self) -> &mut ffi::z_stream;
+
+ fn stream_apply<F>(&mut self, input: &[u8], output: &mut Vec<u8>, each: F) -> Result<()>
+ where
+ F: Fn(&mut ffi::z_stream) -> Option<Result<()>>,
+ {
+ debug_assert!(output.len() == 0, "Output vector is not empty.");
+
+ let stream = self.stream();
+
+ stream.next_in = input.as_ptr() as *mut _;
+ stream.avail_in = input.len() as c_uint;
+
+ let mut output_size;
+
+ loop {
+ output_size = output.len();
+
+ if output_size == output.capacity() {
+ output.reserve(input.len())
+ }
+
+ let out_slice = unsafe {
+ slice::from_raw_parts_mut(
+ output.as_mut_ptr().offset(output_size as isize),
+ output.capacity() - output_size,
+ )
+ };
+
+ stream.next_out = out_slice.as_mut_ptr();
+ stream.avail_out = out_slice.len() as c_uint;
+
+ let before = stream.total_out;
+ let cont = each(stream);
+
+ unsafe {
+ output.set_len((stream.total_out - before) as usize + output_size);
+ }
+
+ if let Some(result) = cont {
+ return result;
+ }
+ }
+ }
+}
+
+pub struct Compressor {
+ // Box the z_stream to ensure it isn't moved. Moving the z_stream
+ // causes zlib to fail, because it maintains internal pointers.
+ stream: Box<ffi::z_stream>,
+}
+
+impl Compressor {
+ pub fn new(window_bits: i8) -> Compressor {
+ debug_assert!(window_bits >= 9, "Received too small window size.");
+ debug_assert!(window_bits <= 15, "Received too large window size.");
+
+ unsafe {
+ let mut stream: Box<ffi::z_stream> = Box::new(mem::zeroed());
+ let result = ffi::deflateInit2_(
+ stream.as_mut(),
+ 9,
+ ffi::Z_DEFLATED,
+ -window_bits as c_int,
+ 9,
+ ffi::Z_DEFAULT_STRATEGY,
+ ZLIB_VERSION.as_ptr() as *const c_char,
+ mem::size_of::<ffi::z_stream>() as c_int,
+ );
+ assert!(result == ffi::Z_OK, "Failed to initialize compresser.");
+ Compressor { stream: stream }
+ }
+ }
+
+ pub fn compress(&mut self, input: &[u8], output: &mut Vec<u8>) -> Result<()> {
+ self.stream_apply(input, output, |stream| unsafe {
+ match ffi::deflate(stream, ffi::Z_SYNC_FLUSH) {
+ ffi::Z_OK | ffi::Z_BUF_ERROR => {
+ if stream.avail_in == 0 && stream.avail_out > 0 {
+ Some(Ok(()))
+ } else {
+ None
+ }
+ }
+ code => Some(Err(Error::new(
+ Kind::Protocol,
+ format!("Failed to perform compression: {}", code),
+ ))),
+ }
+ })
+ }
+
+ pub fn reset(&mut self) -> Result<()> {
+ match unsafe { ffi::deflateReset(self.stream.as_mut()) } {
+ ffi::Z_OK => Ok(()),
+ code => Err(Error::new(
+ Kind::Protocol,
+ format!("Failed to reset compression context: {}", code),
+ )),
+ }
+ }
+}
+
+impl Context for Compressor {
+ fn stream(&mut self) -> &mut ffi::z_stream {
+ self.stream.as_mut()
+ }
+}
+
+impl Drop for Compressor {
+ fn drop(&mut self) {
+ match unsafe { ffi::deflateEnd(self.stream.as_mut()) } {
+ ffi::Z_STREAM_ERROR => error!("Compression stream encountered bad state."),
+ // Ignore discarded data error because we are raw
+ ffi::Z_OK | ffi::Z_DATA_ERROR => trace!("Deallocated compression context."),
+ code => error!("Bad zlib status encountered: {}", code),
+ }
+ }
+}
+
+pub struct Decompressor {
+ stream: Box<ffi::z_stream>,
+}
+
+impl Decompressor {
+ pub fn new(window_bits: i8) -> Decompressor {
+ debug_assert!(window_bits >= 8, "Received too small window size.");
+ debug_assert!(window_bits <= 15, "Received too large window size.");
+
+ unsafe {
+ let mut stream: Box<ffi::z_stream> = Box::new(mem::zeroed());
+ let result = ffi::inflateInit2_(
+ stream.as_mut(),
+ -window_bits as c_int,
+ ZLIB_VERSION.as_ptr() as *const c_char,
+ mem::size_of::<ffi::z_stream>() as c_int,
+ );
+ assert!(result == ffi::Z_OK, "Failed to initialize decompresser.");
+ Decompressor { stream: stream }
+ }
+ }
+
+ pub fn decompress(&mut self, input: &[u8], output: &mut Vec<u8>) -> Result<()> {
+ self.stream_apply(input, output, |stream| unsafe {
+ match ffi::inflate(stream, ffi::Z_SYNC_FLUSH) {
+ ffi::Z_OK | ffi::Z_BUF_ERROR => {
+ if stream.avail_in == 0 && stream.avail_out > 0 {
+ Some(Ok(()))
+ } else {
+ None
+ }
+ }
+ code => Some(Err(Error::new(
+ Kind::Protocol,
+ format!("Failed to perform decompression: {}", code),
+ ))),
+ }
+ })
+ }
+
+ pub fn reset(&mut self) -> Result<()> {
+ match unsafe { ffi::inflateReset(self.stream.as_mut()) } {
+ ffi::Z_OK => Ok(()),
+ code => Err(Error::new(
+ Kind::Protocol,
+ format!("Failed to reset compression context: {}", code),
+ )),
+ }
+ }
+}
+
+impl Context for Decompressor {
+ fn stream(&mut self) -> &mut ffi::z_stream {
+ self.stream.as_mut()
+ }
+}
+
+impl Drop for Decompressor {
+ fn drop(&mut self) {
+ match unsafe { ffi::inflateEnd(self.stream.as_mut()) } {
+ ffi::Z_STREAM_ERROR => error!("Decompression stream encountered bad state."),
+ ffi::Z_OK => trace!("Deallocated decompression context."),
+ code => error!("Bad zlib status encountered: {}", code),
+ }
+ }
+}
+
+mod test {
+ #![allow(unused_imports, unused_variables, dead_code)]
+ use super::*;
+
+ fn as_hex(s: &[u8]) {
+ for byte in s {
+ print!("0x{:x} ", byte);
+ }
+ print!("\n");
+ }
+
+ #[test]
+ fn round_trip() {
+ for i in 9..16 {
+ let data = "HI THERE THIS IS some data. これはデータだよ。".as_bytes();
+ let mut compressed = Vec::with_capacity(data.len());
+ let mut decompressed = Vec::with_capacity(data.len());
+
+ let com = Compressor::new(i);
+ let mut moved_com = com;
+
+ moved_com
+ .compress(&data, &mut compressed)
+ .expect("Failed to compress data.");
+
+ let dec = Decompressor::new(i);
+ let mut moved_dec = dec;
+
+ moved_dec
+ .decompress(&compressed, &mut decompressed)
+ .expect("Failed to decompress data.");
+
+ assert_eq!(data, &decompressed[..]);
+ }
+ }
+
+ #[test]
+ fn reset() {
+ let data1 = "HI THERE 直子さん".as_bytes();
+ let data2 = "HI THERE 人太郎".as_bytes();
+ let mut compressed1 = Vec::with_capacity(data1.len());
+ let mut compressed2 = Vec::with_capacity(data2.len());
+ let mut compressed2_ind = Vec::with_capacity(data2.len());
+
+ let mut decompressed1 = Vec::with_capacity(data1.len());
+ let mut decompressed2 = Vec::with_capacity(data2.len());
+ let mut decompressed2_ind = Vec::with_capacity(data2.len());
+
+ let mut com = Compressor::new(9);
+
+ com.compress(&data1, &mut compressed1).unwrap();
+ com.compress(&data2, &mut compressed2).unwrap();
+ com.reset().unwrap();
+ com.compress(&data2, &mut compressed2_ind).unwrap();
+
+ let mut dec = Decompressor::new(9);
+
+ dec.decompress(&compressed1, &mut decompressed1).unwrap();
+ dec.decompress(&compressed2, &mut decompressed2).unwrap();
+ dec.reset().unwrap();
+ dec.decompress(&compressed2_ind, &mut decompressed2_ind)
+ .unwrap();
+
+ assert_eq!(data1, &decompressed1[..]);
+ assert_eq!(data2, &decompressed2[..]);
+ assert_eq!(data2, &decompressed2_ind[..]);
+ assert!(compressed2 != compressed2_ind);
+ assert!(compressed2.len() < compressed2_ind.len());
+ }
+}
diff --git a/third_party/rust/ws/src/deflate/extension.rs b/third_party/rust/ws/src/deflate/extension.rs
new file mode 100644
index 0000000000..712e11fb8e
--- /dev/null
+++ b/third_party/rust/ws/src/deflate/extension.rs
@@ -0,0 +1,565 @@
+use std::mem::replace;
+
+#[cfg(feature = "ssl")]
+use openssl::ssl::SslStream;
+#[cfg(feature = "nativetls")]
+use native_tls::TlsStream as SslStream;
+use url;
+
+use frame::Frame;
+use handler::Handler;
+use handshake::{Handshake, Request, Response};
+use message::Message;
+use protocol::{CloseCode, OpCode};
+use result::{Error, Kind, Result};
+#[cfg(any(feature = "ssl", feature = "nativetls"))]
+use util::TcpStream;
+use util::{Timeout, Token};
+
+use super::context::{Compressor, Decompressor};
+
+/// Deflate Extension Handler Settings
+#[derive(Debug, Clone, Copy)]
+pub struct DeflateSettings {
+ /// The max size of the sliding window. If the other endpoint selects a smaller size, that size
+ /// will be used instead. This must be an integer between 9 and 15 inclusive.
+ /// Default: 15
+ pub max_window_bits: u8,
+ /// Indicates whether to ask the other endpoint to reset the sliding window for each message.
+ /// Default: false
+ pub request_no_context_takeover: bool,
+ /// Indicates whether this endpoint will agree to reset the sliding window for each message it
+ /// compresses. If this endpoint won't agree to reset the sliding window, then the handshake
+ /// will fail if this endpoint is a client and the server requests no context takeover.
+ /// Default: true
+ pub accept_no_context_takeover: bool,
+ /// The number of WebSocket frames to store when defragmenting an incoming fragmented
+ /// compressed message.
+ /// This setting may be different from the `fragments_capacity` setting of the WebSocket in order to
+ /// allow for differences between compressed and uncompressed messages.
+ /// Default: 10
+ pub fragments_capacity: usize,
+ /// Indicates whether the extension handler will reallocate if the `fragments_capacity` is
+ /// exceeded. If this is not true, a capacity error will be triggered instead.
+ /// Default: true
+ pub fragments_grow: bool,
+}
+
+impl Default for DeflateSettings {
+ fn default() -> DeflateSettings {
+ DeflateSettings {
+ max_window_bits: 15,
+ request_no_context_takeover: false,
+ accept_no_context_takeover: true,
+ fragments_capacity: 10,
+ fragments_grow: true,
+ }
+ }
+}
+
+/// Utility for applying the permessage-deflate extension to a handler with particular deflate
+/// settings.
+#[derive(Debug, Clone, Copy)]
+pub struct DeflateBuilder {
+ settings: DeflateSettings,
+}
+
+impl DeflateBuilder {
+ /// Create a new DeflateBuilder with the default settings.
+ pub fn new() -> DeflateBuilder {
+ DeflateBuilder {
+ settings: DeflateSettings::default(),
+ }
+ }
+
+ /// Configure the DeflateBuilder with the given deflate settings.
+ pub fn with_settings(&mut self, settings: DeflateSettings) -> &mut DeflateBuilder {
+ self.settings = settings;
+ self
+ }
+
+ /// Wrap another handler in with a deflate handler as configured.
+ pub fn build<H: Handler>(&self, handler: H) -> DeflateHandler<H> {
+ DeflateHandler {
+ com: Compressor::new(self.settings.max_window_bits as i8),
+ dec: Decompressor::new(self.settings.max_window_bits as i8),
+ fragments: Vec::with_capacity(self.settings.fragments_capacity),
+ compress_reset: false,
+ decompress_reset: false,
+ pass: false,
+ settings: self.settings,
+ inner: handler,
+ }
+ }
+}
+
+/// A WebSocket handler that implements the permessage-deflate extension.
+///
+/// This handler wraps a child handler and proxies all handler methods to it. The handler will
+/// decompress incoming WebSocket message frames in their reserved bits match the
+/// permessage-deflate specification and pass them to the child handler. Message frames sent from
+/// the child handler will be compressed and sent to the other endpoint using deflate compression.
+pub struct DeflateHandler<H: Handler> {
+ com: Compressor,
+ dec: Decompressor,
+ fragments: Vec<Frame>,
+ compress_reset: bool,
+ decompress_reset: bool,
+ pass: bool,
+ settings: DeflateSettings,
+ inner: H,
+}
+
+impl<H: Handler> DeflateHandler<H> {
+ /// Wrap a child handler to provide the permessage-deflate extension.
+ pub fn new(handler: H) -> DeflateHandler<H> {
+ trace!("Using permessage-deflate handler.");
+ let settings = DeflateSettings::default();
+ DeflateHandler {
+ com: Compressor::new(settings.max_window_bits as i8),
+ dec: Decompressor::new(settings.max_window_bits as i8),
+ fragments: Vec::with_capacity(settings.fragments_capacity),
+ compress_reset: false,
+ decompress_reset: false,
+ pass: false,
+ settings: settings,
+ inner: handler,
+ }
+ }
+
+ #[doc(hidden)]
+ #[inline]
+ fn decline(&mut self, mut res: Response) -> Result<Response> {
+ trace!("Declined permessage-deflate offer");
+ self.pass = true;
+ res.remove_extension("permessage-deflate");
+ Ok(res)
+ }
+}
+
+impl<H: Handler> Handler for DeflateHandler<H> {
+ fn build_request(&mut self, url: &url::Url) -> Result<Request> {
+ let mut req = self.inner.build_request(url)?;
+ let mut req_ext = String::with_capacity(100);
+ req_ext.push_str("permessage-deflate");
+ if self.settings.max_window_bits < 15 {
+ req_ext.push_str(&format!(
+ "; client_max_window_bits={}; server_max_window_bits={}",
+ self.settings.max_window_bits, self.settings.max_window_bits
+ ))
+ } else {
+ req_ext.push_str("; client_max_window_bits")
+ }
+ if self.settings.request_no_context_takeover {
+ req_ext.push_str("; server_no_context_takeover")
+ }
+ req.add_extension(&req_ext);
+ Ok(req)
+ }
+
+ fn on_request(&mut self, req: &Request) -> Result<Response> {
+ let mut res = self.inner.on_request(req)?;
+
+ 'ext: for req_ext in req.extensions()?
+ .iter()
+ .filter(|&&ext| ext.contains("permessage-deflate"))
+ {
+ let mut res_ext = String::with_capacity(req_ext.len());
+ let mut s_takeover = false;
+ let mut c_takeover = false;
+ let mut s_max = false;
+ let mut c_max = false;
+
+ for param in req_ext.split(';') {
+ match param.trim() {
+ "permessage-deflate" => res_ext.push_str("permessage-deflate"),
+ "server_no_context_takeover" => {
+ if s_takeover {
+ return self.decline(res);
+ } else {
+ s_takeover = true;
+ if self.settings.accept_no_context_takeover {
+ self.compress_reset = true;
+ res_ext.push_str("; server_no_context_takeover");
+ } else {
+ continue 'ext;
+ }
+ }
+ }
+ "client_no_context_takeover" => {
+ if c_takeover {
+ return self.decline(res);
+ } else {
+ c_takeover = true;
+ self.decompress_reset = true;
+ res_ext.push_str("; client_no_context_takeover");
+ }
+ }
+ param if param.starts_with("server_max_window_bits") => {
+ if s_max {
+ return self.decline(res);
+ } else {
+ s_max = true;
+ let mut param_iter = param.split('=');
+ param_iter.next(); // we already know the name
+ if let Some(window_bits_str) = param_iter.next() {
+ if let Ok(window_bits) = window_bits_str.trim().parse() {
+ if window_bits >= 9 && window_bits <= 15 {
+ if window_bits < self.settings.max_window_bits as i8 {
+ self.com = Compressor::new(window_bits);
+ res_ext.push_str("; ");
+ res_ext.push_str(param)
+ }
+ } else {
+ return self.decline(res);
+ }
+ } else {
+ return self.decline(res);
+ }
+ }
+ }
+ }
+ param if param.starts_with("client_max_window_bits") => {
+ if c_max {
+ return self.decline(res);
+ } else {
+ c_max = true;
+ let mut param_iter = param.split('=');
+ param_iter.next(); // we already know the name
+ if let Some(window_bits_str) = param_iter.next() {
+ if let Ok(window_bits) = window_bits_str.trim().parse() {
+ if window_bits >= 9 && window_bits <= 15 {
+ if window_bits < self.settings.max_window_bits as i8 {
+ self.dec = Decompressor::new(window_bits);
+ res_ext.push_str("; ");
+ res_ext.push_str(param);
+ continue;
+ }
+ } else {
+ return self.decline(res);
+ }
+ } else {
+ return self.decline(res);
+ }
+ }
+ res_ext.push_str("; ");
+ res_ext.push_str(&format!(
+ "client_max_window_bits={}",
+ self.settings.max_window_bits
+ ))
+ }
+ }
+ _ => {
+ // decline all extension offers because we got a bad parameter
+ return self.decline(res);
+ }
+ }
+ }
+
+ if !res_ext.contains("client_no_context_takeover")
+ && self.settings.request_no_context_takeover
+ {
+ self.decompress_reset = true;
+ res_ext.push_str("; client_no_context_takeover");
+ }
+
+ if !res_ext.contains("server_max_window_bits") {
+ res_ext.push_str("; ");
+ res_ext.push_str(&format!(
+ "server_max_window_bits={}",
+ self.settings.max_window_bits
+ ))
+ }
+
+ if !res_ext.contains("client_max_window_bits") && self.settings.max_window_bits < 15 {
+ continue;
+ }
+
+ res.add_extension(&res_ext);
+ return Ok(res);
+ }
+ self.decline(res)
+ }
+
+ fn on_response(&mut self, res: &Response) -> Result<()> {
+ if let Some(res_ext) = res.extensions()?
+ .iter()
+ .find(|&&ext| ext.contains("permessage-deflate"))
+ {
+ let mut name = false;
+ let mut s_takeover = false;
+ let mut c_takeover = false;
+ let mut s_max = false;
+ let mut c_max = false;
+
+ for param in res_ext.split(';') {
+ match param.trim() {
+ "permessage-deflate" => {
+ if name {
+ return Err(Error::new(
+ Kind::Protocol,
+ format!("Duplicate extension name permessage-deflate"),
+ ));
+ } else {
+ name = true;
+ }
+ }
+ "server_no_context_takeover" => {
+ if s_takeover {
+ return Err(Error::new(
+ Kind::Protocol,
+ format!("Duplicate extension parameter server_no_context_takeover"),
+ ));
+ } else {
+ s_takeover = true;
+ self.decompress_reset = true;
+ }
+ }
+ "client_no_context_takeover" => {
+ if c_takeover {
+ return Err(Error::new(
+ Kind::Protocol,
+ format!("Duplicate extension parameter client_no_context_takeover"),
+ ));
+ } else {
+ c_takeover = true;
+ if self.settings.accept_no_context_takeover {
+ self.compress_reset = true;
+ } else {
+ return Err(Error::new(
+ Kind::Protocol,
+ format!("The client requires context takeover."),
+ ));
+ }
+ }
+ }
+ param if param.starts_with("server_max_window_bits") => {
+ if s_max {
+ return Err(Error::new(
+ Kind::Protocol,
+ format!("Duplicate extension parameter server_max_window_bits"),
+ ));
+ } else {
+ s_max = true;
+ let mut param_iter = param.split('=');
+ param_iter.next(); // we already know the name
+ if let Some(window_bits_str) = param_iter.next() {
+ if let Ok(window_bits) = window_bits_str.trim().parse() {
+ if window_bits >= 9 && window_bits <= 15 {
+ if window_bits as u8 != self.settings.max_window_bits {
+ self.dec = Decompressor::new(window_bits);
+ }
+ } else {
+ return Err(Error::new(
+ Kind::Protocol,
+ format!(
+ "Invalid server_max_window_bits parameter: {}",
+ window_bits
+ ),
+ ));
+ }
+ } else {
+ return Err(Error::new(
+ Kind::Protocol,
+ format!(
+ "Invalid server_max_window_bits parameter: {}",
+ window_bits_str
+ ),
+ ));
+ }
+ }
+ }
+ }
+ param if param.starts_with("client_max_window_bits") => {
+ if c_max {
+ return Err(Error::new(
+ Kind::Protocol,
+ format!("Duplicate extension parameter client_max_window_bits"),
+ ));
+ } else {
+ c_max = true;
+ let mut param_iter = param.split('=');
+ param_iter.next(); // we already know the name
+ if let Some(window_bits_str) = param_iter.next() {
+ if let Ok(window_bits) = window_bits_str.trim().parse() {
+ if window_bits >= 9 && window_bits <= 15 {
+ if window_bits as u8 != self.settings.max_window_bits {
+ self.com = Compressor::new(window_bits);
+ }
+ } else {
+ return Err(Error::new(
+ Kind::Protocol,
+ format!(
+ "Invalid client_max_window_bits parameter: {}",
+ window_bits
+ ),
+ ));
+ }
+ } else {
+ return Err(Error::new(
+ Kind::Protocol,
+ format!(
+ "Invalid client_max_window_bits parameter: {}",
+ window_bits_str
+ ),
+ ));
+ }
+ }
+ }
+ }
+ param => {
+ // fail the connection because we got a bad parameter
+ return Err(Error::new(
+ Kind::Protocol,
+ format!("Bad extension parameter: {}", param),
+ ));
+ }
+ }
+ }
+ } else {
+ self.pass = true
+ }
+
+ Ok(())
+ }
+
+ fn on_frame(&mut self, mut frame: Frame) -> Result<Option<Frame>> {
+ if !self.pass && !frame.is_control() {
+ if !self.fragments.is_empty() || frame.has_rsv1() {
+ frame.set_rsv1(false);
+
+ if !frame.is_final() {
+ self.fragments.push(frame);
+ return Ok(None);
+ } else {
+ if frame.opcode() == OpCode::Continue {
+ if self.fragments.is_empty() {
+ return Err(Error::new(
+ Kind::Protocol,
+ "Unable to reconstruct fragmented message. No first frame.",
+ ));
+ } else {
+ if !self.settings.fragments_grow
+ && self.settings.fragments_capacity == self.fragments.len()
+ {
+ return Err(Error::new(Kind::Capacity, "Exceeded max fragments."));
+ } else {
+ self.fragments.push(frame);
+ }
+
+ // it's safe to unwrap because of the above check for empty
+ let opcode = self.fragments.first().unwrap().opcode();
+ let size = self.fragments
+ .iter()
+ .fold(0, |len, frame| len + frame.payload().len());
+ let mut compressed = Vec::with_capacity(size);
+ let mut decompressed = Vec::with_capacity(size * 2);
+ for frag in replace(
+ &mut self.fragments,
+ Vec::with_capacity(self.settings.fragments_capacity),
+ ) {
+ compressed.extend(frag.into_data())
+ }
+
+ compressed.extend(&[0, 0, 255, 255]);
+ self.dec.decompress(&compressed, &mut decompressed)?;
+ frame = Frame::message(decompressed, opcode, true);
+ }
+ } else {
+ let mut decompressed = Vec::with_capacity(frame.payload().len() * 2);
+ frame.payload_mut().extend(&[0, 0, 255, 255]);
+
+ self.dec.decompress(frame.payload(), &mut decompressed)?;
+
+ *frame.payload_mut() = decompressed;
+ }
+
+ if self.decompress_reset {
+ self.dec.reset()?
+ }
+ }
+ }
+ }
+ self.inner.on_frame(frame)
+ }
+
+ fn on_send_frame(&mut self, frame: Frame) -> Result<Option<Frame>> {
+ if let Some(mut frame) = self.inner.on_send_frame(frame)? {
+ if !self.pass && !frame.is_control() {
+ debug_assert!(
+ frame.is_final(),
+ "Received non-final frame from upstream handler!"
+ );
+ debug_assert!(
+ frame.opcode() != OpCode::Continue,
+ "Received continue frame from upstream handler!"
+ );
+
+ frame.set_rsv1(true);
+ let mut compressed = Vec::with_capacity(frame.payload().len());
+ self.com.compress(frame.payload(), &mut compressed)?;
+ let len = compressed.len();
+ compressed.truncate(len - 4);
+ *frame.payload_mut() = compressed;
+
+ if self.compress_reset {
+ self.com.reset()?
+ }
+ }
+ Ok(Some(frame))
+ } else {
+ Ok(None)
+ }
+ }
+
+ #[inline]
+ fn on_shutdown(&mut self) {
+ self.inner.on_shutdown()
+ }
+
+ #[inline]
+ fn on_open(&mut self, shake: Handshake) -> Result<()> {
+ self.inner.on_open(shake)
+ }
+
+ #[inline]
+ fn on_message(&mut self, msg: Message) -> Result<()> {
+ self.inner.on_message(msg)
+ }
+
+ #[inline]
+ fn on_close(&mut self, code: CloseCode, reason: &str) {
+ self.inner.on_close(code, reason)
+ }
+
+ #[inline]
+ fn on_error(&mut self, err: Error) {
+ self.inner.on_error(err)
+ }
+
+ #[inline]
+ fn on_timeout(&mut self, event: Token) -> Result<()> {
+ self.inner.on_timeout(event)
+ }
+
+ #[inline]
+ fn on_new_timeout(&mut self, tok: Token, timeout: Timeout) -> Result<()> {
+ self.inner.on_new_timeout(tok, timeout)
+ }
+
+ #[inline]
+ #[cfg(any(feature = "ssl", feature = "nativetls"))]
+ fn upgrade_ssl_client(
+ &mut self,
+ stream: TcpStream,
+ url: &url::Url,
+ ) -> Result<SslStream<TcpStream>> {
+ self.inner.upgrade_ssl_client(stream, url)
+ }
+
+ #[inline]
+ #[cfg(any(feature = "ssl", feature = "nativetls"))]
+ fn upgrade_ssl_server(&mut self, stream: TcpStream) -> Result<SslStream<TcpStream>> {
+ self.inner.upgrade_ssl_server(stream)
+ }
+}
diff --git a/third_party/rust/ws/src/deflate/mod.rs b/third_party/rust/ws/src/deflate/mod.rs
new file mode 100644
index 0000000000..8d79012e73
--- /dev/null
+++ b/third_party/rust/ws/src/deflate/mod.rs
@@ -0,0 +1,9 @@
+//! The deflate module provides tools for applying the permessage-deflate extension.
+
+extern crate libc;
+extern crate libz_sys as ffi;
+
+mod context;
+mod extension;
+
+pub use self::extension::{DeflateBuilder, DeflateHandler, DeflateSettings};
diff --git a/third_party/rust/ws/src/factory.rs b/third_party/rust/ws/src/factory.rs
new file mode 100644
index 0000000000..048ac78110
--- /dev/null
+++ b/third_party/rust/ws/src/factory.rs
@@ -0,0 +1,188 @@
+use communication::Sender;
+use handler::Handler;
+
+/// A trait for creating new WebSocket handlers.
+pub trait Factory {
+ type Handler: Handler;
+
+ /// Called when a TCP connection is made.
+ fn connection_made(&mut self, _: Sender) -> Self::Handler;
+
+ /// Called when the WebSocket is shutting down.
+ #[inline]
+ fn on_shutdown(&mut self) {
+ debug!("Factory received WebSocket shutdown request.");
+ }
+
+ /// Called when a new connection is established for a client endpoint.
+ /// This method can be used to differentiate a client aspect for a handler.
+ ///
+ /// ```
+ /// use ws::{Sender, Factory, Handler};
+ ///
+ /// struct MyHandler {
+ /// ws: Sender,
+ /// is_client: bool,
+ /// }
+ ///
+ /// impl Handler for MyHandler {}
+ ///
+ /// struct MyFactory;
+ ///
+ /// impl Factory for MyFactory {
+ /// type Handler = MyHandler;
+ ///
+ /// fn connection_made(&mut self, ws: Sender) -> MyHandler {
+ /// MyHandler {
+ /// ws: ws,
+ /// // default to server
+ /// is_client: false,
+ /// }
+ /// }
+ ///
+ /// fn client_connected(&mut self, ws: Sender) -> MyHandler {
+ /// MyHandler {
+ /// ws: ws,
+ /// is_client: true,
+ /// }
+ /// }
+ /// }
+ /// ```
+ #[inline]
+ fn client_connected(&mut self, ws: Sender) -> Self::Handler {
+ self.connection_made(ws)
+ }
+
+ /// Called when a new connection is established for a server endpoint.
+ /// This method can be used to differentiate a server aspect for a handler.
+ ///
+ /// ```
+ /// use ws::{Sender, Factory, Handler};
+ ///
+ /// struct MyHandler {
+ /// ws: Sender,
+ /// is_server: bool,
+ /// }
+ ///
+ /// impl Handler for MyHandler {}
+ ///
+ /// struct MyFactory;
+ ///
+ /// impl Factory for MyFactory {
+ /// type Handler = MyHandler;
+ ///
+ /// fn connection_made(&mut self, ws: Sender) -> MyHandler {
+ /// MyHandler {
+ /// ws: ws,
+ /// // default to client
+ /// is_server: false,
+ /// }
+ /// }
+ ///
+ /// fn server_connected(&mut self, ws: Sender) -> MyHandler {
+ /// MyHandler {
+ /// ws: ws,
+ /// is_server: true,
+ /// }
+ /// }
+ /// }
+ #[inline]
+ fn server_connected(&mut self, ws: Sender) -> Self::Handler {
+ self.connection_made(ws)
+ }
+
+ /// Called when a TCP connection is lost with the handler that was
+ /// setup for that connection.
+ ///
+ /// The default implementation is a noop that simply drops the handler.
+ /// You can use this to track connections being destroyed or to finalize
+ /// state that was not internally tracked by the handler.
+ #[inline]
+ fn connection_lost(&mut self, _: Self::Handler) {}
+}
+
+impl<F, H> Factory for F
+where
+ H: Handler,
+ F: FnMut(Sender) -> H,
+{
+ type Handler = H;
+
+ fn connection_made(&mut self, out: Sender) -> H {
+ self(out)
+ }
+}
+
+mod test {
+ #![allow(unused_imports, unused_variables, dead_code)]
+ use super::*;
+ use communication::{Command, Sender};
+ use frame;
+ use handler::Handler;
+ use handshake::{Handshake, Request, Response};
+ use message;
+ use mio;
+ use protocol::CloseCode;
+ use result::Result;
+
+ #[derive(Debug, Eq, PartialEq)]
+ struct M;
+ impl Handler for M {
+ fn on_message(&mut self, _: message::Message) -> Result<()> {
+ println!("test");
+ Ok(())
+ }
+
+ fn on_frame(&mut self, f: frame::Frame) -> Result<Option<frame::Frame>> {
+ Ok(None)
+ }
+ }
+
+ #[test]
+ fn impl_factory() {
+ struct X;
+
+ impl Factory for X {
+ type Handler = M;
+ fn connection_made(&mut self, _: Sender) -> M {
+ M
+ }
+ }
+
+ let (chn, _) = mio::channel::sync_channel(42);
+
+ let mut x = X;
+ let m = x.connection_made(Sender::new(mio::Token(0), chn, 0));
+ assert_eq!(m, M);
+ }
+
+ #[test]
+ fn closure_factory() {
+ let (chn, _) = mio::channel::sync_channel(42);
+
+ let mut factory = |_| |_| Ok(());
+
+ factory.connection_made(Sender::new(mio::Token(0), chn, 0));
+ }
+
+ #[test]
+ fn connection_lost() {
+ struct X;
+
+ impl Factory for X {
+ type Handler = M;
+ fn connection_made(&mut self, _: Sender) -> M {
+ M
+ }
+ fn connection_lost(&mut self, handler: M) {
+ assert_eq!(handler, M);
+ }
+ }
+
+ let (chn, _) = mio::channel::sync_channel(42);
+
+ let mut x = X;
+ let m = x.connection_made(Sender::new(mio::Token(0), chn, 0));
+ x.connection_lost(m);
+ }
+}
diff --git a/third_party/rust/ws/src/frame.rs b/third_party/rust/ws/src/frame.rs
new file mode 100644
index 0000000000..154816c7ad
--- /dev/null
+++ b/third_party/rust/ws/src/frame.rs
@@ -0,0 +1,495 @@
+use std::default::Default;
+use std::fmt;
+use std::io::{Cursor, ErrorKind, Read, Write};
+
+use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
+use rand;
+
+use protocol::{CloseCode, OpCode};
+use result::{Error, Kind, Result};
+use stream::TryReadBuf;
+
+fn apply_mask(buf: &mut [u8], mask: &[u8; 4]) {
+ let iter = buf.iter_mut().zip(mask.iter().cycle());
+ for (byte, &key) in iter {
+ *byte ^= key
+ }
+}
+
+/// A struct representing a WebSocket frame.
+#[derive(Debug, Clone)]
+pub struct Frame {
+ finished: bool,
+ rsv1: bool,
+ rsv2: bool,
+ rsv3: bool,
+ opcode: OpCode,
+
+ mask: Option<[u8; 4]>,
+
+ payload: Vec<u8>,
+}
+
+impl Frame {
+ /// Get the length of the frame.
+ /// This is the length of the header + the length of the payload.
+ #[inline]
+ pub fn len(&self) -> usize {
+ let mut header_length = 2;
+ let payload_len = self.payload().len();
+ if payload_len > 125 {
+ if payload_len <= u16::max_value() as usize {
+ header_length += 2;
+ } else {
+ header_length += 8;
+ }
+ }
+
+ if self.is_masked() {
+ header_length += 4;
+ }
+
+ header_length + payload_len
+ }
+
+ /// Return `false`: a frame is never empty since it has a header.
+ #[inline]
+ pub fn is_empty(&self) -> bool {
+ false
+ }
+
+ /// Test whether the frame is a final frame.
+ #[inline]
+ pub fn is_final(&self) -> bool {
+ self.finished
+ }
+
+ /// Test whether the first reserved bit is set.
+ #[inline]
+ pub fn has_rsv1(&self) -> bool {
+ self.rsv1
+ }
+
+ /// Test whether the second reserved bit is set.
+ #[inline]
+ pub fn has_rsv2(&self) -> bool {
+ self.rsv2
+ }
+
+ /// Test whether the third reserved bit is set.
+ #[inline]
+ pub fn has_rsv3(&self) -> bool {
+ self.rsv3
+ }
+
+ /// Get the OpCode of the frame.
+ #[inline]
+ pub fn opcode(&self) -> OpCode {
+ self.opcode
+ }
+
+ /// Test whether this is a control frame.
+ #[inline]
+ pub fn is_control(&self) -> bool {
+ self.opcode.is_control()
+ }
+
+ /// Get a reference to the frame's payload.
+ #[inline]
+ pub fn payload(&self) -> &Vec<u8> {
+ &self.payload
+ }
+
+ // Test whether the frame is masked.
+ #[doc(hidden)]
+ #[inline]
+ pub fn is_masked(&self) -> bool {
+ self.mask.is_some()
+ }
+
+ // Get an optional reference to the frame's mask.
+ #[doc(hidden)]
+ #[allow(dead_code)]
+ #[inline]
+ pub fn mask(&self) -> Option<&[u8; 4]> {
+ self.mask.as_ref()
+ }
+
+ /// Make this frame a final frame.
+ #[allow(dead_code)]
+ #[inline]
+ pub fn set_final(&mut self, is_final: bool) -> &mut Frame {
+ self.finished = is_final;
+ self
+ }
+
+ /// Set the first reserved bit.
+ #[inline]
+ pub fn set_rsv1(&mut self, has_rsv1: bool) -> &mut Frame {
+ self.rsv1 = has_rsv1;
+ self
+ }
+
+ /// Set the second reserved bit.
+ #[inline]
+ pub fn set_rsv2(&mut self, has_rsv2: bool) -> &mut Frame {
+ self.rsv2 = has_rsv2;
+ self
+ }
+
+ /// Set the third reserved bit.
+ #[inline]
+ pub fn set_rsv3(&mut self, has_rsv3: bool) -> &mut Frame {
+ self.rsv3 = has_rsv3;
+ self
+ }
+
+ /// Set the OpCode.
+ #[allow(dead_code)]
+ #[inline]
+ pub fn set_opcode(&mut self, opcode: OpCode) -> &mut Frame {
+ self.opcode = opcode;
+ self
+ }
+
+ /// Edit the frame's payload.
+ #[allow(dead_code)]
+ #[inline]
+ pub fn payload_mut(&mut self) -> &mut Vec<u8> {
+ &mut self.payload
+ }
+
+ // Generate a new mask for this frame.
+ //
+ // This method simply generates and stores the mask. It does not change the payload data.
+ // Instead, the payload data will be masked with the generated mask when the frame is sent
+ // to the other endpoint.
+ #[doc(hidden)]
+ #[inline]
+ pub fn set_mask(&mut self) -> &mut Frame {
+ self.mask = Some(rand::random());
+ self
+ }
+
+ // This method unmasks the payload and should only be called on frames that are actually
+ // masked. In other words, those frames that have just been received from a client endpoint.
+ #[doc(hidden)]
+ #[inline]
+ pub fn remove_mask(&mut self) -> &mut Frame {
+ self.mask
+ .take()
+ .map(|mask| apply_mask(&mut self.payload, &mask));
+ self
+ }
+
+ /// Consume the frame into its payload.
+ pub fn into_data(self) -> Vec<u8> {
+ self.payload
+ }
+
+ /// Create a new data frame.
+ #[inline]
+ pub fn message(data: Vec<u8>, code: OpCode, finished: bool) -> Frame {
+ debug_assert!(
+ match code {
+ OpCode::Text | OpCode::Binary | OpCode::Continue => true,
+ _ => false,
+ },
+ "Invalid opcode for data frame."
+ );
+
+ Frame {
+ finished,
+ opcode: code,
+ payload: data,
+ ..Frame::default()
+ }
+ }
+
+ /// Create a new Pong control frame.
+ #[inline]
+ pub fn pong(data: Vec<u8>) -> Frame {
+ Frame {
+ opcode: OpCode::Pong,
+ payload: data,
+ ..Frame::default()
+ }
+ }
+
+ /// Create a new Ping control frame.
+ #[inline]
+ pub fn ping(data: Vec<u8>) -> Frame {
+ Frame {
+ opcode: OpCode::Ping,
+ payload: data,
+ ..Frame::default()
+ }
+ }
+
+ /// Create a new Close control frame.
+ #[inline]
+ pub fn close(code: CloseCode, reason: &str) -> Frame {
+ let payload = if let CloseCode::Empty = code {
+ Vec::new()
+ } else {
+ let u: u16 = code.into();
+ let raw = [(u >> 8) as u8, u as u8];
+ [&raw, reason.as_bytes()].concat()
+ };
+
+ Frame {
+ payload,
+ ..Frame::default()
+ }
+ }
+
+ /// Parse the input stream into a frame.
+ pub fn parse(cursor: &mut Cursor<Vec<u8>>, max_payload_length: u64) -> Result<Option<Frame>> {
+ let size = cursor.get_ref().len() as u64 - cursor.position();
+ let initial = cursor.position();
+ trace!("Position in buffer {}", initial);
+
+ let mut head = [0u8; 2];
+ if cursor.read(&mut head)? != 2 {
+ cursor.set_position(initial);
+ return Ok(None);
+ }
+
+ trace!("Parsed headers {:?}", head);
+
+ let first = head[0];
+ let second = head[1];
+ trace!("First: {:b}", first);
+ trace!("Second: {:b}", second);
+
+ let finished = first & 0x80 != 0;
+
+ let rsv1 = first & 0x40 != 0;
+ let rsv2 = first & 0x20 != 0;
+ let rsv3 = first & 0x10 != 0;
+
+ let opcode = OpCode::from(first & 0x0F);
+ trace!("Opcode: {:?}", opcode);
+
+ let masked = second & 0x80 != 0;
+ trace!("Masked: {:?}", masked);
+
+ let mut header_length = 2;
+
+ let mut length = u64::from(second & 0x7F);
+
+ if let Some(length_nbytes) = match length {
+ 126 => Some(2),
+ 127 => Some(8),
+ _ => None,
+ } {
+ match cursor.read_uint::<BigEndian>(length_nbytes) {
+ Err(ref err) if err.kind() == ErrorKind::UnexpectedEof => {
+ cursor.set_position(initial);
+ return Ok(None);
+ }
+ Err(err) => {
+ return Err(Error::from(err));
+ }
+ Ok(read) => {
+ length = read;
+ }
+ };
+ header_length += length_nbytes as u64;
+ }
+ trace!("Payload length: {}", length);
+
+ if length > max_payload_length {
+ return Err(Error::new(
+ Kind::Protocol,
+ format!(
+ "Rejected frame with payload length exceeding defined max: {}.",
+ max_payload_length
+ ),
+ ));
+ }
+
+ let mask = if masked {
+ let mut mask_bytes = [0u8; 4];
+ if cursor.read(&mut mask_bytes)? != 4 {
+ cursor.set_position(initial);
+ return Ok(None);
+ } else {
+ header_length += 4;
+ Some(mask_bytes)
+ }
+ } else {
+ None
+ };
+
+ match length.checked_add(header_length) {
+ Some(l) if size < l => {
+ cursor.set_position(initial);
+ return Ok(None);
+ }
+ Some(_) => (),
+ None => return Ok(None),
+ };
+
+ let mut data = Vec::with_capacity(length as usize);
+ if length > 0 {
+ if let Some(read) = cursor.try_read_buf(&mut data)? {
+ debug_assert!(read == length as usize, "Read incorrect payload length!");
+ }
+ }
+
+ // Disallow bad opcode
+ if let OpCode::Bad = opcode {
+ return Err(Error::new(
+ Kind::Protocol,
+ format!("Encountered invalid opcode: {}", first & 0x0F),
+ ));
+ }
+
+ // control frames must have length <= 125
+ match opcode {
+ OpCode::Ping | OpCode::Pong if length > 125 => {
+ return Err(Error::new(
+ Kind::Protocol,
+ format!(
+ "Rejected WebSocket handshake.Received control frame with length: {}.",
+ length
+ ),
+ ))
+ }
+ OpCode::Close if length > 125 => {
+ debug!("Received close frame with payload length exceeding 125. Morphing to protocol close frame.");
+ return Ok(Some(Frame::close(
+ CloseCode::Protocol,
+ "Received close frame with payload length exceeding 125.",
+ )));
+ }
+ _ => (),
+ }
+
+ let frame = Frame {
+ finished,
+ rsv1,
+ rsv2,
+ rsv3,
+ opcode,
+ mask,
+ payload: data,
+ };
+
+ Ok(Some(frame))
+ }
+
+ /// Write a frame out to a buffer
+ pub fn format<W>(&mut self, w: &mut W) -> Result<()>
+ where
+ W: Write,
+ {
+ let mut one = 0u8;
+ let code: u8 = self.opcode.into();
+ if self.is_final() {
+ one |= 0x80;
+ }
+ if self.has_rsv1() {
+ one |= 0x40;
+ }
+ if self.has_rsv2() {
+ one |= 0x20;
+ }
+ if self.has_rsv3() {
+ one |= 0x10;
+ }
+ one |= code;
+
+ let mut two = 0u8;
+ if self.is_masked() {
+ two |= 0x80;
+ }
+
+ match self.payload.len() {
+ len if len < 126 => {
+ two |= len as u8;
+ }
+ len if len <= 65535 => {
+ two |= 126;
+ }
+ _ => {
+ two |= 127;
+ }
+ }
+ w.write_all(&[one, two])?;
+
+ if let Some(length_bytes) = match self.payload.len() {
+ len if len < 126 => None,
+ len if len <= 65535 => Some(2),
+ _ => Some(8),
+ } {
+ w.write_uint::<BigEndian>(self.payload.len() as u64, length_bytes)?;
+ }
+
+ if self.is_masked() {
+ let mask = self.mask.take().unwrap();
+ apply_mask(&mut self.payload, &mask);
+ w.write_all(&mask)?;
+ }
+
+ w.write_all(&self.payload)?;
+ Ok(())
+ }
+}
+
+impl Default for Frame {
+ fn default() -> Frame {
+ Frame {
+ finished: true,
+ rsv1: false,
+ rsv2: false,
+ rsv3: false,
+ opcode: OpCode::Close,
+ mask: None,
+ payload: Vec::new(),
+ }
+ }
+}
+
+impl fmt::Display for Frame {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(
+ f,
+ "
+<FRAME>
+final: {}
+reserved: {} {} {}
+opcode: {}
+length: {}
+payload length: {}
+payload: 0x{}
+ ",
+ self.finished,
+ self.rsv1,
+ self.rsv2,
+ self.rsv3,
+ self.opcode,
+ // self.mask.map(|mask| format!("{:?}", mask)).unwrap_or("NONE".into()),
+ self.len(),
+ self.payload.len(),
+ self.payload
+ .iter()
+ .map(|byte| format!("{:x}", byte))
+ .collect::<String>()
+ )
+ }
+}
+
+mod test {
+ #![allow(unused_imports, unused_variables, dead_code)]
+ use super::*;
+ use protocol::OpCode;
+
+ #[test]
+ fn display_frame() {
+ let f = Frame::message("hi there".into(), OpCode::Text, true);
+ let view = format!("{}", f);
+ view.contains("payload:");
+ }
+}
diff --git a/third_party/rust/ws/src/handler.rs b/third_party/rust/ws/src/handler.rs
new file mode 100644
index 0000000000..7c1ef21b4b
--- /dev/null
+++ b/third_party/rust/ws/src/handler.rs
@@ -0,0 +1,423 @@
+use log::Level::Error as ErrorLevel;
+#[cfg(feature = "nativetls")]
+use native_tls::{TlsConnector, TlsStream as SslStream};
+#[cfg(feature = "ssl")]
+use openssl::ssl::{SslConnector, SslMethod, SslStream};
+use url;
+
+use frame::Frame;
+use handshake::{Handshake, Request, Response};
+use message::Message;
+use protocol::CloseCode;
+use result::{Error, Kind, Result};
+use util::{Timeout, Token};
+
+#[cfg(any(feature = "ssl", feature = "nativetls"))]
+use util::TcpStream;
+
+/// The core trait of this library.
+/// Implementing this trait provides the business logic of the WebSocket application.
+pub trait Handler {
+ // general
+
+ /// Called when a request to shutdown all connections has been received.
+ #[inline]
+ fn on_shutdown(&mut self) {
+ debug!("Handler received WebSocket shutdown request.");
+ }
+
+ // WebSocket events
+
+ /// Called when the WebSocket handshake is successful and the connection is open for sending
+ /// and receiving messages.
+ fn on_open(&mut self, shake: Handshake) -> Result<()> {
+ if let Some(addr) = shake.remote_addr()? {
+ debug!("Connection with {} now open", addr);
+ }
+ Ok(())
+ }
+
+ /// Called on incoming messages.
+ fn on_message(&mut self, msg: Message) -> Result<()> {
+ debug!("Received message {:?}", msg);
+ Ok(())
+ }
+
+ /// Called any time this endpoint receives a close control frame.
+ /// This may be because the other endpoint is initiating a closing handshake,
+ /// or it may be the other endpoint confirming the handshake initiated by this endpoint.
+ fn on_close(&mut self, code: CloseCode, reason: &str) {
+ debug!("Connection closing due to ({:?}) {}", code, reason);
+ }
+
+ /// Called when an error occurs on the WebSocket.
+ fn on_error(&mut self, err: Error) {
+ // Ignore connection reset errors by default, but allow library clients to see them by
+ // overriding this method if they want
+ if let Kind::Io(ref err) = err.kind {
+ if let Some(104) = err.raw_os_error() {
+ return;
+ }
+ }
+
+ error!("{:?}", err);
+ if !log_enabled!(ErrorLevel) {
+ println!(
+ "Encountered an error: {}\nEnable a logger to see more information.",
+ err
+ );
+ }
+ }
+
+ // handshake events
+
+ /// A method for handling the low-level workings of the request portion of the WebSocket
+ /// handshake.
+ ///
+ /// Implementors should select a WebSocket protocol and extensions where they are supported.
+ ///
+ /// Implementors can inspect the Request and must return a Response or an error
+ /// indicating that the handshake failed. The default implementation provides conformance with
+ /// the WebSocket protocol, and implementors should use the `Response::from_request` method and
+ /// then modify the resulting response as necessary in order to maintain conformance.
+ ///
+ /// This method will not be called when the handler represents a client endpoint. Use
+ /// `build_request` to provide an initial handshake request.
+ ///
+ /// # Examples
+ ///
+ /// ```ignore
+ /// let mut res = try!(Response::from_request(req));
+ /// if try!(req.extensions()).iter().find(|&&ext| ext.contains("myextension-name")).is_some() {
+ /// res.add_extension("myextension-name")
+ /// }
+ /// Ok(res)
+ /// ```
+ #[inline]
+ fn on_request(&mut self, req: &Request) -> Result<Response> {
+ debug!("Handler received request:\n{}", req);
+ Response::from_request(req)
+ }
+
+ /// A method for handling the low-level workings of the response portion of the WebSocket
+ /// handshake.
+ ///
+ /// Implementors can inspect the Response and choose to fail the connection by
+ /// returning an error. This method will not be called when the handler represents a server
+ /// endpoint. The response should indicate which WebSocket protocol and extensions the server
+ /// has agreed to if any.
+ #[inline]
+ fn on_response(&mut self, res: &Response) -> Result<()> {
+ debug!("Handler received response:\n{}", res);
+ Ok(())
+ }
+
+ // timeout events
+
+ /// Called when a timeout is triggered.
+ ///
+ /// This method will be called when the eventloop encounters a timeout on the specified
+ /// token. To schedule a timeout with your specific token use the `Sender::timeout` method.
+ ///
+ /// # Examples
+ ///
+ /// ```ignore
+ /// const GRATI: Token = Token(1);
+ ///
+ /// ... Handler
+ ///
+ /// fn on_open(&mut self, _: Handshake) -> Result<()> {
+ /// // schedule a timeout to send a gratuitous pong every 5 seconds
+ /// self.ws.timeout(5_000, GRATI)
+ /// }
+ ///
+ /// fn on_timeout(&mut self, event: Token) -> Result<()> {
+ /// if event == GRATI {
+ /// // send gratuitous pong
+ /// try!(self.ws.pong(vec![]))
+ /// // reschedule the timeout
+ /// self.ws.timeout(5_000, GRATI)
+ /// } else {
+ /// Err(Error::new(ErrorKind::Internal, "Invalid timeout token encountered!"))
+ /// }
+ /// }
+ /// ```
+ #[inline]
+ fn on_timeout(&mut self, event: Token) -> Result<()> {
+ debug!("Handler received timeout token: {:?}", event);
+ Ok(())
+ }
+
+ /// Called when a timeout has been scheduled on the eventloop.
+ ///
+ /// This method is the hook for obtaining a Timeout object that may be used to cancel a
+ /// timeout. This is a noop by default.
+ ///
+ /// # Examples
+ ///
+ /// ```ignore
+ /// const PING: Token = Token(1);
+ /// const EXPIRE: Token = Token(2);
+ ///
+ /// ... Handler
+ ///
+ /// fn on_open(&mut self, _: Handshake) -> Result<()> {
+ /// // schedule a timeout to send a ping every 5 seconds
+ /// try!(self.ws.timeout(5_000, PING));
+ /// // schedule a timeout to close the connection if there is no activity for 30 seconds
+ /// self.ws.timeout(30_000, EXPIRE)
+ /// }
+ ///
+ /// fn on_timeout(&mut self, event: Token) -> Result<()> {
+ /// match event {
+ /// PING => {
+ /// self.ws.ping(vec![]);
+ /// self.ws.timeout(5_000, PING)
+ /// }
+ /// EXPIRE => self.ws.close(CloseCode::Away),
+ /// _ => Err(Error::new(ErrorKind::Internal, "Invalid timeout token encountered!")),
+ /// }
+ /// }
+ ///
+ /// fn on_new_timeout(&mut self, event: Token, timeout: Timeout) -> Result<()> {
+ /// if event == EXPIRE {
+ /// if let Some(t) = self.timeout.take() {
+ /// try!(self.ws.cancel(t))
+ /// }
+ /// self.timeout = Some(timeout)
+ /// }
+ /// Ok(())
+ /// }
+ ///
+ /// fn on_frame(&mut self, frame: Frame) -> Result<Option<Frame>> {
+ /// // some activity has occurred, let's reset the expiration
+ /// try!(self.ws.timeout(30_000, EXPIRE));
+ /// Ok(Some(frame))
+ /// }
+ /// ```
+ #[inline]
+ fn on_new_timeout(&mut self, _: Token, _: Timeout) -> Result<()> {
+ // default implementation discards the timeout handle
+ Ok(())
+ }
+
+ // frame events
+
+ /// A method for handling incoming frames.
+ ///
+ /// This method provides very low-level access to the details of the WebSocket protocol. It may
+ /// be necessary to implement this method in order to provide a particular extension, but
+ /// incorrect implementation may cause the other endpoint to fail the connection.
+ ///
+ /// Returning `Ok(None)` will cause the connection to forget about a particular frame. This is
+ /// useful if you want ot filter out a frame or if you don't want any of the default handler
+ /// methods to run.
+ ///
+ /// By default this method simply ensures that no reserved bits are set.
+ #[inline]
+ fn on_frame(&mut self, frame: Frame) -> Result<Option<Frame>> {
+ debug!("Handler received: {}", frame);
+ // default implementation doesn't allow for reserved bits to be set
+ if frame.has_rsv1() || frame.has_rsv2() || frame.has_rsv3() {
+ Err(Error::new(
+ Kind::Protocol,
+ "Encountered frame with reserved bits set.",
+ ))
+ } else {
+ Ok(Some(frame))
+ }
+ }
+
+ /// A method for handling outgoing frames.
+ ///
+ /// This method provides very low-level access to the details of the WebSocket protocol. It may
+ /// be necessary to implement this method in order to provide a particular extension, but
+ /// incorrect implementation may cause the other endpoint to fail the connection.
+ ///
+ /// Returning `Ok(None)` will cause the connection to forget about a particular frame, meaning
+ /// that it will not be sent. You can use this approach to merge multiple frames into a single
+ /// frame before sending the message.
+ ///
+ /// For messages, this method will be called with a single complete, final frame before any
+ /// fragmentation is performed. Automatic fragmentation will be performed on the returned
+ /// frame, if any, based on the `fragment_size` setting.
+ ///
+ /// By default this method simply ensures that no reserved bits are set.
+ #[inline]
+ fn on_send_frame(&mut self, frame: Frame) -> Result<Option<Frame>> {
+ trace!("Handler will send: {}", frame);
+ // default implementation doesn't allow for reserved bits to be set
+ if frame.has_rsv1() || frame.has_rsv2() || frame.has_rsv3() {
+ Err(Error::new(
+ Kind::Protocol,
+ "Encountered frame with reserved bits set.",
+ ))
+ } else {
+ Ok(Some(frame))
+ }
+ }
+
+ // constructors
+
+ /// A method for creating the initial handshake request for WebSocket clients.
+ ///
+ /// The default implementation provides conformance with the WebSocket protocol, but this
+ /// method may be overridden. In order to facilitate conformance,
+ /// implementors should use the `Request::from_url` method and then modify the resulting
+ /// request as necessary.
+ ///
+ /// Implementors should indicate any available WebSocket extensions here.
+ ///
+ /// # Examples
+ /// ```ignore
+ /// let mut req = try!(Request::from_url(url));
+ /// req.add_extension("permessage-deflate; client_max_window_bits");
+ /// Ok(req)
+ /// ```
+ #[inline]
+ fn build_request(&mut self, url: &url::Url) -> Result<Request> {
+ trace!("Handler is building request to {}.", url);
+ Request::from_url(url)
+ }
+
+ /// A method for wrapping a client TcpStream with Ssl Authentication machinery
+ ///
+ /// Override this method to customize how the connection is encrypted. By default
+ /// this will use the Server Name Indication extension in conformance with RFC6455.
+ #[inline]
+ #[cfg(feature = "ssl")]
+ fn upgrade_ssl_client(
+ &mut self,
+ stream: TcpStream,
+ url: &url::Url,
+ ) -> Result<SslStream<TcpStream>> {
+ let domain = url.domain().ok_or(Error::new(
+ Kind::Protocol,
+ format!("Unable to parse domain from {}. Needed for SSL.", url),
+ ))?;
+ let connector = SslConnector::builder(SslMethod::tls())
+ .map_err(|e| {
+ Error::new(
+ Kind::Internal,
+ format!("Failed to upgrade client to SSL: {}", e),
+ )
+ })?
+ .build();
+ connector.connect(domain, stream).map_err(Error::from)
+ }
+
+ #[inline]
+ #[cfg(feature = "nativetls")]
+ fn upgrade_ssl_client(
+ &mut self,
+ stream: TcpStream,
+ url: &url::Url,
+ ) -> Result<SslStream<TcpStream>> {
+ let domain = url.domain().ok_or(Error::new(
+ Kind::Protocol,
+ format!("Unable to parse domain from {}. Needed for SSL.", url),
+ ))?;
+
+ let connector = TlsConnector::new().map_err(|e| {
+ Error::new(
+ Kind::Internal,
+ format!("Failed to upgrade client to SSL: {}", e),
+ )
+ })?;
+
+ connector.connect(domain, stream).map_err(Error::from)
+ }
+ /// A method for wrapping a server TcpStream with Ssl Authentication machinery
+ ///
+ /// Override this method to customize how the connection is encrypted. By default
+ /// this method is not implemented.
+ #[inline]
+ #[cfg(any(feature = "ssl", feature = "nativetls"))]
+ fn upgrade_ssl_server(&mut self, _: TcpStream) -> Result<SslStream<TcpStream>> {
+ unimplemented!()
+ }
+}
+
+impl<F> Handler for F
+where
+ F: Fn(Message) -> Result<()>,
+{
+ fn on_message(&mut self, msg: Message) -> Result<()> {
+ self(msg)
+ }
+}
+
+mod test {
+ #![allow(unused_imports, unused_variables, dead_code)]
+ use super::*;
+ use frame;
+ use handshake::{Handshake, Request, Response};
+ use message;
+ use mio;
+ use protocol::CloseCode;
+ use result::Result;
+ use url;
+
+ #[derive(Debug, Eq, PartialEq)]
+ struct M;
+ impl Handler for M {
+ fn on_message(&mut self, _: message::Message) -> Result<()> {
+ println!("test");
+ Ok(())
+ }
+
+ fn on_frame(&mut self, f: frame::Frame) -> Result<Option<frame::Frame>> {
+ Ok(None)
+ }
+ }
+
+ #[test]
+ fn handler() {
+ struct H;
+
+ impl Handler for H {
+ fn on_open(&mut self, shake: Handshake) -> Result<()> {
+ assert!(shake.request.key().is_ok());
+ assert!(shake.response.key().is_ok());
+ Ok(())
+ }
+
+ fn on_message(&mut self, msg: message::Message) -> Result<()> {
+ Ok(assert_eq!(
+ msg,
+ message::Message::Text(String::from("testme"))
+ ))
+ }
+
+ fn on_close(&mut self, code: CloseCode, _: &str) {
+ assert_eq!(code, CloseCode::Normal)
+ }
+ }
+
+ let mut h = H;
+ let url = url::Url::parse("wss://127.0.0.1:3012").unwrap();
+ let req = Request::from_url(&url).unwrap();
+ let res = Response::from_request(&req).unwrap();
+ h.on_open(Handshake {
+ request: req,
+ response: res,
+ peer_addr: None,
+ local_addr: None,
+ }).unwrap();
+ h.on_message(message::Message::Text("testme".to_owned()))
+ .unwrap();
+ h.on_close(CloseCode::Normal, "");
+ }
+
+ #[test]
+ fn closure_handler() {
+ let mut close = |msg| {
+ assert_eq!(msg, message::Message::Binary(vec![1, 2, 3]));
+ Ok(())
+ };
+
+ close
+ .on_message(message::Message::Binary(vec![1, 2, 3]))
+ .unwrap();
+ }
+}
diff --git a/third_party/rust/ws/src/handshake.rs b/third_party/rust/ws/src/handshake.rs
new file mode 100644
index 0000000000..b7520bde56
--- /dev/null
+++ b/third_party/rust/ws/src/handshake.rs
@@ -0,0 +1,740 @@
+use std::fmt;
+use std::io::Write;
+use std::net::SocketAddr;
+use std::str::from_utf8;
+
+use httparse;
+use rand;
+use sha1::{self, Digest};
+use url;
+
+use result::{Error, Kind, Result};
+
+static WS_GUID: &'static str = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
+static BASE64: &'static [u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+const MAX_HEADERS: usize = 124;
+
+fn generate_key() -> String {
+ let key: [u8; 16] = rand::random();
+ encode_base64(&key)
+}
+
+pub fn hash_key(key: &[u8]) -> String {
+ let mut hasher = sha1::Sha1::new();
+
+ hasher.input(key);
+ hasher.input(WS_GUID.as_bytes());
+
+ encode_base64(&hasher.result())
+}
+
+// This code is based on rustc_serialize base64 STANDARD
+fn encode_base64(data: &[u8]) -> String {
+ let len = data.len();
+ let mod_len = len % 3;
+
+ let mut encoded = vec![b'='; (len + 2) / 3 * 4];
+ {
+ let mut in_iter = data[..len - mod_len].iter().map(|&c| u32::from(c));
+ let mut out_iter = encoded.iter_mut();
+
+ let enc = |val| BASE64[val as usize];
+ let mut write = |val| *out_iter.next().unwrap() = val;
+
+ while let (Some(one), Some(two), Some(three)) =
+ (in_iter.next(), in_iter.next(), in_iter.next())
+ {
+ let g24 = one << 16 | two << 8 | three;
+ write(enc((g24 >> 18) & 63));
+ write(enc((g24 >> 12) & 63));
+ write(enc((g24 >> 6) & 63));
+ write(enc(g24 & 63));
+ }
+
+ match mod_len {
+ 1 => {
+ let pad = (u32::from(data[len - 1])) << 16;
+ write(enc((pad >> 18) & 63));
+ write(enc((pad >> 12) & 63));
+ }
+ 2 => {
+ let pad = (u32::from(data[len - 2])) << 16 | (u32::from(data[len - 1])) << 8;
+ write(enc((pad >> 18) & 63));
+ write(enc((pad >> 12) & 63));
+ write(enc((pad >> 6) & 63));
+ }
+ _ => (),
+ }
+ }
+
+ String::from_utf8(encoded).unwrap()
+}
+
+/// A struct representing the two halves of the WebSocket handshake.
+#[derive(Debug)]
+pub struct Handshake {
+ /// The HTTP request sent to begin the handshake.
+ pub request: Request,
+ /// The HTTP response from the server confirming the handshake.
+ pub response: Response,
+ /// The socket address of the other endpoint. This address may
+ /// be an intermediary such as a proxy server.
+ pub peer_addr: Option<SocketAddr>,
+ /// The socket address of this endpoint.
+ pub local_addr: Option<SocketAddr>,
+}
+
+impl Handshake {
+ /// Get the IP address of the remote connection.
+ ///
+ /// This is the preferred method of obtaining the client's IP address.
+ /// It will attempt to retrieve the most likely IP address based on request
+ /// headers, falling back to the address of the peer.
+ ///
+ /// # Note
+ /// This assumes that the peer is a client. If you are implementing a
+ /// WebSocket client and want to obtain the address of the server, use
+ /// `Handshake::peer_addr` instead.
+ ///
+ /// This method does not ensure that the address is a valid IP address.
+ #[allow(dead_code)]
+ pub fn remote_addr(&self) -> Result<Option<String>> {
+ Ok(self.request.client_addr()?.map(String::from).or_else(|| {
+ if let Some(addr) = self.peer_addr {
+ Some(addr.ip().to_string())
+ } else {
+ None
+ }
+ }))
+ }
+}
+
+/// The handshake request.
+#[derive(Debug)]
+pub struct Request {
+ path: String,
+ method: String,
+ headers: Vec<(String, Vec<u8>)>,
+}
+
+impl Request {
+ /// Get the value of the first instance of an HTTP header.
+ pub fn header(&self, header: &str) -> Option<&Vec<u8>> {
+ self.headers
+ .iter()
+ .find(|&&(ref key, _)| key.to_lowercase() == header.to_lowercase())
+ .map(|&(_, ref val)| val)
+ }
+
+ /// Edit the value of the first instance of an HTTP header.
+ pub fn header_mut(&mut self, header: &str) -> Option<&mut Vec<u8>> {
+ self.headers
+ .iter_mut()
+ .find(|&&mut (ref key, _)| key.to_lowercase() == header.to_lowercase())
+ .map(|&mut (_, ref mut val)| val)
+ }
+
+ /// Access the request headers.
+ #[allow(dead_code)]
+ #[inline]
+ pub fn headers(&self) -> &Vec<(String, Vec<u8>)> {
+ &self.headers
+ }
+
+ /// Edit the request headers.
+ #[allow(dead_code)]
+ #[inline]
+ pub fn headers_mut(&mut self) -> &mut Vec<(String, Vec<u8>)> {
+ &mut self.headers
+ }
+
+ /// Get the origin of the request if it comes from a browser.
+ #[allow(dead_code)]
+ pub fn origin(&self) -> Result<Option<&str>> {
+ if let Some(origin) = self.header("origin") {
+ Ok(Some(from_utf8(origin)?))
+ } else {
+ Ok(None)
+ }
+ }
+
+ /// Get the unhashed WebSocket key sent in the request.
+ pub fn key(&self) -> Result<&Vec<u8>> {
+ self.header("sec-websocket-key")
+ .ok_or_else(|| Error::new(Kind::Protocol, "Unable to parse WebSocket key."))
+ }
+
+ /// Get the hashed WebSocket key from this request.
+ pub fn hashed_key(&self) -> Result<String> {
+ Ok(hash_key(self.key()?))
+ }
+
+ /// Get the WebSocket protocol version from the request (should be 13).
+ #[allow(dead_code)]
+ pub fn version(&self) -> Result<&str> {
+ if let Some(version) = self.header("sec-websocket-version") {
+ from_utf8(version).map_err(Error::from)
+ } else {
+ Err(Error::new(
+ Kind::Protocol,
+ "The Sec-WebSocket-Version header is missing.",
+ ))
+ }
+ }
+
+ /// Get the request method.
+ #[inline]
+ pub fn method(&self) -> &str {
+ &self.method
+ }
+
+ /// Get the path of the request.
+ #[allow(dead_code)]
+ #[inline]
+ pub fn resource(&self) -> &str {
+ &self.path
+ }
+
+ /// Get the possible protocols for the WebSocket connection.
+ #[allow(dead_code)]
+ pub fn protocols(&self) -> Result<Vec<&str>> {
+ if let Some(protos) = self.header("sec-websocket-protocol") {
+ Ok(from_utf8(protos)?
+ .split(',')
+ .map(|proto| proto.trim())
+ .collect())
+ } else {
+ Ok(Vec::new())
+ }
+ }
+
+ /// Add a possible protocol to this request.
+ /// This may result in duplicate protocols listed.
+ #[allow(dead_code)]
+ pub fn add_protocol(&mut self, protocol: &str) {
+ if let Some(protos) = self.header_mut("sec-websocket-protocol") {
+ protos.push(b","[0]);
+ protos.extend(protocol.as_bytes());
+ return;
+ }
+ self.headers_mut()
+ .push(("Sec-WebSocket-Protocol".into(), protocol.into()))
+ }
+
+ /// Remove a possible protocol from this request.
+ #[allow(dead_code)]
+ pub fn remove_protocol(&mut self, protocol: &str) {
+ if let Some(protos) = self.header_mut("sec-websocket-protocol") {
+ let mut new_protos = Vec::with_capacity(protos.len());
+
+ if let Ok(protos_str) = from_utf8(protos) {
+ new_protos = protos_str
+ .split(',')
+ .filter(|proto| proto.trim() == protocol)
+ .collect::<Vec<&str>>()
+ .join(",")
+ .into();
+ }
+ if new_protos.len() < protos.len() {
+ *protos = new_protos
+ }
+ }
+ }
+
+ /// Get the possible extensions for the WebSocket connection.
+ #[allow(dead_code)]
+ pub fn extensions(&self) -> Result<Vec<&str>> {
+ if let Some(exts) = self.header("sec-websocket-extensions") {
+ Ok(from_utf8(exts)?.split(',').map(|ext| ext.trim()).collect())
+ } else {
+ Ok(Vec::new())
+ }
+ }
+
+ /// Add a possible extension to this request.
+ /// This may result in duplicate extensions listed. Also, the order of extensions
+ /// indicates preference, so if the preference matters, consider using the
+ /// `Sec-WebSocket-Protocol` header directly.
+ #[allow(dead_code)]
+ pub fn add_extension(&mut self, ext: &str) {
+ if let Some(exts) = self.header_mut("sec-websocket-extensions") {
+ exts.push(b","[0]);
+ exts.extend(ext.as_bytes());
+ return;
+ }
+ self.headers_mut()
+ .push(("Sec-WebSocket-Extensions".into(), ext.into()))
+ }
+
+ /// Remove a possible extension from this request.
+ /// This will remove all configurations of the extension.
+ #[allow(dead_code)]
+ pub fn remove_extension(&mut self, ext: &str) {
+ if let Some(exts) = self.header_mut("sec-websocket-extensions") {
+ let mut new_exts = Vec::with_capacity(exts.len());
+
+ if let Ok(exts_str) = from_utf8(exts) {
+ new_exts = exts_str
+ .split(',')
+ .filter(|e| e.trim().starts_with(ext))
+ .collect::<Vec<&str>>()
+ .join(",")
+ .into();
+ }
+ if new_exts.len() < exts.len() {
+ *exts = new_exts
+ }
+ }
+ }
+
+ /// Get the IP address of the client.
+ ///
+ /// This method will attempt to retrieve the most likely IP address of the requester
+ /// in the following manner:
+ ///
+ /// If the `X-Forwarded-For` header exists, this method will return the left most
+ /// address in the list.
+ ///
+ /// If the [Forwarded HTTP Header Field](https://tools.ietf.org/html/rfc7239) exits,
+ /// this method will return the left most address indicated by the `for` parameter,
+ /// if it exists.
+ ///
+ /// # Note
+ /// This method does not ensure that the address is a valid IP address.
+ #[allow(dead_code)]
+ pub fn client_addr(&self) -> Result<Option<&str>> {
+ if let Some(x_forward) = self.header("x-forwarded-for") {
+ return Ok(from_utf8(x_forward)?.split(',').next());
+ }
+
+ // We only care about the first forwarded header, so header is ok
+ if let Some(forward) = self.header("forwarded") {
+ if let Some(_for) = from_utf8(forward)?
+ .split(';')
+ .find(|f| f.trim().starts_with("for"))
+ {
+ if let Some(_for_eq) = _for.trim().split(',').next() {
+ let mut it = _for_eq.split('=');
+ it.next();
+ return Ok(it.next());
+ }
+ }
+ }
+ Ok(None)
+ }
+
+ /// Attempt to parse an HTTP request from a buffer. If the buffer does not contain a complete
+ /// request, this will return `Ok(None)`.
+ pub fn parse(buf: &[u8]) -> Result<Option<Request>> {
+ let mut headers = [httparse::EMPTY_HEADER; MAX_HEADERS];
+ let mut req = httparse::Request::new(&mut headers);
+ let parsed = req.parse(buf)?;
+ if !parsed.is_partial() {
+ Ok(Some(Request {
+ path: req.path.unwrap().into(),
+ method: req.method.unwrap().into(),
+ headers: req.headers
+ .iter()
+ .map(|h| (h.name.into(), h.value.into()))
+ .collect(),
+ }))
+ } else {
+ Ok(None)
+ }
+ }
+
+ /// Construct a new WebSocket handshake HTTP request from a url.
+ pub fn from_url(url: &url::Url) -> Result<Request> {
+ let query = if let Some(q) = url.query() {
+ format!("?{}", q)
+ } else {
+ "".into()
+ };
+
+ let mut headers = vec![
+ ("Connection".into(), "Upgrade".into()),
+ (
+ "Host".into(),
+ format!(
+ "{}:{}",
+ url.host_str().ok_or_else(|| Error::new(
+ Kind::Internal,
+ "No host passed for WebSocket connection.",
+ ))?,
+ url.port_or_known_default().unwrap_or(80)
+ ).into(),
+ ),
+ ("Sec-WebSocket-Version".into(), "13".into()),
+ ("Sec-WebSocket-Key".into(), generate_key().into()),
+ ("Upgrade".into(), "websocket".into()),
+ ];
+
+ if url.password().is_some() || url.username() != "" {
+ let basic = encode_base64(format!("{}:{}", url.username(), url.password().unwrap_or("")).as_bytes());
+ headers.push(("Authorization".into(), format!("Basic {}", basic).into()))
+ }
+
+ let req = Request {
+ path: format!("{}{}", url.path(), query),
+ method: "GET".to_owned(),
+ headers: headers,
+ };
+
+ debug!("Built request from URL:\n{}", req);
+
+ Ok(req)
+ }
+
+ /// Write a request out to a buffer
+ pub fn format<W>(&self, w: &mut W) -> Result<()>
+ where
+ W: Write,
+ {
+ write!(w, "{} {} HTTP/1.1\r\n", self.method, self.path)?;
+ for &(ref key, ref val) in &self.headers {
+ write!(w, "{}: ", key)?;
+ w.write_all(val)?;
+ write!(w, "\r\n")?;
+ }
+ write!(w, "\r\n")?;
+ Ok(())
+ }
+}
+
+impl fmt::Display for Request {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let mut s = Vec::with_capacity(2048);
+ self.format(&mut s).map_err(|err| {
+ error!("{:?}", err);
+ fmt::Error
+ })?;
+ write!(
+ f,
+ "{}",
+ from_utf8(&s).map_err(|err| {
+ error!("Unable to format request as utf8: {:?}", err);
+ fmt::Error
+ })?
+ )
+ }
+}
+
+/// The handshake response.
+#[derive(Debug)]
+pub struct Response {
+ status: u16,
+ reason: String,
+ headers: Vec<(String, Vec<u8>)>,
+ body: Vec<u8>,
+}
+
+impl Response {
+ // TODO: resolve the overlap with Request
+
+ /// Construct a generic HTTP response with a body.
+ pub fn new<R>(status: u16, reason: R, body: Vec<u8>) -> Response
+ where
+ R: Into<String>,
+ {
+ Response {
+ status,
+ reason: reason.into(),
+ headers: vec![("Content-Length".into(), body.len().to_string().into())],
+ body,
+ }
+ }
+
+ /// Get the response body.
+ #[inline]
+ pub fn body(&self) -> &[u8] {
+ &self.body
+ }
+
+ /// Get the value of the first instance of an HTTP header.
+ fn header(&self, header: &str) -> Option<&Vec<u8>> {
+ self.headers
+ .iter()
+ .find(|&&(ref key, _)| key.to_lowercase() == header.to_lowercase())
+ .map(|&(_, ref val)| val)
+ }
+ /// Edit the value of the first instance of an HTTP header.
+ pub fn header_mut(&mut self, header: &str) -> Option<&mut Vec<u8>> {
+ self.headers
+ .iter_mut()
+ .find(|&&mut (ref key, _)| key.to_lowercase() == header.to_lowercase())
+ .map(|&mut (_, ref mut val)| val)
+ }
+
+ /// Access the request headers.
+ #[allow(dead_code)]
+ #[inline]
+ pub fn headers(&self) -> &Vec<(String, Vec<u8>)> {
+ &self.headers
+ }
+
+ /// Edit the request headers.
+ #[allow(dead_code)]
+ #[inline]
+ pub fn headers_mut(&mut self) -> &mut Vec<(String, Vec<u8>)> {
+ &mut self.headers
+ }
+
+ /// Get the HTTP status code.
+ #[allow(dead_code)]
+ #[inline]
+ pub fn status(&self) -> u16 {
+ self.status
+ }
+
+ /// Set the HTTP status code.
+ #[allow(dead_code)]
+ #[inline]
+ pub fn set_status(&mut self, status: u16) {
+ self.status = status
+ }
+
+ /// Get the HTTP status reason.
+ #[allow(dead_code)]
+ #[inline]
+ pub fn reason(&self) -> &str {
+ &self.reason
+ }
+
+ /// Set the HTTP status reason.
+ #[allow(dead_code)]
+ #[inline]
+ pub fn set_reason<R>(&mut self, reason: R)
+ where
+ R: Into<String>,
+ {
+ self.reason = reason.into()
+ }
+
+ /// Get the hashed WebSocket key.
+ pub fn key(&self) -> Result<&Vec<u8>> {
+ self.header("sec-websocket-accept")
+ .ok_or_else(|| Error::new(Kind::Protocol, "Unable to parse WebSocket key."))
+ }
+
+ /// Get the protocol that the server has decided to use.
+ #[allow(dead_code)]
+ pub fn protocol(&self) -> Result<Option<&str>> {
+ if let Some(proto) = self.header("sec-websocket-protocol") {
+ Ok(Some(from_utf8(proto)?))
+ } else {
+ Ok(None)
+ }
+ }
+
+ /// Set the protocol that the server has decided to use.
+ #[allow(dead_code)]
+ pub fn set_protocol(&mut self, protocol: &str) {
+ if let Some(proto) = self.header_mut("sec-websocket-protocol") {
+ *proto = protocol.into();
+ return;
+ }
+ self.headers_mut()
+ .push(("Sec-WebSocket-Protocol".into(), protocol.into()))
+ }
+
+ /// Get the extensions that the server has decided to use. If these are unacceptable, it is
+ /// appropriate to send an Extension close code.
+ #[allow(dead_code)]
+ pub fn extensions(&self) -> Result<Vec<&str>> {
+ if let Some(exts) = self.header("sec-websocket-extensions") {
+ Ok(from_utf8(exts)?
+ .split(',')
+ .map(|proto| proto.trim())
+ .collect())
+ } else {
+ Ok(Vec::new())
+ }
+ }
+
+ /// Add an accepted extension to this response.
+ /// This may result in duplicate extensions listed.
+ #[allow(dead_code)]
+ pub fn add_extension(&mut self, ext: &str) {
+ if let Some(exts) = self.header_mut("sec-websocket-extensions") {
+ exts.push(b","[0]);
+ exts.extend(ext.as_bytes());
+ return;
+ }
+ self.headers_mut()
+ .push(("Sec-WebSocket-Extensions".into(), ext.into()))
+ }
+
+ /// Remove an accepted extension from this response.
+ /// This will remove all configurations of the extension.
+ #[allow(dead_code)]
+ pub fn remove_extension(&mut self, ext: &str) {
+ if let Some(exts) = self.header_mut("sec-websocket-extensions") {
+ let mut new_exts = Vec::with_capacity(exts.len());
+
+ if let Ok(exts_str) = from_utf8(exts) {
+ new_exts = exts_str
+ .split(',')
+ .filter(|e| e.trim().starts_with(ext))
+ .collect::<Vec<&str>>()
+ .join(",")
+ .into();
+ }
+ if new_exts.len() < exts.len() {
+ *exts = new_exts
+ }
+ }
+ }
+
+ /// Attempt to parse an HTTP response from a buffer. If the buffer does not contain a complete
+ /// response, thiw will return `Ok(None)`.
+ pub fn parse(buf: &[u8]) -> Result<Option<Response>> {
+ let mut headers = [httparse::EMPTY_HEADER; MAX_HEADERS];
+ let mut res = httparse::Response::new(&mut headers);
+
+ let parsed = res.parse(buf)?;
+ if !parsed.is_partial() {
+ Ok(Some(Response {
+ status: res.code.unwrap(),
+ reason: res.reason.unwrap().into(),
+ headers: res.headers
+ .iter()
+ .map(|h| (h.name.into(), h.value.into()))
+ .collect(),
+ body: Vec::new(),
+ }))
+ } else {
+ Ok(None)
+ }
+ }
+
+ /// Construct a new WebSocket handshake HTTP response from a request.
+ /// This will create a response that ignores protocols and extensions. Edit this response to
+ /// accept a protocol and extensions as necessary.
+ pub fn from_request(req: &Request) -> Result<Response> {
+ let res = Response {
+ status: 101,
+ reason: "Switching Protocols".into(),
+ headers: vec![
+ ("Connection".into(), "Upgrade".into()),
+ ("Sec-WebSocket-Accept".into(), req.hashed_key()?.into()),
+ ("Upgrade".into(), "websocket".into()),
+ ],
+ body: Vec::new(),
+ };
+
+ debug!("Built response from request:\n{}", res);
+ Ok(res)
+ }
+
+ /// Write a response out to a buffer
+ pub fn format<W>(&self, w: &mut W) -> Result<()>
+ where
+ W: Write,
+ {
+ write!(w, "HTTP/1.1 {} {}\r\n", self.status, self.reason)?;
+ for &(ref key, ref val) in &self.headers {
+ write!(w, "{}: ", key)?;
+ w.write_all(val)?;
+ write!(w, "\r\n")?;
+ }
+ write!(w, "\r\n")?;
+ w.write_all(&self.body)?;
+ Ok(())
+ }
+}
+
+impl fmt::Display for Response {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let mut s = Vec::with_capacity(2048);
+ self.format(&mut s).map_err(|err| {
+ error!("{:?}", err);
+ fmt::Error
+ })?;
+ write!(
+ f,
+ "{}",
+ from_utf8(&s).map_err(|err| {
+ error!("Unable to format response as utf8: {:?}", err);
+ fmt::Error
+ })?
+ )
+ }
+}
+
+mod test {
+ #![allow(unused_imports, unused_variables, dead_code)]
+ use super::*;
+ use std::io::Write;
+ use std::net::SocketAddr;
+ use std::str::FromStr;
+
+ #[test]
+ fn remote_addr() {
+ let mut buf = Vec::with_capacity(2048);
+ write!(
+ &mut buf,
+ "GET / HTTP/1.1\r\n\
+ Connection: Upgrade\r\n\
+ Upgrade: websocket\r\n\
+ Sec-WebSocket-Version: 13\r\n\
+ Sec-WebSocket-Key: q16eN37NCfVwUChPvBdk4g==\r\n\r\n"
+ ).unwrap();
+
+ let req = Request::parse(&buf).unwrap().unwrap();
+ let res = Response::from_request(&req).unwrap();
+ let shake = Handshake {
+ request: req,
+ response: res,
+ peer_addr: Some(SocketAddr::from_str("127.0.0.1:8888").unwrap()),
+ local_addr: None,
+ };
+ assert_eq!(shake.remote_addr().unwrap().unwrap(), "127.0.0.1");
+ }
+
+ #[test]
+ fn remote_addr_x_forwarded_for() {
+ let mut buf = Vec::with_capacity(2048);
+ write!(
+ &mut buf,
+ "GET / HTTP/1.1\r\n\
+ Connection: Upgrade\r\n\
+ Upgrade: websocket\r\n\
+ X-Forwarded-For: 192.168.1.1, 192.168.1.2, 192.168.1.3\r\n\
+ Sec-WebSocket-Version: 13\r\n\
+ Sec-WebSocket-Key: q16eN37NCfVwUChPvBdk4g==\r\n\r\n"
+ ).unwrap();
+
+ let req = Request::parse(&buf).unwrap().unwrap();
+ let res = Response::from_request(&req).unwrap();
+ let shake = Handshake {
+ request: req,
+ response: res,
+ peer_addr: None,
+ local_addr: None,
+ };
+ assert_eq!(shake.remote_addr().unwrap().unwrap(), "192.168.1.1");
+ }
+
+ #[test]
+ fn remote_addr_forwarded() {
+ let mut buf = Vec::with_capacity(2048);
+ write!(
+ &mut buf,
+ "GET / HTTP/1.1\r\n\
+ Connection: Upgrade\r\n\
+ Upgrade: websocket\r\n\
+ Forwarded: by=192.168.1.1; for=192.0.2.43, for=\"[2001:db8:cafe::17]\", for=unknown\r\n\
+ Sec-WebSocket-Version: 13\r\n\
+ Sec-WebSocket-Key: q16eN37NCfVwUChPvBdk4g==\r\n\r\n")
+ .unwrap();
+ let req = Request::parse(&buf).unwrap().unwrap();
+ let res = Response::from_request(&req).unwrap();
+ let shake = Handshake {
+ request: req,
+ response: res,
+ peer_addr: None,
+ local_addr: None,
+ };
+ assert_eq!(shake.remote_addr().unwrap().unwrap(), "192.0.2.43");
+ }
+}
diff --git a/third_party/rust/ws/src/io.rs b/third_party/rust/ws/src/io.rs
new file mode 100644
index 0000000000..7739cdc472
--- /dev/null
+++ b/third_party/rust/ws/src/io.rs
@@ -0,0 +1,985 @@
+use std::borrow::Borrow;
+use std::io::{Error as IoError, ErrorKind};
+use std::net::{SocketAddr, ToSocketAddrs};
+use std::time::Duration;
+use std::usize;
+
+use mio;
+use mio::tcp::{TcpListener, TcpStream};
+use mio::{Poll, PollOpt, Ready, Token};
+use mio_extras;
+
+use url::Url;
+
+#[cfg(feature = "native_tls")]
+use native_tls::Error as SslError;
+
+use super::Settings;
+use communication::{Command, Sender, Signal};
+use connection::Connection;
+use factory::Factory;
+use slab::Slab;
+use result::{Error, Kind, Result};
+
+
+const QUEUE: Token = Token(usize::MAX - 3);
+const TIMER: Token = Token(usize::MAX - 4);
+pub const ALL: Token = Token(usize::MAX - 5);
+const SYSTEM: Token = Token(usize::MAX - 6);
+
+type Conn<F> = Connection<<F as Factory>::Handler>;
+
+const MAX_EVENTS: usize = 1024;
+const MESSAGES_PER_TICK: usize = 256;
+const TIMER_TICK_MILLIS: u64 = 100;
+const TIMER_WHEEL_SIZE: usize = 1024;
+const TIMER_CAPACITY: usize = 65_536;
+
+#[cfg(not(windows))]
+const CONNECTION_REFUSED: i32 = 111;
+#[cfg(windows)]
+const CONNECTION_REFUSED: i32 = 61;
+
+fn url_to_addrs(url: &Url) -> Result<Vec<SocketAddr>> {
+ let host = url.host_str();
+ if host.is_none() || (url.scheme() != "ws" && url.scheme() != "wss") {
+ return Err(Error::new(
+ Kind::Internal,
+ format!("Not a valid websocket url: {}", url),
+ ));
+ }
+ let host = host.unwrap();
+
+ let port = url.port_or_known_default().unwrap_or(80);
+ let mut addrs = (&host[..], port)
+ .to_socket_addrs()?
+ .collect::<Vec<SocketAddr>>();
+ addrs.dedup();
+ Ok(addrs)
+}
+
+enum State {
+ Active,
+ Inactive,
+}
+
+impl State {
+ fn is_active(&self) -> bool {
+ match *self {
+ State::Active => true,
+ State::Inactive => false,
+ }
+ }
+}
+
+#[derive(Debug, Clone, Copy)]
+pub struct Timeout {
+ connection: Token,
+ event: Token,
+}
+
+pub struct Handler<F>
+where
+ F: Factory,
+{
+ listener: Option<TcpListener>,
+ connections: Slab<Conn<F>>,
+ factory: F,
+ settings: Settings,
+ state: State,
+ queue_tx: mio::channel::SyncSender<Command>,
+ queue_rx: mio::channel::Receiver<Command>,
+ timer: mio_extras::timer::Timer<Timeout>,
+ next_connection_id: u32,
+}
+
+impl<F> Handler<F>
+where
+ F: Factory,
+{
+ pub fn new(factory: F, settings: Settings) -> Handler<F> {
+ let (tx, rx) = mio::channel::sync_channel(settings.max_connections * settings.queue_size);
+ let timer = mio_extras::timer::Builder::default()
+ .tick_duration(Duration::from_millis(TIMER_TICK_MILLIS))
+ .num_slots(TIMER_WHEEL_SIZE)
+ .capacity(TIMER_CAPACITY)
+ .build();
+ Handler {
+ listener: None,
+ connections: Slab::with_capacity(settings.max_connections),
+ factory,
+ settings,
+ state: State::Inactive,
+ queue_tx: tx,
+ queue_rx: rx,
+ timer,
+ next_connection_id: 0,
+ }
+ }
+
+ pub fn sender(&self) -> Sender {
+ Sender::new(ALL, self.queue_tx.clone(), 0)
+ }
+
+ pub fn listen(&mut self, poll: &mut Poll, addr: &SocketAddr) -> Result<&mut Handler<F>> {
+ debug_assert!(
+ self.listener.is_none(),
+ "Attempted to listen for connections from two addresses on the same websocket."
+ );
+
+ let tcp = TcpListener::bind(addr)?;
+ // TODO: consider net2 in order to set reuse_addr
+ poll.register(&tcp, ALL, Ready::readable(), PollOpt::level())?;
+ self.listener = Some(tcp);
+ Ok(self)
+ }
+
+ pub fn local_addr(&self) -> ::std::io::Result<SocketAddr> {
+ if let Some(ref listener) = self.listener {
+ listener.local_addr()
+ } else {
+ Err(IoError::new(ErrorKind::NotFound, "Not a listening socket"))
+ }
+ }
+
+ #[cfg(any(feature = "ssl", feature = "nativetls"))]
+ pub fn connect(&mut self, poll: &mut Poll, url: Url) -> Result<()> {
+ let settings = self.settings;
+
+ let (tok, addresses) = {
+ let (tok, entry, connection_id, handler) =
+ if self.connections.len() < settings.max_connections {
+ let entry = self.connections.vacant_entry();
+ let tok = Token(entry.key());
+ let connection_id = self.next_connection_id;
+ self.next_connection_id = self.next_connection_id.wrapping_add(1);
+ (
+ tok,
+ entry,
+ connection_id,
+ self.factory.client_connected(Sender::new(
+ tok,
+ self.queue_tx.clone(),
+ connection_id,
+ )),
+ )
+ } else {
+ return Err(Error::new(
+ Kind::Capacity,
+ "Unable to add another connection to the event loop.",
+ ));
+ };
+
+ let mut addresses = match url_to_addrs(&url) {
+ Ok(addresses) => addresses,
+ Err(err) => {
+ self.factory.connection_lost(handler);
+ return Err(err);
+ }
+ };
+
+ loop {
+ if let Some(addr) = addresses.pop() {
+ if let Ok(sock) = TcpStream::connect(&addr) {
+ if settings.tcp_nodelay {
+ sock.set_nodelay(true)?
+ }
+ addresses.push(addr); // Replace the first addr in case ssl fails and we fallback
+ entry.insert(Connection::new(tok, sock, handler, settings, connection_id));
+ break;
+ }
+ } else {
+ self.factory.connection_lost(handler);
+ return Err(Error::new(
+ Kind::Internal,
+ format!("Unable to obtain any socket address for {}", url),
+ ));
+ }
+ }
+
+ (tok, addresses)
+ };
+
+ let will_encrypt = url.scheme() == "wss";
+
+ if let Err(error) = self.connections[tok.into()].as_client(url, addresses) {
+ let handler = self.connections.remove(tok.into()).consume();
+ self.factory.connection_lost(handler);
+ return Err(error);
+ }
+
+ if will_encrypt {
+ while let Err(ssl_error) = self.connections[tok.into()].encrypt() {
+ match ssl_error.kind {
+ #[cfg(feature = "ssl")]
+ Kind::Ssl(ref inner_ssl_error) => {
+ if let Some(io_error) = inner_ssl_error.io_error() {
+ if let Some(errno) = io_error.raw_os_error() {
+ if errno == CONNECTION_REFUSED {
+ if let Err(reset_error) = self.connections[tok.into()].reset() {
+ trace!(
+ "Encountered error while trying to reset connection: {:?}",
+ reset_error
+ );
+ } else {
+ continue;
+ }
+ }
+ }
+ }
+ }
+ #[cfg(feature = "nativetls")]
+ Kind::Ssl(_) => {
+ if let Err(reset_error) = self.connections[tok.into()].reset() {
+ trace!(
+ "Encountered error while trying to reset connection: {:?}",
+ reset_error
+ );
+ } else {
+ continue;
+ }
+ }
+ _ => (),
+ }
+ self.connections[tok.into()].error(ssl_error);
+ // Allow socket to be registered anyway to await hangup
+ break;
+ }
+ }
+
+ poll.register(
+ self.connections[tok.into()].socket(),
+ self.connections[tok.into()].token(),
+ self.connections[tok.into()].events(),
+ PollOpt::edge() | PollOpt::oneshot(),
+ ).map_err(Error::from)
+ .or_else(|err| {
+ error!(
+ "Encountered error while trying to build WebSocket connection: {}",
+ err
+ );
+ let handler = self.connections.remove(tok.into()).consume();
+ self.factory.connection_lost(handler);
+ Err(err)
+ })
+ }
+
+ #[cfg(not(any(feature = "ssl", feature = "nativetls")))]
+ pub fn connect(&mut self, poll: &mut Poll, url: Url) -> Result<()> {
+ let settings = self.settings;
+
+ let (tok, addresses) = {
+ let (tok, entry, connection_id, handler) =
+ if self.connections.len() < settings.max_connections {
+ let entry = self.connections.vacant_entry();
+ let tok = Token(entry.key());
+ let connection_id = self.next_connection_id;
+ self.next_connection_id = self.next_connection_id.wrapping_add(1);
+ (
+ tok,
+ entry,
+ connection_id,
+ self.factory.client_connected(Sender::new(
+ tok,
+ self.queue_tx.clone(),
+ connection_id,
+ )),
+ )
+ } else {
+ return Err(Error::new(
+ Kind::Capacity,
+ "Unable to add another connection to the event loop.",
+ ));
+ };
+
+ let mut addresses = match url_to_addrs(&url) {
+ Ok(addresses) => addresses,
+ Err(err) => {
+ self.factory.connection_lost(handler);
+ return Err(err);
+ }
+ };
+
+ loop {
+ if let Some(addr) = addresses.pop() {
+ if let Ok(sock) = TcpStream::connect(&addr) {
+ if settings.tcp_nodelay {
+ sock.set_nodelay(true)?
+ }
+ entry.insert(Connection::new(tok, sock, handler, settings, connection_id));
+ break;
+ }
+ } else {
+ self.factory.connection_lost(handler);
+ return Err(Error::new(
+ Kind::Internal,
+ format!("Unable to obtain any socket address for {}", url),
+ ));
+ }
+ }
+
+ (tok, addresses)
+ };
+
+ if url.scheme() == "wss" {
+ let error = Error::new(
+ Kind::Protocol,
+ "The ssl feature is not enabled. Please enable it to use wss urls.",
+ );
+ let handler = self.connections.remove(tok.into()).consume();
+ self.factory.connection_lost(handler);
+ return Err(error);
+ }
+
+ if let Err(error) = self.connections[tok.into()].as_client(url, addresses) {
+ let handler = self.connections.remove(tok.into()).consume();
+ self.factory.connection_lost(handler);
+ return Err(error);
+ }
+
+ poll.register(
+ self.connections[tok.into()].socket(),
+ self.connections[tok.into()].token(),
+ self.connections[tok.into()].events(),
+ PollOpt::edge() | PollOpt::oneshot(),
+ ).map_err(Error::from)
+ .or_else(|err| {
+ error!(
+ "Encountered error while trying to build WebSocket connection: {}",
+ err
+ );
+ let handler = self.connections.remove(tok.into()).consume();
+ self.factory.connection_lost(handler);
+ Err(err)
+ })
+ }
+
+ #[cfg(any(feature = "ssl", feature = "nativetls"))]
+ pub fn accept(&mut self, poll: &mut Poll, sock: TcpStream) -> Result<()> {
+ let factory = &mut self.factory;
+ let settings = self.settings;
+
+ if settings.tcp_nodelay {
+ sock.set_nodelay(true)?
+ }
+
+ let tok = {
+ if self.connections.len() < settings.max_connections {
+ let entry = self.connections.vacant_entry();
+ let tok = Token(entry.key());
+ let connection_id = self.next_connection_id;
+ self.next_connection_id = self.next_connection_id.wrapping_add(1);
+ let handler = factory.server_connected(Sender::new(
+ tok,
+ self.queue_tx.clone(),
+ connection_id,
+ ));
+ entry.insert(Connection::new(tok, sock, handler, settings, connection_id));
+ tok
+ } else {
+ return Err(Error::new(
+ Kind::Capacity,
+ "Unable to add another connection to the event loop.",
+ ));
+ }
+ };
+
+ let conn = &mut self.connections[tok.into()];
+
+ conn.as_server()?;
+ if settings.encrypt_server {
+ conn.encrypt()?
+ }
+
+ poll.register(
+ conn.socket(),
+ conn.token(),
+ conn.events(),
+ PollOpt::edge() | PollOpt::oneshot(),
+ ).map_err(Error::from)
+ .or_else(|err| {
+ error!(
+ "Encountered error while trying to build WebSocket connection: {}",
+ err
+ );
+ conn.error(err);
+ if settings.panic_on_new_connection {
+ panic!("Encountered error while trying to build WebSocket connection.");
+ }
+ Ok(())
+ })
+ }
+
+ #[cfg(not(any(feature = "ssl", feature = "nativetls")))]
+ pub fn accept(&mut self, poll: &mut Poll, sock: TcpStream) -> Result<()> {
+ let factory = &mut self.factory;
+ let settings = self.settings;
+
+ if settings.tcp_nodelay {
+ sock.set_nodelay(true)?
+ }
+
+ let tok = {
+ if self.connections.len() < settings.max_connections {
+ let entry = self.connections.vacant_entry();
+ let tok = Token(entry.key());
+ let connection_id = self.next_connection_id;
+ self.next_connection_id = self.next_connection_id.wrapping_add(1);
+ let handler = factory.server_connected(Sender::new(
+ tok,
+ self.queue_tx.clone(),
+ connection_id,
+ ));
+ entry.insert(Connection::new(tok, sock, handler, settings, connection_id));
+ tok
+ } else {
+ return Err(Error::new(
+ Kind::Capacity,
+ "Unable to add another connection to the event loop.",
+ ));
+ }
+ };
+
+ let conn = &mut self.connections[tok.into()];
+
+ conn.as_server()?;
+ if settings.encrypt_server {
+ return Err(Error::new(
+ Kind::Protocol,
+ "The ssl feature is not enabled. Please enable it to use wss urls.",
+ ));
+ }
+
+ poll.register(
+ conn.socket(),
+ conn.token(),
+ conn.events(),
+ PollOpt::edge() | PollOpt::oneshot(),
+ ).map_err(Error::from)
+ .or_else(|err| {
+ error!(
+ "Encountered error while trying to build WebSocket connection: {}",
+ err
+ );
+ conn.error(err);
+ if settings.panic_on_new_connection {
+ panic!("Encountered error while trying to build WebSocket connection.");
+ }
+ Ok(())
+ })
+ }
+
+ pub fn run(&mut self, poll: &mut Poll) -> Result<()> {
+ trace!("Running event loop");
+ poll.register(
+ &self.queue_rx,
+ QUEUE,
+ Ready::readable(),
+ PollOpt::edge() | PollOpt::oneshot(),
+ )?;
+ poll.register(&self.timer, TIMER, Ready::readable(), PollOpt::edge())?;
+
+ self.state = State::Active;
+ let result = self.event_loop(poll);
+ self.state = State::Inactive;
+
+ result
+ .and(poll.deregister(&self.timer).map_err(Error::from))
+ .and(poll.deregister(&self.queue_rx).map_err(Error::from))
+ }
+
+ #[inline]
+ fn event_loop(&mut self, poll: &mut Poll) -> Result<()> {
+ let mut events = mio::Events::with_capacity(MAX_EVENTS);
+ while self.state.is_active() {
+ trace!("Waiting for event");
+ let nevents = match poll.poll(&mut events, None) {
+ Ok(nevents) => nevents,
+ Err(err) => {
+ if err.kind() == ErrorKind::Interrupted {
+ if self.settings.shutdown_on_interrupt {
+ error!("Websocket shutting down for interrupt.");
+ self.state = State::Inactive;
+ } else {
+ error!("Websocket received interrupt.");
+ }
+ 0
+ } else {
+ return Err(Error::from(err));
+ }
+ }
+ };
+ trace!("Processing {} events", nevents);
+
+ for i in 0..nevents {
+ let evt = events.get(i).unwrap();
+ self.handle_event(poll, evt.token(), evt.kind());
+ }
+
+ self.check_count();
+ }
+ Ok(())
+ }
+
+ #[inline]
+ fn schedule(&self, poll: &mut Poll, conn: &Conn<F>) -> Result<()> {
+ trace!(
+ "Scheduling connection to {} as {:?}",
+ conn.socket()
+ .peer_addr()
+ .map(|addr| addr.to_string())
+ .unwrap_or_else(|_| "UNKNOWN".into()),
+ conn.events()
+ );
+ poll.reregister(
+ conn.socket(),
+ conn.token(),
+ conn.events(),
+ PollOpt::edge() | PollOpt::oneshot(),
+ )?;
+ Ok(())
+ }
+
+ fn shutdown(&mut self) {
+ debug!("Received shutdown signal. WebSocket is attempting to shut down.");
+ for (_, conn) in self.connections.iter_mut() {
+ conn.shutdown();
+ }
+ self.factory.on_shutdown();
+ self.state = State::Inactive;
+ if self.settings.panic_on_shutdown {
+ panic!("Panicking on shutdown as per setting.")
+ }
+ }
+
+ #[inline]
+ fn check_active(&mut self, poll: &mut Poll, active: bool, token: Token) {
+ // NOTE: Closing state only applies after a ws connection was successfully
+ // established. It's possible that we may go inactive while in a connecting
+ // state if the handshake fails.
+ if !active {
+ if let Ok(addr) = self.connections[token.into()].socket().peer_addr() {
+ debug!("WebSocket connection to {} disconnected.", addr);
+ } else {
+ trace!("WebSocket connection to token={:?} disconnected.", token);
+ }
+ let handler = self.connections.remove(token.into()).consume();
+ self.factory.connection_lost(handler);
+ } else {
+ self.schedule(poll, &self.connections[token.into()])
+ .or_else(|err| {
+ // This will be an io error, so disconnect will already be called
+ self.connections[token.into()].error(err);
+ let handler = self.connections.remove(token.into()).consume();
+ self.factory.connection_lost(handler);
+ Ok::<(), Error>(())
+ })
+ .unwrap()
+ }
+ }
+
+ #[inline]
+ fn is_client(&self) -> bool {
+ self.listener.is_none()
+ }
+
+ #[inline]
+ fn check_count(&mut self) {
+ trace!("Active connections {:?}", self.connections.len());
+ if self.connections.is_empty() {
+ if !self.state.is_active() {
+ debug!("Shutting down websocket server.");
+ } else if self.is_client() {
+ debug!("Shutting down websocket client.");
+ self.factory.on_shutdown();
+ self.state = State::Inactive;
+ }
+ }
+ }
+
+ fn handle_event(&mut self, poll: &mut Poll, token: Token, events: Ready) {
+ match token {
+ SYSTEM => {
+ debug_assert!(false, "System token used for io event. This is a bug!");
+ error!("System token used for io event. This is a bug!");
+ }
+ ALL => {
+ if events.is_readable() {
+ match self.listener
+ .as_ref()
+ .expect("No listener provided for server websocket connections")
+ .accept()
+ {
+ Ok((sock, addr)) => {
+ info!("Accepted a new tcp connection from {}.", addr);
+ if let Err(err) = self.accept(poll, sock) {
+ error!("Unable to build WebSocket connection {:?}", err);
+ if self.settings.panic_on_new_connection {
+ panic!("Unable to build WebSocket connection {:?}", err);
+ }
+ }
+ }
+ Err(err) => error!(
+ "Encountered an error {:?} while accepting tcp connection.",
+ err
+ ),
+ }
+ }
+ }
+ TIMER => while let Some(t) = self.timer.poll() {
+ self.handle_timeout(poll, t);
+ },
+ QUEUE => {
+ for _ in 0..MESSAGES_PER_TICK {
+ match self.queue_rx.try_recv() {
+ Ok(cmd) => self.handle_queue(poll, cmd),
+ _ => break,
+ }
+ }
+ let _ = poll.reregister(
+ &self.queue_rx,
+ QUEUE,
+ Ready::readable(),
+ PollOpt::edge() | PollOpt::oneshot(),
+ );
+ }
+ _ => {
+ let active = {
+ let conn_events = self.connections[token.into()].events();
+
+ if (events & conn_events).is_readable() {
+ if let Err(err) = self.connections[token.into()].read() {
+ trace!("Encountered error while reading: {}", err);
+ if let Kind::Io(ref err) = err.kind {
+ if let Some(errno) = err.raw_os_error() {
+ if errno == CONNECTION_REFUSED {
+ match self.connections[token.into()].reset() {
+ Ok(_) => {
+ poll.register(
+ self.connections[token.into()].socket(),
+ self.connections[token.into()].token(),
+ self.connections[token.into()].events(),
+ PollOpt::edge() | PollOpt::oneshot(),
+ ).or_else(|err| {
+ self.connections[token.into()]
+ .error(Error::from(err));
+ let handler = self.connections
+ .remove(token.into())
+ .consume();
+ self.factory.connection_lost(handler);
+ Ok::<(), Error>(())
+ })
+ .unwrap();
+ return;
+ }
+ Err(err) => {
+ trace!("Encountered error while trying to reset connection: {:?}", err);
+ }
+ }
+ }
+ }
+ }
+ // This will trigger disconnect if the connection is open
+ self.connections[token.into()].error(err)
+ }
+ }
+
+ let conn_events = self.connections[token.into()].events();
+
+ if (events & conn_events).is_writable() {
+ if let Err(err) = self.connections[token.into()].write() {
+ trace!("Encountered error while writing: {}", err);
+ if let Kind::Io(ref err) = err.kind {
+ if let Some(errno) = err.raw_os_error() {
+ if errno == CONNECTION_REFUSED {
+ match self.connections[token.into()].reset() {
+ Ok(_) => {
+ poll.register(
+ self.connections[token.into()].socket(),
+ self.connections[token.into()].token(),
+ self.connections[token.into()].events(),
+ PollOpt::edge() | PollOpt::oneshot(),
+ ).or_else(|err| {
+ self.connections[token.into()]
+ .error(Error::from(err));
+ let handler = self.connections
+ .remove(token.into())
+ .consume();
+ self.factory.connection_lost(handler);
+ Ok::<(), Error>(())
+ })
+ .unwrap();
+ return;
+ }
+ Err(err) => {
+ trace!("Encountered error while trying to reset connection: {:?}", err);
+ }
+ }
+ }
+ }
+ }
+ // This will trigger disconnect if the connection is open
+ self.connections[token.into()].error(err)
+ }
+ }
+
+ // connection events may have changed
+ self.connections[token.into()].events().is_readable()
+ || self.connections[token.into()].events().is_writable()
+ };
+
+ self.check_active(poll, active, token)
+ }
+ }
+ }
+
+ fn handle_queue(&mut self, poll: &mut Poll, cmd: Command) {
+ match cmd.token() {
+ SYSTEM => {
+ // Scaffolding for system events such as internal timeouts
+ }
+ ALL => {
+ let mut dead = Vec::with_capacity(self.connections.len());
+
+ match cmd.into_signal() {
+ Signal::Message(msg) => {
+ trace!("Broadcasting message: {:?}", msg);
+ for (_, conn) in self.connections.iter_mut() {
+ if let Err(err) = conn.send_message(msg.clone()) {
+ dead.push((conn.token(), err))
+ }
+ }
+ }
+ Signal::Close(code, reason) => {
+ trace!("Broadcasting close: {:?} - {}", code, reason);
+ for (_, conn) in self.connections.iter_mut() {
+ if let Err(err) = conn.send_close(code, reason.borrow()) {
+ dead.push((conn.token(), err))
+ }
+ }
+ }
+ Signal::Ping(data) => {
+ trace!("Broadcasting ping");
+ for (_, conn) in self.connections.iter_mut() {
+ if let Err(err) = conn.send_ping(data.clone()) {
+ dead.push((conn.token(), err))
+ }
+ }
+ }
+ Signal::Pong(data) => {
+ trace!("Broadcasting pong");
+ for (_, conn) in self.connections.iter_mut() {
+ if let Err(err) = conn.send_pong(data.clone()) {
+ dead.push((conn.token(), err))
+ }
+ }
+ }
+ Signal::Connect(url) => {
+ if let Err(err) = self.connect(poll, url.clone()) {
+ if self.settings.panic_on_new_connection {
+ panic!("Unable to establish connection to {}: {:?}", url, err);
+ }
+ error!("Unable to establish connection to {}: {:?}", url, err);
+ }
+ return;
+ }
+ Signal::Shutdown => self.shutdown(),
+ Signal::Timeout {
+ delay,
+ token: event,
+ } => {
+ let timeout = self.timer.set_timeout(
+ Duration::from_millis(delay),
+ Timeout {
+ connection: ALL,
+ event,
+ },
+ );
+ for (_, conn) in self.connections.iter_mut() {
+ if let Err(err) = conn.new_timeout(event, timeout.clone()) {
+ conn.error(err);
+ }
+ }
+ return;
+ }
+ Signal::Cancel(timeout) => {
+ self.timer.cancel_timeout(&timeout);
+ return;
+ }
+ }
+
+ for (_, conn) in self.connections.iter() {
+ if let Err(err) = self.schedule(poll, conn) {
+ dead.push((conn.token(), err))
+ }
+ }
+ for (token, err) in dead {
+ // note the same connection may be called twice
+ self.connections[token.into()].error(err)
+ }
+ }
+ token => {
+ let connection_id = cmd.connection_id();
+ match cmd.into_signal() {
+ Signal::Message(msg) => {
+ if let Some(conn) = self.connections.get_mut(token.into()) {
+ if conn.connection_id() == connection_id {
+ if let Err(err) = conn.send_message(msg) {
+ conn.error(err)
+ }
+ } else {
+ trace!("Connection disconnected while a message was waiting in the queue.")
+ }
+ } else {
+ trace!(
+ "Connection disconnected while a message was waiting in the queue."
+ )
+ }
+ }
+ Signal::Close(code, reason) => {
+ if let Some(conn) = self.connections.get_mut(token.into()) {
+ if conn.connection_id() == connection_id {
+ if let Err(err) = conn.send_close(code, reason) {
+ conn.error(err)
+ }
+ } else {
+ trace!("Connection disconnected while close signal was waiting in the queue.")
+ }
+ } else {
+ trace!("Connection disconnected while close signal was waiting in the queue.")
+ }
+ }
+ Signal::Ping(data) => {
+ if let Some(conn) = self.connections.get_mut(token.into()) {
+ if conn.connection_id() == connection_id {
+ if let Err(err) = conn.send_ping(data) {
+ conn.error(err)
+ }
+ } else {
+ trace!("Connection disconnected while ping signal was waiting in the queue.")
+ }
+ } else {
+ trace!("Connection disconnected while ping signal was waiting in the queue.")
+ }
+ }
+ Signal::Pong(data) => {
+ if let Some(conn) = self.connections.get_mut(token.into()) {
+ if conn.connection_id() == connection_id {
+ if let Err(err) = conn.send_pong(data) {
+ conn.error(err)
+ }
+ } else {
+ trace!("Connection disconnected while pong signal was waiting in the queue.")
+ }
+ } else {
+ trace!("Connection disconnected while pong signal was waiting in the queue.")
+ }
+ }
+ Signal::Connect(url) => {
+ if let Err(err) = self.connect(poll, url.clone()) {
+ if let Some(conn) = self.connections.get_mut(token.into()) {
+ conn.error(err)
+ } else {
+ if self.settings.panic_on_new_connection {
+ panic!("Unable to establish connection to {}: {:?}", url, err);
+ }
+ error!("Unable to establish connection to {}: {:?}", url, err);
+ }
+ }
+ return;
+ }
+ Signal::Shutdown => self.shutdown(),
+ Signal::Timeout {
+ delay,
+ token: event,
+ } => {
+ let timeout = self.timer.set_timeout(
+ Duration::from_millis(delay),
+ Timeout {
+ connection: token,
+ event,
+ },
+ );
+ if let Some(conn) = self.connections.get_mut(token.into()) {
+ if let Err(err) = conn.new_timeout(event, timeout) {
+ conn.error(err)
+ }
+ } else {
+ trace!("Connection disconnected while pong signal was waiting in the queue.")
+ }
+ return;
+ }
+ Signal::Cancel(timeout) => {
+ self.timer.cancel_timeout(&timeout);
+ return;
+ }
+ }
+
+ if self.connections.get(token.into()).is_some() {
+ if let Err(err) = self.schedule(poll, &self.connections[token.into()]) {
+ self.connections[token.into()].error(err)
+ }
+ }
+ }
+ }
+ }
+
+ fn handle_timeout(&mut self, poll: &mut Poll, Timeout { connection, event }: Timeout) {
+ let active = {
+ if let Some(conn) = self.connections.get_mut(connection.into()) {
+ if let Err(err) = conn.timeout_triggered(event) {
+ conn.error(err)
+ }
+
+ conn.events().is_readable() || conn.events().is_writable()
+ } else {
+ trace!("Connection disconnected while timeout was waiting.");
+ return;
+ }
+ };
+ self.check_active(poll, active, connection);
+ }
+}
+
+mod test {
+ #![allow(unused_imports, unused_variables, dead_code)]
+ use std::str::FromStr;
+
+ use url::Url;
+
+ use super::url_to_addrs;
+ use super::*;
+ use result::{Error, Kind};
+
+ #[test]
+ fn test_url_to_addrs() {
+ let ws_url = Url::from_str("ws://example.com?query=me").unwrap();
+ let wss_url = Url::from_str("wss://example.com/suburl#fragment").unwrap();
+ let bad_url = Url::from_str("http://howdy.bad.com").unwrap();
+ let no_resolve = Url::from_str("ws://bad.elucitrans.com").unwrap();
+
+ assert!(url_to_addrs(&ws_url).is_ok());
+ assert!(url_to_addrs(&ws_url).unwrap().len() > 0);
+ assert!(url_to_addrs(&wss_url).is_ok());
+ assert!(url_to_addrs(&wss_url).unwrap().len() > 0);
+
+ match url_to_addrs(&bad_url) {
+ Ok(_) => panic!("url_to_addrs accepts http urls."),
+ Err(Error {
+ kind: Kind::Internal,
+ details: _,
+ }) => (), // pass
+ err => panic!("{:?}", err),
+ }
+
+ match url_to_addrs(&no_resolve) {
+ Ok(_) => panic!("url_to_addrs creates addresses for non-existent domains."),
+ Err(Error {
+ kind: Kind::Io(_),
+ details: _,
+ }) => (), // pass
+ err => panic!("{:?}", err),
+ }
+ }
+
+}
diff --git a/third_party/rust/ws/src/lib.rs b/third_party/rust/ws/src/lib.rs
new file mode 100644
index 0000000000..ea9f1a54e4
--- /dev/null
+++ b/third_party/rust/ws/src/lib.rs
@@ -0,0 +1,391 @@
+//! Lightweight, event-driven WebSockets for Rust.
+#![allow(deprecated)]
+#![deny(missing_copy_implementations, trivial_casts, trivial_numeric_casts, unstable_features,
+ unused_import_braces)]
+
+extern crate byteorder;
+extern crate bytes;
+extern crate httparse;
+extern crate mio;
+extern crate mio_extras;
+#[cfg(feature = "ssl")]
+extern crate openssl;
+#[cfg(feature = "nativetls")]
+extern crate native_tls;
+extern crate rand;
+extern crate sha1;
+extern crate slab;
+extern crate url;
+#[macro_use]
+extern crate log;
+
+mod communication;
+mod connection;
+mod factory;
+mod frame;
+mod handler;
+mod handshake;
+mod io;
+mod message;
+mod protocol;
+mod result;
+mod stream;
+
+#[cfg(feature = "permessage-deflate")]
+pub mod deflate;
+
+pub mod util;
+
+pub use factory::Factory;
+pub use handler::Handler;
+
+pub use communication::Sender;
+pub use frame::Frame;
+pub use handshake::{Handshake, Request, Response};
+pub use message::Message;
+pub use protocol::{CloseCode, OpCode};
+pub use result::Kind as ErrorKind;
+pub use result::{Error, Result};
+
+use std::borrow::Borrow;
+use std::default::Default;
+use std::fmt;
+use std::net::{SocketAddr, ToSocketAddrs};
+
+use mio::Poll;
+
+/// A utility function for setting up a WebSocket server.
+///
+/// # Safety
+///
+/// This function blocks until the event loop finishes running. Avoid calling this method within
+/// another WebSocket handler.
+///
+/// # Examples
+///
+/// ```no_run
+/// use ws::listen;
+///
+/// listen("127.0.0.1:3012", |out| {
+/// move |msg| {
+/// out.send(msg)
+/// }
+/// }).unwrap()
+/// ```
+///
+pub fn listen<A, F, H>(addr: A, factory: F) -> Result<()>
+where
+ A: ToSocketAddrs + fmt::Debug,
+ F: FnMut(Sender) -> H,
+ H: Handler,
+{
+ let ws = WebSocket::new(factory)?;
+ ws.listen(addr)?;
+ Ok(())
+}
+
+/// A utility function for setting up a WebSocket client.
+///
+/// # Safety
+///
+/// This function blocks until the event loop finishes running. Avoid calling this method within
+/// another WebSocket handler. If you need to establish a connection from inside of a handler,
+/// use the `connect` method on the Sender.
+///
+/// # Examples
+///
+/// ```no_run
+/// use ws::{connect, CloseCode};
+///
+/// connect("ws://127.0.0.1:3012", |out| {
+/// out.send("Hello WebSocket").unwrap();
+///
+/// move |msg| {
+/// println!("Got message: {}", msg);
+/// out.close(CloseCode::Normal)
+/// }
+/// }).unwrap()
+/// ```
+///
+pub fn connect<U, F, H>(url: U, factory: F) -> Result<()>
+where
+ U: Borrow<str>,
+ F: FnMut(Sender) -> H,
+ H: Handler,
+{
+ let mut ws = WebSocket::new(factory)?;
+ let parsed = url::Url::parse(url.borrow()).map_err(|err| {
+ Error::new(
+ ErrorKind::Internal,
+ format!("Unable to parse {} as url due to {:?}", url.borrow(), err),
+ )
+ })?;
+ ws.connect(parsed)?;
+ ws.run()?;
+ Ok(())
+}
+
+/// WebSocket settings
+#[derive(Debug, Clone, Copy)]
+pub struct Settings {
+ /// The maximum number of connections that this WebSocket will support.
+ /// The default setting is low and should be increased when expecting more
+ /// connections because this is a hard limit and no new connections beyond
+ /// this limit can be made until an old connection is dropped.
+ /// Default: 100
+ pub max_connections: usize,
+ /// The number of events anticipated per connection. The event loop queue size will
+ /// be `queue_size` * `max_connections`. In order to avoid an overflow error,
+ /// `queue_size` * `max_connections` must be less than or equal to `usize::max_value()`.
+ /// The queue is shared between connections, which means that a connection may schedule
+ /// more events than `queue_size` provided that another connection is using less than
+ /// `queue_size`. However, if the queue is maxed out a Queue error will occur.
+ /// Default: 5
+ pub queue_size: usize,
+ /// Whether to panic when unable to establish a new TCP connection.
+ /// Default: false
+ pub panic_on_new_connection: bool,
+ /// Whether to panic when a shutdown of the WebSocket is requested.
+ /// Default: false
+ pub panic_on_shutdown: bool,
+ /// The maximum number of fragments the connection can handle without reallocating.
+ /// Default: 10
+ pub fragments_capacity: usize,
+ /// Whether to reallocate when `fragments_capacity` is reached. If this is false,
+ /// a Capacity error will be triggered instead.
+ /// Default: true
+ pub fragments_grow: bool,
+ /// The maximum length of outgoing frames. Messages longer than this will be fragmented.
+ /// Default: 65,535
+ pub fragment_size: usize,
+ /// The maximum length of acceptable incoming frames. Messages longer than this will be rejected.
+ /// Default: unlimited
+ pub max_fragment_size: usize,
+ /// The size of the incoming buffer. A larger buffer uses more memory but will allow for fewer
+ /// reallocations.
+ /// Default: 2048
+ pub in_buffer_capacity: usize,
+ /// Whether to reallocate the incoming buffer when `in_buffer_capacity` is reached. If this is
+ /// false, a Capacity error will be triggered instead.
+ /// Default: true
+ pub in_buffer_grow: bool,
+ /// The size of the outgoing buffer. A larger buffer uses more memory but will allow for fewer
+ /// reallocations.
+ /// Default: 2048
+ pub out_buffer_capacity: usize,
+ /// Whether to reallocate the incoming buffer when `out_buffer_capacity` is reached. If this is
+ /// false, a Capacity error will be triggered instead.
+ /// Default: true
+ pub out_buffer_grow: bool,
+ /// Whether to panic when an Internal error is encountered. Internal errors should generally
+ /// not occur, so this setting defaults to true as a debug measure, whereas production
+ /// applications should consider setting it to false.
+ /// Default: true
+ pub panic_on_internal: bool,
+ /// Whether to panic when a Capacity error is encountered.
+ /// Default: false
+ pub panic_on_capacity: bool,
+ /// Whether to panic when a Protocol error is encountered.
+ /// Default: false
+ pub panic_on_protocol: bool,
+ /// Whether to panic when an Encoding error is encountered.
+ /// Default: false
+ pub panic_on_encoding: bool,
+ /// Whether to panic when a Queue error is encountered.
+ /// Default: false
+ pub panic_on_queue: bool,
+ /// Whether to panic when an Io error is encountered.
+ /// Default: false
+ pub panic_on_io: bool,
+ /// Whether to panic when a Timer error is encountered.
+ /// Default: false
+ pub panic_on_timeout: bool,
+ /// Whether to shutdown the eventloop when an interrupt is received.
+ /// Default: true
+ pub shutdown_on_interrupt: bool,
+ /// The WebSocket protocol requires frames sent from client endpoints to be masked as a
+ /// security and sanity precaution. Enforcing this requirement, which may be removed at some
+ /// point may cause incompatibilities. If you need the extra security, set this to true.
+ /// Default: false
+ pub masking_strict: bool,
+ /// The WebSocket protocol requires clients to verify the key returned by a server to ensure
+ /// that the server and all intermediaries can perform the protocol. Verifying the key will
+ /// consume processing time and other resources with the benefit that we can fail the
+ /// connection early. The default in WS-RS is to accept any key from the server and instead
+ /// fail late if a protocol error occurs. Change this setting to enable key verification.
+ /// Default: false
+ pub key_strict: bool,
+ /// The WebSocket protocol requires clients to perform an opening handshake using the HTTP
+ /// GET method for the request. However, since only WebSockets are supported on the connection,
+ /// verifying the method of handshake requests is not always necessary. To enforce the
+ /// requirement that handshakes begin with a GET method, set this to true.
+ /// Default: false
+ pub method_strict: bool,
+ /// Indicate whether server connections should use ssl encryption when accepting connections.
+ /// Setting this to true means that clients should use the `wss` scheme to connect to this
+ /// server. Note that using this flag will in general necessitate overriding the
+ /// `Handler::upgrade_ssl_server` method in order to provide the details of the ssl context. It may be
+ /// simpler for most users to use a reverse proxy such as nginx to provide server side
+ /// encryption.
+ ///
+ /// Default: false
+ pub encrypt_server: bool,
+ /// Disables Nagle's algorithm.
+ /// Usually tcp socket tries to accumulate packets to send them all together (every 200ms).
+ /// When enabled socket will try to send packet as fast as possible.
+ ///
+ /// Default: false
+ pub tcp_nodelay: bool,
+}
+
+impl Default for Settings {
+ fn default() -> Settings {
+ Settings {
+ max_connections: 100,
+ queue_size: 5,
+ panic_on_new_connection: false,
+ panic_on_shutdown: false,
+ fragments_capacity: 10,
+ fragments_grow: true,
+ fragment_size: u16::max_value() as usize,
+ max_fragment_size: usize::max_value(),
+ in_buffer_capacity: 2048,
+ in_buffer_grow: true,
+ out_buffer_capacity: 2048,
+ out_buffer_grow: true,
+ panic_on_internal: true,
+ panic_on_capacity: false,
+ panic_on_protocol: false,
+ panic_on_encoding: false,
+ panic_on_queue: false,
+ panic_on_io: false,
+ panic_on_timeout: false,
+ shutdown_on_interrupt: true,
+ masking_strict: false,
+ key_strict: false,
+ method_strict: false,
+ encrypt_server: false,
+ tcp_nodelay: false,
+ }
+ }
+}
+
+/// The WebSocket struct. A WebSocket can support multiple incoming and outgoing connections.
+pub struct WebSocket<F>
+where
+ F: Factory,
+{
+ poll: Poll,
+ handler: io::Handler<F>,
+}
+
+impl<F> WebSocket<F>
+where
+ F: Factory,
+{
+ /// Create a new WebSocket using the given Factory to create handlers.
+ pub fn new(factory: F) -> Result<WebSocket<F>> {
+ Builder::new().build(factory)
+ }
+
+ /// Consume the WebSocket and bind to the specified address.
+ /// If the `addr_spec` yields multiple addresses this will return after the
+ /// first successful bind. `local_addr` can be called to determine which
+ /// address it ended up binding to.
+ /// After the server is successfully bound you should start it using `run`.
+ pub fn bind<A>(mut self, addr_spec: A) -> Result<WebSocket<F>>
+ where
+ A: ToSocketAddrs,
+ {
+ let mut last_error = Error::new(ErrorKind::Internal, "No address given");
+
+ for addr in addr_spec.to_socket_addrs()? {
+ if let Err(e) = self.handler.listen(&mut self.poll, &addr) {
+ error!("Unable to listen on {}", addr);
+ last_error = e;
+ } else {
+ let actual_addr = self.handler.local_addr().unwrap_or(addr);
+ info!("Listening for new connections on {}.", actual_addr);
+ return Ok(self);
+ }
+ }
+
+ Err(last_error)
+ }
+
+ /// Consume the WebSocket and listen for new connections on the specified address.
+ ///
+ /// # Safety
+ ///
+ /// This method will block until the event loop finishes running.
+ pub fn listen<A>(self, addr_spec: A) -> Result<WebSocket<F>>
+ where
+ A: ToSocketAddrs,
+ {
+ self.bind(addr_spec).and_then(|server| server.run())
+ }
+
+ /// Queue an outgoing connection on this WebSocket. This method may be called multiple times,
+ /// but the actual connections will not be established until `run` is called.
+ pub fn connect(&mut self, url: url::Url) -> Result<&mut WebSocket<F>> {
+ let sender = self.handler.sender();
+ info!("Queuing connection to {}", url);
+ sender.connect(url)?;
+ Ok(self)
+ }
+
+ /// Run the WebSocket. This will run the encapsulated event loop blocking the calling thread until
+ /// the WebSocket is shutdown.
+ pub fn run(mut self) -> Result<WebSocket<F>> {
+ self.handler.run(&mut self.poll)?;
+ Ok(self)
+ }
+
+ /// Get a Sender that can be used to send messages on all connections.
+ /// Calling `send` on this Sender is equivalent to calling `broadcast`.
+ /// Calling `shutdown` on this Sender will shutdown the WebSocket even if no connections have
+ /// been established.
+ #[inline]
+ pub fn broadcaster(&self) -> Sender {
+ self.handler.sender()
+ }
+
+ /// Get the local socket address this socket is bound to. Will return an error
+ /// if the backend returns an error. Will return a `NotFound` error if
+ /// this WebSocket is not a listening socket.
+ pub fn local_addr(&self) -> ::std::io::Result<SocketAddr> {
+ self.handler.local_addr()
+ }
+}
+
+/// Utility for constructing a WebSocket from various settings.
+#[derive(Debug, Default, Clone, Copy)]
+pub struct Builder {
+ settings: Settings,
+}
+
+// TODO: add convenience methods for each setting
+impl Builder {
+ /// Create a new Builder with default settings.
+ pub fn new() -> Builder {
+ Builder::default()
+ }
+
+ /// Build a WebSocket using this builder and a factory.
+ /// It is possible to use the same builder to create multiple WebSockets.
+ pub fn build<F>(&self, factory: F) -> Result<WebSocket<F>>
+ where
+ F: Factory,
+ {
+ Ok(WebSocket {
+ poll: Poll::new()?,
+ handler: io::Handler::new(factory, self.settings),
+ })
+ }
+
+ /// Set the WebSocket settings to use.
+ pub fn with_settings(&mut self, settings: Settings) -> &mut Builder {
+ self.settings = settings;
+ self
+ }
+}
diff --git a/third_party/rust/ws/src/message.rs b/third_party/rust/ws/src/message.rs
new file mode 100644
index 0000000000..08509a5e1d
--- /dev/null
+++ b/third_party/rust/ws/src/message.rs
@@ -0,0 +1,173 @@
+use std::convert::{From, Into};
+use std::fmt;
+use std::result::Result as StdResult;
+use std::str::from_utf8;
+
+use protocol::OpCode;
+use result::Result;
+
+use self::Message::*;
+
+/// An enum representing the various forms of a WebSocket message.
+#[derive(Debug, Eq, PartialEq, Clone)]
+pub enum Message {
+ /// A text WebSocket message
+ Text(String),
+ /// A binary WebSocket message
+ Binary(Vec<u8>),
+}
+
+impl Message {
+ /// Create a new text WebSocket message from a stringable.
+ pub fn text<S>(string: S) -> Message
+ where
+ S: Into<String>,
+ {
+ Message::Text(string.into())
+ }
+
+ /// Create a new binary WebSocket message by converting to Vec<u8>.
+ pub fn binary<B>(bin: B) -> Message
+ where
+ B: Into<Vec<u8>>,
+ {
+ Message::Binary(bin.into())
+ }
+
+ /// Indicates whether a message is a text message.
+ pub fn is_text(&self) -> bool {
+ match *self {
+ Text(_) => true,
+ Binary(_) => false,
+ }
+ }
+
+ /// Indicates whether a message is a binary message.
+ pub fn is_binary(&self) -> bool {
+ match *self {
+ Text(_) => false,
+ Binary(_) => true,
+ }
+ }
+
+ /// Get the length of the WebSocket message.
+ pub fn len(&self) -> usize {
+ match *self {
+ Text(ref string) => string.len(),
+ Binary(ref data) => data.len(),
+ }
+ }
+
+ /// Returns true if the WebSocket message has no content.
+ /// For example, if the other side of the connection sent an empty string.
+ pub fn is_empty(&self) -> bool {
+ match *self {
+ Text(ref string) => string.is_empty(),
+ Binary(ref data) => data.is_empty(),
+ }
+ }
+
+ #[doc(hidden)]
+ pub fn opcode(&self) -> OpCode {
+ match *self {
+ Text(_) => OpCode::Text,
+ Binary(_) => OpCode::Binary,
+ }
+ }
+
+ /// Consume the WebSocket and return it as binary data.
+ pub fn into_data(self) -> Vec<u8> {
+ match self {
+ Text(string) => string.into_bytes(),
+ Binary(data) => data,
+ }
+ }
+
+ /// Attempt to consume the WebSocket message and convert it to a String.
+ pub fn into_text(self) -> Result<String> {
+ match self {
+ Text(string) => Ok(string),
+ Binary(data) => Ok(String::from_utf8(data).map_err(|err| err.utf8_error())?),
+ }
+ }
+
+ /// Attempt to get a &str from the WebSocket message,
+ /// this will try to convert binary data to utf8.
+ pub fn as_text(&self) -> Result<&str> {
+ match *self {
+ Text(ref string) => Ok(string),
+ Binary(ref data) => Ok(from_utf8(data)?),
+ }
+ }
+}
+
+impl From<String> for Message {
+ fn from(string: String) -> Message {
+ Message::text(string)
+ }
+}
+
+impl<'s> From<&'s str> for Message {
+ fn from(string: &'s str) -> Message {
+ Message::text(string)
+ }
+}
+
+impl<'b> From<&'b [u8]> for Message {
+ fn from(data: &'b [u8]) -> Message {
+ Message::binary(data)
+ }
+}
+
+impl From<Vec<u8>> for Message {
+ fn from(data: Vec<u8>) -> Message {
+ Message::binary(data)
+ }
+}
+
+impl fmt::Display for Message {
+ fn fmt(&self, f: &mut fmt::Formatter) -> StdResult<(), fmt::Error> {
+ if let Ok(string) = self.as_text() {
+ write!(f, "{}", string)
+ } else {
+ write!(f, "Binary Data<length={}>", self.len())
+ }
+ }
+}
+
+mod test {
+ #![allow(unused_imports, unused_variables, dead_code)]
+ use super::*;
+
+ #[test]
+ fn display() {
+ let t = Message::text(format!("test"));
+ assert_eq!(t.to_string(), "test".to_owned());
+
+ let bin = Message::binary(vec![0, 1, 3, 4, 241]);
+ assert_eq!(bin.to_string(), "Binary Data<length=5>".to_owned());
+ }
+
+ #[test]
+ fn binary_convert() {
+ let bin = [6u8, 7, 8, 9, 10, 241];
+ let msg = Message::from(&bin[..]);
+ assert!(msg.is_binary());
+ assert!(msg.into_text().is_err());
+ }
+
+ #[test]
+ fn binary_convert_vec() {
+ let bin = vec![6u8, 7, 8, 9, 10, 241];
+ let msg = Message::from(bin);
+ assert!(msg.is_binary());
+ assert!(msg.into_text().is_err());
+ }
+
+ #[test]
+ fn text_convert() {
+ let s = "kiwotsukete";
+ let msg = Message::from(s);
+ assert!(msg.is_text());
+ }
+}
diff --git a/third_party/rust/ws/src/protocol.rs b/third_party/rust/ws/src/protocol.rs
new file mode 100644
index 0000000000..d4c1f2e326
--- /dev/null
+++ b/third_party/rust/ws/src/protocol.rs
@@ -0,0 +1,227 @@
+use std::convert::{From, Into};
+use std::fmt;
+
+use self::OpCode::*;
+/// Operation codes as part of rfc6455.
+#[derive(Debug, Eq, PartialEq, Clone, Copy)]
+pub enum OpCode {
+ /// Indicates a continuation frame of a fragmented message.
+ Continue,
+ /// Indicates a text data frame.
+ Text,
+ /// Indicates a binary data frame.
+ Binary,
+ /// Indicates a close control frame.
+ Close,
+ /// Indicates a ping control frame.
+ Ping,
+ /// Indicates a pong control frame.
+ Pong,
+ /// Indicates an invalid opcode was received.
+ Bad,
+}
+
+impl OpCode {
+ /// Test whether the opcode indicates a control frame.
+ pub fn is_control(&self) -> bool {
+ match *self {
+ Text | Binary | Continue => false,
+ _ => true,
+ }
+ }
+}
+
+impl fmt::Display for OpCode {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match *self {
+ Continue => write!(f, "CONTINUE"),
+ Text => write!(f, "TEXT"),
+ Binary => write!(f, "BINARY"),
+ Close => write!(f, "CLOSE"),
+ Ping => write!(f, "PING"),
+ Pong => write!(f, "PONG"),
+ Bad => write!(f, "BAD"),
+ }
+ }
+}
+
+impl Into<u8> for OpCode {
+ fn into(self) -> u8 {
+ match self {
+ Continue => 0,
+ Text => 1,
+ Binary => 2,
+ Close => 8,
+ Ping => 9,
+ Pong => 10,
+ Bad => {
+ debug_assert!(
+ false,
+ "Attempted to convert invalid opcode to u8. This is a bug."
+ );
+ 8 // if this somehow happens, a close frame will help us tear down quickly
+ }
+ }
+ }
+}
+
+impl From<u8> for OpCode {
+ fn from(byte: u8) -> OpCode {
+ match byte {
+ 0 => Continue,
+ 1 => Text,
+ 2 => Binary,
+ 8 => Close,
+ 9 => Ping,
+ 10 => Pong,
+ _ => Bad,
+ }
+ }
+}
+
+use self::CloseCode::*;
+/// Status code used to indicate why an endpoint is closing the WebSocket connection.
+#[derive(Debug, Eq, PartialEq, Clone, Copy)]
+pub enum CloseCode {
+ /// Indicates a normal closure, meaning that the purpose for
+ /// which the connection was established has been fulfilled.
+ Normal,
+ /// Indicates that an endpoint is "going away", such as a server
+ /// going down or a browser having navigated away from a page.
+ Away,
+ /// Indicates that an endpoint is terminating the connection due
+ /// to a protocol error.
+ Protocol,
+ /// Indicates that an endpoint is terminating the connection
+ /// because it has received a type of data it cannot accept (e.g., an
+ /// endpoint that understands only text data MAY send this if it
+ /// receives a binary message).
+ Unsupported,
+ /// Indicates that no status code was included in a closing frame. This
+ /// close code makes it possible to use a single method, `on_close` to
+ /// handle even cases where no close code was provided.
+ Status,
+ /// Indicates an abnormal closure. If the abnormal closure was due to an
+ /// error, this close code will not be used. Instead, the `on_error` method
+ /// of the handler will be called with the error. However, if the connection
+ /// is simply dropped, without an error, this close code will be sent to the
+ /// handler.
+ Abnormal,
+ /// Indicates that an endpoint is terminating the connection
+ /// because it has received data within a message that was not
+ /// consistent with the type of the message (e.g., non-UTF-8 [RFC3629]
+ /// data within a text message).
+ Invalid,
+ /// Indicates that an endpoint is terminating the connection
+ /// because it has received a message that violates its policy. This
+ /// is a generic status code that can be returned when there is no
+ /// other more suitable status code (e.g., Unsupported or Size) or if there
+ /// is a need to hide specific details about the policy.
+ Policy,
+ /// Indicates that an endpoint is terminating the connection
+ /// because it has received a message that is too big for it to
+ /// process.
+ Size,
+ /// Indicates that an endpoint (client) is terminating the
+ /// connection because it has expected the server to negotiate one or
+ /// more extension, but the server didn't return them in the response
+ /// message of the WebSocket handshake. The list of extensions that
+ /// are needed should be given as the reason for closing.
+ /// Note that this status code is not used by the server, because it
+ /// can fail the WebSocket handshake instead.
+ Extension,
+ /// Indicates that a server is terminating the connection because
+ /// it encountered an unexpected condition that prevented it from
+ /// fulfilling the request.
+ Error,
+ /// Indicates that the server is restarting. A client may choose to reconnect,
+ /// and if it does, it should use a randomized delay of 5-30 seconds between attempts.
+ Restart,
+ /// Indicates that the server is overloaded and the client should either connect
+ /// to a different IP (when multiple targets exist), or reconnect to the same IP
+ /// when a user has performed an action.
+ Again,
+ #[doc(hidden)]
+ Tls,
+ #[doc(hidden)]
+ Empty,
+ #[doc(hidden)]
+ Other(u16),
+}
+
+impl Into<u16> for CloseCode {
+ fn into(self) -> u16 {
+ match self {
+ Normal => 1000,
+ Away => 1001,
+ Protocol => 1002,
+ Unsupported => 1003,
+ Status => 1005,
+ Abnormal => 1006,
+ Invalid => 1007,
+ Policy => 1008,
+ Size => 1009,
+ Extension => 1010,
+ Error => 1011,
+ Restart => 1012,
+ Again => 1013,
+ Tls => 1015,
+ Empty => 0,
+ Other(code) => code,
+ }
+ }
+}
+
+impl From<u16> for CloseCode {
+ fn from(code: u16) -> CloseCode {
+ match code {
+ 1000 => Normal,
+ 1001 => Away,
+ 1002 => Protocol,
+ 1003 => Unsupported,
+ 1005 => Status,
+ 1006 => Abnormal,
+ 1007 => Invalid,
+ 1008 => Policy,
+ 1009 => Size,
+ 1010 => Extension,
+ 1011 => Error,
+ 1012 => Restart,
+ 1013 => Again,
+ 1015 => Tls,
+ 0 => Empty,
+ _ => Other(code),
+ }
+ }
+}
+
+mod test {
+ #![allow(unused_imports, unused_variables, dead_code)]
+ use super::*;
+
+ #[test]
+ fn opcode_from_u8() {
+ let byte = 2u8;
+ assert_eq!(OpCode::from(byte), OpCode::Binary);
+ }
+
+ #[test]
+ fn opcode_into_u8() {
+ let text = OpCode::Text;
+ let byte: u8 = text.into();
+ assert_eq!(byte, 1u8);
+ }
+
+ #[test]
+ fn closecode_from_u16() {
+ let byte = 1008u16;
+ assert_eq!(CloseCode::from(byte), CloseCode::Policy);
+ }
+
+ #[test]
+ fn closecode_into_u16() {
+ let text = CloseCode::Away;
+ let byte: u16 = text.into();
+ assert_eq!(byte, 1001u16);
+ }
+}
diff --git a/third_party/rust/ws/src/result.rs b/third_party/rust/ws/src/result.rs
new file mode 100644
index 0000000000..eb3c151813
--- /dev/null
+++ b/third_party/rust/ws/src/result.rs
@@ -0,0 +1,204 @@
+use std::borrow::Cow;
+use std::convert::{From, Into};
+use std::error::Error as StdError;
+use std::fmt;
+use std::io;
+use std::result::Result as StdResult;
+use std::str::Utf8Error;
+
+use httparse;
+use mio;
+#[cfg(feature = "ssl")]
+use openssl::ssl::{Error as SslError, HandshakeError as SslHandshakeError};
+#[cfg(feature = "nativetls")]
+use native_tls::{Error as SslError, HandshakeError as SslHandshakeError};
+#[cfg(any(feature = "ssl", feature = "nativetls"))]
+type HandshakeError = SslHandshakeError<mio::tcp::TcpStream>;
+
+use communication::Command;
+
+pub type Result<T> = StdResult<T, Error>;
+
+/// The type of an error, which may indicate other kinds of errors as the underlying cause.
+#[derive(Debug)]
+pub enum Kind {
+ /// Indicates an internal application error.
+ /// If panic_on_internal is true, which is the default, then the application will panic.
+ /// Otherwise the WebSocket will automatically attempt to send an Error (1011) close code.
+ Internal,
+ /// Indicates a state where some size limit has been exceeded, such as an inability to accept
+ /// any more new connections.
+ /// If a Connection is active, the WebSocket will automatically attempt to send
+ /// a Size (1009) close code.
+ Capacity,
+ /// Indicates a violation of the WebSocket protocol.
+ /// The WebSocket will automatically attempt to send a Protocol (1002) close code, or if
+ /// this error occurs during a handshake, an HTTP 400 response will be generated.
+ Protocol,
+ /// Indicates that the WebSocket received data that should be utf8 encoded but was not.
+ /// The WebSocket will automatically attempt to send a Invalid Frame Payload Data (1007) close
+ /// code.
+ Encoding(Utf8Error),
+ /// Indicates an underlying IO Error.
+ /// This kind of error will result in a WebSocket Connection disconnecting.
+ Io(io::Error),
+ /// Indicates a failure to parse an HTTP message.
+ /// This kind of error should only occur during a WebSocket Handshake, and a HTTP 500 response
+ /// will be generated.
+ Http(httparse::Error),
+ /// Indicates a failure to send a signal on the internal EventLoop channel. This means that
+ /// the WebSocket is overloaded. In order to avoid this error, it is important to set
+ /// `Settings::max_connections` and `Settings:queue_size` high enough to handle the load.
+ /// If encountered, retuning from a handler method and waiting for the EventLoop to consume
+ /// the queue may relieve the situation.
+ Queue(mio::channel::SendError<Command>),
+ /// Indicates a failure to perform SSL encryption.
+ #[cfg(any(feature = "ssl", feature = "nativetls"))]
+ Ssl(SslError),
+ /// Indicates a failure to perform SSL encryption.
+ #[cfg(any(feature = "ssl", feature = "nativetls"))]
+ SslHandshake(HandshakeError),
+ /// A custom error kind for use by applications. This error kind involves extra overhead
+ /// because it will allocate the memory on the heap. The WebSocket ignores such errors by
+ /// default, simply passing them to the Connection Handler.
+ Custom(Box<dyn StdError + Send + Sync>),
+}
+
+/// A struct indicating the kind of error that has occurred and any precise details of that error.
+pub struct Error {
+ pub kind: Kind,
+ pub details: Cow<'static, str>,
+}
+
+impl Error {
+ pub fn new<I>(kind: Kind, details: I) -> Error
+ where
+ I: Into<Cow<'static, str>>,
+ {
+ Error {
+ kind,
+ details: details.into(),
+ }
+ }
+
+ pub fn into_box(self) -> Box<dyn StdError> {
+ match self.kind {
+ Kind::Custom(err) => err,
+ _ => Box::new(self),
+ }
+ }
+}
+
+impl fmt::Debug for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ if self.details.len() > 0 {
+ write!(f, "WS Error <{:?}>: {}", self.kind, self.details)
+ } else {
+ write!(f, "WS Error <{:?}>", self.kind)
+ }
+ }
+}
+
+impl fmt::Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ if self.details.len() > 0 {
+ write!(f, "{}: {}", self.description(), self.details)
+ } else {
+ write!(f, "{}", self.description())
+ }
+ }
+}
+
+impl StdError for Error {
+ fn description(&self) -> &str {
+ match self.kind {
+ Kind::Internal => "Internal Application Error",
+ Kind::Capacity => "WebSocket at Capacity",
+ Kind::Protocol => "WebSocket Protocol Error",
+ Kind::Encoding(ref err) => err.description(),
+ Kind::Io(ref err) => err.description(),
+ Kind::Http(_) => "Unable to parse HTTP",
+ #[cfg(any(feature = "ssl", feature = "nativetls"))]
+ Kind::Ssl(ref err) => err.description(),
+ #[cfg(any(feature = "ssl", feature = "nativetls"))]
+ Kind::SslHandshake(ref err) => err.description(),
+ Kind::Queue(_) => "Unable to send signal on event loop",
+ Kind::Custom(ref err) => err.description(),
+ }
+ }
+
+ fn cause(&self) -> Option<&dyn StdError> {
+ match self.kind {
+ Kind::Encoding(ref err) => Some(err),
+ Kind::Io(ref err) => Some(err),
+ #[cfg(any(feature = "ssl", feature = "nativetls"))]
+ Kind::Ssl(ref err) => Some(err),
+ #[cfg(any(feature = "ssl", feature = "nativetls"))]
+ Kind::SslHandshake(ref err) => err.cause(),
+ Kind::Custom(ref err) => Some(err.as_ref()),
+ _ => None,
+ }
+ }
+}
+
+impl From<io::Error> for Error {
+ fn from(err: io::Error) -> Error {
+ Error::new(Kind::Io(err), "")
+ }
+}
+
+impl From<httparse::Error> for Error {
+ fn from(err: httparse::Error) -> Error {
+ let details = match err {
+ httparse::Error::HeaderName => "Invalid byte in header name.",
+ httparse::Error::HeaderValue => "Invalid byte in header value.",
+ httparse::Error::NewLine => "Invalid byte in new line.",
+ httparse::Error::Status => "Invalid byte in Response status.",
+ httparse::Error::Token => "Invalid byte where token is required.",
+ httparse::Error::TooManyHeaders => {
+ "Parsed more headers than provided buffer can contain."
+ }
+ httparse::Error::Version => "Invalid byte in HTTP version.",
+ };
+
+ Error::new(Kind::Http(err), details)
+ }
+}
+
+impl From<mio::channel::SendError<Command>> for Error {
+ fn from(err: mio::channel::SendError<Command>) -> Error {
+ match err {
+ mio::channel::SendError::Io(err) => Error::from(err),
+ _ => Error::new(Kind::Queue(err), ""),
+ }
+ }
+}
+
+impl From<Utf8Error> for Error {
+ fn from(err: Utf8Error) -> Error {
+ Error::new(Kind::Encoding(err), "")
+ }
+}
+
+#[cfg(any(feature = "ssl", feature = "nativetls"))]
+impl From<SslError> for Error {
+ fn from(err: SslError) -> Error {
+ Error::new(Kind::Ssl(err), "")
+ }
+}
+
+#[cfg(any(feature = "ssl", feature = "nativetls"))]
+impl From<HandshakeError> for Error {
+ fn from(err: HandshakeError) -> Error {
+ Error::new(Kind::SslHandshake(err), "")
+ }
+}
+
+impl<B> From<Box<B>> for Error
+where
+ B: StdError + Send + Sync + 'static,
+{
+ fn from(err: Box<B>) -> Error {
+ Error::new(Kind::Custom(err), "")
+ }
+}
diff --git a/third_party/rust/ws/src/stream.rs b/third_party/rust/ws/src/stream.rs
new file mode 100644
index 0000000000..3b8d9c441b
--- /dev/null
+++ b/third_party/rust/ws/src/stream.rs
@@ -0,0 +1,358 @@
+use std::io;
+use std::io::ErrorKind::WouldBlock;
+#[cfg(any(feature = "ssl", feature = "nativetls"))]
+use std::mem::replace;
+use std::net::SocketAddr;
+
+use bytes::{Buf, BufMut};
+use mio::tcp::TcpStream;
+#[cfg(feature = "nativetls")]
+use native_tls::{
+ HandshakeError, MidHandshakeTlsStream as MidHandshakeSslStream, TlsStream as SslStream,
+};
+#[cfg(feature = "ssl")]
+use openssl::ssl::{ErrorCode as SslErrorCode, HandshakeError, MidHandshakeSslStream, SslStream};
+
+use result::{Error, Kind, Result};
+
+fn map_non_block<T>(res: io::Result<T>) -> io::Result<Option<T>> {
+ match res {
+ Ok(value) => Ok(Some(value)),
+ Err(err) => {
+ if let WouldBlock = err.kind() {
+ Ok(None)
+ } else {
+ Err(err)
+ }
+ }
+ }
+}
+
+pub trait TryReadBuf: io::Read {
+ fn try_read_buf<B: BufMut>(&mut self, buf: &mut B) -> io::Result<Option<usize>>
+ where
+ Self: Sized,
+ {
+ // Reads the length of the slice supplied by buf.mut_bytes into the buffer
+ // This is not guaranteed to consume an entire datagram or segment.
+ // If your protocol is msg based (instead of continuous stream) you should
+ // ensure that your buffer is large enough to hold an entire segment (1532 bytes if not jumbo
+ // frames)
+ let res = map_non_block(self.read(unsafe { buf.bytes_mut() }));
+
+ if let Ok(Some(cnt)) = res {
+ unsafe {
+ buf.advance_mut(cnt);
+ }
+ }
+
+ res
+ }
+}
+
+pub trait TryWriteBuf: io::Write {
+ fn try_write_buf<B: Buf>(&mut self, buf: &mut B) -> io::Result<Option<usize>>
+ where
+ Self: Sized,
+ {
+ let res = map_non_block(self.write(buf.bytes()));
+
+ if let Ok(Some(cnt)) = res {
+ buf.advance(cnt);
+ }
+
+ res
+ }
+}
+
+impl<T: io::Read> TryReadBuf for T {}
+impl<T: io::Write> TryWriteBuf for T {}
+
+use self::Stream::*;
+pub enum Stream {
+ Tcp(TcpStream),
+ #[cfg(any(feature = "ssl", feature = "nativetls"))]
+ Tls(TlsStream),
+}
+
+impl Stream {
+ pub fn tcp(stream: TcpStream) -> Stream {
+ Tcp(stream)
+ }
+
+ #[cfg(any(feature = "ssl", feature = "nativetls"))]
+ pub fn tls(stream: MidHandshakeSslStream<TcpStream>) -> Stream {
+ Tls(TlsStream::Handshake {
+ sock: stream,
+ negotiating: false,
+ })
+ }
+
+ #[cfg(any(feature = "ssl", feature = "nativetls"))]
+ pub fn tls_live(stream: SslStream<TcpStream>) -> Stream {
+ Tls(TlsStream::Live(stream))
+ }
+
+ #[cfg(any(feature = "ssl", feature = "nativetls"))]
+ pub fn is_tls(&self) -> bool {
+ match *self {
+ Tcp(_) => false,
+ Tls(_) => true,
+ }
+ }
+
+ pub fn evented(&self) -> &TcpStream {
+ match *self {
+ Tcp(ref sock) => sock,
+ #[cfg(any(feature = "ssl", feature = "nativetls"))]
+ Tls(ref inner) => inner.evented(),
+ }
+ }
+
+ pub fn is_negotiating(&self) -> bool {
+ match *self {
+ Tcp(_) => false,
+ #[cfg(any(feature = "ssl", feature = "nativetls"))]
+ Tls(ref inner) => inner.is_negotiating(),
+ }
+ }
+
+ pub fn clear_negotiating(&mut self) -> Result<()> {
+ match *self {
+ Tcp(_) => Err(Error::new(
+ Kind::Internal,
+ "Attempted to clear negotiating flag on non ssl connection.",
+ )),
+ #[cfg(any(feature = "ssl", feature = "nativetls"))]
+ Tls(ref mut inner) => inner.clear_negotiating(),
+ }
+ }
+
+ pub fn peer_addr(&self) -> io::Result<SocketAddr> {
+ match *self {
+ Tcp(ref sock) => sock.peer_addr(),
+ #[cfg(any(feature = "ssl", feature = "nativetls"))]
+ Tls(ref inner) => inner.peer_addr(),
+ }
+ }
+
+ pub fn local_addr(&self) -> io::Result<SocketAddr> {
+ match *self {
+ Tcp(ref sock) => sock.local_addr(),
+ #[cfg(any(feature = "ssl", feature = "nativetls"))]
+ Tls(ref inner) => inner.local_addr(),
+ }
+ }
+}
+
+impl io::Read for Stream {
+ fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+ match *self {
+ Tcp(ref mut sock) => sock.read(buf),
+ #[cfg(any(feature = "ssl", feature = "nativetls"))]
+ Tls(TlsStream::Live(ref mut sock)) => sock.read(buf),
+ #[cfg(any(feature = "ssl", feature = "nativetls"))]
+ Tls(ref mut tls_stream) => {
+ trace!("Attempting to read ssl handshake.");
+ match replace(tls_stream, TlsStream::Upgrading) {
+ TlsStream::Live(_) | TlsStream::Upgrading => unreachable!(),
+ TlsStream::Handshake {
+ sock,
+ mut negotiating,
+ } => match sock.handshake() {
+ Ok(mut sock) => {
+ trace!("Completed SSL Handshake");
+ let res = sock.read(buf);
+ *tls_stream = TlsStream::Live(sock);
+ res
+ }
+ #[cfg(feature = "ssl")]
+ Err(HandshakeError::SetupFailure(err)) => {
+ Err(io::Error::new(io::ErrorKind::Other, err))
+ }
+ #[cfg(feature = "ssl")]
+ Err(HandshakeError::Failure(mid))
+ | Err(HandshakeError::WouldBlock(mid)) => {
+ if mid.error().code() == SslErrorCode::WANT_READ {
+ negotiating = true;
+ }
+ let err = if let Some(io_error) = mid.error().io_error() {
+ Err(io::Error::new(
+ io_error.kind(),
+ format!("{:?}", io_error.get_ref()),
+ ))
+ } else {
+ Err(io::Error::new(
+ io::ErrorKind::Other,
+ format!("{}", mid.error()),
+ ))
+ };
+ *tls_stream = TlsStream::Handshake {
+ sock: mid,
+ negotiating,
+ };
+ err
+ }
+ #[cfg(feature = "nativetls")]
+ Err(HandshakeError::WouldBlock(mid)) => {
+ negotiating = true;
+ *tls_stream = TlsStream::Handshake {
+ sock: mid,
+ negotiating: negotiating,
+ };
+ Err(io::Error::new(io::ErrorKind::WouldBlock, "SSL would block"))
+ }
+ #[cfg(feature = "nativetls")]
+ Err(HandshakeError::Failure(err)) => {
+ Err(io::Error::new(io::ErrorKind::Other, format!("{}", err)))
+ }
+ },
+ }
+ }
+ }
+ }
+}
+
+impl io::Write for Stream {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ match *self {
+ Tcp(ref mut sock) => sock.write(buf),
+ #[cfg(any(feature = "ssl", feature = "nativetls"))]
+ Tls(TlsStream::Live(ref mut sock)) => sock.write(buf),
+ #[cfg(any(feature = "ssl", feature = "nativetls"))]
+ Tls(ref mut tls_stream) => {
+ trace!("Attempting to write ssl handshake.");
+ match replace(tls_stream, TlsStream::Upgrading) {
+ TlsStream::Live(_) | TlsStream::Upgrading => unreachable!(),
+ TlsStream::Handshake {
+ sock,
+ mut negotiating,
+ } => match sock.handshake() {
+ Ok(mut sock) => {
+ trace!("Completed SSL Handshake");
+ let res = sock.write(buf);
+ *tls_stream = TlsStream::Live(sock);
+ res
+ }
+ #[cfg(feature = "ssl")]
+ Err(HandshakeError::SetupFailure(err)) => {
+ Err(io::Error::new(io::ErrorKind::Other, err))
+ }
+ #[cfg(feature = "ssl")]
+ Err(HandshakeError::Failure(mid))
+ | Err(HandshakeError::WouldBlock(mid)) => {
+ if mid.error().code() == SslErrorCode::WANT_READ {
+ negotiating = true;
+ } else {
+ negotiating = false;
+ }
+ let err = if let Some(io_error) = mid.error().io_error() {
+ Err(io::Error::new(
+ io_error.kind(),
+ format!("{:?}", io_error.get_ref()),
+ ))
+ } else {
+ Err(io::Error::new(
+ io::ErrorKind::Other,
+ format!("{}", mid.error()),
+ ))
+ };
+ *tls_stream = TlsStream::Handshake {
+ sock: mid,
+ negotiating,
+ };
+ err
+ }
+ #[cfg(feature = "nativetls")]
+ Err(HandshakeError::WouldBlock(mid)) => {
+ negotiating = true;
+ *tls_stream = TlsStream::Handshake {
+ sock: mid,
+ negotiating: negotiating,
+ };
+ Err(io::Error::new(io::ErrorKind::WouldBlock, "SSL would block"))
+ }
+ #[cfg(feature = "nativetls")]
+ Err(HandshakeError::Failure(err)) => {
+ Err(io::Error::new(io::ErrorKind::Other, format!("{}", err)))
+ }
+ },
+ }
+ }
+ }
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ match *self {
+ Tcp(ref mut sock) => sock.flush(),
+ #[cfg(any(feature = "ssl", feature = "nativetls"))]
+ Tls(TlsStream::Live(ref mut sock)) => sock.flush(),
+ #[cfg(any(feature = "ssl", feature = "nativetls"))]
+ Tls(TlsStream::Handshake { ref mut sock, .. }) => sock.get_mut().flush(),
+ #[cfg(any(feature = "ssl", feature = "nativetls"))]
+ Tls(TlsStream::Upgrading) => panic!("Tried to access actively upgrading TlsStream"),
+ }
+ }
+}
+
+#[cfg(any(feature = "ssl", feature = "nativetls"))]
+pub enum TlsStream {
+ Live(SslStream<TcpStream>),
+ Handshake {
+ sock: MidHandshakeSslStream<TcpStream>,
+ negotiating: bool,
+ },
+ Upgrading,
+}
+
+#[cfg(any(feature = "ssl", feature = "nativetls"))]
+impl TlsStream {
+ pub fn evented(&self) -> &TcpStream {
+ match *self {
+ TlsStream::Live(ref sock) => sock.get_ref(),
+ TlsStream::Handshake { ref sock, .. } => sock.get_ref(),
+ TlsStream::Upgrading => panic!("Tried to access actively upgrading TlsStream"),
+ }
+ }
+
+ pub fn is_negotiating(&self) -> bool {
+ match *self {
+ TlsStream::Live(_) => false,
+ TlsStream::Handshake {
+ sock: _,
+ negotiating,
+ } => negotiating,
+ TlsStream::Upgrading => panic!("Tried to access actively upgrading TlsStream"),
+ }
+ }
+
+ pub fn clear_negotiating(&mut self) -> Result<()> {
+ match *self {
+ TlsStream::Live(_) => Err(Error::new(
+ Kind::Internal,
+ "Attempted to clear negotiating flag on live ssl connection.",
+ )),
+ TlsStream::Handshake {
+ sock: _,
+ ref mut negotiating,
+ } => Ok(*negotiating = false),
+ TlsStream::Upgrading => panic!("Tried to access actively upgrading TlsStream"),
+ }
+ }
+
+ pub fn peer_addr(&self) -> io::Result<SocketAddr> {
+ match *self {
+ TlsStream::Live(ref sock) => sock.get_ref().peer_addr(),
+ TlsStream::Handshake { ref sock, .. } => sock.get_ref().peer_addr(),
+ TlsStream::Upgrading => panic!("Tried to access actively upgrading TlsStream"),
+ }
+ }
+
+ pub fn local_addr(&self) -> io::Result<SocketAddr> {
+ match *self {
+ TlsStream::Live(ref sock) => sock.get_ref().local_addr(),
+ TlsStream::Handshake { ref sock, .. } => sock.get_ref().local_addr(),
+ TlsStream::Upgrading => panic!("Tried to access actively upgrading TlsStream"),
+ }
+ }
+}
diff --git a/third_party/rust/ws/src/util.rs b/third_party/rust/ws/src/util.rs
new file mode 100644
index 0000000000..fc66394a74
--- /dev/null
+++ b/third_party/rust/ws/src/util.rs
@@ -0,0 +1,9 @@
+//! The util module rexports some tools from mio in order to facilitate handling timeouts.
+
+/// Used to identify some timed-out event.
+pub use mio::Token;
+/// A handle to a specific timeout.
+pub use mio_extras::timer::Timeout;
+#[cfg(any(feature = "ssl", feature = "nativetls"))]
+/// TcpStream underlying the WebSocket
+pub use mio::tcp::TcpStream;
diff --git a/third_party/rust/ws/tests/bind.rs b/third_party/rust/ws/tests/bind.rs
new file mode 100644
index 0000000000..2a0c534621
--- /dev/null
+++ b/third_party/rust/ws/tests/bind.rs
@@ -0,0 +1,31 @@
+extern crate ws;
+
+use std::net::Ipv4Addr;
+
+struct Handler;
+impl ws::Handler for Handler {}
+
+#[test]
+fn bind_port_zero() {
+ let ws = ws::WebSocket::new(|_sender| Handler).unwrap();
+ let ws = ws.bind("127.0.0.1:0").unwrap();
+
+ let local_addr = ws.local_addr().unwrap();
+ println!("Listening on {}", local_addr);
+
+ assert_eq!(Ipv4Addr::new(127, 0, 0, 1), local_addr.ip());
+ assert_ne!(0, local_addr.port());
+}
+
+#[test]
+fn bind_try_multiple_addrs() {
+ let invalid_addr = "99.99.99.99:0".parse().unwrap();
+ let valid_addr = "127.0.0.1:9876".parse().unwrap();
+ let addrs = vec![invalid_addr, valid_addr, invalid_addr];
+
+ let ws = ws::WebSocket::new(|_sender| Handler).unwrap();
+ let ws = ws.bind(&addrs[..]).unwrap();
+
+ let local_addr = ws.local_addr().unwrap();
+ assert_eq!(valid_addr, local_addr);
+}
diff --git a/third_party/rust/ws/tests/deflate.rs b/third_party/rust/ws/tests/deflate.rs
new file mode 100644
index 0000000000..dfbfaddbf2
--- /dev/null
+++ b/third_party/rust/ws/tests/deflate.rs
@@ -0,0 +1,78 @@
+#![cfg(feature = "permessage-deflate")]
+extern crate env_logger;
+extern crate url;
+extern crate ws;
+
+use ws::deflate::DeflateHandler;
+use ws::{Builder, Message, Sender, Settings, WebSocket};
+
+#[test]
+fn round_trip() {
+ const MESSAGE: &'static str = "this is the message that will be sent as a message";
+
+ let mut name = "Client";
+
+ let mut ws = WebSocket::new(|output: Sender| {
+ if name == "Client" {
+ output.send(MESSAGE).unwrap();
+ }
+
+ let handler = move |msg: Message| {
+ if name == "Server" {
+ output.send(msg)
+ } else {
+ assert!(msg.as_text().unwrap() == MESSAGE);
+ output.shutdown()
+ }
+ };
+
+ name = "Server";
+
+ DeflateHandler::new(handler)
+ }).unwrap();
+
+ let url = url::Url::parse("ws://127.0.0.1:3012").unwrap();
+
+ ws.connect(url).unwrap();
+
+ ws.listen("127.0.0.1:3012").unwrap();
+}
+
+#[test]
+fn fragment() {
+ env_logger::init();
+ const MESSAGE: &'static str = "Hello";
+
+ let mut name = "Client";
+
+ let mut ws = Builder::new()
+ .with_settings(Settings {
+ fragment_size: 4,
+ ..Default::default()
+ })
+ .build(|output: Sender| {
+ if name == "Client" {
+ output.send(MESSAGE).unwrap();
+ }
+
+ let handler = move |msg: Message| {
+ if name == "Server" {
+ output.send(msg)
+ } else {
+ assert!(msg.as_text().unwrap() == MESSAGE);
+ output.shutdown()
+ }
+ };
+
+ name = "Server";
+
+ DeflateHandler::new(handler)
+ })
+ .unwrap();
+
+ let url = url::Url::parse("ws://127.0.0.1:3024").unwrap();
+
+ ws.connect(url).unwrap();
+
+ ws.listen("127.0.0.1:3024").unwrap();
+}
diff --git a/third_party/rust/ws/tests/fuzzingclient.json b/third_party/rust/ws/tests/fuzzingclient.json
new file mode 100644
index 0000000000..b5e8a000bf
--- /dev/null
+++ b/third_party/rust/ws/tests/fuzzingclient.json
@@ -0,0 +1,11 @@
+{
+ "outdir": "./reports/servers",
+
+ "servers": [{
+ "agent": "WS-RS",
+ "url": "ws://localhost:3012"
+ }],
+
+ "cases": ["*"],
+ "exclude-cases": []
+}
diff --git a/third_party/rust/ws/tests/fuzzingserver.json b/third_party/rust/ws/tests/fuzzingserver.json
new file mode 100644
index 0000000000..89d77d047e
--- /dev/null
+++ b/third_party/rust/ws/tests/fuzzingserver.json
@@ -0,0 +1,8 @@
+
+{
+ "url": "ws://127.0.0.1:9001",
+ "outdir": "./reports/clients",
+ "cases": ["*"],
+ "exclude-cases": [],
+ "exclude-agent-cases": {}
+}
diff --git a/third_party/rust/ws/tests/shutdown.rs b/third_party/rust/ws/tests/shutdown.rs
new file mode 100644
index 0000000000..1c12345726
--- /dev/null
+++ b/third_party/rust/ws/tests/shutdown.rs
@@ -0,0 +1,41 @@
+extern crate ws;
+
+use std::sync::mpsc::channel;
+use std::thread;
+use std::time::Duration;
+
+#[test]
+fn shutdown_before_connections() {
+ let (tx, rx) = channel();
+ let mut connections = 0;
+
+ let socket = ws::Builder::new()
+ .build(move |_| {
+ tx.send(1).unwrap();
+ |_| Ok(())
+ })
+ .unwrap();
+
+ let handle = socket.broadcaster();
+
+ let t = thread::spawn(move || {
+ socket.listen("127.0.0.1:3012").unwrap();
+ });
+
+ loop {
+ thread::sleep(Duration::from_millis(500));
+ let res = rx.try_recv();
+ assert!(res.is_err());
+ match res {
+ Ok(n) => connections += n,
+ Err(_) => {
+ if connections < 1 {
+ handle.shutdown().unwrap();
+ break;
+ }
+ }
+ }
+ }
+
+ assert!(t.join().is_ok());
+}
diff --git a/third_party/rust/ws2_32-sys/.cargo-checksum.json b/third_party/rust/ws2_32-sys/.cargo-checksum.json
new file mode 100644
index 0000000000..984121d42d
--- /dev/null
+++ b/third_party/rust/ws2_32-sys/.cargo-checksum.json
@@ -0,0 +1 @@
+{"files":{"Cargo.toml":"b5c32ebeb474fcf68bd5d6f296f12163d1627dca02dbe06341ee3d378535cdaa","README.md":"ad92627d07dcd015a10440590041e72723b1e5a3ca86f50d6d059e7e4e78433f","build.rs":"84b7d5871797983a021d1ae8cd9698687b7d2bddd4622e82b30c693218400f09","src/lib.rs":"71b9f929fe5227d63326e71ed2f49a96adb560318b8862d55927aac2d247e82d"},"package":"d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e"} \ No newline at end of file
diff --git a/third_party/rust/ws2_32-sys/Cargo.toml b/third_party/rust/ws2_32-sys/Cargo.toml
new file mode 100644
index 0000000000..71dc56645d
--- /dev/null
+++ b/third_party/rust/ws2_32-sys/Cargo.toml
@@ -0,0 +1,17 @@
+[package]
+name = "ws2_32-sys"
+version = "0.2.1"
+authors = ["Peter Atashian <retep998@gmail.com>"]
+description = "Contains function definitions for the Windows API library ws2_32. See winapi for types and constants."
+documentation = "https://retep998.github.io/doc/ws2_32/"
+repository = "https://github.com/retep998/winapi-rs"
+readme = "README.md"
+keywords = ["windows", "ffi", "win32"]
+license = "MIT"
+build = "build.rs"
+[lib]
+name = "ws2_32"
+[dependencies]
+winapi = { version = "0.2.5", path = "../.." }
+[build-dependencies]
+winapi-build = { version = "0.1.1", path = "../../build" }
diff --git a/third_party/rust/ws2_32-sys/README.md b/third_party/rust/ws2_32-sys/README.md
new file mode 100644
index 0000000000..8dd54c8e3b
--- /dev/null
+++ b/third_party/rust/ws2_32-sys/README.md
@@ -0,0 +1,13 @@
+# ws2_32 #
+Contains function definitions for the Windows API library ws2_32. See winapi for types and constants.
+
+```toml
+[dependencies]
+ws2_32-sys = "0.2.0"
+```
+
+```rust
+extern crate ws2_32;
+```
+
+[Documentation](https://retep998.github.io/doc/ws2_32/)
diff --git a/third_party/rust/ws2_32-sys/build.rs b/third_party/rust/ws2_32-sys/build.rs
new file mode 100644
index 0000000000..30b8283bb9
--- /dev/null
+++ b/third_party/rust/ws2_32-sys/build.rs
@@ -0,0 +1,6 @@
+// Copyright © 2015, Peter Atashian
+// Licensed under the MIT License <LICENSE.md>
+extern crate build;
+fn main() {
+ build::link("ws2_32", false)
+}
diff --git a/third_party/rust/ws2_32-sys/src/lib.rs b/third_party/rust/ws2_32-sys/src/lib.rs
new file mode 100644
index 0000000000..8a1cd4f47e
--- /dev/null
+++ b/third_party/rust/ws2_32-sys/src/lib.rs
@@ -0,0 +1,483 @@
+// Copyright © 2015, Peter Atashian
+// Licensed under the MIT License <LICENSE.md>
+//! FFI bindings to ws2_32.
+#![cfg(windows)]
+extern crate winapi;
+use winapi::*;
+extern "system" {
+ pub fn FreeAddrInfoEx(pAddrInfoEx: PADDRINFOEXA);
+ pub fn FreeAddrInfoExW(pAddrInfoEx: PADDRINFOEXW);
+ pub fn FreeAddrInfoW(pAddrInfo: PADDRINFOW);
+ pub fn GetAddrInfoExA(
+ pName: PCSTR, pServiceName: PCSTR, dwNameSpace: DWORD, lpNspId: LPGUID,
+ hints: *const ADDRINFOEXA, ppResult: *mut PADDRINFOEXA, timeout: *mut timeval,
+ lpOverlapped: LPOVERLAPPED, lpCompletionRoutine: LPLOOKUPSERVICE_COMPLETION_ROUTINE,
+ lpNameHandle: LPHANDLE,
+ ) -> INT;
+ pub fn GetAddrInfoExCancel(lpHandle: LPHANDLE) -> INT;
+ pub fn GetAddrInfoExOverlappedResult(lpOverlapped: LPOVERLAPPED) -> INT;
+ pub fn GetAddrInfoExW(
+ pName: PCWSTR, pServiceName: PCWSTR, dwNameSpace: DWORD, lpNspId: LPGUID,
+ hints: *const ADDRINFOEXW, ppResult: *mut PADDRINFOEXW, timeout: *mut timeval,
+ lpOverlapped: LPOVERLAPPED, lpCompletionRoutine: LPLOOKUPSERVICE_COMPLETION_ROUTINE,
+ lpNameHandle: LPHANDLE,
+ ) -> INT;
+ pub fn GetAddrInfoW(
+ pNodeName: PCWSTR, pServiceName: PCWSTR, pHints: *const ADDRINFOW,
+ ppResult: *mut PADDRINFOW,
+ ) -> INT;
+ pub fn GetHostNameW(name: PWSTR, namelen: c_int) -> c_int;
+ pub fn GetNameInfoW(
+ pSockaddr: *const SOCKADDR, SockaddrLength: socklen_t, pNodeBuffer: PWCHAR,
+ NodeBufferSize: DWORD, pServiceBuffer: PWCHAR, ServiceBufferSize: DWORD, Flags: INT,
+ ) -> INT;
+ pub fn InetNtopW(Family: INT, pAddr: PVOID, pStringBuf: PWSTR, StringBufSize: size_t) -> PCWSTR;
+ pub fn InetPtonW(Family: INT, pszAddrString: PCWSTR, pAddrBuf: PVOID) -> INT;
+ pub fn SetAddrInfoExA(
+ pName: PCSTR, pServiceName: PCSTR, pAddresses: *mut SOCKET_ADDRESS, dwAddressCount: DWORD,
+ lpBlob: LPBLOB, dwFlags: DWORD, dwNameSpace: DWORD, lpNspId: LPGUID, timeout: *mut timeval,
+ lpOverlapped: LPOVERLAPPED, lpCompletionRoutine: LPLOOKUPSERVICE_COMPLETION_ROUTINE,
+ lpNameHandle: LPHANDLE,
+ ) -> INT;
+ pub fn SetAddrInfoExW(
+ pName: PCWSTR, pServiceName: PCWSTR, pAddresses: *mut SOCKET_ADDRESS, dwAddressCount: DWORD,
+ lpBlob: LPBLOB, dwFlags: DWORD, dwNameSpace: DWORD, lpNspId: LPGUID, timeout: *mut timeval,
+ lpOverlapped: LPOVERLAPPED, lpCompletionRoutine: LPLOOKUPSERVICE_COMPLETION_ROUTINE,
+ lpNameHandle: LPHANDLE,
+ ) -> INT;
+ // pub fn WEP();
+ pub fn WPUCompleteOverlappedRequest(
+ s: SOCKET, lpOverlapped: LPWSAOVERLAPPED, dwError: DWORD, cbTransferred: DWORD,
+ lpErrno: LPINT,
+ ) -> c_int;
+ // pub fn WPUGetProviderPathEx();
+ pub fn WSAAccept(
+ s: SOCKET, addr: *mut SOCKADDR, addrlen: LPINT, lpfnCondition: LPCONDITIONPROC,
+ dwCallbackData: DWORD_PTR,
+ ) -> SOCKET;
+ pub fn WSAAddressToStringA(
+ lpsaAddress: LPSOCKADDR, dwAddressLength: DWORD, lpProtocolInfo: LPWSAPROTOCOL_INFOA,
+ lpszAddressString: LPSTR, lpdwAddressStringLength: LPDWORD,
+ ) -> INT;
+ pub fn WSAAddressToStringW(
+ lpsaAddress: LPSOCKADDR, dwAddressLength: DWORD, lpProtocolInfo: LPWSAPROTOCOL_INFOW,
+ lpszAddressString: LPWSTR, lpdwAddressStringLength: LPDWORD,
+ ) -> INT;
+ pub fn WSAAdvertiseProvider(
+ puuidProviderId: *const GUID, pNSPv2Routine: *const LPCNSPV2_ROUTINE,
+ ) -> INT;
+ pub fn WSAAsyncGetHostByAddr(
+ hWnd: HWND, wMsg: u_int, addr: *const c_char, len: c_int, _type: c_int, buf: *mut c_char,
+ buflen: c_int,
+ ) -> HANDLE;
+ pub fn WSAAsyncGetHostByName(
+ hWnd: HWND, wMsg: u_int, name: *const c_char, buf: *mut c_char, buflen: c_int,
+ ) -> HANDLE;
+ pub fn WSAAsyncGetProtoByName(
+ hWnd: HWND, wMsg: u_int, name: *const c_char, buf: *mut c_char, buflen: c_int,
+ ) -> HANDLE;
+ pub fn WSAAsyncGetProtoByNumber(
+ hWnd: HWND, wMsg: u_int, number: c_int, buf: *mut c_char, buflen: c_int,
+ ) -> HANDLE;
+ pub fn WSAAsyncGetServByName(
+ hWnd: HWND, wMsg: u_int, name: *const c_char, proto: *const c_char, buf: *mut c_char,
+ buflen: c_int,
+ ) -> HANDLE;
+ pub fn WSAAsyncGetServByPort(
+ hWnd: HWND, wMsg: u_int, port: c_int, proto: *const c_char, buf: *mut c_char, buflen: c_int,
+ ) -> HANDLE;
+ pub fn WSAAsyncSelect(s: SOCKET, hWnd: HWND, wMsg: u_int, lEvent: c_long) -> c_int;
+ pub fn WSACancelAsyncRequest(hAsyncTaskHandle: HANDLE) -> c_int;
+ pub fn WSACancelBlockingCall() -> c_int;
+ pub fn WSACleanup() -> c_int;
+ pub fn WSACloseEvent(hEvent: WSAEVENT) -> BOOL;
+ pub fn WSAConnect(
+ s: SOCKET, name: *const SOCKADDR, namelen: c_int, lpCallerData: LPWSABUF,
+ lpCalleeData: LPWSABUF, lpSQOS: LPQOS, lpGQOS: LPQOS,
+ ) -> c_int;
+ pub fn WSAConnectByList(
+ s: SOCKET, SocketAddress: PSOCKET_ADDRESS_LIST, LocalAddressLength: LPDWORD,
+ LocalAddress: LPSOCKADDR, RemoteAddressLength: LPDWORD, RemoteAddress: LPSOCKADDR,
+ timeout: *const timeval, Reserved: LPWSAOVERLAPPED,
+ ) -> BOOL;
+ pub fn WSAConnectByNameA(
+ s: SOCKET, nodename: LPCSTR, servicename: LPCSTR, LocalAddressLength: LPDWORD,
+ LocalAddress: LPSOCKADDR, RemoteAddressLength: LPDWORD, RemoteAddress: LPSOCKADDR,
+ timeout: *const timeval, Reserved: LPWSAOVERLAPPED,
+ ) -> BOOL;
+ pub fn WSAConnectByNameW(
+ s: SOCKET, nodename: LPWSTR, servicename: LPWSTR, LocalAddressLength: LPDWORD,
+ LocalAddress: LPSOCKADDR, RemoteAddressLength: LPDWORD, RemoteAddress: LPSOCKADDR,
+ timeout: *const timeval, Reserved: LPWSAOVERLAPPED,
+ ) -> BOOL;
+ pub fn WSACreateEvent() -> WSAEVENT;
+ pub fn WSADuplicateSocketA(
+ s: SOCKET, dwProcessId: DWORD, lpProtocolInfo: LPWSAPROTOCOL_INFOA,
+ ) -> c_int;
+ pub fn WSADuplicateSocketW(
+ s: SOCKET, dwProcessId: DWORD, lpProtocolInfo: LPWSAPROTOCOL_INFOW,
+ ) -> c_int;
+ pub fn WSAEnumNameSpaceProvidersA(
+ lpdwBufferLength: LPDWORD, lpnspBuffer: LPWSANAMESPACE_INFOA,
+ ) -> INT;
+ pub fn WSAEnumNameSpaceProvidersExA(
+ lpdwBufferLength: LPDWORD, lpnspBuffer: LPWSANAMESPACE_INFOEXA,
+ ) -> INT;
+ pub fn WSAEnumNameSpaceProvidersExW(
+ lpdwBufferLength: LPDWORD, lpnspBuffer: LPWSANAMESPACE_INFOEXW,
+ ) -> INT;
+ pub fn WSAEnumNameSpaceProvidersW(
+ lpdwBufferLength: LPDWORD, lpnspBuffer: LPWSANAMESPACE_INFOW,
+ ) -> INT;
+ pub fn WSAEnumNetworkEvents(
+ s: SOCKET, hEventObject: WSAEVENT, lpNetworkEvents: LPWSANETWORKEVENTS,
+ ) -> c_int;
+ pub fn WSAEnumProtocolsA(
+ lpiProtocols: LPINT, lpProtocolBuffer: LPWSAPROTOCOL_INFOA, lpdwBufferLength: LPDWORD,
+ ) -> c_int;
+ pub fn WSAEnumProtocolsW(
+ lpiProtocols: LPINT, lpProtocolBuffer: LPWSAPROTOCOL_INFOW, lpdwBufferLength: LPDWORD,
+ ) -> c_int;
+ pub fn WSAEventSelect(s: SOCKET, hEventObject: WSAEVENT, lNetworkEvents: c_long) -> c_int;
+ pub fn WSAGetLastError() -> c_int;
+ pub fn WSAGetOverlappedResult(
+ s: SOCKET, lpOverlapped: LPWSAOVERLAPPED, lpcbTransfer: LPDWORD, fWait: BOOL,
+ lpdwFlags: LPDWORD,
+ ) -> BOOL;
+ pub fn WSAGetQOSByName(s: SOCKET, lpQOSName: LPWSABUF, lpQOS: LPQOS) -> BOOL;
+ pub fn WSAGetServiceClassInfoA(
+ lpProviderId: LPGUID, lpServiceClassId: LPGUID, lpdwBufSize: LPDWORD,
+ lpServiceClassInfo: LPWSASERVICECLASSINFOA,
+ ) -> INT;
+ pub fn WSAGetServiceClassInfoW(
+ lpProviderId: LPGUID, lpServiceClassId: LPGUID, lpdwBufSize: LPDWORD,
+ lpServiceClassInfo: LPWSASERVICECLASSINFOW,
+ ) -> INT;
+ pub fn WSAGetServiceClassNameByClassIdA(
+ lpServiceClassId: LPGUID, lpszServiceClassName: LPSTR, lpdwBufferLength: LPDWORD,
+ ) -> INT;
+ pub fn WSAGetServiceClassNameByClassIdW(
+ lpServiceClassId: LPGUID, lpszServiceClassName: LPWSTR, lpdwBufferLength: LPDWORD,
+ ) -> INT;
+ pub fn WSAHtonl(s: SOCKET, hostlong: u_long, lpnetlong: *mut u_long) -> c_int;
+ pub fn WSAHtons(s: SOCKET, hostshort: u_short, lpnetshort: *mut u_short) -> c_int;
+ pub fn WSAInstallServiceClassA(lpServiceClassInfo: LPWSASERVICECLASSINFOA) -> INT;
+ pub fn WSAInstallServiceClassW(lpServiceClassInfo: LPWSASERVICECLASSINFOW) -> INT;
+ pub fn WSAIoctl(
+ s: SOCKET, dwIoControlCode: DWORD, lpvInBuffer: LPVOID, cbInBuffer: DWORD,
+ lpvOutBuffer: LPVOID, cbOutBuffer: DWORD, lpcbBytesReturned: LPDWORD,
+ lpOverlapped: LPWSAOVERLAPPED, lpCompletionRoutine: LPWSAOVERLAPPED_COMPLETION_ROUTINE,
+ ) -> c_int;
+ pub fn WSAIsBlocking() -> BOOL;
+ pub fn WSAJoinLeaf(
+ s: SOCKET, name: *const SOCKADDR, namelen: c_int, lpCallerData: LPWSABUF,
+ lpCalleeData: LPWSABUF, lpSQOS: LPQOS, lpGQOS: LPQOS, dwFlags: DWORD,
+ ) -> SOCKET;
+ pub fn WSALookupServiceBeginA(
+ lpqsRestrictions: LPWSAQUERYSETA, dwControlFlags: DWORD, lphLookup: LPHANDLE,
+ ) -> INT;
+ pub fn WSALookupServiceBeginW(
+ lpqsRestrictions: LPWSAQUERYSETW, dwControlFlags: DWORD, lphLookup: LPHANDLE,
+ ) -> INT;
+ pub fn WSALookupServiceEnd(hLookup: HANDLE) -> INT;
+ pub fn WSALookupServiceNextA(
+ hLookup: HANDLE, dwControlFlags: DWORD, lpdwBufferLength: LPDWORD,
+ lpqsResults: LPWSAQUERYSETA,
+ ) -> INT;
+ pub fn WSALookupServiceNextW(
+ hLookup: HANDLE, dwControlFlags: DWORD, lpdwBufferLength: LPDWORD,
+ lpqsResults: LPWSAQUERYSETW,
+ ) -> INT;
+ pub fn WSANSPIoctl(
+ hLookup: HANDLE, dwControlFlags: DWORD, lpvInBuffer: LPVOID, cbInBuffer: DWORD,
+ lpvOutBuffer: LPVOID, cbOutBuffer: DWORD, lpcbBytesReturned: LPDWORD,
+ lpCompletion: LPWSACOMPLETION,
+ ) -> INT;
+ pub fn WSANtohl(s: SOCKET, netlong: u_long, lphostlong: *mut c_long) -> c_int;
+ pub fn WSANtohs(s: SOCKET, netshort: u_short, lphostshort: *mut c_short) -> c_int;
+ pub fn WSAPoll(fdArray: LPWSAPOLLFD, fds: ULONG, timeout: INT) -> c_int;
+ pub fn WSAProviderCompleteAsyncCall(hAsyncCall: HANDLE, iRetCode: INT) -> INT;
+ pub fn WSAProviderConfigChange(
+ lpNotificationHandle: LPHANDLE, lpOverlapped: LPWSAOVERLAPPED,
+ lpCompletionRoutine: LPWSAOVERLAPPED_COMPLETION_ROUTINE,
+ ) -> INT;
+ pub fn WSARecv(
+ s: SOCKET, lpBuffers: LPWSABUF, dwBufferCount: DWORD, lpNumberOfBytesRecvd: LPDWORD,
+ lpFlags: LPDWORD, lpOverlapped: LPWSAOVERLAPPED,
+ lpCompletionRoutine: LPWSAOVERLAPPED_COMPLETION_ROUTINE,
+ ) -> c_int;
+ pub fn WSARecvDisconnect(s: SOCKET, lpInboundDisconnectData: LPWSABUF) -> c_int;
+ pub fn WSARecvFrom(
+ s: SOCKET, lpBuffers: LPWSABUF, dwBufferCount: DWORD, lpNumberOfBytesRecvd: LPDWORD,
+ lpFlags: LPDWORD, lpFrom: *mut SOCKADDR, lpFromlen: LPINT, lpOverlapped: LPWSAOVERLAPPED,
+ lpCompletionRoutine: LPWSAOVERLAPPED_COMPLETION_ROUTINE,
+ ) -> c_int;
+ pub fn WSARemoveServiceClass(lpServiceClassId: LPGUID) -> INT;
+ pub fn WSAResetEvent(hEvent: WSAEVENT) -> BOOL;
+ pub fn WSASend(
+ s: SOCKET, lpBuffers: LPWSABUF, dwBufferCount: DWORD, lpNumberOfBytesSent: LPDWORD,
+ dwFlags: DWORD, lpOverlapped: LPWSAOVERLAPPED,
+ lpCompletionRoutine: LPWSAOVERLAPPED_COMPLETION_ROUTINE,
+ ) -> c_int;
+ pub fn WSASendDisconnect(s: SOCKET, lpOutboundDisconnectData: LPWSABUF) -> c_int;
+ pub fn WSASendMsg(
+ Handle: SOCKET, lpMsg: LPWSAMSG, dwFlags: DWORD, lpNumberOfBytesSent: LPDWORD,
+ lpOverlapped: LPWSAOVERLAPPED, lpCompletionRoutine: LPWSAOVERLAPPED_COMPLETION_ROUTINE,
+ ) -> c_int;
+ pub fn WSASendTo(
+ s: SOCKET, lpBuffers: LPWSABUF, dwBufferCount: DWORD, lpNumberOfBytesSent: LPDWORD,
+ dwFlags: DWORD, lpTo: *const SOCKADDR, iToLen: c_int, lpOverlapped: LPWSAOVERLAPPED,
+ lpCompletionRoutine: LPWSAOVERLAPPED_COMPLETION_ROUTINE,
+ ) -> c_int;
+ pub fn WSASetBlockingHook(lpBlockFunc: FARPROC) -> FARPROC;
+ pub fn WSASetEvent(hEvent: WSAEVENT) -> BOOL;
+ pub fn WSASetLastError(iError: c_int);
+ pub fn WSASetServiceA(
+ lpqsRegInfo: LPWSAQUERYSETA, essoperation: WSAESETSERVICEOP, dwControlFlags: DWORD,
+ ) -> INT;
+ pub fn WSASetServiceW(
+ lpqsRegInfo: LPWSAQUERYSETW, essoperation: WSAESETSERVICEOP, dwControlFlags: DWORD,
+ ) -> INT;
+ pub fn WSASocketA(
+ af: c_int, _type: c_int, protocol: c_int, lpProtocolInfo: LPWSAPROTOCOL_INFOA, g: GROUP,
+ dwFlags: DWORD,
+ ) -> SOCKET;
+ pub fn WSASocketW(
+ af: c_int, _type: c_int, protocol: c_int, lpProtocolInfo: LPWSAPROTOCOL_INFOW, g: GROUP,
+ dwFlags: DWORD,
+ ) -> SOCKET;
+ pub fn WSAStartup(wVersionRequested: WORD, lpWSAData: LPWSADATA) -> c_int;
+ pub fn WSAStringToAddressA(
+ AddressString: LPSTR, AddressFamily: INT, lpProtocolInfo: LPWSAPROTOCOL_INFOA,
+ lpAddress: LPSOCKADDR, lpAddressLength: LPINT,
+ ) -> INT;
+ pub fn WSAStringToAddressW(
+ AddressString: LPWSTR, AddressFamily: INT, lpProtocolInfo: LPWSAPROTOCOL_INFOW,
+ lpAddress: LPSOCKADDR, lpAddressLength: LPINT,
+ ) -> INT;
+ pub fn WSAUnadvertiseProvider(puuidProviderId: *const GUID) -> INT;
+ pub fn WSAUnhookBlockingHook() -> c_int;
+ pub fn WSAWaitForMultipleEvents(
+ cEvents: DWORD, lphEvents: *const WSAEVENT, fWaitAll: BOOL, dwTimeout: DWORD,
+ fAlertable: BOOL,
+ ) -> DWORD;
+ pub fn WSCDeinstallProvider(lpProviderId: LPGUID, lpErrno: LPINT) -> c_int;
+ // pub fn WSCDeinstallProviderEx();
+ pub fn WSCEnableNSProvider(lpProviderId: LPGUID, fEnable: BOOL) -> INT;
+ pub fn WSCEnumProtocols(
+ lpiProtocols: LPINT, lpProtocolBuffer: LPWSAPROTOCOL_INFOW, lpdwBufferLength: LPDWORD,
+ lpErrno: LPINT,
+ ) -> c_int;
+ // pub fn WSCEnumProtocolsEx();
+ pub fn WSCGetApplicationCategory(
+ Path: LPCWSTR, PathLength: DWORD, Extra: LPCWSTR, ExtraLength: DWORD,
+ pPermittedLspCategories: *mut DWORD, lpErrno: LPINT,
+ ) -> c_int;
+ // pub fn WSCGetApplicationCategoryEx();
+ pub fn WSCGetProviderInfo(
+ lpProviderId: LPGUID, InfoType: WSC_PROVIDER_INFO_TYPE, Info: PBYTE, InfoSize: *mut size_t,
+ Flags: DWORD, lpErrno: LPINT,
+ ) -> c_int;
+ pub fn WSCGetProviderPath(
+ lpProviderId: LPGUID, lpszProviderDllPath: *mut WCHAR, lpProviderDllPathLen: LPINT,
+ lpErrno: LPINT,
+ ) -> c_int;
+ pub fn WSCInstallNameSpace(
+ lpszIdentifier: LPWSTR, lpszPathName: LPWSTR, dwNameSpace: DWORD, dwVersion: DWORD,
+ lpProviderId: LPGUID,
+ ) -> INT;
+ pub fn WSCInstallNameSpaceEx(
+ lpszIdentifier: LPWSTR, lpszPathName: LPWSTR, dwNameSpace: DWORD, dwVersion: DWORD,
+ lpProviderId: LPGUID, lpProviderSpecific: LPBLOB,
+ ) -> INT;
+ // pub fn WSCInstallNameSpaceEx2();
+ pub fn WSCInstallProvider(
+ lpProviderId: LPGUID, lpszProviderDllPath: *const WCHAR,
+ lpProtocolInfoList: LPWSAPROTOCOL_INFOW, dwNumberOfEntries: DWORD, lpErrno: LPINT,
+ ) -> c_int;
+ // pub fn WSCInstallProviderEx();
+ pub fn WSCSetApplicationCategory(
+ Path: LPCWSTR, PathLength: DWORD, Extra: LPCWSTR, ExtraLength: DWORD,
+ PermittedLspCategories: DWORD, pPrevPermLspCat: *mut DWORD, lpErrno: LPINT,
+ ) -> c_int;
+ // pub fn WSCSetApplicationCategoryEx();
+ pub fn WSCSetProviderInfo(
+ lpProviderId: LPGUID, InfoType: WSC_PROVIDER_INFO_TYPE, Info: PBYTE, InfoSize: size_t,
+ Flags: DWORD, lpErrno: LPINT,
+ ) -> c_int;
+ pub fn WSCUnInstallNameSpace(lpProviderId: LPGUID) -> INT;
+ // pub fn WSCUnInstallNameSpaceEx2();
+ pub fn WSCUpdateProvider(
+ lpProviderId: LPGUID, lpszProviderDllPath: *const WCHAR,
+ lpProtocolInfoList: LPWSAPROTOCOL_INFOW, dwNumberOfEntries: DWORD, lpErrno: LPINT,
+ ) -> c_int;
+ // pub fn WSCUpdateProviderEx();
+ pub fn WSCWriteNameSpaceOrder(lpProviderId: LPGUID, dwNumberOfEntries: DWORD) -> c_int;
+ pub fn WSCWriteProviderOrder(lpwdCatalogEntryId: LPDWORD, dwNumberOfEntries: DWORD) -> c_int;
+ // pub fn WSCWriteProviderOrderEx();
+ // pub fn WahCloseApcHelper();
+ // pub fn WahCloseHandleHelper();
+ // pub fn WahCloseNotificationHandleHelper();
+ // pub fn WahCloseSocketHandle();
+ // pub fn WahCloseThread();
+ // pub fn WahCompleteRequest();
+ // pub fn WahCreateHandleContextTable();
+ // pub fn WahCreateNotificationHandle();
+ // pub fn WahCreateSocketHandle();
+ // pub fn WahDestroyHandleContextTable();
+ // pub fn WahDisableNonIFSHandleSupport();
+ // pub fn WahEnableNonIFSHandleSupport();
+ // pub fn WahEnumerateHandleContexts();
+ // pub fn WahInsertHandleContext();
+ // pub fn WahNotifyAllProcesses();
+ // pub fn WahOpenApcHelper();
+ // pub fn WahOpenCurrentThread();
+ // pub fn WahOpenHandleHelper();
+ // pub fn WahOpenNotificationHandleHelper();
+ // pub fn WahQueueUserApc();
+ // pub fn WahReferenceContextByHandle();
+ // pub fn WahRemoveHandleContext();
+ // pub fn WahWaitForNotification();
+ // pub fn WahWriteLSPEvent();
+ pub fn __WSAFDIsSet(fd: SOCKET, _: *mut fd_set) -> c_int;
+ pub fn accept(s: SOCKET, addr: *mut SOCKADDR, addrlen: *mut c_int) -> SOCKET;
+ pub fn bind(s: SOCKET, name: *const SOCKADDR, namelen: c_int) -> c_int;
+ pub fn closesocket(s: SOCKET) -> c_int;
+ pub fn connect(s: SOCKET, name: *const SOCKADDR, namelen: c_int) -> c_int;
+ pub fn freeaddrinfo(pAddrInfo: PADDRINFOA);
+ pub fn getaddrinfo(
+ pNodeName: PCSTR, pServiceName: PCSTR, pHints: *const ADDRINFOA, ppResult: *mut PADDRINFOA,
+ ) -> INT;
+ pub fn gethostbyaddr(addr: *const c_char, len: c_int, _type: c_int) -> *mut hostent;
+ pub fn gethostbyname(name: *const c_char) -> *mut hostent;
+ pub fn gethostname(name: *mut c_char, namelen: c_int) -> c_int;
+ pub fn getnameinfo(
+ pSockaddr: *const SOCKADDR, SockaddrLength: socklen_t, pNodeBuffer: PCHAR,
+ NodeBufferSize: DWORD, pServiceBuffer: PCHAR, ServiceBufferSize: DWORD, Flags: INT,
+ ) -> INT;
+ pub fn getpeername(s: SOCKET, name: *mut SOCKADDR, namelen: *mut c_int) -> c_int;
+ pub fn getprotobyname(name: *const c_char) -> *mut protoent;
+ pub fn getprotobynumber(number: c_int) -> *mut protoent;
+ pub fn getservbyname(name: *const c_char, proto: *const c_char) -> *mut servent;
+ pub fn getservbyport(port: c_int, proto: *const c_char) -> *mut servent;
+ pub fn getsockname(s: SOCKET, name: *mut SOCKADDR, namelen: *mut c_int) -> c_int;
+ pub fn getsockopt(
+ s: SOCKET, level: c_int, optname: c_int, optval: *mut c_char, optlen: *mut c_int,
+ ) -> c_int;
+ pub fn htonl(hostlong: u_long) -> u_long;
+ pub fn htons(hostshort: u_short) -> u_short;
+ pub fn inet_addr(cp: *const c_char) -> c_ulong;
+ pub fn inet_ntoa(_in: in_addr) -> *mut c_char;
+ pub fn inet_ntop(Family: INT, pAddr: PVOID, pStringBuf: PSTR, StringBufSize: size_t) -> PCSTR;
+ pub fn inet_pton(Family: INT, pszAddrString: PCSTR, pAddrBuf: PVOID) -> INT;
+ pub fn ioctlsocket(s: SOCKET, cmd: c_long, argp: *mut u_long) -> c_int;
+ pub fn listen(s: SOCKET, backlog: c_int) -> c_int;
+ pub fn ntohl(netlong: u_long) -> u_long;
+ pub fn ntohs(netshort: u_short) -> u_short;
+ pub fn recv(s: SOCKET, buf: *mut c_char, len: c_int, flags: c_int) -> c_int;
+ pub fn recvfrom(
+ s: SOCKET, buf: *mut c_char, len: c_int, flags: c_int, from: *mut SOCKADDR,
+ fromlen: *mut c_int,
+ ) -> c_int;
+ pub fn select(
+ nfds: c_int, readfds: *mut fd_set, writefds: *mut fd_set, exceptfds: *mut fd_set,
+ timeout: *const timeval,
+ ) -> c_int;
+ pub fn send(s: SOCKET, buf: *const c_char, len: c_int, flags: c_int) -> c_int;
+ pub fn sendto(
+ s: SOCKET, buf: *const c_char, len: c_int, flags: c_int, to: *const SOCKADDR, tolen: c_int,
+ ) -> c_int;
+ pub fn setsockopt(
+ s: SOCKET, level: c_int, optname: c_int, optval: *const c_char, optlen: c_int,
+ ) -> c_int;
+ pub fn shutdown(s: SOCKET, how: c_int) -> c_int;
+ pub fn socket(af: c_int, _type: c_int, protocol: c_int) -> SOCKET;
+}
+#[cfg(any(target_arch = "x86", target_arch = "arm"))]
+extern "system" {
+ pub fn WSCInstallProviderAndChains(
+ lpProviderId: LPGUID, lpszProviderDllPath: LPWSTR, lpszLspName: LPWSTR,
+ dwServiceFlags: DWORD, lpProtocolInfoList: LPWSAPROTOCOL_INFOW, dwNumberOfEntries: DWORD,
+ lpdwCatalogEntryId: LPDWORD, lpErrno: LPINT,
+ ) -> c_int;
+}
+#[cfg(target_arch = "x86_64")]
+extern "system" {
+ pub fn WSCDeinstallProvider32(lpProviderId: LPGUID, lpErrno: LPINT) -> c_int;
+ pub fn WSCEnableNSProvider32(lpProviderId: LPGUID, fEnable: BOOL) -> INT;
+ pub fn WSCEnumNameSpaceProviders32(
+ lpdwBufferLength: LPDWORD, lpnspBuffer: LPWSANAMESPACE_INFOW,
+ ) -> INT;
+ pub fn WSCEnumNameSpaceProvidersEx32(
+ lpdwBufferLength: LPDWORD, lpnspBuffer: LPWSANAMESPACE_INFOEXW,
+ ) -> INT;
+ pub fn WSCEnumProtocols32(
+ lpiProtocols: LPINT, lpProtocolBuffer: LPWSAPROTOCOL_INFOW, lpdwBufferLength: LPDWORD,
+ lpErrno: LPINT,
+ ) -> c_int;
+ pub fn WSCGetProviderInfo32(
+ lpProviderId: LPGUID, InfoType: WSC_PROVIDER_INFO_TYPE, Info: PBYTE, InfoSize: *mut size_t,
+ Flags: DWORD, lpErrno: LPINT,
+ ) -> c_int;
+ pub fn WSCGetProviderPath32(
+ lpProviderId: LPGUID, lpszProviderDllPath: *mut WCHAR, lpProviderDllPathLen: LPINT,
+ lpErrno: LPINT,
+ ) -> c_int;
+ pub fn WSCInstallNameSpace32(
+ lpszIdentifier: LPWSTR, lpszPathName: LPWSTR, dwNameSpace: DWORD, dwVersion: DWORD,
+ lpProviderId: LPGUID,
+ ) -> INT;
+ pub fn WSCInstallNameSpaceEx32(
+ lpszIdentifier: LPWSTR, lpszPathName: LPWSTR, dwNameSpace: DWORD, dwVersion: DWORD,
+ lpProviderId: LPGUID, lpProviderSpecific: LPBLOB,
+ ) -> INT;
+ pub fn WSCInstallProvider64_32(
+ lpProviderId: LPGUID, lpszProviderDllPath: *const WCHAR,
+ lpProtocolInfoList: LPWSAPROTOCOL_INFOW, dwNumberOfEntries: DWORD, lpErrno: LPINT,
+ ) -> c_int;
+ pub fn WSCInstallProviderAndChains64_32(
+ lpProviderId: LPGUID, lpszProviderDllPath: LPWSTR, lpszProviderDllPath32: LPWSTR,
+ lpszLspName: LPWSTR, dwServiceFlags: DWORD, lpProtocolInfoList: LPWSAPROTOCOL_INFOW,
+ dwNumberOfEntries: DWORD, lpdwCatalogEntryId: LPDWORD, lpErrno: LPINT,
+ ) -> c_int;
+ pub fn WSCSetProviderInfo32(
+ lpProviderId: LPGUID, InfoType: WSC_PROVIDER_INFO_TYPE, Info: PBYTE, InfoSize: size_t,
+ Flags: DWORD, lpErrno: LPINT,
+ ) -> c_int;
+ pub fn WSCUnInstallNameSpace32(lpProviderId: LPGUID) -> INT;
+ pub fn WSCUpdateProvider32(
+ lpProviderId: LPGUID, lpszProviderDllPath: *const WCHAR,
+ lpProtocolInfoList: LPWSAPROTOCOL_INFOW, dwNumberOfEntries: DWORD, lpErrno: LPINT,
+ ) -> c_int;
+ pub fn WSCWriteNameSpaceOrder32(lpProviderId: LPGUID, dwNumberOfEntries: DWORD) -> c_int;
+ pub fn WSCWriteProviderOrder32(lpwdCatalogEntryId: LPDWORD, dwNumberOfEntries: DWORD) -> c_int;
+}
+extern {
+ // pub static AddressFamilyInformation;
+ // pub static eui48_broadcast;
+ // pub static in4addr_alligmpv3routersonlink;
+ // pub static in4addr_allnodesonlink;
+ // pub static in4addr_allroutersonlink;
+ // pub static in4addr_allteredohostsonlink;
+ // pub static in4addr_any;
+ // pub static in4addr_broadcast;
+ // pub static in4addr_linklocalprefix;
+ // pub static in4addr_loopback;
+ // pub static in4addr_multicastprefix;
+ // pub static in6addr_6to4prefix;
+ // pub static in6addr_allmldv2routersonlink;
+ // pub static in6addr_allnodesonlink;
+ // pub static in6addr_allnodesonnode;
+ // pub static in6addr_allroutersonlink;
+ // pub static in6addr_any;
+ // pub static in6addr_linklocalprefix;
+ // pub static in6addr_loopback;
+ // pub static in6addr_multicastprefix;
+ // pub static in6addr_solicitednodemulticastprefix;
+ // pub static in6addr_teredoinitiallinklocaladdress;
+ // pub static in6addr_teredoprefix;
+ // pub static in6addr_teredoprefix_old;
+ // pub static in6addr_v4mappedprefix;
+ // pub static scopeid_unspecified;
+ // pub static sockaddr_size;
+}