refactor: rewrite

- Better Client struct (TCP and WebSocket in one type)
- WebSocket is now not a proxy to tcp
- Use async-std instead a tokio
- Use Arc and Client type now have a Clone derive
- Add global CLIENTS list
This commit is contained in:
MedzikUser 2022-08-12 22:32:22 +02:00
parent 868671848b
commit d0120a0703
No known key found for this signature in database
GPG Key ID: A5FAC1E185C112DB
26 changed files with 1005 additions and 1096 deletions

View File

@ -1,47 +0,0 @@
# Changelog
<!-- next-header -->
## [Unreleased]
## [0.3.0] - 2022-08-04
## **Breaking**
- **tcp**: use tcp from tokio instead of std
## Features
- **client**: added `peer_addr` function
- **server**: added `/help` command
- **api**: re-export `async_trait` so that it doesn't have to be added to dependencies in plugins
## Changed
- **server**: the `/help` command has been accelerated
- **cli**: moved to the `cli.rs` file
- **logger**: changed `log` to `tracing`
- **dependencies**: updated
- **cli**: deleted option `--disable-websocket` and added `--enable-websocket`
## [0.2.0] - 2022-06-26
### Features
- **plugins**: add `Result<()>` in `fn execute()` (Event and Command traits)
- **websocket**: WS Client <-> TCP Proxy (default port 9998) <-> TCP (default port 9999)
### Chore
- **deps**: upgrade
## [0.1.0] - 2022-06-17
### Default commands
- help
### Dynamic plugins loader
You can create custom commands and events (events executed if client connected or send message)
### Cli
You set custom host and port `./servers --host 0.0.0.0 --port 9999`
Show cli help `./servers --help`
<!-- next-url -->
[Unreleased]: https://github.com/MedzikUser/servers/compare/v0.3.0...HEAD
[0.3.0]: https://github.com/MedzikUser/servers/commits/v0.3.0
[0.2.0]: https://github.com/MedzikUser/servers/commits/v0.2.0
[0.1.0]: https://github.com/MedzikUser/servers/commits/v0.1.0

685
Cargo.lock generated
View File

