Merge pull request #1 from jasonwitty/feature/wss-selfsigned
SSL Support
This commit is contained in:
commit
d346c61c28
668
Cargo.lock
generated
668
Cargo.lock
generated
@ -47,12 +47,40 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle"
|
||||||
|
version = "1.0.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anyhow"
|
name = "anyhow"
|
||||||
version = "1.0.98"
|
version = "1.0.98"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
|
checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "arc-swap"
|
||||||
|
version = "1.7.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "assert_cmd"
|
||||||
|
version = "2.0.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2bd389a4b2970a01282ee455294913c0a43724daedcd1a24c3eb0ec1c1320b66"
|
||||||
|
dependencies = [
|
||||||
|
"anstyle",
|
||||||
|
"bstr",
|
||||||
|
"doc-comment",
|
||||||
|
"libc",
|
||||||
|
"predicates",
|
||||||
|
"predicates-core",
|
||||||
|
"predicates-tree",
|
||||||
|
"wait-timeout",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-trait"
|
name = "async-trait"
|
||||||
version = "0.1.88"
|
version = "0.1.88"
|
||||||
@ -64,12 +92,41 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "atomic-waker"
|
||||||
|
version = "1.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.5.0"
|
version = "1.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aws-lc-rs"
|
||||||
|
version = "1.13.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5c953fe1ba023e6b7730c0d4b031d06f267f23a46167dcbd40316644b10a17ba"
|
||||||
|
dependencies = [
|
||||||
|
"aws-lc-sys",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aws-lc-sys"
|
||||||
|
version = "0.30.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dbfd150b5dbdb988bcc8fb1fe787eb6b7ee6180ca24da683b61ea5405f3d43ff"
|
||||||
|
dependencies = [
|
||||||
|
"bindgen",
|
||||||
|
"cc",
|
||||||
|
"cmake",
|
||||||
|
"dunce",
|
||||||
|
"fs_extra",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "axum"
|
name = "axum"
|
||||||
version = "0.7.9"
|
version = "0.7.9"
|
||||||
@ -102,7 +159,7 @@ dependencies = [
|
|||||||
"sync_wrapper",
|
"sync_wrapper",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-tungstenite",
|
"tokio-tungstenite",
|
||||||
"tower",
|
"tower 0.5.2",
|
||||||
"tower-layer",
|
"tower-layer",
|
||||||
"tower-service",
|
"tower-service",
|
||||||
"tracing",
|
"tracing",
|
||||||
@ -140,6 +197,29 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "axum-server"
|
||||||
|
version = "0.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c1ad46c3ec4e12f4a4b6835e173ba21c25e484c9d02b49770bf006ce5367c036"
|
||||||
|
dependencies = [
|
||||||
|
"arc-swap",
|
||||||
|
"bytes",
|
||||||
|
"futures-util",
|
||||||
|
"http",
|
||||||
|
"http-body",
|
||||||
|
"http-body-util",
|
||||||
|
"hyper",
|
||||||
|
"hyper-util",
|
||||||
|
"pin-project-lite",
|
||||||
|
"rustls 0.21.12",
|
||||||
|
"rustls-pemfile",
|
||||||
|
"tokio",
|
||||||
|
"tokio-rustls 0.24.1",
|
||||||
|
"tower 0.4.13",
|
||||||
|
"tower-service",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "backtrace"
|
name = "backtrace"
|
||||||
version = "0.3.75"
|
version = "0.3.75"
|
||||||
@ -161,6 +241,29 @@ version = "0.22.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bindgen"
|
||||||
|
version = "0.69.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"cexpr",
|
||||||
|
"clang-sys",
|
||||||
|
"itertools 0.12.1",
|
||||||
|
"lazy_static",
|
||||||
|
"lazycell",
|
||||||
|
"log",
|
||||||
|
"prettyplease",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"regex",
|
||||||
|
"rustc-hash",
|
||||||
|
"shlex",
|
||||||
|
"syn",
|
||||||
|
"which",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "2.9.1"
|
version = "2.9.1"
|
||||||
@ -176,6 +279,17 @@ dependencies = [
|
|||||||
"generic-array",
|
"generic-array",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bstr"
|
||||||
|
version = "1.12.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
"regex-automata 0.4.9",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bumpalo"
|
name = "bumpalo"
|
||||||
version = "3.19.0"
|
version = "3.19.0"
|
||||||
@ -215,9 +329,20 @@ version = "1.2.31"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c3a42d84bb6b69d3a8b3eaacf0d88f179e1929695e1ad012b6cf64d9caaa5fd2"
|
checksum = "c3a42d84bb6b69d3a8b3eaacf0d88f179e1929695e1ad012b6cf64d9caaa5fd2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"jobserver",
|
||||||
|
"libc",
|
||||||
"shlex",
|
"shlex",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cexpr"
|
||||||
|
version = "0.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
|
||||||
|
dependencies = [
|
||||||
|
"nom",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
@ -239,6 +364,26 @@ dependencies = [
|
|||||||
"windows-link",
|
"windows-link",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clang-sys"
|
||||||
|
version = "1.8.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4"
|
||||||
|
dependencies = [
|
||||||
|
"glob",
|
||||||
|
"libc",
|
||||||
|
"libloading",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cmake"
|
||||||
|
version = "0.1.54"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "compact_str"
|
name = "compact_str"
|
||||||
version = "0.8.1"
|
version = "0.8.1"
|
||||||
@ -313,7 +458,7 @@ dependencies = [
|
|||||||
"crossterm_winapi",
|
"crossterm_winapi",
|
||||||
"mio 1.0.4",
|
"mio 1.0.4",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
"rustix",
|
"rustix 0.38.44",
|
||||||
"signal-hook",
|
"signal-hook",
|
||||||
"signal-hook-mio",
|
"signal-hook-mio",
|
||||||
"winapi",
|
"winapi",
|
||||||
@ -379,6 +524,12 @@ version = "2.9.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476"
|
checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "difflib"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "digest"
|
name = "digest"
|
||||||
version = "0.10.7"
|
version = "0.10.7"
|
||||||
@ -400,6 +551,18 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "doc-comment"
|
||||||
|
version = "0.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dunce"
|
||||||
|
version = "1.0.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "either"
|
name = "either"
|
||||||
version = "1.15.0"
|
version = "1.15.0"
|
||||||
@ -422,6 +585,12 @@ dependencies = [
|
|||||||
"windows-sys 0.60.2",
|
"windows-sys 0.60.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fastrand"
|
||||||
|
version = "2.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "flate2"
|
name = "flate2"
|
||||||
version = "1.1.2"
|
version = "1.1.2"
|
||||||
@ -444,6 +613,21 @@ version = "0.1.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
|
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "foreign-types"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
|
||||||
|
dependencies = [
|
||||||
|
"foreign-types-shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "foreign-types-shared"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "form_urlencoded"
|
name = "form_urlencoded"
|
||||||
version = "1.2.1"
|
version = "1.2.1"
|
||||||
@ -453,6 +637,12 @@ dependencies = [
|
|||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fs_extra"
|
||||||
|
version = "1.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures"
|
name = "futures"
|
||||||
version = "0.3.31"
|
version = "0.3.31"
|
||||||
@ -596,6 +786,31 @@ version = "0.31.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
|
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "glob"
|
||||||
|
version = "0.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "h2"
|
||||||
|
version = "0.4.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386"
|
||||||
|
dependencies = [
|
||||||
|
"atomic-waker",
|
||||||
|
"bytes",
|
||||||
|
"fnv",
|
||||||
|
"futures-core",
|
||||||
|
"futures-sink",
|
||||||
|
"http",
|
||||||
|
"indexmap",
|
||||||
|
"slab",
|
||||||
|
"tokio",
|
||||||
|
"tokio-util",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.15.4"
|
version = "0.15.4"
|
||||||
@ -613,6 +828,26 @@ version = "0.5.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "home"
|
||||||
|
version = "0.5.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf"
|
||||||
|
dependencies = [
|
||||||
|
"windows-sys 0.59.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hostname"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"match_cfg",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "http"
|
name = "http"
|
||||||
version = "1.3.1"
|
version = "1.3.1"
|
||||||
@ -668,6 +903,7 @@ dependencies = [
|
|||||||
"bytes",
|
"bytes",
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
|
"h2",
|
||||||
"http",
|
"http",
|
||||||
"http-body",
|
"http-body",
|
||||||
"httparse",
|
"httparse",
|
||||||
@ -831,6 +1067,16 @@ dependencies = [
|
|||||||
"icu_properties",
|
"icu_properties",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "indexmap"
|
||||||
|
version = "2.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661"
|
||||||
|
dependencies = [
|
||||||
|
"equivalent",
|
||||||
|
"hashbrown",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "indoc"
|
name = "indoc"
|
||||||
version = "2.0.6"
|
version = "2.0.6"
|
||||||
@ -871,6 +1117,15 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itertools"
|
||||||
|
version = "0.12.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itertools"
|
name = "itertools"
|
||||||
version = "0.13.0"
|
version = "0.13.0"
|
||||||
@ -886,6 +1141,16 @@ version = "1.0.15"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jobserver"
|
||||||
|
version = "0.1.33"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom 0.3.3",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "js-sys"
|
name = "js-sys"
|
||||||
version = "0.3.77"
|
version = "0.3.77"
|
||||||
@ -902,6 +1167,12 @@ version = "1.5.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lazycell"
|
||||||
|
version = "1.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.174"
|
version = "0.2.174"
|
||||||
@ -933,6 +1204,12 @@ version = "0.4.15"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
|
checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "linux-raw-sys"
|
||||||
|
version = "0.9.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "litemap"
|
name = "litemap"
|
||||||
version = "0.8.0"
|
version = "0.8.0"
|
||||||
@ -973,6 +1250,12 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "match_cfg"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "matchers"
|
name = "matchers"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@ -1000,6 +1283,12 @@ version = "0.3.17"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
|
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "minimal-lexical"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "miniz_oxide"
|
name = "miniz_oxide"
|
||||||
version = "0.8.9"
|
version = "0.8.9"
|
||||||
@ -1033,6 +1322,16 @@ dependencies = [
|
|||||||
"windows-sys 0.59.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nom"
|
||||||
|
version = "7.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
"minimal-lexical",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ntapi"
|
name = "ntapi"
|
||||||
version = "0.4.1"
|
version = "0.4.1"
|
||||||
@ -1118,6 +1417,54 @@ version = "1.21.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "openssl"
|
||||||
|
version = "0.10.73"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"cfg-if",
|
||||||
|
"foreign-types",
|
||||||
|
"libc",
|
||||||
|
"once_cell",
|
||||||
|
"openssl-macros",
|
||||||
|
"openssl-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "openssl-macros"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "openssl-src"
|
||||||
|
version = "300.5.2+3.5.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d270b79e2926f5150189d475bc7e9d2c69f9c4697b185fa917d5a32b792d21b4"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "openssl-sys"
|
||||||
|
version = "0.9.109"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"libc",
|
||||||
|
"openssl-src",
|
||||||
|
"pkg-config",
|
||||||
|
"vcpkg",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "overload"
|
name = "overload"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
@ -1159,6 +1506,26 @@ version = "2.3.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pin-project"
|
||||||
|
version = "1.1.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a"
|
||||||
|
dependencies = [
|
||||||
|
"pin-project-internal",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pin-project-internal"
|
||||||
|
version = "1.1.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pin-project-lite"
|
name = "pin-project-lite"
|
||||||
version = "0.2.16"
|
version = "0.2.16"
|
||||||
@ -1171,6 +1538,12 @@ version = "0.1.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pkg-config"
|
||||||
|
version = "0.3.32"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "potential_utf"
|
name = "potential_utf"
|
||||||
version = "0.1.2"
|
version = "0.1.2"
|
||||||
@ -1189,6 +1562,43 @@ dependencies = [
|
|||||||
"zerocopy",
|
"zerocopy",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "predicates"
|
||||||
|
version = "3.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a5d19ee57562043d37e82899fade9a22ebab7be9cef5026b07fda9cdd4293573"
|
||||||
|
dependencies = [
|
||||||
|
"anstyle",
|
||||||
|
"difflib",
|
||||||
|
"predicates-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "predicates-core"
|
||||||
|
version = "1.0.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "727e462b119fe9c93fd0eb1429a5f7647394014cf3c04ab2c0350eeb09095ffa"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "predicates-tree"
|
||||||
|
version = "1.0.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "72dd2d6d381dfb73a193c7fca536518d7caee39fc8503f74e7dc0be0531b425c"
|
||||||
|
dependencies = [
|
||||||
|
"predicates-core",
|
||||||
|
"termtree",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "prettyplease"
|
||||||
|
version = "0.2.36"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ff24dfcda44452b9816fff4cd4227e1bb73ff5a2f1bc1105aa92fb8565ce44d2"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.95"
|
version = "1.0.95"
|
||||||
@ -1283,7 +1693,7 @@ dependencies = [
|
|||||||
"compact_str",
|
"compact_str",
|
||||||
"crossterm 0.28.1",
|
"crossterm 0.28.1",
|
||||||
"instability",
|
"instability",
|
||||||
"itertools",
|
"itertools 0.13.0",
|
||||||
"lru",
|
"lru",
|
||||||
"paste",
|
"paste",
|
||||||
"strum",
|
"strum",
|
||||||
@ -1346,12 +1756,32 @@ version = "0.8.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ring"
|
||||||
|
version = "0.17.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"cfg-if",
|
||||||
|
"getrandom 0.2.16",
|
||||||
|
"libc",
|
||||||
|
"untrusted",
|
||||||
|
"windows-sys 0.52.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc-demangle"
|
name = "rustc-demangle"
|
||||||
version = "0.1.26"
|
version = "0.1.26"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace"
|
checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustc-hash"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustix"
|
name = "rustix"
|
||||||
version = "0.38.44"
|
version = "0.38.44"
|
||||||
@ -1361,10 +1791,90 @@ dependencies = [
|
|||||||
"bitflags",
|
"bitflags",
|
||||||
"errno",
|
"errno",
|
||||||
"libc",
|
"libc",
|
||||||
"linux-raw-sys",
|
"linux-raw-sys 0.4.15",
|
||||||
"windows-sys 0.59.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustix"
|
||||||
|
version = "1.0.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"errno",
|
||||||
|
"libc",
|
||||||
|
"linux-raw-sys 0.9.4",
|
||||||
|
"windows-sys 0.60.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustls"
|
||||||
|
version = "0.21.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
"ring",
|
||||||
|
"rustls-webpki 0.101.7",
|
||||||
|
"sct",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustls"
|
||||||
|
version = "0.23.31"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c0ebcbd2f03de0fc1122ad9bb24b127a5a6cd51d72604a3f3c50ac459762b6cc"
|
||||||
|
dependencies = [
|
||||||
|
"aws-lc-rs",
|
||||||
|
"log",
|
||||||
|
"once_cell",
|
||||||
|
"rustls-pki-types",
|
||||||
|
"rustls-webpki 0.103.4",
|
||||||
|
"subtle",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustls-pemfile"
|
||||||
|
version = "2.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50"
|
||||||
|
dependencies = [
|
||||||
|
"rustls-pki-types",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustls-pki-types"
|
||||||
|
version = "1.12.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79"
|
||||||
|
dependencies = [
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustls-webpki"
|
||||||
|
version = "0.101.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765"
|
||||||
|
dependencies = [
|
||||||
|
"ring",
|
||||||
|
"untrusted",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustls-webpki"
|
||||||
|
version = "0.103.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc"
|
||||||
|
dependencies = [
|
||||||
|
"aws-lc-rs",
|
||||||
|
"ring",
|
||||||
|
"rustls-pki-types",
|
||||||
|
"untrusted",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustversion"
|
name = "rustversion"
|
||||||
version = "1.0.21"
|
version = "1.0.21"
|
||||||
@ -1383,6 +1893,16 @@ version = "1.2.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sct"
|
||||||
|
version = "0.7.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414"
|
||||||
|
dependencies = [
|
||||||
|
"ring",
|
||||||
|
"untrusted",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.219"
|
version = "1.0.219"
|
||||||
@ -1521,17 +2041,19 @@ name = "socktop"
|
|||||||
version = "0.1.11"
|
version = "0.1.11"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
"assert_cmd",
|
||||||
"chrono",
|
"chrono",
|
||||||
"crossterm 0.27.0",
|
"crossterm 0.27.0",
|
||||||
"flate2",
|
"flate2",
|
||||||
"futures",
|
"futures",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"ratatui",
|
"ratatui",
|
||||||
|
"rustls 0.23.31",
|
||||||
|
"rustls-pemfile",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-tungstenite",
|
"tokio-tungstenite",
|
||||||
"tungstenite 0.27.0",
|
|
||||||
"url",
|
"url",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -1539,16 +2061,24 @@ dependencies = [
|
|||||||
name = "socktop_agent"
|
name = "socktop_agent"
|
||||||
version = "0.1.11"
|
version = "0.1.11"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"assert_cmd",
|
||||||
"axum",
|
"axum",
|
||||||
|
"axum-server",
|
||||||
"flate2",
|
"flate2",
|
||||||
"futures",
|
"futures",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"gfxinfo",
|
"gfxinfo",
|
||||||
|
"hostname",
|
||||||
"nvml-wrapper",
|
"nvml-wrapper",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
|
"openssl",
|
||||||
|
"rustls 0.23.31",
|
||||||
|
"rustls-pemfile",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sysinfo",
|
"sysinfo",
|
||||||
|
"tempfile",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
@ -1595,6 +2125,12 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "subtle"
|
||||||
|
version = "2.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.104"
|
version = "2.0.104"
|
||||||
@ -1637,6 +2173,25 @@ dependencies = [
|
|||||||
"windows 0.61.3",
|
"windows 0.61.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tempfile"
|
||||||
|
version = "3.20.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1"
|
||||||
|
dependencies = [
|
||||||
|
"fastrand",
|
||||||
|
"getrandom 0.3.3",
|
||||||
|
"once_cell",
|
||||||
|
"rustix 1.0.8",
|
||||||
|
"windows-sys 0.59.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "termtree"
|
||||||
|
version = "0.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.69"
|
version = "1.0.69"
|
||||||
@ -1727,6 +2282,26 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-rustls"
|
||||||
|
version = "0.24.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081"
|
||||||
|
dependencies = [
|
||||||
|
"rustls 0.21.12",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-rustls"
|
||||||
|
version = "0.26.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b"
|
||||||
|
dependencies = [
|
||||||
|
"rustls 0.23.31",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-tungstenite"
|
name = "tokio-tungstenite"
|
||||||
version = "0.24.0"
|
version = "0.24.0"
|
||||||
@ -1735,10 +2310,41 @@ checksum = "edc5f74e248dc973e0dbb7b74c7e0d6fcc301c694ff50049504004ef4d0cdcd9"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"log",
|
"log",
|
||||||
|
"rustls 0.23.31",
|
||||||
|
"rustls-pki-types",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"tokio-rustls 0.26.2",
|
||||||
"tungstenite 0.24.0",
|
"tungstenite 0.24.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-util"
|
||||||
|
version = "0.7.16"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"futures-core",
|
||||||
|
"futures-sink",
|
||||||
|
"pin-project-lite",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tower"
|
||||||
|
version = "0.4.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core",
|
||||||
|
"futures-util",
|
||||||
|
"pin-project",
|
||||||
|
"pin-project-lite",
|
||||||
|
"tower-layer",
|
||||||
|
"tower-service",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tower"
|
name = "tower"
|
||||||
version = "0.5.2"
|
version = "0.5.2"
|
||||||
@ -1842,6 +2448,8 @@ dependencies = [
|
|||||||
"httparse",
|
"httparse",
|
||||||
"log",
|
"log",
|
||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
|
"rustls 0.23.31",
|
||||||
|
"rustls-pki-types",
|
||||||
"sha1",
|
"sha1",
|
||||||
"thiserror 1.0.69",
|
"thiserror 1.0.69",
|
||||||
"utf-8",
|
"utf-8",
|
||||||
@ -1888,7 +2496,7 @@ version = "1.1.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b3644627a5af5fa321c95b9b235a72fd24cd29c648c2c379431e6628655627bf"
|
checksum = "b3644627a5af5fa321c95b9b235a72fd24cd29c648c2c379431e6628655627bf"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itertools",
|
"itertools 0.13.0",
|
||||||
"unicode-segmentation",
|
"unicode-segmentation",
|
||||||
"unicode-width",
|
"unicode-width",
|
||||||
]
|
]
|
||||||
@ -1899,6 +2507,12 @@ version = "0.1.14"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
|
checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "untrusted"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "url"
|
name = "url"
|
||||||
version = "2.5.4"
|
version = "2.5.4"
|
||||||
@ -1928,12 +2542,27 @@ version = "0.1.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
|
checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "vcpkg"
|
||||||
|
version = "0.2.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "version_check"
|
name = "version_check"
|
||||||
version = "0.9.5"
|
version = "0.9.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wait-timeout"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasi"
|
name = "wasi"
|
||||||
version = "0.11.1+wasi-snapshot-preview1"
|
version = "0.11.1+wasi-snapshot-preview1"
|
||||||
@ -2007,6 +2636,18 @@ dependencies = [
|
|||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "which"
|
||||||
|
version = "4.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
"home",
|
||||||
|
"once_cell",
|
||||||
|
"rustix 0.38.44",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi"
|
name = "winapi"
|
||||||
version = "0.3.9"
|
version = "0.3.9"
|
||||||
@ -2215,6 +2856,15 @@ dependencies = [
|
|||||||
"windows-targets 0.48.5",
|
"windows-targets 0.48.5",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.52.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets 0.52.6",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-sys"
|
name = "windows-sys"
|
||||||
version = "0.59.0"
|
version = "0.59.0"
|
||||||
@ -2535,6 +3185,12 @@ dependencies = [
|
|||||||
"synstructure",
|
"synstructure",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zeroize"
|
||||||
|
version = "1.8.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zerotrie"
|
name = "zerotrie"
|
||||||
version = "0.2.2"
|
version = "0.2.2"
|
||||||
|
|||||||
@ -13,7 +13,7 @@ futures-util = "0.3"
|
|||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
|
|
||||||
# websocket
|
# websocket
|
||||||
tokio-tungstenite = "0.24"
|
tokio-tungstenite = { version = "0.24", features = ["__rustls-tls", "connect"] }
|
||||||
tungstenite = "0.24"
|
tungstenite = "0.24"
|
||||||
url = "2.5"
|
url = "2.5"
|
||||||
|
|
||||||
|
|||||||
67
README.md
67
README.md
@ -12,6 +12,7 @@ socktop is a remote system monitor with a rich TUI, inspired by top/btop, talkin
|
|||||||
## Features
|
## Features
|
||||||
|
|
||||||
- Remote monitoring via WebSocket (JSON over WS)
|
- Remote monitoring via WebSocket (JSON over WS)
|
||||||
|
- Optional WSS (TLS): agent auto‑generates a self‑signed cert on first run; client pins the cert via --tls-ca/-t
|
||||||
- TUI built with ratatui
|
- TUI built with ratatui
|
||||||
- CPU
|
- CPU
|
||||||
- Overall sparkline + per-core mini bars
|
- Overall sparkline + per-core mini bars
|
||||||
@ -67,7 +68,7 @@ Two components:
|
|||||||
|
|
||||||
1) Agent (remote): small Rust WS server using sysinfo + /proc. It collects on demand when the client asks (fast metrics ~500 ms, processes ~2 s, disks ~5 s). No background loop when nobody is connected.
|
1) Agent (remote): small Rust WS server using sysinfo + /proc. It collects on demand when the client asks (fast metrics ~500 ms, processes ~2 s, disks ~5 s). No background loop when nobody is connected.
|
||||||
|
|
||||||
2) Client (local): TUI that connects to ws://HOST:PORT/ws and renders updates.
|
2) Client (local): TUI that connects to ws://HOST:PORT/ws (or wss://HOST:PORT/ws when TLS is enabled) and renders updates.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -95,6 +96,30 @@ cargo build --release
|
|||||||
|
|
||||||
Tip: Add ?token=... if you enable auth (see Security).
|
Tip: Add ?token=... if you enable auth (see Security).
|
||||||
|
|
||||||
|
TLS quick start (optional, recommended on untrusted networks):
|
||||||
|
|
||||||
|
- Start the agent with TLS enabled (default TLS port 8443). On first run it will generate a self‑signed certificate and key under your config directory.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./target/release/socktop_agent --enableSSL --port 8443 # or: -p 8443
|
||||||
|
# First run prints the cert and key paths, e.g.:
|
||||||
|
# socktop_agent: generated self-signed TLS certificate at /home/you/.config/socktop_agent/tls/cert.pem
|
||||||
|
# socktop_agent: private key at /home/you/.config/socktop_agent/tls/key.pem
|
||||||
|
```
|
||||||
|
|
||||||
|
- Copy the certificate file to the client machine (keep the key private on the server):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
scp /home/you/.config/socktop_agent/tls/cert.pem you@client:/tmp/socktop-agent-ca.pem
|
||||||
|
```
|
||||||
|
|
||||||
|
- Connect with the TUI, pinning the server cert:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./target/release/socktop --tls-ca /tmp/socktop-agent-ca.pem wss://REMOTE_HOST:8443/ws
|
||||||
|
# Note: if you pass --tls-ca but use ws://, the client auto-upgrades to wss://
|
||||||
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Install (from crates.io)
|
## Install (from crates.io)
|
||||||
@ -135,6 +160,8 @@ Agent (server):
|
|||||||
socktop_agent --port 3000
|
socktop_agent --port 3000
|
||||||
# or env: SOCKTOP_PORT=3000 socktop_agent
|
# or env: SOCKTOP_PORT=3000 socktop_agent
|
||||||
# optional auth: SOCKTOP_TOKEN=changeme socktop_agent
|
# optional auth: SOCKTOP_TOKEN=changeme socktop_agent
|
||||||
|
# enable TLS (self‑signed cert, default port 8443; you can also use -p):
|
||||||
|
socktop_agent --enableSSL --port 8443
|
||||||
```
|
```
|
||||||
|
|
||||||
Client (TUI):
|
Client (TUI):
|
||||||
@ -143,6 +170,11 @@ Client (TUI):
|
|||||||
socktop ws://HOST:3000/ws
|
socktop ws://HOST:3000/ws
|
||||||
# with token:
|
# with token:
|
||||||
socktop "ws://HOST:3000/ws?token=changeme"
|
socktop "ws://HOST:3000/ws?token=changeme"
|
||||||
|
# TLS with pinned server certificate (recommended over the internet):
|
||||||
|
socktop --tls-ca /path/to/cert.pem wss://HOST:8443/ws
|
||||||
|
# shorthand:
|
||||||
|
socktop -t /path/to/cert.pem wss://HOST:8443/ws
|
||||||
|
# Note: providing --tls-ca/-t automatically upgrades ws:// to wss:// if you forget
|
||||||
```
|
```
|
||||||
|
|
||||||
Intervals (client-driven):
|
Intervals (client-driven):
|
||||||
@ -188,6 +220,13 @@ Tip: If only the binary changed, restart is enough. If the unit file changed, ru
|
|||||||
- Flag: --port 8080 or -p 8080
|
- Flag: --port 8080 or -p 8080
|
||||||
- Positional: socktop_agent 8080
|
- Positional: socktop_agent 8080
|
||||||
- Env: SOCKTOP_PORT=8080
|
- Env: SOCKTOP_PORT=8080
|
||||||
|
- TLS (self‑signed):
|
||||||
|
- Enable: --enableSSL
|
||||||
|
- Default TLS port: 8443 (override with --port/-p)
|
||||||
|
- Certificate/Key location (created on first TLS run):
|
||||||
|
- Linux (XDG): $XDG_CONFIG_HOME/socktop_agent/tls/{cert.pem,key.pem} (defaults to ~/.config)
|
||||||
|
- The agent prints these paths on creation.
|
||||||
|
- You can set XDG_CONFIG_HOME before first run to control where certs are written.
|
||||||
- Auth token (optional): SOCKTOP_TOKEN=changeme
|
- Auth token (optional): SOCKTOP_TOKEN=changeme
|
||||||
- Disable GPU metrics: SOCKTOP_AGENT_GPU=0
|
- Disable GPU metrics: SOCKTOP_AGENT_GPU=0
|
||||||
- Disable CPU temperature: SOCKTOP_AGENT_TEMP=0
|
- Disable CPU temperature: SOCKTOP_AGENT_TEMP=0
|
||||||
@ -250,6 +289,27 @@ Client:
|
|||||||
socktop "ws://HOST:3000/ws?token=changeme"
|
socktop "ws://HOST:3000/ws?token=changeme"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### TLS / WSS
|
||||||
|
|
||||||
|
For encrypted connections, enable TLS on the agent and pin the server certificate on the client.
|
||||||
|
|
||||||
|
Server (generates self‑signed cert and key on first run):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
socktop_agent --enableSSL --port 8443
|
||||||
|
```
|
||||||
|
|
||||||
|
Client (trust/pin the server cert; copy cert.pem from the agent):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
socktop --tls-ca /path/to/agent/cert.pem wss://HOST:8443/ws
|
||||||
|
```
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
- Do not copy the private key off the server; only the cert.pem is needed by clients.
|
||||||
|
- When --tls-ca/-t is supplied, the client auto‑upgrades ws:// to wss:// to avoid protocol mismatch.
|
||||||
|
- You can run multiple clients with different cert paths by passing --tls-ca per invocation.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Using tmux to monitor multiple hosts
|
## Using tmux to monitor multiple hosts
|
||||||
@ -319,7 +379,8 @@ Tips:
|
|||||||
cargo fmt
|
cargo fmt
|
||||||
cargo clippy --all-targets --all-features
|
cargo clippy --all-targets --all-features
|
||||||
cargo run -p socktop -- ws://127.0.0.1:3000/ws
|
cargo run -p socktop -- ws://127.0.0.1:3000/ws
|
||||||
cargo run -p socktop_agent -- --port 3000
|
# TLS (dev): first run will create certs under ~/.config/socktop_agent/tls/
|
||||||
|
cargo run -p socktop_agent -- --enableSSL --port 8443
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
@ -331,7 +392,7 @@ cargo run -p socktop_agent -- --port 3000
|
|||||||
- [x] Sort top processes in the TUI
|
- [x] Sort top processes in the TUI
|
||||||
- [ ] Configurable refresh intervals (client)
|
- [ ] Configurable refresh intervals (client)
|
||||||
- [ ] Export metrics to file
|
- [ ] Export metrics to file
|
||||||
- [ ] TLS / WSS support
|
- [x] TLS / WSS support (self‑signed server cert + client pinning)
|
||||||
- [x] Split processes/disks to separate WS calls with independent cadences (already logical on client; formalize API)
|
- [x] Split processes/disks to separate WS calls with independent cadences (already logical on client; formalize API)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@ -19,4 +19,8 @@ crossterm = { workspace = true }
|
|||||||
chrono = { workspace = true }
|
chrono = { workspace = true }
|
||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
flate2 = { version = "1", default-features = false, features = ["rust_backend"] }
|
flate2 = { version = "1", default-features = false, features = ["rust_backend"] }
|
||||||
tungstenite = "0.27.0"
|
rustls = "0.23"
|
||||||
|
rustls-pemfile = "2.1"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
assert_cmd = "2.0"
|
||||||
@ -98,10 +98,15 @@ impl App {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn run(&mut self, url: &str) -> Result<(), Box<dyn std::error::Error>> {
|
pub async fn run(
|
||||||
|
&mut self,
|
||||||
|
url: &str,
|
||||||
|
tls_ca: Option<&str>,
|
||||||
|
) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
// Connect to agent
|
// Connect to agent
|
||||||
|
//let mut ws = connect(url, tls_ca).await?;
|
||||||
self.ws_url = url.to_string();
|
self.ws_url = url.to_string();
|
||||||
let mut ws = connect(url).await?;
|
let mut ws = connect(url, tls_ca).await?;
|
||||||
|
|
||||||
// Terminal setup
|
// Terminal setup
|
||||||
enable_raw_mode()?;
|
enable_raw_mode()?;
|
||||||
@ -249,10 +254,7 @@ impl App {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw current frame first so the UI never feels blocked
|
// Fetch and update
|
||||||
terminal.draw(|f| self.draw(f))?;
|
|
||||||
|
|
||||||
// Then fetch and update
|
|
||||||
if let Some(m) = request_metrics(ws).await {
|
if let Some(m) = request_metrics(ws).await {
|
||||||
self.update_with_metrics(m);
|
self.update_with_metrics(m);
|
||||||
|
|
||||||
@ -276,13 +278,11 @@ impl App {
|
|||||||
}
|
}
|
||||||
self.last_disks_poll = Instant::now();
|
self.last_disks_poll = Instant::now();
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// If we couldn't get metrics, try to reconnect once
|
|
||||||
if let Ok(new_ws) = connect(&self.ws_url).await {
|
|
||||||
*ws = new_ws;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Draw
|
||||||
|
terminal.draw(|f| self.draw(f))?;
|
||||||
|
|
||||||
// Tick rate
|
// Tick rate
|
||||||
sleep(Duration::from_millis(500)).await;
|
sleep(Duration::from_millis(500)).await;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,22 +9,60 @@ mod ws;
|
|||||||
use app::App;
|
use app::App;
|
||||||
use std::env;
|
use std::env;
|
||||||
|
|
||||||
|
fn parse_args<I: IntoIterator<Item = String>>(args: I) -> Result<(String, Option<String>), String> {
|
||||||
|
let mut it = args.into_iter();
|
||||||
|
let prog = it.next().unwrap_or_else(|| "socktop".into());
|
||||||
|
let mut url: Option<String> = None;
|
||||||
|
let mut tls_ca: Option<String> = None;
|
||||||
|
|
||||||
|
while let Some(arg) = it.next() {
|
||||||
|
match arg.as_str() {
|
||||||
|
"-h" | "--help" => {
|
||||||
|
return Err(format!(
|
||||||
|
"Usage: {prog} [--tls-ca CERT_PEM|-t CERT_PEM] ws://HOST:PORT/ws"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
"--tls-ca" | "-t" => {
|
||||||
|
tls_ca = it.next();
|
||||||
|
}
|
||||||
|
_ if arg.starts_with("--tls-ca=") => {
|
||||||
|
if let Some((_, v)) = arg.split_once('=') {
|
||||||
|
if !v.is_empty() {
|
||||||
|
tls_ca = Some(v.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
if url.is_none() {
|
||||||
|
url = Some(arg);
|
||||||
|
} else {
|
||||||
|
return Err(format!(
|
||||||
|
"Unexpected argument. Usage: {prog} [--tls-ca CERT_PEM|-t CERT_PEM] ws://HOST:PORT/ws"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match url {
|
||||||
|
Some(u) => Ok((u, tls_ca)),
|
||||||
|
None => Err(format!(
|
||||||
|
"Usage: {prog} [--tls-ca CERT_PEM|-t CERT_PEM] ws://HOST:PORT/ws"
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let mut args = env::args();
|
// Reuse the same parsing logic for testability
|
||||||
let prog = args.next().unwrap_or_else(|| "socktop".into());
|
let (url, tls_ca) = match parse_args(env::args()) {
|
||||||
let url = match args.next() {
|
Ok(v) => v,
|
||||||
Some(flag) if flag == "-h" || flag == "--help" => {
|
Err(msg) => {
|
||||||
println!("Usage: {prog} ws://HOST:PORT/ws");
|
eprintln!("{msg}");
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
Some(url) => url,
|
|
||||||
None => {
|
|
||||||
eprintln!("Usage: {prog} ws://HOST:PORT/ws");
|
|
||||||
std::process::exit(1);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut app = App::new();
|
let mut app = App::new();
|
||||||
app.run(&url).await
|
app.run(&url, tls_ca.as_deref()).await
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,17 +1,62 @@
|
|||||||
//! Minimal WebSocket client helpers for requesting metrics from the agent.
|
//! Minimal WebSocket client helpers for requesting metrics from the agent.
|
||||||
|
|
||||||
use flate2::read::GzDecoder;
|
use flate2::bufread::GzDecoder;
|
||||||
use futures_util::{SinkExt, StreamExt};
|
use futures_util::{SinkExt, StreamExt};
|
||||||
|
use rustls::{ClientConfig, RootCertStore};
|
||||||
|
use rustls_pemfile::Item;
|
||||||
use std::io::{Cursor, Read};
|
use std::io::{Cursor, Read};
|
||||||
use std::sync::OnceLock;
|
use std::sync::OnceLock;
|
||||||
|
use std::{fs::File, io::BufReader, sync::Arc};
|
||||||
use tokio::net::TcpStream;
|
use tokio::net::TcpStream;
|
||||||
use tokio::time::{timeout, Duration};
|
use tokio::time::{interval, timeout, Duration};
|
||||||
use tokio_tungstenite::{connect_async, tungstenite::Message, MaybeTlsStream, WebSocketStream};
|
use tokio_tungstenite::{
|
||||||
|
connect_async, connect_async_tls_with_config, tungstenite::client::IntoClientRequest,
|
||||||
|
tungstenite::Message, Connector, MaybeTlsStream, WebSocketStream,
|
||||||
|
};
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
use crate::types::{DiskInfo, Metrics, ProcessesPayload};
|
use crate::types::{DiskInfo, Metrics, ProcessesPayload};
|
||||||
|
|
||||||
pub type WsStream = WebSocketStream<MaybeTlsStream<TcpStream>>;
|
pub type WsStream = WebSocketStream<MaybeTlsStream<TcpStream>>;
|
||||||
|
|
||||||
|
// Connect to the agent and return the WS stream
|
||||||
|
pub async fn connect(
|
||||||
|
url: &str,
|
||||||
|
tls_ca: Option<&str>,
|
||||||
|
) -> Result<WsStream, Box<dyn std::error::Error>> {
|
||||||
|
let mut u = Url::parse(url)?;
|
||||||
|
if let Some(ca_path) = tls_ca {
|
||||||
|
if u.scheme() == "ws" {
|
||||||
|
let _ = u.set_scheme("wss");
|
||||||
|
}
|
||||||
|
return connect_with_ca(u.as_str(), ca_path).await;
|
||||||
|
}
|
||||||
|
let (ws, _) = connect_async(u.as_str()).await?;
|
||||||
|
Ok(ws)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn connect_with_ca(url: &str, ca_path: &str) -> Result<WsStream, Box<dyn std::error::Error>> {
|
||||||
|
let mut root = RootCertStore::empty();
|
||||||
|
let mut reader = BufReader::new(File::open(ca_path)?);
|
||||||
|
let mut der_certs = Vec::new();
|
||||||
|
while let Ok(Some(item)) = rustls_pemfile::read_one(&mut reader) {
|
||||||
|
if let Item::X509Certificate(der) = item {
|
||||||
|
der_certs.push(der);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
root.add_parsable_certificates(der_certs);
|
||||||
|
|
||||||
|
let cfg = ClientConfig::builder()
|
||||||
|
.with_root_certificates(root)
|
||||||
|
.with_no_client_auth();
|
||||||
|
let cfg = Arc::new(cfg);
|
||||||
|
|
||||||
|
let req = url.into_client_request()?;
|
||||||
|
let (ws, _) =
|
||||||
|
connect_async_tls_with_config(req, None, true, Some(Connector::Rustls(cfg))).await?;
|
||||||
|
Ok(ws)
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn debug_on() -> bool {
|
fn debug_on() -> bool {
|
||||||
static ON: OnceLock<bool> = OnceLock::new();
|
static ON: OnceLock<bool> = OnceLock::new();
|
||||||
@ -22,61 +67,28 @@ fn debug_on() -> bool {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn log_msg(msg: &Message) {
|
// Send a "get_metrics" request and await a single JSON reply
|
||||||
match msg {
|
pub async fn request_metrics(ws: &mut WsStream) -> Option<Metrics> {
|
||||||
Message::Binary(b) => eprintln!("ws: Binary {} bytes", b.len()),
|
if ws.send(Message::Text("get_metrics".into())).await.is_err() {
|
||||||
Message::Text(s) => eprintln!("ws: Text {} bytes", s.len()),
|
return None;
|
||||||
Message::Close(_) => eprintln!("ws: Close"),
|
|
||||||
_ => eprintln!("ws: Other frame"),
|
|
||||||
}
|
}
|
||||||
}
|
match ws.next().await {
|
||||||
|
Some(Ok(Message::Binary(b))) => {
|
||||||
// Connect to the agent and return the WS stream
|
gunzip_to_string(&b).and_then(|s| serde_json::from_str::<Metrics>(&s).ok())
|
||||||
pub async fn connect(url: &str) -> Result<WsStream, Box<dyn std::error::Error>> {
|
}
|
||||||
if debug_on() {
|
Some(Ok(Message::Text(json))) => serde_json::from_str::<Metrics>(&json).ok(),
|
||||||
eprintln!("ws: connecting to {url}");
|
_ => None,
|
||||||
}
|
}
|
||||||
let (ws, _) = connect_async(url).await?;
|
|
||||||
if debug_on() {
|
|
||||||
eprintln!("ws: connected");
|
|
||||||
}
|
|
||||||
Ok(ws)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decompress a gzip-compressed binary frame into a String.
|
// Decompress a gzip-compressed binary frame into a String.
|
||||||
fn gunzip_to_string(bytes: &[u8]) -> Option<String> {
|
fn gunzip_to_string(bytes: &[u8]) -> Option<String> {
|
||||||
let cursor = Cursor::new(bytes);
|
let mut dec = GzDecoder::new(bytes);
|
||||||
let mut dec = GzDecoder::new(cursor);
|
|
||||||
let mut out = String::new();
|
let mut out = String::new();
|
||||||
dec.read_to_string(&mut out).ok()?;
|
dec.read_to_string(&mut out).ok()?;
|
||||||
if debug_on() {
|
|
||||||
eprintln!("ws: gunzip decoded {} bytes", out.len());
|
|
||||||
}
|
|
||||||
Some(out)
|
Some(out)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn message_to_json(msg: &Message) -> Option<String> {
|
|
||||||
match msg {
|
|
||||||
Message::Binary(b) => {
|
|
||||||
if debug_on() {
|
|
||||||
eprintln!("ws: <- Binary frame {} bytes", b.len());
|
|
||||||
}
|
|
||||||
if let Some(s) = gunzip_to_string(b) {
|
|
||||||
return Some(s);
|
|
||||||
}
|
|
||||||
// Fallback: try interpreting as UTF-8 JSON in a binary frame
|
|
||||||
String::from_utf8(b.clone()).ok()
|
|
||||||
}
|
|
||||||
Message::Text(s) => {
|
|
||||||
if debug_on() {
|
|
||||||
eprintln!("ws: <- Text frame {} bytes", s.len());
|
|
||||||
}
|
|
||||||
Some(s.clone())
|
|
||||||
}
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Suppress dead_code until these are wired into the app
|
// Suppress dead_code until these are wired into the app
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub enum Payload {
|
pub enum Payload {
|
||||||
@ -85,6 +97,7 @@ pub enum Payload {
|
|||||||
Processes(ProcessesPayload),
|
Processes(ProcessesPayload),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
fn parse_any_payload(json: &str) -> Result<Payload, serde_json::Error> {
|
fn parse_any_payload(json: &str) -> Result<Payload, serde_json::Error> {
|
||||||
if let Ok(m) = serde_json::from_str::<Metrics>(json) {
|
if let Ok(m) = serde_json::from_str::<Metrics>(json) {
|
||||||
return Ok(Payload::Metrics(m));
|
return Ok(Payload::Metrics(m));
|
||||||
@ -101,108 +114,22 @@ fn parse_any_payload(json: &str) -> Result<Payload, serde_json::Error> {
|
|||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send a "get_metrics" request and await a single JSON reply
|
|
||||||
pub async fn request_metrics(ws: &mut WsStream) -> Option<Metrics> {
|
|
||||||
if debug_on() {
|
|
||||||
eprintln!("ws: -> get_metrics");
|
|
||||||
}
|
|
||||||
if ws.send(Message::Text("get_metrics".into())).await.is_err() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
// Drain a few messages until we find Metrics (handle out-of-order replies)
|
|
||||||
for _ in 0..8 {
|
|
||||||
match timeout(Duration::from_millis(800), ws.next()).await {
|
|
||||||
Ok(Some(Ok(msg))) => {
|
|
||||||
if debug_on() {
|
|
||||||
log_msg(&msg);
|
|
||||||
}
|
|
||||||
if let Some(json) = message_to_json(&msg) {
|
|
||||||
match parse_any_payload(&json) {
|
|
||||||
Ok(Payload::Metrics(m)) => return Some(m),
|
|
||||||
Ok(Payload::Disks(_)) => {
|
|
||||||
if debug_on() {
|
|
||||||
eprintln!("ws: got Disks while waiting for Metrics");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(Payload::Processes(_)) => {
|
|
||||||
if debug_on() {
|
|
||||||
eprintln!("ws: got Processes while waiting for Metrics");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(_e) => {
|
|
||||||
if debug_on() {
|
|
||||||
eprintln!(
|
|
||||||
"ws: unknown payload while waiting for Metrics (len={})",
|
|
||||||
json.len()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if debug_on() {
|
|
||||||
eprintln!("ws: non-json frame while waiting for Metrics");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(Some(Err(_e))) => continue,
|
|
||||||
Ok(None) => return None,
|
|
||||||
Err(_elapsed) => continue,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send a "get_disks" request and await a JSON Vec<DiskInfo>
|
// Send a "get_disks" request and await a JSON Vec<DiskInfo>
|
||||||
pub async fn request_disks(ws: &mut WsStream) -> Option<Vec<DiskInfo>> {
|
pub async fn request_disks(ws: &mut WsStream) -> Option<Vec<DiskInfo>> {
|
||||||
if debug_on() {
|
|
||||||
eprintln!("ws: -> get_disks");
|
|
||||||
}
|
|
||||||
if ws.send(Message::Text("get_disks".into())).await.is_err() {
|
if ws.send(Message::Text("get_disks".into())).await.is_err() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
for _ in 0..8 {
|
match ws.next().await {
|
||||||
match timeout(Duration::from_millis(800), ws.next()).await {
|
Some(Ok(Message::Binary(b))) => {
|
||||||
Ok(Some(Ok(msg))) => {
|
gunzip_to_string(&b).and_then(|s| serde_json::from_str::<Vec<DiskInfo>>(&s).ok())
|
||||||
if debug_on() {
|
|
||||||
log_msg(&msg);
|
|
||||||
}
|
|
||||||
if let Some(json) = message_to_json(&msg) {
|
|
||||||
match parse_any_payload(&json) {
|
|
||||||
Ok(Payload::Disks(d)) => return Some(d),
|
|
||||||
Ok(Payload::Metrics(_)) => {
|
|
||||||
if debug_on() {
|
|
||||||
eprintln!("ws: got Metrics while waiting for Disks");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(Payload::Processes(_)) => {
|
|
||||||
if debug_on() {
|
|
||||||
eprintln!("ws: got Processes while waiting for Disks");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(_e) => {
|
|
||||||
if debug_on() {
|
|
||||||
eprintln!(
|
|
||||||
"ws: unknown payload while waiting for Disks (len={})",
|
|
||||||
json.len()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if debug_on() {
|
|
||||||
eprintln!("ws: non-json frame while waiting for Disks");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(Some(Err(_e))) => continue,
|
|
||||||
Ok(None) => return None,
|
|
||||||
Err(_elapsed) => continue,
|
|
||||||
}
|
}
|
||||||
|
Some(Ok(Message::Text(json))) => serde_json::from_str::<Vec<DiskInfo>>(&json).ok(),
|
||||||
|
_ => None,
|
||||||
}
|
}
|
||||||
None
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send a "get_processes" request and await a JSON ProcessesPayload
|
// Send a "get_processes" request and await a JSON ProcessesPayload
|
||||||
pub async fn request_processes(ws: &mut WsStream) -> Option<ProcessesPayload> {
|
pub async fn request_processes(ws: &mut WsStream) -> Option<ProcessesPayload> {
|
||||||
if debug_on() {
|
|
||||||
eprintln!("ws: -> get_processes");
|
|
||||||
}
|
|
||||||
if ws
|
if ws
|
||||||
.send(Message::Text("get_processes".into()))
|
.send(Message::Text("get_processes".into()))
|
||||||
.await
|
.await
|
||||||
@ -210,43 +137,70 @@ pub async fn request_processes(ws: &mut WsStream) -> Option<ProcessesPayload> {
|
|||||||
{
|
{
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
for _ in 0..16 {
|
match ws.next().await {
|
||||||
// allow a few more cycles due to gzip size
|
Some(Ok(Message::Binary(b))) => {
|
||||||
match timeout(Duration::from_millis(1200), ws.next()).await {
|
gunzip_to_string(&b).and_then(|s| serde_json::from_str::<ProcessesPayload>(&s).ok())
|
||||||
Ok(Some(Ok(msg))) => {
|
}
|
||||||
if debug_on() {
|
Some(Ok(Message::Text(json))) => serde_json::from_str::<ProcessesPayload>(&json).ok(),
|
||||||
log_msg(&msg);
|
_ => None,
|
||||||
}
|
}
|
||||||
if let Some(json) = message_to_json(&msg) {
|
}
|
||||||
match parse_any_payload(&json) {
|
|
||||||
Ok(Payload::Processes(p)) => return Some(p),
|
#[allow(dead_code)]
|
||||||
Ok(Payload::Metrics(_)) => {
|
pub async fn start_ws_polling(mut ws: WsStream) {
|
||||||
if debug_on() {
|
let mut t_fast = interval(Duration::from_millis(500));
|
||||||
eprintln!("ws: got Metrics while waiting for Processes");
|
let mut t_procs = interval(Duration::from_secs(2));
|
||||||
}
|
let mut t_disks = interval(Duration::from_secs(5));
|
||||||
}
|
|
||||||
Ok(Payload::Disks(_)) => {
|
let _ = ws.send(Message::Text("get_metrics".into())).await;
|
||||||
if debug_on() {
|
let _ = ws.send(Message::Text("get_processes".into())).await;
|
||||||
eprintln!("ws: got Disks while waiting for Processes");
|
let _ = ws.send(Message::Text("get_disks".into())).await;
|
||||||
}
|
|
||||||
}
|
loop {
|
||||||
Err(_e) => {
|
tokio::select! {
|
||||||
if debug_on() {
|
_ = t_fast.tick() => {
|
||||||
eprintln!(
|
let _ = ws.send(Message::Text("get_metrics".into())).await;
|
||||||
"ws: unknown payload while waiting for Processes (len={})",
|
}
|
||||||
json.len()
|
_ = t_procs.tick() => {
|
||||||
);
|
let _ = ws.send(Message::Text("get_processes".into())).await;
|
||||||
|
}
|
||||||
|
_ = t_disks.tick() => {
|
||||||
|
let _ = ws.send(Message::Text("get_disks".into())).await;
|
||||||
|
}
|
||||||
|
maybe = ws.next() => {
|
||||||
|
let Some(result) = maybe else { break; };
|
||||||
|
let Ok(msg) = result else { break; };
|
||||||
|
match msg {
|
||||||
|
Message::Binary(b) => {
|
||||||
|
if let Some(json) = gunzip_to_string(&b) {
|
||||||
|
if let Ok(payload) = parse_any_payload(&json) {
|
||||||
|
match payload {
|
||||||
|
Payload::Metrics(_m) => {
|
||||||
|
// update your app state with fast metrics
|
||||||
|
}
|
||||||
|
Payload::Disks(_d) => {
|
||||||
|
// update your app state with disks
|
||||||
|
}
|
||||||
|
Payload::Processes(_p) => {
|
||||||
|
// update your app state with processes
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if debug_on() {
|
Message::Text(s) => {
|
||||||
eprintln!("ws: non-json frame while waiting for Processes");
|
if let Ok(payload) = parse_any_payload(&s) {
|
||||||
|
match payload {
|
||||||
|
Payload::Metrics(_m) => {}
|
||||||
|
Payload::Disks(_d) => {}
|
||||||
|
Payload::Processes(_p) => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Message::Close(_) => break,
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(Some(Err(_e))) => continue,
|
|
||||||
Ok(None) => return None,
|
|
||||||
Err(_elapsed) => continue,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None
|
|
||||||
}
|
}
|
||||||
|
|||||||
56
socktop/tests/cli_args.rs
Normal file
56
socktop/tests/cli_args.rs
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
//! CLI arg parsing tests for socktop (client)
|
||||||
|
use std::process::Command;
|
||||||
|
|
||||||
|
// We test the parsing by invoking the binary with --help and ensuring the help mentions short and long flags.
|
||||||
|
// Also directly test the parse_args function via a tiny helper in a doctest-like fashion using a small
|
||||||
|
// reimplementation here kept in sync with main (compile-time test).
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_help_mentions_short_and_long_flags() {
|
||||||
|
let output = Command::new(env!("CARGO_BIN_EXE_socktop"))
|
||||||
|
.arg("--help")
|
||||||
|
.output()
|
||||||
|
.expect("run socktop --help");
|
||||||
|
let text = format!(
|
||||||
|
"{}{}",
|
||||||
|
String::from_utf8_lossy(&output.stdout),
|
||||||
|
String::from_utf8_lossy(&output.stderr)
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
text.contains("--tls-ca") && text.contains("-t"),
|
||||||
|
"help text missing --tls-ca/-t\n{text}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_tlc_ca_arg_long_and_short_parsed() {
|
||||||
|
// Use --help combined with flags to avoid network and still exercise arg acceptance
|
||||||
|
let exe = env!("CARGO_BIN_EXE_socktop");
|
||||||
|
// Long form with help
|
||||||
|
let out = Command::new(exe)
|
||||||
|
.args(["--tls-ca", "/tmp/cert.pem", "--help"])
|
||||||
|
.output()
|
||||||
|
.expect("run socktop");
|
||||||
|
assert!(
|
||||||
|
out.status.success(),
|
||||||
|
"socktop --tls-ca … --help did not succeed"
|
||||||
|
);
|
||||||
|
let text = format!(
|
||||||
|
"{}{}",
|
||||||
|
String::from_utf8_lossy(&out.stdout),
|
||||||
|
String::from_utf8_lossy(&out.stderr)
|
||||||
|
);
|
||||||
|
assert!(text.contains("Usage:"));
|
||||||
|
// Short form with help
|
||||||
|
let out2 = Command::new(exe)
|
||||||
|
.args(["-t", "/tmp/cert.pem", "--help"])
|
||||||
|
.output()
|
||||||
|
.expect("run socktop");
|
||||||
|
assert!(out2.status.success(), "socktop -t … --help did not succeed");
|
||||||
|
let text2 = format!(
|
||||||
|
"{}{}",
|
||||||
|
String::from_utf8_lossy(&out2.stdout),
|
||||||
|
String::from_utf8_lossy(&out2.stderr)
|
||||||
|
);
|
||||||
|
assert!(text2.contains("Usage:"));
|
||||||
|
}
|
||||||
@ -20,4 +20,13 @@ tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
|||||||
nvml-wrapper = "0.10"
|
nvml-wrapper = "0.10"
|
||||||
gfxinfo = "0.1.2"
|
gfxinfo = "0.1.2"
|
||||||
tungstenite = "0.27.0"
|
tungstenite = "0.27.0"
|
||||||
once_cell = "1.19"
|
once_cell = "1.19"
|
||||||
|
axum-server = { version = "0.6", features = ["tls-rustls"] }
|
||||||
|
rustls = "0.23"
|
||||||
|
rustls-pemfile = "2.1"
|
||||||
|
openssl = { version = "0.10", features = ["vendored"] } # for cross‑platform self‑signed generation
|
||||||
|
anyhow = "1"
|
||||||
|
hostname = "0.3"
|
||||||
|
[dev-dependencies]
|
||||||
|
assert_cmd = "2.0"
|
||||||
|
tempfile = "3.10"
|
||||||
@ -10,13 +10,30 @@ mod ws;
|
|||||||
|
|
||||||
use axum::{routing::get, Router};
|
use axum::{routing::get, Router};
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
mod tls;
|
||||||
|
|
||||||
use crate::sampler::{spawn_disks_sampler, spawn_process_sampler, spawn_sampler};
|
use crate::sampler::{spawn_disks_sampler, spawn_process_sampler, spawn_sampler};
|
||||||
use state::AppState;
|
use state::AppState;
|
||||||
use ws::ws_handler;
|
|
||||||
|
fn arg_flag(name: &str) -> bool {
|
||||||
|
std::env::args().any(|a| a == name)
|
||||||
|
}
|
||||||
|
fn arg_value(name: &str) -> Option<String> {
|
||||||
|
let mut it = std::env::args();
|
||||||
|
while let Some(a) = it.next() {
|
||||||
|
if a == name {
|
||||||
|
return it.next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
// (tests moved to end of file to satisfy clippy::items_after_test_module)
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() -> anyhow::Result<()> {
|
||||||
tracing_subscriber::fmt::init();
|
tracing_subscriber::fmt::init();
|
||||||
|
|
||||||
let state = AppState::new();
|
let state = AppState::new();
|
||||||
@ -29,71 +46,85 @@ async fn main() {
|
|||||||
// 5s disks
|
// 5s disks
|
||||||
let _h_disks = spawn_disks_sampler(state.clone(), std::time::Duration::from_secs(5));
|
let _h_disks = spawn_disks_sampler(state.clone(), std::time::Duration::from_secs(5));
|
||||||
|
|
||||||
// Web app
|
// Web app: route /ws to the websocket handler
|
||||||
let port = resolve_port();
|
|
||||||
let app = Router::new()
|
let app = Router::new()
|
||||||
.route("/ws", get(ws_handler))
|
.route("/ws", get(ws::ws_handler))
|
||||||
.with_state(state);
|
.with_state(state.clone());
|
||||||
|
|
||||||
|
let enable_ssl =
|
||||||
|
arg_flag("--enableSSL") || std::env::var("SOCKTOP_ENABLE_SSL").ok().as_deref() == Some("1");
|
||||||
|
if enable_ssl {
|
||||||
|
// Port can be overridden by --port or SOCKTOP_PORT; default to 8443 when SSL
|
||||||
|
let port = arg_value("--port")
|
||||||
|
.or_else(|| arg_value("-p"))
|
||||||
|
.or_else(|| std::env::var("SOCKTOP_PORT").ok())
|
||||||
|
.and_then(|s| s.parse::<u16>().ok())
|
||||||
|
.unwrap_or(8443);
|
||||||
|
|
||||||
|
let (cert_path, key_path) = tls::ensure_self_signed_cert()?;
|
||||||
|
let cfg = axum_server::tls_rustls::RustlsConfig::from_pem_file(cert_path, key_path).await?;
|
||||||
|
|
||||||
|
let addr = SocketAddr::from_str(&format!("0.0.0.0:{port}"))?;
|
||||||
|
println!("socktop_agent: TLS enabled. Listening on wss://{addr}/ws");
|
||||||
|
axum_server::bind_rustls(addr, cfg)
|
||||||
|
.serve(app.into_make_service())
|
||||||
|
.await?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Non-TLS HTTP/WS path
|
||||||
|
let port = arg_value("--port")
|
||||||
|
.or_else(|| arg_value("-p"))
|
||||||
|
.or_else(|| std::env::var("SOCKTOP_PORT").ok())
|
||||||
|
.and_then(|s| s.parse::<u16>().ok())
|
||||||
|
.unwrap_or(3000);
|
||||||
let addr = SocketAddr::from(([0, 0, 0, 0], port));
|
let addr = SocketAddr::from(([0, 0, 0, 0], port));
|
||||||
|
println!("socktop_agent: Listening on ws://{addr}/ws");
|
||||||
//output to console
|
axum_server::bind(addr)
|
||||||
println!("Remote agent running at http://{addr}");
|
.serve(app.into_make_service())
|
||||||
println!("WebSocket endpoint: ws://{addr}/ws");
|
.await?;
|
||||||
|
Ok(())
|
||||||
//trace logging
|
|
||||||
tracing::info!("Remote agent running at http://{} (ws at /ws)", addr);
|
|
||||||
tracing::info!("WebSocket endpoint: ws://{}/ws", addr);
|
|
||||||
|
|
||||||
let listener = tokio::net::TcpListener::bind(addr).await.unwrap();
|
|
||||||
axum::serve(listener, app).await.unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolve the listening port from CLI args/env with a 3000 default.
|
#[cfg(test)]
|
||||||
// Supports: --port <PORT>, -p <PORT>, a bare numeric positional arg, or SOCKTOP_PORT.
|
mod tests_cli_agent {
|
||||||
fn resolve_port() -> u16 {
|
// Local helper for testing port parsing
|
||||||
const DEFAULT: u16 = 3000;
|
fn parse_port<I: IntoIterator<Item = String>>(args: I, default_port: u16) -> u16 {
|
||||||
|
let mut it = args.into_iter();
|
||||||
// Env takes precedence over positional, but is overridden by explicit flags if present.
|
let _ = it.next(); // prog
|
||||||
if let Ok(s) = std::env::var("SOCKTOP_PORT") {
|
let mut long: Option<String> = None;
|
||||||
if let Ok(p) = s.parse::<u16>() {
|
let mut short: Option<String> = None;
|
||||||
if p != 0 {
|
while let Some(a) = it.next() {
|
||||||
return p;
|
match a.as_str() {
|
||||||
}
|
"--port" => long = it.next(),
|
||||||
}
|
"-p" => short = it.next(),
|
||||||
eprintln!("Warning: invalid SOCKTOP_PORT='{s}'; using default {DEFAULT}");
|
_ if a.starts_with("--port=") => {
|
||||||
}
|
if let Some((_, v)) = a.split_once('=') {
|
||||||
|
long = Some(v.to_string());
|
||||||
let mut args = std::env::args().skip(1);
|
|
||||||
while let Some(arg) = args.next() {
|
|
||||||
match arg.as_str() {
|
|
||||||
"--port" | "-p" => {
|
|
||||||
if let Some(v) = args.next() {
|
|
||||||
match v.parse::<u16>() {
|
|
||||||
Ok(p) if p != 0 => return p,
|
|
||||||
_ => {
|
|
||||||
eprintln!("Invalid port '{v}'; using default {DEFAULT}");
|
|
||||||
return DEFAULT;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
eprintln!("Missing value for {arg} ; using default {DEFAULT}");
|
|
||||||
return DEFAULT;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"--help" | "-h" => {
|
|
||||||
println!("Usage: socktop_agent [--port <PORT>] [PORT]\n SOCKTOP_PORT=<PORT> socktop_agent");
|
|
||||||
std::process::exit(0);
|
|
||||||
}
|
|
||||||
s => {
|
|
||||||
if let Ok(p) = s.parse::<u16>() {
|
|
||||||
if p != 0 {
|
|
||||||
return p;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
long.or(short)
|
||||||
|
.and_then(|s| s.parse::<u16>().ok())
|
||||||
|
.unwrap_or(default_port)
|
||||||
}
|
}
|
||||||
|
|
||||||
DEFAULT
|
#[test]
|
||||||
|
fn port_long_short_and_assign() {
|
||||||
|
assert_eq!(
|
||||||
|
parse_port(vec!["agent".into(), "--port".into(), "9001".into()], 8443),
|
||||||
|
9001
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
parse_port(vec!["agent".into(), "-p".into(), "9002".into()], 8443),
|
||||||
|
9002
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
parse_port(vec!["agent".into(), "--port=9003".into()], 8443),
|
||||||
|
9003
|
||||||
|
);
|
||||||
|
assert_eq!(parse_port(vec!["agent".into()], 8443), 8443);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
96
socktop_agent/src/tls.rs
Normal file
96
socktop_agent/src/tls.rs
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
use openssl::asn1::Asn1Time;
|
||||||
|
use openssl::hash::MessageDigest;
|
||||||
|
use openssl::nid::Nid;
|
||||||
|
use openssl::pkey::PKey;
|
||||||
|
use openssl::rsa::Rsa;
|
||||||
|
use openssl::x509::extension::{
|
||||||
|
BasicConstraints, ExtendedKeyUsage, KeyUsage, SubjectAlternativeName,
|
||||||
|
};
|
||||||
|
use openssl::x509::{X509NameBuilder, X509};
|
||||||
|
use std::{
|
||||||
|
fs,
|
||||||
|
io::Write,
|
||||||
|
net::{IpAddr, Ipv4Addr},
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
};
|
||||||
|
|
||||||
|
fn config_dir() -> PathBuf {
|
||||||
|
std::env::var_os("XDG_CONFIG_HOME")
|
||||||
|
.map(PathBuf::from)
|
||||||
|
.or_else(|| std::env::var_os("HOME").map(|h| Path::new(&h).join(".config")))
|
||||||
|
.unwrap_or_else(|| PathBuf::from("."))
|
||||||
|
.join("socktop_agent")
|
||||||
|
.join("tls")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cert_paths() -> (PathBuf, PathBuf) {
|
||||||
|
let dir = config_dir();
|
||||||
|
(dir.join("cert.pem"), dir.join("key.pem"))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ensure_self_signed_cert() -> anyhow::Result<(PathBuf, PathBuf)> {
|
||||||
|
let (cert_path, key_path) = cert_paths();
|
||||||
|
if cert_path.exists() && key_path.exists() {
|
||||||
|
return Ok((cert_path, key_path));
|
||||||
|
}
|
||||||
|
fs::create_dir_all(cert_path.parent().unwrap())?;
|
||||||
|
|
||||||
|
// Key
|
||||||
|
let rsa = Rsa::generate(4096)?;
|
||||||
|
let pkey = PKey::from_rsa(rsa)?;
|
||||||
|
|
||||||
|
// Subject/issuer
|
||||||
|
let hostname = hostname::get()
|
||||||
|
.ok()
|
||||||
|
.and_then(|s| s.into_string().ok())
|
||||||
|
.unwrap_or_else(|| "localhost".to_string());
|
||||||
|
let mut name = X509NameBuilder::new()?;
|
||||||
|
name.append_entry_by_nid(Nid::COMMONNAME, &hostname)?;
|
||||||
|
let name = name.build();
|
||||||
|
|
||||||
|
// Cert builder
|
||||||
|
let mut builder = X509::builder()?;
|
||||||
|
builder.set_version(2)?;
|
||||||
|
builder.set_subject_name(&name)?;
|
||||||
|
builder.set_issuer_name(&name)?;
|
||||||
|
builder.set_pubkey(&pkey)?;
|
||||||
|
|
||||||
|
builder.set_not_before(Asn1Time::days_from_now(0)?.as_ref())?;
|
||||||
|
builder.set_not_after(Asn1Time::days_from_now(397)?.as_ref())?;
|
||||||
|
|
||||||
|
// SANs: hostname + localhost loopbacks
|
||||||
|
let mut san = SubjectAlternativeName::new();
|
||||||
|
san.dns(&hostname)
|
||||||
|
.dns("localhost")
|
||||||
|
.ip("127.0.0.1")
|
||||||
|
.ip("::1");
|
||||||
|
// Add a generic 0.0.0.0 for convenience; some TLS libs ignore this, but harmless.
|
||||||
|
let _ = san.ip(&IpAddr::V4(Ipv4Addr::UNSPECIFIED).to_string());
|
||||||
|
let san = san.build(&builder.x509v3_context(None, None))?;
|
||||||
|
// End-entity cert: not a CA
|
||||||
|
builder.append_extension(BasicConstraints::new().critical().build()?)?;
|
||||||
|
builder.append_extension(
|
||||||
|
KeyUsage::new()
|
||||||
|
.digital_signature()
|
||||||
|
.key_encipherment()
|
||||||
|
.build()?,
|
||||||
|
)?;
|
||||||
|
// TLS server usage
|
||||||
|
builder.append_extension(ExtendedKeyUsage::new().server_auth().build()?)?;
|
||||||
|
builder.append_extension(san)?;
|
||||||
|
|
||||||
|
builder.sign(&pkey, MessageDigest::sha256())?;
|
||||||
|
let cert: X509 = builder.build();
|
||||||
|
|
||||||
|
let mut f = fs::File::create(&cert_path)?;
|
||||||
|
f.write_all(&cert.to_pem()?)?;
|
||||||
|
let mut k = fs::File::create(&key_path)?;
|
||||||
|
k.write_all(&pkey.private_key_to_pem_pkcs8()?)?;
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"socktop_agent: generated self-signed TLS certificate at {}",
|
||||||
|
cert_path.display()
|
||||||
|
);
|
||||||
|
println!("socktop_agent: private key at {}", key_path.display());
|
||||||
|
Ok((cert_path, key_path))
|
||||||
|
}
|
||||||
28
socktop_agent/tests/cli_args.rs
Normal file
28
socktop_agent/tests/cli_args.rs
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
//! CLI arg parsing tests for socktop_agent (server)
|
||||||
|
use std::process::Command;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_help_and_port_short_long() {
|
||||||
|
// We verify port flags are accepted by ensuring the process starts (then we kill quickly).
|
||||||
|
// Use an unlikely port to avoid conflicts.
|
||||||
|
let exe = env!("CARGO_BIN_EXE_socktop_agent");
|
||||||
|
|
||||||
|
// TLS enabled with long --port
|
||||||
|
let mut child = Command::new(exe)
|
||||||
|
.args(["--enableSSL", "--port", "9555"])
|
||||||
|
.spawn()
|
||||||
|
.expect("spawn agent");
|
||||||
|
// Give it a moment to bind
|
||||||
|
std::thread::sleep(std::time::Duration::from_millis(150));
|
||||||
|
let _ = child.kill();
|
||||||
|
let _ = child.wait();
|
||||||
|
|
||||||
|
// TLS enabled with short -p
|
||||||
|
let mut child2 = Command::new(exe)
|
||||||
|
.args(["--enableSSL", "-p", "9556"])
|
||||||
|
.spawn()
|
||||||
|
.expect("spawn agent");
|
||||||
|
std::thread::sleep(std::time::Duration::from_millis(150));
|
||||||
|
let _ = child2.kill();
|
||||||
|
let _ = child2.wait();
|
||||||
|
}
|
||||||
59
socktop_agent/tests/tls_cert_creation.rs
Normal file
59
socktop_agent/tests/tls_cert_creation.rs
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
use assert_cmd::prelude::*;
|
||||||
|
use std::fs;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::process::Command;
|
||||||
|
use std::time::Duration;
|
||||||
|
use std::time::Instant;
|
||||||
|
|
||||||
|
fn expected_paths(config_home: &std::path::Path) -> (PathBuf, PathBuf) {
|
||||||
|
let base = config_home.join("socktop_agent").join("tls");
|
||||||
|
(base.join("cert.pem"), base.join("key.pem"))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn generates_self_signed_cert_and_key_in_xdg_path() {
|
||||||
|
// Create an isolated fake XDG_CONFIG_HOME
|
||||||
|
let tmpdir = tempfile::tempdir().expect("tempdir");
|
||||||
|
let xdg = tmpdir.path().to_path_buf();
|
||||||
|
|
||||||
|
// Run the agent once with --enableSSL, short timeout so it exits quickly when killed
|
||||||
|
let mut cmd = Command::cargo_bin("socktop_agent").expect("binary exists");
|
||||||
|
// Bind to an ephemeral port (-p 0) to avoid conflicts/flakes
|
||||||
|
cmd.env("XDG_CONFIG_HOME", &xdg)
|
||||||
|
.arg("--enableSSL")
|
||||||
|
.arg("-p")
|
||||||
|
.arg("0");
|
||||||
|
|
||||||
|
// Spawn the process and poll for cert generation
|
||||||
|
let mut child = cmd.spawn().expect("spawn agent");
|
||||||
|
|
||||||
|
// Poll up to ~3s for files to appear to avoid timing flakes
|
||||||
|
let (cert_path, key_path) = expected_paths(&xdg);
|
||||||
|
let start = Instant::now();
|
||||||
|
let timeout = Duration::from_millis(3000);
|
||||||
|
let interval = Duration::from_millis(50);
|
||||||
|
while start.elapsed() < timeout {
|
||||||
|
if cert_path.exists() && key_path.exists() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
std::thread::sleep(interval);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Terminate the process regardless
|
||||||
|
let _ = child.kill();
|
||||||
|
let _ = child.wait();
|
||||||
|
|
||||||
|
// Verify files exist at expected paths
|
||||||
|
assert!(
|
||||||
|
cert_path.exists(),
|
||||||
|
"cert not found at {}",
|
||||||
|
cert_path.display()
|
||||||
|
);
|
||||||
|
assert!(key_path.exists(), "key not found at {}", key_path.display());
|
||||||
|
|
||||||
|
// Also ensure they are non-empty
|
||||||
|
let cert_md = fs::metadata(&cert_path).expect("cert metadata");
|
||||||
|
let key_md = fs::metadata(&key_path).expect("key metadata");
|
||||||
|
assert!(cert_md.len() > 0, "cert is empty");
|
||||||
|
assert!(key_md.len() > 0, "key is empty");
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user