From 4d8573c3fe6dd8a72c90385ff50398099a6f8662 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Fri, 18 Feb 2022 01:44:53 +0000 Subject: [PATCH] update all websocket examples to v4 --- Cargo.lock | 1119 ++++------------- Cargo.toml | 6 +- basics/todo/src/main.rs | 2 +- websockets/autobahn/Cargo.toml | 10 +- websockets/autobahn/README.md | 13 +- websockets/autobahn/config/fuzzingclient.json | 1 + websockets/autobahn/src/main.rs | 25 +- websockets/chat-broker/Cargo.toml | 17 +- websockets/chat-broker/src/main.rs | 38 +- websockets/chat-broker/src/server.rs | 4 +- websockets/chat-broker/src/session.rs | 15 +- websockets/chat-broker/static/index.html | 2 +- websockets/{tcp-chat => chat-tcp}/Cargo.toml | 2 +- websockets/{tcp-chat => chat-tcp}/README.md | 0 websockets/{tcp-chat => chat-tcp}/client.py | 0 .../{tcp-chat => chat-tcp}/src/client.rs | 0 .../{tcp-chat => chat-tcp}/src/codec.rs | 0 websockets/{tcp-chat => chat-tcp}/src/main.rs | 32 +- .../{tcp-chat => chat-tcp}/src/server.rs | 0 .../{tcp-chat => chat-tcp}/src/session.rs | 0 websockets/chat-tcp/static/index.html | 204 +++ websockets/chat/Cargo.toml | 16 +- websockets/chat/src/main.rs | 254 +--- websockets/chat/src/server.rs | 26 +- websockets/chat/src/session.rs | 206 +++ websockets/chat/static/index.html | 198 +++ websockets/chat/static/websocket.html | 112 -- websockets/echo/Cargo.toml | 27 + websockets/{websocket => echo}/README.md | 0 websockets/echo/src/client.rs | 72 ++ websockets/echo/src/main.rs | 42 + .../src/main.rs => echo/src/server.rs} | 92 +- websockets/echo/static/index.html | 171 +++ .../{websocket => echo}/websocket-client.py | 0 websockets/tcp-chat/static/websocket.html | 90 -- websockets/websocket/Cargo.toml | 23 - websockets/websocket/src/client.rs | 113 -- websockets/websocket/static/actixLogo.png | Bin 13131 -> 0 bytes websockets/websocket/static/favicon.ico | Bin 32527 -> 0 bytes websockets/websocket/static/index.html | 90 -- 40 files changed, 1340 insertions(+), 1682 deletions(-) rename websockets/{tcp-chat => chat-tcp}/Cargo.toml (91%) rename websockets/{tcp-chat => chat-tcp}/README.md (100%) rename websockets/{tcp-chat => chat-tcp}/client.py (100%) rename websockets/{tcp-chat => chat-tcp}/src/client.rs (100%) rename websockets/{tcp-chat => chat-tcp}/src/codec.rs (100%) rename websockets/{tcp-chat => chat-tcp}/src/main.rs (92%) rename websockets/{tcp-chat => chat-tcp}/src/server.rs (100%) rename websockets/{tcp-chat => chat-tcp}/src/session.rs (100%) create mode 100644 websockets/chat-tcp/static/index.html create mode 100644 websockets/chat/src/session.rs create mode 100644 websockets/chat/static/index.html delete mode 100644 websockets/chat/static/websocket.html create mode 100644 websockets/echo/Cargo.toml rename websockets/{websocket => echo}/README.md (100%) create mode 100644 websockets/echo/src/client.rs create mode 100644 websockets/echo/src/main.rs rename websockets/{websocket/src/main.rs => echo/src/server.rs} (65%) create mode 100644 websockets/echo/static/index.html rename websockets/{websocket => echo}/websocket-client.py (100%) delete mode 100644 websockets/tcp-chat/static/websocket.html delete mode 100644 websockets/websocket/Cargo.toml delete mode 100644 websockets/websocket/src/client.rs delete mode 100644 websockets/websocket/static/actixLogo.png delete mode 100644 websockets/websocket/static/favicon.ico delete mode 100644 websockets/websocket/static/index.html diff --git a/Cargo.lock b/Cargo.lock index e42cf93..8ff87ef 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -29,42 +29,17 @@ dependencies = [ "ureq", ] -[[package]] -name = "actix" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1be241f88f3b1e7e9a3fbe3b5a8a0f6915b5a1d7ee0d9a248d3376d01068cc60" -dependencies = [ - "actix-rt 1.1.1", - "actix_derive 0.5.0", - "bitflags", - "bytes 0.5.6", - "crossbeam-channel 0.4.4", - "derive_more", - "futures-channel", - "futures-util", - "log", - "once_cell", - "parking_lot 0.11.2", - "pin-project 0.4.29", - "smallvec", - "tokio 0.2.25", - "tokio-util 0.3.1", - "trust-dns-proto 0.19.7", - "trust-dns-resolver 0.19.7", -] - [[package]] name = "actix" version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3720d0064a0ce5c0de7bd93bdb0a6caebab2a9b5668746145d7b3b0c5da02914" dependencies = [ - "actix-rt 2.6.0", - "actix_derive 0.6.0", + "actix-rt", + "actix_derive", "bitflags", "bytes 1.1.0", - "crossbeam-channel 0.5.2", + "crossbeam-channel", "futures-core", "futures-sink", "futures-task", @@ -80,12 +55,12 @@ dependencies = [ [[package]] name = "actix-broker" -version = "0.3.1" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e075eaa04b5a9a1b36ce801fa338f7d477f6b5f28d5f8339a8c2baba5beec33" +checksum = "c030a37e7cae6b3026d445b15566423ffb92983dcaf7b225da7e6dba6c1fc6b0" dependencies = [ - "actix 0.10.0", - "fnv", + "actix", + "ahash", "log", ] @@ -93,28 +68,12 @@ dependencies = [ name = "actix-casbin-example" version = "1.0.0" dependencies = [ - "actix-web 4.0.0-rc.3", + "actix-web", "casbin", "loge", "tokio 1.17.0", ] -[[package]] -name = "actix-codec" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78d1833b3838dbe990df0f1f87baf640cf6146e898166afe401839d1b001e570" -dependencies = [ - "bitflags", - "bytes 0.5.6", - "futures-core", - "futures-sink", - "log", - "pin-project 0.4.29", - "tokio 0.2.25", - "tokio-util 0.3.1", -] - [[package]] name = "actix-codec" version = "0.4.2" @@ -149,34 +108,15 @@ dependencies = [ "tokio-util 0.7.0", ] -[[package]] -name = "actix-connect" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "177837a10863f15ba8d3ae3ec12fac1099099529ed20083a27fdfe247381d0dc" -dependencies = [ - "actix-codec 0.3.0", - "actix-rt 1.1.1", - "actix-service 1.0.6", - "actix-utils 2.0.0", - "derive_more", - "either", - "futures-util", - "http", - "log", - "trust-dns-proto 0.19.7", - "trust-dns-resolver 0.19.7", -] - [[package]] name = "actix-cors" version = "0.6.0-beta.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8debd30414af03e9411186aac95e0230b0bb1e91146f48015dfab5c049940223" dependencies = [ - "actix-service 2.0.2", - "actix-utils 3.0.0", - "actix-web 4.0.0-rc.3", + "actix-service", + "actix-utils", + "actix-web", "derive_more", "futures-util", "log", @@ -184,57 +124,16 @@ dependencies = [ "smallvec", ] -[[package]] -name = "actix-files" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8035f08f194893b199f4928b40425bd727c0257cf0fcf36f4ac214968d649ec7" -dependencies = [ - "actix-http 2.2.2", - "actix-service 1.0.6", - "actix-web 3.3.3", - "bitflags", - "bytes 0.5.6", - "derive_more", - "futures-core", - "futures-util", - "log", - "mime", - "mime_guess", - "percent-encoding", - "v_htmlescape 0.10.4", -] - -[[package]] -name = "actix-files" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d031468a7859f71674e5531bd05137e0ea5de05ec9a917314330b88c582e2e0a" -dependencies = [ - "actix-service 1.0.6", - "actix-web 3.3.3", - "bitflags", - "bytes 0.5.6", - "derive_more", - "futures-core", - "futures-util", - "log", - "mime", - "mime_guess", - "percent-encoding", - "v_htmlescape 0.11.0", -] - [[package]] name = "actix-files" version = "0.6.0-beta.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b49f1b48724a52605ba40b67ede24f5a6cbc246817f9278d280d393a28e8b0e" dependencies = [ - "actix-http 3.0.0-rc.3", - "actix-service 2.0.2", - "actix-utils 3.0.0", - "actix-web 4.0.0-rc.3", + "actix-http", + "actix-service", + "actix-utils", + "actix-web", "askama_escape", "bitflags", "bytes 1.1.0", @@ -248,53 +147,6 @@ dependencies = [ "pin-project-lite 0.2.8", ] -[[package]] -name = "actix-http" -version = "2.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2be6b66b62a794a8e6d366ac9415bb7d475ffd1e9f4671f38c1d8a8a5df950b3" -dependencies = [ - "actix-codec 0.3.0", - "actix-connect", - "actix-rt 1.1.1", - "actix-service 1.0.6", - "actix-threadpool", - "actix-utils 2.0.0", - "base64 0.13.0", - "bitflags", - "brotli", - "bytes 0.5.6", - "cookie 0.14.4", - "copyless", - "derive_more", - "either", - "encoding_rs", - "flate2", - "futures-channel", - "futures-core", - "futures-util", - "fxhash", - "h2 0.2.7", - "http", - "httparse", - "indexmap", - "itoa 0.4.8", - "language-tags 0.2.2", - "lazy_static", - "log", - "mime", - "percent-encoding", - "pin-project 1.0.10", - "rand 0.7.3", - "regex", - "serde 1.0.136", - "serde_json", - "serde_urlencoded", - "sha-1 0.9.8", - "slab", - "time 0.2.27", -] - [[package]] name = "actix-http" version = "3.0.0-rc.3" @@ -302,10 +154,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8b5ba038f3bb4aa29ad9bdd7eba09955ff04503263c497fc61a389d6412f4e8" dependencies = [ "actix-codec 0.5.0", - "actix-rt 2.6.0", - "actix-service 2.0.2", - "actix-tls 3.0.3", - "actix-utils 3.0.0", + "actix-rt", + "actix-service", + "actix-tls", + "actix-utils", "ahash", "base64 0.13.0", "bitflags", @@ -321,7 +173,7 @@ dependencies = [ "httparse", "httpdate 1.0.2", "itoa 1.0.1", - "language-tags 0.3.2", + "language-tags", "local-channel", "log", "mime", @@ -340,12 +192,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09594b8a7eaa15da5cfd0e25c779d4b1d6ded62d4a79904f1d91b0dcf97c78e7" dependencies = [ "actix-codec 0.5.0", - "actix-rt 2.6.0", - "actix-server 2.0.0", - "actix-service 2.0.2", - "actix-tls 3.0.3", - "actix-utils 3.0.0", - "awc 3.0.0-beta.21", + "actix-rt", + "actix-server", + "actix-service", + "actix-tls", + "actix-utils", + "awc", "base64 0.13.0", "bytes 1.1.0", "futures-core", @@ -365,25 +217,15 @@ version = "0.4.0-beta.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f084963856cf7990b1f21d6298626de4ae6178385cadece312e12c9f7a9f432" dependencies = [ - "actix-service 2.0.2", - "actix-utils 3.0.0", - "actix-web 4.0.0-rc.3", + "actix-service", + "actix-utils", + "actix-web", "futures-util", "serde 1.0.136", "serde_json", "time 0.3.7", ] -[[package]] -name = "actix-macros" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4ca8ce00b267af8ccebbd647de0d61e0674b6e61185cc7a592ff88772bed655" -dependencies = [ - "quote", - "syn", -] - [[package]] name = "actix-macros" version = "0.2.3" @@ -400,8 +242,8 @@ version = "0.4.0-beta.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c59b1f14a8b2bc14df9be544d173f5390da5b62d531e406fd0f0ce9b825fea5a" dependencies = [ - "actix-utils 3.0.0", - "actix-web 4.0.0-rc.3", + "actix-utils", + "actix-web", "bytes 1.1.0", "derive_more", "futures-core", @@ -418,7 +260,7 @@ version = "0.7.0-beta.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c142e6182e45703d7197aaeb1e0096b21e9abe9626836b50eab6899d2f753bac" dependencies = [ - "actix-web 4.0.0-rc.3", + "actix-web", "derive_more", "futures-util", "prost", @@ -430,12 +272,12 @@ version = "0.10.0-beta.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "184451f4945b4b9bf47417014658781ffad2d415bf5a886809cba9303a70b420" dependencies = [ - "actix 0.12.0", - "actix-rt 2.6.0", - "actix-service 2.0.2", + "actix", + "actix-rt", + "actix-service", "actix-session", - "actix-tls 3.0.3", - "actix-web 4.0.0-rc.3", + "actix-tls", + "actix-web", "backoff", "derive_more", "futures-core", @@ -450,19 +292,6 @@ dependencies = [ "tokio-util 0.6.9", ] -[[package]] -name = "actix-router" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ad299af73649e1fc893e333ccf86f377751eb95ff875d095131574c6f43452c" -dependencies = [ - "bytestring", - "http", - "log", - "regex", - "serde 1.0.136", -] - [[package]] name = "actix-router" version = "0.5.0-rc.3" @@ -477,61 +306,26 @@ dependencies = [ "serde 1.0.136", ] -[[package]] -name = "actix-rt" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "143fcc2912e0d1de2bcf4e2f720d2a60c28652ab4179685a1ee159e0fb3db227" -dependencies = [ - "actix-macros 0.1.3", - "actix-threadpool", - "copyless", - "futures-channel", - "futures-util", - "smallvec", - "tokio 0.2.25", -] - [[package]] name = "actix-rt" version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdf3f2183be1241ed4dd22611850b85d38de0b08a09f1f7bcccbd0809084b359" dependencies = [ - "actix-macros 0.2.3", + "actix-macros", "futures-core", "tokio 1.17.0", ] -[[package]] -name = "actix-server" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45407e6e672ca24784baa667c5d32ef109ccdd8d5e0b5ebb9ef8a67f4dfb708e" -dependencies = [ - "actix-codec 0.3.0", - "actix-rt 1.1.1", - "actix-service 1.0.6", - "actix-utils 2.0.0", - "futures-channel", - "futures-util", - "log", - "mio 0.6.23", - "mio-uds", - "num_cpus", - "slab", - "socket2 0.3.19", -] - [[package]] name = "actix-server" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9e7472ac180abb0a8e592b653744345983a7a14f44691c8394a799d0df4dbbf" dependencies = [ - "actix-rt 2.6.0", - "actix-service 2.0.2", - "actix-utils 3.0.0", + "actix-rt", + "actix-service", + "actix-utils", "futures-core", "futures-util", "log", @@ -541,16 +335,6 @@ dependencies = [ "tokio 1.17.0", ] -[[package]] -name = "actix-service" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0052435d581b5be835d11f4eb3bce417c8af18d87ddf8ace99f8e67e595882bb" -dependencies = [ - "futures-util", - "pin-project 0.4.29", -] - [[package]] name = "actix-service" version = "2.0.2" @@ -568,9 +352,9 @@ version = "0.5.0-beta.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c5562e924654d706f9fe3fe5a1644bb8ee311b58a1aa5d1451007efa35890975" dependencies = [ - "actix-service 2.0.2", - "actix-utils 3.0.0", - "actix-web 4.0.0-rc.3", + "actix-service", + "actix-utils", + "actix-web", "derive_more", "futures-util", "log", @@ -586,13 +370,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7919a113ef74b5d52218ea0bd310d0d1b0d2be822994716754e265f20734d601" dependencies = [ "actix-codec 0.5.0", - "actix-http 3.0.0-rc.3", + "actix-http", "actix-http-test", - "actix-rt 2.6.0", - "actix-service 2.0.2", - "actix-utils 3.0.0", - "actix-web 4.0.0-rc.3", - "awc 3.0.0-beta.21", + "actix-rt", + "actix-service", + "actix-utils", + "actix-web", + "awc", "futures-core", "futures-util", "log", @@ -602,47 +386,6 @@ dependencies = [ "tokio 1.17.0", ] -[[package]] -name = "actix-testing" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47239ca38799ab74ee6a8a94d1ce857014b2ac36f242f70f3f75a66f691e791c" -dependencies = [ - "actix-macros 0.1.3", - "actix-rt 1.1.1", - "actix-server 1.0.4", - "actix-service 1.0.6", - "log", - "socket2 0.3.19", -] - -[[package]] -name = "actix-threadpool" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d209f04d002854b9afd3743032a27b066158817965bf5d036824d19ac2cc0e30" -dependencies = [ - "derive_more", - "futures-channel", - "lazy_static", - "log", - "num_cpus", - "parking_lot 0.11.2", - "threadpool", -] - -[[package]] -name = "actix-tls" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24789b7d7361cf5503a504ebe1c10806896f61e96eca9a7350e23001aca715fb" -dependencies = [ - "actix-codec 0.3.0", - "actix-service 1.0.6", - "actix-utils 2.0.0", - "futures-util", -] - [[package]] name = "actix-tls" version = "3.0.3" @@ -650,9 +393,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fde0cf292f7cdc7f070803cb9a0d45c018441321a78b1042ffbbb81ec333297" dependencies = [ "actix-codec 0.5.0", - "actix-rt 2.6.0", - "actix-service 2.0.2", - "actix-utils 3.0.0", + "actix-rt", + "actix-service", + "actix-utils", "futures-core", "http", "log", @@ -664,26 +407,6 @@ dependencies = [ "webpki-roots 0.22.2", ] -[[package]] -name = "actix-utils" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e9022dec56632d1d7979e59af14f0597a28a830a9c1c7fec8b2327eb9f16b5a" -dependencies = [ - "actix-codec 0.3.0", - "actix-rt 1.1.1", - "actix-service 1.0.6", - "bitflags", - "bytes 0.5.6", - "either", - "futures-channel", - "futures-sink", - "futures-util", - "log", - "pin-project 0.4.29", - "slab", -] - [[package]] name = "actix-utils" version = "3.0.0" @@ -694,45 +417,6 @@ dependencies = [ "pin-project-lite 0.2.8", ] -[[package]] -name = "actix-web" -version = "3.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6534a126df581caf443ba2751cab42092c89b3f1d06a9d829b1e17edfe3e277" -dependencies = [ - "actix-codec 0.3.0", - "actix-http 2.2.2", - "actix-macros 0.1.3", - "actix-router 0.2.7", - "actix-rt 1.1.1", - "actix-server 1.0.4", - "actix-service 1.0.6", - "actix-testing", - "actix-threadpool", - "actix-tls 2.0.0", - "actix-utils 2.0.0", - "actix-web-codegen 0.4.0", - "awc 2.0.3", - "bytes 0.5.6", - "derive_more", - "encoding_rs", - "futures-channel", - "futures-core", - "futures-util", - "fxhash", - "log", - "mime", - "pin-project 1.0.10", - "regex", - "serde 1.0.136", - "serde_json", - "serde_urlencoded", - "socket2 0.3.19", - "time 0.2.27", - "tinyvec", - "url", -] - [[package]] name = "actix-web" version = "4.0.0-rc.3" @@ -740,15 +424,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83e3c85bc4116b69913b03f16cff8cade1212508fcd321847d9cfe3d3e41f991" dependencies = [ "actix-codec 0.4.2", - "actix-http 3.0.0-rc.3", - "actix-macros 0.2.3", - "actix-router 0.5.0-rc.3", - "actix-rt 2.6.0", - "actix-server 2.0.0", - "actix-service 2.0.2", - "actix-tls 3.0.3", - "actix-utils 3.0.0", - "actix-web-codegen 0.5.0-rc.2", + "actix-http", + "actix-macros", + "actix-router", + "actix-rt", + "actix-server", + "actix-service", + "actix-tls", + "actix-utils", + "actix-web-codegen", "ahash", "bytes 1.1.0", "cfg-if 1.0.0", @@ -758,7 +442,7 @@ dependencies = [ "futures-core", "futures-util", "itoa 1.0.1", - "language-tags 0.3.2", + "language-tags", "log", "mime", "once_cell", @@ -773,32 +457,16 @@ dependencies = [ "url", ] -[[package]] -name = "actix-web-actors" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f6edf3c2693e2a8c422800c87ee89a6a4eac7dd01109bc172a1093ce1f4f001" -dependencies = [ - "actix 0.10.0", - "actix-codec 0.3.0", - "actix-http 2.2.2", - "actix-web 3.3.3", - "bytes 0.5.6", - "futures-channel", - "futures-core", - "pin-project 0.4.29", -] - [[package]] name = "actix-web-actors" version = "4.0.0-beta.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95319210d3670dcdc918e670c076bd77f0b8ee557d84c157d8c408796bcd5eb9" dependencies = [ - "actix 0.12.0", + "actix", "actix-codec 0.5.0", - "actix-http 3.0.0-rc.3", - "actix-web 4.0.0-rc.3", + "actix-http", + "actix-web", "bytes 1.1.0", "bytestring", "futures-core", @@ -806,24 +474,13 @@ dependencies = [ "tokio 1.17.0", ] -[[package]] -name = "actix-web-codegen" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad26f77093333e0e7c6ffe54ebe3582d908a104e448723eec6d43d08b07143fb" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "actix-web-codegen" version = "0.5.0-rc.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d0976042e6ddc82c7d0dedd64d39959bc26d9bba098b2f6c32a73fbef784eaf" dependencies = [ - "actix-router 0.5.0-rc.3", + "actix-router", "proc-macro2", "quote", "syn", @@ -834,8 +491,8 @@ name = "actix-web-cors" version = "1.0.0" dependencies = [ "actix-cors", - "actix-web 4.0.0-rc.3", - "env_logger 0.9.0", + "actix-web", + "env_logger", "futures", "serde 1.0.136", "serde_json", @@ -847,11 +504,11 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2cd7a15518fb68eb657abc5b6b16c387f99b6db7f549f4884c72d05165139b8" dependencies = [ - "actix-files 0.6.0-beta.16", - "actix-http 3.0.0-rc.3", - "actix-service 2.0.2", - "actix-utils 3.0.0", - "actix-web 4.0.0-rc.3", + "actix-files", + "actix-http", + "actix-service", + "actix-utils", + "actix-web", "bytes 1.1.0", "csv", "derive_more", @@ -870,17 +527,6 @@ dependencies = [ "tokio 1.17.0", ] -[[package]] -name = "actix_derive" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b95aceadaf327f18f0df5962fedc1bde2f870566a0b9f65c89508a3b1f79334c" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "actix_derive" version = "0.6.0" @@ -896,10 +542,10 @@ dependencies = [ name = "actix_redis" version = "1.0.0" dependencies = [ - "actix 0.12.0", + "actix", "actix-redis", - "actix-web 4.0.0-rc.3", - "env_logger 0.9.0", + "actix-web", + "env_logger", "futures-util", "log", "redis-async", @@ -964,7 +610,7 @@ checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" dependencies = [ "getrandom 0.2.4", "once_cell", - "version_check 0.9.4", + "version_check", ] [[package]] @@ -1141,10 +787,10 @@ version = "3.0.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb7df7399ff10b17ced5104c1046152ec8a05f3c8facb3b671fbe72ffed0c3ad" dependencies = [ - "actix 0.12.0", - "actix-http 3.0.0-rc.3", - "actix-web 4.0.0-rc.3", - "actix-web-actors 4.0.0-beta.12", + "actix", + "actix-http", + "actix-web", + "actix-web-actors", "async-channel", "async-graphql", "futures-channel", @@ -1159,11 +805,11 @@ name = "async-graphql-demo" version = "1.0.0" dependencies = [ "actix-cors", - "actix-web 4.0.0-rc.3", + "actix-web", "actix-web-lab", "async-graphql", "async-graphql-actix-web", - "env_logger 0.9.0", + "env_logger", "log", "slab", ] @@ -1245,7 +891,7 @@ dependencies = [ name = "async_data_factory" version = "1.0.0" dependencies = [ - "actix-web 4.0.0-rc.3", + "actix-web", "num_cpus", "redis", "redis_tang", @@ -1255,8 +901,8 @@ dependencies = [ name = "async_db" version = "1.0.0" dependencies = [ - "actix-web 4.0.0-rc.3", - "env_logger 0.9.0", + "actix-web", + "env_logger", "futures-util", "log", "r2d2", @@ -1270,8 +916,8 @@ dependencies = [ name = "async_ex2" version = "1.0.0" dependencies = [ - "actix-web 4.0.0-rc.3", - "env_logger 0.9.0", + "actix-web", + "env_logger", "serde 1.0.136", "serde_json", ] @@ -1280,7 +926,7 @@ dependencies = [ name = "async_pg" version = "1.0.0" dependencies = [ - "actix-web 4.0.0-rc.3", + "actix-web", "config", "deadpool-postgres", "derive_more", @@ -1317,30 +963,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" -[[package]] -name = "awc" -version = "2.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b381e490e7b0cfc37ebc54079b0413d8093ef43d14a4e4747083f7fa47a9e691" -dependencies = [ - "actix-codec 0.3.0", - "actix-http 2.2.2", - "actix-rt 1.1.1", - "actix-service 1.0.6", - "base64 0.13.0", - "bytes 0.5.6", - "cfg-if 1.0.0", - "derive_more", - "futures-core", - "log", - "mime", - "percent-encoding", - "rand 0.7.3", - "serde 1.0.136", - "serde_json", - "serde_urlencoded", -] - [[package]] name = "awc" version = "3.0.0-beta.21" @@ -1348,11 +970,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d91ad5074d7d2d8be6ef74d4ecb79c0f4d5f58bdc7ec896d5fbaa9f2fb9bfa5e" dependencies = [ "actix-codec 0.5.0", - "actix-http 3.0.0-rc.3", - "actix-rt 2.6.0", - "actix-service 2.0.2", - "actix-tls 3.0.3", - "actix-utils 3.0.0", + "actix-http", + "actix-rt", + "actix-service", + "actix-tls", + "actix-utils", "ahash", "base64 0.13.0", "bytes 1.1.0", @@ -1380,9 +1002,9 @@ dependencies = [ name = "awc_https" version = "1.0.0" dependencies = [ - "actix-web 4.0.0-rc.3", - "awc 3.0.0-beta.21", - "env_logger 0.9.0", + "actix-web", + "awc", + "env_logger", "log", "mime", "rustls 0.20.3", @@ -1436,11 +1058,11 @@ checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" name = "basics" version = "1.0.0" dependencies = [ - "actix-files 0.6.0-beta.16", + "actix-files", "actix-session", - "actix-web 4.0.0-rc.3", + "actix-web", "async-stream", - "env_logger 0.9.0", + "env_logger", "log", ] @@ -1581,24 +1203,6 @@ dependencies = [ "serde 1.0.136", ] -[[package]] -name = "buf-min" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6ae7069aad07c7cdefe6a22a671f00650728bd2331a4cc62e1e5d0becdf9ca4" -dependencies = [ - "bytes 0.5.6", -] - -[[package]] -name = "buf-min" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "881e704e61d0fb41d7c6c9ae2bd790eb8c13dc974ae102fb98c788b4fdea4349" -dependencies = [ - "bytes 0.6.0", -] - [[package]] name = "buf-min" version = "0.6.1" @@ -1648,12 +1252,6 @@ version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38" -[[package]] -name = "bytes" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0dcbc35f504eb6fc275a6d20e4ebcda18cf50d40ba6fabff8c711fa16cb3b16" - [[package]] name = "bytes" version = "1.1.0" @@ -1886,7 +1484,7 @@ checksum = "03a5d7b21829bc7b4bf4754a978a241ae54ea55a40f92bb20216e54096f4b951" dependencies = [ "percent-encoding", "time 0.2.27", - "version_check 0.9.4", + "version_check", ] [[package]] @@ -1904,7 +1502,7 @@ dependencies = [ "sha2 0.10.2", "subtle", "time 0.3.7", - "version_check 0.9.4", + "version_check", ] [[package]] @@ -1912,8 +1510,8 @@ name = "cookie-auth" version = "1.0.0" dependencies = [ "actix-identity", - "actix-web 4.0.0-rc.3", - "env_logger 0.9.0", + "actix-web", + "env_logger", "rand 0.8.5", ] @@ -1922,8 +1520,8 @@ name = "cookie-session" version = "1.0.0" dependencies = [ "actix-session", - "actix-web 4.0.0-rc.3", - "env_logger 0.9.0", + "actix-web", + "env_logger", "log", ] @@ -1943,12 +1541,6 @@ dependencies = [ "url", ] -[[package]] -name = "copyless" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2df960f5d869b2dd8532793fde43eb5427cceb126c929747a26823ab0eeb536" - [[package]] name = "core-foundation" version = "0.9.3" @@ -1998,16 +1590,6 @@ dependencies = [ "cfg-if 1.0.0", ] -[[package]] -name = "crossbeam-channel" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b153fe7cbef478c567df0f972e02e6d736db11affe43dfc9c56a9374d1adfb87" -dependencies = [ - "crossbeam-utils 0.7.2", - "maybe-uninit", -] - [[package]] name = "crossbeam-channel" version = "0.5.2" @@ -2015,7 +1597,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e54ea8bc3fb1ee042f5aace6e3c6e025d3874866da222930f70ce62aceba0bfa" dependencies = [ "cfg-if 1.0.0", - "crossbeam-utils 0.8.7", + "crossbeam-utils", ] [[package]] @@ -2025,18 +1607,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dd435b205a4842da59efd07628f921c096bc1cc0a156835b4fa0bcb9a19bcce" dependencies = [ "cfg-if 1.0.0", - "crossbeam-utils 0.8.7", -] - -[[package]] -name = "crossbeam-utils" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" -dependencies = [ - "autocfg", - "cfg-if 0.1.10", - "lazy_static", + "crossbeam-utils", ] [[package]] @@ -2303,8 +1874,8 @@ checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" name = "docker_sample" version = "1.0.0" dependencies = [ - "actix-web 4.0.0-rc.3", - "env_logger 0.9.0", + "actix-web", + "env_logger", "log", ] @@ -2350,19 +1921,6 @@ dependencies = [ "syn", ] -[[package]] -name = "env_logger" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3" -dependencies = [ - "atty", - "humantime", - "log", - "regex", - "termcolor", -] - [[package]] name = "env_logger" version = "0.9.0" @@ -2380,9 +1938,9 @@ dependencies = [ name = "error_handling" version = "1.0.0" dependencies = [ - "actix-web 4.0.0-rc.3", + "actix-web", "derive_more", - "env_logger 0.9.0", + "env_logger", "rand 0.8.5", ] @@ -2489,7 +2047,7 @@ checksum = "0b279436a715a9de95dcd26b151db590a71961cc06e54918b24fe0dd5b7d3fc4" dependencies = [ "futures-core", "futures-sink", - "pin-project 1.0.10", + "pin-project", "spin 0.9.2", ] @@ -2518,7 +2076,7 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" name = "form-example" version = "1.0.0" dependencies = [ - "actix-web 4.0.0-rc.3", + "actix-web", "serde 1.0.136", ] @@ -2669,15 +2227,6 @@ dependencies = [ "slab", ] -[[package]] -name = "fxhash" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" -dependencies = [ - "byteorder", -] - [[package]] name = "generic-array" version = "0.12.4" @@ -2694,7 +2243,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803" dependencies = [ "typenum", - "version_check 0.9.4", + "version_check", ] [[package]] @@ -2860,8 +2409,8 @@ checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" name = "hello-world" version = "1.0.0" dependencies = [ - "actix-web 4.0.0-rc.3", - "env_logger 0.9.0", + "actix-web", + "env_logger", ] [[package]] @@ -2963,10 +2512,10 @@ dependencies = [ name = "http-proxy" version = "1.0.0" dependencies = [ - "actix-web 4.0.0-rc.3", - "awc 3.0.0-beta.21", + "actix-web", + "awc", "clap", - "env_logger 0.9.0", + "env_logger", "log", "url", ] @@ -3023,7 +2572,7 @@ dependencies = [ "httparse", "httpdate 0.3.2", "itoa 0.4.8", - "pin-project 1.0.10", + "pin-project", "socket2 0.3.19", "tokio 0.2.25", "tower-service", @@ -3110,7 +2659,7 @@ version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "713f1b139373f96a2e0ce3ac931cd01ee973c3c5dd7c40c0c2efe96ad2b6751d" dependencies = [ - "crossbeam-utils 0.8.7", + "crossbeam-utils", "globset", "lazy_static", "log", @@ -3236,8 +2785,8 @@ checksum = "078e285eafdfb6c4b434e0d31e8cfcb5115b651496faca5749b88fafd4f23bfd" name = "json-example" version = "1.0.0" dependencies = [ - "actix-web 4.0.0-rc.3", - "env_logger 0.9.0", + "actix-web", + "env_logger", "futures", "json", "serde 1.0.136", @@ -3248,9 +2797,9 @@ dependencies = [ name = "json-validation" version = "1.0.0" dependencies = [ - "actix-web 4.0.0-rc.3", - "awc 3.0.0-beta.21", - "env_logger 0.9.0", + "actix-web", + "awc", + "env_logger", "futures", "log", "serde 1.0.136", @@ -3263,7 +2812,7 @@ dependencies = [ name = "json_decode_error" version = "1.0.0" dependencies = [ - "actix-web 4.0.0-rc.3", + "actix-web", "serde 1.0.136", ] @@ -3271,7 +2820,7 @@ dependencies = [ name = "json_error" version = "1.0.0" dependencies = [ - "actix-web 4.0.0-rc.3", + "actix-web", "failure", "serde 1.0.136", "serde_json", @@ -3281,9 +2830,9 @@ dependencies = [ name = "jsonrpc-example" version = "1.0.0" dependencies = [ - "actix-web 4.0.0-rc.3", + "actix-web", "bytes 1.1.0", - "env_logger 0.9.0", + "env_logger", "futures-util", "log", "serde 1.0.136", @@ -3317,10 +2866,10 @@ name = "juniper-advanced" version = "1.0.0" dependencies = [ "actix-cors", - "actix-web 4.0.0-rc.3", + "actix-web", "actix-web-lab", "dotenv", - "env_logger 0.9.0", + "env_logger", "juniper", "log", "mysql", @@ -3336,9 +2885,9 @@ name = "juniper-example" version = "1.0.0" dependencies = [ "actix-cors", - "actix-web 4.0.0-rc.3", + "actix-web", "actix-web-lab", - "env_logger 0.9.0", + "env_logger", "juniper", "log", "serde 1.0.136", @@ -3367,12 +2916,6 @@ dependencies = [ "winapi-build", ] -[[package]] -name = "language-tags" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a" - [[package]] name = "language-tags" version = "0.3.2" @@ -3553,12 +3096,6 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" -[[package]] -name = "maybe-uninit" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" - [[package]] name = "md-5" version = "0.9.1" @@ -3589,18 +3126,18 @@ checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" name = "middleware-example" version = "1.0.0" dependencies = [ - "actix-web 4.0.0-rc.3", - "env_logger 0.9.0", + "actix-web", + "env_logger", "futures", - "pin-project 1.0.10", + "pin-project", ] [[package]] name = "middleware-ext-mut" version = "1.0.0" dependencies = [ - "actix-web 4.0.0-rc.3", - "env_logger 0.9.0", + "actix-web", + "env_logger", "log", ] @@ -3608,7 +3145,7 @@ dependencies = [ name = "middleware-http-to-https" version = "1.0.0" dependencies = [ - "actix-web 4.0.0-rc.3", + "actix-web", "futures", "rustls 0.20.3", "rustls-pemfile", @@ -3678,17 +3215,6 @@ dependencies = [ "winapi 0.3.9", ] -[[package]] -name = "mio-uds" -version = "0.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afcb699eb26d4332647cc848492bbc15eafb26f08d0304550d5aa1f612e066f0" -dependencies = [ - "iovec", - "libc", - "mio 0.6.23", -] - [[package]] name = "miow" version = "0.2.2" @@ -3714,7 +3240,7 @@ dependencies = [ name = "mongodb" version = "1.0.0" dependencies = [ - "actix-web 4.0.0-rc.3", + "actix-web", "futures-util", "mongodb 2.1.0", "serde 1.0.136", @@ -3759,11 +3285,11 @@ dependencies = [ "tokio 1.17.0", "tokio-rustls 0.22.0", "tokio-util 0.6.9", - "trust-dns-proto 0.20.4", - "trust-dns-resolver 0.20.4", + "trust-dns-proto", + "trust-dns-resolver", "typed-builder", "uuid", - "version_check 0.9.4", + "version_check", "webpki 0.21.4", "webpki-roots 0.21.1", ] @@ -3783,7 +3309,7 @@ dependencies = [ "memchr", "mime", "spin 0.9.2", - "version_check 0.9.4", + "version_check", ] [[package]] @@ -3791,7 +3317,7 @@ name = "multipart-example" version = "1.0.0" dependencies = [ "actix-multipart", - "actix-web 4.0.0-rc.3", + "actix-web", "futures-util", "sanitize-filename", "uuid", @@ -3802,7 +3328,7 @@ name = "multipart-s3" version = "1.0.0" dependencies = [ "actix-multipart", - "actix-web 4.0.0-rc.3", + "actix-web", "dotenv", "futures", "rusoto_core", @@ -3928,16 +3454,6 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" -[[package]] -name = "nom" -version = "4.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6" -dependencies = [ - "memchr", - "version_check 0.1.5", -] - [[package]] name = "nom" version = "5.1.2" @@ -3946,7 +3462,7 @@ checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af" dependencies = [ "lexical-core 0.7.6", "memchr", - "version_check 0.9.4", + "version_check", ] [[package]] @@ -3957,7 +3473,7 @@ checksum = "1b1d11e1ef389c76fe5b81bcaf2ea32cf88b62bc494e19f493d0b30e7a930109" dependencies = [ "memchr", "minimal-lexical", - "version_check 0.9.4", + "version_check", ] [[package]] @@ -4073,10 +3589,10 @@ name = "openssl-auto-le" version = "1.0.0" dependencies = [ "acme-micro", - "actix-files 0.6.0-beta.16", - "actix-web 4.0.0-rc.3", + "actix-files", + "actix-web", "anyhow", - "env_logger 0.9.0", + "env_logger", "futures-util", "log", "openssl", @@ -4087,8 +3603,8 @@ dependencies = [ name = "openssl-example" version = "1.0.0" dependencies = [ - "actix-web 4.0.0-rc.3", - "env_logger 0.9.0", + "actix-web", + "env_logger", "openssl", ] @@ -4328,33 +3844,13 @@ dependencies = [ "uncased", ] -[[package]] -name = "pin-project" -version = "0.4.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9615c18d31137579e9ff063499264ddc1278e7b1982757ebc111028c4d1dc909" -dependencies = [ - "pin-project-internal 0.4.29", -] - [[package]] name = "pin-project" version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "58ad3879ad3baf4e44784bc6a718a8698867bb991f8ce24d1bcbe2cfb4c3a75e" dependencies = [ - "pin-project-internal 1.0.10", -] - -[[package]] -name = "pin-project-internal" -version = "0.4.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "044964427019eed9d49d9d5bbce6047ef18f37100ea400912a9fa4a3523ab12a" -dependencies = [ - "proc-macro2", - "quote", - "syn", + "pin-project-internal", ] [[package]] @@ -4484,7 +3980,7 @@ dependencies = [ "proc-macro2", "quote", "syn", - "version_check 0.9.4", + "version_check", ] [[package]] @@ -4495,7 +3991,7 @@ checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ "proc-macro2", "quote", - "version_check 0.9.4", + "version_check", ] [[package]] @@ -4540,10 +4036,10 @@ dependencies = [ name = "protobuf-example" version = "1.0.0" dependencies = [ - "actix 0.12.0", + "actix", "actix-protobuf", - "actix-web 4.0.0-rc.3", - "env_logger 0.9.0", + "actix-web", + "env_logger", "log", "prost", "prost-derive", @@ -4745,8 +4241,8 @@ dependencies = [ "actix-redis", "actix-session", "actix-test", - "actix-web 4.0.0-rc.3", - "env_logger 0.9.0", + "actix-web", + "env_logger", "serde 1.0.136", "serde_json", "time 0.3.7", @@ -4951,8 +4447,8 @@ dependencies = [ name = "run-in-thread" version = "1.0.0" dependencies = [ - "actix-web 4.0.0-rc.3", - "env_logger 0.9.0", + "actix-web", + "env_logger", "log", ] @@ -5062,7 +4558,7 @@ dependencies = [ "base64 0.13.0", "blake2b_simd", "constant_time_eq", - "crossbeam-utils 0.8.7", + "crossbeam-utils", ] [[package]] @@ -5135,9 +4631,9 @@ dependencies = [ name = "rustls-client-cert" version = "1.0.0" dependencies = [ - "actix-tls 3.0.3", - "actix-web 4.0.0-rc.3", - "env_logger 0.9.0", + "actix-tls", + "actix-web", + "env_logger", "log", "rustls 0.20.3", "rustls-pemfile", @@ -5147,9 +4643,9 @@ dependencies = [ name = "rustls-example" version = "1.0.0" dependencies = [ - "actix-files 0.6.0-beta.16", - "actix-web 4.0.0-rc.3", - "env_logger 0.9.0", + "actix-files", + "actix-web", + "env_logger", "rustls 0.20.3", "rustls-pemfile", ] @@ -5184,7 +4680,7 @@ dependencies = [ "itoap", "ryu", "sailfish-macros", - "version_check 0.9.4", + "version_check", ] [[package]] @@ -5418,8 +4914,8 @@ dependencies = [ name = "server-sent-events" version = "1.0.0" dependencies = [ - "actix-web 4.0.0-rc.3", - "env_logger 0.9.0", + "actix-web", + "env_logger", "futures-util", "log", "parking_lot 0.12.0", @@ -5524,8 +5020,8 @@ checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" name = "shutdown-server" version = "1.0.0" dependencies = [ - "actix-web 4.0.0-rc.3", - "env_logger 0.9.0", + "actix-web", + "env_logger", "futures", "tokio 1.17.0", ] @@ -5544,12 +5040,12 @@ name = "simple-auth-server" version = "1.0.0" dependencies = [ "actix-identity", - "actix-web 4.0.0-rc.3", + "actix-web", "chrono", "derive_more", "diesel", "dotenv", - "env_logger 0.9.0", + "env_logger", "futures", "lazy_static", "r2d2", @@ -5679,9 +5175,9 @@ dependencies = [ "byteorder", "bytes 1.1.0", "crc", - "crossbeam-channel 0.5.2", + "crossbeam-channel", "crossbeam-queue", - "crossbeam-utils 0.8.7", + "crossbeam-utils", "either", "flume", "futures-channel", @@ -5753,15 +5249,15 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e113fb6f3de07a243d434a56ec6f186dfd51cb08448239fe7bcae73f87ff28ff" dependencies = [ - "version_check 0.9.4", + "version_check", ] [[package]] name = "state" version = "1.0.0" dependencies = [ - "actix-web 4.0.0-rc.3", - "env_logger 0.9.0", + "actix-web", + "env_logger", ] [[package]] @@ -5780,9 +5276,9 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" name = "static_index" version = "1.0.0" dependencies = [ - "actix-files 0.6.0-beta.16", - "actix-web 4.0.0-rc.3", - "env_logger 0.9.0", + "actix-files", + "actix-web", + "env_logger", ] [[package]] @@ -5935,17 +5431,17 @@ dependencies = [ name = "template-askama" version = "1.0.0" dependencies = [ - "actix-web 4.0.0-rc.3", + "actix-web", "askama", - "env_logger 0.9.0", + "env_logger", ] [[package]] name = "template-tera" version = "1.0.0" dependencies = [ - "actix-web 4.0.0-rc.3", - "env_logger 0.9.0", + "actix-web", + "env_logger", "tera", ] @@ -5953,8 +5449,8 @@ dependencies = [ name = "template-tinytemplate" version = "1.0.0" dependencies = [ - "actix-web 4.0.0-rc.3", - "env_logger 0.9.0", + "actix-web", + "env_logger", "serde_json", "tinytemplate", ] @@ -5963,7 +5459,7 @@ dependencies = [ name = "template_handlebars" version = "1.0.0" dependencies = [ - "actix-web 4.0.0-rc.3", + "actix-web", "handlebars", "serde_json", ] @@ -5972,9 +5468,9 @@ dependencies = [ name = "template_sailfish" version = "0.1.0" dependencies = [ - "actix-web 4.0.0-rc.3", + "actix-web", "actix-web-lab", - "env_logger 0.9.0", + "env_logger", "log", "sailfish", ] @@ -5983,9 +5479,9 @@ dependencies = [ name = "template_yarte" version = "1.0.0" dependencies = [ - "actix-web 4.0.0-rc.3", + "actix-web", "derive_more", - "env_logger 0.9.0", + "env_logger", "yarte", "yarte_helpers", ] @@ -6067,15 +5563,6 @@ dependencies = [ "once_cell", ] -[[package]] -name = "threadpool" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" -dependencies = [ - "num_cpus", -] - [[package]] name = "time" version = "0.1.43" @@ -6097,7 +5584,7 @@ dependencies = [ "standback", "stdweb", "time-macros 0.1.1", - "version_check 0.9.4", + "version_check", "winapi 0.3.9", ] @@ -6171,11 +5658,11 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" name = "todo" version = "1.0.0" dependencies = [ - "actix-files 0.6.0-beta.16", + "actix-files", "actix-session", - "actix-web 4.0.0-rc.3", + "actix-web", "dotenv", - "env_logger 0.9.0", + "env_logger", "futures-util", "log", "serde 1.0.136", @@ -6195,15 +5682,11 @@ dependencies = [ "futures-core", "iovec", "lazy_static", - "libc", "memchr", "mio 0.6.23", - "mio-uds", "num_cpus", "pin-project-lite 0.1.12", - "signal-hook-registry", "slab", - "winapi 0.3.9", ] [[package]] @@ -6353,7 +5836,6 @@ checksum = "be8242891f2b6cbef26a2d7e8605133c2c554cd35b3e4948ea892d6d68436499" dependencies = [ "bytes 0.5.6", "futures-core", - "futures-io", "futures-sink", "log", "pin-project-lite 0.1.12", @@ -6405,9 +5887,9 @@ checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" [[package]] name = "tracing" -version = "0.1.30" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d8d93354fe2a8e50d5953f5ae2e47a3fc2ef03292e7ea46e3cc38f549525fb9" +checksum = "f6c650a8ef0cd2dd93736f033d21cbd1224c5a967aa0c258d00fcf7dafef9b9f" dependencies = [ "cfg-if 1.0.0", "log", @@ -6430,30 +5912,10 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" dependencies = [ - "pin-project 1.0.10", + "pin-project", "tracing", ] -[[package]] -name = "trust-dns-proto" -version = "0.19.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cad71a0c0d68ab9941d2fb6e82f8fb2e86d9945b94e1661dd0aaea2b88215a9" -dependencies = [ - "async-trait", - "cfg-if 1.0.0", - "enum-as-inner", - "futures", - "idna", - "lazy_static", - "log", - "rand 0.7.3", - "smallvec", - "thiserror", - "tokio 0.2.25", - "url", -] - [[package]] name = "trust-dns-proto" version = "0.20.4" @@ -6479,25 +5941,6 @@ dependencies = [ "url", ] -[[package]] -name = "trust-dns-resolver" -version = "0.19.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "710f593b371175db53a26d0b38ed2978fafb9e9e8d3868b1acd753ea18df0ceb" -dependencies = [ - "cfg-if 0.1.10", - "futures", - "ipconfig", - "lazy_static", - "log", - "lru-cache", - "resolv-conf", - "smallvec", - "thiserror", - "tokio 0.2.25", - "trust-dns-proto 0.19.7", -] - [[package]] name = "trust-dns-resolver" version = "0.20.4" @@ -6515,7 +5958,7 @@ dependencies = [ "smallvec", "thiserror", "tokio 1.17.0", - "trust-dns-proto 0.20.4", + "trust-dns-proto", ] [[package]] @@ -6574,7 +6017,7 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5baeed7327e25054889b9bd4f975f32e5f4c5d434042d59ab6cd4142c0a76ed0" dependencies = [ - "version_check 0.9.4", + "version_check", ] [[package]] @@ -6639,7 +6082,7 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" dependencies = [ - "version_check 0.9.4", + "version_check", ] [[package]] @@ -6695,8 +6138,8 @@ dependencies = [ name = "unix-socket" version = "1.0.0" dependencies = [ - "actix-web 4.0.0-rc.3", - "env_logger 0.9.0", + "actix-web", + "env_logger", ] [[package]] @@ -6761,46 +6204,14 @@ dependencies = [ "serde 1.0.136", ] -[[package]] -name = "v_escape" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "039a44473286eb84e4e74f90165feff67c802dbeced7ee4c5b00d719b0d0475e" -dependencies = [ - "buf-min 0.1.1", - "v_escape_derive 0.8.5", -] - -[[package]] -name = "v_escape" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccca9e73c678b882900cbaec16dae4d3662ace5a17774ac45af04e0f3988fafa" -dependencies = [ - "buf-min 0.2.0", - "v_escape_derive 0.8.5", -] - [[package]] name = "v_escape" version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79d297315e8ca0b98255614f409699ea189e5929e820f07f69afcebf96c41f9b" dependencies = [ - "buf-min 0.6.1", - "v_escape_derive 0.9.1", -] - -[[package]] -name = "v_escape_derive" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29769400af8b264944b851c961a4a6930e76604f59b1fcd51246bab6a296c8c" -dependencies = [ - "nom 4.2.3", - "proc-macro2", - "quote", - "syn", + "buf-min", + "v_escape_derive", ] [[package]] @@ -6822,26 +6233,6 @@ dependencies = [ "syn", ] -[[package]] -name = "v_htmlescape" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11d7c2a33ed7cf0dc1b42bcf39e01b6512f9df08f09e1cd8a49d9dc49a6a9482" -dependencies = [ - "cfg-if 1.0.0", - "v_escape 0.13.2", -] - -[[package]] -name = "v_htmlescape" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db00c903248abee8499af60bf20d242e7882335bbbffd2614915184cbb207402" -dependencies = [ - "cfg-if 1.0.0", - "v_escape 0.14.1", -] - [[package]] name = "v_htmlescape" version = "0.14.1" @@ -6849,7 +6240,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04b32732bcd549ad15fcb01ee63ad03dd6a0289e9ba72b8164707d1f9fa80478" dependencies = [ "cfg-if 1.0.0", - "v_escape 0.18.0", + "v_escape", ] [[package]] @@ -6900,12 +6291,6 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" -[[package]] -name = "version_check" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" - [[package]] name = "version_check" version = "0.9.4" @@ -7071,56 +6456,56 @@ dependencies = [ name = "websocket" version = "1.0.0" dependencies = [ - "actix 0.10.0", - "actix-codec 0.3.0", - "actix-files 0.3.0", - "actix-web 3.3.3", - "actix-web-actors 3.0.0", - "awc 2.0.3", - "bytes 0.5.6", - "env_logger 0.8.4", + "actix", + "actix-codec 0.5.0", + "actix-files", + "actix-rt", + "actix-web", + "actix-web-actors", + "awc", + "env_logger", "futures", + "log", + "tokio 1.17.0", + "tokio-stream", ] [[package]] name = "websocket-autobahn" version = "1.0.0" dependencies = [ - "actix 0.10.0", - "actix-web 3.3.3", - "actix-web-actors 3.0.0", - "env_logger 0.8.4", + "actix", + "actix-web", + "actix-web-actors", + "env_logger", + "log", ] [[package]] name = "websocket-chat-broker" version = "1.0.0" dependencies = [ - "actix 0.10.0", + "actix", "actix-broker", - "actix-files 0.4.1", - "actix-rt 1.1.1", - "actix-web 3.3.3", - "actix-web-actors 3.0.0", - "env_logger 0.8.4", - "futures", + "actix-files", + "actix-web", + "actix-web-actors", + "env_logger", "log", - "rand 0.7.3", + "rand 0.8.5", ] [[package]] name = "websocket-example" version = "1.0.0" dependencies = [ - "actix 0.10.0", - "actix-files 0.3.0", - "actix-web 3.3.3", - "actix-web-actors 3.0.0", - "byteorder", - "bytes 0.5.6", - "env_logger 0.8.4", - "futures", - "rand 0.7.3", + "actix", + "actix-files", + "actix-web", + "actix-web-actors", + "env_logger", + "log", + "rand 0.8.5", "serde 1.0.136", "serde_json", ] @@ -7129,14 +6514,14 @@ dependencies = [ name = "websocket-tcp-example" version = "1.0.0" dependencies = [ - "actix 0.12.0", + "actix", "actix-codec 0.4.2", - "actix-files 0.6.0-beta.16", - "actix-web 4.0.0-rc.3", - "actix-web-actors 4.0.0-beta.12", + "actix-files", + "actix-web", + "actix-web-actors", "byteorder", "bytes 1.1.0", - "env_logger 0.9.0", + "env_logger", "futures", "log", "rand 0.8.5", @@ -7297,7 +6682,7 @@ version = "0.15.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c716c25f8cee3c289a749a10255f2b8eac52f8ac7279242f99eeb25acf2b51ce" dependencies = [ - "buf-min 0.6.1", + "buf-min", "yarte_derive", "yarte_helpers", ] @@ -7352,7 +6737,7 @@ version = "0.15.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c752e264ef064fb624c5d85e5f174fe130d273e9221d47daaad091281067b212" dependencies = [ - "buf-min 0.6.1", + "buf-min", "dtoa", "itoa 0.4.8", "prettyplease", @@ -7360,7 +6745,7 @@ dependencies = [ "serde 1.0.136", "syn", "toml", - "v_htmlescape 0.14.1", + "v_htmlescape", ] [[package]] @@ -7374,7 +6759,7 @@ dependencies = [ "quote", "syn", "v_eval", - "v_htmlescape 0.14.1", + "v_htmlescape", "yarte_helpers", "yarte_parser", ] diff --git a/Cargo.toml b/Cargo.toml index 5b33487..3977d8f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -53,8 +53,8 @@ members = [ "template_engines/yarte", "template_engines/sailfish", "websockets/autobahn", - "websockets/chat-broker", "websockets/chat", - "websockets/tcp-chat", - "websockets/websocket", + "websockets/chat-broker", + "websockets/chat-tcp", + "websockets/echo", ] diff --git a/basics/todo/src/main.rs b/basics/todo/src/main.rs index fd8857b..a5b6348 100644 --- a/basics/todo/src/main.rs +++ b/basics/todo/src/main.rs @@ -55,7 +55,7 @@ async fn main() -> io::Result<()> { .service(web::resource("/").route(web::get().to(api::index))) .service(web::resource("/todo").route(web::post().to(api::create))) .service(web::resource("/todo/{id}").route(web::post().to(api::update))) - .service(Files::new("/static", "./static/")) + .service(Files::new("/static", "./static")) }) .bind(("127.0.0.1", 8080))? .workers(2) diff --git a/websockets/autobahn/Cargo.toml b/websockets/autobahn/Cargo.toml index b4982a0..4ad5e7e 100644 --- a/websockets/autobahn/Cargo.toml +++ b/websockets/autobahn/Cargo.toml @@ -8,7 +8,9 @@ name = "websocket-autobahn-server" path = "src/main.rs" [dependencies] -actix = "0.10" -actix-web = "3" -actix-web-actors = "3" -env_logger = "0.8" +actix = "0.12" +actix-web = "4.0.0-rc.3" +actix-web-actors = "4.0.0-beta.12" + +env_logger = "0.9" +log = "0.4" diff --git a/websockets/autobahn/README.md b/websockets/autobahn/README.md index b7dc9bd..b991fdf 100644 --- a/websockets/autobahn/README.md +++ b/websockets/autobahn/README.md @@ -1,22 +1,21 @@ -# websocket +# WebSocket Autobahn Test Server -Websocket server for autobahn suite testing. +WebSocket server for the [Autobahn WebSocket protocol testsuite](https://github.com/crossbario/autobahn-testsuite). ## Usage -### server +### Server ```bash cd websockets/autobahn -cargo run --bin websocket-autobahn-server +cargo run ``` ### Running Autobahn Test Suite -Running the autobahn test suite is easiest using the docker image -as explained on the [autobahn-testsuite repo](https://github.com/crossbario/autobahn-testsuite#using-the-testsuite-docker-image). +Running the autobahn test suite is easiest using the docker image as explained on the [autobahn test suite repo](https://github.com/crossbario/autobahn-testsuite#using-the-testsuite-docker-image). -First, start a server (see above). Then, run the test suite in fuzzingclient mode: +After starting the server, in the same directory, run the test suite in "fuzzing client" mode: ```bash docker run -it --rm \ diff --git a/websockets/autobahn/config/fuzzingclient.json b/websockets/autobahn/config/fuzzingclient.json index d4b765f..cd6a8ba 100644 --- a/websockets/autobahn/config/fuzzingclient.json +++ b/websockets/autobahn/config/fuzzingclient.json @@ -8,6 +8,7 @@ ], "cases": ["*"], "exclude-cases": [ + "9.*", "12.*", "13.*" ], diff --git a/websockets/autobahn/src/main.rs b/websockets/autobahn/src/main.rs index 5ae97b8..8919c81 100644 --- a/websockets/autobahn/src/main.rs +++ b/websockets/autobahn/src/main.rs @@ -3,18 +3,17 @@ use actix_web::{middleware, web, App, Error, HttpRequest, HttpResponse, HttpServ use actix_web_actors::ws; async fn ws_index(r: HttpRequest, stream: web::Payload) -> Result { - ws::start(WebSocket::new(), &r, stream) + ws::start(AutobahnWebSocket::default(), &r, stream) } -struct WebSocket {} +#[derive(Debug, Clone, Default)] +struct AutobahnWebSocket; -impl Actor for WebSocket { +impl Actor for AutobahnWebSocket { type Context = ws::WebsocketContext; - - fn started(&mut self, _ctx: &mut Self::Context) {} } -impl StreamHandler> for WebSocket { +impl StreamHandler> for AutobahnWebSocket { fn handle( &mut self, msg: Result, @@ -37,23 +36,19 @@ impl StreamHandler> for WebSocket { } } -impl WebSocket { - fn new() -> Self { - Self {} - } -} - #[actix_web::main] async fn main() -> std::io::Result<()> { - std::env::set_var("RUST_LOG", "actix_server=info,actix_web=info"); - env_logger::init(); + env_logger::init_from_env(env_logger::Env::new().default_filter_or("info")); + + log::info!("starting HTTP server at http://localhost:9001"); HttpServer::new(|| { App::new() .wrap(middleware::Logger::default()) .service(web::resource("/").route(web::get().to(ws_index))) }) - .bind("127.0.0.1:9001")? + .workers(2) + .bind(("127.0.0.1", 9001))? .run() .await } diff --git a/websockets/chat-broker/Cargo.toml b/websockets/chat-broker/Cargo.toml index 9b37d11..6d2d7a5 100644 --- a/websockets/chat-broker/Cargo.toml +++ b/websockets/chat-broker/Cargo.toml @@ -8,13 +8,12 @@ name = "server" path = "src/main.rs" [dependencies] -actix = "0.10" -actix-broker = "0.3.1" -actix-files = "0.4" -actix-rt = "1" -actix-web = "3" -actix-web-actors = "3" -env_logger = "0.8" -futures = "0.3" +actix = "0.12" +actix-broker = "0.4" +actix-files = "0.6.0-beta.16" +actix-web = "4.0.0-rc.3" +actix-web-actors = "4.0.0-beta.12" + +env_logger = "0.9" log = "0.4" -rand = "0.7" +rand = "0.8" diff --git a/websockets/chat-broker/src/main.rs b/websockets/chat-broker/src/main.rs index 049363a..13f9969 100644 --- a/websockets/chat-broker/src/main.rs +++ b/websockets/chat-broker/src/main.rs @@ -1,7 +1,7 @@ -use log::info; - -use actix_files::Files; -use actix_web::{web, App, Error, HttpRequest, HttpResponse, HttpServer}; +use actix_files::{Files, NamedFile}; +use actix_web::{ + middleware::Logger, web, App, Error, HttpRequest, HttpServer, Responder, +}; use actix_web_actors::ws; mod message; @@ -10,28 +10,32 @@ mod session; use session::WsChatSession; -async fn chat_route( +async fn index() -> impl Responder { + NamedFile::open_async("./static/index.html").await.unwrap() +} + +async fn chat_ws( req: HttpRequest, stream: web::Payload, -) -> Result { +) -> Result { ws::start(WsChatSession::default(), &req, stream) } #[actix_web::main] async fn main() -> std::io::Result<()> { - env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")) - .init(); + env_logger::init_from_env(env_logger::Env::new().default_filter_or("info")); - let addr = ("127.0.0.1", 8080); + log::info!("starting HTTP server at http://localhost:8080"); - let srv = HttpServer::new(move || { + HttpServer::new(move || { App::new() - .service(web::resource("/ws/").to(chat_route)) - .service(Files::new("/", "./static/").index_file("index.html")) + .service(web::resource("/").to(index)) + .service(web::resource("/ws").to(chat_ws)) + .service(Files::new("/static", "./static")) + .wrap(Logger::default()) }) - .bind(&addr)?; - - info!("Starting http server: {}", &addr); - - srv.run().await + .workers(2) + .bind(("127.0.0.1", 8080))? + .run() + .await } diff --git a/websockets/chat-broker/src/server.rs b/websockets/chat-broker/src/server.rs index aa02e3b..a2c3327 100644 --- a/websockets/chat-broker/src/server.rs +++ b/websockets/chat-broker/src/server.rs @@ -1,8 +1,8 @@ +use std::collections::HashMap; + use actix::prelude::*; use actix_broker::BrokerSubscribe; -use std::collections::HashMap; - use crate::message::{ChatMessage, JoinRoom, LeaveRoom, ListRooms, SendMessage}; type Client = Recipient; diff --git a/websockets/chat-broker/src/session.rs b/websockets/chat-broker/src/session.rs index a502954..5a22323 100644 --- a/websockets/chat-broker/src/session.rs +++ b/websockets/chat-broker/src/session.rs @@ -1,12 +1,11 @@ -use log::{debug, info}; - -use actix::fut; -use actix::prelude::*; +use actix::{fut, prelude::*}; use actix_broker::BrokerIssue; use actix_web_actors::ws; -use crate::message::{ChatMessage, JoinRoom, LeaveRoom, ListRooms, SendMessage}; -use crate::server::WsChatServer; +use crate::{ + message::{ChatMessage, JoinRoom, LeaveRoom, ListRooms, SendMessage}, + server::WsChatServer, +}; #[derive(Default)] pub struct WsChatSession { @@ -84,7 +83,7 @@ impl Actor for WsChatSession { } fn stopped(&mut self, _ctx: &mut Self::Context) { - info!( + log::info!( "WsChatSession closed for {}({}) in room {}", self.name.clone().unwrap_or_else(|| "anon".to_string()), self.id, @@ -115,7 +114,7 @@ impl StreamHandler> for WsChatSession { Ok(msg) => msg, }; - debug!("WEBSOCKET MESSAGE: {:?}", msg); + log::debug!("WEBSOCKET MESSAGE: {:?}", msg); match msg { ws::Message::Text(text) => { diff --git a/websockets/chat-broker/static/index.html b/websockets/chat-broker/static/index.html index 85afb17..61c74cd 100644 --- a/websockets/chat-broker/static/index.html +++ b/websockets/chat-broker/static/index.html @@ -130,7 +130,7 @@ const { location } = window const proto = location.protocol.startsWith('https') ? 'wss' : 'ws' - const wsUri = `${proto}://${location.host}/ws/` + const wsUri = `${proto}://${location.host}/ws` log('Connecting...') socket = new WebSocket(wsUri) diff --git a/websockets/tcp-chat/Cargo.toml b/websockets/chat-tcp/Cargo.toml similarity index 91% rename from websockets/tcp-chat/Cargo.toml rename to websockets/chat-tcp/Cargo.toml index 4ae3556..be80c5c 100644 --- a/websockets/tcp-chat/Cargo.toml +++ b/websockets/chat-tcp/Cargo.toml @@ -26,6 +26,6 @@ log = "0.4" rand = "0.8" serde = { version = "1", features = ["derive"] } serde_json = "1" -tokio = { version = "1", features = ["full"] } +tokio = { version = "1.13.1", features = ["full"] } tokio-util = { version = "0.6", features = ["codec"] } tokio-stream = "0.1.8" diff --git a/websockets/tcp-chat/README.md b/websockets/chat-tcp/README.md similarity index 100% rename from websockets/tcp-chat/README.md rename to websockets/chat-tcp/README.md diff --git a/websockets/tcp-chat/client.py b/websockets/chat-tcp/client.py similarity index 100% rename from websockets/tcp-chat/client.py rename to websockets/chat-tcp/client.py diff --git a/websockets/tcp-chat/src/client.rs b/websockets/chat-tcp/src/client.rs similarity index 100% rename from websockets/tcp-chat/src/client.rs rename to websockets/chat-tcp/src/client.rs diff --git a/websockets/tcp-chat/src/codec.rs b/websockets/chat-tcp/src/codec.rs similarity index 100% rename from websockets/tcp-chat/src/codec.rs rename to websockets/chat-tcp/src/codec.rs diff --git a/websockets/tcp-chat/src/main.rs b/websockets/chat-tcp/src/main.rs similarity index 92% rename from websockets/tcp-chat/src/main.rs rename to websockets/chat-tcp/src/main.rs index 2a36bb9..166e35b 100644 --- a/websockets/tcp-chat/src/main.rs +++ b/websockets/chat-tcp/src/main.rs @@ -1,8 +1,10 @@ use std::time::{Duration, Instant}; -use actix::*; -use actix_files as fs; -use actix_web::{http::header, web, App, Error, HttpRequest, HttpResponse, HttpServer}; +use actix::prelude::*; +use actix_files::NamedFile; +use actix_web::{ + middleware::Logger, web, App, Error, HttpRequest, HttpServer, Responder, +}; use actix_web_actors::ws; mod codec; @@ -11,15 +13,20 @@ mod session; /// How often heartbeat pings are sent const HEARTBEAT_INTERVAL: Duration = Duration::from_secs(5); + /// How long before lack of client response causes a timeout const CLIENT_TIMEOUT: Duration = Duration::from_secs(10); +async fn index() -> impl Responder { + NamedFile::open_async("./static/index.html").await.unwrap() +} + /// Entry point for our route async fn chat_route( req: HttpRequest, stream: web::Payload, srv: web::Data>, -) -> Result { +) -> Result { ws::start( WsChatSession { id: 0, @@ -109,7 +116,7 @@ impl StreamHandler> for WsChatSession { Ok(msg) => msg, }; - println!("WEBSOCKET MESSAGE: {:?}", msg); + log::debug!("WEBSOCKET MESSAGE: {:?}", msg); match msg { ws::Message::Ping(msg) => { self.hb = Instant::now(); @@ -235,19 +242,14 @@ async fn main() -> std::io::Result<()> { HttpServer::new(move || { App::new() .app_data(web::Data::new(server.clone())) - // redirect to websocket.html - .service(web::resource("/").route(web::get().to(|| async { - HttpResponse::Found() - .insert_header((header::LOCATION, "/static/websocket.html")) - .finish() - }))) + // WebSocket UI HTML file + .service(web::resource("/").to(index)) // websocket - .service(web::resource("/ws/").to(chat_route)) - // static resources - .service(fs::Files::new("/static/", "static/")) + .service(web::resource("/ws").to(chat_route)) + .wrap(Logger::default()) }) .bind(("127.0.0.1", 8080))? - .workers(1) + .workers(2) .run() .await } diff --git a/websockets/tcp-chat/src/server.rs b/websockets/chat-tcp/src/server.rs similarity index 100% rename from websockets/tcp-chat/src/server.rs rename to websockets/chat-tcp/src/server.rs diff --git a/websockets/tcp-chat/src/session.rs b/websockets/chat-tcp/src/session.rs similarity index 100% rename from websockets/tcp-chat/src/session.rs rename to websockets/chat-tcp/src/session.rs diff --git a/websockets/chat-tcp/static/index.html b/websockets/chat-tcp/static/index.html new file mode 100644 index 0000000..61c74cd --- /dev/null +++ b/websockets/chat-tcp/static/index.html @@ -0,0 +1,204 @@ + + + + + Websocket Chat Broker + + + + +

Chat!

+ +
+ + Status: + disconnected +
+ +
+ +
+ + +
+ +
+ +
+

Commands

+ + + + + + + + + + + + + + + + + +
+ /list + + list all available rooms +
+ /join name + + join room, if room does not exist, create new one +
+ /name name + + set session name +
+ some message + + just string, send message to all peers in same room +
+
+ + + + diff --git a/websockets/chat/Cargo.toml b/websockets/chat/Cargo.toml index 1e533e0..eea1a5c 100644 --- a/websockets/chat/Cargo.toml +++ b/websockets/chat/Cargo.toml @@ -8,15 +8,13 @@ name = "websocket-chat-server" path = "src/main.rs" [dependencies] -actix = "0.10" -actix-web = "3" -actix-web-actors = "3" -actix-files = "0.3" +actix = "0.12" +actix-files = "0.6.0-beta.16" +actix-web = "4.0.0-rc.3" +actix-web-actors = "4.0.0-beta.12" -rand = "0.7" -bytes = "0.5" -byteorder = "1.3" -futures = "0.3" -env_logger = "0.8" +env_logger = "0.9" +log = "0.4" +rand = "0.8" serde = "1" serde_json = "1" diff --git a/websockets/chat/src/main.rs b/websockets/chat/src/main.rs index 4bbd76a..e7f4b2e 100644 --- a/websockets/chat/src/main.rs +++ b/websockets/chat/src/main.rs @@ -1,20 +1,25 @@ -use std::sync::{ - atomic::{AtomicUsize, Ordering}, - Arc, +use std::{ + sync::{ + atomic::{AtomicUsize, Ordering}, + Arc, + }, + time::Instant, }; -use std::time::{Duration, Instant}; use actix::*; -use actix_files as fs; -use actix_web::{web, App, Error, HttpRequest, HttpResponse, HttpServer, Responder}; +use actix_files::{Files, NamedFile}; +use actix_web::{ + middleware::Logger, web, App, Error, HttpRequest, HttpResponse, HttpServer, + Responder, +}; use actix_web_actors::ws; mod server; +mod session; -/// How often heartbeat pings are sent -const HEARTBEAT_INTERVAL: Duration = Duration::from_secs(5); -/// How long before lack of client response causes a timeout -const CLIENT_TIMEOUT: Duration = Duration::from_secs(10); +async fn index() -> impl Responder { + NamedFile::open_async("./static/index.html").await.unwrap() +} /// Entry point for our websocket route async fn chat_route( @@ -23,7 +28,7 @@ async fn chat_route( srv: web::Data>, ) -> Result { ws::start( - WsChatSession { + session::WsChatSession { id: 0, hb: Instant::now(), room: "Main".to_owned(), @@ -35,229 +40,36 @@ async fn chat_route( ) } -/// Displays and affects state -async fn get_count(count: web::Data>) -> impl Responder { - let current_count = count.fetch_add(1, Ordering::SeqCst); +/// Displays state +async fn get_count(count: web::Data) -> impl Responder { + let current_count = count.load(Ordering::SeqCst); format!("Visitors: {}", current_count) } -struct WsChatSession { - /// unique session id - id: usize, - /// Client must send ping at least once per 10 seconds (CLIENT_TIMEOUT), - /// otherwise we drop connection. - hb: Instant, - /// joined room - room: String, - /// peer name - name: Option, - /// Chat server - addr: Addr, -} - -impl Actor for WsChatSession { - type Context = ws::WebsocketContext; - - /// Method is called on actor start. - /// We register ws session with ChatServer - fn started(&mut self, ctx: &mut Self::Context) { - // we'll start heartbeat process on session start. - self.hb(ctx); - - // register self in chat server. `AsyncContext::wait` register - // future within context, but context waits until this future resolves - // before processing any other events. - // HttpContext::state() is instance of WsChatSessionState, state is shared - // across all routes within application - let addr = ctx.address(); - self.addr - .send(server::Connect { - addr: addr.recipient(), - }) - .into_actor(self) - .then(|res, act, ctx| { - match res { - Ok(res) => act.id = res, - // something is wrong with chat server - _ => ctx.stop(), - } - fut::ready(()) - }) - .wait(ctx); - } - - fn stopping(&mut self, _: &mut Self::Context) -> Running { - // notify chat server - self.addr.do_send(server::Disconnect { id: self.id }); - Running::Stop - } -} - -/// Handle messages from chat server, we simply send it to peer websocket -impl Handler for WsChatSession { - type Result = (); - - fn handle(&mut self, msg: server::Message, ctx: &mut Self::Context) { - ctx.text(msg.0); - } -} - -/// WebSocket message handler -impl StreamHandler> for WsChatSession { - fn handle( - &mut self, - msg: Result, - ctx: &mut Self::Context, - ) { - let msg = match msg { - Err(_) => { - ctx.stop(); - return; - } - Ok(msg) => msg, - }; - - println!("WEBSOCKET MESSAGE: {:?}", msg); - match msg { - ws::Message::Ping(msg) => { - self.hb = Instant::now(); - ctx.pong(&msg); - } - ws::Message::Pong(_) => { - self.hb = Instant::now(); - } - ws::Message::Text(text) => { - let m = text.trim(); - // we check for /sss type of messages - if m.starts_with('/') { - let v: Vec<&str> = m.splitn(2, ' ').collect(); - match v[0] { - "/list" => { - // Send ListRooms message to chat server and wait for - // response - println!("List rooms"); - self.addr - .send(server::ListRooms) - .into_actor(self) - .then(|res, _, ctx| { - match res { - Ok(rooms) => { - for room in rooms { - ctx.text(room); - } - } - _ => println!("Something is wrong"), - } - fut::ready(()) - }) - .wait(ctx) - // .wait(ctx) pauses all events in context, - // so actor wont receive any new messages until it get list - // of rooms back - } - "/join" => { - if v.len() == 2 { - self.room = v[1].to_owned(); - self.addr.do_send(server::Join { - id: self.id, - name: self.room.clone(), - }); - - ctx.text("joined"); - } else { - ctx.text("!!! room name is required"); - } - } - "/name" => { - if v.len() == 2 { - self.name = Some(v[1].to_owned()); - } else { - ctx.text("!!! name is required"); - } - } - _ => ctx.text(format!("!!! unknown command: {:?}", m)), - } - } else { - let msg = if let Some(ref name) = self.name { - format!("{}: {}", name, m) - } else { - m.to_owned() - }; - // send message to chat server - self.addr.do_send(server::ClientMessage { - id: self.id, - msg, - room: self.room.clone(), - }) - } - } - ws::Message::Binary(_) => println!("Unexpected binary"), - ws::Message::Close(reason) => { - ctx.close(reason); - ctx.stop(); - } - ws::Message::Continuation(_) => { - ctx.stop(); - } - ws::Message::Nop => (), - } - } -} - -impl WsChatSession { - /// helper method that sends ping to client every second. - /// - /// also this method checks heartbeats from client - fn hb(&self, ctx: &mut ws::WebsocketContext) { - ctx.run_interval(HEARTBEAT_INTERVAL, |act, ctx| { - // check client heartbeats - if Instant::now().duration_since(act.hb) > CLIENT_TIMEOUT { - // heartbeat timed out - println!("Websocket Client heartbeat failed, disconnecting!"); - - // notify chat server - act.addr.do_send(server::Disconnect { id: act.id }); - - // stop actor - ctx.stop(); - - // don't try to send a ping - return; - } - - ctx.ping(b""); - }); - } -} - #[actix_web::main] async fn main() -> std::io::Result<()> { - env_logger::init(); + env_logger::init_from_env(env_logger::Env::new().default_filter_or("info")); - // App state - // We are keeping a count of the number of visitors + // set up applications state + // keep a count of the number of visitors let app_state = Arc::new(AtomicUsize::new(0)); - // Start chat server actor + // start chat server actor let server = server::ChatServer::new(app_state.clone()).start(); - // Create Http server with websocket support + log::info!("starting HTTP server at http://localhost:8080"); + HttpServer::new(move || { App::new() - .data(app_state.clone()) - .data(server.clone()) - // redirect to websocket.html - .service(web::resource("/").route(web::get().to(|| { - HttpResponse::Found() - .header("LOCATION", "/static/websocket.html") - .finish() - }))) - .route("/count/", web::get().to(get_count)) - // websocket - .service(web::resource("/ws/").to(chat_route)) - // static resources - .service(fs::Files::new("/static/", "static/")) + .app_data(web::Data::from(app_state.clone())) + .app_data(web::Data::new(server.clone())) + .service(web::resource("/").to(index)) + .route("/count", web::get().to(get_count)) + .route("/ws", web::get().to(chat_route)) + .service(Files::new("/static", "./static")) + .wrap(Logger::default()) }) + .workers(2) .bind(("127.0.0.1", 8080))? .run() .await diff --git a/websockets/chat/src/server.rs b/websockets/chat/src/server.rs index d78fd29..fb23691 100644 --- a/websockets/chat/src/server.rs +++ b/websockets/chat/src/server.rs @@ -2,15 +2,16 @@ //! And manages available rooms. Peers send messages to other peers in same //! room through `ChatServer`. -use actix::prelude::*; -use rand::{self, rngs::ThreadRng, Rng}; - -use std::sync::{ - atomic::{AtomicUsize, Ordering}, - Arc, +use std::{ + collections::{HashMap, HashSet}, + sync::{ + atomic::{AtomicUsize, Ordering}, + Arc, + }, }; -use std::collections::{HashMap, HashSet}; +use actix::prelude::*; +use rand::{self, rngs::ThreadRng, Rng}; /// Chat server sends this messages to session #[derive(Message)] @@ -56,14 +57,17 @@ impl actix::Message for ListRooms { #[derive(Message)] #[rtype(result = "()")] pub struct Join { - /// Client id + /// Client ID pub id: usize, + /// Room name pub name: String, } -/// `ChatServer` manages chat rooms and responsible for coordinating chat -/// session. implementation is super primitive +/// `ChatServer` manages chat rooms and responsible for coordinating chat session. +/// +/// Implementation is very naïve. +#[derive(Debug)] pub struct ChatServer { sessions: HashMap>, rooms: HashMap>, @@ -118,7 +122,7 @@ impl Handler for ChatServer { println!("Someone joined"); // notify all users in same room - self.send_message(&"Main".to_owned(), "Someone joined", 0); + self.send_message("Main", "Someone joined", 0); // register session with random id let id = self.rng.gen::(); diff --git a/websockets/chat/src/session.rs b/websockets/chat/src/session.rs new file mode 100644 index 0000000..a521d03 --- /dev/null +++ b/websockets/chat/src/session.rs @@ -0,0 +1,206 @@ +use std::time::{Duration, Instant}; + +use actix::prelude::*; +use actix_web_actors::ws; + +use crate::server; + +/// How often heartbeat pings are sent +const HEARTBEAT_INTERVAL: Duration = Duration::from_secs(5); + +/// How long before lack of client response causes a timeout +const CLIENT_TIMEOUT: Duration = Duration::from_secs(10); + +#[derive(Debug)] +pub struct WsChatSession { + /// unique session id + pub id: usize, + + /// Client must send ping at least once per 10 seconds (CLIENT_TIMEOUT), + /// otherwise we drop connection. + pub hb: Instant, + + /// joined room + pub room: String, + + /// peer name + pub name: Option, + + /// Chat server + pub addr: Addr, +} + +impl WsChatSession { + /// helper method that sends ping to client every second. + /// + /// also this method checks heartbeats from client + fn hb(&self, ctx: &mut ws::WebsocketContext) { + ctx.run_interval(HEARTBEAT_INTERVAL, |act, ctx| { + // check client heartbeats + if Instant::now().duration_since(act.hb) > CLIENT_TIMEOUT { + // heartbeat timed out + println!("Websocket Client heartbeat failed, disconnecting!"); + + // notify chat server + act.addr.do_send(server::Disconnect { id: act.id }); + + // stop actor + ctx.stop(); + + // don't try to send a ping + return; + } + + ctx.ping(b""); + }); + } +} + +impl Actor for WsChatSession { + type Context = ws::WebsocketContext; + + /// Method is called on actor start. + /// We register ws session with ChatServer + fn started(&mut self, ctx: &mut Self::Context) { + // we'll start heartbeat process on session start. + self.hb(ctx); + + // register self in chat server. `AsyncContext::wait` register + // future within context, but context waits until this future resolves + // before processing any other events. + // HttpContext::state() is instance of WsChatSessionState, state is shared + // across all routes within application + let addr = ctx.address(); + self.addr + .send(server::Connect { + addr: addr.recipient(), + }) + .into_actor(self) + .then(|res, act, ctx| { + match res { + Ok(res) => act.id = res, + // something is wrong with chat server + _ => ctx.stop(), + } + fut::ready(()) + }) + .wait(ctx); + } + + fn stopping(&mut self, _: &mut Self::Context) -> Running { + // notify chat server + self.addr.do_send(server::Disconnect { id: self.id }); + Running::Stop + } +} + +/// Handle messages from chat server, we simply send it to peer websocket +impl Handler for WsChatSession { + type Result = (); + + fn handle(&mut self, msg: server::Message, ctx: &mut Self::Context) { + ctx.text(msg.0); + } +} + +/// WebSocket message handler +impl StreamHandler> for WsChatSession { + fn handle( + &mut self, + msg: Result, + ctx: &mut Self::Context, + ) { + let msg = match msg { + Err(_) => { + ctx.stop(); + return; + } + Ok(msg) => msg, + }; + + log::debug!("WEBSOCKET MESSAGE: {:?}", msg); + match msg { + ws::Message::Ping(msg) => { + self.hb = Instant::now(); + ctx.pong(&msg); + } + ws::Message::Pong(_) => { + self.hb = Instant::now(); + } + ws::Message::Text(text) => { + let m = text.trim(); + // we check for /sss type of messages + if m.starts_with('/') { + let v: Vec<&str> = m.splitn(2, ' ').collect(); + match v[0] { + "/list" => { + // Send ListRooms message to chat server and wait for + // response + println!("List rooms"); + self.addr + .send(server::ListRooms) + .into_actor(self) + .then(|res, _, ctx| { + match res { + Ok(rooms) => { + for room in rooms { + ctx.text(room); + } + } + _ => println!("Something is wrong"), + } + fut::ready(()) + }) + .wait(ctx) + // .wait(ctx) pauses all events in context, + // so actor wont receive any new messages until it get list + // of rooms back + } + "/join" => { + if v.len() == 2 { + self.room = v[1].to_owned(); + self.addr.do_send(server::Join { + id: self.id, + name: self.room.clone(), + }); + + ctx.text("joined"); + } else { + ctx.text("!!! room name is required"); + } + } + "/name" => { + if v.len() == 2 { + self.name = Some(v[1].to_owned()); + } else { + ctx.text("!!! name is required"); + } + } + _ => ctx.text(format!("!!! unknown command: {:?}", m)), + } + } else { + let msg = if let Some(ref name) = self.name { + format!("{}: {}", name, m) + } else { + m.to_owned() + }; + // send message to chat server + self.addr.do_send(server::ClientMessage { + id: self.id, + msg, + room: self.room.clone(), + }) + } + } + ws::Message::Binary(_) => println!("Unexpected binary"), + ws::Message::Close(reason) => { + ctx.close(reason); + ctx.stop(); + } + ws::Message::Continuation(_) => { + ctx.stop(); + } + ws::Message::Nop => (), + } + } +} diff --git a/websockets/chat/static/index.html b/websockets/chat/static/index.html new file mode 100644 index 0000000..c41adb4 --- /dev/null +++ b/websockets/chat/static/index.html @@ -0,0 +1,198 @@ + + + + + Chat! + + + + + +

Chat!

+ +
+ + Status: + disconnected +
+ +
+ +
+ + +
+ +
+ +
+

Commands

+ + + + + + + + + + + + + + + + + +
+ /list + list all available rooms
+ /join name + join room, if room does not exist, create new one
+ /name name + set session name
+ some message + just string, send message to all peers in same room
+
+ + + + diff --git a/websockets/chat/static/websocket.html b/websockets/chat/static/websocket.html deleted file mode 100644 index 9373576..0000000 --- a/websockets/chat/static/websocket.html +++ /dev/null @@ -1,112 +0,0 @@ - - - - Chat! - - - - - - - -

Chat!

-
- - Status: disconnected -
- -
- - - - - diff --git a/websockets/echo/Cargo.toml b/websockets/echo/Cargo.toml new file mode 100644 index 0000000..d1a73ef --- /dev/null +++ b/websockets/echo/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "websocket" +version = "1.0.0" +edition = "2021" + +[[bin]] +name = "websocket-server" +path = "src/main.rs" + +[[bin]] +name = "websocket-client" +path = "src/client.rs" + +[dependencies] +actix = "0.12" +actix-codec = "0.5" +actix-files = "0.6.0-beta.16" +actix-rt = "2" +actix-web = "4.0.0-rc.3" +actix-web-actors = "4.0.0-beta.12" +awc = "3.0.0-beta.21" + +env_logger = "0.9" +log = "0.4" +futures = "0.3.7" +tokio = { version = "1.13.1", features = ["full"] } +tokio-stream = "0.1.8" diff --git a/websockets/websocket/README.md b/websockets/echo/README.md similarity index 100% rename from websockets/websocket/README.md rename to websockets/echo/README.md diff --git a/websockets/echo/src/client.rs b/websockets/echo/src/client.rs new file mode 100644 index 0000000..9f76781 --- /dev/null +++ b/websockets/echo/src/client.rs @@ -0,0 +1,72 @@ +//! Simple websocket client. + +use std::{io, thread}; + +use actix_web::web::Bytes; +use awc::ws; +use futures::{SinkExt as _, StreamExt as _}; +use tokio::{select, sync::mpsc}; +use tokio_stream::wrappers::UnboundedReceiverStream; + +#[actix_web::main] +async fn main() { + env_logger::init_from_env(env_logger::Env::new().default_filter_or("info")); + + log::info!("starting echo WebSocket client"); + + let (cmd_tx, cmd_rx) = mpsc::unbounded_channel(); + let mut cmd_rx = UnboundedReceiverStream::new(cmd_rx); + + // run blocking terminal input reader on separate thread + let input_thread = thread::spawn(move || loop { + let mut cmd = String::with_capacity(32); + + if io::stdin().read_line(&mut cmd).is_err() { + log::error!("error reading line"); + return; + } + + cmd_tx.send(cmd).unwrap(); + }); + + let (res, mut ws) = awc::Client::new() + .ws("ws://127.0.0.1:8080/ws") + .connect() + .await + .unwrap(); + + log::debug!("response: {res:?}"); + log::info!("connected; server will echo messages sent"); + + loop { + select! { + Some(msg) = ws.next() => { + match msg { + Ok(ws::Frame::Text(txt)) => { + // log echoed messages from server + log::info!("Server: {:?}", txt) + } + + Ok(ws::Frame::Ping(_)) => { + // respond to ping probes + ws.send(ws::Message::Pong(Bytes::new())).await.unwrap(); + } + + _ => {} + } + } + + Some(cmd) = cmd_rx.next() => { + if cmd.is_empty() { + continue; + } + + ws.send(ws::Message::Text(cmd.into())).await.unwrap(); + } + + else => break + } + } + + input_thread.join().unwrap(); +} diff --git a/websockets/echo/src/main.rs b/websockets/echo/src/main.rs new file mode 100644 index 0000000..f6a5153 --- /dev/null +++ b/websockets/echo/src/main.rs @@ -0,0 +1,42 @@ +//! Simple echo websocket server. +//! +//! Open `http://localhost:8080/` in browser to test. + +use actix_files::NamedFile; +use actix_web::{ + middleware, web, App, Error, HttpRequest, HttpResponse, HttpServer, Responder, +}; +use actix_web_actors::ws; + +mod server; +use self::server::MyWebSocket; + +async fn index() -> impl Responder { + NamedFile::open_async("./static/index.html").await.unwrap() +} + +/// WebSocket handshake and start `MyWebSocket` actor. +async fn echo_ws(req: HttpRequest, stream: web::Payload) -> Result { + ws::start(MyWebSocket::new(), &req, stream) +} + +#[actix_web::main] +async fn main() -> std::io::Result<()> { + env_logger::init_from_env(env_logger::Env::new().default_filter_or("info")); + + log::info!("starting HTTP server at http://localhost:8080"); + + HttpServer::new(|| { + App::new() + // WebSocket UI HTML file + .service(web::resource("/").to(index)) + // websocket route + .service(web::resource("/ws").route(web::get().to(echo_ws))) + // enable logger + .wrap(middleware::Logger::default()) + }) + .workers(2) + .bind(("127.0.0.1", 8080))? + .run() + .await +} diff --git a/websockets/websocket/src/main.rs b/websockets/echo/src/server.rs similarity index 65% rename from websockets/websocket/src/main.rs rename to websockets/echo/src/server.rs index 485db1e..8503196 100644 --- a/websockets/websocket/src/main.rs +++ b/websockets/echo/src/server.rs @@ -1,36 +1,49 @@ -//! Simple echo websocket server. -//! Open `http://localhost:8080/index.html` in browser -//! or [python console client](https://github.com/actix/examples/blob/master/websocket/websocket-client.py) -//! could be used for testing. - use std::time::{Duration, Instant}; use actix::prelude::*; -use actix_files as fs; -use actix_web::{middleware, web, App, Error, HttpRequest, HttpResponse, HttpServer}; use actix_web_actors::ws; /// How often heartbeat pings are sent const HEARTBEAT_INTERVAL: Duration = Duration::from_secs(5); + /// How long before lack of client response causes a timeout const CLIENT_TIMEOUT: Duration = Duration::from_secs(10); -/// do websocket handshake and start `MyWebSocket` actor -async fn ws_index(r: HttpRequest, stream: web::Payload) -> Result { - println!("{:?}", r); - let res = ws::start(MyWebSocket::new(), &r, stream); - println!("{:?}", res); - res -} - /// websocket connection is long running connection, it easier /// to handle with an actor -struct MyWebSocket { +pub struct MyWebSocket { /// Client must send ping at least once per 10 seconds (CLIENT_TIMEOUT), /// otherwise we drop connection. hb: Instant, } +impl MyWebSocket { + pub fn new() -> Self { + Self { hb: Instant::now() } + } + + /// helper method that sends ping to client every second. + /// + /// also this method checks heartbeats from client + fn hb(&self, ctx: &mut ::Context) { + ctx.run_interval(HEARTBEAT_INTERVAL, |act, ctx| { + // check client heartbeats + if Instant::now().duration_since(act.hb) > CLIENT_TIMEOUT { + // heartbeat timed out + println!("Websocket Client heartbeat failed, disconnecting!"); + + // stop actor + ctx.stop(); + + // don't try to send a ping + return; + } + + ctx.ping(b""); + }); + } +} + impl Actor for MyWebSocket { type Context = ws::WebsocketContext; @@ -67,50 +80,3 @@ impl StreamHandler> for MyWebSocket { } } } - -impl MyWebSocket { - fn new() -> Self { - Self { hb: Instant::now() } - } - - /// helper method that sends ping to client every second. - /// - /// also this method checks heartbeats from client - fn hb(&self, ctx: &mut ::Context) { - ctx.run_interval(HEARTBEAT_INTERVAL, |act, ctx| { - // check client heartbeats - if Instant::now().duration_since(act.hb) > CLIENT_TIMEOUT { - // heartbeat timed out - println!("Websocket Client heartbeat failed, disconnecting!"); - - // stop actor - ctx.stop(); - - // don't try to send a ping - return; - } - - ctx.ping(b""); - }); - } -} - -#[actix_web::main] -async fn main() -> std::io::Result<()> { - std::env::set_var("RUST_LOG", "actix_server=info,actix_web=info"); - env_logger::init(); - - HttpServer::new(|| { - App::new() - // enable logger - .wrap(middleware::Logger::default()) - // websocket route - .service(web::resource("/ws/").route(web::get().to(ws_index))) - // static files - .service(fs::Files::new("/", "static/").index_file("index.html")) - }) - // start http server on 127.0.0.1:8080 - .bind(("127.0.0.1", 8080))? - .run() - .await -} diff --git a/websockets/echo/static/index.html b/websockets/echo/static/index.html new file mode 100644 index 0000000..aefa94a --- /dev/null +++ b/websockets/echo/static/index.html @@ -0,0 +1,171 @@ + + + + + Websocket Echo + + + + +

Chat!

+ +
+ + Status: + disconnected +
+ +
+ +
+ + +
+ +
+ +
+

Usage

+

After connecting, type into the text box and the server will echo your message.

+
+ + + + diff --git a/websockets/websocket/websocket-client.py b/websockets/echo/websocket-client.py similarity index 100% rename from websockets/websocket/websocket-client.py rename to websockets/echo/websocket-client.py diff --git a/websockets/tcp-chat/static/websocket.html b/websockets/tcp-chat/static/websocket.html deleted file mode 100644 index e59e13f..0000000 --- a/websockets/tcp-chat/static/websocket.html +++ /dev/null @@ -1,90 +0,0 @@ - - - - - - - - -

Chat!

-
-  | Status: - disconnected -
-
-
-
- - -
- - diff --git a/websockets/websocket/Cargo.toml b/websockets/websocket/Cargo.toml deleted file mode 100644 index 17e80de..0000000 --- a/websockets/websocket/Cargo.toml +++ /dev/null @@ -1,23 +0,0 @@ -[package] -name = "websocket" -version = "1.0.0" -edition = "2021" - -[[bin]] -name = "websocket-server" -path = "src/main.rs" - -[[bin]] -name = "websocket-client" -path = "src/client.rs" - -[dependencies] -actix = "0.10" -actix-codec = "0.3" -actix-web = "3" -actix-web-actors = "3" -actix-files = "0.3" -awc = "2" -env_logger = "0.8" -futures = "0.3.1" -bytes = "0.5.3" diff --git a/websockets/websocket/src/client.rs b/websockets/websocket/src/client.rs deleted file mode 100644 index 53d9b86..0000000 --- a/websockets/websocket/src/client.rs +++ /dev/null @@ -1,113 +0,0 @@ -//! Simple websocket client. -use std::time::Duration; -use std::{io, thread}; - -use actix::io::SinkWrite; -use actix::*; -use actix_codec::Framed; -use awc::{ - error::WsProtocolError, - ws::{Codec, Frame, Message}, - BoxedSocket, Client, -}; -use bytes::Bytes; -use futures::stream::{SplitSink, StreamExt}; - -fn main() { - ::std::env::set_var("RUST_LOG", "actix_web=info"); - env_logger::init(); - - let sys = System::new("websocket-client"); - - Arbiter::spawn(async { - let (response, framed) = Client::new() - .ws("http://127.0.0.1:8080/ws/") - .connect() - .await - .map_err(|e| { - println!("Error: {}", e); - }) - .unwrap(); - - println!("{:?}", response); - let (sink, stream) = framed.split(); - let addr = ChatClient::create(|ctx| { - ChatClient::add_stream(stream, ctx); - ChatClient(SinkWrite::new(sink, ctx)) - }); - - // start console loop - thread::spawn(move || loop { - let mut cmd = String::new(); - if io::stdin().read_line(&mut cmd).is_err() { - println!("error"); - return; - } - addr.do_send(ClientCommand(cmd)); - }); - }); - sys.run().unwrap(); -} - -struct ChatClient(SinkWrite, Message>>); - -#[derive(Message)] -#[rtype(result = "()")] -struct ClientCommand(String); - -impl Actor for ChatClient { - type Context = Context; - - fn started(&mut self, ctx: &mut Context) { - // start heartbeats otherwise server will disconnect after 10 seconds - self.hb(ctx) - } - - fn stopped(&mut self, _: &mut Context) { - println!("Disconnected"); - - // Stop application on disconnect - System::current().stop(); - } -} - -impl ChatClient { - fn hb(&self, ctx: &mut Context) { - ctx.run_later(Duration::new(1, 0), |act, ctx| { - act.0.write(Message::Ping(Bytes::from_static(b""))); - act.hb(ctx); - - // client should also check for a timeout here, similar to the - // server code - }); - } -} - -/// Handle stdin commands -impl Handler for ChatClient { - type Result = (); - - fn handle(&mut self, msg: ClientCommand, _ctx: &mut Context) { - self.0.write(Message::Text(msg.0)); - } -} - -/// Handle server websocket messages -impl StreamHandler> for ChatClient { - fn handle(&mut self, msg: Result, _: &mut Context) { - if let Ok(Frame::Text(txt)) = msg { - println!("Server: {:?}", txt) - } - } - - fn started(&mut self, _ctx: &mut Context) { - println!("Connected"); - } - - fn finished(&mut self, ctx: &mut Context) { - println!("Server disconnected"); - ctx.stop() - } -} - -impl actix::io::WriteHandler for ChatClient {} diff --git a/websockets/websocket/static/actixLogo.png b/websockets/websocket/static/actixLogo.png deleted file mode 100644 index 1e2509a75a75b950e331348b1241e077cbaa3222..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13131 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 diff --git a/websockets/websocket/static/favicon.ico b/websockets/websocket/static/favicon.ico deleted file mode 100644 index 1eac423cca9ffaf88f2e03441bd9b40f8e69a7c2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32527 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 diff --git a/websockets/websocket/static/index.html b/websockets/websocket/static/index.html deleted file mode 100644 index e59e13f..0000000 --- a/websockets/websocket/static/index.html +++ /dev/null @@ -1,90 +0,0 @@ - - - - - - - - -

Chat!

-
-  | Status: - disconnected -
-
-
-
- - -
- -