├── .gitignore ├── .travis.yml ├── Cargo.lock ├── Cargo.toml ├── README.md ├── src ├── client.rs ├── consts.rs ├── http_client.rs ├── lib.rs ├── proxy.rs └── toxic.rs └── tests └── toxiproxy_rust.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | before_install: 3 | - wget -O go.tar.gz https://golang.org/dl/go1.25.1.linux-amd64.tar.gz 4 | - tar -C $HOME -xzf go.tar.gz 5 | - export PATH=$HOME/go/bin:$PATH 6 | - export GOPATH=$HOME/go 7 | - export GOBIN=$HOME/go/bin 8 | - export GOROOT=$HOME/go 9 | - go version 10 | - git clone https://github.com/Shopify/toxiproxy.git 11 | - cd toxiproxy 12 | - make build 13 | - ./toxiproxy-server & 14 | script: 15 | - cargo test --verbose -- --test-threads 1 16 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "addr2line" 7 | version = "0.15.2" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "e7a2e47a1fbe209ee101dd6d61285226744c6c8d3c21c8dc878ba6cb9f467f3a" 10 | dependencies = [ 11 | "gimli", 12 | ] 13 | 14 | [[package]] 15 | name = "adler" 16 | version = "1.0.2" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" 19 | 20 | [[package]] 21 | name = "autocfg" 22 | version = "1.0.1" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" 25 | 26 | [[package]] 27 | name = "backtrace" 28 | version = "0.3.59" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "4717cfcbfaa661a0fd48f8453951837ae7e8f81e481fbb136e3202d72805a744" 31 | dependencies = [ 32 | "addr2line", 33 | "cc", 34 | "cfg-if", 35 | "libc", 36 | "miniz_oxide", 37 | "object", 38 | "rustc-demangle", 39 | ] 40 | 41 | [[package]] 42 | name = "base64" 43 | version = "0.21.7" 44 | source = "registry+https://github.com/rust-lang/crates.io-index" 45 | checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" 46 | 47 | [[package]] 48 | name = "bitflags" 49 | version = "1.2.1" 50 | source = "registry+https://github.com/rust-lang/crates.io-index" 51 | checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" 52 | 53 | [[package]] 54 | name = "bumpalo" 55 | version = "3.6.1" 56 | source = "registry+https://github.com/rust-lang/crates.io-index" 57 | checksum = "63396b8a4b9de3f4fdfb320ab6080762242f66a8ef174c49d8e19b674db4cdbe" 58 | 59 | [[package]] 60 | name = "bytes" 61 | version = "1.0.1" 62 | source = "registry+https://github.com/rust-lang/crates.io-index" 63 | checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040" 64 | 65 | [[package]] 66 | name = "cc" 67 | version = "1.2.38" 68 | source = "registry+https://github.com/rust-lang/crates.io-index" 69 | checksum = "80f41ae168f955c12fb8960b057d70d0ca153fb83182b57d86380443527be7e9" 70 | dependencies = [ 71 | "find-msvc-tools", 72 | "shlex", 73 | ] 74 | 75 | [[package]] 76 | name = "cfg-if" 77 | version = "1.0.0" 78 | source = "registry+https://github.com/rust-lang/crates.io-index" 79 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 80 | 81 | [[package]] 82 | name = "core-foundation" 83 | version = "0.9.1" 84 | source = "registry+https://github.com/rust-lang/crates.io-index" 85 | checksum = "0a89e2ae426ea83155dccf10c0fa6b1463ef6d5fcb44cee0b224a408fa640a62" 86 | dependencies = [ 87 | "core-foundation-sys", 88 | "libc", 89 | ] 90 | 91 | [[package]] 92 | name = "core-foundation-sys" 93 | version = "0.8.2" 94 | source = "registry+https://github.com/rust-lang/crates.io-index" 95 | checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b" 96 | 97 | [[package]] 98 | name = "displaydoc" 99 | version = "0.2.5" 100 | source = "registry+https://github.com/rust-lang/crates.io-index" 101 | checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" 102 | dependencies = [ 103 | "proc-macro2", 104 | "quote", 105 | "syn 2.0.106", 106 | ] 107 | 108 | [[package]] 109 | name = "encoding_rs" 110 | version = "0.8.28" 111 | source = "registry+https://github.com/rust-lang/crates.io-index" 112 | checksum = "80df024fbc5ac80f87dfef0d9f5209a252f2a497f7f42944cff24d8253cac065" 113 | dependencies = [ 114 | "cfg-if", 115 | ] 116 | 117 | [[package]] 118 | name = "equivalent" 119 | version = "1.0.2" 120 | source = "registry+https://github.com/rust-lang/crates.io-index" 121 | checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" 122 | 123 | [[package]] 124 | name = "find-msvc-tools" 125 | version = "0.1.2" 126 | source = "registry+https://github.com/rust-lang/crates.io-index" 127 | checksum = "1ced73b1dacfc750a6db6c0a0c3a3853c8b41997e2e2c563dc90804ae6867959" 128 | 129 | [[package]] 130 | name = "fnv" 131 | version = "1.0.7" 132 | source = "registry+https://github.com/rust-lang/crates.io-index" 133 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 134 | 135 | [[package]] 136 | name = "form_urlencoded" 137 | version = "1.2.2" 138 | source = "registry+https://github.com/rust-lang/crates.io-index" 139 | checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" 140 | dependencies = [ 141 | "percent-encoding", 142 | ] 143 | 144 | [[package]] 145 | name = "futures-channel" 146 | version = "0.3.13" 147 | source = "registry+https://github.com/rust-lang/crates.io-index" 148 | checksum = "8c2dd2df839b57db9ab69c2c9d8f3e8c81984781937fe2807dc6dcf3b2ad2939" 149 | dependencies = [ 150 | "futures-core", 151 | ] 152 | 153 | [[package]] 154 | name = "futures-core" 155 | version = "0.3.31" 156 | source = "registry+https://github.com/rust-lang/crates.io-index" 157 | checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" 158 | 159 | [[package]] 160 | name = "futures-io" 161 | version = "0.3.31" 162 | source = "registry+https://github.com/rust-lang/crates.io-index" 163 | checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" 164 | 165 | [[package]] 166 | name = "futures-sink" 167 | version = "0.3.31" 168 | source = "registry+https://github.com/rust-lang/crates.io-index" 169 | checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" 170 | 171 | [[package]] 172 | name = "futures-task" 173 | version = "0.3.31" 174 | source = "registry+https://github.com/rust-lang/crates.io-index" 175 | checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" 176 | 177 | [[package]] 178 | name = "futures-util" 179 | version = "0.3.31" 180 | source = "registry+https://github.com/rust-lang/crates.io-index" 181 | checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" 182 | dependencies = [ 183 | "futures-core", 184 | "futures-io", 185 | "futures-task", 186 | "memchr", 187 | "pin-project-lite", 188 | "pin-utils", 189 | "slab", 190 | ] 191 | 192 | [[package]] 193 | name = "gimli" 194 | version = "0.24.0" 195 | source = "registry+https://github.com/rust-lang/crates.io-index" 196 | checksum = "0e4075386626662786ddb0ec9081e7c7eeb1ba31951f447ca780ef9f5d568189" 197 | 198 | [[package]] 199 | name = "h2" 200 | version = "0.3.27" 201 | source = "registry+https://github.com/rust-lang/crates.io-index" 202 | checksum = "0beca50380b1fc32983fc1cb4587bfa4bb9e78fc259aad4a0032d2080309222d" 203 | dependencies = [ 204 | "bytes", 205 | "fnv", 206 | "futures-core", 207 | "futures-sink", 208 | "futures-util", 209 | "http 0.2.12", 210 | "indexmap", 211 | "slab", 212 | "tokio", 213 | "tokio-util", 214 | "tracing", 215 | ] 216 | 217 | [[package]] 218 | name = "hashbrown" 219 | version = "0.16.0" 220 | source = "registry+https://github.com/rust-lang/crates.io-index" 221 | checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" 222 | 223 | [[package]] 224 | name = "http" 225 | version = "0.2.12" 226 | source = "registry+https://github.com/rust-lang/crates.io-index" 227 | checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" 228 | dependencies = [ 229 | "bytes", 230 | "fnv", 231 | "itoa 1.0.15", 232 | ] 233 | 234 | [[package]] 235 | name = "http" 236 | version = "1.3.1" 237 | source = "registry+https://github.com/rust-lang/crates.io-index" 238 | checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" 239 | dependencies = [ 240 | "bytes", 241 | "fnv", 242 | "itoa 1.0.15", 243 | ] 244 | 245 | [[package]] 246 | name = "http-body" 247 | version = "0.4.6" 248 | source = "registry+https://github.com/rust-lang/crates.io-index" 249 | checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" 250 | dependencies = [ 251 | "bytes", 252 | "http 0.2.12", 253 | "pin-project-lite", 254 | ] 255 | 256 | [[package]] 257 | name = "httparse" 258 | version = "1.10.1" 259 | source = "registry+https://github.com/rust-lang/crates.io-index" 260 | checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" 261 | 262 | [[package]] 263 | name = "httpdate" 264 | version = "1.0.3" 265 | source = "registry+https://github.com/rust-lang/crates.io-index" 266 | checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" 267 | 268 | [[package]] 269 | name = "hyper" 270 | version = "0.14.32" 271 | source = "registry+https://github.com/rust-lang/crates.io-index" 272 | checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7" 273 | dependencies = [ 274 | "bytes", 275 | "futures-channel", 276 | "futures-core", 277 | "futures-util", 278 | "h2", 279 | "http 0.2.12", 280 | "http-body", 281 | "httparse", 282 | "httpdate", 283 | "itoa 1.0.15", 284 | "pin-project-lite", 285 | "socket2", 286 | "tokio", 287 | "tower-service", 288 | "tracing", 289 | "want", 290 | ] 291 | 292 | [[package]] 293 | name = "icu_collections" 294 | version = "2.0.0" 295 | source = "registry+https://github.com/rust-lang/crates.io-index" 296 | checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" 297 | dependencies = [ 298 | "displaydoc", 299 | "potential_utf", 300 | "yoke", 301 | "zerofrom", 302 | "zerovec", 303 | ] 304 | 305 | [[package]] 306 | name = "icu_locale_core" 307 | version = "2.0.0" 308 | source = "registry+https://github.com/rust-lang/crates.io-index" 309 | checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" 310 | dependencies = [ 311 | "displaydoc", 312 | "litemap", 313 | "tinystr", 314 | "writeable", 315 | "zerovec", 316 | ] 317 | 318 | [[package]] 319 | name = "icu_normalizer" 320 | version = "2.0.0" 321 | source = "registry+https://github.com/rust-lang/crates.io-index" 322 | checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" 323 | dependencies = [ 324 | "displaydoc", 325 | "icu_collections", 326 | "icu_normalizer_data", 327 | "icu_properties", 328 | "icu_provider", 329 | "smallvec", 330 | "zerovec", 331 | ] 332 | 333 | [[package]] 334 | name = "icu_normalizer_data" 335 | version = "2.0.0" 336 | source = "registry+https://github.com/rust-lang/crates.io-index" 337 | checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" 338 | 339 | [[package]] 340 | name = "icu_properties" 341 | version = "2.0.1" 342 | source = "registry+https://github.com/rust-lang/crates.io-index" 343 | checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" 344 | dependencies = [ 345 | "displaydoc", 346 | "icu_collections", 347 | "icu_locale_core", 348 | "icu_properties_data", 349 | "icu_provider", 350 | "potential_utf", 351 | "zerotrie", 352 | "zerovec", 353 | ] 354 | 355 | [[package]] 356 | name = "icu_properties_data" 357 | version = "2.0.1" 358 | source = "registry+https://github.com/rust-lang/crates.io-index" 359 | checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" 360 | 361 | [[package]] 362 | name = "icu_provider" 363 | version = "2.0.0" 364 | source = "registry+https://github.com/rust-lang/crates.io-index" 365 | checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" 366 | dependencies = [ 367 | "displaydoc", 368 | "icu_locale_core", 369 | "stable_deref_trait", 370 | "tinystr", 371 | "writeable", 372 | "yoke", 373 | "zerofrom", 374 | "zerotrie", 375 | "zerovec", 376 | ] 377 | 378 | [[package]] 379 | name = "idna" 380 | version = "1.1.0" 381 | source = "registry+https://github.com/rust-lang/crates.io-index" 382 | checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" 383 | dependencies = [ 384 | "idna_adapter", 385 | "smallvec", 386 | "utf8_iter", 387 | ] 388 | 389 | [[package]] 390 | name = "idna_adapter" 391 | version = "1.2.1" 392 | source = "registry+https://github.com/rust-lang/crates.io-index" 393 | checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" 394 | dependencies = [ 395 | "icu_normalizer", 396 | "icu_properties", 397 | ] 398 | 399 | [[package]] 400 | name = "indexmap" 401 | version = "2.11.4" 402 | source = "registry+https://github.com/rust-lang/crates.io-index" 403 | checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5" 404 | dependencies = [ 405 | "equivalent", 406 | "hashbrown", 407 | ] 408 | 409 | [[package]] 410 | name = "ipnet" 411 | version = "2.3.0" 412 | source = "registry+https://github.com/rust-lang/crates.io-index" 413 | checksum = "47be2f14c678be2fdcab04ab1171db51b2762ce6f0a8ee87c8dd4a04ed216135" 414 | 415 | [[package]] 416 | name = "itoa" 417 | version = "0.4.7" 418 | source = "registry+https://github.com/rust-lang/crates.io-index" 419 | checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" 420 | 421 | [[package]] 422 | name = "itoa" 423 | version = "1.0.15" 424 | source = "registry+https://github.com/rust-lang/crates.io-index" 425 | checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" 426 | 427 | [[package]] 428 | name = "js-sys" 429 | version = "0.3.48" 430 | source = "registry+https://github.com/rust-lang/crates.io-index" 431 | checksum = "dc9f84f9b115ce7843d60706df1422a916680bfdfcbdb0447c5614ff9d7e4d78" 432 | dependencies = [ 433 | "wasm-bindgen", 434 | ] 435 | 436 | [[package]] 437 | name = "lazy_static" 438 | version = "1.5.0" 439 | source = "registry+https://github.com/rust-lang/crates.io-index" 440 | checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" 441 | 442 | [[package]] 443 | name = "libc" 444 | version = "0.2.176" 445 | source = "registry+https://github.com/rust-lang/crates.io-index" 446 | checksum = "58f929b4d672ea937a23a1ab494143d968337a5f47e56d0815df1e0890ddf174" 447 | 448 | [[package]] 449 | name = "litemap" 450 | version = "0.8.0" 451 | source = "registry+https://github.com/rust-lang/crates.io-index" 452 | checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" 453 | 454 | [[package]] 455 | name = "log" 456 | version = "0.4.28" 457 | source = "registry+https://github.com/rust-lang/crates.io-index" 458 | checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" 459 | 460 | [[package]] 461 | name = "memchr" 462 | version = "2.3.4" 463 | source = "registry+https://github.com/rust-lang/crates.io-index" 464 | checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" 465 | 466 | [[package]] 467 | name = "mime" 468 | version = "0.3.16" 469 | source = "registry+https://github.com/rust-lang/crates.io-index" 470 | checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" 471 | 472 | [[package]] 473 | name = "miniz_oxide" 474 | version = "0.4.4" 475 | source = "registry+https://github.com/rust-lang/crates.io-index" 476 | checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" 477 | dependencies = [ 478 | "adler", 479 | "autocfg", 480 | ] 481 | 482 | [[package]] 483 | name = "mio" 484 | version = "1.0.4" 485 | source = "registry+https://github.com/rust-lang/crates.io-index" 486 | checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" 487 | dependencies = [ 488 | "libc", 489 | "wasi", 490 | "windows-sys 0.59.0", 491 | ] 492 | 493 | [[package]] 494 | name = "object" 495 | version = "0.24.0" 496 | source = "registry+https://github.com/rust-lang/crates.io-index" 497 | checksum = "1a5b3dd1c072ee7963717671d1ca129f1048fda25edea6b752bfc71ac8854170" 498 | 499 | [[package]] 500 | name = "once_cell" 501 | version = "1.21.3" 502 | source = "registry+https://github.com/rust-lang/crates.io-index" 503 | checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" 504 | 505 | [[package]] 506 | name = "percent-encoding" 507 | version = "2.3.2" 508 | source = "registry+https://github.com/rust-lang/crates.io-index" 509 | checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" 510 | 511 | [[package]] 512 | name = "pin-project-lite" 513 | version = "0.2.16" 514 | source = "registry+https://github.com/rust-lang/crates.io-index" 515 | checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" 516 | 517 | [[package]] 518 | name = "pin-utils" 519 | version = "0.1.0" 520 | source = "registry+https://github.com/rust-lang/crates.io-index" 521 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 522 | 523 | [[package]] 524 | name = "potential_utf" 525 | version = "0.1.3" 526 | source = "registry+https://github.com/rust-lang/crates.io-index" 527 | checksum = "84df19adbe5b5a0782edcab45899906947ab039ccf4573713735ee7de1e6b08a" 528 | dependencies = [ 529 | "zerovec", 530 | ] 531 | 532 | [[package]] 533 | name = "proc-macro2" 534 | version = "1.0.101" 535 | source = "registry+https://github.com/rust-lang/crates.io-index" 536 | checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" 537 | dependencies = [ 538 | "unicode-ident", 539 | ] 540 | 541 | [[package]] 542 | name = "quote" 543 | version = "1.0.40" 544 | source = "registry+https://github.com/rust-lang/crates.io-index" 545 | checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" 546 | dependencies = [ 547 | "proc-macro2", 548 | ] 549 | 550 | [[package]] 551 | name = "reqwest" 552 | version = "0.11.27" 553 | source = "registry+https://github.com/rust-lang/crates.io-index" 554 | checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" 555 | dependencies = [ 556 | "base64", 557 | "bytes", 558 | "encoding_rs", 559 | "futures-core", 560 | "futures-util", 561 | "h2", 562 | "http 0.2.12", 563 | "http-body", 564 | "hyper", 565 | "ipnet", 566 | "js-sys", 567 | "log", 568 | "mime", 569 | "once_cell", 570 | "percent-encoding", 571 | "pin-project-lite", 572 | "serde", 573 | "serde_json", 574 | "serde_urlencoded", 575 | "sync_wrapper", 576 | "system-configuration", 577 | "tokio", 578 | "tower-service", 579 | "url", 580 | "wasm-bindgen", 581 | "wasm-bindgen-futures", 582 | "web-sys", 583 | "winreg", 584 | ] 585 | 586 | [[package]] 587 | name = "rustc-demangle" 588 | version = "0.1.26" 589 | source = "registry+https://github.com/rust-lang/crates.io-index" 590 | checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" 591 | 592 | [[package]] 593 | name = "rustversion" 594 | version = "1.0.22" 595 | source = "registry+https://github.com/rust-lang/crates.io-index" 596 | checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" 597 | 598 | [[package]] 599 | name = "ryu" 600 | version = "1.0.5" 601 | source = "registry+https://github.com/rust-lang/crates.io-index" 602 | checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" 603 | 604 | [[package]] 605 | name = "serde" 606 | version = "1.0.123" 607 | source = "registry+https://github.com/rust-lang/crates.io-index" 608 | checksum = "92d5161132722baa40d802cc70b15262b98258453e85e5d1d365c757c73869ae" 609 | dependencies = [ 610 | "serde_derive", 611 | ] 612 | 613 | [[package]] 614 | name = "serde_derive" 615 | version = "1.0.123" 616 | source = "registry+https://github.com/rust-lang/crates.io-index" 617 | checksum = "9391c295d64fc0abb2c556bad848f33cb8296276b1ad2677d1ae1ace4f258f31" 618 | dependencies = [ 619 | "proc-macro2", 620 | "quote", 621 | "syn 1.0.60", 622 | ] 623 | 624 | [[package]] 625 | name = "serde_json" 626 | version = "1.0.64" 627 | source = "registry+https://github.com/rust-lang/crates.io-index" 628 | checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79" 629 | dependencies = [ 630 | "itoa 0.4.7", 631 | "ryu", 632 | "serde", 633 | ] 634 | 635 | [[package]] 636 | name = "serde_urlencoded" 637 | version = "0.7.1" 638 | source = "registry+https://github.com/rust-lang/crates.io-index" 639 | checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" 640 | dependencies = [ 641 | "form_urlencoded", 642 | "itoa 1.0.15", 643 | "ryu", 644 | "serde", 645 | ] 646 | 647 | [[package]] 648 | name = "shlex" 649 | version = "1.3.0" 650 | source = "registry+https://github.com/rust-lang/crates.io-index" 651 | checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 652 | 653 | [[package]] 654 | name = "slab" 655 | version = "0.4.2" 656 | source = "registry+https://github.com/rust-lang/crates.io-index" 657 | checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" 658 | 659 | [[package]] 660 | name = "smallvec" 661 | version = "1.15.1" 662 | source = "registry+https://github.com/rust-lang/crates.io-index" 663 | checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" 664 | 665 | [[package]] 666 | name = "socket2" 667 | version = "0.5.10" 668 | source = "registry+https://github.com/rust-lang/crates.io-index" 669 | checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" 670 | dependencies = [ 671 | "libc", 672 | "windows-sys 0.52.0", 673 | ] 674 | 675 | [[package]] 676 | name = "stable_deref_trait" 677 | version = "1.2.0" 678 | source = "registry+https://github.com/rust-lang/crates.io-index" 679 | checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" 680 | 681 | [[package]] 682 | name = "syn" 683 | version = "1.0.60" 684 | source = "registry+https://github.com/rust-lang/crates.io-index" 685 | checksum = "c700597eca8a5a762beb35753ef6b94df201c81cca676604f547495a0d7f0081" 686 | dependencies = [ 687 | "proc-macro2", 688 | "quote", 689 | "unicode-xid", 690 | ] 691 | 692 | [[package]] 693 | name = "syn" 694 | version = "2.0.106" 695 | source = "registry+https://github.com/rust-lang/crates.io-index" 696 | checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" 697 | dependencies = [ 698 | "proc-macro2", 699 | "quote", 700 | "unicode-ident", 701 | ] 702 | 703 | [[package]] 704 | name = "sync_wrapper" 705 | version = "0.1.2" 706 | source = "registry+https://github.com/rust-lang/crates.io-index" 707 | checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" 708 | 709 | [[package]] 710 | name = "synstructure" 711 | version = "0.13.2" 712 | source = "registry+https://github.com/rust-lang/crates.io-index" 713 | checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" 714 | dependencies = [ 715 | "proc-macro2", 716 | "quote", 717 | "syn 2.0.106", 718 | ] 719 | 720 | [[package]] 721 | name = "system-configuration" 722 | version = "0.5.1" 723 | source = "registry+https://github.com/rust-lang/crates.io-index" 724 | checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" 725 | dependencies = [ 726 | "bitflags", 727 | "core-foundation", 728 | "system-configuration-sys", 729 | ] 730 | 731 | [[package]] 732 | name = "system-configuration-sys" 733 | version = "0.5.0" 734 | source = "registry+https://github.com/rust-lang/crates.io-index" 735 | checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" 736 | dependencies = [ 737 | "core-foundation-sys", 738 | "libc", 739 | ] 740 | 741 | [[package]] 742 | name = "tinystr" 743 | version = "0.8.1" 744 | source = "registry+https://github.com/rust-lang/crates.io-index" 745 | checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" 746 | dependencies = [ 747 | "displaydoc", 748 | "zerovec", 749 | ] 750 | 751 | [[package]] 752 | name = "tokio" 753 | version = "1.42.1" 754 | source = "registry+https://github.com/rust-lang/crates.io-index" 755 | checksum = "2209a14885b74764cce87ffa777ffa1b8ce81a3f3166c6f886b83337fe7e077f" 756 | dependencies = [ 757 | "backtrace", 758 | "bytes", 759 | "libc", 760 | "mio", 761 | "pin-project-lite", 762 | "socket2", 763 | "windows-sys 0.52.0", 764 | ] 765 | 766 | [[package]] 767 | name = "tokio-util" 768 | version = "0.7.13" 769 | source = "registry+https://github.com/rust-lang/crates.io-index" 770 | checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078" 771 | dependencies = [ 772 | "bytes", 773 | "futures-core", 774 | "futures-sink", 775 | "pin-project-lite", 776 | "tokio", 777 | ] 778 | 779 | [[package]] 780 | name = "tower-service" 781 | version = "0.3.1" 782 | source = "registry+https://github.com/rust-lang/crates.io-index" 783 | checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" 784 | 785 | [[package]] 786 | name = "toxiproxy_rust" 787 | version = "0.1.9" 788 | dependencies = [ 789 | "http 1.3.1", 790 | "lazy_static", 791 | "reqwest", 792 | "serde", 793 | "serde_json", 794 | ] 795 | 796 | [[package]] 797 | name = "tracing" 798 | version = "0.1.41" 799 | source = "registry+https://github.com/rust-lang/crates.io-index" 800 | checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" 801 | dependencies = [ 802 | "pin-project-lite", 803 | "tracing-core", 804 | ] 805 | 806 | [[package]] 807 | name = "tracing-core" 808 | version = "0.1.34" 809 | source = "registry+https://github.com/rust-lang/crates.io-index" 810 | checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" 811 | dependencies = [ 812 | "once_cell", 813 | ] 814 | 815 | [[package]] 816 | name = "try-lock" 817 | version = "0.2.3" 818 | source = "registry+https://github.com/rust-lang/crates.io-index" 819 | checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" 820 | 821 | [[package]] 822 | name = "unicode-ident" 823 | version = "1.0.19" 824 | source = "registry+https://github.com/rust-lang/crates.io-index" 825 | checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" 826 | 827 | [[package]] 828 | name = "unicode-xid" 829 | version = "0.2.1" 830 | source = "registry+https://github.com/rust-lang/crates.io-index" 831 | checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" 832 | 833 | [[package]] 834 | name = "url" 835 | version = "2.5.7" 836 | source = "registry+https://github.com/rust-lang/crates.io-index" 837 | checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" 838 | dependencies = [ 839 | "form_urlencoded", 840 | "idna", 841 | "percent-encoding", 842 | "serde", 843 | ] 844 | 845 | [[package]] 846 | name = "utf8_iter" 847 | version = "1.0.4" 848 | source = "registry+https://github.com/rust-lang/crates.io-index" 849 | checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" 850 | 851 | [[package]] 852 | name = "want" 853 | version = "0.3.0" 854 | source = "registry+https://github.com/rust-lang/crates.io-index" 855 | checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" 856 | dependencies = [ 857 | "log", 858 | "try-lock", 859 | ] 860 | 861 | [[package]] 862 | name = "wasi" 863 | version = "0.11.1+wasi-snapshot-preview1" 864 | source = "registry+https://github.com/rust-lang/crates.io-index" 865 | checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" 866 | 867 | [[package]] 868 | name = "wasm-bindgen" 869 | version = "0.2.103" 870 | source = "registry+https://github.com/rust-lang/crates.io-index" 871 | checksum = "ab10a69fbd0a177f5f649ad4d8d3305499c42bab9aef2f7ff592d0ec8f833819" 872 | dependencies = [ 873 | "cfg-if", 874 | "once_cell", 875 | "rustversion", 876 | "wasm-bindgen-macro", 877 | "wasm-bindgen-shared", 878 | ] 879 | 880 | [[package]] 881 | name = "wasm-bindgen-backend" 882 | version = "0.2.103" 883 | source = "registry+https://github.com/rust-lang/crates.io-index" 884 | checksum = "0bb702423545a6007bbc368fde243ba47ca275e549c8a28617f56f6ba53b1d1c" 885 | dependencies = [ 886 | "bumpalo", 887 | "log", 888 | "proc-macro2", 889 | "quote", 890 | "syn 2.0.106", 891 | "wasm-bindgen-shared", 892 | ] 893 | 894 | [[package]] 895 | name = "wasm-bindgen-futures" 896 | version = "0.4.21" 897 | source = "registry+https://github.com/rust-lang/crates.io-index" 898 | checksum = "8e67a5806118af01f0d9045915676b22aaebecf4178ae7021bc171dab0b897ab" 899 | dependencies = [ 900 | "cfg-if", 901 | "js-sys", 902 | "wasm-bindgen", 903 | "web-sys", 904 | ] 905 | 906 | [[package]] 907 | name = "wasm-bindgen-macro" 908 | version = "0.2.103" 909 | source = "registry+https://github.com/rust-lang/crates.io-index" 910 | checksum = "fc65f4f411d91494355917b605e1480033152658d71f722a90647f56a70c88a0" 911 | dependencies = [ 912 | "quote", 913 | "wasm-bindgen-macro-support", 914 | ] 915 | 916 | [[package]] 917 | name = "wasm-bindgen-macro-support" 918 | version = "0.2.103" 919 | source = "registry+https://github.com/rust-lang/crates.io-index" 920 | checksum = "ffc003a991398a8ee604a401e194b6b3a39677b3173d6e74495eb51b82e99a32" 921 | dependencies = [ 922 | "proc-macro2", 923 | "quote", 924 | "syn 2.0.106", 925 | "wasm-bindgen-backend", 926 | "wasm-bindgen-shared", 927 | ] 928 | 929 | [[package]] 930 | name = "wasm-bindgen-shared" 931 | version = "0.2.103" 932 | source = "registry+https://github.com/rust-lang/crates.io-index" 933 | checksum = "293c37f4efa430ca14db3721dfbe48d8c33308096bd44d80ebaa775ab71ba1cf" 934 | dependencies = [ 935 | "unicode-ident", 936 | ] 937 | 938 | [[package]] 939 | name = "web-sys" 940 | version = "0.3.48" 941 | source = "registry+https://github.com/rust-lang/crates.io-index" 942 | checksum = "ec600b26223b2948cedfde2a0aa6756dcf1fef616f43d7b3097aaf53a6c4d92b" 943 | dependencies = [ 944 | "js-sys", 945 | "wasm-bindgen", 946 | ] 947 | 948 | [[package]] 949 | name = "windows-sys" 950 | version = "0.48.0" 951 | source = "registry+https://github.com/rust-lang/crates.io-index" 952 | checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" 953 | dependencies = [ 954 | "windows-targets 0.48.5", 955 | ] 956 | 957 | [[package]] 958 | name = "windows-sys" 959 | version = "0.52.0" 960 | source = "registry+https://github.com/rust-lang/crates.io-index" 961 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 962 | dependencies = [ 963 | "windows-targets 0.52.6", 964 | ] 965 | 966 | [[package]] 967 | name = "windows-sys" 968 | version = "0.59.0" 969 | source = "registry+https://github.com/rust-lang/crates.io-index" 970 | checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" 971 | dependencies = [ 972 | "windows-targets 0.52.6", 973 | ] 974 | 975 | [[package]] 976 | name = "windows-targets" 977 | version = "0.48.5" 978 | source = "registry+https://github.com/rust-lang/crates.io-index" 979 | checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" 980 | dependencies = [ 981 | "windows_aarch64_gnullvm 0.48.5", 982 | "windows_aarch64_msvc 0.48.5", 983 | "windows_i686_gnu 0.48.5", 984 | "windows_i686_msvc 0.48.5", 985 | "windows_x86_64_gnu 0.48.5", 986 | "windows_x86_64_gnullvm 0.48.5", 987 | "windows_x86_64_msvc 0.48.5", 988 | ] 989 | 990 | [[package]] 991 | name = "windows-targets" 992 | version = "0.52.6" 993 | source = "registry+https://github.com/rust-lang/crates.io-index" 994 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 995 | dependencies = [ 996 | "windows_aarch64_gnullvm 0.52.6", 997 | "windows_aarch64_msvc 0.52.6", 998 | "windows_i686_gnu 0.52.6", 999 | "windows_i686_gnullvm", 1000 | "windows_i686_msvc 0.52.6", 1001 | "windows_x86_64_gnu 0.52.6", 1002 | "windows_x86_64_gnullvm 0.52.6", 1003 | "windows_x86_64_msvc 0.52.6", 1004 | ] 1005 | 1006 | [[package]] 1007 | name = "windows_aarch64_gnullvm" 1008 | version = "0.48.5" 1009 | source = "registry+https://github.com/rust-lang/crates.io-index" 1010 | checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" 1011 | 1012 | [[package]] 1013 | name = "windows_aarch64_gnullvm" 1014 | version = "0.52.6" 1015 | source = "registry+https://github.com/rust-lang/crates.io-index" 1016 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 1017 | 1018 | [[package]] 1019 | name = "windows_aarch64_msvc" 1020 | version = "0.48.5" 1021 | source = "registry+https://github.com/rust-lang/crates.io-index" 1022 | checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" 1023 | 1024 | [[package]] 1025 | name = "windows_aarch64_msvc" 1026 | version = "0.52.6" 1027 | source = "registry+https://github.com/rust-lang/crates.io-index" 1028 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 1029 | 1030 | [[package]] 1031 | name = "windows_i686_gnu" 1032 | version = "0.48.5" 1033 | source = "registry+https://github.com/rust-lang/crates.io-index" 1034 | checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" 1035 | 1036 | [[package]] 1037 | name = "windows_i686_gnu" 1038 | version = "0.52.6" 1039 | source = "registry+https://github.com/rust-lang/crates.io-index" 1040 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 1041 | 1042 | [[package]] 1043 | name = "windows_i686_gnullvm" 1044 | version = "0.52.6" 1045 | source = "registry+https://github.com/rust-lang/crates.io-index" 1046 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 1047 | 1048 | [[package]] 1049 | name = "windows_i686_msvc" 1050 | version = "0.48.5" 1051 | source = "registry+https://github.com/rust-lang/crates.io-index" 1052 | checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" 1053 | 1054 | [[package]] 1055 | name = "windows_i686_msvc" 1056 | version = "0.52.6" 1057 | source = "registry+https://github.com/rust-lang/crates.io-index" 1058 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 1059 | 1060 | [[package]] 1061 | name = "windows_x86_64_gnu" 1062 | version = "0.48.5" 1063 | source = "registry+https://github.com/rust-lang/crates.io-index" 1064 | checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" 1065 | 1066 | [[package]] 1067 | name = "windows_x86_64_gnu" 1068 | version = "0.52.6" 1069 | source = "registry+https://github.com/rust-lang/crates.io-index" 1070 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 1071 | 1072 | [[package]] 1073 | name = "windows_x86_64_gnullvm" 1074 | version = "0.48.5" 1075 | source = "registry+https://github.com/rust-lang/crates.io-index" 1076 | checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" 1077 | 1078 | [[package]] 1079 | name = "windows_x86_64_gnullvm" 1080 | version = "0.52.6" 1081 | source = "registry+https://github.com/rust-lang/crates.io-index" 1082 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 1083 | 1084 | [[package]] 1085 | name = "windows_x86_64_msvc" 1086 | version = "0.48.5" 1087 | source = "registry+https://github.com/rust-lang/crates.io-index" 1088 | checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" 1089 | 1090 | [[package]] 1091 | name = "windows_x86_64_msvc" 1092 | version = "0.52.6" 1093 | source = "registry+https://github.com/rust-lang/crates.io-index" 1094 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 1095 | 1096 | [[package]] 1097 | name = "winreg" 1098 | version = "0.50.0" 1099 | source = "registry+https://github.com/rust-lang/crates.io-index" 1100 | checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" 1101 | dependencies = [ 1102 | "cfg-if", 1103 | "windows-sys 0.48.0", 1104 | ] 1105 | 1106 | [[package]] 1107 | name = "writeable" 1108 | version = "0.6.1" 1109 | source = "registry+https://github.com/rust-lang/crates.io-index" 1110 | checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" 1111 | 1112 | [[package]] 1113 | name = "yoke" 1114 | version = "0.8.0" 1115 | source = "registry+https://github.com/rust-lang/crates.io-index" 1116 | checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" 1117 | dependencies = [ 1118 | "serde", 1119 | "stable_deref_trait", 1120 | "yoke-derive", 1121 | "zerofrom", 1122 | ] 1123 | 1124 | [[package]] 1125 | name = "yoke-derive" 1126 | version = "0.8.0" 1127 | source = "registry+https://github.com/rust-lang/crates.io-index" 1128 | checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" 1129 | dependencies = [ 1130 | "proc-macro2", 1131 | "quote", 1132 | "syn 2.0.106", 1133 | "synstructure", 1134 | ] 1135 | 1136 | [[package]] 1137 | name = "zerofrom" 1138 | version = "0.1.6" 1139 | source = "registry+https://github.com/rust-lang/crates.io-index" 1140 | checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" 1141 | dependencies = [ 1142 | "zerofrom-derive", 1143 | ] 1144 | 1145 | [[package]] 1146 | name = "zerofrom-derive" 1147 | version = "0.1.6" 1148 | source = "registry+https://github.com/rust-lang/crates.io-index" 1149 | checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" 1150 | dependencies = [ 1151 | "proc-macro2", 1152 | "quote", 1153 | "syn 2.0.106", 1154 | "synstructure", 1155 | ] 1156 | 1157 | [[package]] 1158 | name = "zerotrie" 1159 | version = "0.2.2" 1160 | source = "registry+https://github.com/rust-lang/crates.io-index" 1161 | checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" 1162 | dependencies = [ 1163 | "displaydoc", 1164 | "yoke", 1165 | "zerofrom", 1166 | ] 1167 | 1168 | [[package]] 1169 | name = "zerovec" 1170 | version = "0.11.4" 1171 | source = "registry+https://github.com/rust-lang/crates.io-index" 1172 | checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" 1173 | dependencies = [ 1174 | "yoke", 1175 | "zerofrom", 1176 | "zerovec-derive", 1177 | ] 1178 | 1179 | [[package]] 1180 | name = "zerovec-derive" 1181 | version = "0.11.1" 1182 | source = "registry+https://github.com/rust-lang/crates.io-index" 1183 | checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" 1184 | dependencies = [ 1185 | "proc-macro2", 1186 | "quote", 1187 | "syn 2.0.106", 1188 | ] 1189 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "toxiproxy_rust" 3 | version = "0.1.9" 4 | authors = ["Peter Arato "] 5 | edition = "2018" 6 | description = "Lightweight client for Toxiproxy" 7 | license = "MIT" 8 | repository = "https://github.com/itarato/toxiproxy_rust" 9 | 10 | [dependencies] 11 | serde = { version = "1.0", features = ["derive"] } 12 | serde_json = "1.0" 13 | reqwest = { version = "0.11", default-features = false, features = [ 14 | "blocking", 15 | "json", 16 | ] } 17 | lazy_static = "1.5" 18 | http = "1.3" 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Toxiproxy - Rust client 2 | 3 | [![Crates.io][crates-badge]][crates-url] 4 | [![Docs][docs-badge]][docs-url] 5 | 6 | [crates-badge]: https://img.shields.io/crates/v/toxiproxy_rust.svg 7 | [crates-url]: https://crates.io/crates/toxiproxy_rust 8 | [docs-url]: https://docs.rs/toxiproxy_rust/latest/toxiproxy_rust/ 9 | [docs-badge]: https://img.shields.io/docsrs/toxiproxy_rust 10 | 11 | Rust client for [Toxiproxy](https://github.com/Shopify/toxiproxy). 12 | 13 | ## Usage 14 | 15 | Populating proxies: 16 | 17 | ```rust 18 | let proxies = TOXIPROXY.populate(vec![ 19 | Proxy::new( 20 | "socket_service".into(), 21 | "localhost:2001".into(), 22 | "localhost:2000".into(), 23 | ), 24 | Proxy::new( 25 | "redis".into(), 26 | "localhost:6000".into(), 27 | "localhost:6379".into(), 28 | ) 29 | ])?; 30 | ``` 31 | 32 | Testing with an unavailable connection: 33 | 34 | ```rust 35 | TOXIPROXY.find_and_reset_proxy("redis")?.with_down(|| { 36 | // Calling the desired service... 37 | })?; 38 | ``` 39 | 40 | Testing with toxics (for full documentation on available toxics see [the original docs](https://github.com/Shopify/toxiproxy#toxics)): 41 | 42 | ```rust 43 | TOXIPROXY.find_and_reset_proxy("redis")?.with_latency("downstream".into(), 2000, 0, 1.0).apply(|| { 44 | // Calling the desired service... 45 | })?; 46 | ``` 47 | 48 | Or without a safe lambda (that takes care of resetting a proxy): 49 | 50 | ```rust 51 | TOXIPROXY.find_proxy("redis")?.with_latency("downstream".into(), 2000, 0, 1.0) 52 | // Calling the desired service... 53 | 54 | TOXIPROXY.find_proxy("redis")?.disable(); 55 | // Test unavailability. 56 | TOXIPROXY.find_proxy("redis")?.enable(); 57 | ``` 58 | 59 | Supported toxics: 60 | 61 | - [latency](https://github.com/Shopify/toxiproxy#latency) 62 | - [down](https://github.com/Shopify/toxiproxy#down) 63 | - [bandwith](https://github.com/Shopify/toxiproxy#bandwith) 64 | - [slow close](https://github.com/Shopify/toxiproxy#slow_close) 65 | - [timeout](https://github.com/Shopify/toxiproxy#timeout) 66 | - [slicer](https://github.com/Shopify/toxiproxy#slicer) 67 | - [limit data](https://github.com/Shopify/toxiproxy#limit_data) 68 | 69 | Using a custom address for Toxiproxy server: 70 | 71 | ```rust 72 | let toxiclient: Client = toxiproxy_rust::Client::new("1.2.3.4:5678"); 73 | ``` 74 | 75 | ## Development 76 | 77 | Tests: 78 | 79 | ```bash 80 | $> cargo test -- --test-threads 1 81 | ``` 82 | -------------------------------------------------------------------------------- /src/client.rs: -------------------------------------------------------------------------------- 1 | //! Main client for communicating with the Toxiproxy server. 2 | 3 | use serde_json; 4 | use std::net::ToSocketAddrs; 5 | use std::sync::{Arc, Mutex}; 6 | use std::{collections::HashMap, io::Read}; 7 | 8 | use super::http_client::*; 9 | use super::proxy::*; 10 | 11 | /// Server client. 12 | #[derive(Clone)] 13 | pub struct Client { 14 | client: Arc>, 15 | } 16 | 17 | impl Client { 18 | /// Creates a new client. There is also a prepopulated client, `toxiproxy_rust::TOXIPROXY` 19 | /// connected to the server's default address. 20 | /// 21 | /// # Examples 22 | /// 23 | /// ``` 24 | /// # use toxiproxy_rust::client::Client; 25 | /// let client = Client::new("127.0.0.1:8474"); 26 | /// ``` 27 | pub fn new(toxiproxy_addr: U) -> Self { 28 | Self { 29 | client: Arc::new(Mutex::new(HttpClient::new(toxiproxy_addr))), 30 | } 31 | } 32 | 33 | /// Establish a set of proxies to work with. 34 | /// 35 | /// # Examples 36 | /// 37 | /// ``` 38 | /// # use toxiproxy_rust::client::Client; 39 | /// # use toxiproxy_rust::proxy::ProxyPack; 40 | /// let client = Client::new("127.0.0.1:8474"); 41 | /// let proxies = client.populate(vec![ProxyPack::new( 42 | /// "socket".into(), 43 | /// "localhost:2001".into(), 44 | /// "localhost:2000".into(), 45 | /// )]).expect("populate has completed"); 46 | /// ``` 47 | /// 48 | /// ``` 49 | /// let proxies = toxiproxy_rust::TOXIPROXY.populate(vec![toxiproxy_rust::proxy::ProxyPack::new( 50 | /// "socket".into(), 51 | /// "localhost:2001".into(), 52 | /// "localhost:2000".into(), 53 | /// )]).expect("populate has completed"); 54 | /// ``` 55 | pub fn populate(&self, proxies: Vec) -> Result, String> { 56 | let proxies_json = serde_json::to_string(&proxies).unwrap(); 57 | self.client 58 | .lock() 59 | .map_err(|err| format!("lock error: {}", err))? 60 | .post_with_data("populate", proxies_json) 61 | .and_then(|response| { 62 | response 63 | .json::>>() 64 | .map_err(|err| format!("json deserialize failed: {}", err)) 65 | }) 66 | .map(|ref mut response_obj| response_obj.remove("proxies").unwrap_or(vec![])) 67 | .map(|proxy_packs| { 68 | proxy_packs 69 | .into_iter() 70 | .map(|proxy_pack| Proxy::new(proxy_pack, self.client.clone())) 71 | .collect::>() 72 | }) 73 | } 74 | 75 | /// Enable all proxies and remove all active toxics. 76 | /// 77 | /// # Examples 78 | /// 79 | /// ``` 80 | /// # use toxiproxy_rust::client::Client; 81 | /// # use toxiproxy_rust::proxy::ProxyPack; 82 | /// let client = Client::new("127.0.0.1:8474"); 83 | /// client.reset(); 84 | /// ``` 85 | /// 86 | /// ``` 87 | /// toxiproxy_rust::TOXIPROXY.reset(); 88 | /// ``` 89 | pub fn reset(&self) -> Result<(), String> { 90 | self.client 91 | .lock() 92 | .map_err(|err| format!("lock error: {}", err))? 93 | .post("reset") 94 | .map(|_| ()) 95 | } 96 | 97 | /// Returns all registered proxies and their toxics. 98 | /// 99 | /// # Examples 100 | /// 101 | /// ``` 102 | /// let proxies = toxiproxy_rust::TOXIPROXY.all().expect("all proxies were fetched"); 103 | /// ``` 104 | pub fn all(&self) -> Result, String> { 105 | self.client 106 | .lock() 107 | .map_err(|err| format!("lock error: {}", err))? 108 | .get("proxies") 109 | .and_then(|response| { 110 | response 111 | .json() 112 | .map(|proxy_map: HashMap| { 113 | proxy_map 114 | .into_iter() 115 | .map(|(name, proxy_pack)| { 116 | (name, Proxy::new(proxy_pack, self.client.clone())) 117 | }) 118 | .collect() 119 | }) 120 | .map_err(|err| format!("json deserialize failed: {}", err)) 121 | }) 122 | } 123 | 124 | /// Health check for the Toxiproxy server. 125 | /// 126 | /// # Examples 127 | /// 128 | /// ``` 129 | /// if !toxiproxy_rust::TOXIPROXY.is_running() { 130 | /// /* signal the problem */ 131 | /// } 132 | /// ``` 133 | pub fn is_running(&self) -> bool { 134 | self.client.lock().expect("Client lock failed").is_alive() 135 | } 136 | 137 | /// Version of the Toxiproxy server. 138 | /// 139 | /// # Examples 140 | /// 141 | /// ``` 142 | /// let version = toxiproxy_rust::TOXIPROXY.version().expect("version is returned"); 143 | /// ``` 144 | pub fn version(&self) -> Result { 145 | self.client 146 | .lock() 147 | .map_err(|err| format!("lock error: {}", err))? 148 | .get("version") 149 | .map(|ref mut response| { 150 | let mut body = String::new(); 151 | response 152 | .read_to_string(&mut body) 153 | .expect("HTTP response cannot be read"); 154 | body 155 | }) 156 | } 157 | 158 | /// Fetches a proxy a resets its state (remove active toxics). Usually a good way to start a test and to start setting up 159 | /// toxics fresh against the proxy. 160 | /// 161 | /// # Examples 162 | /// 163 | /// ``` 164 | /// # toxiproxy_rust::TOXIPROXY.populate(vec![toxiproxy_rust::proxy::ProxyPack::new( 165 | /// # "socket".into(), 166 | /// # "localhost:2001".into(), 167 | /// # "localhost:2000".into(), 168 | /// # )]).unwrap(); 169 | /// let proxy = toxiproxy_rust::TOXIPROXY.find_and_reset_proxy("socket").expect("proxy returned"); 170 | /// ``` 171 | pub fn find_and_reset_proxy(&self, name: &str) -> Result { 172 | self.find_proxy(name).and_then(|proxy| { 173 | proxy.delete_all_toxics()?; 174 | proxy.enable()?; 175 | Ok(proxy) 176 | }) 177 | } 178 | 179 | /// Fetches a proxy. Useful to fetch a proxy for a test where more fine grained control is required 180 | /// over a proxy and its toxics. 181 | /// 182 | /// # Examples 183 | /// 184 | /// ``` 185 | /// # toxiproxy_rust::TOXIPROXY.populate(vec![toxiproxy_rust::proxy::ProxyPack::new( 186 | /// # "socket".into(), 187 | /// # "localhost:2001".into(), 188 | /// # "localhost:2000".into(), 189 | /// # )]).unwrap(); 190 | /// let proxy = toxiproxy_rust::TOXIPROXY.find_proxy("socket").expect("proxy returned"); 191 | /// ``` 192 | pub fn find_proxy(&self, name: &str) -> Result { 193 | let path = format!("proxies/{}", name); 194 | 195 | self.client 196 | .lock() 197 | .map_err(|err| format!("lock error: {}", err))? 198 | .get(&path) 199 | .and_then(|response| { 200 | response 201 | .json() 202 | .map_err(|err| format!("json deserialize failed: {}", err)) 203 | }) 204 | .and_then(|proxy_pack: ProxyPack| Ok(Proxy::new(proxy_pack, self.client.clone()))) 205 | } 206 | } 207 | -------------------------------------------------------------------------------- /src/consts.rs: -------------------------------------------------------------------------------- 1 | pub const ERR_LOCK: &str = "Lock cannot be granted"; 2 | pub const ERR_JSON_SERIALIZE: &str = "JSON serialization failed"; 3 | -------------------------------------------------------------------------------- /src/http_client.rs: -------------------------------------------------------------------------------- 1 | use reqwest::{blocking::Client, blocking::Response, Url}; 2 | use std::{ 3 | net::{SocketAddr, ToSocketAddrs}, 4 | str::FromStr, 5 | }; 6 | 7 | #[derive(Debug)] 8 | pub struct HttpClient { 9 | client: Client, 10 | toxiproxy_addr: SocketAddr, 11 | } 12 | 13 | impl HttpClient { 14 | pub(crate) fn new(toxiproxy_addr: U) -> Self { 15 | Self { 16 | client: Client::new(), 17 | toxiproxy_addr: toxiproxy_addr.to_socket_addrs().unwrap().next().unwrap(), 18 | } 19 | } 20 | 21 | pub(crate) fn get(&self, path: &str) -> Result { 22 | self.client 23 | .get(self.uri_with_path(path)?) 24 | .header("Content-Type", "application/json") 25 | .send() 26 | .map_err(|err| format!("GET error: {}", err)) 27 | } 28 | 29 | pub(crate) fn post(&self, path: &str) -> Result { 30 | self.client 31 | .post(self.uri_with_path(path)?) 32 | .header("Content-Type", "application/json") 33 | .send() 34 | .map_err(|err| format!("POST error: {}", err)) 35 | } 36 | 37 | pub(crate) fn post_with_data(&self, path: &str, body: String) -> Result { 38 | self.client 39 | .post(self.uri_with_path(path)?) 40 | .header("Content-Type", "application/json") 41 | .body(body) 42 | .send() 43 | .map_err(|err| format!("POST error: {}", err)) 44 | } 45 | 46 | pub(crate) fn delete(&self, path: &str) -> Result { 47 | self.client 48 | .delete(self.uri_with_path(path)?) 49 | .header("Content-Type", "application/json") 50 | .send() 51 | .map_err(|err| format!("DELETE error: {}", err)) 52 | } 53 | 54 | fn uri_with_path(&self, path: &str) -> Result { 55 | let mut base: String = "http://".into(); 56 | base.push_str(&self.toxiproxy_addr.to_string()); 57 | 58 | let mut url = Url::from_str(&base).map_err(|err| format!("Incorrect address: {}", err))?; 59 | 60 | url.set_scheme("http") 61 | .map_err(|_| "invalid scheme".to_owned())?; 62 | url.set_path(path); 63 | Ok(url) 64 | } 65 | 66 | pub(crate) fn is_alive(&self) -> bool { 67 | std::net::TcpStream::connect(self.toxiproxy_addr) 68 | .map(|_| true) 69 | .unwrap_or(false) 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Toxiproxy-Rust is a [Toxiproxy] client written for the Rust language. 2 | //! It's designed for testing network code and its resiliency against various 3 | //! network issues, such as latency or unavailability (and many more). 4 | //! 5 | //! ## Setting up a test 6 | //! 7 | //! ```rust 8 | //! use toxiproxy_rust::{TOXIPROXY, proxy::ProxyPack}; 9 | //! 10 | //! TOXIPROXY.populate(vec![ProxyPack::new( 11 | //! "socket".into(), 12 | //! "localhost:2001".into(), 13 | //! "localhost:2000".into(), 14 | //! )]); 15 | //! 16 | //! TOXIPROXY 17 | //! .find_and_reset_proxy("socket") 18 | //! .unwrap() 19 | //! .with_down(|| { 20 | //! /* For example: 21 | //! let result = MyService::Server.call(); 22 | //! assert!(result.is_ok()); 23 | //! */ 24 | //! }); 25 | //! ``` 26 | //! 27 | //! ## Setting up a more advanced test 28 | //! 29 | //! ```rust 30 | //! use toxiproxy_rust::{TOXIPROXY, proxy::ProxyPack}; 31 | //! 32 | //! TOXIPROXY.populate(vec![ProxyPack::new( 33 | //! "socket".into(), 34 | //! "localhost:2001".into(), 35 | //! "localhost:2000".into(), 36 | //! )]); 37 | //! 38 | //! TOXIPROXY 39 | //! .find_and_reset_proxy("socket") 40 | //! .unwrap() 41 | //! .with_slicer("downstream".into(), 2048, 128, 0, 0.8) 42 | //! .with_bandwidth("downstream".into(), 32, 0.5) 43 | //! .apply(|| { 44 | //! /* For example: 45 | //! let result = MyService::Server.call(); 46 | //! assert!(result.is_ok()); 47 | //! */ 48 | //! }); 49 | //! ``` 50 | //! 51 | //! [Toxiproxy]: https://github.com/Shopify/toxiproxy 52 | 53 | #[macro_use] 54 | extern crate lazy_static; 55 | 56 | pub mod client; 57 | mod consts; 58 | mod http_client; 59 | pub mod proxy; 60 | pub mod toxic; 61 | 62 | use client::*; 63 | 64 | lazy_static! { 65 | /// Pre-built client using the default connection address. 66 | pub static ref TOXIPROXY: Client = Client::new("127.0.0.1:8474"); 67 | } 68 | -------------------------------------------------------------------------------- /src/proxy.rs: -------------------------------------------------------------------------------- 1 | //! Represents a [Proxy] - a connection to a service. Connection reliability can be set by 2 | //! specifying a [`Toxic`] on it. 3 | //! 4 | //! [Proxy]: https://github.com/Shopify/toxiproxy#2-populating-toxiproxy 5 | //! [`Toxic`]: toxic.ToxicPack.html 6 | 7 | use super::consts::*; 8 | use super::http_client::*; 9 | use super::toxic::*; 10 | use serde::{Deserialize, Serialize}; 11 | use std::collections::HashMap; 12 | use std::sync::{Arc, Mutex}; 13 | 14 | /// Raw info about a Proxy. 15 | #[derive(Serialize, Deserialize, Debug)] 16 | pub struct ProxyPack { 17 | pub name: String, 18 | pub listen: String, 19 | pub upstream: String, 20 | pub enabled: bool, 21 | pub toxics: Vec, 22 | } 23 | 24 | impl ProxyPack { 25 | /// Create a new Proxy configuration. 26 | /// 27 | /// # Examples 28 | /// 29 | /// ``` 30 | /// let proxy_pack = toxiproxy_rust::proxy::ProxyPack::new( 31 | /// "socket".into(), 32 | /// "localhost:2001".into(), 33 | /// "localhost:2000".into(), 34 | /// ); 35 | /// ``` 36 | pub fn new(name: String, listen: String, upstream: String) -> Self { 37 | Self { 38 | name, 39 | listen, 40 | upstream, 41 | enabled: true, 42 | toxics: vec![], 43 | } 44 | } 45 | } 46 | 47 | /// Client handler of the Proxy object. 48 | #[derive(Debug)] 49 | pub struct Proxy { 50 | pub proxy_pack: ProxyPack, 51 | client: Arc>, 52 | } 53 | 54 | impl Proxy { 55 | pub(crate) fn new(proxy_pack: ProxyPack, client: Arc>) -> Self { 56 | Self { proxy_pack, client } 57 | } 58 | 59 | /// Disables the proxy - making all connections running through them fail immediately. 60 | /// 61 | /// # Examples 62 | /// 63 | /// ``` 64 | /// # toxiproxy_rust::TOXIPROXY.populate(vec![toxiproxy_rust::proxy::ProxyPack::new( 65 | /// # "socket".into(), 66 | /// # "localhost:2001".into(), 67 | /// # "localhost:2000".into(), 68 | /// # )]); 69 | /// toxiproxy_rust::TOXIPROXY.find_proxy("socket").unwrap().disable(); 70 | /// ``` 71 | pub fn disable(&self) -> Result<(), String> { 72 | let mut payload: HashMap = HashMap::new(); 73 | payload.insert("enabled".into(), false); 74 | let body = serde_json::to_string(&payload).map_err(|_| ERR_JSON_SERIALIZE)?; 75 | 76 | self.update(body) 77 | } 78 | 79 | /// Enables the proxy. 80 | /// 81 | /// # Examples 82 | /// 83 | /// ``` 84 | /// # toxiproxy_rust::TOXIPROXY.populate(vec![toxiproxy_rust::proxy::ProxyPack::new( 85 | /// # "socket".into(), 86 | /// # "localhost:2001".into(), 87 | /// # "localhost:2000".into(), 88 | /// # )]); 89 | /// toxiproxy_rust::TOXIPROXY.find_proxy("socket").unwrap().enable(); 90 | /// ``` 91 | pub fn enable(&self) -> Result<(), String> { 92 | let mut payload: HashMap = HashMap::new(); 93 | payload.insert("enabled".into(), true); 94 | let body = serde_json::to_string(&payload).map_err(|_| ERR_JSON_SERIALIZE)?; 95 | 96 | self.update(body) 97 | } 98 | 99 | fn update(&self, payload: String) -> Result<(), String> { 100 | let path = format!("proxies/{}", self.proxy_pack.name); 101 | 102 | self.client 103 | .lock() 104 | .map_err(|err| format!("lock error: {}", err))? 105 | .post_with_data(&path, payload) 106 | .map(|_| ()) 107 | } 108 | 109 | /// Removes the proxy and all of its toxics. 110 | /// 111 | /// # Examples 112 | /// 113 | /// ``` 114 | /// # toxiproxy_rust::TOXIPROXY.populate(vec![toxiproxy_rust::proxy::ProxyPack::new( 115 | /// # "socket".into(), 116 | /// # "localhost:2001".into(), 117 | /// # "localhost:2000".into(), 118 | /// # )]); 119 | /// toxiproxy_rust::TOXIPROXY.find_proxy("socket").unwrap().delete(); 120 | /// ``` 121 | pub fn delete(&self) -> Result<(), String> { 122 | let path = format!("proxies/{}", self.proxy_pack.name); 123 | 124 | self.client 125 | .lock() 126 | .map_err(|err| format!("lock error: {}", err))? 127 | .delete(&path) 128 | .map(|_| ()) 129 | } 130 | 131 | /// Retrieve all toxics registered on the proxy. 132 | /// 133 | /// # Examples 134 | /// 135 | /// ``` 136 | /// # toxiproxy_rust::TOXIPROXY.populate(vec![toxiproxy_rust::proxy::ProxyPack::new( 137 | /// # "socket".into(), 138 | /// # "localhost:2001".into(), 139 | /// # "localhost:2000".into(), 140 | /// # )]); 141 | /// let toxics = toxiproxy_rust::TOXIPROXY.find_proxy("socket").unwrap().toxics().unwrap(); 142 | /// ``` 143 | pub fn toxics(&self) -> Result, String> { 144 | let path = format!("proxies/{}/toxics", self.proxy_pack.name); 145 | 146 | self.client 147 | .lock() 148 | .map_err(|err| format!("lock error: {}", err))? 149 | .get(&path) 150 | .and_then(|response| { 151 | response 152 | .json() 153 | .map_err(|err| format!("json deserialize failed: {}", err)) 154 | }) 155 | } 156 | 157 | /// Registers a [latency] Toxic. 158 | /// 159 | /// # Examples 160 | /// 161 | /// ``` 162 | /// # toxiproxy_rust::TOXIPROXY.populate(vec![toxiproxy_rust::proxy::ProxyPack::new( 163 | /// # "socket".into(), 164 | /// # "localhost:2001".into(), 165 | /// # "localhost:2000".into(), 166 | /// # )]); 167 | /// toxiproxy_rust::TOXIPROXY 168 | /// .find_proxy("socket") 169 | /// .unwrap() 170 | /// .with_latency("downstream".into(), 2000, 0, 1.0); 171 | /// ``` 172 | /// 173 | /// [latency]: https://github.com/Shopify/toxiproxy#latency 174 | pub fn with_latency( 175 | &self, 176 | stream: String, 177 | latency: ToxicValueType, 178 | jitter: ToxicValueType, 179 | toxicity: f32, 180 | ) -> &Self { 181 | let mut attributes = HashMap::new(); 182 | attributes.insert("latency".into(), latency); 183 | attributes.insert("jitter".into(), jitter); 184 | 185 | self.create_toxic(ToxicPack::new( 186 | "latency".into(), 187 | stream, 188 | toxicity, 189 | attributes, 190 | )) 191 | } 192 | 193 | /// Registers a [bandwith] Toxic. 194 | /// 195 | /// # Examples 196 | /// 197 | /// ``` 198 | /// # toxiproxy_rust::TOXIPROXY.populate(vec![toxiproxy_rust::proxy::ProxyPack::new( 199 | /// # "socket".into(), 200 | /// # "localhost:2001".into(), 201 | /// # "localhost:2000".into(), 202 | /// # )]); 203 | /// toxiproxy_rust::TOXIPROXY 204 | /// .find_proxy("socket") 205 | /// .unwrap() 206 | /// .with_bandwidth("downstream".into(), 500, 1.0); 207 | /// ``` 208 | /// 209 | /// [bandwith]: https://github.com/Shopify/toxiproxy#bandwith 210 | pub fn with_bandwidth(&self, stream: String, rate: ToxicValueType, toxicity: f32) -> &Self { 211 | let mut attributes = HashMap::new(); 212 | attributes.insert("rate".into(), rate); 213 | 214 | self.create_toxic(ToxicPack::new( 215 | "bandwidth".into(), 216 | stream, 217 | toxicity, 218 | attributes, 219 | )) 220 | } 221 | 222 | /// Registers a [slow_close] Toxic. 223 | /// 224 | /// # Examples 225 | /// 226 | /// ``` 227 | /// # toxiproxy_rust::TOXIPROXY.populate(vec![toxiproxy_rust::proxy::ProxyPack::new( 228 | /// # "socket".into(), 229 | /// # "localhost:2001".into(), 230 | /// # "localhost:2000".into(), 231 | /// # )]); 232 | /// toxiproxy_rust::TOXIPROXY 233 | /// .find_proxy("socket") 234 | /// .unwrap() 235 | /// .with_slow_close("downstream".into(), 500, 1.0); 236 | /// ``` 237 | /// 238 | /// [slow_close]: https://github.com/Shopify/toxiproxy#slow_close 239 | pub fn with_slow_close(&self, stream: String, delay: ToxicValueType, toxicity: f32) -> &Self { 240 | let mut attributes = HashMap::new(); 241 | attributes.insert("delay".into(), delay); 242 | 243 | self.create_toxic(ToxicPack::new( 244 | "slow_close".into(), 245 | stream, 246 | toxicity, 247 | attributes, 248 | )) 249 | } 250 | 251 | /// Registers a [timeout] Toxic. 252 | /// 253 | /// # Examples 254 | /// 255 | /// ``` 256 | /// # toxiproxy_rust::TOXIPROXY.populate(vec![toxiproxy_rust::proxy::ProxyPack::new( 257 | /// # "socket".into(), 258 | /// # "localhost:2001".into(), 259 | /// # "localhost:2000".into(), 260 | /// # )]); 261 | /// toxiproxy_rust::TOXIPROXY 262 | /// .find_proxy("socket") 263 | /// .unwrap() 264 | /// .with_timeout("downstream".into(), 5000, 1.0); 265 | /// ``` 266 | /// 267 | /// [timeout]: https://github.com/Shopify/toxiproxy#timeout 268 | pub fn with_timeout(&self, stream: String, timeout: ToxicValueType, toxicity: f32) -> &Self { 269 | let mut attributes = HashMap::new(); 270 | attributes.insert("timeout".into(), timeout); 271 | 272 | self.create_toxic(ToxicPack::new( 273 | "timeout".into(), 274 | stream, 275 | toxicity, 276 | attributes, 277 | )) 278 | } 279 | 280 | /// Registers a [slicer] Toxic. 281 | /// 282 | /// # Examples 283 | /// 284 | /// ``` 285 | /// # toxiproxy_rust::TOXIPROXY.populate(vec![toxiproxy_rust::proxy::ProxyPack::new( 286 | /// # "socket".into(), 287 | /// # "localhost:2001".into(), 288 | /// # "localhost:2000".into(), 289 | /// # )]); 290 | /// toxiproxy_rust::TOXIPROXY 291 | /// .find_proxy("socket") 292 | /// .unwrap() 293 | /// .with_slicer("downstream".into(), 1024, 128, 500, 1.0); 294 | /// ``` 295 | /// 296 | /// [slicer]: https://github.com/Shopify/toxiproxy#slicer 297 | pub fn with_slicer( 298 | &self, 299 | stream: String, 300 | average_size: ToxicValueType, 301 | size_variation: ToxicValueType, 302 | delay: ToxicValueType, 303 | toxicity: f32, 304 | ) -> &Self { 305 | let mut attributes = HashMap::new(); 306 | attributes.insert("average_size".into(), average_size); 307 | attributes.insert("size_variation".into(), size_variation); 308 | attributes.insert("delay".into(), delay); 309 | 310 | self.create_toxic(ToxicPack::new( 311 | "slicer".into(), 312 | stream, 313 | toxicity, 314 | attributes, 315 | )) 316 | } 317 | 318 | /// Registers a [limit_data] Toxic. 319 | /// 320 | /// # Examples 321 | /// 322 | /// ``` 323 | /// # toxiproxy_rust::TOXIPROXY.populate(vec![toxiproxy_rust::proxy::ProxyPack::new( 324 | /// # "socket".into(), 325 | /// # "localhost:2001".into(), 326 | /// # "localhost:2000".into(), 327 | /// # )]); 328 | /// toxiproxy_rust::TOXIPROXY 329 | /// .find_proxy("socket") 330 | /// .unwrap() 331 | /// .with_limit_data("downstream".into(), 2048, 1.0); 332 | /// ``` 333 | /// 334 | /// [limit_data]: https://github.com/Shopify/toxiproxy#limit_data 335 | pub fn with_limit_data(&self, stream: String, bytes: ToxicValueType, toxicity: f32) -> &Self { 336 | let mut attributes = HashMap::new(); 337 | attributes.insert("bytes".into(), bytes); 338 | 339 | self.create_toxic(ToxicPack::new( 340 | "limit_data".into(), 341 | stream, 342 | toxicity, 343 | attributes, 344 | )) 345 | } 346 | 347 | fn create_toxic(&self, toxic: ToxicPack) -> &Self { 348 | let body = serde_json::to_string(&toxic).expect(ERR_JSON_SERIALIZE); 349 | let path = format!("proxies/{}/toxics", self.proxy_pack.name); 350 | 351 | let _ = self 352 | .client 353 | .lock() 354 | .expect(ERR_LOCK) 355 | .post_with_data(&path, body) 356 | .map_err(|err| { 357 | panic!(". creation has failed: {}", err); 358 | }); 359 | 360 | self 361 | } 362 | 363 | /// Runs a call as if the proxy was [disabled]. 364 | /// 365 | /// # Examples 366 | /// 367 | /// ``` 368 | /// # toxiproxy_rust::TOXIPROXY.populate(vec![toxiproxy_rust::proxy::ProxyPack::new( 369 | /// # "socket".into(), 370 | /// # "localhost:2001".into(), 371 | /// # "localhost:2000".into(), 372 | /// # )]); 373 | /// toxiproxy_rust::TOXIPROXY 374 | /// .find_proxy("socket") 375 | /// .unwrap() 376 | /// .with_down(|| { 377 | /// /* Example test: 378 | /// let service_result = MyService::Server::call(params); 379 | /// assert!(service_result.is_err()); 380 | /// */ 381 | /// }); 382 | /// ``` 383 | /// 384 | /// [disabled]: https://github.com/Shopify/toxiproxy#down 385 | pub fn with_down(&self, closure: F) -> Result<(), String> 386 | where 387 | F: FnOnce(), 388 | { 389 | self.disable()?; 390 | closure(); 391 | self.enable() 392 | } 393 | 394 | /// Runs a call with the current Toxic setup for the proxy. 395 | /// It restores proxy state after the call. 396 | /// 397 | /// # Examples 398 | /// 399 | /// ``` 400 | /// # toxiproxy_rust::TOXIPROXY.populate(vec![toxiproxy_rust::proxy::ProxyPack::new( 401 | /// # "socket".into(), 402 | /// # "localhost:2001".into(), 403 | /// # "localhost:2000".into(), 404 | /// # )]); 405 | /// toxiproxy_rust::TOXIPROXY 406 | /// .find_proxy("socket") 407 | /// .unwrap() 408 | /// .with_limit_data("downstream".into(), 2048, 1.0) 409 | /// .apply(|| { 410 | /// /* Example test: 411 | /// let service_result = MyService::Server::call(giant_payload); 412 | /// assert!(service_result.is_err()); 413 | /// 414 | /// let service_result = MyService::Server::call(small_payload); 415 | /// assert!(service_result.is_ok()); 416 | /// */ 417 | /// }); 418 | /// ``` 419 | pub fn apply(&self, closure: F) -> Result<(), String> 420 | where 421 | F: FnOnce(), 422 | { 423 | closure(); 424 | self.delete_all_toxics() 425 | } 426 | 427 | /// Deletes all toxics on the proxy. 428 | /// 429 | /// # Examples 430 | /// 431 | /// ``` 432 | /// # toxiproxy_rust::TOXIPROXY.populate(vec![toxiproxy_rust::proxy::ProxyPack::new( 433 | /// # "socket".into(), 434 | /// # "localhost:2001".into(), 435 | /// # "localhost:2000".into(), 436 | /// # )]); 437 | /// toxiproxy_rust::TOXIPROXY 438 | /// .find_proxy("socket") 439 | /// .unwrap() 440 | /// .delete_all_toxics(); 441 | /// ``` 442 | pub fn delete_all_toxics(&self) -> Result<(), String> { 443 | self.toxics().and_then(|toxic_list| { 444 | for toxic in toxic_list { 445 | let path = format!("proxies/{}/toxics/{}", self.proxy_pack.name, toxic.name); 446 | self.client 447 | .lock() 448 | .map_err(|err| format!("lock error: {}", err))? 449 | .delete(&path)?; 450 | } 451 | 452 | Ok(()) 453 | }) 454 | } 455 | } 456 | -------------------------------------------------------------------------------- /src/toxic.rs: -------------------------------------------------------------------------------- 1 | //! Represents a [Toxic] - an effect on the network connection. 2 | //! 3 | //! [Toxic]: https://github.com/Shopify/toxiproxy#toxics 4 | 5 | use serde::{Deserialize, Serialize}; 6 | use std::collections::HashMap; 7 | 8 | pub type ToxicValueType = u32; 9 | 10 | /// Config of a Toxic. 11 | #[derive(Serialize, Deserialize, Debug)] 12 | pub struct ToxicPack { 13 | pub name: String, 14 | pub r#type: String, 15 | pub stream: String, 16 | pub toxicity: f32, 17 | pub attributes: HashMap, 18 | } 19 | 20 | impl ToxicPack { 21 | pub(crate) fn new( 22 | r#type: String, 23 | stream: String, 24 | toxicity: f32, 25 | attributes: HashMap, 26 | ) -> Self { 27 | let name = format!("{}_{}", r#type, stream); 28 | Self { 29 | name, 30 | r#type, 31 | stream, 32 | toxicity, 33 | attributes, 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /tests/toxiproxy_rust.rs: -------------------------------------------------------------------------------- 1 | #![deny(warnings)] 2 | 3 | use std::net::TcpListener; 4 | use std::net::TcpStream; 5 | use std::thread::spawn; 6 | use std::time::SystemTime; 7 | use std::{io::prelude::*, time::Duration}; 8 | 9 | use proxy::*; 10 | use toxiproxy_rust::*; 11 | 12 | /** 13 | * WARNING!!!: This test depends on Toxiproxy (https://github.com/Shopify/toxiproxy) server running locally on default port. 14 | */ 15 | 16 | #[test] 17 | fn test_is_running() { 18 | assert!(TOXIPROXY.is_running()); 19 | } 20 | 21 | #[test] 22 | fn test_reset() { 23 | assert!(TOXIPROXY.reset().is_ok()); 24 | } 25 | 26 | #[test] 27 | fn test_populate() { 28 | let result = TOXIPROXY.populate(vec![ProxyPack::new( 29 | "socket".into(), 30 | "localhost:2001".into(), 31 | "localhost:2000".into(), 32 | )]); 33 | 34 | assert!(result.is_ok()); 35 | 36 | assert_eq!(1, result.as_ref().unwrap().len()); 37 | assert_eq!("socket", result.as_ref().unwrap()[0].proxy_pack.name); 38 | } 39 | 40 | #[test] 41 | fn test_all() { 42 | populate_example(); 43 | 44 | let result = TOXIPROXY.all(); 45 | assert!(result.is_ok()); 46 | assert_eq!(1, result.as_ref().unwrap().len()); 47 | } 48 | 49 | #[test] 50 | fn test_version() { 51 | assert!(TOXIPROXY.version().is_ok()); 52 | } 53 | 54 | #[test] 55 | fn test_find_and_reset_proxy() { 56 | populate_example(); 57 | 58 | let result = TOXIPROXY.find_and_reset_proxy("socket"); 59 | assert!(result.is_ok()); 60 | 61 | assert_eq!("socket", result.as_ref().unwrap().proxy_pack.name); 62 | } 63 | 64 | #[test] 65 | fn test_find_and_reset_proxy_invalid() { 66 | let result = TOXIPROXY.find_and_reset_proxy("bad-proxy"); 67 | assert!(result.is_err()); 68 | } 69 | 70 | #[test] 71 | fn test_proxy_down() { 72 | populate_example(); 73 | 74 | let result = TOXIPROXY.find_and_reset_proxy("socket"); 75 | assert!(result.is_ok()); 76 | assert!(result.as_ref().unwrap().proxy_pack.enabled); 77 | 78 | assert!(result 79 | .as_ref() 80 | .unwrap() 81 | .with_down(|| { 82 | let result = TOXIPROXY.find_and_reset_proxy("socket"); 83 | assert!(result.is_ok()); 84 | assert!(!result.as_ref().unwrap().proxy_pack.enabled); 85 | }) 86 | .is_ok()); 87 | 88 | let result = TOXIPROXY.find_and_reset_proxy("socket"); 89 | assert!(result.is_ok()); 90 | assert!(result.as_ref().unwrap().proxy_pack.enabled); 91 | } 92 | 93 | #[test] 94 | fn test_proxy_apply_with_latency() { 95 | populate_example(); 96 | 97 | let proxy_result = TOXIPROXY.find_and_reset_proxy("socket"); 98 | assert!(proxy_result.is_ok()); 99 | 100 | let proxy_toxics = proxy_result.as_ref().unwrap().toxics(); 101 | assert!(proxy_toxics.is_ok()); 102 | assert_eq!(0, proxy_toxics.as_ref().unwrap().len()); 103 | 104 | let apply_result = proxy_result 105 | .as_ref() 106 | .unwrap() 107 | .with_latency("downstream".into(), 2000, 0, 1.0) 108 | .apply(|| { 109 | let all = TOXIPROXY.all(); 110 | assert!(all.is_ok()); 111 | let proxy = all.as_ref().unwrap().get("socket"); 112 | assert!(proxy.is_some()); 113 | 114 | let proxy_toxics = proxy.as_ref().unwrap().toxics(); 115 | assert!(proxy_toxics.is_ok()); 116 | assert_eq!(1, proxy_toxics.as_ref().unwrap().len()); 117 | }); 118 | 119 | assert!(apply_result.is_ok()); 120 | 121 | let proxy_toxics = proxy_result.as_ref().unwrap().toxics(); 122 | assert!(proxy_toxics.is_ok()); 123 | assert_eq!(0, proxy_toxics.as_ref().unwrap().len()); 124 | } 125 | 126 | #[test] 127 | fn test_proxy_apply_with_latency_as_separate_calls_for_test() { 128 | populate_example(); 129 | 130 | let proxy_result = TOXIPROXY.find_and_reset_proxy("socket"); 131 | assert!(proxy_result.is_ok()); 132 | 133 | let proxy_toxics = proxy_result.as_ref().unwrap().toxics(); 134 | assert!(proxy_toxics.is_ok()); 135 | assert_eq!(0, proxy_toxics.as_ref().unwrap().len()); 136 | 137 | let _ = proxy_result 138 | .as_ref() 139 | .unwrap() 140 | .with_latency("downstream".into(), 2000, 0, 1.0); 141 | 142 | let all = TOXIPROXY.all(); 143 | assert!(all.is_ok()); 144 | let proxy = all.as_ref().unwrap().get("socket"); 145 | assert!(proxy.is_some()); 146 | 147 | let proxy_toxics = proxy.as_ref().unwrap().toxics(); 148 | assert!(proxy_toxics.is_ok()); 149 | assert_eq!(1, proxy_toxics.as_ref().unwrap().len()); 150 | } 151 | 152 | #[test] 153 | fn test_proxy_apply_with_latency_with_real_request() { 154 | let server_thread = spawn(|| one_take_server()); 155 | populate_example(); 156 | 157 | let proxy_result = TOXIPROXY.find_and_reset_proxy("socket"); 158 | assert!(proxy_result.is_ok()); 159 | 160 | let apply_result = proxy_result 161 | .as_ref() 162 | .unwrap() 163 | .with_latency("downstream".into(), 2000, 0, 1.0) 164 | .apply(|| { 165 | let client_thread = spawn(|| one_shot_client()); 166 | 167 | server_thread.join().expect("Failed closing server thread"); 168 | let duration = client_thread.join().expect("Failed closing client thread"); 169 | 170 | assert!(duration.as_secs() >= 2); 171 | }); 172 | 173 | assert!(apply_result.is_ok()); 174 | } 175 | 176 | /** 177 | * Support functions. 178 | */ 179 | 180 | fn populate_example() { 181 | let result = TOXIPROXY.populate(vec![ProxyPack::new( 182 | "socket".into(), 183 | "localhost:2001".into(), 184 | "localhost:2000".into(), 185 | )]); 186 | 187 | assert!(result.is_ok()); 188 | } 189 | 190 | fn one_shot_client() -> Duration { 191 | let t_start = SystemTime::now(); 192 | 193 | let mut stream = TcpStream::connect("localhost:2001").expect("Failed to connect to server"); 194 | 195 | stream 196 | .write("hello".as_bytes()) 197 | .expect("Client failed sending request"); 198 | 199 | stream 200 | .read(&mut [0u8; 1024]) 201 | .expect("Client failed reading response"); 202 | 203 | t_start.elapsed().expect("Cannot establish duration") 204 | } 205 | 206 | fn one_take_server() { 207 | let mut stream = TcpListener::bind("localhost:2000") 208 | .expect("TcpListener cannot connect") 209 | .incoming() 210 | .next() 211 | .expect("Failed to listen for incoming") 212 | .expect("Request failes"); 213 | 214 | stream 215 | .read(&mut [0u8; 1024]) 216 | .expect("Server failed reading request"); 217 | 218 | stream 219 | .write("byebye".as_bytes()) 220 | .expect("Server failed writing response"); 221 | 222 | stream.flush().expect("Failed flushing connection"); 223 | } 224 | --------------------------------------------------------------------------------