initial commit
This commit is contained in:
commit
379ae045d0
|
@ -0,0 +1 @@
|
||||||
|
target
|
|
@ -0,0 +1,537 @@
|
||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "arrayref"
|
||||||
|
version = "0.3.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "base64"
|
||||||
|
version = "0.21.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "2.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "block-buffer"
|
||||||
|
version = "0.10.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cc"
|
||||||
|
version = "1.0.82"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "305fe645edc1442a0fa8b6726ba61d422798d37a52e12eaecf4b022ebbb88f01"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "0.1.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "colored"
|
||||||
|
version = "2.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2674ec482fbc38012cf31e6c42ba0177b431a0cb6f15fe40efa5aab1bda516f6"
|
||||||
|
dependencies = [
|
||||||
|
"is-terminal",
|
||||||
|
"lazy_static",
|
||||||
|
"windows-sys 0.48.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cpufeatures"
|
||||||
|
version = "0.2.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crypto-common"
|
||||||
|
version = "0.1.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array",
|
||||||
|
"typenum",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "deranged"
|
||||||
|
version = "0.3.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7684a49fb1af197853ef7b2ee694bc1f5b4179556f1e5710e1760c5db6f5e929"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "digest"
|
||||||
|
version = "0.10.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
||||||
|
dependencies = [
|
||||||
|
"block-buffer",
|
||||||
|
"crypto-common",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "errno"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6b30f669a7961ef1631673d2766cc92f52d64f7ef354d4fe0ddfd30ed52f0f4f"
|
||||||
|
dependencies = [
|
||||||
|
"errno-dragonfly",
|
||||||
|
"libc",
|
||||||
|
"windows-sys 0.48.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "errno-dragonfly"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "form_urlencoded"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652"
|
||||||
|
dependencies = [
|
||||||
|
"percent-encoding",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "generic-array"
|
||||||
|
version = "0.14.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
|
||||||
|
dependencies = [
|
||||||
|
"typenum",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "getrandom"
|
||||||
|
version = "0.2.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if 1.0.0",
|
||||||
|
"libc",
|
||||||
|
"wasi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hermit-abi"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hornbeam"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"base64",
|
||||||
|
"log",
|
||||||
|
"radix64",
|
||||||
|
"rand",
|
||||||
|
"sha1",
|
||||||
|
"simple_logger",
|
||||||
|
"url",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hornbeam-client"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "idna"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-bidi",
|
||||||
|
"unicode-normalization",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "is-terminal"
|
||||||
|
version = "0.4.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b"
|
||||||
|
dependencies = [
|
||||||
|
"hermit-abi",
|
||||||
|
"rustix",
|
||||||
|
"windows-sys 0.48.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itoa"
|
||||||
|
version = "1.0.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lazy_static"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.147"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "linux-raw-sys"
|
||||||
|
version = "0.4.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "log"
|
||||||
|
version = "0.4.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num_threads"
|
||||||
|
version = "0.1.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "percent-encoding"
|
||||||
|
version = "2.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ppv-lite86"
|
||||||
|
version = "0.2.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "radix64"
|
||||||
|
version = "0.6.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "999718fa65c3be3a74f3f6dae5a98526ff436ea58a82a574f0de89eecd342bee"
|
||||||
|
dependencies = [
|
||||||
|
"arrayref",
|
||||||
|
"cfg-if 0.1.10",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand"
|
||||||
|
version = "0.8.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"rand_chacha",
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_chacha"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||||
|
dependencies = [
|
||||||
|
"ppv-lite86",
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.6.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustix"
|
||||||
|
version = "0.38.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "172891ebdceb05aa0005f533a6cbfca599ddd7d966f6f5d4d9b2e70478e70399"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"errno",
|
||||||
|
"libc",
|
||||||
|
"linux-raw-sys",
|
||||||
|
"windows-sys 0.48.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde"
|
||||||
|
version = "1.0.183"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "32ac8da02677876d532745a130fc9d8e6edfa81a269b107c5b00829b91d8eb3c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sha1"
|
||||||
|
version = "0.10.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if 1.0.0",
|
||||||
|
"cpufeatures",
|
||||||
|
"digest",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "simple_logger"
|
||||||
|
version = "4.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2230cd5c29b815c9b699fb610b49a5ed65588f3509d9f0108be3a885da629333"
|
||||||
|
dependencies = [
|
||||||
|
"colored",
|
||||||
|
"log",
|
||||||
|
"time",
|
||||||
|
"windows-sys 0.42.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "time"
|
||||||
|
version = "0.3.25"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b0fdd63d58b18d663fbdf70e049f00a22c8e42be082203be7f26589213cd75ea"
|
||||||
|
dependencies = [
|
||||||
|
"deranged",
|
||||||
|
"itoa",
|
||||||
|
"libc",
|
||||||
|
"num_threads",
|
||||||
|
"serde",
|
||||||
|
"time-core",
|
||||||
|
"time-macros",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "time-core"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "time-macros"
|
||||||
|
version = "0.2.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "eb71511c991639bb078fd5bf97757e03914361c48100d52878b8e52b46fb92cd"
|
||||||
|
dependencies = [
|
||||||
|
"time-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tinyvec"
|
||||||
|
version = "1.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
|
||||||
|
dependencies = [
|
||||||
|
"tinyvec_macros",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tinyvec_macros"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "typenum"
|
||||||
|
version = "1.16.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-bidi"
|
||||||
|
version = "0.3.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-normalization"
|
||||||
|
version = "0.1.22"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921"
|
||||||
|
dependencies = [
|
||||||
|
"tinyvec",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "url"
|
||||||
|
version = "2.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb"
|
||||||
|
dependencies = [
|
||||||
|
"form_urlencoded",
|
||||||
|
"idna",
|
||||||
|
"percent-encoding",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "version_check"
|
||||||
|
version = "0.9.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasi"
|
||||||
|
version = "0.11.0+wasi-snapshot-preview1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.42.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
|
||||||
|
dependencies = [
|
||||||
|
"windows_aarch64_gnullvm 0.42.2",
|
||||||
|
"windows_aarch64_msvc 0.42.2",
|
||||||
|
"windows_i686_gnu 0.42.2",
|
||||||
|
"windows_i686_msvc 0.42.2",
|
||||||
|
"windows_x86_64_gnu 0.42.2",
|
||||||
|
"windows_x86_64_gnullvm 0.42.2",
|
||||||
|
"windows_x86_64_msvc 0.42.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.48.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-targets"
|
||||||
|
version = "0.48.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f"
|
||||||
|
dependencies = [
|
||||||
|
"windows_aarch64_gnullvm 0.48.0",
|
||||||
|
"windows_aarch64_msvc 0.48.0",
|
||||||
|
"windows_i686_gnu 0.48.0",
|
||||||
|
"windows_i686_msvc 0.48.0",
|
||||||
|
"windows_x86_64_gnu 0.48.0",
|
||||||
|
"windows_x86_64_gnullvm 0.48.0",
|
||||||
|
"windows_x86_64_msvc 0.48.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_gnullvm"
|
||||||
|
version = "0.42.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_gnullvm"
|
||||||
|
version = "0.48.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_msvc"
|
||||||
|
version = "0.42.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_msvc"
|
||||||
|
version = "0.48.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnu"
|
||||||
|
version = "0.42.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnu"
|
||||||
|
version = "0.48.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_msvc"
|
||||||
|
version = "0.42.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_msvc"
|
||||||
|
version = "0.48.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnu"
|
||||||
|
version = "0.42.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnu"
|
||||||
|
version = "0.48.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnullvm"
|
||||||
|
version = "0.42.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnullvm"
|
||||||
|
version = "0.48.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_msvc"
|
||||||
|
version = "0.42.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_msvc"
|
||||||
|
version = "0.48.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
|
|
@ -0,0 +1,5 @@
|
||||||
|
[workspace]
|
||||||
|
members = [
|
||||||
|
"hornbeam",
|
||||||
|
"hornbeam-client"
|
||||||
|
]
|
|
@ -0,0 +1 @@
|
||||||
|
doc-valid-idents = ["WebSocket", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "DirectX", "ECMAScript", "GPLv2", "GPLv3", "GitHub", "GitLab", "IPv4", "IPv6", "ClojureScript", "CoffeeScript", "JavaScript", "PureScript", "TypeScript", "NaN", "NaNs", "OAuth", "GraphQL", "OCaml", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenDNS", "WebGL", "TensorFlow", "TrueType", "iOS", "macOS", "FreeBSD", "TeX", "LaTeX", "BibTeX", "BibLaTeX", "MinGW", "CamelCase"]
|
|
@ -0,0 +1,8 @@
|
||||||
|
[package]
|
||||||
|
name = "hornbeam-client"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
|
@ -0,0 +1,14 @@
|
||||||
|
pub fn add(left: usize, right: usize) -> usize {
|
||||||
|
left + right
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn it_works() {
|
||||||
|
let result = add(2, 2);
|
||||||
|
assert_eq!(result, 4);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
/target
|
|
@ -0,0 +1,30 @@
|
||||||
|
[package]
|
||||||
|
name = "hornbeam"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
url = "2.3"
|
||||||
|
sha1 = "0.10"
|
||||||
|
|
||||||
|
rand = { version = "0.8", optional = true } # feature: csprng-rand
|
||||||
|
|
||||||
|
base64 = { version = "0.21", optional = true } # feature: base64-base64
|
||||||
|
radix64 = { version = "0.6", optional = true } # feature: base64-radix64
|
||||||
|
|
||||||
|
log = { version = "0.4", optional = true } # feature: logging
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
simple_logger = "4.1"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["csprng-rand", "base64-radix64", "logging"]
|
||||||
|
|
||||||
|
csprng-rand = ["rand"]
|
||||||
|
|
||||||
|
base64-base64 = ["base64"]
|
||||||
|
base64-radix64 = ["radix64"]
|
||||||
|
|
||||||
|
logging = ["log"]
|
|
@ -0,0 +1,10 @@
|
||||||
|
use base64::Engine;
|
||||||
|
use crate::b64::b64_common::DecodeError;
|
||||||
|
|
||||||
|
pub(crate) fn base64_encode(b: &[u8]) -> String {
|
||||||
|
base64::engine::general_purpose::STANDARD.encode(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn base64_decode(b: &str) -> Result<Vec<u8>, DecodeError> {
|
||||||
|
Ok(base64::engine::general_purpose::STANDARD.decode(b)?)
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
use std::fmt::{Display, Formatter};
|
||||||
|
|
||||||
|
/// Errors that can occur during decoding.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub enum DecodeError {
|
||||||
|
/// An invalid byte was found in the input. The offending byte is provided.
|
||||||
|
InvalidByte(u8),
|
||||||
|
/// The length of the input is invalid.
|
||||||
|
InvalidLength,
|
||||||
|
/// The last non-padding byte of input has discarded bits and those bits are
|
||||||
|
/// not zero. While this could be decoded it likely represents a corrupted or
|
||||||
|
/// invalid encoding.
|
||||||
|
InvalidTrailingBits,
|
||||||
|
/// invalid padding
|
||||||
|
InvalidPadding
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for DecodeError {
|
||||||
|
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
DecodeError::InvalidByte(byte) => write!(f, "invalid byte {}", byte),
|
||||||
|
DecodeError::InvalidLength => write!(f, "encoded text cannot have a 6-bit remainder"),
|
||||||
|
DecodeError::InvalidTrailingBits => {
|
||||||
|
write!(f, "last byte has unnecessary trailing bits")
|
||||||
|
},
|
||||||
|
DecodeError::InvalidPadding => {
|
||||||
|
write!(f, "invalid padding")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "base64-radix64")]
|
||||||
|
impl From<radix64::DecodeError> for DecodeError {
|
||||||
|
fn from(value: radix64::DecodeError) -> Self {
|
||||||
|
match value {
|
||||||
|
radix64::DecodeError::InvalidByte(b) => Self::InvalidByte(b),
|
||||||
|
radix64::DecodeError::InvalidLength => Self::InvalidLength,
|
||||||
|
radix64::DecodeError::InvalidTrailingBits => Self::InvalidTrailingBits
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "base64-base64")]
|
||||||
|
impl From<base64::DecodeError> for DecodeError {
|
||||||
|
fn from(value: base64::DecodeError) -> Self {
|
||||||
|
match value {
|
||||||
|
base64::DecodeError::InvalidByte(_, b) => Self::InvalidByte(b),
|
||||||
|
base64::DecodeError::InvalidLength => Self::InvalidLength,
|
||||||
|
base64::DecodeError::InvalidLastSymbol(_, _) => Self::InvalidTrailingBits,
|
||||||
|
base64::DecodeError::InvalidPadding => Self::InvalidPadding
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
use crate::b64::b64_common::DecodeError;
|
||||||
|
|
||||||
|
pub(crate) fn base64_encode(b: &[u8]) -> String {
|
||||||
|
radix64::STD.encode(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn base64_decode(b: &str) -> Result<Vec<u8>, DecodeError> {
|
||||||
|
Ok(radix64::STD.decode(b)?)
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
#[cfg(not(any(feature = "base64-base64", feature = "base64-radix64")))]
|
||||||
|
compile_error!("You need to select one Base64 implementation");
|
||||||
|
|
||||||
|
#[cfg(all(feature = "base64-base64", feature = "base64-radix64"))]
|
||||||
|
compile_error!("You can only select one Base64 implementation");
|
||||||
|
|
||||||
|
#[cfg(feature = "base64-base64")]
|
||||||
|
#[path = "b64_base64.rs"]
|
||||||
|
pub(crate) mod impl_b64;
|
||||||
|
|
||||||
|
#[cfg(feature = "base64-radix64")]
|
||||||
|
#[path = "b64_radix64.rs"]
|
||||||
|
pub(crate) mod impl_b64;
|
||||||
|
|
||||||
|
pub(crate) mod b64_common;
|
|
@ -0,0 +1,331 @@
|
||||||
|
use std::error::Error;
|
||||||
|
use std::fmt::{Display, Formatter};
|
||||||
|
use std::io;
|
||||||
|
use std::io::{Read, Write};
|
||||||
|
use std::string::FromUtf8Error;
|
||||||
|
use url::Url;
|
||||||
|
use crate::handshake_common::{HeaderMap, WEBSOCKET_PROTOCOL_VERSION};
|
||||||
|
use crate::random::websocket_client_key;
|
||||||
|
|
||||||
|
/// Contains the information needed to perform the WebSocket client handshake. Create from a URL with `ClientConnectionInfo::from(url)`,
|
||||||
|
/// or build from it's components with `ClientConnectionInfo::build()`.
|
||||||
|
pub struct ClientConnectionInfo {
|
||||||
|
url: Url,
|
||||||
|
websocket_key: [u8; 16],
|
||||||
|
origin: Option<String>,
|
||||||
|
extra_headers: HeaderMap
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
/// Errors that can happen while sending the client handshake
|
||||||
|
pub enum ClientHandshakeSendError {
|
||||||
|
/// The URL scheme on the URL provided was invalid (not ws or wss)
|
||||||
|
InvalidUrlScheme {
|
||||||
|
/// The scheme provided
|
||||||
|
got_scheme: String
|
||||||
|
},
|
||||||
|
/// The URL provided does not have a host
|
||||||
|
UrlMissingHost,
|
||||||
|
/// There was an IO error while trying to write the handshake
|
||||||
|
IoError(io::Error)
|
||||||
|
}
|
||||||
|
impl Display for ClientHandshakeSendError {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
ClientHandshakeSendError::InvalidUrlScheme { got_scheme } => {
|
||||||
|
write!(f, "invalid url scheme: expected one of 'ws', 'wss', got '{}'", got_scheme)
|
||||||
|
},
|
||||||
|
ClientHandshakeSendError::UrlMissingHost => {
|
||||||
|
write!(f, "invalid url: url missing host")
|
||||||
|
}
|
||||||
|
ClientHandshakeSendError::IoError(e) => {
|
||||||
|
write!(f, "io error: {}", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Error for ClientHandshakeSendError {}
|
||||||
|
|
||||||
|
impl From<io::Error> for ClientHandshakeSendError {
|
||||||
|
fn from(value: io::Error) -> Self {
|
||||||
|
Self::IoError(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
/// Errors that can happen while receiving the client handshake response
|
||||||
|
pub enum ClientHandshakeRecvError {
|
||||||
|
/// There was an IO error while trying to read the handshake response
|
||||||
|
IoError(io::Error),
|
||||||
|
/// Reached an EOF while trying to read the handshake
|
||||||
|
UnexpectedEOF,
|
||||||
|
/// Received invalid UTF-8 data while trying to parse the handshake
|
||||||
|
InvalidUTF8(FromUtf8Error),
|
||||||
|
/// Server did not respond with a HTTP/1.1 response
|
||||||
|
IncorrectHTTPVersion {
|
||||||
|
/// The version that the server actually sent
|
||||||
|
server_response: String
|
||||||
|
},
|
||||||
|
/// WebSocket handshake responses must be 4 lines at minimum, but this response was too short
|
||||||
|
ResponseTooShort,
|
||||||
|
/// WebSocket handshake responses must be valid HTTP responses, but the header was improperly formatted
|
||||||
|
IncorrectHTTPHeader,
|
||||||
|
/// The HTTP response indicated failure
|
||||||
|
HttpErrorCode {
|
||||||
|
/// The error code returned by the server
|
||||||
|
code: String,
|
||||||
|
/// The error code description returned by the server
|
||||||
|
message: String
|
||||||
|
},
|
||||||
|
/// The handshake response was missing the `Connection: Upgrade` header
|
||||||
|
MissingConnectionUpgrade,
|
||||||
|
/// The handshake response was missing the `Upgrade: websocket` header
|
||||||
|
MissingUpgradeWebsocket,
|
||||||
|
/// The handshake response was missing the `Sec-WebSocket-Accept` header
|
||||||
|
MissingSecWebsocketAccept,
|
||||||
|
/// The handshake response had the `Sec-WebSocket-Accept` header, but it's value was incorrect
|
||||||
|
IncorrectSecWebsocketAccept
|
||||||
|
}
|
||||||
|
impl Display for ClientHandshakeRecvError {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::IoError(e) => {
|
||||||
|
write!(f, "io error: {e}")
|
||||||
|
},
|
||||||
|
Self::UnexpectedEOF => {
|
||||||
|
write!(f, "unexpected EOF")
|
||||||
|
},
|
||||||
|
Self::InvalidUTF8(e) => {
|
||||||
|
write!(f, "invalid UTF-8: {e}")
|
||||||
|
},
|
||||||
|
Self::IncorrectHTTPVersion { server_response } => {
|
||||||
|
write!(f, "incorrect HTTP version while reading response, expected HTTP/1.1, got '{server_response}'")
|
||||||
|
},
|
||||||
|
Self::ResponseTooShort => {
|
||||||
|
write!(f, "websocket response invalid (too short)")
|
||||||
|
},
|
||||||
|
Self::IncorrectHTTPHeader => {
|
||||||
|
write!(f, "websocket response invalid (improper http response)")
|
||||||
|
},
|
||||||
|
Self::HttpErrorCode { code, message } => {
|
||||||
|
write!(f, "websocket error: HTTP {code} {message}")
|
||||||
|
},
|
||||||
|
Self::MissingConnectionUpgrade => write!(f, "websocket response invalid (missing Connection: Upgrade)"),
|
||||||
|
Self::MissingUpgradeWebsocket => write!(f, "websocket response invalid (missing Upgrade: websocket)"),
|
||||||
|
Self::MissingSecWebsocketAccept => write!(f, "websocket response invalid (missing Sec-WebSocket-Accept)"),
|
||||||
|
Self::IncorrectSecWebsocketAccept => write!(f, "websocket response invalid (Sec-WebSocket-Accept incorrect)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Error for ClientHandshakeRecvError {}
|
||||||
|
|
||||||
|
impl From<io::Error> for ClientHandshakeRecvError {
|
||||||
|
fn from(value: io::Error) -> Self {
|
||||||
|
Self::IoError(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<FromUtf8Error> for ClientHandshakeRecvError {
|
||||||
|
fn from(value: FromUtf8Error) -> Self {
|
||||||
|
Self::InvalidUTF8(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ClientConnectionInfo {
|
||||||
|
/// Creates a new `ClientConnectionInfo` with the connection target URL, an optional Origin header value, and
|
||||||
|
/// any headers you wish to set in the initial request (cookies, authentication, etc)
|
||||||
|
pub fn build(url: Url, origin: Option<String>, extra_headers: HeaderMap) -> Self {
|
||||||
|
Self {
|
||||||
|
url,
|
||||||
|
websocket_key: websocket_client_key(),
|
||||||
|
origin,
|
||||||
|
extra_headers
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Send the WebSocket client handshake over the given stream.
|
||||||
|
/// # Errors
|
||||||
|
/// This function will return an error if the handshake fails for any reason.
|
||||||
|
/// See `ClientHandshakeError` for more details.
|
||||||
|
pub fn send_handshake<W: Write>(&self, w: &mut W) -> Result<(), ClientHandshakeSendError> {
|
||||||
|
let resource_url = self.url.path().to_string() + self.url.query().unwrap_or("");
|
||||||
|
|
||||||
|
// send the handshake:
|
||||||
|
// 1. HTTP GET
|
||||||
|
write!(w, "GET {resource_url} HTTP/1.1\r\n")?;
|
||||||
|
write!(w, "Host: {}\r\n", self.url.host_str().ok_or(ClientHandshakeSendError::UrlMissingHost)?)?;
|
||||||
|
write!(w, "Upgrade: websocket\r\n")?;
|
||||||
|
write!(w, "Connection: upgrade\r\n")?;
|
||||||
|
write!(w, "Sec-WebSocket-Key: {}\r\n", crate::b64::impl_b64::base64_encode(&self.websocket_key))?;
|
||||||
|
|
||||||
|
if let Some(origin) = &self.origin {
|
||||||
|
write!(w, "Origin: {origin}\r\n")?;
|
||||||
|
}
|
||||||
|
write!(w, "Sec-WebSocket-Version: {WEBSOCKET_PROTOCOL_VERSION}\r\n")?;
|
||||||
|
|
||||||
|
// TODO: permessage-deflate (7692)
|
||||||
|
|
||||||
|
for (key, val) in &self.extra_headers {
|
||||||
|
write!(w, "{key}: {val}\r\n")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
write!(w, "\r\n")?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read the WebSocket handshake response from the given stream.
|
||||||
|
/// # Errors
|
||||||
|
/// This function will return an error in many circumstances. See `ClientHandshakeRecvError` for details.
|
||||||
|
pub fn read_handshake<R: Read>(&self, r: &mut R) -> Result<(), ClientHandshakeRecvError> {
|
||||||
|
// read until we see the double \r\n
|
||||||
|
let mut read_bytes = vec![];
|
||||||
|
let mut read_buf = [0u8; 1024];
|
||||||
|
loop {
|
||||||
|
let read = r.read(&mut read_buf)?;
|
||||||
|
|
||||||
|
if read == 0 {
|
||||||
|
return Err(ClientHandshakeRecvError::UnexpectedEOF);
|
||||||
|
}
|
||||||
|
|
||||||
|
read_bytes.extend_from_slice(&read_buf[0..read-1]);
|
||||||
|
|
||||||
|
if read_bytes.ends_with(&[0x0d, 0x0a, 0x0d]) || read_bytes.ends_with(&[0x0d, 0x0a, 0x0d, 0x0a]) {
|
||||||
|
// we have hit the end of the server ws handshake
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
debug!("read {:?} bytes from server, parsing as handshake", read_bytes);
|
||||||
|
|
||||||
|
let handshake_response = String::from_utf8(read_bytes)?;
|
||||||
|
|
||||||
|
trace!("{}", handshake_response);
|
||||||
|
|
||||||
|
let lines = handshake_response.lines().collect::<Vec<_>>();
|
||||||
|
|
||||||
|
if lines.len() < 4 {
|
||||||
|
return Err(ClientHandshakeRecvError::ResponseTooShort);
|
||||||
|
}
|
||||||
|
|
||||||
|
let http_header_parts = lines[0].split(' ').collect::<Vec<_>>();
|
||||||
|
|
||||||
|
if http_header_parts.len() < 3 {
|
||||||
|
return Err(ClientHandshakeRecvError::IncorrectHTTPHeader);
|
||||||
|
}
|
||||||
|
|
||||||
|
if http_header_parts[0] != "HTTP/1.1" {
|
||||||
|
return Err(ClientHandshakeRecvError::IncorrectHTTPVersion {
|
||||||
|
server_response: http_header_parts[0].to_string()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if http_header_parts[1] != "101" {
|
||||||
|
let error_code = http_header_parts[1].to_string();
|
||||||
|
let error_response = http_header_parts[2..].join(" ");
|
||||||
|
return Err(ClientHandshakeRecvError::HttpErrorCode {
|
||||||
|
code: error_code,
|
||||||
|
message: error_response
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut has_connection = false;
|
||||||
|
let mut has_upgrade = false;
|
||||||
|
let mut has_sec_accept = false;
|
||||||
|
|
||||||
|
for line in &lines[1..] {
|
||||||
|
if line.to_lowercase() == "connection: upgrade" {
|
||||||
|
has_connection = true;
|
||||||
|
} else if line.to_lowercase() == "upgrade: websocket" {
|
||||||
|
has_upgrade = true;
|
||||||
|
} else if line.to_lowercase().starts_with("sec-websocket-accept: ") {
|
||||||
|
let accept_key = line.split(' ').nth(1).unwrap();
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !has_connection {
|
||||||
|
return Err(ClientHandshakeRecvError::MissingConnectionUpgrade);
|
||||||
|
}
|
||||||
|
if !has_upgrade {
|
||||||
|
return Err(ClientHandshakeRecvError::MissingUpgradeWebsocket);
|
||||||
|
}
|
||||||
|
if !has_sec_accept {
|
||||||
|
return Err(ClientHandshakeRecvError::MissingSecWebsocketAccept);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Url> for ClientConnectionInfo {
|
||||||
|
fn from(value: Url) -> Self {
|
||||||
|
Self::build(
|
||||||
|
value,
|
||||||
|
None,
|
||||||
|
HeaderMap::new()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
#[allow(clippy::unwrap_used)]
|
||||||
|
mod tests {
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::io::{Cursor};
|
||||||
|
use std::net::TcpStream;
|
||||||
|
use std::str::FromStr;
|
||||||
|
use std::sync::Once;
|
||||||
|
use url::Url;
|
||||||
|
use crate::handshake_client::ClientConnectionInfo;
|
||||||
|
|
||||||
|
static LOG: Once = Once::new();
|
||||||
|
fn setup_logger() {
|
||||||
|
LOG.call_once(|| simple_logger::init().unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn client_handshake_test() {
|
||||||
|
setup_logger();
|
||||||
|
|
||||||
|
let mut client = ClientConnectionInfo::build(
|
||||||
|
Url::from_str("ws://somesite.com").unwrap(),
|
||||||
|
Some("whatever".to_string()),
|
||||||
|
HashMap::from([
|
||||||
|
("Authorization".to_string(), "Bearer 12345".to_string())
|
||||||
|
])
|
||||||
|
);
|
||||||
|
client.websocket_key = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10];
|
||||||
|
|
||||||
|
let mut fakesock = Cursor::new(Vec::new());
|
||||||
|
|
||||||
|
client.send_handshake(&mut fakesock).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
String::from_utf8(fakesock.into_inner()).unwrap(),
|
||||||
|
|
||||||
|
"GET / HTTP/1.1\r\n\
|
||||||
|
Host: somesite.com\r\n\
|
||||||
|
Upgrade: websocket\r\n\
|
||||||
|
Connection: upgrade\r\n\
|
||||||
|
Sec-WebSocket-Key: AQIDBAUGBwgJCgsMDQ4PEA==\r\n\
|
||||||
|
Origin: whatever\r\n\
|
||||||
|
Sec-WebSocket-Version: 13\r\n\
|
||||||
|
Authorization: Bearer 12345\r\n\r\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn live_client_test() {
|
||||||
|
setup_logger();
|
||||||
|
|
||||||
|
let client = ClientConnectionInfo::from(Url::from_str("ws://10.16.1.1:3204/ws").unwrap());
|
||||||
|
|
||||||
|
let mut tcpstream = TcpStream::connect("10.16.1.1:3204").unwrap();
|
||||||
|
|
||||||
|
client.send_handshake(&mut tcpstream).unwrap();
|
||||||
|
client.read_handshake(&mut tcpstream).unwrap();
|
||||||
|
|
||||||
|
panic!();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use sha1::{Sha1, Digest};
|
||||||
|
|
||||||
|
/// The WebSocket protocol version, as defined by RFC 6455 to be 13.
|
||||||
|
pub const WEBSOCKET_PROTOCOL_VERSION: i32 = 13;
|
||||||
|
|
||||||
|
/// A type alias for a key-value header map
|
||||||
|
pub type HeaderMap = HashMap<String, String>;
|
||||||
|
|
||||||
|
pub(crate) fn derive_handshake_response(input: [u8; 16]) -> String {
|
||||||
|
let mut hasher = Sha1::new();
|
||||||
|
hasher.update(input);
|
||||||
|
hasher.update(b"258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
|
||||||
|
crate::b64::impl_b64::base64_encode(&hasher.finalize())
|
||||||
|
}
|
|
@ -0,0 +1,84 @@
|
||||||
|
//! # Hornbeam
|
||||||
|
//! Hornbeam is a simple WebSocket protocol library. For the client or server crates, check out
|
||||||
|
//! `hornbeam-client` or `hornbeam-server`.
|
||||||
|
|
||||||
|
#![deny(clippy::correctness)]
|
||||||
|
#![warn(clippy::suspicious)]
|
||||||
|
#![warn(clippy::style)]
|
||||||
|
#![warn(clippy::complexity)]
|
||||||
|
#![deny(clippy::perf)]
|
||||||
|
#![warn(clippy::pedantic)]
|
||||||
|
#![warn(clippy::nursery)]
|
||||||
|
//#![deny(clippy::unwrap_used)]
|
||||||
|
#![warn(clippy::expect_used)]
|
||||||
|
#![deny(missing_docs)]
|
||||||
|
#![allow(clippy::must_use_candidate)] // This gets annoying
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
#[macro_use]
|
||||||
|
pub(crate) mod logging {
|
||||||
|
macro_rules! trace {
|
||||||
|
($($x:tt)*) => (
|
||||||
|
#[cfg(feature = "logging")] {
|
||||||
|
log::trace!($($x)*)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! debug {
|
||||||
|
($($x:tt)*) => (
|
||||||
|
#[cfg(feature = "logging")] {
|
||||||
|
log::debug!($($x)*)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! info {
|
||||||
|
($($x:tt)*) => (
|
||||||
|
#[cfg(feature = "logging")] {
|
||||||
|
log::info!($($x)*)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! warn {
|
||||||
|
($($x:tt)*) => (
|
||||||
|
#[cfg(feature = "logging")] {
|
||||||
|
log::warn!($($x)*)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! error {
|
||||||
|
($($x:tt)*) => (
|
||||||
|
#[cfg(feature = "logging")] {
|
||||||
|
log::error!($($x)*)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Contains the state definitions of the WebSocket state machine
|
||||||
|
pub mod state;
|
||||||
|
|
||||||
|
/// Contains the common code for the client and server handshakes
|
||||||
|
pub mod handshake_common;
|
||||||
|
|
||||||
|
/// Contains the code for the client handshake
|
||||||
|
pub mod handshake_client;
|
||||||
|
|
||||||
|
/// Contains the code for the server handshake
|
||||||
|
pub mod handshake_server;
|
||||||
|
|
||||||
|
/// Contains the generic WebSocket stream implementation, that streams are upgraded into after completing the client or server handshake
|
||||||
|
pub mod stream;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "csprng-rand"))]
|
||||||
|
compile_error!("You need to select one CSPRNG implementation");
|
||||||
|
|
||||||
|
/// Pluggable CSPRNG implementation
|
||||||
|
#[cfg(feature = "csprng-rand")]
|
||||||
|
#[path = "random_rand.rs"]
|
||||||
|
pub mod random;
|
||||||
|
|
||||||
|
pub(crate) mod b64;
|
|
@ -0,0 +1,6 @@
|
||||||
|
use rand::Rng;
|
||||||
|
|
||||||
|
/// Generates a random WebSocket client key
|
||||||
|
pub fn websocket_client_key() -> [u8; 16] {
|
||||||
|
rand::thread_rng().gen()
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
/// The state that the websocket stream is currently in
|
||||||
|
pub enum StreamState {
|
||||||
|
/// The stream is connecting - the handshake has not finished
|
||||||
|
Connecting,
|
||||||
|
/// The stream is open - data can be sent
|
||||||
|
Open,
|
||||||
|
/// The stream is closing - no more data can be sent
|
||||||
|
Closing,
|
||||||
|
/// The stream is closed
|
||||||
|
Closed
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
/// An upgraded TCP stream, used to send WebSocket frames.
|
||||||
|
pub struct WebsocketStream {
|
||||||
|
enable_masking: bool
|
||||||
|
}
|
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue