feat: openssl based hoppscotch-relay for request forwarding (#4442)
This commit is contained in:
321
packages/hoppscotch-agent/src-tauri/Cargo.lock
generated
321
packages/hoppscotch-agent/src-tauri/Cargo.lock
generated
@@ -142,9 +142,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anyhow"
|
name = "anyhow"
|
||||||
version = "1.0.89"
|
version = "1.0.91"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6"
|
checksum = "c042108f3ed77fd83760a5fd79b53be043192bb3b9dba91d8c574c0ada7850c8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "arbitrary"
|
name = "arbitrary"
|
||||||
@@ -267,7 +267,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.79",
|
"syn 2.0.85",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -302,7 +302,7 @@ checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.79",
|
"syn 2.0.85",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -516,9 +516,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "brotli"
|
name = "brotli"
|
||||||
version = "6.0.0"
|
version = "7.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "74f7971dbd9326d58187408ab83117d8ac1bb9c17b085fdacd1cf2f598719b6b"
|
checksum = "cc97b8f16f944bba54f0433f07e30be199b6dc2bd25937444bbad560bcea29bd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"alloc-no-stdlib",
|
"alloc-no-stdlib",
|
||||||
"alloc-stdlib",
|
"alloc-stdlib",
|
||||||
@@ -543,9 +543,9 @@ checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytemuck"
|
name = "bytemuck"
|
||||||
version = "1.18.0"
|
version = "1.19.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "94bbb0ad554ad961ddc5da507a12a29b14e4ae5bda06b19f575a3e6079d2e2ae"
|
checksum = "8334215b81e418a0a7bdb8ef0849474f40bb10c8b71f1c4ed315cff49f32494d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "byteorder"
|
name = "byteorder"
|
||||||
@@ -561,9 +561,9 @@ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytes"
|
name = "bytes"
|
||||||
version = "1.7.2"
|
version = "1.8.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3"
|
checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
@@ -637,9 +637,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.1.28"
|
version = "1.1.31"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2e80e3b6a3ab07840e1cae9b0666a63970dc28e8ed5ffbcdacbfc760c281bfc1"
|
checksum = "c2e7962b54006dcfcc61cb72735f4d89bb97061dd6a7ed882ec6b8ee53714c6f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"shlex",
|
"shlex",
|
||||||
]
|
]
|
||||||
@@ -887,7 +887,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331"
|
checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.79",
|
"syn 2.0.85",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -897,7 +897,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "edb49164822f3ee45b17acd4a208cfc1251410cf0cad9a833234c9890774dd9f"
|
checksum = "edb49164822f3ee45b17acd4a208cfc1251410cf0cad9a833234c9890774dd9f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.79",
|
"syn 2.0.85",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -912,30 +912,25 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "curl"
|
name = "curl"
|
||||||
version = "0.4.47"
|
version = "0.4.47"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "git+https://github.com/CuriousCorrelation/curl-rust.git#1ec8079cf527b9cf47cc7a48c68b458affdae273"
|
||||||
checksum = "d9fb4d13a1be2b58f14d60adba57c9834b78c62fd86c3e76a148f732686e9265"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"curl-sys",
|
"curl-sys",
|
||||||
"libc",
|
"libc",
|
||||||
"openssl-probe",
|
"openssl-probe",
|
||||||
"openssl-sys",
|
"openssl-sys",
|
||||||
"schannel",
|
|
||||||
"socket2",
|
"socket2",
|
||||||
"windows-sys 0.52.0",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "curl-sys"
|
name = "curl-sys"
|
||||||
version = "0.4.77+curl-8.10.1"
|
version = "0.4.77+curl-8.10.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "git+https://github.com/CuriousCorrelation/curl-rust.git#1ec8079cf527b9cf47cc7a48c68b458affdae273"
|
||||||
checksum = "f469e8a5991f277a208224f6c7ad72ecb5f986e36d09ae1f2c1bb9259478a480"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
"libc",
|
"libc",
|
||||||
"libz-sys",
|
"libz-sys",
|
||||||
"openssl-sys",
|
"openssl-sys",
|
||||||
"pkg-config",
|
"pkg-config",
|
||||||
"vcpkg",
|
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -962,7 +957,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.79",
|
"syn 2.0.85",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -986,7 +981,7 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"strsim",
|
"strsim",
|
||||||
"syn 2.0.79",
|
"syn 2.0.85",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -997,7 +992,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"darling_core",
|
"darling_core",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.79",
|
"syn 2.0.85",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1044,7 +1039,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.79",
|
"syn 2.0.85",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1057,7 +1052,7 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"rustc_version",
|
"rustc_version",
|
||||||
"syn 2.0.79",
|
"syn 2.0.85",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1125,7 +1120,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.79",
|
"syn 2.0.85",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1157,7 +1152,7 @@ checksum = "f2b99bf03862d7f545ebc28ddd33a665b50865f4dfd84031a393823879bd4c54"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.79",
|
"syn 2.0.85",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1255,7 +1250,7 @@ checksum = "de0d48a183585823424a4ce1aa132d174a6a81bd540895822eb4c8373a8e49e8"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.79",
|
"syn 2.0.85",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1423,7 +1418,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.79",
|
"syn 2.0.85",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1510,7 +1505,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.79",
|
"syn 2.0.85",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1764,7 +1759,7 @@ dependencies = [
|
|||||||
"proc-macro-error",
|
"proc-macro-error",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.79",
|
"syn 2.0.85",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1843,7 +1838,7 @@ dependencies = [
|
|||||||
"proc-macro-error",
|
"proc-macro-error",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.79",
|
"syn 2.0.85",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1946,15 +1941,12 @@ dependencies = [
|
|||||||
"axum-extra",
|
"axum-extra",
|
||||||
"base16",
|
"base16",
|
||||||
"chrono",
|
"chrono",
|
||||||
"curl",
|
|
||||||
"dashmap",
|
"dashmap",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
"http",
|
"hoppscotch-relay",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"log",
|
"log",
|
||||||
"mockito",
|
"mockito",
|
||||||
"openssl",
|
|
||||||
"openssl-sys",
|
|
||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
@@ -1969,11 +1961,27 @@ dependencies = [
|
|||||||
"tokio",
|
"tokio",
|
||||||
"tokio-util",
|
"tokio-util",
|
||||||
"tower-http",
|
"tower-http",
|
||||||
"url-escape",
|
|
||||||
"uuid",
|
"uuid",
|
||||||
"x25519-dalek",
|
"x25519-dalek",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hoppscotch-relay"
|
||||||
|
version = "0.1.1"
|
||||||
|
dependencies = [
|
||||||
|
"curl",
|
||||||
|
"env_logger",
|
||||||
|
"http",
|
||||||
|
"log",
|
||||||
|
"openssl",
|
||||||
|
"openssl-sys",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"thiserror",
|
||||||
|
"tokio-util",
|
||||||
|
"url-escape",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "html5ever"
|
name = "html5ever"
|
||||||
version = "0.26.0"
|
version = "0.26.0"
|
||||||
@@ -2042,9 +2050,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hyper"
|
name = "hyper"
|
||||||
version = "1.4.1"
|
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 = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05"
|
checksum = "bbbff0a806a4728c99295b254c8838933b5b082d75e3cb70c8dab21fdfbcfa9a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
@@ -2149,9 +2157,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "image"
|
name = "image"
|
||||||
version = "0.25.2"
|
version = "0.25.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "99314c8a2152b8ddb211f924cdae532d8c5e4c8bb54728e12fff1b0cd5963a10"
|
checksum = "bc144d44a31d753b02ce64093d532f55ff8dc4ebf2ffb8a63c0dda691385acae"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"byteorder-lite",
|
"byteorder-lite",
|
||||||
@@ -2298,9 +2306,9 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "js-sys"
|
name = "js-sys"
|
||||||
version = "0.3.70"
|
version = "0.3.72"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a"
|
checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
@@ -2311,7 +2319,19 @@ version = "2.0.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5b1fb8864823fad91877e6caea0baca82e49e8db50f8e5c9f9a453e27d3330fc"
|
checksum = "5b1fb8864823fad91877e6caea0baca82e49e8db50f8e5c9f9a453e27d3330fc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"jsonptr",
|
"jsonptr 0.4.7",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "json-patch"
|
||||||
|
version = "3.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "863726d7afb6bc2590eeff7135d923545e5e964f004c2ccf8716c25e70a86f08"
|
||||||
|
dependencies = [
|
||||||
|
"jsonptr 0.6.3",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
@@ -2328,6 +2348,16 @@ dependencies = [
|
|||||||
"serde_json",
|
"serde_json",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jsonptr"
|
||||||
|
version = "0.6.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5dea2b27dd239b2556ed7a25ba842fe47fd602e7fc7433c2a8d6106d4d9edd70"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "keyboard-types"
|
name = "keyboard-types"
|
||||||
version = "0.7.0"
|
version = "0.7.0"
|
||||||
@@ -2384,9 +2414,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.159"
|
version = "0.2.161"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5"
|
checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libloading"
|
name = "libloading"
|
||||||
@@ -2674,7 +2704,7 @@ dependencies = [
|
|||||||
"proc-macro-crate 2.0.2",
|
"proc-macro-crate 2.0.2",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.79",
|
"syn 2.0.85",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2960,7 +2990,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.79",
|
"syn 2.0.85",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2971,9 +3001,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "openssl-src"
|
name = "openssl-src"
|
||||||
version = "300.3.2+3.3.2"
|
version = "300.4.0+3.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a211a18d945ef7e648cc6e0058f4c548ee46aab922ea203e0d30e966ea23647b"
|
checksum = "a709e02f2b4aca747929cca5ed248880847c650233cf8b8cdc48f40aaf4898a6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
]
|
]
|
||||||
@@ -3073,9 +3103,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pathdiff"
|
name = "pathdiff"
|
||||||
version = "0.2.1"
|
version = "0.2.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd"
|
checksum = "d61c5ce1153ab5b689d0c074c4e7fc613e942dfb7dd9eea5ab202d2ad91fe361"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "percent-encoding"
|
name = "percent-encoding"
|
||||||
@@ -3187,7 +3217,7 @@ dependencies = [
|
|||||||
"phf_shared 0.11.2",
|
"phf_shared 0.11.2",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.79",
|
"syn 2.0.85",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3372,9 +3402,9 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.87"
|
version = "1.0.89"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b3e4daa0dcf6feba26f985457cdf104d4b4256fc5a09547140f3631bb076b19a"
|
checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
@@ -3708,9 +3738,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustls"
|
name = "rustls"
|
||||||
version = "0.23.14"
|
version = "0.23.15"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "415d9944693cb90382053259f89fbb077ea730ad7273047ec63b19bc9b160ba8"
|
checksum = "5fbb44d7acc4e873d613422379f69f237a1b141928c02f6bc6ccfddddc2d7993"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"ring",
|
"ring",
|
||||||
@@ -3731,9 +3761,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustls-pki-types"
|
name = "rustls-pki-types"
|
||||||
version = "1.9.0"
|
version = "1.10.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0e696e35370c65c9c541198af4543ccd580cf17fc25d8e05c5a242b202488c55"
|
checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustls-webpki"
|
name = "rustls-webpki"
|
||||||
@@ -3748,9 +3778,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustversion"
|
name = "rustversion"
|
||||||
version = "1.0.17"
|
version = "1.0.18"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6"
|
checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ryu"
|
name = "ryu"
|
||||||
@@ -3767,15 +3797,6 @@ dependencies = [
|
|||||||
"winapi-util",
|
"winapi-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "schannel"
|
|
||||||
version = "0.1.26"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "01227be5826fa0690321a2ba6c5cd57a19cf3f6a09e76973b58e61de6ab9d1c1"
|
|
||||||
dependencies = [
|
|
||||||
"windows-sys 0.59.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "schemars"
|
name = "schemars"
|
||||||
version = "0.8.21"
|
version = "0.8.21"
|
||||||
@@ -3800,7 +3821,7 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"serde_derive_internals",
|
"serde_derive_internals",
|
||||||
"syn 2.0.79",
|
"syn 2.0.85",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3846,9 +3867,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.210"
|
version = "1.0.213"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a"
|
checksum = "3ea7893ff5e2466df8d720bb615088341b295f849602c6956047f8f80f0e9bc1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
@@ -3866,13 +3887,13 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.210"
|
version = "1.0.213"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f"
|
checksum = "7e85ad2009c50b58e87caa8cd6dac16bdf511bbfb7af6c33df902396aa480fa5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.79",
|
"syn 2.0.85",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3883,14 +3904,14 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.79",
|
"syn 2.0.85",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.128"
|
version = "1.0.132"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8"
|
checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa 1.0.11",
|
"itoa 1.0.11",
|
||||||
"memchr",
|
"memchr",
|
||||||
@@ -3916,7 +3937,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.79",
|
"syn 2.0.85",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3967,7 +3988,7 @@ dependencies = [
|
|||||||
"darling",
|
"darling",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.79",
|
"syn 2.0.85",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -4220,9 +4241,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.79"
|
version = "2.0.85"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590"
|
checksum = "5023162dfcd14ef8f32034d8bcd4cc5ddc61ef7a247c024a33e24e1f24d21b56"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -4304,7 +4325,7 @@ checksum = "f4e16beb8b2ac17db28eab8bca40e62dbfbb34c0fcdc6d9826b11b7b5d047dfd"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.79",
|
"syn 2.0.85",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -4326,9 +4347,9 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri"
|
name = "tauri"
|
||||||
version = "2.0.4"
|
version = "2.0.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "44438500b50708bfc1e6083844e135d1b516325aae58710dcd8fb67e050ae87c"
|
checksum = "d3889b392db6d32a105d3757230ea0220090b8f94c90d3e60b6c5eb91178ab1b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bytes",
|
"bytes",
|
||||||
@@ -4377,16 +4398,16 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-build"
|
name = "tauri-build"
|
||||||
version = "2.0.1"
|
version = "2.0.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "935f9b3c49b22b3e2e485a57f46d61cd1ae07b1cbb2ba87387a387caf2d8c4e7"
|
checksum = "9f96827ccfb1aa40d55d0ded79562d18ba18566657a553f992a982d755148376"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"cargo_toml",
|
"cargo_toml",
|
||||||
"dirs 5.0.1",
|
"dirs 5.0.1",
|
||||||
"glob",
|
"glob",
|
||||||
"heck 0.5.0",
|
"heck 0.5.0",
|
||||||
"json-patch",
|
"json-patch 3.0.1",
|
||||||
"schemars",
|
"schemars",
|
||||||
"semver",
|
"semver",
|
||||||
"serde",
|
"serde",
|
||||||
@@ -4399,14 +4420,14 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-codegen"
|
name = "tauri-codegen"
|
||||||
version = "2.0.1"
|
version = "2.0.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "95d7443dd4f0b597704b6a14b964ee2ed16e99928d8e6292ae9825f09fbcd30e"
|
checksum = "8947f16f47becd9e9cd39b74ee337fd1981574d78819be18e4384d85e5a0b82f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
"brotli",
|
"brotli",
|
||||||
"ico",
|
"ico",
|
||||||
"json-patch",
|
"json-patch 2.0.0",
|
||||||
"plist",
|
"plist",
|
||||||
"png",
|
"png",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
@@ -4415,7 +4436,7 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sha2",
|
"sha2",
|
||||||
"syn 2.0.79",
|
"syn 2.0.85",
|
||||||
"tauri-utils",
|
"tauri-utils",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"time",
|
"time",
|
||||||
@@ -4426,23 +4447,23 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-macros"
|
name = "tauri-macros"
|
||||||
version = "2.0.1"
|
version = "2.0.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4d2c0963ccfc3f5194415f2cce7acc975942a8797fbabfb0aa1ed6f59326ae7f"
|
checksum = "8bd1c8d4a66799d3438747c3a79705cd665a95d6f24cb5f315413ff7a981fe2a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heck 0.5.0",
|
"heck 0.5.0",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.79",
|
"syn 2.0.85",
|
||||||
"tauri-codegen",
|
"tauri-codegen",
|
||||||
"tauri-utils",
|
"tauri-utils",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-plugin"
|
name = "tauri-plugin"
|
||||||
version = "2.0.1"
|
version = "2.0.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b2e6660a409963e4d57b9bfab4addd141eeff41bd3a7fb14e13004a832cf7ef6"
|
checksum = "6fa4e6c94cb1d635f65a770c69e23de1bc054b0e4c554fa037a7cc7676333d39"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"glob",
|
"glob",
|
||||||
@@ -4472,9 +4493,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-plugin-dialog"
|
name = "tauri-plugin-dialog"
|
||||||
version = "2.0.1"
|
version = "2.0.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ddb2fe88b602461c118722c574e2775ab26a4e68886680583874b2f6520608b7"
|
checksum = "4307310e1d2c09ab110235834722e7c2b85099b683e1eb7342ab351b0be5ada3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"raw-window-handle",
|
"raw-window-handle",
|
||||||
@@ -4490,9 +4511,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-plugin-fs"
|
name = "tauri-plugin-fs"
|
||||||
version = "2.0.1"
|
version = "2.0.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ab300488ebec3487ca5f56289692e7e45feb07eea8d5e1dba497f7dc9dd9c407"
|
checksum = "96ba7d46e86db8c830d143ef90ab5a453328365b0cc834c24edea4267b16aba0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"dunce",
|
"dunce",
|
||||||
@@ -4511,9 +4532,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-plugin-shell"
|
name = "tauri-plugin-shell"
|
||||||
version = "2.0.1"
|
version = "2.0.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "371fb9aca2823990a2d0db7970573be5fdf07881fcaa2b835b29631feb84aec1"
|
checksum = "0ad7880c5586b6b2104be451e3d7fc0f3800c84bda69e9ba81c828f87cb34267"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"encoding_rs",
|
"encoding_rs",
|
||||||
"log",
|
"log",
|
||||||
@@ -4532,9 +4553,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-plugin-store"
|
name = "tauri-plugin-store"
|
||||||
version = "2.0.1"
|
version = "2.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5058f179f7215390fc5a68eeffcb805b7e2681d6e817a5d08094fae7ab649e68"
|
checksum = "e9a580be53f04bb62422d239aa798e88522877f58a0d4a0e745f030055a51bb4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"dunce",
|
"dunce",
|
||||||
"log",
|
"log",
|
||||||
@@ -4578,9 +4599,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-runtime"
|
name = "tauri-runtime"
|
||||||
version = "2.1.0"
|
version = "2.1.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c8f437293d6f5e5dce829250f4dbdce4e0b52905e297a6689cc2963eb53ac728"
|
checksum = "a1ef7363e7229ac8d04e8a5d405670dbd43dde8fc4bc3bc56105c35452d03784"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"dpi",
|
"dpi",
|
||||||
"gtk",
|
"gtk",
|
||||||
@@ -4597,9 +4618,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-runtime-wry"
|
name = "tauri-runtime-wry"
|
||||||
version = "2.1.1"
|
version = "2.1.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1431602bcc71f2f840ad623915c9842ecc32999b867c4a787d975a17a9625cc6"
|
checksum = "62fa2068e8498ad007b54d5773d03d57c3ff6dd96f8c8ce58beff44d0d5e0d30"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"gtk",
|
"gtk",
|
||||||
"http",
|
"http",
|
||||||
@@ -4623,9 +4644,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-utils"
|
name = "tauri-utils"
|
||||||
version = "2.0.1"
|
version = "2.0.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c38b0230d6880cf6dd07b6d7dd7789a0869f98ac12146e0d18d1c1049215a045"
|
checksum = "1fc65d6f5c54e56b66258948a6d9e47a82ea41f4b5a7612bfbdd1634c2913ed0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"brotli",
|
"brotli",
|
||||||
"cargo_metadata",
|
"cargo_metadata",
|
||||||
@@ -4634,7 +4655,7 @@ dependencies = [
|
|||||||
"glob",
|
"glob",
|
||||||
"html5ever",
|
"html5ever",
|
||||||
"infer",
|
"infer",
|
||||||
"json-patch",
|
"json-patch 2.0.0",
|
||||||
"kuchikiki",
|
"kuchikiki",
|
||||||
"log",
|
"log",
|
||||||
"memchr",
|
"memchr",
|
||||||
@@ -4699,22 +4720,22 @@ checksum = "8eaa81235c7058867fa8c0e7314f33dcce9c215f535d1913822a2b3f5e289f3c"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.64"
|
version = "1.0.65"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84"
|
checksum = "5d11abd9594d9b38965ef50805c5e469ca9cc6f197f883f717e0269a3057b3d5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror-impl",
|
"thiserror-impl",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror-impl"
|
name = "thiserror-impl"
|
||||||
version = "1.0.64"
|
version = "1.0.65"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3"
|
checksum = "ae71770322cbd277e69d762a16c444af02aa0575ac0d174f0b9562d3b37f8602"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.79",
|
"syn 2.0.85",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -4765,9 +4786,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio"
|
name = "tokio"
|
||||||
version = "1.40.0"
|
version = "1.41.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998"
|
checksum = "145f3413504347a2be84393cc8a7d2fb4d863b375909ea59f2158261aa258bbb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"backtrace",
|
"backtrace",
|
||||||
"bytes",
|
"bytes",
|
||||||
@@ -4790,7 +4811,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.79",
|
"syn 2.0.85",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -4938,7 +4959,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.79",
|
"syn 2.0.85",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -5211,9 +5232,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen"
|
name = "wasm-bindgen"
|
||||||
version = "0.2.93"
|
version = "0.2.95"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5"
|
checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
@@ -5222,24 +5243,24 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-backend"
|
name = "wasm-bindgen-backend"
|
||||||
version = "0.2.93"
|
version = "0.2.95"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b"
|
checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
"log",
|
"log",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.79",
|
"syn 2.0.85",
|
||||||
"wasm-bindgen-shared",
|
"wasm-bindgen-shared",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-futures"
|
name = "wasm-bindgen-futures"
|
||||||
version = "0.4.43"
|
version = "0.4.45"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "61e9300f63a621e96ed275155c108eb6f843b6a26d053f122ab69724559dc8ed"
|
checksum = "cc7ec4f8827a71586374db3e87abdb5a2bb3a15afed140221307c3ec06b1f63b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
@@ -5249,9 +5270,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-macro"
|
name = "wasm-bindgen-macro"
|
||||||
version = "0.2.93"
|
version = "0.2.95"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf"
|
checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"quote",
|
"quote",
|
||||||
"wasm-bindgen-macro-support",
|
"wasm-bindgen-macro-support",
|
||||||
@@ -5259,22 +5280,22 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-macro-support"
|
name = "wasm-bindgen-macro-support"
|
||||||
version = "0.2.93"
|
version = "0.2.95"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836"
|
checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.79",
|
"syn 2.0.85",
|
||||||
"wasm-bindgen-backend",
|
"wasm-bindgen-backend",
|
||||||
"wasm-bindgen-shared",
|
"wasm-bindgen-shared",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-shared"
|
name = "wasm-bindgen-shared"
|
||||||
version = "0.2.93"
|
version = "0.2.95"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484"
|
checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-streams"
|
name = "wasm-streams"
|
||||||
@@ -5305,9 +5326,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wayland-client"
|
name = "wayland-client"
|
||||||
version = "0.31.6"
|
version = "0.31.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e3f45d1222915ef1fd2057220c1d9d9624b7654443ea35c3877f7a52bd0a5a2d"
|
checksum = "b66249d3fc69f76fd74c82cc319300faa554e9d865dab1f7cd66cc20db10b280"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.6.0",
|
"bitflags 2.6.0",
|
||||||
"rustix",
|
"rustix",
|
||||||
@@ -5317,9 +5338,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wayland-protocols"
|
name = "wayland-protocols"
|
||||||
version = "0.32.4"
|
version = "0.32.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2b5755d77ae9040bb872a25026555ce4cb0ae75fd923e90d25fba07d81057de0"
|
checksum = "7cd0ade57c4e6e9a8952741325c30bf82f4246885dca8bf561898b86d0c1f58e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.6.0",
|
"bitflags 2.6.0",
|
||||||
"wayland-backend",
|
"wayland-backend",
|
||||||
@@ -5351,9 +5372,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "web-sys"
|
name = "web-sys"
|
||||||
version = "0.3.70"
|
version = "0.3.72"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0"
|
checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
@@ -5434,7 +5455,7 @@ checksum = "1d228f15bba3b9d56dde8bddbee66fa24545bd17b48d5128ccf4a8742b18e431"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.79",
|
"syn 2.0.85",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -5533,7 +5554,7 @@ checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.79",
|
"syn 2.0.85",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -5544,7 +5565,7 @@ checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.79",
|
"syn 2.0.85",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -5830,9 +5851,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wry"
|
name = "wry"
|
||||||
version = "0.46.2"
|
version = "0.46.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6fa1c8c760041c64ce6be99f83d6cb55fe3fcd85a1ad46d32895f6e65cee87ba"
|
checksum = "cd5cdf57c66813d97601181349c63b96994b3074fc3d7a31a8cce96e968e3bbd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
"block2",
|
"block2",
|
||||||
@@ -6000,7 +6021,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.79",
|
"syn 2.0.85",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -6020,7 +6041,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.79",
|
"syn 2.0.85",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
name = "hoppscotch-agent"
|
name = "hoppscotch-agent"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
description = "A cross-platform HTTP request agent for Hoppscotch for advanced request handling including custom headers, certificates, proxies, and local system integration."
|
description = "A cross-platform HTTP request agent for Hoppscotch for advanced request handling including custom headers, certificates, proxies, and local system integration."
|
||||||
authors = ["CuriousCorrelation", "AndrewBastin"]
|
authors = ["AndrewBastin", "CuriousCorrelation"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
@@ -31,18 +31,14 @@ chrono = { version = "0.4", features = ["serde"] }
|
|||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
log = "0.4.22"
|
log = "0.4.22"
|
||||||
env_logger = "0.11.5"
|
env_logger = "0.11.5"
|
||||||
curl = { version = "0.4.47", features = ["ntlm", "static-curl", "static-ssl"] }
|
hoppscotch-relay = { path = "../../hoppscotch-relay" }
|
||||||
openssl = { version = "0.10.68", features = ["vendored"] }
|
|
||||||
openssl-sys = { version = "0.9.104", features = ["vendored"] }
|
|
||||||
url-escape = "0.1.1"
|
|
||||||
thiserror = "1.0.64"
|
thiserror = "1.0.64"
|
||||||
tauri-plugin-store = "2.0.1"
|
tauri-plugin-store = "2.1.0"
|
||||||
x25519-dalek = { version = "2.0.1", features = ["getrandom"] }
|
x25519-dalek = { version = "2.0.1", features = ["getrandom"] }
|
||||||
base16 = "0.2.1"
|
base16 = "0.2.1"
|
||||||
aes-gcm = { version = "0.10.3", features = ["aes"] }
|
aes-gcm = { version = "0.10.3", features = ["aes"] }
|
||||||
tauri-plugin-updater = "2.0.2"
|
tauri-plugin-updater = "2.0.2"
|
||||||
tauri-plugin-dialog = "2.0.1"
|
tauri-plugin-dialog = "2.0.1"
|
||||||
http = "1.1.0"
|
|
||||||
lazy_static = "1.5.0"
|
lazy_static = "1.5.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
|||||||
@@ -8,16 +8,14 @@ use axum_extra::{
|
|||||||
headers::{authorization::Bearer, Authorization},
|
headers::{authorization::Bearer, Authorization},
|
||||||
TypedHeader,
|
TypedHeader,
|
||||||
};
|
};
|
||||||
|
use hoppscotch_relay::{RequestWithMetadata, ResponseWithMetadata};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tauri::{AppHandle, Emitter};
|
use tauri::{AppHandle, Emitter};
|
||||||
use x25519_dalek::{EphemeralSecret, PublicKey};
|
use x25519_dalek::{EphemeralSecret, PublicKey};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
error::{AppError, AppResult},
|
error::{AppError, AppResult},
|
||||||
model::{
|
model::{AuthKeyResponse, ConfirmedRegistrationRequest, HandshakeResponse},
|
||||||
AuthKeyResponse, ConfirmedRegistrationRequest, HandshakeResponse, RequestDef,
|
|
||||||
RunRequestResponse,
|
|
||||||
},
|
|
||||||
state::{AppState, Registration},
|
state::{AppState, Registration},
|
||||||
util::EncryptedJson,
|
util::EncryptedJson,
|
||||||
};
|
};
|
||||||
@@ -27,19 +25,18 @@ use serde_json::json;
|
|||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
fn generate_otp() -> String {
|
fn generate_otp() -> String {
|
||||||
let otp: u32 = rand::thread_rng().gen_range(0..1_000_000);
|
let otp: u32 = rand::thread_rng().gen_range(0..1_000_000);
|
||||||
|
|
||||||
format!("{:06}", otp)
|
format!("{:06}", otp)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn handshake(
|
pub async fn handshake(
|
||||||
State((_, app_handle)): State<(Arc<AppState>, AppHandle)>
|
State((_, app_handle)): State<(Arc<AppState>, AppHandle)>,
|
||||||
) -> AppResult<Json<HandshakeResponse>> {
|
) -> AppResult<Json<HandshakeResponse>> {
|
||||||
Ok(Json(HandshakeResponse {
|
Ok(Json(HandshakeResponse {
|
||||||
status: "success".to_string(),
|
status: "success".to_string(),
|
||||||
__hoppscotch__agent__: true,
|
__hoppscotch__agent__: true,
|
||||||
agent_version: app_handle.package_info().version.to_string()
|
agent_version: app_handle.package_info().version.to_string(),
|
||||||
|
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,11 +83,11 @@ pub async fn verify_registration(
|
|||||||
let agent_public_key = PublicKey::from(&agent_secret_key);
|
let agent_public_key = PublicKey::from(&agent_secret_key);
|
||||||
|
|
||||||
let their_public_key = {
|
let their_public_key = {
|
||||||
let public_key_slice: &[u8; 32] = &base16::decode(&confirmed_registration.client_public_key_b16)
|
let public_key_slice: &[u8; 32] =
|
||||||
.map_err(|_| AppError::InvalidClientPublicKey)?
|
&base16::decode(&confirmed_registration.client_public_key_b16)
|
||||||
[0..32]
|
.map_err(|_| AppError::InvalidClientPublicKey)?[0..32]
|
||||||
.try_into()
|
.try_into()
|
||||||
.map_err(|_| AppError::InvalidClientPublicKey)?;
|
.map_err(|_| AppError::InvalidClientPublicKey)?;
|
||||||
|
|
||||||
PublicKey::from(public_key_slice.to_owned())
|
PublicKey::from(public_key_slice.to_owned())
|
||||||
};
|
};
|
||||||
@@ -98,10 +95,13 @@ pub async fn verify_registration(
|
|||||||
let shared_secret = agent_secret_key.diffie_hellman(&their_public_key);
|
let shared_secret = agent_secret_key.diffie_hellman(&their_public_key);
|
||||||
|
|
||||||
let _ = state.update_registrations(app_handle.clone(), |regs| {
|
let _ = state.update_registrations(app_handle.clone(), |regs| {
|
||||||
regs.insert(auth_key_copy, Registration {
|
regs.insert(
|
||||||
registered_at: created_at,
|
auth_key_copy,
|
||||||
shared_secret_b16: base16::encode_lower(shared_secret.as_bytes())
|
Registration {
|
||||||
});
|
registered_at: created_at,
|
||||||
|
shared_secret_b16: base16::encode_lower(shared_secret.as_bytes()),
|
||||||
|
},
|
||||||
|
);
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let auth_payload = json!({
|
let auth_payload = json!({
|
||||||
@@ -124,25 +124,28 @@ pub async fn run_request<T>(
|
|||||||
State((state, _app_handle)): State<(Arc<AppState>, T)>,
|
State((state, _app_handle)): State<(Arc<AppState>, T)>,
|
||||||
TypedHeader(auth_header): TypedHeader<Authorization<Bearer>>,
|
TypedHeader(auth_header): TypedHeader<Authorization<Bearer>>,
|
||||||
headers: HeaderMap,
|
headers: HeaderMap,
|
||||||
body: Bytes
|
body: Bytes,
|
||||||
) -> AppResult<EncryptedJson<RunRequestResponse>> {
|
) -> AppResult<EncryptedJson<ResponseWithMetadata>> {
|
||||||
let nonce = headers.get("X-Hopp-Nonce")
|
let nonce = headers
|
||||||
.ok_or(AppError::Unauthorized)?
|
.get("X-Hopp-Nonce")
|
||||||
.to_str()
|
.ok_or(AppError::Unauthorized)?
|
||||||
.map_err(|_| AppError::Unauthorized)?;
|
.to_str()
|
||||||
|
.map_err(|_| AppError::Unauthorized)?;
|
||||||
|
|
||||||
let req: RequestDef = state.validate_access_and_get_data(auth_header.token(), nonce, &body)
|
let req: RequestWithMetadata = state
|
||||||
|
.validate_access_and_get_data(auth_header.token(), nonce, &body)
|
||||||
.ok_or(AppError::Unauthorized)?;
|
.ok_or(AppError::Unauthorized)?;
|
||||||
|
|
||||||
let reg_info = state.get_registration_info(auth_header.token())
|
let req_id = req.req_id;
|
||||||
|
|
||||||
|
let reg_info = state
|
||||||
|
.get_registration_info(auth_header.token())
|
||||||
.ok_or(AppError::Unauthorized)?;
|
.ok_or(AppError::Unauthorized)?;
|
||||||
|
|
||||||
let cancel_token = tokio_util::sync::CancellationToken::new();
|
let cancel_token = tokio_util::sync::CancellationToken::new();
|
||||||
state.add_cancellation_token(req.req_id, cancel_token.clone());
|
state.add_cancellation_token(req.req_id, cancel_token.clone());
|
||||||
|
|
||||||
let req_id = req.req_id;
|
|
||||||
let cancel_token_clone = cancel_token.clone();
|
let cancel_token_clone = cancel_token.clone();
|
||||||
|
|
||||||
// Execute the HTTP request in a blocking thread pool and handles cancellation.
|
// Execute the HTTP request in a blocking thread pool and handles cancellation.
|
||||||
//
|
//
|
||||||
// It:
|
// It:
|
||||||
@@ -158,9 +161,9 @@ pub async fn run_request<T>(
|
|||||||
// - `spawn_blocking` moves this operation to a thread pool designed for
|
// - `spawn_blocking` moves this operation to a thread pool designed for
|
||||||
// blocking tasks, so other async operations to continue unblocked.
|
// blocking tasks, so other async operations to continue unblocked.
|
||||||
let result = tokio::select! {
|
let result = tokio::select! {
|
||||||
res = tokio::task::spawn_blocking(move || crate::interceptor::run_request_task(&req, cancel_token_clone)) => {
|
res = tokio::task::spawn_blocking(move || hoppscotch_relay::run_request_task(&req, cancel_token_clone)) => {
|
||||||
match res {
|
match res {
|
||||||
Ok(task_result) => task_result,
|
Ok(task_result) => Ok(task_result?),
|
||||||
Err(_) => Err(AppError::InternalServerError),
|
Err(_) => Err(AppError::InternalServerError),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -171,11 +174,9 @@ pub async fn run_request<T>(
|
|||||||
|
|
||||||
state.remove_cancellation_token(req_id);
|
state.remove_cancellation_token(req_id);
|
||||||
|
|
||||||
result.map(|val| {
|
result.map(|val| EncryptedJson {
|
||||||
EncryptedJson {
|
|
||||||
key_b16: reg_info.shared_secret_b16,
|
key_b16: reg_info.shared_secret_b16,
|
||||||
data: val
|
data: val,
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -42,6 +42,8 @@ pub enum AppError {
|
|||||||
RegistrationSaveError,
|
RegistrationSaveError,
|
||||||
#[error("Store error: {0}")]
|
#[error("Store error: {0}")]
|
||||||
TauriPluginStore(#[from] tauri_plugin_store::Error),
|
TauriPluginStore(#[from] tauri_plugin_store::Error),
|
||||||
|
#[error("Relay error: {0}")]
|
||||||
|
Relay(#[from] hoppscotch_relay::RelayError),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IntoResponse for AppError {
|
impl IntoResponse for AppError {
|
||||||
|
|||||||
@@ -1,567 +0,0 @@
|
|||||||
use crate::{
|
|
||||||
error::AppError,
|
|
||||||
model::{BodyDef, ClientCertDef, FormDataValue, KeyValuePair, RequestDef, RunRequestResponse},
|
|
||||||
util::get_status_text,
|
|
||||||
};
|
|
||||||
use curl::easy::{Easy, List};
|
|
||||||
use openssl::{pkcs12::Pkcs12, ssl::SslContextBuilder, x509::X509};
|
|
||||||
use openssl_sys::SSL_CTX;
|
|
||||||
use std::time::SystemTime;
|
|
||||||
use tokio_util::sync::CancellationToken;
|
|
||||||
|
|
||||||
pub(crate) fn run_request_task(
|
|
||||||
req: &RequestDef,
|
|
||||||
cancel_token: CancellationToken,
|
|
||||||
) -> Result<RunRequestResponse, AppError> {
|
|
||||||
let mut curl_handle = Easy::new();
|
|
||||||
|
|
||||||
curl_handle
|
|
||||||
.progress(true)
|
|
||||||
.map_err(|err| AppError::RequestRunError(err.description().to_string()))?;
|
|
||||||
|
|
||||||
curl_handle
|
|
||||||
.custom_request(&req.method)
|
|
||||||
.map_err(|_| AppError::InvalidMethod)?;
|
|
||||||
|
|
||||||
curl_handle
|
|
||||||
.url(&req.endpoint)
|
|
||||||
.map_err(|_| AppError::InvalidUrl)?;
|
|
||||||
|
|
||||||
curl_handle
|
|
||||||
.http_headers(get_headers_list(&req)?)
|
|
||||||
.map_err(|_| AppError::InvalidHeaders)?;
|
|
||||||
|
|
||||||
apply_body_to_curl_handle(&mut curl_handle, &req)?;
|
|
||||||
|
|
||||||
curl_handle
|
|
||||||
.ssl_verify_peer(req.validate_certs)
|
|
||||||
.map_err(|err| AppError::RequestRunError(err.description().to_string()))?;
|
|
||||||
|
|
||||||
curl_handle
|
|
||||||
.ssl_verify_host(req.validate_certs)
|
|
||||||
.map_err(|err| AppError::RequestRunError(err.description().to_string()))?;
|
|
||||||
|
|
||||||
apply_client_cert_to_curl_handle(&mut curl_handle, &req)?;
|
|
||||||
|
|
||||||
apply_proxy_config_to_curl_handle(&mut curl_handle, &req)?;
|
|
||||||
|
|
||||||
let mut response_body = Vec::new();
|
|
||||||
let mut response_headers = Vec::new();
|
|
||||||
|
|
||||||
let (start_time_ms, end_time_ms) = {
|
|
||||||
let mut transfer = curl_handle.transfer();
|
|
||||||
|
|
||||||
transfer
|
|
||||||
.ssl_ctx_function(|ssl_ctx_ptr| {
|
|
||||||
let cert_list = get_x509_certs_from_root_cert_bundle(&req);
|
|
||||||
|
|
||||||
if !cert_list.is_empty() {
|
|
||||||
let mut ssl_ctx_builder =
|
|
||||||
unsafe { SslContextBuilder::from_ptr(ssl_ctx_ptr as *mut SSL_CTX) };
|
|
||||||
|
|
||||||
let cert_store = ssl_ctx_builder.cert_store_mut();
|
|
||||||
|
|
||||||
for cert in cert_list {
|
|
||||||
if let Err(e) = cert_store.add_cert(cert) {
|
|
||||||
eprintln!("Failed writing cert into cert store: {}", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
.map_err(|err| AppError::RequestRunError(err.description().to_string()))?;
|
|
||||||
|
|
||||||
transfer
|
|
||||||
.progress_function(|_, _, _, _| !cancel_token.is_cancelled())
|
|
||||||
.map_err(|err| AppError::RequestRunError(err.description().to_string()))?;
|
|
||||||
|
|
||||||
transfer
|
|
||||||
.header_function(|header| {
|
|
||||||
let header = String::from_utf8_lossy(header).into_owned();
|
|
||||||
|
|
||||||
if let Some((key, value)) = header.split_once(':') {
|
|
||||||
response_headers.push(KeyValuePair {
|
|
||||||
key: key.trim().to_string(),
|
|
||||||
value: value.trim().to_string(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
true
|
|
||||||
})
|
|
||||||
.map_err(|err| AppError::RequestRunError(err.description().to_string()))?;
|
|
||||||
|
|
||||||
transfer
|
|
||||||
.write_function(|data| {
|
|
||||||
response_body.extend_from_slice(data);
|
|
||||||
Ok(data.len())
|
|
||||||
})
|
|
||||||
.map_err(|err| AppError::RequestRunError(err.description().to_string()))?;
|
|
||||||
|
|
||||||
let start_time_ms = SystemTime::now()
|
|
||||||
.duration_since(std::time::UNIX_EPOCH)
|
|
||||||
.unwrap()
|
|
||||||
.as_millis();
|
|
||||||
|
|
||||||
transfer
|
|
||||||
.perform()
|
|
||||||
.map_err(|err| AppError::RequestRunError(err.description().to_string()))?;
|
|
||||||
|
|
||||||
let end_time_ms = SystemTime::now()
|
|
||||||
.duration_since(std::time::UNIX_EPOCH)
|
|
||||||
.unwrap()
|
|
||||||
.as_millis();
|
|
||||||
|
|
||||||
(start_time_ms, end_time_ms)
|
|
||||||
};
|
|
||||||
|
|
||||||
let response_status = curl_handle
|
|
||||||
.response_code()
|
|
||||||
.map_err(|err| AppError::RequestRunError(err.description().to_string()))?
|
|
||||||
as u16;
|
|
||||||
|
|
||||||
let response_status_text = get_status_text(response_status).to_string();
|
|
||||||
|
|
||||||
Ok(RunRequestResponse {
|
|
||||||
status: response_status,
|
|
||||||
status_text: response_status_text,
|
|
||||||
headers: response_headers,
|
|
||||||
data: response_body,
|
|
||||||
time_start_ms: start_time_ms,
|
|
||||||
time_end_ms: end_time_ms,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_headers_list(req: &RequestDef) -> Result<List, AppError> {
|
|
||||||
let mut result = List::new();
|
|
||||||
|
|
||||||
for KeyValuePair { key, value } in &req.headers {
|
|
||||||
result
|
|
||||||
.append(&format!("{}: {}", key, value))
|
|
||||||
.map_err(|err| AppError::RequestRunError(err.description().to_string()))?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(result)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn apply_body_to_curl_handle(curl_handle: &mut Easy, req: &RequestDef) -> Result<(), AppError> {
|
|
||||||
match &req.body {
|
|
||||||
Some(BodyDef::Text(text)) => {
|
|
||||||
curl_handle
|
|
||||||
.post_fields_copy(text.as_bytes())
|
|
||||||
.map_err(|err| {
|
|
||||||
AppError::RequestRunError(format!(
|
|
||||||
"Error while setting body: {}",
|
|
||||||
err.description()
|
|
||||||
))
|
|
||||||
})?;
|
|
||||||
}
|
|
||||||
Some(BodyDef::FormData(entries)) => {
|
|
||||||
let mut form = curl::easy::Form::new();
|
|
||||||
|
|
||||||
for entry in entries {
|
|
||||||
let mut part = form.part(&entry.key);
|
|
||||||
|
|
||||||
match &entry.value {
|
|
||||||
FormDataValue::Text(data) => {
|
|
||||||
part.contents(data.as_bytes());
|
|
||||||
}
|
|
||||||
FormDataValue::File {
|
|
||||||
filename,
|
|
||||||
data,
|
|
||||||
mime,
|
|
||||||
} => {
|
|
||||||
part.buffer(filename, data.clone()).content_type(mime);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
part.add().map_err(|err| {
|
|
||||||
AppError::RequestRunError(format!(
|
|
||||||
"Error while setting body: {}",
|
|
||||||
err.description()
|
|
||||||
))
|
|
||||||
})?;
|
|
||||||
}
|
|
||||||
|
|
||||||
curl_handle.httppost(form).map_err(|err| {
|
|
||||||
AppError::RequestRunError(format!(
|
|
||||||
"Error while setting body: {}",
|
|
||||||
err.description()
|
|
||||||
))
|
|
||||||
})?;
|
|
||||||
}
|
|
||||||
Some(BodyDef::URLEncoded(entries)) => {
|
|
||||||
let data = entries
|
|
||||||
.iter()
|
|
||||||
.map(|KeyValuePair { key, value }| {
|
|
||||||
format!(
|
|
||||||
"{}={}",
|
|
||||||
&url_escape::encode_www_form_urlencoded(key),
|
|
||||||
url_escape::encode_www_form_urlencoded(value)
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect::<Vec<String>>()
|
|
||||||
.join("&");
|
|
||||||
|
|
||||||
curl_handle
|
|
||||||
.post_fields_copy(data.as_bytes())
|
|
||||||
.map_err(|err| {
|
|
||||||
AppError::RequestRunError(format!(
|
|
||||||
"Error while setting body: {}",
|
|
||||||
err.description()
|
|
||||||
))
|
|
||||||
})?;
|
|
||||||
}
|
|
||||||
None => {}
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn apply_client_cert_to_curl_handle(handle: &mut Easy, req: &RequestDef) -> Result<(), AppError> {
|
|
||||||
match &req.client_cert {
|
|
||||||
Some(ClientCertDef::PEMCert {
|
|
||||||
certificate_pem,
|
|
||||||
key_pem,
|
|
||||||
}) => {
|
|
||||||
handle.ssl_cert_type("PEM").map_err(|err| {
|
|
||||||
AppError::RequestRunError(format!(
|
|
||||||
"Failed setting PEM Cert Type: {}",
|
|
||||||
err.description()
|
|
||||||
))
|
|
||||||
})?;
|
|
||||||
|
|
||||||
handle.ssl_cert_blob(certificate_pem).map_err(|err| {
|
|
||||||
AppError::RequestRunError(format!(
|
|
||||||
"Failed setting PEM Cert Blob: {}",
|
|
||||||
err.description()
|
|
||||||
))
|
|
||||||
})?;
|
|
||||||
|
|
||||||
handle.ssl_key_type("PEM").map_err(|err| {
|
|
||||||
AppError::RequestRunError(format!(
|
|
||||||
"Failed setting PEM key type: {}",
|
|
||||||
err.description()
|
|
||||||
))
|
|
||||||
})?;
|
|
||||||
|
|
||||||
handle.ssl_key_blob(key_pem).map_err(|err| {
|
|
||||||
AppError::RequestRunError(format!(
|
|
||||||
"Failed setting PEM Cert blob: {}",
|
|
||||||
err.description()
|
|
||||||
))
|
|
||||||
})?;
|
|
||||||
}
|
|
||||||
Some(ClientCertDef::PFXCert {
|
|
||||||
certificate_pfx,
|
|
||||||
password,
|
|
||||||
}) => {
|
|
||||||
let pkcs12 = Pkcs12::from_der(&certificate_pfx).map_err(|err| {
|
|
||||||
AppError::RequestRunError(format!(
|
|
||||||
"Failed to parse PFX certificate from DER: {}",
|
|
||||||
err
|
|
||||||
))
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let parsed = pkcs12.parse2(password).map_err(|err| {
|
|
||||||
AppError::RequestRunError(format!(
|
|
||||||
"Failed to parse PFX certificate with provided password: {}",
|
|
||||||
err
|
|
||||||
))
|
|
||||||
})?;
|
|
||||||
|
|
||||||
if let (Some(cert), Some(key)) = (parsed.cert, parsed.pkey) {
|
|
||||||
let certificate_pem = cert.to_pem().map_err(|err| {
|
|
||||||
AppError::RequestRunError(format!(
|
|
||||||
"Failed to convert PFX certificate to PEM format: {}",
|
|
||||||
err
|
|
||||||
))
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let key_pem = key.private_key_to_pem_pkcs8().map_err(|err| {
|
|
||||||
AppError::RequestRunError(format!(
|
|
||||||
"Failed to convert PFX private key to PEM format: {}",
|
|
||||||
err
|
|
||||||
))
|
|
||||||
})?;
|
|
||||||
|
|
||||||
handle.ssl_cert_type("PEM").map_err(|err| {
|
|
||||||
AppError::RequestRunError(format!(
|
|
||||||
"Failed setting PEM Cert Type for converted PFX: {}",
|
|
||||||
err.description()
|
|
||||||
))
|
|
||||||
})?;
|
|
||||||
|
|
||||||
handle.ssl_cert_blob(&certificate_pem).map_err(|err| {
|
|
||||||
AppError::RequestRunError(format!(
|
|
||||||
"Failed setting PEM Cert Blob for converted PFX: {}",
|
|
||||||
err.description()
|
|
||||||
))
|
|
||||||
})?;
|
|
||||||
|
|
||||||
handle.ssl_key_type("PEM").map_err(|err| {
|
|
||||||
AppError::RequestRunError(format!(
|
|
||||||
"Failed setting PEM key type for converted PFX: {}",
|
|
||||||
err.description()
|
|
||||||
))
|
|
||||||
})?;
|
|
||||||
|
|
||||||
handle.ssl_key_blob(&key_pem).map_err(|err| {
|
|
||||||
AppError::RequestRunError(format!(
|
|
||||||
"Failed setting PEM key blob for converted PFX: {}",
|
|
||||||
err.description()
|
|
||||||
))
|
|
||||||
})?;
|
|
||||||
} else {
|
|
||||||
return Err(AppError::RequestRunError(
|
|
||||||
"PFX certificate parsing succeeded, but either cert or private key is missing"
|
|
||||||
.to_string(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => {}
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_x509_certs_from_root_cert_bundle(req: &RequestDef) -> Vec<X509> {
|
|
||||||
req.root_cert_bundle_files
|
|
||||||
.iter()
|
|
||||||
.map(|pem_bundle| openssl::x509::X509::stack_from_pem(pem_bundle))
|
|
||||||
.filter_map(|certs| {
|
|
||||||
if let Ok(certs) = certs {
|
|
||||||
Some(certs)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.flatten()
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn apply_proxy_config_to_curl_handle(handle: &mut Easy, req: &RequestDef) -> Result<(), AppError> {
|
|
||||||
if let Some(proxy_config) = &req.proxy {
|
|
||||||
handle
|
|
||||||
.proxy_auth(curl::easy::Auth::new().auto(true))
|
|
||||||
.map_err(|err| {
|
|
||||||
AppError::RequestRunError(format!(
|
|
||||||
"Failed to set proxy Auth Mode: {}",
|
|
||||||
err.description()
|
|
||||||
))
|
|
||||||
})?;
|
|
||||||
|
|
||||||
handle.proxy(&proxy_config.url).map_err(|err| {
|
|
||||||
AppError::RequestRunError(format!("Failed to set proxy URL: {}", err.description()))
|
|
||||||
})?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use crate::model::FormDataEntry;
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
use mockito::Server;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_run_request_task_success() {
|
|
||||||
let mut server = Server::new();
|
|
||||||
let mock = server
|
|
||||||
.mock("GET", "/test")
|
|
||||||
.with_status(200)
|
|
||||||
.with_header("content-type", "text/plain")
|
|
||||||
.with_body("Hello, World!")
|
|
||||||
.create();
|
|
||||||
|
|
||||||
let req = RequestDef {
|
|
||||||
req_id: 1,
|
|
||||||
method: "GET".to_string(),
|
|
||||||
endpoint: format!("{}/test", server.url()),
|
|
||||||
headers: vec![],
|
|
||||||
body: None,
|
|
||||||
validate_certs: false,
|
|
||||||
root_cert_bundle_files: vec![],
|
|
||||||
client_cert: None,
|
|
||||||
proxy: None,
|
|
||||||
};
|
|
||||||
let cancel_token = CancellationToken::new();
|
|
||||||
|
|
||||||
let result = run_request_task(&req, cancel_token);
|
|
||||||
assert!(result.is_ok());
|
|
||||||
|
|
||||||
let response = result.unwrap();
|
|
||||||
assert_eq!(response.status, 200);
|
|
||||||
assert_eq!(response.status_text, "OK");
|
|
||||||
assert!(response
|
|
||||||
.headers
|
|
||||||
.iter()
|
|
||||||
.any(|h| h.key == "content-type" && h.value == "text/plain"));
|
|
||||||
assert_eq!(response.data, b"Hello, World!");
|
|
||||||
|
|
||||||
mock.assert();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_run_request_task_with_headers() {
|
|
||||||
let mut server = Server::new();
|
|
||||||
let mock = server
|
|
||||||
.mock("GET", "/test")
|
|
||||||
.match_header("X-Custom-Header", "TestValue")
|
|
||||||
.with_status(200)
|
|
||||||
.create();
|
|
||||||
|
|
||||||
let req = RequestDef {
|
|
||||||
req_id: 1,
|
|
||||||
method: "GET".to_string(),
|
|
||||||
endpoint: format!("{}/test", server.url()),
|
|
||||||
headers: vec![KeyValuePair {
|
|
||||||
key: "X-Custom-Header".to_string(),
|
|
||||||
value: "TestValue".to_string(),
|
|
||||||
}],
|
|
||||||
body: None,
|
|
||||||
validate_certs: false,
|
|
||||||
root_cert_bundle_files: vec![],
|
|
||||||
client_cert: None,
|
|
||||||
proxy: None,
|
|
||||||
};
|
|
||||||
let cancel_token = CancellationToken::new();
|
|
||||||
|
|
||||||
let result = run_request_task(&req, cancel_token);
|
|
||||||
assert!(result.is_ok());
|
|
||||||
|
|
||||||
mock.assert();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_run_request_task_with_body() {
|
|
||||||
let mut server = Server::new();
|
|
||||||
let mock = server
|
|
||||||
.mock("POST", "/test")
|
|
||||||
.match_body("test_body")
|
|
||||||
.with_status(201)
|
|
||||||
.create();
|
|
||||||
|
|
||||||
let req = RequestDef {
|
|
||||||
req_id: 1,
|
|
||||||
method: "POST".to_string(),
|
|
||||||
endpoint: format!("{}/test", server.url()),
|
|
||||||
headers: vec![],
|
|
||||||
body: Some(BodyDef::Text("test_body".to_string())),
|
|
||||||
validate_certs: false,
|
|
||||||
root_cert_bundle_files: vec![],
|
|
||||||
client_cert: None,
|
|
||||||
proxy: None,
|
|
||||||
};
|
|
||||||
let cancel_token = CancellationToken::new();
|
|
||||||
|
|
||||||
let result = run_request_task(&req, cancel_token);
|
|
||||||
assert!(result.is_ok());
|
|
||||||
assert_eq!(result.unwrap().status, 201);
|
|
||||||
|
|
||||||
mock.assert();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_run_request_task_with_url_encoded_body() {
|
|
||||||
let mut server = Server::new();
|
|
||||||
let mock = server
|
|
||||||
.mock("POST", "/test")
|
|
||||||
.match_body("key1=value1&key2=value2")
|
|
||||||
.with_status(200)
|
|
||||||
.create();
|
|
||||||
|
|
||||||
let req = RequestDef {
|
|
||||||
req_id: 1,
|
|
||||||
method: "POST".to_string(),
|
|
||||||
endpoint: format!("{}/test", server.url()),
|
|
||||||
headers: vec![],
|
|
||||||
body: Some(BodyDef::URLEncoded(vec![
|
|
||||||
KeyValuePair {
|
|
||||||
key: "key1".to_string(),
|
|
||||||
value: "value1".to_string(),
|
|
||||||
},
|
|
||||||
KeyValuePair {
|
|
||||||
key: "key2".to_string(),
|
|
||||||
value: "value2".to_string(),
|
|
||||||
},
|
|
||||||
])),
|
|
||||||
validate_certs: false,
|
|
||||||
root_cert_bundle_files: vec![],
|
|
||||||
client_cert: None,
|
|
||||||
proxy: None,
|
|
||||||
};
|
|
||||||
let cancel_token = CancellationToken::new();
|
|
||||||
|
|
||||||
let result = run_request_task(&req, cancel_token);
|
|
||||||
assert!(result.is_ok());
|
|
||||||
|
|
||||||
mock.assert();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_run_request_task_with_invalid_url() {
|
|
||||||
let req = RequestDef {
|
|
||||||
req_id: 1,
|
|
||||||
method: "GET".to_string(),
|
|
||||||
endpoint: "invalid_url".to_string(),
|
|
||||||
headers: vec![],
|
|
||||||
body: None,
|
|
||||||
validate_certs: false,
|
|
||||||
root_cert_bundle_files: vec![],
|
|
||||||
client_cert: None,
|
|
||||||
proxy: None,
|
|
||||||
};
|
|
||||||
let cancel_token = CancellationToken::new();
|
|
||||||
|
|
||||||
let result = run_request_task(&req, cancel_token);
|
|
||||||
|
|
||||||
assert!(result.is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_run_request_task_with_form_data() {
|
|
||||||
let mut server = Server::new();
|
|
||||||
let mock = server
|
|
||||||
.mock("POST", "/test")
|
|
||||||
.match_header(
|
|
||||||
"content-type",
|
|
||||||
mockito::Matcher::Regex("multipart/form-data.*".to_string()),
|
|
||||||
)
|
|
||||||
.with_status(200)
|
|
||||||
.create();
|
|
||||||
|
|
||||||
let req = RequestDef {
|
|
||||||
req_id: 1,
|
|
||||||
method: "POST".to_string(),
|
|
||||||
endpoint: format!("{}/test", server.url()),
|
|
||||||
headers: vec![],
|
|
||||||
body: Some(BodyDef::FormData(vec![
|
|
||||||
FormDataEntry {
|
|
||||||
key: "text_field".to_string(),
|
|
||||||
value: FormDataValue::Text("text_value".to_string()),
|
|
||||||
},
|
|
||||||
FormDataEntry {
|
|
||||||
key: "file_field".to_string(),
|
|
||||||
value: FormDataValue::File {
|
|
||||||
filename: "test.txt".to_string(),
|
|
||||||
data: b"file_content".to_vec(),
|
|
||||||
mime: "text/plain".to_string(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
])),
|
|
||||||
validate_certs: false,
|
|
||||||
root_cert_bundle_files: vec![],
|
|
||||||
client_cert: None,
|
|
||||||
proxy: None,
|
|
||||||
};
|
|
||||||
let cancel_token = CancellationToken::new();
|
|
||||||
|
|
||||||
let result = run_request_task(&req, cancel_token);
|
|
||||||
assert!(result.is_ok());
|
|
||||||
|
|
||||||
mock.assert();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
pub mod controller;
|
pub mod controller;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod interceptor;
|
|
||||||
pub mod model;
|
pub mod model;
|
||||||
pub mod route;
|
pub mod route;
|
||||||
pub mod server;
|
pub mod server;
|
||||||
@@ -73,7 +72,7 @@ pub fn run() {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
let app_state = Arc::new(AppState::new(app_handle.clone()));
|
let app_state = Arc::new(AppState::new(app_handle.clone())?);
|
||||||
|
|
||||||
app.manage(app_state.clone());
|
app.manage(app_state.clone());
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ pub struct HandshakeResponse {
|
|||||||
pub __hoppscotch__agent__: bool,
|
pub __hoppscotch__agent__: bool,
|
||||||
|
|
||||||
pub status: String,
|
pub status: String,
|
||||||
pub agent_version: String
|
pub agent_version: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
@@ -18,7 +18,7 @@ pub struct ConfirmedRegistrationRequest {
|
|||||||
/// to the agent so that the agent can establish a shared secret
|
/// to the agent so that the agent can establish a shared secret
|
||||||
/// which will be used to encrypt traffic between agent
|
/// which will be used to encrypt traffic between agent
|
||||||
/// and client after registration
|
/// and client after registration
|
||||||
pub client_public_key_b16: String
|
pub client_public_key_b16: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
@@ -30,74 +30,5 @@ pub struct AuthKeyResponse {
|
|||||||
/// agent so that the client can establish a shared secret
|
/// agent so that the client can establish a shared secret
|
||||||
/// which will be used to encrypt traffic between agent
|
/// which will be used to encrypt traffic between agent
|
||||||
/// and client after registration
|
/// and client after registration
|
||||||
pub agent_public_key_b16: String
|
pub agent_public_key_b16: String,
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
|
||||||
pub struct KeyValuePair {
|
|
||||||
pub key: String,
|
|
||||||
pub value: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
pub enum FormDataValue {
|
|
||||||
Text(String),
|
|
||||||
File {
|
|
||||||
filename: String,
|
|
||||||
data: Vec<u8>,
|
|
||||||
mime: String,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
pub struct FormDataEntry {
|
|
||||||
pub key: String,
|
|
||||||
pub value: FormDataValue,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
pub enum BodyDef {
|
|
||||||
Text(String),
|
|
||||||
URLEncoded(Vec<KeyValuePair>),
|
|
||||||
FormData(Vec<FormDataEntry>),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
pub struct RequestDef {
|
|
||||||
pub req_id: usize,
|
|
||||||
pub method: String,
|
|
||||||
pub endpoint: String,
|
|
||||||
pub headers: Vec<KeyValuePair>,
|
|
||||||
pub body: Option<BodyDef>,
|
|
||||||
pub validate_certs: bool,
|
|
||||||
pub root_cert_bundle_files: Vec<Vec<u8>>,
|
|
||||||
pub client_cert: Option<ClientCertDef>,
|
|
||||||
pub proxy: Option<ProxyConfig>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
pub struct ProxyConfig {
|
|
||||||
pub url: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
pub enum ClientCertDef {
|
|
||||||
PEMCert {
|
|
||||||
certificate_pem: Vec<u8>,
|
|
||||||
key_pem: Vec<u8>,
|
|
||||||
},
|
|
||||||
PFXCert {
|
|
||||||
certificate_pfx: Vec<u8>,
|
|
||||||
password: String,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
pub struct RunRequestResponse {
|
|
||||||
pub status: u16,
|
|
||||||
pub status_text: String,
|
|
||||||
pub headers: Vec<KeyValuePair>,
|
|
||||||
pub data: Vec<u8>,
|
|
||||||
pub time_start_ms: u128,
|
|
||||||
pub time_end_ms: u128,
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use tauri_plugin_store::StoreBuilder;
|
|||||||
use tokio::sync::RwLock;
|
use tokio::sync::RwLock;
|
||||||
use tokio_util::sync::CancellationToken;
|
use tokio_util::sync::CancellationToken;
|
||||||
|
|
||||||
use crate::error::AppError;
|
use crate::error::{AppError, AppResult};
|
||||||
|
|
||||||
/// Describes one registered app instance
|
/// Describes one registered app instance
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
@@ -34,10 +34,10 @@ pub struct AppState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl AppState {
|
impl AppState {
|
||||||
pub fn new(app_handle: tauri::AppHandle) -> Self {
|
pub fn new(app_handle: tauri::AppHandle) -> AppResult<Self> {
|
||||||
let store = StoreBuilder::new(&app_handle, "app_data.bin").build();
|
let store = StoreBuilder::new(&app_handle, "app_data.bin").build()?;
|
||||||
|
|
||||||
let _ = store.load();
|
let _ = store.reload();
|
||||||
|
|
||||||
// Try loading and parsing registrations from the store, if that failed,
|
// Try loading and parsing registrations from the store, if that failed,
|
||||||
// load the default list
|
// load the default list
|
||||||
@@ -46,11 +46,11 @@ impl AppState {
|
|||||||
.and_then(|val| serde_json::from_value(val.clone()).ok())
|
.and_then(|val| serde_json::from_value(val.clone()).ok())
|
||||||
.unwrap_or_else(|| DashMap::new());
|
.unwrap_or_else(|| DashMap::new());
|
||||||
|
|
||||||
Self {
|
Ok(Self {
|
||||||
active_registration_code: RwLock::new(None),
|
active_registration_code: RwLock::new(None),
|
||||||
cancellation_tokens: DashMap::new(),
|
cancellation_tokens: DashMap::new(),
|
||||||
registrations,
|
registrations,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets you a readonly reference to the registrations list
|
/// Gets you a readonly reference to the registrations list
|
||||||
@@ -70,9 +70,9 @@ impl AppState {
|
|||||||
) -> Result<(), AppError> {
|
) -> Result<(), AppError> {
|
||||||
update_func(&self.registrations);
|
update_func(&self.registrations);
|
||||||
|
|
||||||
let store = StoreBuilder::new(&app_handle, "app_data.bin").build();
|
let store = StoreBuilder::new(&app_handle, "app_data.bin").build()?;
|
||||||
|
|
||||||
let _ = store.load()?;
|
let _ = store.reload()?;
|
||||||
|
|
||||||
let _ = store
|
let _ = store
|
||||||
.delete("registrations")
|
.delete("registrations")
|
||||||
|
|||||||
@@ -3,13 +3,6 @@ use axum::{body::Body, response::{IntoResponse, Response}};
|
|||||||
use rand::rngs::OsRng;
|
use rand::rngs::OsRng;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
pub fn get_status_text(status: u16) -> &'static str {
|
|
||||||
http::StatusCode::from_u16(status)
|
|
||||||
.map(|status| status.canonical_reason())
|
|
||||||
.unwrap_or(Some("Unknown Status"))
|
|
||||||
.unwrap_or("Unknown Status")
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct EncryptedJson<T: Serialize> {
|
pub struct EncryptedJson<T: Serialize> {
|
||||||
pub key_b16: String,
|
pub key_b16: String,
|
||||||
|
|||||||
@@ -79,6 +79,7 @@
|
|||||||
"not_running_title": "Agent not detected",
|
"not_running_title": "Agent not detected",
|
||||||
"registration_title": "Agent registration",
|
"registration_title": "Agent registration",
|
||||||
"verify_ssl_certs": "Verify SSL Certificates",
|
"verify_ssl_certs": "Verify SSL Certificates",
|
||||||
|
"ca_certs": "CA Certificates",
|
||||||
"client_certs": "Client Certificates",
|
"client_certs": "Client Certificates",
|
||||||
"use_http_proxy": "Use HTTP Proxy",
|
"use_http_proxy": "Use HTTP Proxy",
|
||||||
"proxy_capabilities": "Hoppscotch Agent supports HTTP/HTTPS/SOCKS proxies along with NTLM and Basic Auth in those proxies. Include the username and password for the proxy authentication in the URL itself.",
|
"proxy_capabilities": "Hoppscotch Agent supports HTTP/HTTPS/SOCKS proxies along with NTLM and Basic Auth in those proxies. Include the username and password for the proxy authentication in the URL itself.",
|
||||||
@@ -382,6 +383,7 @@
|
|||||||
"gql_prettify_invalid_query": "Couldn't prettify an invalid query, solve query syntax errors and try again",
|
"gql_prettify_invalid_query": "Couldn't prettify an invalid query, solve query syntax errors and try again",
|
||||||
"incomplete_config_urls": "Incomplete configuration URLs",
|
"incomplete_config_urls": "Incomplete configuration URLs",
|
||||||
"incorrect_email": "Incorrect email",
|
"incorrect_email": "Incorrect email",
|
||||||
|
"invalid_file_type": "Invalid file type for `{filename}`.",
|
||||||
"invalid_link": "Invalid link",
|
"invalid_link": "Invalid link",
|
||||||
"invalid_link_description": "The link you clicked is invalid or expired.",
|
"invalid_link_description": "The link you clicked is invalid or expired.",
|
||||||
"invalid_embed_link": "The embed does not exist or is invalid.",
|
"invalid_embed_link": "The embed does not exist or is invalid.",
|
||||||
|
|||||||
@@ -193,6 +193,7 @@ declare module 'vue' {
|
|||||||
ImportExportImportExportStepsFileImport: typeof import('./components/importExport/ImportExportSteps/FileImport.vue')['default']
|
ImportExportImportExportStepsFileImport: typeof import('./components/importExport/ImportExportSteps/FileImport.vue')['default']
|
||||||
ImportExportImportExportStepsMyCollectionImport: typeof import('./components/importExport/ImportExportSteps/MyCollectionImport.vue')['default']
|
ImportExportImportExportStepsMyCollectionImport: typeof import('./components/importExport/ImportExportSteps/MyCollectionImport.vue')['default']
|
||||||
ImportExportImportExportStepsUrlImport: typeof import('./components/importExport/ImportExportSteps/UrlImport.vue')['default']
|
ImportExportImportExportStepsUrlImport: typeof import('./components/importExport/ImportExportSteps/UrlImport.vue')['default']
|
||||||
|
InterceptorsAgentModalNativeCACertificates: typeof import('./components/interceptors/agent/ModalNativeCACertificates.vue')['default']
|
||||||
InterceptorsAgentModalNativeClientCertificates: typeof import('./components/interceptors/agent/ModalNativeClientCertificates.vue')['default']
|
InterceptorsAgentModalNativeClientCertificates: typeof import('./components/interceptors/agent/ModalNativeClientCertificates.vue')['default']
|
||||||
InterceptorsAgentModalNativeClientCertsAdd: typeof import('./components/interceptors/agent/ModalNativeClientCertsAdd.vue')['default']
|
InterceptorsAgentModalNativeClientCertsAdd: typeof import('./components/interceptors/agent/ModalNativeClientCertsAdd.vue')['default']
|
||||||
InterceptorsAgentRegistrationModal: typeof import('./components/interceptors/agent/RegistrationModal.vue')['default']
|
InterceptorsAgentRegistrationModal: typeof import('./components/interceptors/agent/RegistrationModal.vue')['default']
|
||||||
|
|||||||
@@ -0,0 +1,181 @@
|
|||||||
|
<template>
|
||||||
|
<HoppSmartModal
|
||||||
|
v-if="show"
|
||||||
|
dialog
|
||||||
|
:title="t('agent.ca_certs')"
|
||||||
|
@close="emit('hide-modal')"
|
||||||
|
>
|
||||||
|
<template #body>
|
||||||
|
<div class="flex flex-col space-y-4">
|
||||||
|
<ul
|
||||||
|
v-if="certificates.length > 0"
|
||||||
|
class="mx-4 border border-dividerDark rounded"
|
||||||
|
>
|
||||||
|
<li
|
||||||
|
v-for="(certificate, index) in certificates"
|
||||||
|
:key="index"
|
||||||
|
class="flex border-dividerDark px-2 items-center justify-between"
|
||||||
|
:class="{ 'border-t border-dividerDark': index !== 0 }"
|
||||||
|
>
|
||||||
|
<div class="truncate">
|
||||||
|
{{ certificate.filename }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex items-center">
|
||||||
|
<HoppButtonSecondary
|
||||||
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
|
:icon="certificate.enabled ? IconCheckCircle : IconCircle"
|
||||||
|
:title="
|
||||||
|
certificate.enabled
|
||||||
|
? t('action.turn_off')
|
||||||
|
: t('action.turn_on')
|
||||||
|
"
|
||||||
|
color="green"
|
||||||
|
@click="toggleEntryEnabled(index)"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<HoppButtonSecondary
|
||||||
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
|
:icon="IconTrash"
|
||||||
|
:title="t('action.remove')"
|
||||||
|
@click="deleteEntry(index)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<HoppButtonSecondary
|
||||||
|
class="mx-4"
|
||||||
|
:icon="IconPlus"
|
||||||
|
:label="t('agent.add_cert_file')"
|
||||||
|
:loading="selectedFiles && selectedFiles!.length > 0"
|
||||||
|
filled
|
||||||
|
outline
|
||||||
|
@click="openFilePicker"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<p class="text-center text-secondaryLight">
|
||||||
|
Hoppscotch supports .crt, .cer or .pem files containing one or more
|
||||||
|
certificates.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
<div class="flex space-x-2">
|
||||||
|
<HoppButtonPrimary :label="t('action.save')" @click="save" />
|
||||||
|
<HoppButtonSecondary
|
||||||
|
:label="t('action.cancel')"
|
||||||
|
filled
|
||||||
|
outline
|
||||||
|
@click="emit('hide-modal')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</HoppSmartModal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- TODO: i18n -->
|
||||||
|
<script setup lang="ts">
|
||||||
|
import IconPlus from "~icons/lucide/plus"
|
||||||
|
import IconCheckCircle from "~icons/lucide/check-circle"
|
||||||
|
import IconCircle from "~icons/lucide/circle"
|
||||||
|
import IconTrash from "~icons/lucide/trash"
|
||||||
|
import { useService } from "dioc/vue"
|
||||||
|
import { ref, watch } from "vue"
|
||||||
|
import { useFileDialog } from "@vueuse/core"
|
||||||
|
import { cloneDeep } from "lodash-es"
|
||||||
|
import { useI18n } from "@composables/i18n"
|
||||||
|
import {
|
||||||
|
CACertificateEntry,
|
||||||
|
AgentInterceptorService,
|
||||||
|
} from "~/platform/std/interceptors/agent"
|
||||||
|
import { useToast } from "@composables/toast"
|
||||||
|
import { hasValidExtension } from "~/helpers/utils/file-extension"
|
||||||
|
|
||||||
|
const t = useI18n()
|
||||||
|
const toast = useToast()
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
show: boolean
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(e: "hide-modal"): void
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const nativeInterceptorService = useService(AgentInterceptorService)
|
||||||
|
|
||||||
|
const certificates = ref<CACertificateEntry[]>([])
|
||||||
|
|
||||||
|
const {
|
||||||
|
files: selectedFiles,
|
||||||
|
open: openFilePicker,
|
||||||
|
reset: resetFilePicker,
|
||||||
|
onChange: onSelectedFilesChange,
|
||||||
|
} = useFileDialog({
|
||||||
|
multiple: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
const ALLOWED_EXTENSIONS = [".crt", ".cer", ".pem"]
|
||||||
|
|
||||||
|
function isValidCertType(filename: string): boolean {
|
||||||
|
return hasValidExtension(filename, ALLOWED_EXTENSIONS)
|
||||||
|
}
|
||||||
|
|
||||||
|
// When files are selected, add them to the list of certificates and reset the file list
|
||||||
|
onSelectedFilesChange(async (files) => {
|
||||||
|
if (files) {
|
||||||
|
const addedCertificates: CACertificateEntry[] = []
|
||||||
|
|
||||||
|
for (let i = 0; i < files.length; i++) {
|
||||||
|
const file = files[i]
|
||||||
|
|
||||||
|
if (!isValidCertType(file.name)) {
|
||||||
|
toast.error(t("error.invalid_file_type", { filename: file.name }))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = new Uint8Array(await file.arrayBuffer())
|
||||||
|
|
||||||
|
addedCertificates.push({
|
||||||
|
filename: file.name,
|
||||||
|
enabled: true,
|
||||||
|
certificate: data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
certificates.value.push(...addedCertificates)
|
||||||
|
|
||||||
|
resetFilePicker()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// When the modal is shown, clone the certificates from the service,
|
||||||
|
// We only write to the service when the user clicks on save
|
||||||
|
watch(
|
||||||
|
() => props.show,
|
||||||
|
(show) => {
|
||||||
|
if (show) {
|
||||||
|
certificates.value = cloneDeep(
|
||||||
|
nativeInterceptorService.caCertificates.value
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
resetFilePicker()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
function save() {
|
||||||
|
nativeInterceptorService.caCertificates.value = certificates.value
|
||||||
|
emit("hide-modal")
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteEntry(index: number) {
|
||||||
|
certificates.value.splice(index, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleEntryEnabled(index: number) {
|
||||||
|
certificates.value[index].enabled = !certificates.value[index].enabled
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -9,37 +9,29 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex space-x-4">
|
<div class="flex space-x-4">
|
||||||
<!--
|
|
||||||
<HoppButtonSecondary
|
<HoppButtonSecondary
|
||||||
:icon="IconLucideFileBadge"
|
:icon="IconLucideFileBadge"
|
||||||
:label="'CA Certificates'"
|
:label="'CA Certificates'"
|
||||||
outline
|
outline
|
||||||
@click="showCACertificatesModal = true"
|
@click="showCACertificatesModal = true"
|
||||||
/>
|
/>
|
||||||
-->
|
|
||||||
<!--
|
|
||||||
<HoppButtonSecondary
|
<HoppButtonSecondary
|
||||||
:icon="IconLucideFileKey"
|
:icon="IconLucideFileKey"
|
||||||
:label="t('agent.client_certs')"
|
:label="t('agent.client_certs')"
|
||||||
outline
|
outline
|
||||||
@click="showClientCertificatesModal = true"
|
@click="showClientCertificatesModal = true"
|
||||||
/>
|
/>
|
||||||
-->
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!--
|
<InterceptorsAgentModalNativeCACertificates
|
||||||
<ModalsNativeCACertificates
|
|
||||||
:show="showCACertificatesModal"
|
:show="showCACertificatesModal"
|
||||||
@hide-modal="showCACertificatesModal = false"
|
@hide-modal="showCACertificatesModal = false"
|
||||||
/>
|
/>
|
||||||
-->
|
|
||||||
|
|
||||||
<!--
|
|
||||||
<InterceptorsAgentModalNativeClientCertificates
|
<InterceptorsAgentModalNativeClientCertificates
|
||||||
:show="showClientCertificatesModal"
|
:show="showClientCertificatesModal"
|
||||||
@hide-modal="showClientCertificatesModal = false"
|
@hide-modal="showClientCertificatesModal = false"
|
||||||
/>
|
/>
|
||||||
-->
|
|
||||||
|
|
||||||
<div class="pt-4 space-y-4">
|
<div class="pt-4 space-y-4">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
@@ -68,7 +60,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, ref } from "vue"
|
import { computed, ref } from "vue"
|
||||||
import { useI18n } from "@composables/i18n"
|
import { useI18n } from "@composables/i18n"
|
||||||
// import IconLucideFileKey from "~icons/lucide/file-key"
|
import IconLucideFileKey from "~icons/lucide/file-key"
|
||||||
import { useService } from "dioc/vue"
|
import { useService } from "dioc/vue"
|
||||||
import {
|
import {
|
||||||
RequestDef,
|
RequestDef,
|
||||||
@@ -84,8 +76,8 @@ const agentInterceptorService = useService(AgentInterceptorService)
|
|||||||
|
|
||||||
const allowSSLVerification = agentInterceptorService.validateCerts
|
const allowSSLVerification = agentInterceptorService.validateCerts
|
||||||
|
|
||||||
// const showCACertificatesModal = ref(false)
|
const showCACertificatesModal = ref(false)
|
||||||
// const showClientCertificatesModal = ref(false)
|
const showClientCertificatesModal = ref(false)
|
||||||
|
|
||||||
const allowProxy = ref(false)
|
const allowProxy = ref(false)
|
||||||
const proxyURL = ref("")
|
const proxyURL = ref("")
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
export function hasValidExtension(
|
||||||
|
filename: string,
|
||||||
|
allowedExtensions: string[]
|
||||||
|
): boolean {
|
||||||
|
const ext = filename.slice(((filename.lastIndexOf(".") - 1) >>> 0) + 2)
|
||||||
|
return allowedExtensions.includes(`.${ext.toLowerCase()}`)
|
||||||
|
}
|
||||||
3
packages/hoppscotch-relay/.envrc
Normal file
3
packages/hoppscotch-relay/.envrc
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
source_url "https://raw.githubusercontent.com/cachix/devenv/95f329d49a8a5289d31e0982652f7058a189bfca/direnvrc" "sha256-d+8cBpDfDBj41inrADaJt+bDWhOktwslgoP5YiGJ1v0="
|
||||||
|
|
||||||
|
use devenv
|
||||||
10
packages/hoppscotch-relay/.gitignore
vendored
Normal file
10
packages/hoppscotch-relay/.gitignore
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
/target
|
||||||
|
# Devenv
|
||||||
|
.devenv*
|
||||||
|
devenv.local.nix
|
||||||
|
|
||||||
|
# direnv
|
||||||
|
.direnv
|
||||||
|
|
||||||
|
# pre-commit
|
||||||
|
.pre-commit-config.yaml
|
||||||
644
packages/hoppscotch-relay/Cargo.lock
generated
Normal file
644
packages/hoppscotch-relay/Cargo.lock
generated
Normal file
@@ -0,0 +1,644 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "addr2line"
|
||||||
|
version = "0.24.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
|
||||||
|
dependencies = [
|
||||||
|
"gimli",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "adler2"
|
||||||
|
version = "2.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aho-corasick"
|
||||||
|
version = "1.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstream"
|
||||||
|
version = "0.6.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526"
|
||||||
|
dependencies = [
|
||||||
|
"anstyle",
|
||||||
|
"anstyle-parse",
|
||||||
|
"anstyle-query",
|
||||||
|
"anstyle-wincon",
|
||||||
|
"colorchoice",
|
||||||
|
"is_terminal_polyfill",
|
||||||
|
"utf8parse",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle"
|
||||||
|
version = "1.0.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle-parse"
|
||||||
|
version = "0.2.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb"
|
||||||
|
dependencies = [
|
||||||
|
"utf8parse",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle-query"
|
||||||
|
version = "1.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a"
|
||||||
|
dependencies = [
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle-wincon"
|
||||||
|
version = "3.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8"
|
||||||
|
dependencies = [
|
||||||
|
"anstyle",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "backtrace"
|
||||||
|
version = "0.3.74"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a"
|
||||||
|
dependencies = [
|
||||||
|
"addr2line",
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"miniz_oxide",
|
||||||
|
"object",
|
||||||
|
"rustc-demangle",
|
||||||
|
"windows-targets",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "2.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bytes"
|
||||||
|
version = "1.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cc"
|
||||||
|
version = "1.1.31"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c2e7962b54006dcfcc61cb72735f4d89bb97061dd6a7ed882ec6b8ee53714c6f"
|
||||||
|
dependencies = [
|
||||||
|
"shlex",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "colorchoice"
|
||||||
|
version = "1.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "curl"
|
||||||
|
version = "0.4.47"
|
||||||
|
source = "git+https://github.com/CuriousCorrelation/curl-rust.git#1ec8079cf527b9cf47cc7a48c68b458affdae273"
|
||||||
|
dependencies = [
|
||||||
|
"curl-sys",
|
||||||
|
"libc",
|
||||||
|
"openssl-probe",
|
||||||
|
"openssl-sys",
|
||||||
|
"socket2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "curl-sys"
|
||||||
|
version = "0.4.77+curl-8.10.1"
|
||||||
|
source = "git+https://github.com/CuriousCorrelation/curl-rust.git#1ec8079cf527b9cf47cc7a48c68b458affdae273"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"libc",
|
||||||
|
"libz-sys",
|
||||||
|
"openssl-sys",
|
||||||
|
"pkg-config",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "env_filter"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
"regex",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "env_logger"
|
||||||
|
version = "0.11.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d"
|
||||||
|
dependencies = [
|
||||||
|
"anstream",
|
||||||
|
"anstyle",
|
||||||
|
"env_filter",
|
||||||
|
"humantime",
|
||||||
|
"log",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fnv"
|
||||||
|
version = "1.0.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||||
|
|
||||||
|
[[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]]
|
||||||
|
name = "futures-core"
|
||||||
|
version = "0.3.31"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-sink"
|
||||||
|
version = "0.3.31"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gimli"
|
||||||
|
version = "0.31.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hoppscotch-relay"
|
||||||
|
version = "0.1.1"
|
||||||
|
dependencies = [
|
||||||
|
"curl",
|
||||||
|
"env_logger",
|
||||||
|
"http",
|
||||||
|
"log",
|
||||||
|
"openssl",
|
||||||
|
"openssl-sys",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"thiserror",
|
||||||
|
"tokio-util",
|
||||||
|
"url-escape",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "http"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"fnv",
|
||||||
|
"itoa",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "humantime"
|
||||||
|
version = "2.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "is_terminal_polyfill"
|
||||||
|
version = "1.70.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itoa"
|
||||||
|
version = "1.0.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.161"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libz-sys"
|
||||||
|
version = "1.1.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d2d16453e800a8cf6dd2fc3eb4bc99b786a9b90c663b8559a5b1a041bf89e472"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"libc",
|
||||||
|
"pkg-config",
|
||||||
|
"vcpkg",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "log"
|
||||||
|
version = "0.4.22"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.7.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "miniz_oxide"
|
||||||
|
version = "0.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1"
|
||||||
|
dependencies = [
|
||||||
|
"adler2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "object"
|
||||||
|
version = "0.36.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell"
|
||||||
|
version = "1.20.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "openssl"
|
||||||
|
version = "0.10.68"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5"
|
||||||
|
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-probe"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "openssl-src"
|
||||||
|
version = "300.4.0+3.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a709e02f2b4aca747929cca5ed248880847c650233cf8b8cdc48f40aaf4898a6"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "openssl-sys"
|
||||||
|
version = "0.9.104"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"libc",
|
||||||
|
"openssl-src",
|
||||||
|
"pkg-config",
|
||||||
|
"vcpkg",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "percent-encoding"
|
||||||
|
version = "2.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pin-project-lite"
|
||||||
|
version = "0.2.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pkg-config"
|
||||||
|
version = "0.3.31"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.89"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.37"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex"
|
||||||
|
version = "1.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-automata",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-automata"
|
||||||
|
version = "0.4.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-syntax"
|
||||||
|
version = "0.8.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustc-demangle"
|
||||||
|
version = "0.1.24"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ryu"
|
||||||
|
version = "1.0.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde"
|
||||||
|
version = "1.0.213"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3ea7893ff5e2466df8d720bb615088341b295f849602c6956047f8f80f0e9bc1"
|
||||||
|
dependencies = [
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_derive"
|
||||||
|
version = "1.0.213"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7e85ad2009c50b58e87caa8cd6dac16bdf511bbfb7af6c33df902396aa480fa5"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_json"
|
||||||
|
version = "1.0.132"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03"
|
||||||
|
dependencies = [
|
||||||
|
"itoa",
|
||||||
|
"memchr",
|
||||||
|
"ryu",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "shlex"
|
||||||
|
version = "1.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "socket2"
|
||||||
|
version = "0.5.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.82"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "83540f837a8afc019423a8edb95b52a8effe46957ee402287f4292fae35be021"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror"
|
||||||
|
version = "1.0.65"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5d11abd9594d9b38965ef50805c5e469ca9cc6f197f883f717e0269a3057b3d5"
|
||||||
|
dependencies = [
|
||||||
|
"thiserror-impl",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror-impl"
|
||||||
|
version = "1.0.65"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ae71770322cbd277e69d762a16c444af02aa0575ac0d174f0b9562d3b37f8602"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio"
|
||||||
|
version = "1.41.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "145f3413504347a2be84393cc8a7d2fb4d863b375909ea59f2158261aa258bbb"
|
||||||
|
dependencies = [
|
||||||
|
"backtrace",
|
||||||
|
"pin-project-lite",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-util"
|
||||||
|
version = "0.7.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"futures-core",
|
||||||
|
"futures-sink",
|
||||||
|
"pin-project-lite",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "url-escape"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "44e0ce4d1246d075ca5abec4b41d33e87a6054d08e2366b63205665e950db218"
|
||||||
|
dependencies = [
|
||||||
|
"percent-encoding",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "utf8parse"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "vcpkg"
|
||||||
|
version = "0.2.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.52.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-targets"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||||
|
dependencies = [
|
||||||
|
"windows_aarch64_gnullvm",
|
||||||
|
"windows_aarch64_msvc",
|
||||||
|
"windows_i686_gnu",
|
||||||
|
"windows_i686_gnullvm",
|
||||||
|
"windows_i686_msvc",
|
||||||
|
"windows_x86_64_gnu",
|
||||||
|
"windows_x86_64_gnullvm",
|
||||||
|
"windows_x86_64_msvc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_gnullvm"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_msvc"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnu"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnullvm"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_msvc"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnu"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnullvm"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_msvc"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||||
21
packages/hoppscotch-relay/Cargo.toml
Normal file
21
packages/hoppscotch-relay/Cargo.toml
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
[package]
|
||||||
|
name = "hoppscotch-relay"
|
||||||
|
version = "0.1.1"
|
||||||
|
description = "A HTTP request-response relay used by Hoppscotch Desktop and Hoppscotch Agent for advanced request handling including custom headers, certificates, proxies, and local system integration."
|
||||||
|
authors = ["CuriousCorrelation"]
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
curl = { git = "https://github.com/CuriousCorrelation/curl-rust.git", features = ["ntlm"] }
|
||||||
|
tokio-util = "0.7.12"
|
||||||
|
openssl = { version = "0.10.66", features = ["vendored"] }
|
||||||
|
# NOTE: This crate follows `openssl-sys` from https://github.com/CuriousCorrelation/curl-rust.git
|
||||||
|
# to avoid issues from version mismatch when compiling from source.
|
||||||
|
openssl-sys = { version = "0.9.64", features = ["vendored"] }
|
||||||
|
log = "0.4.22"
|
||||||
|
env_logger = "0.11.5"
|
||||||
|
thiserror = "1.0.64"
|
||||||
|
http = "1.1.0"
|
||||||
|
url-escape = "0.1.1"
|
||||||
|
serde = { version = "1", features = ["derive"] }
|
||||||
|
serde_json = "1"
|
||||||
21
packages/hoppscotch-relay/LICENSE.md
Normal file
21
packages/hoppscotch-relay/LICENSE.md
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2024 CuriousCorrelation
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
201
packages/hoppscotch-relay/README.md
Normal file
201
packages/hoppscotch-relay/README.md
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
# Hoppscotch Relay
|
||||||
|
|
||||||
|
[](https://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
A high-performance HTTP request-response relay used by Hoppscotch Desktop and Hoppscotch Agent for advanced request handling including CORS override, custom headers, certificates, proxies, and local system integration. It uses a custom fork of curl-rust with static OpenSSL builds for consistent SSL/TLS behavior across different platforms.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- 🚀 **Full HTTP Support**: Handle GET, POST, PUT, DELETE, and other HTTP methods
|
||||||
|
- 📦 **Multiple Body Types**:
|
||||||
|
- Raw text/JSON
|
||||||
|
- URL-encoded forms
|
||||||
|
- Multipart form data
|
||||||
|
- File uploads
|
||||||
|
- 🔒 **Security**:
|
||||||
|
- Client certificate authentication (PEM & PFX/PKCS#12)
|
||||||
|
- Custom root certificate bundles
|
||||||
|
- Certificate validation control
|
||||||
|
- 🌐 **Proxy Support**:
|
||||||
|
- HTTP/HTTPS proxy configuration
|
||||||
|
- Authentication support
|
||||||
|
- NTLM support
|
||||||
|
- ⚡ **Performance**:
|
||||||
|
- Async design
|
||||||
|
- Request cancellation support
|
||||||
|
- Progress logs
|
||||||
|
- 📊 **Detailed Metrics**:
|
||||||
|
- Response timing
|
||||||
|
- Status tracking
|
||||||
|
- Header parsing
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
Add this to your `Cargo.toml`:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[dependencies]
|
||||||
|
hoppscotch-relay = "0.1.1"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### Basic Request
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use hoppscotch_relay::{RequestWithMetadata, KeyValuePair};
|
||||||
|
use tokio_util::sync::CancellationToken;
|
||||||
|
|
||||||
|
// Create a basic GET request
|
||||||
|
let request = RequestWithMetadata::new(
|
||||||
|
1, // Request ID
|
||||||
|
"GET".to_string(), // Method
|
||||||
|
"https://api.example.com/data".to_string(), // Endpoint
|
||||||
|
vec![ // Headers
|
||||||
|
KeyValuePair {
|
||||||
|
key: "Accept".to_string(),
|
||||||
|
value: "application/json".to_string(),
|
||||||
|
}
|
||||||
|
],
|
||||||
|
None, // Body
|
||||||
|
true, // Validate certificates
|
||||||
|
vec![], // Root certificate bundles
|
||||||
|
None, // Client certificate
|
||||||
|
None, // Proxy configuration
|
||||||
|
);
|
||||||
|
|
||||||
|
// Execute the request with cancellation support
|
||||||
|
let cancel_token = CancellationToken::new();
|
||||||
|
let response = hoppscotch_relay::run_request_task(&request, cancel_token)?;
|
||||||
|
|
||||||
|
println!("Status: {} {}", response.status, response.status_text);
|
||||||
|
println!("Response time: {}ms", response.time_end_ms - response.time_start_ms);
|
||||||
|
```
|
||||||
|
|
||||||
|
### POST Request with JSON Body
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let mut request = RequestWithMetadata::new(
|
||||||
|
2,
|
||||||
|
"POST".to_string(),
|
||||||
|
"https://api.example.com/users".to_string(),
|
||||||
|
vec![
|
||||||
|
KeyValuePair {
|
||||||
|
key: "Content-Type".to_string(),
|
||||||
|
value: "application/json".to_string(),
|
||||||
|
}
|
||||||
|
],
|
||||||
|
Some(BodyDef::Text(r#"{"name": "John Doe"}"#.to_string())),
|
||||||
|
true,
|
||||||
|
vec![],
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
|
let response = hoppscotch_relay::run_request_task(&request, CancellationToken::new())?;
|
||||||
|
```
|
||||||
|
|
||||||
|
### File Upload with Form Data
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let form_data = vec![
|
||||||
|
FormDataEntry {
|
||||||
|
key: "file".to_string(),
|
||||||
|
value: FormDataValue::File {
|
||||||
|
filename: "document.pdf".to_string(),
|
||||||
|
data: std::fs::read("document.pdf")?,
|
||||||
|
mime: "application/pdf".to_string(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
FormDataEntry {
|
||||||
|
key: "description".to_string(),
|
||||||
|
value: FormDataValue::Text("Important document".to_string()),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
let mut request = RequestWithMetadata::new(
|
||||||
|
3,
|
||||||
|
"POST".to_string(),
|
||||||
|
"https://api.example.com/upload".to_string(),
|
||||||
|
vec![],
|
||||||
|
Some(BodyDef::FormData(form_data)),
|
||||||
|
true,
|
||||||
|
vec![],
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Client Certificate Authentication
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let client_cert = ClientCertDef::PEMCert {
|
||||||
|
certificate_pem: std::fs::read("client.crt")?,
|
||||||
|
key_pem: std::fs::read("client.key")?,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut request = RequestWithMetadata::new(
|
||||||
|
4,
|
||||||
|
"GET".to_string(),
|
||||||
|
"https://secure-api.example.com".to_string(),
|
||||||
|
vec![],
|
||||||
|
None,
|
||||||
|
true,
|
||||||
|
vec![],
|
||||||
|
Some(client_cert),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Proxy Configuration
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let proxy_config = ProxyConfig {
|
||||||
|
url: "http://proxy.example.com:8080".to_string(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut request = RequestWithMetadata::new(
|
||||||
|
5,
|
||||||
|
"GET".to_string(),
|
||||||
|
"https://api.example.com".to_string(),
|
||||||
|
vec![],
|
||||||
|
None,
|
||||||
|
true,
|
||||||
|
vec![],
|
||||||
|
None,
|
||||||
|
Some(proxy_config),
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Request Cancellation
|
||||||
|
|
||||||
|
The library supports request cancellation through Tokio's `CancellationToken`:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use tokio_util::sync::CancellationToken;
|
||||||
|
|
||||||
|
let cancel_token = CancellationToken::new();
|
||||||
|
let cancel_token_clone = cancel_token.clone();
|
||||||
|
|
||||||
|
// Spawn the request in a separate task
|
||||||
|
let request_handle = tokio::spawn(async move {
|
||||||
|
hoppscotch_relay::run_request_task(&request, cancel_token_clone)
|
||||||
|
});
|
||||||
|
|
||||||
|
// Cancel the request after 5 seconds
|
||||||
|
tokio::time::sleep(Duration::from_secs(5)).await;
|
||||||
|
cancel_token.cancel();
|
||||||
|
```
|
||||||
|
|
||||||
|
## Building from Source
|
||||||
|
|
||||||
|
1. Clone the repository:
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/hoppscotch/hoppscotch-relay
|
||||||
|
cd hoppscotch-relay
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Build the project:
|
||||||
|
```bash
|
||||||
|
cargo build --release
|
||||||
|
```
|
||||||
153
packages/hoppscotch-relay/devenv.lock
Normal file
153
packages/hoppscotch-relay/devenv.lock
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"devenv": {
|
||||||
|
"locked": {
|
||||||
|
"dir": "src/modules",
|
||||||
|
"lastModified": 1729641677,
|
||||||
|
"owner": "cachix",
|
||||||
|
"repo": "devenv",
|
||||||
|
"rev": "4f634c92037d3fb7a7cc2feddc4d686ace83b57f",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"dir": "src/modules",
|
||||||
|
"owner": "cachix",
|
||||||
|
"repo": "devenv",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fenix": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs"
|
||||||
|
],
|
||||||
|
"rust-analyzer-src": "rust-analyzer-src"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1729578683,
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "fenix",
|
||||||
|
"rev": "d66cda53e8193a878742dcadb5bb75f4df7c3c0a",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "fenix",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"flake-compat": {
|
||||||
|
"flake": false,
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1696426674,
|
||||||
|
"owner": "edolstra",
|
||||||
|
"repo": "flake-compat",
|
||||||
|
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "edolstra",
|
||||||
|
"repo": "flake-compat",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"gitignore": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"pre-commit-hooks",
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1709087332,
|
||||||
|
"owner": "hercules-ci",
|
||||||
|
"repo": "gitignore.nix",
|
||||||
|
"rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "hercules-ci",
|
||||||
|
"repo": "gitignore.nix",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1729501122,
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "56c7c4a3f5fdbef5bf81c7d9c28fbb45dc626611",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixpkgs-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs-stable": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1729449015,
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "89172919243df199fe237ba0f776c3e3e3d72367",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixos-24.05",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pre-commit-hooks": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-compat": "flake-compat",
|
||||||
|
"gitignore": "gitignore",
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs"
|
||||||
|
],
|
||||||
|
"nixpkgs-stable": "nixpkgs-stable"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1729104314,
|
||||||
|
"owner": "cachix",
|
||||||
|
"repo": "pre-commit-hooks.nix",
|
||||||
|
"rev": "3c3e88f0f544d6bb54329832616af7eb971b6be6",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "cachix",
|
||||||
|
"repo": "pre-commit-hooks.nix",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"devenv": "devenv",
|
||||||
|
"fenix": "fenix",
|
||||||
|
"nixpkgs": "nixpkgs",
|
||||||
|
"pre-commit-hooks": "pre-commit-hooks"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"rust-analyzer-src": {
|
||||||
|
"flake": false,
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1729618852,
|
||||||
|
"owner": "rust-lang",
|
||||||
|
"repo": "rust-analyzer",
|
||||||
|
"rev": "c2867868889a549562a7b53fb572719f852a8a6f",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "rust-lang",
|
||||||
|
"ref": "nightly",
|
||||||
|
"repo": "rust-analyzer",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
||||||
54
packages/hoppscotch-relay/devenv.nix
Normal file
54
packages/hoppscotch-relay/devenv.nix
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
{ pkgs, lib, config, inputs, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
# https://devenv.sh/packages/
|
||||||
|
packages = with pkgs; [
|
||||||
|
git
|
||||||
|
# Cargo
|
||||||
|
cargo-edit
|
||||||
|
];
|
||||||
|
|
||||||
|
# https://devenv.sh/basics/
|
||||||
|
env = {
|
||||||
|
APP_GREET = "Hoppscotch";
|
||||||
|
};
|
||||||
|
|
||||||
|
# https://devenv.sh/scripts/
|
||||||
|
scripts.hello.exec = "echo hello from $APP_GREET";
|
||||||
|
|
||||||
|
enterShell = ''
|
||||||
|
git --version
|
||||||
|
'';
|
||||||
|
|
||||||
|
# https://devenv.sh/tests/
|
||||||
|
enterTest = ''
|
||||||
|
echo "Running tests"
|
||||||
|
'';
|
||||||
|
|
||||||
|
# https://devenv.sh/integrations/dotenv/
|
||||||
|
dotenv.enable = true;
|
||||||
|
|
||||||
|
# https://devenv.sh/languages/
|
||||||
|
languages.rust = {
|
||||||
|
enable = true;
|
||||||
|
channel = "nightly";
|
||||||
|
components = [
|
||||||
|
"rustc"
|
||||||
|
"cargo"
|
||||||
|
"clippy"
|
||||||
|
"rustfmt"
|
||||||
|
"rust-analyzer"
|
||||||
|
"llvm-tools-preview"
|
||||||
|
"rust-src"
|
||||||
|
"rustc-codegen-cranelift-preview"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
# https://devenv.sh/pre-commit-hooks/
|
||||||
|
# pre-commit.hooks.shellcheck.enable = true;
|
||||||
|
|
||||||
|
# https://devenv.sh/processes/
|
||||||
|
# processes.ping.exec = "ping example.com";
|
||||||
|
|
||||||
|
# See full reference at https://devenv.sh/reference/options/
|
||||||
|
}
|
||||||
23
packages/hoppscotch-relay/devenv.yaml
Normal file
23
packages/hoppscotch-relay/devenv.yaml
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
# yaml-language-server: $schema=https://devenv.sh/devenv.schema.json
|
||||||
|
inputs:
|
||||||
|
# For NodeJS-22 and above
|
||||||
|
nixpkgs:
|
||||||
|
url: github:NixOS/nixpkgs/nixpkgs-unstable
|
||||||
|
# nixpkgs:
|
||||||
|
# url: github:cachix/devenv-nixpkgs/rolling
|
||||||
|
fenix:
|
||||||
|
url: github:nix-community/fenix
|
||||||
|
inputs:
|
||||||
|
nixpkgs:
|
||||||
|
follows: nixpkgs
|
||||||
|
|
||||||
|
# If you're using non-OSS software, you can set allowUnfree to true.
|
||||||
|
allowUnfree: true
|
||||||
|
|
||||||
|
# If you're willing to use a package that's vulnerable
|
||||||
|
# permittedInsecurePackages:
|
||||||
|
# - "openssl-1.1.1w"
|
||||||
|
|
||||||
|
# If you have more than one devenv you can merge them
|
||||||
|
#imports:
|
||||||
|
# - ./backend
|
||||||
16
packages/hoppscotch-relay/src/error.rs
Normal file
16
packages/hoppscotch-relay/src/error.rs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
use serde::Serialize;
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
#[derive(Debug, Error, Serialize)]
|
||||||
|
pub enum RelayError {
|
||||||
|
#[error("Invalid method")]
|
||||||
|
InvalidMethod,
|
||||||
|
#[error("Invalid URL")]
|
||||||
|
InvalidUrl,
|
||||||
|
#[error("Invalid headers")]
|
||||||
|
InvalidHeaders,
|
||||||
|
#[error("Request run error: {0}")]
|
||||||
|
RequestRunError(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type RelayResult<T> = std::result::Result<T, RelayError>;
|
||||||
96
packages/hoppscotch-relay/src/interop.rs
Normal file
96
packages/hoppscotch-relay/src/interop.rs
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct KeyValuePair {
|
||||||
|
pub key: String,
|
||||||
|
pub value: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub enum FormDataValue {
|
||||||
|
Text(String),
|
||||||
|
File {
|
||||||
|
filename: String,
|
||||||
|
data: Vec<u8>,
|
||||||
|
mime: String,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct FormDataEntry {
|
||||||
|
pub key: String,
|
||||||
|
pub value: FormDataValue,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub enum BodyDef {
|
||||||
|
Text(String),
|
||||||
|
URLEncoded(Vec<KeyValuePair>),
|
||||||
|
FormData(Vec<FormDataEntry>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct RequestWithMetadata {
|
||||||
|
pub req_id: usize,
|
||||||
|
pub method: String,
|
||||||
|
pub endpoint: String,
|
||||||
|
pub headers: Vec<KeyValuePair>,
|
||||||
|
pub body: Option<BodyDef>,
|
||||||
|
pub validate_certs: bool,
|
||||||
|
pub root_cert_bundle_files: Vec<Vec<u8>>,
|
||||||
|
pub client_cert: Option<ClientCertDef>,
|
||||||
|
pub proxy: Option<ProxyConfig>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RequestWithMetadata {
|
||||||
|
pub fn new(
|
||||||
|
req_id: usize,
|
||||||
|
method: String,
|
||||||
|
endpoint: String,
|
||||||
|
headers: Vec<KeyValuePair>,
|
||||||
|
body: Option<BodyDef>,
|
||||||
|
validate_certs: bool,
|
||||||
|
root_cert_bundle_files: Vec<Vec<u8>>,
|
||||||
|
client_cert: Option<ClientCertDef>,
|
||||||
|
proxy: Option<ProxyConfig>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
req_id,
|
||||||
|
method,
|
||||||
|
endpoint,
|
||||||
|
headers,
|
||||||
|
body,
|
||||||
|
validate_certs,
|
||||||
|
root_cert_bundle_files,
|
||||||
|
client_cert,
|
||||||
|
proxy,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct ProxyConfig {
|
||||||
|
pub url: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub enum ClientCertDef {
|
||||||
|
PEMCert {
|
||||||
|
certificate_pem: Vec<u8>,
|
||||||
|
key_pem: Vec<u8>,
|
||||||
|
},
|
||||||
|
PFXCert {
|
||||||
|
certificate_pfx: Vec<u8>,
|
||||||
|
password: String,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
pub struct ResponseWithMetadata {
|
||||||
|
pub status: u16,
|
||||||
|
pub status_text: String,
|
||||||
|
pub headers: Vec<KeyValuePair>,
|
||||||
|
pub data: Vec<u8>,
|
||||||
|
pub time_start_ms: u128,
|
||||||
|
pub time_end_ms: u128,
|
||||||
|
}
|
||||||
23
packages/hoppscotch-relay/src/lib.rs
Normal file
23
packages/hoppscotch-relay/src/lib.rs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
pub(crate) mod error;
|
||||||
|
pub(crate) mod interop;
|
||||||
|
pub(crate) mod relay;
|
||||||
|
pub(crate) mod util;
|
||||||
|
|
||||||
|
pub use error::{RelayError, RelayResult};
|
||||||
|
pub use interop::{RequestWithMetadata, ResponseWithMetadata};
|
||||||
|
pub use relay::run_request_task;
|
||||||
|
|
||||||
|
pub fn add(left: u64, right: u64) -> u64 {
|
||||||
|
left + right
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn it_works() {
|
||||||
|
let result = add(2, 2);
|
||||||
|
assert_eq!(result, 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
595
packages/hoppscotch-relay/src/relay.rs
Normal file
595
packages/hoppscotch-relay/src/relay.rs
Normal file
@@ -0,0 +1,595 @@
|
|||||||
|
use curl::easy::{Easy, List};
|
||||||
|
use openssl::{pkcs12::Pkcs12, ssl::SslContextBuilder, x509::X509};
|
||||||
|
use openssl_sys::SSL_CTX;
|
||||||
|
use std::time::SystemTime;
|
||||||
|
use tokio_util::sync::CancellationToken;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
error::RelayError,
|
||||||
|
interop::{
|
||||||
|
BodyDef, ClientCertDef, FormDataValue, KeyValuePair, RequestWithMetadata,
|
||||||
|
ResponseWithMetadata,
|
||||||
|
},
|
||||||
|
util::get_status_text,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn run_request_task(
|
||||||
|
req: &RequestWithMetadata,
|
||||||
|
cancel_token: CancellationToken,
|
||||||
|
) -> Result<ResponseWithMetadata, RelayError> {
|
||||||
|
log::info!(
|
||||||
|
"Starting request task: [Method: {}] [URL: {}] [Validate Certs: {}] [Has Body: {}] [Proxy Enabled: {}]",
|
||||||
|
req.method,
|
||||||
|
req.endpoint,
|
||||||
|
req.validate_certs,
|
||||||
|
req.body.is_some(),
|
||||||
|
req.proxy.is_some()
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut curl_handle = Easy::new();
|
||||||
|
log::debug!("Initialized new curl handle with default settings");
|
||||||
|
|
||||||
|
match curl_handle.progress(true) {
|
||||||
|
Ok(_) => log::debug!("Progress tracking enabled for request monitoring"),
|
||||||
|
Err(err) => {
|
||||||
|
log::error!(
|
||||||
|
"Critical failure enabling progress tracking: {}\nError details: {:?}",
|
||||||
|
err,
|
||||||
|
err
|
||||||
|
);
|
||||||
|
return Err(RelayError::RequestRunError(err.description().to_string()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match curl_handle.custom_request(&req.method) {
|
||||||
|
Ok(_) => log::debug!("HTTP method set: {}", req.method),
|
||||||
|
Err(err) => {
|
||||||
|
log::error!("Failed to set HTTP method '{}'. Error: {}", req.method, err);
|
||||||
|
return Err(RelayError::InvalidMethod);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match curl_handle.url(&req.endpoint) {
|
||||||
|
Ok(_) => log::debug!("Target URL configured: {}", req.endpoint),
|
||||||
|
Err(err) => {
|
||||||
|
log::error!(
|
||||||
|
"URL configuration failed for '{}'\nError: {}",
|
||||||
|
req.endpoint,
|
||||||
|
err
|
||||||
|
);
|
||||||
|
return Err(RelayError::InvalidUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let headers = match get_headers_list(&req) {
|
||||||
|
Ok(headers) => {
|
||||||
|
log::debug!("Generated headers list");
|
||||||
|
headers
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
log::error!("Header generation failed:\nError: {:?}", err);
|
||||||
|
return Err(err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match curl_handle.http_headers(headers) {
|
||||||
|
Ok(_) => log::debug!("Successfully configured request headers"),
|
||||||
|
Err(err) => {
|
||||||
|
log::error!("Failed to set HTTP headers: {}", err);
|
||||||
|
return Err(RelayError::InvalidHeaders);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Err(err) = apply_body_to_curl_handle(&mut curl_handle, &req) {
|
||||||
|
log::error!(
|
||||||
|
"Request body application failed:\nError: {:?}\nContent-Type: {:?}",
|
||||||
|
err,
|
||||||
|
req.headers
|
||||||
|
.iter()
|
||||||
|
.find(|h| h.key.to_lowercase() == "content-type")
|
||||||
|
.map(|h| &h.value)
|
||||||
|
);
|
||||||
|
return Err(err);
|
||||||
|
}
|
||||||
|
log::debug!("Request body configured successfully");
|
||||||
|
|
||||||
|
match curl_handle.ssl_verify_peer(req.validate_certs) {
|
||||||
|
Ok(_) => log::debug!(
|
||||||
|
"SSL peer verification setting applied: {}",
|
||||||
|
req.validate_certs
|
||||||
|
),
|
||||||
|
Err(err) => {
|
||||||
|
log::error!(
|
||||||
|
"SSL peer verification configuration failed: {}\nRequested setting: {}",
|
||||||
|
err,
|
||||||
|
req.validate_certs
|
||||||
|
);
|
||||||
|
return Err(RelayError::RequestRunError(err.description().to_string()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match curl_handle.ssl_verify_host(req.validate_certs) {
|
||||||
|
Ok(_) => log::debug!(
|
||||||
|
"SSL host verification setting applied: {}",
|
||||||
|
req.validate_certs
|
||||||
|
),
|
||||||
|
Err(err) => {
|
||||||
|
log::error!(
|
||||||
|
"SSL host verification configuration failed: {}\nRequested setting: {}",
|
||||||
|
err,
|
||||||
|
req.validate_certs
|
||||||
|
);
|
||||||
|
return Err(RelayError::RequestRunError(err.description().to_string()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Err(err) = apply_client_cert_to_curl_handle(&mut curl_handle, &req) {
|
||||||
|
log::error!(
|
||||||
|
"Client certificate configuration failed:\nError: {:?}\nCert Info: {:#?}",
|
||||||
|
err,
|
||||||
|
req.client_cert.as_ref()
|
||||||
|
);
|
||||||
|
return Err(err);
|
||||||
|
}
|
||||||
|
log::debug!("Client certificate configuration successful");
|
||||||
|
|
||||||
|
if let Err(err) = apply_proxy_config_to_curl_handle(&mut curl_handle, &req) {
|
||||||
|
log::error!(
|
||||||
|
"Proxy configuration failed:\nError: {:?}\nProxy Info: {:?}",
|
||||||
|
err,
|
||||||
|
req.proxy.as_ref()
|
||||||
|
);
|
||||||
|
return Err(err);
|
||||||
|
}
|
||||||
|
log::debug!("Proxy configuration applied successfully");
|
||||||
|
|
||||||
|
let mut response_body = Vec::new();
|
||||||
|
let mut response_headers = Vec::new();
|
||||||
|
let (start_time_ms, end_time_ms) = {
|
||||||
|
let mut transfer = curl_handle.transfer();
|
||||||
|
log::debug!("Created curl transfer object for request execution");
|
||||||
|
|
||||||
|
match transfer.ssl_ctx_function(|ssl_ctx_ptr| {
|
||||||
|
let cert_list = match get_x509_certs_from_root_cert_bundle_safe(&req) {
|
||||||
|
Ok(certs) => {
|
||||||
|
log::debug!("Found {} certificates in root bundle", certs.len());
|
||||||
|
certs
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("Failed to load certificates from bundle: {:?}", e);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if !cert_list.is_empty() {
|
||||||
|
let mut ssl_ctx_builder =
|
||||||
|
unsafe { SslContextBuilder::from_ptr(ssl_ctx_ptr as *mut SSL_CTX) };
|
||||||
|
|
||||||
|
let cert_store = ssl_ctx_builder.cert_store_mut();
|
||||||
|
|
||||||
|
for (index, cert) in cert_list.iter().enumerate() {
|
||||||
|
log::debug!(
|
||||||
|
"Processing certificate {}: Subject: {:?}, Not Before: {:?}, Not After: {:?}",
|
||||||
|
index,
|
||||||
|
cert.subject_name(),
|
||||||
|
cert.not_before(),
|
||||||
|
cert.not_after()
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Err(e) = cert_store.add_cert(cert.clone()) {
|
||||||
|
log::warn!(
|
||||||
|
"Failed to add certificate {} to store\nError: {}\nCert details: {:?}",
|
||||||
|
index,
|
||||||
|
e,
|
||||||
|
cert.subject_name()
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
log::debug!(
|
||||||
|
"Successfully added certificate {} to store\nSubject: {:?}",
|
||||||
|
index,
|
||||||
|
cert.subject_name()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SAFETY: We need to prevent Rust from dropping the `SslContextBuilder` because
|
||||||
|
// the underlying `SSL_CTX` pointer is owned and managed by curl, not us.
|
||||||
|
// From curl docs: "libcurl does not guarantee the lifetime of the passed in
|
||||||
|
// object once this callback function has returned"
|
||||||
|
// and `SslContextBuilder` is just a safe wrapper around curl's `SSL_CTX` from
|
||||||
|
// `openssl_sys::SSL_CTX`.
|
||||||
|
// If dropped, Rust would try to free the `SSL_CTX` which curl still needs.
|
||||||
|
//
|
||||||
|
// This intentional "leak" is safe because:
|
||||||
|
// - We're only leaking the thin Rust wrapper
|
||||||
|
// - Curl manages the actual `SSL_CTX` memory
|
||||||
|
// - Curl will free the `SSL_CTX` during connection cleanup
|
||||||
|
//
|
||||||
|
// See: https://curl.se/libcurl/c/CURLOPT_SSL_CTX_FUNCTION.html
|
||||||
|
std::mem::forget(ssl_ctx_builder);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}) {
|
||||||
|
Ok(_) => log::debug!("SSL context function configured successfully"),
|
||||||
|
Err(err) => {
|
||||||
|
log::error!("SSL context function setup failed: {}", err);
|
||||||
|
return Err(RelayError::RequestRunError(err.description().to_string()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match transfer.progress_function(|dltotal, dlnow, ultotal, ulnow| {
|
||||||
|
let cancelled = cancel_token.is_cancelled();
|
||||||
|
if cancelled {
|
||||||
|
log::warn!(
|
||||||
|
"Request cancelled by user\nDownload: {}/{} bytes\nUpload: {}/{} bytes",
|
||||||
|
dlnow,
|
||||||
|
dltotal,
|
||||||
|
ulnow,
|
||||||
|
ultotal
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
log::debug!(
|
||||||
|
"Progress - Download: {}/{} bytes, Upload: {}/{} bytes",
|
||||||
|
dlnow,
|
||||||
|
dltotal,
|
||||||
|
ulnow,
|
||||||
|
ultotal
|
||||||
|
);
|
||||||
|
}
|
||||||
|
!cancelled
|
||||||
|
}) {
|
||||||
|
Ok(_) => log::debug!("Progress monitoring function configured"),
|
||||||
|
Err(err) => {
|
||||||
|
log::error!("Progress function setup failed: {}", err);
|
||||||
|
return Err(RelayError::RequestRunError(err.description().to_string()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match transfer.header_function(|header| {
|
||||||
|
let header = String::from_utf8_lossy(header).into_owned();
|
||||||
|
if let Some((key, value)) = header.split_once(':') {
|
||||||
|
log::debug!("Received header: [{}] = [{}]", key.trim(), value.trim());
|
||||||
|
response_headers.push(KeyValuePair {
|
||||||
|
key: key.trim().to_string(),
|
||||||
|
value: value.trim().to_string(),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
log::debug!("Received header line (no key-value): {}", header.trim());
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}) {
|
||||||
|
Ok(_) => log::debug!("Header processing function configured"),
|
||||||
|
Err(err) => {
|
||||||
|
log::error!("Header function setup failed: {}", err);
|
||||||
|
return Err(RelayError::RequestRunError(err.description().to_string()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match transfer.write_function(|data| {
|
||||||
|
let chunk_size = data.len();
|
||||||
|
response_body.extend_from_slice(data);
|
||||||
|
log::debug!(
|
||||||
|
"Received response chunk: {} bytes (Total size so far: {} bytes)",
|
||||||
|
chunk_size,
|
||||||
|
response_body.len()
|
||||||
|
);
|
||||||
|
Ok(chunk_size)
|
||||||
|
}) {
|
||||||
|
Ok(_) => log::debug!("Response body processing function configured"),
|
||||||
|
Err(err) => {
|
||||||
|
log::error!("Write function setup failed: {}", err);
|
||||||
|
return Err(RelayError::RequestRunError(err.description().to_string()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let start_time_ms = SystemTime::now()
|
||||||
|
.duration_since(std::time::UNIX_EPOCH)
|
||||||
|
.unwrap()
|
||||||
|
.as_millis();
|
||||||
|
log::info!(
|
||||||
|
"Initiating request transfer at timestamp: {}",
|
||||||
|
start_time_ms
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Err(err) = transfer.perform() {
|
||||||
|
log::error!(
|
||||||
|
"Request transfer failed:\nError: {}\nTime elapsed: {}ms",
|
||||||
|
err,
|
||||||
|
SystemTime::now()
|
||||||
|
.duration_since(std::time::UNIX_EPOCH)
|
||||||
|
.unwrap()
|
||||||
|
.as_millis()
|
||||||
|
- start_time_ms,
|
||||||
|
);
|
||||||
|
return Err(RelayError::RequestRunError(err.description().to_string()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let end_time_ms = SystemTime::now()
|
||||||
|
.duration_since(std::time::UNIX_EPOCH)
|
||||||
|
.unwrap()
|
||||||
|
.as_millis();
|
||||||
|
|
||||||
|
log::info!(
|
||||||
|
"Request transfer completed:\nDuration: {}ms",
|
||||||
|
end_time_ms - start_time_ms,
|
||||||
|
);
|
||||||
|
|
||||||
|
(start_time_ms, end_time_ms)
|
||||||
|
};
|
||||||
|
|
||||||
|
let response_status = match curl_handle.response_code() {
|
||||||
|
Ok(status) => {
|
||||||
|
let status = status as u16;
|
||||||
|
log::info!(
|
||||||
|
"Response status code: {} ({})",
|
||||||
|
status,
|
||||||
|
get_status_text(status)
|
||||||
|
);
|
||||||
|
status
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
log::error!("Failed to retrieve response code: {}", err);
|
||||||
|
return Err(RelayError::RequestRunError(err.description().to_string()));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let response_status_text = get_status_text(response_status).to_string();
|
||||||
|
log::info!(
|
||||||
|
"Request completed successfully:\nStatus: {} ({})\nDuration: {}ms\n\
|
||||||
|
Response size: {} bytes\nHeaders: {} received\nEndpoint: {}",
|
||||||
|
response_status,
|
||||||
|
response_status_text,
|
||||||
|
end_time_ms - start_time_ms,
|
||||||
|
response_body.len(),
|
||||||
|
response_headers.len(),
|
||||||
|
req.endpoint
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(ResponseWithMetadata {
|
||||||
|
status: response_status,
|
||||||
|
status_text: response_status_text,
|
||||||
|
headers: response_headers,
|
||||||
|
data: response_body,
|
||||||
|
time_start_ms: start_time_ms,
|
||||||
|
time_end_ms: end_time_ms,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_headers_list(req: &RequestWithMetadata) -> Result<List, RelayError> {
|
||||||
|
let mut result = List::new();
|
||||||
|
|
||||||
|
for KeyValuePair { key, value } in &req.headers {
|
||||||
|
result
|
||||||
|
.append(&format!("{}: {}", key, value))
|
||||||
|
.map_err(|err| RelayError::RequestRunError(err.description().to_string()))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply_body_to_curl_handle(
|
||||||
|
curl_handle: &mut Easy,
|
||||||
|
req: &RequestWithMetadata,
|
||||||
|
) -> Result<(), RelayError> {
|
||||||
|
match &req.body {
|
||||||
|
Some(BodyDef::Text(text)) => {
|
||||||
|
curl_handle
|
||||||
|
.post_fields_copy(text.as_bytes())
|
||||||
|
.map_err(|err| {
|
||||||
|
RelayError::RequestRunError(format!(
|
||||||
|
"Error while setting body: {}",
|
||||||
|
err.description()
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
Some(BodyDef::FormData(entries)) => {
|
||||||
|
let mut form = curl::easy::Form::new();
|
||||||
|
|
||||||
|
for entry in entries {
|
||||||
|
let mut part = form.part(&entry.key);
|
||||||
|
|
||||||
|
match &entry.value {
|
||||||
|
FormDataValue::Text(data) => {
|
||||||
|
part.contents(data.as_bytes());
|
||||||
|
}
|
||||||
|
FormDataValue::File {
|
||||||
|
filename,
|
||||||
|
data,
|
||||||
|
mime,
|
||||||
|
} => {
|
||||||
|
part.buffer(filename, data.clone()).content_type(mime);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
part.add().map_err(|err| {
|
||||||
|
RelayError::RequestRunError(format!(
|
||||||
|
"Error while setting body: {}",
|
||||||
|
err.description()
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
|
||||||
|
curl_handle.httppost(form).map_err(|err| {
|
||||||
|
RelayError::RequestRunError(format!(
|
||||||
|
"Error while setting body: {}",
|
||||||
|
err.description()
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
Some(BodyDef::URLEncoded(entries)) => {
|
||||||
|
let data = entries
|
||||||
|
.iter()
|
||||||
|
.map(|KeyValuePair { key, value }| {
|
||||||
|
format!(
|
||||||
|
"{}={}",
|
||||||
|
&url_escape::encode_www_form_urlencoded(key),
|
||||||
|
url_escape::encode_www_form_urlencoded(value)
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
.join("&");
|
||||||
|
|
||||||
|
curl_handle
|
||||||
|
.post_fields_copy(data.as_bytes())
|
||||||
|
.map_err(|err| {
|
||||||
|
RelayError::RequestRunError(format!(
|
||||||
|
"Error while setting body: {}",
|
||||||
|
err.description()
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
None => {}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply_client_cert_to_curl_handle(
|
||||||
|
handle: &mut Easy,
|
||||||
|
req: &RequestWithMetadata,
|
||||||
|
) -> Result<(), RelayError> {
|
||||||
|
match &req.client_cert {
|
||||||
|
Some(ClientCertDef::PEMCert {
|
||||||
|
certificate_pem,
|
||||||
|
key_pem,
|
||||||
|
}) => {
|
||||||
|
handle.ssl_cert_type("PEM").map_err(|err| {
|
||||||
|
RelayError::RequestRunError(format!(
|
||||||
|
"Failed setting PEM Cert Type: {}",
|
||||||
|
err.description()
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
handle.ssl_cert_blob(certificate_pem).map_err(|err| {
|
||||||
|
RelayError::RequestRunError(format!(
|
||||||
|
"Failed setting PEM Cert Blob: {}",
|
||||||
|
err.description()
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
handle.ssl_key_type("PEM").map_err(|err| {
|
||||||
|
RelayError::RequestRunError(format!(
|
||||||
|
"Failed setting PEM key type: {}",
|
||||||
|
err.description()
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
handle.ssl_key_blob(key_pem).map_err(|err| {
|
||||||
|
RelayError::RequestRunError(format!(
|
||||||
|
"Failed setting PEM Cert blob: {}",
|
||||||
|
err.description()
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
Some(ClientCertDef::PFXCert {
|
||||||
|
certificate_pfx,
|
||||||
|
password,
|
||||||
|
}) => {
|
||||||
|
let pkcs12 = Pkcs12::from_der(&certificate_pfx).map_err(|err| {
|
||||||
|
RelayError::RequestRunError(format!(
|
||||||
|
"Failed to parse PFX certificate from DER: {}",
|
||||||
|
err
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let parsed = pkcs12.parse2(password).map_err(|err| {
|
||||||
|
RelayError::RequestRunError(format!(
|
||||||
|
"Failed to parse PFX certificate with provided password: {}",
|
||||||
|
err
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
if let (Some(cert), Some(key)) = (parsed.cert, parsed.pkey) {
|
||||||
|
let certificate_pem = cert.to_pem().map_err(|err| {
|
||||||
|
RelayError::RequestRunError(format!(
|
||||||
|
"Failed to convert PFX certificate to PEM format: {}",
|
||||||
|
err
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let key_pem = key.private_key_to_pem_pkcs8().map_err(|err| {
|
||||||
|
RelayError::RequestRunError(format!(
|
||||||
|
"Failed to convert PFX private key to PEM format: {}",
|
||||||
|
err
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
handle.ssl_cert_type("PEM").map_err(|err| {
|
||||||
|
RelayError::RequestRunError(format!(
|
||||||
|
"Failed setting PEM Cert Type for converted PFX: {}",
|
||||||
|
err.description()
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
handle.ssl_cert_blob(&certificate_pem).map_err(|err| {
|
||||||
|
RelayError::RequestRunError(format!(
|
||||||
|
"Failed setting PEM Cert Blob for converted PFX: {}",
|
||||||
|
err.description()
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
handle.ssl_key_type("PEM").map_err(|err| {
|
||||||
|
RelayError::RequestRunError(format!(
|
||||||
|
"Failed setting PEM key type for converted PFX: {}",
|
||||||
|
err.description()
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
handle.ssl_key_blob(&key_pem).map_err(|err| {
|
||||||
|
RelayError::RequestRunError(format!(
|
||||||
|
"Failed setting PEM key blob for converted PFX: {}",
|
||||||
|
err.description()
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
} else {
|
||||||
|
return Err(RelayError::RequestRunError(
|
||||||
|
"PFX certificate parsing succeeded, but either cert or private key is missing"
|
||||||
|
.to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_x509_certs_from_root_cert_bundle_safe(
|
||||||
|
req: &RequestWithMetadata,
|
||||||
|
) -> Result<Vec<X509>, openssl::error::ErrorStack> {
|
||||||
|
let mut certs = Vec::new();
|
||||||
|
|
||||||
|
for pem_bundle in &req.root_cert_bundle_files {
|
||||||
|
match openssl::x509::X509::stack_from_pem(pem_bundle) {
|
||||||
|
Ok(mut bundle_certs) => certs.append(&mut bundle_certs),
|
||||||
|
Err(e) => {
|
||||||
|
log::warn!("Failed to parse certificate bundle: {:?}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(certs)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply_proxy_config_to_curl_handle(
|
||||||
|
handle: &mut Easy,
|
||||||
|
req: &RequestWithMetadata,
|
||||||
|
) -> Result<(), RelayError> {
|
||||||
|
if let Some(proxy_config) = &req.proxy {
|
||||||
|
handle
|
||||||
|
.proxy_auth(curl::easy::Auth::new().auto(true))
|
||||||
|
.map_err(|err| {
|
||||||
|
RelayError::RequestRunError(format!(
|
||||||
|
"Failed to set proxy Auth Mode: {}",
|
||||||
|
err.description()
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
handle.proxy(&proxy_config.url).map_err(|err| {
|
||||||
|
RelayError::RequestRunError(format!("Failed to set proxy URL: {}", err.description()))
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
6
packages/hoppscotch-relay/src/util.rs
Normal file
6
packages/hoppscotch-relay/src/util.rs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
pub fn get_status_text(status: u16) -> &'static str {
|
||||||
|
http::StatusCode::from_u16(status)
|
||||||
|
.map(|status| status.canonical_reason())
|
||||||
|
.unwrap_or(Some("Unknown Status"))
|
||||||
|
.unwrap_or("Unknown Status")
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user