@ -2,21 +2,6 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "addr2line"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b"
dependencies = [
"gimli",
]
[[package]]
name = "adler"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "ansi_term"
version = "0.12.1"
@ -28,9 +13,121 @@ dependencies = [
[[package]]
name = "anyhow"
version = "1.0.60"
version = "1.0.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c794e162a5eff65c72ef524dfe393eb923c354e350bb78b9c7383df13f3bc142"
checksum = "508b352bb5c066aac251f6daf6b36eccd03e8a88e8081cd44959ea277a3af9a8"
[[package]]
name = "async-attributes"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3203e79f4dd9bdda415ed03cf14dae5a2bf775c683a00f94e9cd1faf0f596e5"
dependencies = [
"quote",
"syn",
]
[[package]]
name = "async-channel"
version = "1.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e14485364214912d3b19cc3435dde4df66065127f05fa0d75c712f36f12c2f28"
dependencies = [
"concurrent-queue",
"event-listener",
"futures-core",
]
[[package]]
name = "async-executor"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "871f9bb5e0a22eeb7e8cf16641feb87c9dc67032ccf8ff49e772eb9941d3a965"
dependencies = [
"async-task",
"concurrent-queue",
"fastrand",
"futures-lite",
"once_cell",
"slab",
]
[[package]]
name = "async-global-executor"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5262ed948da60dd8956c6c5aca4d4163593dddb7b32d73267c93dab7b2e98940"
dependencies = [
"async-channel",
"async-executor",
"async-io",
"async-lock",
"blocking",
"futures-lite",
"num_cpus",
"once_cell",
]
[[package]]
name = "async-io"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5e18f61464ae81cde0a23e713ae8fd299580c54d697a35820cfd0625b8b0e07"
dependencies = [
"concurrent-queue",
"futures-lite",
"libc",
"log",
"once_cell",
"parking",
"polling",
"slab",
"socket2",
"waker-fn",
"winapi",
]
[[package]]
name = "async-lock"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e97a171d191782fba31bb902b14ad94e24a68145032b7eedf871ab0bc0d077b6"
dependencies = [
"event-listener",
]
[[package]]
name = "async-std"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d"
dependencies = [
"async-attributes",
"async-channel",
"async-global-executor",
"async-io",
"async-lock",
"crossbeam-utils",
"futures-channel",
"futures-core",
"futures-io",
"futures-lite",
"gloo-timers",
"kv-log-macro",
"log",
"memchr",
"once_cell",
"pin-project-lite",
"pin-utils",
"slab",
"wasm-bindgen-futures",
]
[[package]]
name = "async-task"
version = "4.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a40729d2133846d9ed0ea60a8b9541bccddab49cd30f0715a1da672fe9a2524"
[[package]]
name = "async-trait"
@ -43,6 +140,12 @@ dependencies = [
"syn",
]
[[package]]
name = "atomic-waker"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "065374052e7df7ee4047b1160cca5e1467a12351a40b3da123c870ba0b8eda2a"
[[package]]
name = "atty"
version = "0.2.14"
@ -60,37 +163,12 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "backtrace"
version = "0.3.65"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11a17d453482a265fd5f8479f2a3f405566e6ca627837aaddb85af8b1ab8ef61"
dependencies = [
"addr2line",
"cc",
"cfg-if",
"libc",
"miniz_oxide",
"object",
"rustc-demangle",
]
[[package]]
name = "base64"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
[[package]]
name = "better-panic"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6fa9e1d11a268684cbd90ed36370d7577afb6c62d912ddff5c15fc34343e5036"
dependencies = [
"backtrace",
"console",
]
[[package]]
name = "bitflags"
version = "1.3.2"
@ -106,6 +184,26 @@ dependencies = [
"generic-array",
]
[[package]]
name = "blocking"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6ccb65d468978a086b69884437ded69a90faab3bbe6e67f242173ea728acccc"
dependencies = [
"async-channel",
"async-task",
"atomic-waker",
"fastrand",
"futures-lite",
"once_cell",
]
[[package]]
name = "bumpalo"
version = "3.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37ccbd214614c6783386c1af30caf03192f17891059cecc394b4fb119e363de3"
[[package]]
name = "byteorder"
version = "1.4.3"
@ -114,9 +212,15 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]]
name = "bytes"
version = "1.1.0"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8"
checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db"
[[package]]
name = "cache-padded"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c"
[[package]]
name = "cc"
@ -132,9 +236,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clap"
version = "3.2.16"
version = "3.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3dbbb6653e7c55cc8595ad3e1f7be8f32aba4eb7ff7f0fd1163d4f3d137c0a9"
checksum = "29e724a68d9319343bb3328c9cc2dfde263f4b3142ee1059a9980580171c954b"
dependencies = [
"atty",
"bitflags",
@ -149,9 +253,9 @@ dependencies = [
[[package]]
name = "clap_derive"
version = "3.2.15"
version = "3.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ba52acd3b0a5c33aeada5cdaa3267cdc7c594a98731d4268cdc1532f4264cb4"
checksum = "13547f7012c01ab4a0e8f8967730ada8f9fdf419e8b6c792788f39cf4e46eefa"
dependencies = [
"heck",
"proc-macro-error",
@ -162,24 +266,20 @@ dependencies = [
[[package]]
name = "clap_lex"
version = "0.2.3"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87eba3c8c7f42ef17f6c659fc7416d0f4758cd3e58861ee63c5fa4a4dde649e4"
checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5"
dependencies = [
"os_str_bytes",
]
[[package]]
name = "console"
version = "0.15.0"
name = "concurrent-queue"
version = "1.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a28b32d32ca44b70c3e4acd7db1babf555fa026e385fb95f18028f88848b3c31"
checksum = "af4780a44ab5696ea9e28294517f1fffb421a83a25af521333c838635509db9c"
dependencies = [
"encode_unicode",
"libc",
"once_cell",
"terminal_size",
"winapi",
"cache-padded",
]
[[package]]
@ -192,15 +292,35 @@ dependencies = [
]
[[package]]
name = "crypto-common"
version = "0.1.3"
name = "crossbeam-utils"
version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57952ca27b5e3606ff4dd79b0020231aaf9d6aa76dc05fd30137538c50bd3ce8"
checksum = "51887d4adc7b564537b15adcfb307936f8075dfcd5f00dde9a9f1d29383682bc"
dependencies = [
"cfg-if",
"once_cell",
]
[[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 = "ctor"
version = "0.1.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdffe87e1d521a10f9696f833fe502293ea446d7f256c06128293a4119bdf4cb"
dependencies = [
"quote",
"syn",
]
[[package]]
name = "digest"
version = "0.10.3"
@ -212,10 +332,19 @@ dependencies = [
]
[[package]]
name = "encode_unicode"
version = "0.3.6"
name = "event-listener"
version = "2.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
[[package]]
name = "fastrand"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499"
dependencies = [
"instant",
]
[[package]]
name = "fnv"
@ -233,12 +362,80 @@ dependencies = [
"percent-encoding",
]
[[package]]
name = "futures"
version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f73fe65f54d1e12b726f517d3e2135ca3125a437b6d998caf1962961f7172d9e"
dependencies = [
"futures-channel",
"futures-core",
"futures-executor",
"futures-io",
"futures-sink",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-channel"
version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010"
dependencies = [
"futures-core",
"futures-sink",
]
[[package]]
name = "futures-core"
version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3"
[[package]]
name = "futures-executor"
version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9420b90cfa29e327d0429f19be13e7ddb68fa1cccb09d65e5706b8c7a749b8a6"
dependencies = [
"futures-core",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-io"
version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b"
[[package]]
name = "futures-lite"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7694489acd39452c77daa48516b894c153f192c3578d5a839b62c58099fcbf48"
dependencies = [
"fastrand",
"futures-core",
"futures-io",
"memchr",
"parking",
"pin-project-lite",
"waker-fn",
]
[[package]]
name = "futures-macro"
version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33c1e13800337f4d4d7a316bf45a567dbcb6ffe087f16424852d97e97a91f512"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "futures-sink"
version = "0.3.21"
@ -257,9 +454,13 @@ version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a"
dependencies = [
"futures-channel",
"futures-core",
"futures-io",
"futures-macro",
"futures-sink",
"futures-task",
"memchr",
"pin-project-lite",
"pin-utils",
"slab",
@ -267,9 +468,9 @@ dependencies = [
[[package]]
name = "generic-array"
version = "0.14.5"
version = "0.14.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803"
checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9"
dependencies = [
"typenum",
"version_check",
@ -287,16 +488,22 @@ dependencies = [
]
[[package]]
name = "gimli"
version = "0.26.1"
name = "gloo-timers"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4"
checksum = "5fb7d06c1c8cc2a29bee7ec961009a0b2caa0793ee4900c2ffb348734ba1c8f9"
dependencies = [
"futures-channel",
"futures-core",
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "hashbrown"
version = "0.12.1"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db0d4cf898abf0081f964436dc980e96670a0f36863e4b83aaacdb65c9d7ccc3"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
[[package]]
name = "heck"
@ -352,10 +559,37 @@ dependencies = [
]
[[package]]
name = "itoa"
version = "1.0.2"
name = "instant"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d"
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
dependencies = [
"cfg-if",
]
[[package]]
name = "itoa"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754"
[[package]]
name = "js-sys"
version = "0.3.59"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "258451ab10b34f8af53416d1fdab72c22e805f0c92a1136d59470ec0b11138b2"
dependencies = [
"wasm-bindgen",
]
[[package]]
name = "kv-log-macro"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f"
dependencies = [
"log",
]
[[package]]
name = "lazy_static"
@ -365,9 +599,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.126"
version = "0.2.131"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"
checksum = "04c3b4822ccebfa39c02fc03d1534441b22ead323fa0f48bb7ddd8e6ba076a40"
[[package]]
name = "libloading"
@ -386,6 +620,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
dependencies = [
"cfg-if",
"value-bag",
]
[[package]]
@ -400,27 +635,6 @@ version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
[[package]]
name = "miniz_oxide"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f5c75688da582b8ffc1f1799e9db273f32133c49e048f614d22ec3256773ccc"
dependencies = [
"adler",
]
[[package]]
name = "mio"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf"
dependencies = [
"libc",
"log",
"wasi",
"windows-sys",
]
[[package]]
name = "num_cpus"
version = "1.13.1"
@ -431,15 +645,6 @@ dependencies = [
"libc",
]
[[package]]
name = "object"
version = "0.28.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e42c982f2d955fac81dd7e1d0e1426a7d702acd9c98d19ab01083a6a0328c424"
dependencies = [
"memchr",
]
[[package]]
name = "once_cell"
version = "1.13.0"
@ -448,9 +653,15 @@ checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1"
[[package]]
name = "os_str_bytes"
version = "6.1.0"
version = "6.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21326818e99cfe6ce1e524c2a805c189a99b5ae555a35d19f9a284b427d86afa"
checksum = "648001efe5d5c0102d8cea768e348da85d90af8ba91f0bea908f157951493cd4"
[[package]]
name = "parking"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72"
[[package]]
name = "percent-encoding"
@ -477,6 +688,19 @@ dependencies = [
"servers",
]
[[package]]
name = "polling"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "685404d509889fade3e86fe3a5803bca2ec09b0c0778d5ada6ec8bf7a8de5259"
dependencies = [
"cfg-if",
"libc",
"log",
"wepoll-ffi",
"winapi",
]
[[package]]
name = "ppv-lite86"
version = "0.2.16"
@ -509,18 +733,18 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.40"
version = "1.0.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7"
checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.20"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804"
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
dependencies = [
"proc-macro2",
]
@ -555,25 +779,17 @@ dependencies = [
"getrandom",
]
[[package]]
name = "rustc-demangle"
version = "0.1.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342"
[[package]]
name = "servers"
version = "0.3.0"
version = "0.1.0"
dependencies = [
"anyhow",
"async-std",
"async-trait",
"better-panic",
"clap",
"futures-util",
"futures",
"lazy_static",
"libloading",
"tokio",
"tokio-tungstenite",
"tracing",
"tracing-subscriber",
"tungstenite",
@ -601,9 +817,12 @@ dependencies = [
[[package]]
name = "slab"
version = "0.4.6"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb703cfe953bccee95685111adeedb76fabe4e97549a58d16f03ea7b9367bb32"
checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef"
dependencies = [
"autocfg",
]
[[package]]
name = "smallvec"
@ -629,9 +848,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "syn"
version = "1.0.98"
version = "1.0.99"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd"
checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13"
dependencies = [
"proc-macro2",
"quote",
@ -647,16 +866,6 @@ dependencies = [
"winapi-util",
]
[[package]]
name = "terminal_size"
version = "0.1.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df"
dependencies = [
"libc",
"winapi",
]
[[package]]
name = "textwrap"
version = "0.15.0"
@ -665,18 +874,18 @@ checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb"
[[package]]
name = "thiserror"
version = "1.0.31"
version = "1.0.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a"
checksum = "f5f6586b7f764adc0231f4c79be7b920e766bb2f3e51b3661cdb263828f19994"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.31"
version = "1.0.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a"
checksum = "12bafc5b54507e0149cdf1b145a5d80ab80a90bcd9275df43d4fff68460f6c21"
dependencies = [
"proc-macro2",
"quote",
@ -707,48 +916,6 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
[[package]]
name = "tokio"
version = "1.20.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a8325f63a7d4774dd041e363b2409ed1c5cbbd0f867795e661df066b2b0a581"
dependencies = [
"autocfg",
"bytes",
"libc",
"memchr",
"mio",
"num_cpus",
"once_cell",
"pin-project-lite",
"socket2",
"tokio-macros",
"winapi",
]
[[package]]
name = "tokio-macros"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tokio-tungstenite"
version = "0.17.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f714dd15bead90401d77e04243611caec13726c2408afd5b31901dfcdcb3b181"
dependencies = [
"futures-util",
"log",
"tokio",
"tungstenite",
]
[[package]]
name = "tracing"
version = "0.1.36"
@ -840,15 +1007,15 @@ checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992"
[[package]]
name = "unicode-ident"
version = "1.0.1"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c"
checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf"
[[package]]
name = "unicode-normalization"
version = "0.1.20"
version = "0.1.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81dee68f85cab8cf68dec42158baf3a79a1cdc065a8b103025965d6ccb7f6cbd"
checksum = "854cbdc4f7bc6ae19c820d44abdc3277ac3e1b2b93db20a636825d9322fb60e6"
dependencies = [
"tinyvec",
]
@ -877,18 +1044,119 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
[[package]]
name = "value-bag"
version = "1.0.0-alpha.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2209b78d1249f7e6f3293657c9779fe31ced465df091bbd433a1cf88e916ec55"
dependencies = [
"ctor",
"version_check",
]
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "waker-fn"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca"
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
version = "0.2.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc7652e3f6c4706c8d9cd54832c4a4ccb9b5336e2c3bd154d5cccfbf1c1f5f7d"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "662cd44805586bd52971b9586b1df85cdbbd9112e4ef4d8f41559c334dc6ac3f"
dependencies = [
"bumpalo",
"log",
"once_cell",
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-futures"
version = "0.4.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa76fb221a1f8acddf5b54ace85912606980ad661ac7a503b4570ffd3a624dad"
dependencies = [
"cfg-if",
"js-sys",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b260f13d3012071dfb1512849c033b1925038373aea48ced3012c09df952c602"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5be8e654bdd9b79216c2929ab90721aa82faf65c48cdf08bdc4e7f51357b80da"
dependencies = [
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6598dd0bd3c7d51095ff6531a5b23e02acdc81804e30d8f07afb77b7215a140a"
[[package]]
name = "web-sys"
version = "0.3.59"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed055ab27f941423197eb86b2035720b1a3ce40504df082cac2ecc6ed73335a1"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "wepoll-ffi"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d743fdedc5c64377b5fc2bc036b01c7fd642205a0d96356034ae3404d49eb7fb"
dependencies = [
"cc",
]
[[package]]
name = "winapi"
version = "0.3.9"
@ -919,46 +1187,3 @@ name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-sys"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2"
dependencies = [
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_msvc"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47"
[[package]]
name = "windows_i686_gnu"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6"
[[package]]
name = "windows_i686_msvc"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024"
[[package]]
name = "windows_x86_64_gnu"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1"
[[package]]
name = "windows_x86_64_msvc"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"

View File

@ -4,29 +4,19 @@ resolver = "2"
[package]
name = "servers"
description = "Simple TCP server for clients written in Rust with plugins support."
version = "0.3.0"
license = "MIT"
authors = ["MedzikUser <medzik@duck.com>"]
homepage = "https://github.com/MedzikUser/servers"
repository = "https://github.com/MedzikUser/servers.git"
version = "0.1.0"
edition = "2021"
[profile.release]
lto = true
opt-level = 'z'
codegen-units = 1
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
anyhow = "1.0.60"
async-std = { version = "1.12.0", features = ["attributes"] }
async-trait = "0.1.57"
better-panic = "0.3.0"
lazy_static = "1.4.0"
clap = { version = "3.2.17", features = ["derive"] }
libloading = "0.7.3"
tokio-tungstenite = "0.17.2"
tracing = "0.1.36"
tracing-subscriber = "0.3.15"
tungstenite = "0.17.3"
clap = { version = "3.2.16", features = ["derive"] }
futures-util = { version = "0.3", default-features = false, features = ["sink", "std"] }
tokio = { version = "1.20.1", features = ["rt-multi-thread", "macros", "net"] }
futures = "0.3.21"
lazy_static = "1.4.0"

View File

@ -1,3 +0,0 @@
# NOTE: Custom image specification for freebsd is required until new version of cross is released.
[target.x86_64-unknown-freebsd]
image = "svenstaro/cross-x86_64-unknown-freebsd:latest"

View File

@ -1,31 +0,0 @@
# Servers - Simple TCP and WebSocket server
[docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs
[total-lines]: https://img.shields.io/tokei/lines/github/MedzikUser/servers?style=for-the-badge&logo=github&color=fede00
[code-size]: https://img.shields.io/github/languages/code-size/MedzikUser/servers?style=for-the-badge&color=c8df52&logo=github
[ci]: https://img.shields.io/github/workflow/status/MedzikUser/servers/Rust/main?style=for-the-badge
[image]:https://socialify.git.ci/MedzikUser/servers/image?description=1&font=KoHo&language=1&owner=1&pattern=Circuit%20Board&theme=Light
[![docs-rs]](https://servers.medzik.xyz)
[![total-lines]](https://github.com/MedzikUser/servers)
[![code-size]](https://github.com/MedzikUser/servers)
[![ci]](https://github.com/MedzikUser/servers/actions/workflows/build.yml)
[![image]](https://github.com/MedzikUser/servers)
A simple TCP server for clients and WebSocket server written in Rust 🦀 which can be extended with plugins.
## 👨‍💻 Building
First clone the repository: `git clone https://github.com/MedzikUser/servers.git`
### Requirements
- Rust
To build run the command: `cargo build --release`
The compiled binary can be found in `./target/release/servers`
## Writing plugins
Read the docs from [plugins](https://servers.medzik.xyz/servers/plugins) module.

View File

@ -3,6 +3,8 @@ name = "plugin_test"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
crate-type = ["dylib"]

View File

@ -1,72 +1,45 @@
use servers::{
async_trait,
plugins::{Command, Event, Plugin, PluginManagerType, Registrar, Result},
tcp::Client,
};
use servers::plugins::prelude::*;
struct PluginTest;
/// Create a new plugin
#[async_trait]
impl Plugin for PluginTest {
/// Name of the plugin.
fn name(&self) -> &'static str {
"test"
"test_plugin"
}
/// A function will be executed when plugin loading.
/// Usally used for initialization.
async fn on_plugin_load(&self) {}
/// A function that will be executed when the plugin is loaded.
async fn on_load(&self) {}
}
/// Create a new command
#[async_trait]
impl Command for PluginTest {
/// Command name
/// Name of the command.
fn name(&self) -> &'static str {
"/test"
}
/// Help message of the command
/// Aliases for the command.
fn aliases(&self) -> Vec<&'static str> {
Vec::new()
}
/// Help message of the command.
fn help(&self) -> &'static str {
"Test command from plugin"
"Test commend loaded from dylib"
}
/// Command function
async fn execute(
&self,
client: &mut Client,
_args: Vec<&str>,
_commands: &PluginManagerType,
) -> Result<()> {
client.send("content").await?;
/// Usage message of the command.
fn usage(&self) -> &'static str {
"/test"
}
/// Command function.
async fn execute(&self, client: &Client, _args: Vec<&str>) -> anyhow::Result<()> {
client.send("successful executed command from dylib")?;
Ok(())
}
}
/// Create a new event
#[async_trait]
impl Event for PluginTest {
/// Event name (onConnect or onSend)
fn name(&self) -> &'static str {
"onConnect"
}
/// Event function
async fn execute(&self, client: &mut Client) -> Result<()> {
client
.send(format!("Welcome {}", client.peer_addr().unwrap()))
.await?;
Ok(())
}
}
/// Register plugin
#[no_mangle]
pub fn plugin_entry(registrar: &mut dyn Registrar) {
registrar.register_plugin(Box::new(PluginTest));
registrar.register_command(Box::new(PluginTest));
registrar.register_event(Box::new(PluginTest));
registrar.register_plugins(Box::new(PluginTest));
registrar.register_commands(Box::new(PluginTest));
}

View File

@ -1,13 +0,0 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": ["config:base", "schedule:weekly", "group:allNonMajor", ":semanticCommits"],
"labels": ["dependencies"],
"automergeType": "pr",
"prCreation": "immediate",
"packageRules": [
{
"matchUpdateTypes": ["minor", "patch", "pin", "digest"],
"automerge": true
}
]
}

10
rustfmt.toml Normal file
View File

@ -0,0 +1,10 @@
# https://rust-lang.github.io/rustfmt
# stable
edition = "2021"
newline_style = "Unix"
# nightly
group_imports = "StdExternalCrate"
imports_granularity = "Crate"
format_code_in_doc_comments = true

View File

@ -1,43 +0,0 @@
use clap::Parser;
#[derive(Parser)]
#[clap(
name = env!("CARGO_PKG_NAME"),
version = env!("CARGO_PKG_VERSION"),
about = env!("CARGO_PKG_DESCRIPTION"),
)]
pub struct Cli {
#[clap(
short = 'h',
long = "host",
default_value = "0.0.0.0",
help = "Tcp server host",
display_order = 1
)]
pub host: String,
#[clap(
short = 'p',
long = "port",
default_value = "9999",
help = "Tcp server port [set 0 to random]",
display_order = 2
)]
pub port: String,
#[clap(
short = 'w',
long = "ws-port",
default_value = "9998",
help = "WebSocket server port [set 0 to random]",
display_order = 3
)]
pub ws_port: String,
#[clap(
long = "enable-websocket",
help = "Enable WebSocket proxy to Tcp [default disabled]",
display_order = 4
)]
pub ws_enable: bool,
}

View File

@ -1,31 +1,28 @@
use async_trait::async_trait;
use tokio::io::AsyncWriteExt;
use crate::plugins::prelude::*;
use crate::{
plugins::{Command, PluginManagerType, Result},
tcp::Client,
};
pub struct CommandDisconnect;
pub struct Disconnect;
#[async_trait]
impl Command for CommandDisconnect {
impl Command for Disconnect {
fn name(&self) -> &'static str {
"/disconnect"
}
fn help(&self) -> &'static str {
"Disconnect from the server"
fn aliases(&self) -> Vec<&'static str> {
vec!["/close", "/exit"]
}
async fn execute(
&self,
client: &mut Client,
_args: Vec<&str>,
_plugin_manager: &PluginManagerType,
) -> Result<()> {
fn help(&self) -> &'static str {
"Close the connection"
}
fn usage(&self) -> &'static str {
"/disconnect"
}
async fn execute(&self, client: &Client, _args: Vec<&str>) -> anyhow::Result<()> {
// close the connection
client.stream.shutdown().await?;
client.close()?;
Ok(())
}

View File

@ -1,38 +1,46 @@
use async_trait::async_trait;
use crate::plugins::prelude::*;
use crate::{
plugins::{Command, PluginManagerType, Result},
tcp::Client,
};
pub struct CommandHelp;
pub struct Help;
#[async_trait]
impl Command for CommandHelp {
impl Command for Help {
fn name(&self) -> &'static str {
"/help"
}
fn help(&self) -> &'static str {
"Display all available commands"
fn aliases(&self) -> Vec<&'static str> {
vec!["/h", "/?", "?"]
}
async fn execute(
&self,
client: &mut Client,
_args: Vec<&str>,
plugin_manager: &PluginManagerType,
) -> Result<()> {
// Vector which will contain help messages of the commands
let mut help = Vec::new();
fn help(&self) -> &'static str {
"Show commands help menu"
}
for command in plugin_manager.commands.iter() {
// add a help message for the command
help.push(format!("{} - {}", command.name(), command.help()));
fn usage(&self) -> &'static str {
"/help"
}
async fn execute(&self, client: &Client, _args: Vec<&str>) -> anyhow::Result<()> {
let mut msg = Vec::new();
for cmd in client.plugins_manager.commands.iter() {
let aliases = cmd.aliases();
let aliases = if !aliases.is_empty() {
cmd.aliases().join(", ")
} else {
"none".to_string()
};
msg.push(format!(
"{name} - {help} (Aliases: {aliases})",
name = cmd.name(),
help = cmd.help(),
aliases = aliases,
))
}
// send help message to the client
client.send(help.join("\n\r")).await?;
client.send(msg.join("\n"))?;
Ok(())
}

View File

@ -1,15 +1,9 @@
//! Build-in commands
mod disconnect;
mod help;
use crate::plugins::Command;
use self::{disconnect::Disconnect, help::Help};
use crate::plugins::prelude::*;
/// Register build-in commands
pub fn register_commands() -> Vec<Box<dyn Command>> {
// create array with build-in commands
vec![
Box::new(help::CommandHelp),
Box::new(disconnect::CommandDisconnect),
]
vec![Box::new(Help), Box::new(Disconnect)]
}

View File

@ -1,29 +1,14 @@
//! # Servers - Simple TCP and WebSocket server
//!
//! [image]: https://socialify.git.ci/MedzikUser/servers/image?description=1&font=KoHo&language=1&owner=1&pattern=Circuit%20Board&theme=Light
//!
//! [![image]](https://github.com/MedzikUser/servers)
//!
//! ## 👨‍💻 Building
//!
//! First clone the repository: `git clone https://github.com/MedzikUser/servers.git`
//!
//! ### Requirements
//! - Rust
//!
//! To build run the command: `cargo build --release`
//!
//! The compiled binary can be found in `./target/release/servers`
//!
//! ## Writing plugins
//!
//! Go to [plugins](plugins) module
use std::{collections::HashMap, sync::Mutex};
#![doc(html_root_url = "https://servers.medzik.xyz")]
use lazy_static::lazy_static;
use crate::tcp::Client;
pub mod commands;
pub mod logger;
pub mod plugins;
pub mod tcp;
pub use async_trait::async_trait;
lazy_static! {
pub static ref CLIENTS: Mutex<HashMap<usize, Client>> = Mutex::new(HashMap::new());
pub static ref CLIENT_NEXT: Mutex<usize> = Mutex::new(0);
}

View File

@ -1,9 +0,0 @@
use tracing::metadata::LevelFilter;
pub fn init() {
better_panic::install();
tracing_subscriber::fmt()
.with_max_level(LevelFilter::TRACE)
.init();
}

View File

@ -1,88 +1,46 @@
mod cli;
use clap::Parser;
use servers::{
logger,
plugins::loader,
tcp::{handle_connection, handle_websocket, Client},
};
use tokio::net::TcpListener;
use tracing::{error, info};
use servers::tcp::server;
use crate::cli::Cli;
#[derive(Debug, Parser)]
#[clap(
name = env!("CARGO_PKG_NAME"),
version = env!("CARGO_PKG_VERSION"),
about = env!("CARGO_PKG_DESCRIPTION")
)]
struct Cli {
#[clap(
short = 'i',
long = "host",
help = "Server host",
default_value = "0.0.0.0",
display_order = 1
)]
host: String,
#[clap(
short = 't',
long = "tcp-port",
help = "TCP server port",
default_value = "9999",
display_order = 2
)]
tcp_port: u16,
#[clap(
short = 'w',
long = "websocket-port",
help = "WebSocket server port",
default_value = "9998",
display_order = 3
)]
ws_port: u16,
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
logger::init();
fn main() {
tracing_subscriber::fmt().init();
// parse cli args
let args = Cli::parse();
// if enabled start WebSocket server
if args.ws_enable {
tokio::spawn(start_ws_server(
args.host.clone(),
args.ws_port,
args.port.clone(),
));
}
let tcp_host = format!("{host}:{port}", host = args.host, port = args.tcp_port);
let ws_host = format!("{host}:{port}", host = args.host, port = args.ws_port);
// start tcp server
start_tcp_server(args.host, args.port).await?;
Ok(())
}
/// Start tcp server
async fn start_tcp_server(host: String, port: String) -> anyhow::Result<()> {
// listen TCP server
let listener = TcpListener::bind(format!("{host}:{port}")).await?;
info!("TCP server started at: {}", listener.local_addr()?);
// load plugins, commands and events
let plugin_manager = loader()?;
// Accepts a new incoming connection from this listener.
while let Ok((stream, _address)) = listener.accept().await {
let client = Client::new(stream);
let plugin_manager = plugin_manager.clone();
// handle client connection in new thread
tokio::spawn(async move {
// get ip address of the client
let ip = client.peer_addr().expect("failed to get peer address");
if let Err(e) = handle_connection(client, plugin_manager).await {
error!("Client {ip}: {e}")
}
});
}
// server for a unexpectedly reason be terminated
panic!("TCP server unexpectedly terminated!")
}
/// Start WebSocket server
async fn start_ws_server(host: String, port: String, tcp_port: String) -> anyhow::Result<()> {
// listen Tcp server
let listener = tokio::net::TcpListener::bind(format!("{host}:{port}")).await?;
info!("WebSocket server started at: {}", listener.local_addr()?);
// Accepts a new incoming connection from this listener.
while let Ok((stream, _address)) = listener.accept().await {
let tcp_port = tcp_port.clone();
tokio::spawn(async {
// get ip address of the client
let ip = stream.peer_addr().expect("failed to get peer address");
if let Err(e) = handle_websocket(stream, tcp_port).await {
error!("Client {ip}: {e}")
}
});
}
// server for a unexpectedly reason be terminated
panic!("WebSocket server unexpectedly terminated!")
server::run(tcp_host, ws_host).expect("failed to start tcp server");
}

52
src/plugins/load.rs Normal file
View File

@ -0,0 +1,52 @@
use std::{fs, path::Path, sync::Arc};
use libloading::{Library, Symbol};
use tracing::{info, trace};
use crate::{
commands,
plugins::{
manager::{PluginsManager, PluginsManagerType},
prelude::*,
},
};
pub fn loader(plugins_dir: &str) -> anyhow::Result<PluginsManagerType> {
// if plugins directory doesn't exists, create it
if !Path::new(plugins_dir).exists() {
fs::create_dir_all(plugins_dir)?;
}
// get all files from the plugins directory
let plugins_files = fs::read_dir(plugins_dir)?;
// init a plugins manager
let mut plugins_manager = PluginsManager::new();
// add default commands
plugins_manager.commands = commands::register_commands();
for plugin_path in plugins_files {
let path = plugin_path?.path();
let path_str = path.to_str().unwrap();
info!("Loading plugin {}", path_str);
// loading library from .so is unsafe
unsafe {
// Box::new and Box::leak must be there because
// if it isn't there it throws an segmentation fault
let lib = Box::leak(Box::new(Library::new(&path)?));
trace!("Finding symbol `plugin_entry` in {}", path_str);
let func: Symbol<unsafe extern "C" fn(&mut dyn Registrar) -> ()> =
lib.get(b"plugin_entry")?;
// execute the function `plugin_entry` to load the plugin (possible segmentation fault)
trace!("Running function `plugin_entry` from plugin {}", path_str);
func(&mut plugins_manager);
}
}
Ok(Arc::new(plugins_manager))
}

View File

@ -1,60 +0,0 @@
use std::{fs, path::Path, sync::Arc};
use libloading::{Library, Symbol};
use tracing::{info, trace};
use crate::{commands, plugins::Registrar};
use super::{PluginManager, PluginManagerType};
/// Plugins and Commands loader
pub fn loader() -> anyhow::Result<PluginManagerType> {
let config_dir = "./plugins";
// if config directory doesn't exists, create it
if !Path::new(config_dir).exists() {
fs::create_dir_all(config_dir)?;
}
// get path to .so lib from command argument
let paths = fs::read_dir(config_dir)?;
// create a plugin manager
let mut plugin_manager = PluginManager::new();
// register default commands
for command in commands::register_commands() {
plugin_manager.commands.push(command)
}
// for all plugin in directory
for path in paths {
// get library file path
let path = path?.path();
let plugin_path = path.to_str().unwrap();
info!("Loading plugin `{}`", plugin_path);
// loading library from .so is unsafe
unsafe {
// load library
// Box::new and Box::leak must be there because if it isn't there it throws a segmentation fault
let lib = Box::leak(Box::new(Library::new(&path)?));
// get function `plugin_entry` from library
trace!("Finding symbol `plugin_entry` in `{}`", plugin_path);
let func: Symbol<unsafe extern "C" fn(&mut dyn Registrar) -> ()> =
lib.get(b"plugin_entry")?;
// execute initial plugin function
trace!(
"Running function `plugin_entry` from plugin `{}`",
plugin_path
);
func(&mut plugin_manager);
}
}
// return a `PluginManager`
Ok(Arc::new(plugin_manager))
}

37
src/plugins/manager.rs Normal file
View File

@ -0,0 +1,37 @@
use core::fmt;
use std::sync::Arc;
use crate::plugins::prelude::*;
#[derive(Default)]
pub struct PluginsManager {
/// Vector with all loaded plugins.
pub plugins: Vec<Box<dyn Plugin>>,
/// Vector with all loaded commands.
pub commands: Vec<Box<dyn Command>>,
/// Vector with all loaded events.
pub events: Vec<Box<dyn Event>>,
}
impl PluginsManager {
/// Returns an empty PluginsManager
pub fn new() -> PluginsManager {
Self {
plugins: Vec::new(),
commands: Vec::new(),
events: Vec::new(),
}
}
}
impl fmt::Debug for PluginsManager {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("PluginsManager")
.field("plugins", &self.plugins.len())
.field("commands", &self.commands.len())
.field("events", &self.events.len())
.finish()
}
}
pub type PluginsManagerType = Arc<PluginsManager>;

View File

@ -1,161 +1,16 @@
//! Plugins loader
//!
//! # Writing plugins
//!
//! Create a new project `cargo new --lib plugin`
//!
//! Set a `crate-type` in Cargo.toml (to build a `.so` plugin)
//!
//! ```toml
//! [lib]
//! crate-type = ["dylib"]
//! ```
//!
//! Add a `servers` and `async-trait` dependencies to Cargo.toml
//!
//! ```toml
//! [dependencies]
//! servers = { git = "https://github.com/MedzikUser/servers" }
//! ```
//!
//! In file `src/lib.rs`
//!
//! ```no_run
//! use servers::{plugins::{Plugin, Registrar}, tcp::Client, async_trait};
//!
//! struct PluginTest;
//!
//! /// Create a new plugin
//! #[async_trait]
//! impl Plugin for PluginTest {
//! /// Name of the plugin.
//! fn name(&self) -> &'static str {
//! "test"
//! }
//!
//! /// A function will be executed when plugin loading.
//! /// Usally used for initialization.
//! async fn on_plugin_load(&self) {}
//! }
//!
//! /// Register plugin
//! #[no_mangle]
//! pub fn plugin_entry(registrar: &mut dyn Registrar) {
//! registrar.register_plugin(Box::new(PluginTest));
//! }
//! ```
//!
//! ## Add command
//!
//! ```no_run
//! use servers::{
//! plugins::{Command, PluginManagerType, Registrar, Result, async_trait},
//! tcp::Client,
//! };
//! #
//! # struct PluginTest;
//! #
//! # #[async_trait]
//! # impl servers::plugins::Plugin for PluginTest {
//! # /// Name of the plugin.
//! # fn name(&self) -> &'static str {
//! # "test"
//! # }
//! #
//! # /// A function will be executed when plugin loading.
//! # /// Usally used for initialization.
//! # async fn on_plugin_load(&self) {}
//! # }
//!
//! /// Create a new command
//! #[async_trait]
//! impl Command for PluginTest {
//! /// Command name
//! fn name(&self) -> &'static str {
//! "/test"
//! }
//!
//! /// Help message of the command
//! fn help(&self) -> &'static str {
//! "test command"
//! }
//!
//! /// Command function
//! async fn execute(&self, client: &mut Client, _args: Vec<&str>, _commands: &PluginManagerType) -> Result<()> {
//! client.send("Command executed!").await?;
//!
//! Ok(())
//! }
//! }
//!
//! /// Register plugin
//! #[no_mangle]
//! pub fn plugin_entry(registrar: &mut dyn Registrar) {
//! # registrar.register_plugin(Box::new(PluginTest));
//! registrar.register_command(Box::new(PluginTest));
//! }
//! ```
//!
//! ## Add event
//!
//! In file `src/lib.rs`
//!
//! ```no_run
//! use servers::{
//! plugins::{Event, Registrar, Result, async_trait},
//! tcp::Client,
//! };
//! #
//! # struct PluginTest;
//! #
//! # #[async_trait]
//! # impl servers::plugins::Plugin for PluginTest {
//! # /// Name of the plugin.
//! # fn name(&self) -> &'static str {
//! # "test"
//! # }
//! #
//! # /// A function will be executed when plugin loading.
//! # /// Usally used for initialization.
//! # async fn on_plugin_load(&self) {}
//! # }
//!
//! /// Create a new event
//! #[async_trait]
//! impl Event for PluginTest {
//! /// Event name (onConnect or onSend)
//! fn name(&self) -> &'static str {
//! "onConnect"
//! }
//!
//! /// Event function
//! async fn execute(&self, client: &mut Client) -> Result<()> {
//! client
//! .send(format!("Welcome {}", client.peer_addr()?))
//! .await?;
//!
//! Ok(())
//! }
//! }
//!
//! /// Register plugin
//! #[no_mangle]
//! pub fn plugin_entry(registrar: &mut dyn Registrar) {
//! # registrar.register_plugin(Box::new(PluginTest));
//! registrar.register_event(Box::new(PluginTest));
//! }
//! ```
//!
//! ## Build plugin
//!
//! To build plugin run command: `cargo build --release`
//!
//! The compiled plugin can be found in `target/release/libplugin.so`
//!
//! Move (or create a symlink) the built plugin to the `plugin/` directory in the server root directory.
mod load;
pub mod manager;
pub mod types;
mod loader;
mod types;
pub use load::*;
pub use loader::*;
pub use types::*;
pub mod prelude {
use super::*;
pub extern crate anyhow;
pub extern crate async_std;
pub use async_trait::async_trait;
pub use self::types::*;
pub use crate::tcp::Client;
}

View File

@ -1,20 +1,16 @@
use std::{any::Any, sync::Arc};
use std::any::Any;
use async_trait::async_trait;
use crate::tcp::Client;
use crate::{plugins::manager::PluginsManager, tcp::Client};
/// Custom Result alias, imported from [anyhow::Result].
pub type Result<T> = anyhow::Result<T>;
/// A plugin wich allows you to add extra functionality.
// A main plugin trait.
#[async_trait]
pub trait Plugin: Any + Send + Sync {
/// Name of the plugin.
fn name(&self) -> &'static str;
/// A function will be executed when plugin loading.
/// Usally used for initialization.
async fn on_plugin_load(&self);
/// A function that will be executed when the plugin is loaded.
async fn on_load(&self);
}
/// Add a command to the plugin.
@ -22,76 +18,52 @@ pub trait Plugin: Any + Send + Sync {
pub trait Command: Any + Send + Sync {
/// Name of the command.
fn name(&self) -> &'static str;
/// Aliases for the command.
fn aliases(&self) -> Vec<&'static str>;
/// Help message of the command.
fn help(&self) -> &'static str;
/// Command function
async fn execute(
&self,
client: &mut Client,
args: Vec<&str>,
plugin_manager: &PluginManagerType,
) -> Result<()>;
/// Usage message of the command.
fn usage(&self) -> &'static str;
/// Command function.
async fn execute(&self, client: &Client, args: Vec<&str>) -> anyhow::Result<()>;
}
/// Add a new function that will be executed when the event occurs.
/// All possible to run events.
pub enum EventType {
/// On client connected.
OnConnect,
/// On client sent message.
OnSend,
}
/// Add a event to the plugin.
#[async_trait]
pub trait Event: Any + Send + Sync {
/// Event name (onConnect or onSend)
fn name(&self) -> &'static str;
/// Event function
async fn execute(&self, client: &mut Client) -> Result<()>;
/// Type of the event.
fn event(&self) -> EventType;
/// Event function.
async fn execute(&self) -> anyhow::Result<()>;
}
/// Plugin Manager with all plugins features.
pub struct PluginManager {
/// Array with loaded plugins.
pub plugins: Vec<Box<dyn Plugin>>,
/// Array with all commands.
pub commands: Vec<Box<dyn Command>>,
/// Array with all events.
pub events: Vec<Box<dyn Event>>,
}
impl PluginManager {
/// Create an empty [PluginManager]
pub fn new() -> Self {
Self {
plugins: Vec::new(),
commands: Vec::new(),
events: Vec::new(),
}
}
}
impl Default for PluginManager {
fn default() -> Self {
Self::new()
}
}
/// Arc type of the [PluginManager].
pub type PluginManagerType = Arc<PluginManager>;
/// Plugin Registrar
pub trait Registrar {
/// Function to register the plugin
fn register_plugin(&mut self, plugin: Box<dyn Plugin>);
/// Function to register the command
fn register_command(&mut self, command: Box<dyn Command>);
/// Function to register the event
fn register_event(&mut self, event: Box<dyn Event>);
/// Function to register plugins.
fn register_plugins(&mut self, plugin: Box<dyn Plugin>);
/// Function to register commands.
fn register_commands(&mut self, command: Box<dyn Command>);
/// Function to register events.
fn register_events(&mut self, event: Box<dyn Event>);
}
impl Registrar for PluginManager {
fn register_plugin(&mut self, plugin: Box<dyn Plugin>) {
impl Registrar for PluginsManager {
fn register_plugins(&mut self, plugin: Box<dyn Plugin>) {
self.plugins.push(plugin)
}
fn register_command(&mut self, command: Box<dyn Command>) {
fn register_commands(&mut self, command: Box<dyn Command>) {
self.commands.push(command)
}
fn register_event(&mut self, event: Box<dyn Event>) {
fn register_events(&mut self, event: Box<dyn Event>) {
self.events.push(event)
}
}

View File

@ -1,61 +1,166 @@
use core::fmt;
use std::net::SocketAddr;
use tokio::{
io::{self, AsyncReadExt, AsyncWriteExt},
net::TcpStream,
use std::{
collections::HashMap,
fmt,
io::{Read, Write},
net::{Shutdown, SocketAddr, TcpStream},
sync::{Arc, Mutex},
};
/// Max size of a TCP packet
use tungstenite::{accept, Message, WebSocket};
use super::server::PLUGINS_MANAGER;
use crate::plugins::manager::PluginsManagerType;
/// Max length of a TCP and UDP packet
pub const MAX_PACKET_LEN: usize = 65536;
/// TCP Client
#[derive(Debug, Clone)]
pub struct Client {
/// TCP stream of this client
pub stream: TcpStream,
pub stream: ClientStream,
pub map: HashMap<String, ClientMapValue>,
pub plugins_manager: PluginsManagerType,
}
#[derive(Debug, Clone)]
pub enum ClientMapValue {
/// String type
String(String),
/// Vector with String type
Array(Vec<String>),
/// bool type
Bool(bool),
/// isize type
Int(isize),
/// usize type
UInt(usize),
}
/// Connection stream of the client
#[derive(Debug, Clone)]
pub enum ClientStream {
/// TCP stream
TCP(Arc<TcpStream>),
/// WebSocket stream
WebSocket(Arc<Mutex<WebSocket<TcpStream>>>),
}
impl From<TcpStream> for Client {
fn from(stream: TcpStream) -> Self {
Self {
stream: ClientStream::TCP(Arc::new(stream)),
map: HashMap::new(),
plugins_manager: PLUGINS_MANAGER.clone(),
}
}
}
impl From<WebSocket<TcpStream>> for Client {
fn from(stream: WebSocket<TcpStream>) -> Self {
Self {
stream: ClientStream::WebSocket(Arc::new(Mutex::new(stream))),
map: HashMap::new(),
plugins_manager: PLUGINS_MANAGER.clone(),
}
}
}
impl Client {
/// Create new Client
pub fn new(stream: TcpStream) -> Self {
Self { stream }
/// Create a new TCP Client instance
pub fn new_tcp(stream: TcpStream) -> Self {
Self::from(stream)
}
/// Read message/buffer from the client
pub async fn read(&mut self) -> anyhow::Result<String> {
// allocate an empty buffer
let mut buf = [0; MAX_PACKET_LEN];
/// Create a new WebSocket Client instance
pub fn new_websocket(stream: TcpStream) -> anyhow::Result<Self> {
let websocket = accept(stream)?;
// read buffer from stream
let len = self.stream.read(&mut buf).await?;
Ok(Self::from(websocket))
}
// select only used bytes from the buffer
let buf = &buf[0..len];
/// Recieve a message from the client
pub fn read(&self) -> anyhow::Result<String> {
// read the message from the stream
let mut msg = match &self.stream {
ClientStream::TCP(stream) => {
// allocate an empty buffer
let mut buf = [0; MAX_PACKET_LEN];
// encode buffer (&[u8]) to a String
let mut decoded = String::from_utf8(buf.to_vec())?;
// read the message and get length of it
let len = stream.as_ref().read(&mut buf)?;
// select only used bytes in the buffer
let buf = &buf[0..len];
// decode buffer (&[u8]) to a String
String::from_utf8(buf.to_vec())?
}
ClientStream::WebSocket(stream) => {
// read the message from the stream
let msg = stream.lock().unwrap().read_message()?;
// decode message to a String
msg.to_string()
}
};
// remove new line characters
while decoded.ends_with('\n') || decoded.ends_with('\r') {
decoded.pop();
while msg.ends_with('\n') || msg.ends_with('\r') {
msg.pop();
}
Ok(decoded)
Ok(msg)
}
/// Send message to the client
pub async fn send<S>(&mut self, content: S) -> io::Result<()>
/// Send a message to the client
pub fn send<S>(&self, msg: S) -> anyhow::Result<()>
where
S: ToString + fmt::Display,
S: ToString,
S: fmt::Display,
{
// add a new line at the end of the content
let content = format!("{content}\n\r");
// convert the message into a string
let msg = msg.to_string();
// send message
self.stream.write_all(content.as_bytes()).await
// convert the message into bytes to send it
let buf = msg.as_bytes();
// send the message
match &self.stream {
ClientStream::TCP(stream) => stream.as_ref().write_all(buf)?,
ClientStream::WebSocket(stream) => {
stream.lock().unwrap().write_message(Message::from(msg))?
}
}
Ok(())
}
pub fn peer_addr(&self) -> io::Result<SocketAddr> {
self.stream.peer_addr()
/// Returns the socket address of the remote peer of this connection.
pub fn peer_addr(&self) -> anyhow::Result<SocketAddr> {
let addr = match &self.stream {
ClientStream::TCP(stream) => stream.peer_addr(),
ClientStream::WebSocket(stream) => stream.lock().unwrap().get_ref().peer_addr(),
}?;
Ok(addr)
}
/// Flush this output stream, ensuring that all intermediately buffered contents reach their destination.
pub fn flush(&self) -> anyhow::Result<()> {
match &self.stream {
ClientStream::TCP(stream) => stream.as_ref().flush()?,
ClientStream::WebSocket(_stream) => {}
}
Ok(())
}
/// Close the connection
pub fn close(&self) -> anyhow::Result<()> {
match &self.stream {
ClientStream::TCP(stream) => stream.shutdown(Shutdown::Both)?,
ClientStream::WebSocket(stream) => stream.lock().unwrap().close(None)?,
}
Ok(())
}
}

View File

@ -1,97 +0,0 @@
use tokio::io::AsyncWriteExt;
use tracing::{error, info, trace};
use crate::plugins::PluginManagerType;
use super::Client;
/// Handle Client connection
pub async fn handle_connection(
mut client: Client,
plugin_manager: PluginManagerType,
) -> anyhow::Result<()> {
info!("New Client: {}", client.peer_addr()?);
// run `onConnect` events from plugins
check_event(&mut client, &plugin_manager, "onConnect").await?;
loop {
// read client message/buffer
let buf = client.read().await?;
// run `onSend` events from plugins
check_event(&mut client, &plugin_manager, "onSend").await?;
// split the message by whitespaces and collect it into Vec<&str>
let mut args = buf.split_ascii_whitespace().collect::<Vec<&str>>();
// client sent an empty buffer
if args.is_empty() {
client.send("empty buffer").await?;
// don't execute the following commands because it causes panic
continue;
}
// get command from args
let cmd = args[0];
// remove command name from args
args = args[1..args.len()].to_vec();
// search if a command exists
for command in plugin_manager.commands.iter() {
// if this is the entered command
if cmd == command.name() {
trace!("Executing a command `{}`", command.name());
// execute command
match command.execute(&mut client, args, &plugin_manager).await {
Ok(_) => (),
Err(err) => {
error!("failed to execute command `{cmd}`, error message = `{err}`");
client.send(format!("error: {err}")).await?;
}
}
// don't search for more commands
break;
}
}
// if an I/O or EOF error, abort the connection
if client.stream.flush().await.is_err() {
// terminate connection
break;
}
}
Ok(())
}
/// Search for a events and execute it
async fn check_event(
client: &mut Client,
events: &PluginManagerType,
event_name: &str,
) -> anyhow::Result<()> {
for event in events.events.iter() {
// check if this event should be started
if event.name() == event_name {
trace!("Executing a event `{}`", event.name());
// execute event
match event.execute(client).await {
Ok(_) => (),
Err(err) => {
error!("failed to execute event `{event_name}`, error message = `{err}`");
client.send(format!("error: {err}")).await?;
}
}
}
}
Ok(())
}

View File

@ -1,85 +0,0 @@
#![allow(clippy::unused_io_amount)]
use futures_util::{
stream::{SplitSink, SplitStream},
SinkExt, StreamExt,
};
use tokio::{
io::{AsyncReadExt, AsyncWriteExt},
net::{
tcp::{OwnedReadHalf, OwnedWriteHalf},
TcpStream,
},
};
use tokio_tungstenite::WebSocketStream;
use tracing::info;
use tungstenite::Message;
use super::MAX_PACKET_LEN;
/// Handle WebSocket connection
pub async fn handle_websocket(stream: TcpStream, tcp_port: String) -> anyhow::Result<()> {
info!("New WebSocket Client: {}", stream.peer_addr()?);
// accept connection as WebSocket
let ws_stream = tokio_tungstenite::accept_async(stream).await?;
// connect to Tcp server
let tcp_stream = TcpStream::connect(format!("0.0.0.0:{}", tcp_port)).await?;
// split streams
let (tcp_read, tcp_write) = tcp_stream.into_split();
let (ws_write, ws_read) = ws_stream.split();
// tcp read -> ws write
tokio::spawn(tcp_to_ws(tcp_read, ws_write));
// ws read -> tcp write
ws_to_tcp(tcp_write, ws_read).await?;
Ok(())
}
/// Tcp read -> WebSocket write
async fn tcp_to_ws(
mut tcp_read: OwnedReadHalf,
mut ws_write: SplitSink<WebSocketStream<TcpStream>, Message>,
) -> anyhow::Result<()> {
// allocate an empty buffer
let mut buf = [0; MAX_PACKET_LEN];
loop {
// read buffer from tcp
let len = tcp_read.read(&mut buf).await?;
if len > 0 {
// select only used bytes from the buffer
let recv_buf = &buf[0..len];
// covert &[u8] buffer to a vector
let recv_vec = recv_buf.to_vec();
// create a `Message` type from buffer Vec<u8>
let msg = Message::Binary(recv_vec);
// write buffer to websocket
ws_write.send(msg).await?;
}
}
}
/// WebSocket read -> Tcp write
async fn ws_to_tcp(
mut tcp_write: OwnedWriteHalf,
mut ws_read: SplitStream<WebSocketStream<TcpStream>>,
) -> anyhow::Result<()> {
while let Some(msg) = ws_read.next().await {
// handle error in the message
let msg = msg?;
// create a buffer from a message
let buf = msg.into_data();
// write buffer to tcp
tcp_write.write(&buf).await?;
}
Ok(())
}

View File

@ -1,9 +1,4 @@
//! TCP connection utils
mod client;
mod handle_connection;
mod handle_websocket;
pub mod server;
pub use client::*;
pub use handle_connection::*;
pub use handle_websocket::*;

139
src/tcp/server.rs Normal file
View File

@ -0,0 +1,139 @@
use std::net::TcpListener;
use async_std::task;
use futures::join;
use lazy_static::lazy_static;
use tracing::{error, info};
use crate::{
plugins::{self, manager::PluginsManagerType},
tcp::Client,
CLIENTS, CLIENT_NEXT,
};
pub const PLUGINS_DIR: &str = "plugins";
lazy_static! {
pub static ref PLUGINS_MANAGER: PluginsManagerType =
plugins::loader(PLUGINS_DIR).expect("failed to load plugins");
}
/// Start server
pub fn run(tcp_host: String, ws_host: String) -> anyhow::Result<()> {
info!("Loaded {} plugins", PLUGINS_MANAGER.plugins.len());
info!("Loaded {} commands", PLUGINS_MANAGER.commands.len());
info!("Loaded {} events", PLUGINS_MANAGER.events.len());
let tcp_child = task::spawn(async move {
start_tcp(tcp_host).await.unwrap();
});
let ws_child = task::spawn(async move {
start_websocket(ws_host).await.unwrap();
});
task::block_on(async {
join!(tcp_child, ws_child);
});
Ok(())
}
/// Process client connection
async fn process(client: Client) -> anyhow::Result<()> {
let client_addr = client.peer_addr()?;
info!("Processing client connection: {}", client_addr);
loop {
let buf = client.read()?;
let mut args: Vec<&str> = buf.split_ascii_whitespace().collect();
// if client sent an empty buffer
if buf.is_empty() {
client.send("empty buffer")?;
continue;
}
let cmd = args[0];
// remove command name from args
args = args[1..args.len()].to_vec();
// find command
let command = client
.plugins_manager
.commands
.iter()
.enumerate()
.find(|&(_i, command)| command.name() == cmd || command.aliases().contains(&cmd));
// execute command
if let Some((_i, cmd)) = command {
cmd.execute(&client, args).await?;
} else {
client.send("unknown command")?;
}
client.flush()?;
}
}
async fn start_tcp(host: String) -> anyhow::Result<()> {
let listener = TcpListener::bind(host)?;
let incoming = listener.incoming();
for stream in incoming {
let stream = stream?;
task::spawn(async {
let client = Client::new_tcp(stream);
// get id for the client and add one to next id
let id = (*CLIENT_NEXT.lock().unwrap() + 1).clone();
// insert the cloned client to CLIENTS
CLIENTS.lock().unwrap().insert(id, client.clone());
if let Err(err) = process(client).await {
error!("TCP client error: {}", err);
}
// delete the client from CLIENTS map
CLIENTS.lock().unwrap().remove(&id);
});
}
Ok(())
}
async fn start_websocket(host: String) -> anyhow::Result<()> {
let listener = TcpListener::bind(host)?;
let incoming = listener.incoming();
for stream in incoming {
let stream = stream?;
task::spawn(async {
let client = Client::new_websocket(stream).unwrap();
// get id for the client and add one to next id
let id = (*CLIENT_NEXT.lock().unwrap() + 1).clone();
// insert the cloned client to CLIENTS
CLIENTS.lock().unwrap().insert(id, client.clone());
if let Err(err) = process(client).await {
error!("TCP client error: {}", err);
}
// delete the client from CLIENTS map
CLIENTS.lock().unwrap().remove(&id);
});
}
Ok(())
}