├── .github └── dependabot.yml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── README.md ├── benchmark ├── apisix_conf │ ├── apisix.yaml │ └── config.yaml ├── docker-compose-apisix.yml ├── docker-compose-nginx.yml ├── docker-compose-penguin.yml ├── nginx │ ├── Dockerfile │ └── nginx.conf └── penguin │ ├── Dockerfile │ └── gateway.yaml ├── gateway.yaml └── src ├── builder ├── errors.rs └── mod.rs ├── clusters ├── discovery │ └── mod.rs ├── errors.rs └── mod.rs ├── config ├── args.rs ├── def.rs ├── errors.rs └── mod.rs ├── core ├── lb.rs ├── mod.rs └── plugin.rs ├── errors └── mod.rs ├── lib.rs ├── main.rs ├── plugins ├── cms_rate │ └── mod.rs ├── echo │ └── mod.rs ├── errors.rs └── mod.rs ├── proxy ├── errors.rs ├── mod.rs └── process.rs └── utils └── mod.rs /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "cargo" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "daily" 12 | time: "22:18" 13 | timezone: "Asia/Shanghai" 14 | open-pull-requests-limit: 10 15 | versioning-strategy: auto -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | .vscode -------------------------------------------------------------------------------- /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.24.1" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "f5fb1d8e4442bd405fdfd1dacb42792696b0cf9cb15882e5d097b742a676d375" 10 | dependencies = [ 11 | "gimli", 12 | ] 13 | 14 | [[package]] 15 | name = "adler2" 16 | version = "2.0.0" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" 19 | 20 | [[package]] 21 | name = "ahash" 22 | version = "0.8.11" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" 25 | dependencies = [ 26 | "cfg-if", 27 | "getrandom", 28 | "once_cell", 29 | "version_check", 30 | "zerocopy", 31 | ] 32 | 33 | [[package]] 34 | name = "aho-corasick" 35 | version = "1.1.3" 36 | source = "registry+https://github.com/rust-lang/crates.io-index" 37 | checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" 38 | dependencies = [ 39 | "memchr", 40 | ] 41 | 42 | [[package]] 43 | name = "alloc-no-stdlib" 44 | version = "2.0.4" 45 | source = "registry+https://github.com/rust-lang/crates.io-index" 46 | checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" 47 | 48 | [[package]] 49 | name = "alloc-stdlib" 50 | version = "0.2.2" 51 | source = "registry+https://github.com/rust-lang/crates.io-index" 52 | checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" 53 | dependencies = [ 54 | "alloc-no-stdlib", 55 | ] 56 | 57 | [[package]] 58 | name = "allocator-api2" 59 | version = "0.2.18" 60 | source = "registry+https://github.com/rust-lang/crates.io-index" 61 | checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" 62 | 63 | [[package]] 64 | name = "android-tzdata" 65 | version = "0.1.1" 66 | source = "registry+https://github.com/rust-lang/crates.io-index" 67 | checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" 68 | 69 | [[package]] 70 | name = "android_system_properties" 71 | version = "0.1.5" 72 | source = "registry+https://github.com/rust-lang/crates.io-index" 73 | checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" 74 | dependencies = [ 75 | "libc", 76 | ] 77 | 78 | [[package]] 79 | name = "anstream" 80 | version = "0.6.15" 81 | source = "registry+https://github.com/rust-lang/crates.io-index" 82 | checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" 83 | dependencies = [ 84 | "anstyle", 85 | "anstyle-parse", 86 | "anstyle-query", 87 | "anstyle-wincon", 88 | "colorchoice", 89 | "is_terminal_polyfill", 90 | "utf8parse", 91 | ] 92 | 93 | [[package]] 94 | name = "anstyle" 95 | version = "1.0.8" 96 | source = "registry+https://github.com/rust-lang/crates.io-index" 97 | checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" 98 | 99 | [[package]] 100 | name = "anstyle-parse" 101 | version = "0.2.5" 102 | source = "registry+https://github.com/rust-lang/crates.io-index" 103 | checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" 104 | dependencies = [ 105 | "utf8parse", 106 | ] 107 | 108 | [[package]] 109 | name = "anstyle-query" 110 | version = "1.1.1" 111 | source = "registry+https://github.com/rust-lang/crates.io-index" 112 | checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" 113 | dependencies = [ 114 | "windows-sys 0.52.0", 115 | ] 116 | 117 | [[package]] 118 | name = "anstyle-wincon" 119 | version = "3.0.4" 120 | source = "registry+https://github.com/rust-lang/crates.io-index" 121 | checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" 122 | dependencies = [ 123 | "anstyle", 124 | "windows-sys 0.52.0", 125 | ] 126 | 127 | [[package]] 128 | name = "arc-swap" 129 | version = "1.7.1" 130 | source = "registry+https://github.com/rust-lang/crates.io-index" 131 | checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" 132 | 133 | [[package]] 134 | name = "arraydeque" 135 | version = "0.5.1" 136 | source = "registry+https://github.com/rust-lang/crates.io-index" 137 | checksum = "7d902e3d592a523def97af8f317b08ce16b7ab854c1985a0c671e6f15cebc236" 138 | 139 | [[package]] 140 | name = "arrayvec" 141 | version = "0.7.6" 142 | source = "registry+https://github.com/rust-lang/crates.io-index" 143 | checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" 144 | 145 | [[package]] 146 | name = "async-stream" 147 | version = "0.3.5" 148 | source = "registry+https://github.com/rust-lang/crates.io-index" 149 | checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" 150 | dependencies = [ 151 | "async-stream-impl", 152 | "futures-core", 153 | "pin-project-lite", 154 | ] 155 | 156 | [[package]] 157 | name = "async-stream-impl" 158 | version = "0.3.5" 159 | source = "registry+https://github.com/rust-lang/crates.io-index" 160 | checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" 161 | dependencies = [ 162 | "proc-macro2", 163 | "quote", 164 | "syn 2.0.82", 165 | ] 166 | 167 | [[package]] 168 | name = "async-trait" 169 | version = "0.1.85" 170 | source = "registry+https://github.com/rust-lang/crates.io-index" 171 | checksum = "3f934833b4b7233644e5848f235df3f57ed8c80f1528a26c3dfa13d2147fa056" 172 | dependencies = [ 173 | "proc-macro2", 174 | "quote", 175 | "syn 2.0.82", 176 | ] 177 | 178 | [[package]] 179 | name = "atomic-waker" 180 | version = "1.1.2" 181 | source = "registry+https://github.com/rust-lang/crates.io-index" 182 | checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" 183 | 184 | [[package]] 185 | name = "atty" 186 | version = "0.2.14" 187 | source = "registry+https://github.com/rust-lang/crates.io-index" 188 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 189 | dependencies = [ 190 | "hermit-abi 0.1.19", 191 | "libc", 192 | "winapi", 193 | ] 194 | 195 | [[package]] 196 | name = "autocfg" 197 | version = "1.4.0" 198 | source = "registry+https://github.com/rust-lang/crates.io-index" 199 | checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" 200 | 201 | [[package]] 202 | name = "backtrace" 203 | version = "0.3.74" 204 | source = "registry+https://github.com/rust-lang/crates.io-index" 205 | checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" 206 | dependencies = [ 207 | "addr2line", 208 | "cfg-if", 209 | "libc", 210 | "miniz_oxide", 211 | "object", 212 | "rustc-demangle", 213 | "windows-targets 0.52.6", 214 | ] 215 | 216 | [[package]] 217 | name = "base64" 218 | version = "0.22.1" 219 | source = "registry+https://github.com/rust-lang/crates.io-index" 220 | checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" 221 | 222 | [[package]] 223 | name = "bitflags" 224 | version = "1.3.2" 225 | source = "registry+https://github.com/rust-lang/crates.io-index" 226 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 227 | 228 | [[package]] 229 | name = "bitflags" 230 | version = "2.6.0" 231 | source = "registry+https://github.com/rust-lang/crates.io-index" 232 | checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" 233 | 234 | [[package]] 235 | name = "blake2" 236 | version = "0.10.6" 237 | source = "registry+https://github.com/rust-lang/crates.io-index" 238 | checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" 239 | dependencies = [ 240 | "digest", 241 | ] 242 | 243 | [[package]] 244 | name = "block-buffer" 245 | version = "0.10.4" 246 | source = "registry+https://github.com/rust-lang/crates.io-index" 247 | checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" 248 | dependencies = [ 249 | "generic-array", 250 | ] 251 | 252 | [[package]] 253 | name = "brotli" 254 | version = "3.5.0" 255 | source = "registry+https://github.com/rust-lang/crates.io-index" 256 | checksum = "d640d25bc63c50fb1f0b545ffd80207d2e10a4c965530809b40ba3386825c391" 257 | dependencies = [ 258 | "alloc-no-stdlib", 259 | "alloc-stdlib", 260 | "brotli-decompressor", 261 | ] 262 | 263 | [[package]] 264 | name = "brotli-decompressor" 265 | version = "2.5.1" 266 | source = "registry+https://github.com/rust-lang/crates.io-index" 267 | checksum = "4e2e4afe60d7dd600fdd3de8d0f08c2b7ec039712e3b6137ff98b7004e82de4f" 268 | dependencies = [ 269 | "alloc-no-stdlib", 270 | "alloc-stdlib", 271 | ] 272 | 273 | [[package]] 274 | name = "bumpalo" 275 | version = "3.16.0" 276 | source = "registry+https://github.com/rust-lang/crates.io-index" 277 | checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" 278 | 279 | [[package]] 280 | name = "byteorder" 281 | version = "1.5.0" 282 | source = "registry+https://github.com/rust-lang/crates.io-index" 283 | checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" 284 | 285 | [[package]] 286 | name = "bytes" 287 | version = "1.10.0" 288 | source = "registry+https://github.com/rust-lang/crates.io-index" 289 | checksum = "f61dac84819c6588b558454b194026eb1f09c293b9036ae9b159e74e73ab6cf9" 290 | 291 | [[package]] 292 | name = "cc" 293 | version = "1.1.22" 294 | source = "registry+https://github.com/rust-lang/crates.io-index" 295 | checksum = "9540e661f81799159abee814118cc139a2004b3a3aa3ea37724a1b66530b90e0" 296 | dependencies = [ 297 | "jobserver", 298 | "libc", 299 | "shlex", 300 | ] 301 | 302 | [[package]] 303 | name = "cfg-if" 304 | version = "1.0.0" 305 | source = "registry+https://github.com/rust-lang/crates.io-index" 306 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 307 | 308 | [[package]] 309 | name = "chrono" 310 | version = "0.4.38" 311 | source = "registry+https://github.com/rust-lang/crates.io-index" 312 | checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" 313 | dependencies = [ 314 | "android-tzdata", 315 | "iana-time-zone", 316 | "num-traits", 317 | "serde", 318 | "windows-targets 0.52.6", 319 | ] 320 | 321 | [[package]] 322 | name = "clap" 323 | version = "3.2.25" 324 | source = "registry+https://github.com/rust-lang/crates.io-index" 325 | checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" 326 | dependencies = [ 327 | "atty", 328 | "bitflags 1.3.2", 329 | "clap_derive 3.2.25", 330 | "clap_lex 0.2.4", 331 | "indexmap 1.9.3", 332 | "once_cell", 333 | "strsim 0.10.0", 334 | "termcolor", 335 | "textwrap", 336 | ] 337 | 338 | [[package]] 339 | name = "clap" 340 | version = "4.5.37" 341 | source = "registry+https://github.com/rust-lang/crates.io-index" 342 | checksum = "eccb054f56cbd38340b380d4a8e69ef1f02f1af43db2f0cc817a4774d80ae071" 343 | dependencies = [ 344 | "clap_builder", 345 | "clap_derive 4.5.32", 346 | ] 347 | 348 | [[package]] 349 | name = "clap_builder" 350 | version = "4.5.37" 351 | source = "registry+https://github.com/rust-lang/crates.io-index" 352 | checksum = "efd9466fac8543255d3b1fcad4762c5e116ffe808c8a3043d4263cd4fd4862a2" 353 | dependencies = [ 354 | "anstream", 355 | "anstyle", 356 | "clap_lex 0.7.4", 357 | "strsim 0.11.1", 358 | ] 359 | 360 | [[package]] 361 | name = "clap_derive" 362 | version = "3.2.25" 363 | source = "registry+https://github.com/rust-lang/crates.io-index" 364 | checksum = "ae6371b8bdc8b7d3959e9cf7b22d4435ef3e79e138688421ec654acf8c81b008" 365 | dependencies = [ 366 | "heck 0.4.1", 367 | "proc-macro-error", 368 | "proc-macro2", 369 | "quote", 370 | "syn 1.0.109", 371 | ] 372 | 373 | [[package]] 374 | name = "clap_derive" 375 | version = "4.5.32" 376 | source = "registry+https://github.com/rust-lang/crates.io-index" 377 | checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" 378 | dependencies = [ 379 | "heck 0.5.0", 380 | "proc-macro2", 381 | "quote", 382 | "syn 2.0.82", 383 | ] 384 | 385 | [[package]] 386 | name = "clap_lex" 387 | version = "0.2.4" 388 | source = "registry+https://github.com/rust-lang/crates.io-index" 389 | checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" 390 | dependencies = [ 391 | "os_str_bytes", 392 | ] 393 | 394 | [[package]] 395 | name = "clap_lex" 396 | version = "0.7.4" 397 | source = "registry+https://github.com/rust-lang/crates.io-index" 398 | checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" 399 | 400 | [[package]] 401 | name = "cmake" 402 | version = "0.1.51" 403 | source = "registry+https://github.com/rust-lang/crates.io-index" 404 | checksum = "fb1e43aa7fd152b1f968787f7dbcdeb306d1867ff373c69955211876c053f91a" 405 | dependencies = [ 406 | "cc", 407 | ] 408 | 409 | [[package]] 410 | name = "colorchoice" 411 | version = "1.0.2" 412 | source = "registry+https://github.com/rust-lang/crates.io-index" 413 | checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" 414 | 415 | [[package]] 416 | name = "config" 417 | version = "0.15.6" 418 | source = "registry+https://github.com/rust-lang/crates.io-index" 419 | checksum = "e329294a796e9b22329669c1f433a746983f9e324e07f4ef135be81bb2262de4" 420 | dependencies = [ 421 | "pathdiff", 422 | "serde", 423 | "winnow", 424 | "yaml-rust2", 425 | ] 426 | 427 | [[package]] 428 | name = "core-foundation-sys" 429 | version = "0.8.7" 430 | source = "registry+https://github.com/rust-lang/crates.io-index" 431 | checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" 432 | 433 | [[package]] 434 | name = "crc32fast" 435 | version = "1.4.2" 436 | source = "registry+https://github.com/rust-lang/crates.io-index" 437 | checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" 438 | dependencies = [ 439 | "cfg-if", 440 | ] 441 | 442 | [[package]] 443 | name = "crossbeam-channel" 444 | version = "0.5.13" 445 | source = "registry+https://github.com/rust-lang/crates.io-index" 446 | checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" 447 | dependencies = [ 448 | "crossbeam-utils", 449 | ] 450 | 451 | [[package]] 452 | name = "crossbeam-queue" 453 | version = "0.3.11" 454 | source = "registry+https://github.com/rust-lang/crates.io-index" 455 | checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" 456 | dependencies = [ 457 | "crossbeam-utils", 458 | ] 459 | 460 | [[package]] 461 | name = "crossbeam-utils" 462 | version = "0.8.20" 463 | source = "registry+https://github.com/rust-lang/crates.io-index" 464 | checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" 465 | 466 | [[package]] 467 | name = "crypto-common" 468 | version = "0.1.6" 469 | source = "registry+https://github.com/rust-lang/crates.io-index" 470 | checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" 471 | dependencies = [ 472 | "generic-array", 473 | "typenum", 474 | ] 475 | 476 | [[package]] 477 | name = "daemonize" 478 | version = "0.5.0" 479 | source = "registry+https://github.com/rust-lang/crates.io-index" 480 | checksum = "ab8bfdaacb3c887a54d41bdf48d3af8873b3f5566469f8ba21b92057509f116e" 481 | dependencies = [ 482 | "libc", 483 | ] 484 | 485 | [[package]] 486 | name = "darling" 487 | version = "0.20.10" 488 | source = "registry+https://github.com/rust-lang/crates.io-index" 489 | checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" 490 | dependencies = [ 491 | "darling_core", 492 | "darling_macro", 493 | ] 494 | 495 | [[package]] 496 | name = "darling_core" 497 | version = "0.20.10" 498 | source = "registry+https://github.com/rust-lang/crates.io-index" 499 | checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" 500 | dependencies = [ 501 | "fnv", 502 | "ident_case", 503 | "proc-macro2", 504 | "quote", 505 | "strsim 0.11.1", 506 | "syn 2.0.82", 507 | ] 508 | 509 | [[package]] 510 | name = "darling_macro" 511 | version = "0.20.10" 512 | source = "registry+https://github.com/rust-lang/crates.io-index" 513 | checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" 514 | dependencies = [ 515 | "darling_core", 516 | "quote", 517 | "syn 2.0.82", 518 | ] 519 | 520 | [[package]] 521 | name = "data-encoding" 522 | version = "2.6.0" 523 | source = "registry+https://github.com/rust-lang/crates.io-index" 524 | checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" 525 | 526 | [[package]] 527 | name = "deranged" 528 | version = "0.3.11" 529 | source = "registry+https://github.com/rust-lang/crates.io-index" 530 | checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" 531 | dependencies = [ 532 | "powerfmt", 533 | "serde", 534 | ] 535 | 536 | [[package]] 537 | name = "derivative" 538 | version = "2.2.0" 539 | source = "registry+https://github.com/rust-lang/crates.io-index" 540 | checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" 541 | dependencies = [ 542 | "proc-macro2", 543 | "quote", 544 | "syn 1.0.109", 545 | ] 546 | 547 | [[package]] 548 | name = "digest" 549 | version = "0.10.7" 550 | source = "registry+https://github.com/rust-lang/crates.io-index" 551 | checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" 552 | dependencies = [ 553 | "block-buffer", 554 | "crypto-common", 555 | "subtle", 556 | ] 557 | 558 | [[package]] 559 | name = "displaydoc" 560 | version = "0.2.5" 561 | source = "registry+https://github.com/rust-lang/crates.io-index" 562 | checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" 563 | dependencies = [ 564 | "proc-macro2", 565 | "quote", 566 | "syn 2.0.82", 567 | ] 568 | 569 | [[package]] 570 | name = "encoding_rs" 571 | version = "0.8.34" 572 | source = "registry+https://github.com/rust-lang/crates.io-index" 573 | checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" 574 | dependencies = [ 575 | "cfg-if", 576 | ] 577 | 578 | [[package]] 579 | name = "enum-as-inner" 580 | version = "0.6.1" 581 | source = "registry+https://github.com/rust-lang/crates.io-index" 582 | checksum = "a1e6a265c649f3f5979b601d26f1d05ada116434c87741c9493cb56218f76cbc" 583 | dependencies = [ 584 | "heck 0.5.0", 585 | "proc-macro2", 586 | "quote", 587 | "syn 2.0.82", 588 | ] 589 | 590 | [[package]] 591 | name = "env_filter" 592 | version = "0.1.2" 593 | source = "registry+https://github.com/rust-lang/crates.io-index" 594 | checksum = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab" 595 | dependencies = [ 596 | "log", 597 | "regex", 598 | ] 599 | 600 | [[package]] 601 | name = "env_logger" 602 | version = "0.11.6" 603 | source = "registry+https://github.com/rust-lang/crates.io-index" 604 | checksum = "dcaee3d8e3cfc3fd92428d477bc97fc29ec8716d180c0d74c643bb26166660e0" 605 | dependencies = [ 606 | "anstream", 607 | "anstyle", 608 | "env_filter", 609 | "humantime", 610 | "log", 611 | ] 612 | 613 | [[package]] 614 | name = "equivalent" 615 | version = "1.0.1" 616 | source = "registry+https://github.com/rust-lang/crates.io-index" 617 | checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" 618 | 619 | [[package]] 620 | name = "flate2" 621 | version = "1.0.34" 622 | source = "registry+https://github.com/rust-lang/crates.io-index" 623 | checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0" 624 | dependencies = [ 625 | "crc32fast", 626 | "libz-ng-sys", 627 | "miniz_oxide", 628 | ] 629 | 630 | [[package]] 631 | name = "fnv" 632 | version = "1.0.7" 633 | source = "registry+https://github.com/rust-lang/crates.io-index" 634 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 635 | 636 | [[package]] 637 | name = "foreign-types" 638 | version = "0.3.2" 639 | source = "registry+https://github.com/rust-lang/crates.io-index" 640 | checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" 641 | dependencies = [ 642 | "foreign-types-shared", 643 | ] 644 | 645 | [[package]] 646 | name = "foreign-types-shared" 647 | version = "0.1.1" 648 | source = "registry+https://github.com/rust-lang/crates.io-index" 649 | checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" 650 | 651 | [[package]] 652 | name = "form_urlencoded" 653 | version = "1.2.1" 654 | source = "registry+https://github.com/rust-lang/crates.io-index" 655 | checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" 656 | dependencies = [ 657 | "percent-encoding", 658 | ] 659 | 660 | [[package]] 661 | name = "futures" 662 | version = "0.3.30" 663 | source = "registry+https://github.com/rust-lang/crates.io-index" 664 | checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" 665 | dependencies = [ 666 | "futures-channel", 667 | "futures-core", 668 | "futures-executor", 669 | "futures-io", 670 | "futures-sink", 671 | "futures-task", 672 | "futures-util", 673 | ] 674 | 675 | [[package]] 676 | name = "futures-channel" 677 | version = "0.3.30" 678 | source = "registry+https://github.com/rust-lang/crates.io-index" 679 | checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" 680 | dependencies = [ 681 | "futures-core", 682 | "futures-sink", 683 | ] 684 | 685 | [[package]] 686 | name = "futures-core" 687 | version = "0.3.30" 688 | source = "registry+https://github.com/rust-lang/crates.io-index" 689 | checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" 690 | 691 | [[package]] 692 | name = "futures-executor" 693 | version = "0.3.30" 694 | source = "registry+https://github.com/rust-lang/crates.io-index" 695 | checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" 696 | dependencies = [ 697 | "futures-core", 698 | "futures-task", 699 | "futures-util", 700 | ] 701 | 702 | [[package]] 703 | name = "futures-io" 704 | version = "0.3.30" 705 | source = "registry+https://github.com/rust-lang/crates.io-index" 706 | checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" 707 | 708 | [[package]] 709 | name = "futures-macro" 710 | version = "0.3.30" 711 | source = "registry+https://github.com/rust-lang/crates.io-index" 712 | checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" 713 | dependencies = [ 714 | "proc-macro2", 715 | "quote", 716 | "syn 2.0.82", 717 | ] 718 | 719 | [[package]] 720 | name = "futures-sink" 721 | version = "0.3.30" 722 | source = "registry+https://github.com/rust-lang/crates.io-index" 723 | checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" 724 | 725 | [[package]] 726 | name = "futures-task" 727 | version = "0.3.30" 728 | source = "registry+https://github.com/rust-lang/crates.io-index" 729 | checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" 730 | 731 | [[package]] 732 | name = "futures-util" 733 | version = "0.3.30" 734 | source = "registry+https://github.com/rust-lang/crates.io-index" 735 | checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" 736 | dependencies = [ 737 | "futures-channel", 738 | "futures-core", 739 | "futures-io", 740 | "futures-macro", 741 | "futures-sink", 742 | "futures-task", 743 | "memchr", 744 | "pin-project-lite", 745 | "pin-utils", 746 | "slab", 747 | ] 748 | 749 | [[package]] 750 | name = "generic-array" 751 | version = "0.14.7" 752 | source = "registry+https://github.com/rust-lang/crates.io-index" 753 | checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" 754 | dependencies = [ 755 | "typenum", 756 | "version_check", 757 | ] 758 | 759 | [[package]] 760 | name = "getrandom" 761 | version = "0.2.15" 762 | source = "registry+https://github.com/rust-lang/crates.io-index" 763 | checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" 764 | dependencies = [ 765 | "cfg-if", 766 | "libc", 767 | "wasi", 768 | ] 769 | 770 | [[package]] 771 | name = "gimli" 772 | version = "0.31.0" 773 | source = "registry+https://github.com/rust-lang/crates.io-index" 774 | checksum = "32085ea23f3234fc7846555e85283ba4de91e21016dc0455a16286d87a292d64" 775 | 776 | [[package]] 777 | name = "h2" 778 | version = "0.4.6" 779 | source = "registry+https://github.com/rust-lang/crates.io-index" 780 | checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205" 781 | dependencies = [ 782 | "atomic-waker", 783 | "bytes", 784 | "fnv", 785 | "futures-core", 786 | "futures-sink", 787 | "http", 788 | "indexmap 2.5.0", 789 | "slab", 790 | "tokio", 791 | "tokio-util", 792 | "tracing", 793 | ] 794 | 795 | [[package]] 796 | name = "hashbrown" 797 | version = "0.12.3" 798 | source = "registry+https://github.com/rust-lang/crates.io-index" 799 | checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" 800 | 801 | [[package]] 802 | name = "hashbrown" 803 | version = "0.14.5" 804 | source = "registry+https://github.com/rust-lang/crates.io-index" 805 | checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" 806 | dependencies = [ 807 | "ahash", 808 | "allocator-api2", 809 | ] 810 | 811 | [[package]] 812 | name = "hashlink" 813 | version = "0.8.4" 814 | source = "registry+https://github.com/rust-lang/crates.io-index" 815 | checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7" 816 | dependencies = [ 817 | "hashbrown 0.14.5", 818 | ] 819 | 820 | [[package]] 821 | name = "heck" 822 | version = "0.4.1" 823 | source = "registry+https://github.com/rust-lang/crates.io-index" 824 | checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" 825 | 826 | [[package]] 827 | name = "heck" 828 | version = "0.5.0" 829 | source = "registry+https://github.com/rust-lang/crates.io-index" 830 | checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 831 | 832 | [[package]] 833 | name = "hermit-abi" 834 | version = "0.1.19" 835 | source = "registry+https://github.com/rust-lang/crates.io-index" 836 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 837 | dependencies = [ 838 | "libc", 839 | ] 840 | 841 | [[package]] 842 | name = "hermit-abi" 843 | version = "0.3.9" 844 | source = "registry+https://github.com/rust-lang/crates.io-index" 845 | checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" 846 | 847 | [[package]] 848 | name = "hex" 849 | version = "0.4.3" 850 | source = "registry+https://github.com/rust-lang/crates.io-index" 851 | checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" 852 | 853 | [[package]] 854 | name = "hickory-proto" 855 | version = "0.24.1" 856 | source = "registry+https://github.com/rust-lang/crates.io-index" 857 | checksum = "07698b8420e2f0d6447a436ba999ec85d8fbf2a398bbd737b82cac4a2e96e512" 858 | dependencies = [ 859 | "async-trait", 860 | "cfg-if", 861 | "data-encoding", 862 | "enum-as-inner", 863 | "futures-channel", 864 | "futures-io", 865 | "futures-util", 866 | "idna 0.4.0", 867 | "ipnet", 868 | "once_cell", 869 | "rand", 870 | "thiserror", 871 | "tinyvec", 872 | "tokio", 873 | "tracing", 874 | "url", 875 | ] 876 | 877 | [[package]] 878 | name = "hickory-resolver" 879 | version = "0.24.3" 880 | source = "registry+https://github.com/rust-lang/crates.io-index" 881 | checksum = "dcf287bde7b776e85d7188e6e5db7cf410a2f9531fe82817eb87feed034c8d14" 882 | dependencies = [ 883 | "cfg-if", 884 | "futures-util", 885 | "hickory-proto", 886 | "ipconfig", 887 | "lru-cache", 888 | "once_cell", 889 | "parking_lot", 890 | "rand", 891 | "resolv-conf", 892 | "smallvec", 893 | "thiserror", 894 | "tokio", 895 | "tracing", 896 | ] 897 | 898 | [[package]] 899 | name = "hostname" 900 | version = "0.3.1" 901 | source = "registry+https://github.com/rust-lang/crates.io-index" 902 | checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" 903 | dependencies = [ 904 | "libc", 905 | "match_cfg", 906 | "winapi", 907 | ] 908 | 909 | [[package]] 910 | name = "http" 911 | version = "1.2.0" 912 | source = "registry+https://github.com/rust-lang/crates.io-index" 913 | checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea" 914 | dependencies = [ 915 | "bytes", 916 | "fnv", 917 | "itoa", 918 | ] 919 | 920 | [[package]] 921 | name = "httparse" 922 | version = "1.9.4" 923 | source = "registry+https://github.com/rust-lang/crates.io-index" 924 | checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" 925 | 926 | [[package]] 927 | name = "httpdate" 928 | version = "1.0.3" 929 | source = "registry+https://github.com/rust-lang/crates.io-index" 930 | checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" 931 | 932 | [[package]] 933 | name = "humantime" 934 | version = "2.1.0" 935 | source = "registry+https://github.com/rust-lang/crates.io-index" 936 | checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" 937 | 938 | [[package]] 939 | name = "humantime-serde" 940 | version = "1.1.1" 941 | source = "registry+https://github.com/rust-lang/crates.io-index" 942 | checksum = "57a3db5ea5923d99402c94e9feb261dc5ee9b4efa158b0315f788cf549cc200c" 943 | dependencies = [ 944 | "humantime", 945 | "serde", 946 | ] 947 | 948 | [[package]] 949 | name = "iana-time-zone" 950 | version = "0.1.61" 951 | source = "registry+https://github.com/rust-lang/crates.io-index" 952 | checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" 953 | dependencies = [ 954 | "android_system_properties", 955 | "core-foundation-sys", 956 | "iana-time-zone-haiku", 957 | "js-sys", 958 | "wasm-bindgen", 959 | "windows-core", 960 | ] 961 | 962 | [[package]] 963 | name = "iana-time-zone-haiku" 964 | version = "0.1.2" 965 | source = "registry+https://github.com/rust-lang/crates.io-index" 966 | checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" 967 | dependencies = [ 968 | "cc", 969 | ] 970 | 971 | [[package]] 972 | name = "icu_collections" 973 | version = "1.5.0" 974 | source = "registry+https://github.com/rust-lang/crates.io-index" 975 | checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" 976 | dependencies = [ 977 | "displaydoc", 978 | "yoke", 979 | "zerofrom", 980 | "zerovec", 981 | ] 982 | 983 | [[package]] 984 | name = "icu_locid" 985 | version = "1.5.0" 986 | source = "registry+https://github.com/rust-lang/crates.io-index" 987 | checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" 988 | dependencies = [ 989 | "displaydoc", 990 | "litemap", 991 | "tinystr", 992 | "writeable", 993 | "zerovec", 994 | ] 995 | 996 | [[package]] 997 | name = "icu_locid_transform" 998 | version = "1.5.0" 999 | source = "registry+https://github.com/rust-lang/crates.io-index" 1000 | checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" 1001 | dependencies = [ 1002 | "displaydoc", 1003 | "icu_locid", 1004 | "icu_locid_transform_data", 1005 | "icu_provider", 1006 | "tinystr", 1007 | "zerovec", 1008 | ] 1009 | 1010 | [[package]] 1011 | name = "icu_locid_transform_data" 1012 | version = "1.5.0" 1013 | source = "registry+https://github.com/rust-lang/crates.io-index" 1014 | checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" 1015 | 1016 | [[package]] 1017 | name = "icu_normalizer" 1018 | version = "1.5.0" 1019 | source = "registry+https://github.com/rust-lang/crates.io-index" 1020 | checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" 1021 | dependencies = [ 1022 | "displaydoc", 1023 | "icu_collections", 1024 | "icu_normalizer_data", 1025 | "icu_properties", 1026 | "icu_provider", 1027 | "smallvec", 1028 | "utf16_iter", 1029 | "utf8_iter", 1030 | "write16", 1031 | "zerovec", 1032 | ] 1033 | 1034 | [[package]] 1035 | name = "icu_normalizer_data" 1036 | version = "1.5.0" 1037 | source = "registry+https://github.com/rust-lang/crates.io-index" 1038 | checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" 1039 | 1040 | [[package]] 1041 | name = "icu_properties" 1042 | version = "1.5.1" 1043 | source = "registry+https://github.com/rust-lang/crates.io-index" 1044 | checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" 1045 | dependencies = [ 1046 | "displaydoc", 1047 | "icu_collections", 1048 | "icu_locid_transform", 1049 | "icu_properties_data", 1050 | "icu_provider", 1051 | "tinystr", 1052 | "zerovec", 1053 | ] 1054 | 1055 | [[package]] 1056 | name = "icu_properties_data" 1057 | version = "1.5.0" 1058 | source = "registry+https://github.com/rust-lang/crates.io-index" 1059 | checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" 1060 | 1061 | [[package]] 1062 | name = "icu_provider" 1063 | version = "1.5.0" 1064 | source = "registry+https://github.com/rust-lang/crates.io-index" 1065 | checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" 1066 | dependencies = [ 1067 | "displaydoc", 1068 | "icu_locid", 1069 | "icu_provider_macros", 1070 | "stable_deref_trait", 1071 | "tinystr", 1072 | "writeable", 1073 | "yoke", 1074 | "zerofrom", 1075 | "zerovec", 1076 | ] 1077 | 1078 | [[package]] 1079 | name = "icu_provider_macros" 1080 | version = "1.5.0" 1081 | source = "registry+https://github.com/rust-lang/crates.io-index" 1082 | checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" 1083 | dependencies = [ 1084 | "proc-macro2", 1085 | "quote", 1086 | "syn 2.0.82", 1087 | ] 1088 | 1089 | [[package]] 1090 | name = "ident_case" 1091 | version = "1.0.1" 1092 | source = "registry+https://github.com/rust-lang/crates.io-index" 1093 | checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" 1094 | 1095 | [[package]] 1096 | name = "idna" 1097 | version = "0.4.0" 1098 | source = "registry+https://github.com/rust-lang/crates.io-index" 1099 | checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" 1100 | dependencies = [ 1101 | "unicode-bidi", 1102 | "unicode-normalization", 1103 | ] 1104 | 1105 | [[package]] 1106 | name = "idna" 1107 | version = "0.5.0" 1108 | source = "registry+https://github.com/rust-lang/crates.io-index" 1109 | checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" 1110 | dependencies = [ 1111 | "unicode-bidi", 1112 | "unicode-normalization", 1113 | ] 1114 | 1115 | [[package]] 1116 | name = "idna" 1117 | version = "1.0.3" 1118 | source = "registry+https://github.com/rust-lang/crates.io-index" 1119 | checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" 1120 | dependencies = [ 1121 | "idna_adapter", 1122 | "smallvec", 1123 | "utf8_iter", 1124 | ] 1125 | 1126 | [[package]] 1127 | name = "idna_adapter" 1128 | version = "1.2.0" 1129 | source = "registry+https://github.com/rust-lang/crates.io-index" 1130 | checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" 1131 | dependencies = [ 1132 | "icu_normalizer", 1133 | "icu_properties", 1134 | ] 1135 | 1136 | [[package]] 1137 | name = "indexmap" 1138 | version = "1.9.3" 1139 | source = "registry+https://github.com/rust-lang/crates.io-index" 1140 | checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" 1141 | dependencies = [ 1142 | "autocfg", 1143 | "hashbrown 0.12.3", 1144 | "serde", 1145 | ] 1146 | 1147 | [[package]] 1148 | name = "indexmap" 1149 | version = "2.5.0" 1150 | source = "registry+https://github.com/rust-lang/crates.io-index" 1151 | checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" 1152 | dependencies = [ 1153 | "equivalent", 1154 | "hashbrown 0.14.5", 1155 | "serde", 1156 | ] 1157 | 1158 | [[package]] 1159 | name = "ipconfig" 1160 | version = "0.3.2" 1161 | source = "registry+https://github.com/rust-lang/crates.io-index" 1162 | checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" 1163 | dependencies = [ 1164 | "socket2", 1165 | "widestring", 1166 | "windows-sys 0.48.0", 1167 | "winreg", 1168 | ] 1169 | 1170 | [[package]] 1171 | name = "ipnet" 1172 | version = "2.10.0" 1173 | source = "registry+https://github.com/rust-lang/crates.io-index" 1174 | checksum = "187674a687eed5fe42285b40c6291f9a01517d415fad1c3cbc6a9f778af7fcd4" 1175 | 1176 | [[package]] 1177 | name = "is_terminal_polyfill" 1178 | version = "1.70.1" 1179 | source = "registry+https://github.com/rust-lang/crates.io-index" 1180 | checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" 1181 | 1182 | [[package]] 1183 | name = "itoa" 1184 | version = "1.0.11" 1185 | source = "registry+https://github.com/rust-lang/crates.io-index" 1186 | checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" 1187 | 1188 | [[package]] 1189 | name = "jobserver" 1190 | version = "0.1.32" 1191 | source = "registry+https://github.com/rust-lang/crates.io-index" 1192 | checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" 1193 | dependencies = [ 1194 | "libc", 1195 | ] 1196 | 1197 | [[package]] 1198 | name = "js-sys" 1199 | version = "0.3.70" 1200 | source = "registry+https://github.com/rust-lang/crates.io-index" 1201 | checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" 1202 | dependencies = [ 1203 | "wasm-bindgen", 1204 | ] 1205 | 1206 | [[package]] 1207 | name = "lazy_static" 1208 | version = "1.5.0" 1209 | source = "registry+https://github.com/rust-lang/crates.io-index" 1210 | checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" 1211 | 1212 | [[package]] 1213 | name = "libc" 1214 | version = "0.2.169" 1215 | source = "registry+https://github.com/rust-lang/crates.io-index" 1216 | checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" 1217 | 1218 | [[package]] 1219 | name = "libz-ng-sys" 1220 | version = "1.1.16" 1221 | source = "registry+https://github.com/rust-lang/crates.io-index" 1222 | checksum = "4436751a01da56f1277f323c80d584ffad94a3d14aecd959dd0dff75aa73a438" 1223 | dependencies = [ 1224 | "cmake", 1225 | "libc", 1226 | ] 1227 | 1228 | [[package]] 1229 | name = "linked-hash-map" 1230 | version = "0.5.6" 1231 | source = "registry+https://github.com/rust-lang/crates.io-index" 1232 | checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" 1233 | 1234 | [[package]] 1235 | name = "litemap" 1236 | version = "0.7.3" 1237 | source = "registry+https://github.com/rust-lang/crates.io-index" 1238 | checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704" 1239 | 1240 | [[package]] 1241 | name = "lock_api" 1242 | version = "0.4.12" 1243 | source = "registry+https://github.com/rust-lang/crates.io-index" 1244 | checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" 1245 | dependencies = [ 1246 | "autocfg", 1247 | "scopeguard", 1248 | ] 1249 | 1250 | [[package]] 1251 | name = "log" 1252 | version = "0.4.27" 1253 | source = "registry+https://github.com/rust-lang/crates.io-index" 1254 | checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" 1255 | 1256 | [[package]] 1257 | name = "lru" 1258 | version = "0.12.4" 1259 | source = "registry+https://github.com/rust-lang/crates.io-index" 1260 | checksum = "37ee39891760e7d94734f6f63fedc29a2e4a152f836120753a72503f09fcf904" 1261 | dependencies = [ 1262 | "hashbrown 0.14.5", 1263 | ] 1264 | 1265 | [[package]] 1266 | name = "lru-cache" 1267 | version = "0.1.2" 1268 | source = "registry+https://github.com/rust-lang/crates.io-index" 1269 | checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" 1270 | dependencies = [ 1271 | "linked-hash-map", 1272 | ] 1273 | 1274 | [[package]] 1275 | name = "match_cfg" 1276 | version = "0.1.0" 1277 | source = "registry+https://github.com/rust-lang/crates.io-index" 1278 | checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" 1279 | 1280 | [[package]] 1281 | name = "matchit" 1282 | version = "0.8.6" 1283 | source = "registry+https://github.com/rust-lang/crates.io-index" 1284 | checksum = "2f926ade0c4e170215ae43342bf13b9310a437609c81f29f86c5df6657582ef9" 1285 | 1286 | [[package]] 1287 | name = "memchr" 1288 | version = "2.7.4" 1289 | source = "registry+https://github.com/rust-lang/crates.io-index" 1290 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 1291 | 1292 | [[package]] 1293 | name = "memoffset" 1294 | version = "0.6.5" 1295 | source = "registry+https://github.com/rust-lang/crates.io-index" 1296 | checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" 1297 | dependencies = [ 1298 | "autocfg", 1299 | ] 1300 | 1301 | [[package]] 1302 | name = "miniz_oxide" 1303 | version = "0.8.0" 1304 | source = "registry+https://github.com/rust-lang/crates.io-index" 1305 | checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" 1306 | dependencies = [ 1307 | "adler2", 1308 | ] 1309 | 1310 | [[package]] 1311 | name = "mio" 1312 | version = "1.0.2" 1313 | source = "registry+https://github.com/rust-lang/crates.io-index" 1314 | checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" 1315 | dependencies = [ 1316 | "hermit-abi 0.3.9", 1317 | "libc", 1318 | "wasi", 1319 | "windows-sys 0.52.0", 1320 | ] 1321 | 1322 | [[package]] 1323 | name = "nix" 1324 | version = "0.24.3" 1325 | source = "registry+https://github.com/rust-lang/crates.io-index" 1326 | checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069" 1327 | dependencies = [ 1328 | "bitflags 1.3.2", 1329 | "cfg-if", 1330 | "libc", 1331 | "memoffset", 1332 | ] 1333 | 1334 | [[package]] 1335 | name = "num-conv" 1336 | version = "0.1.0" 1337 | source = "registry+https://github.com/rust-lang/crates.io-index" 1338 | checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" 1339 | 1340 | [[package]] 1341 | name = "num-traits" 1342 | version = "0.2.19" 1343 | source = "registry+https://github.com/rust-lang/crates.io-index" 1344 | checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" 1345 | dependencies = [ 1346 | "autocfg", 1347 | ] 1348 | 1349 | [[package]] 1350 | name = "object" 1351 | version = "0.36.4" 1352 | source = "registry+https://github.com/rust-lang/crates.io-index" 1353 | checksum = "084f1a5821ac4c651660a94a7153d27ac9d8a53736203f58b31945ded098070a" 1354 | dependencies = [ 1355 | "memchr", 1356 | ] 1357 | 1358 | [[package]] 1359 | name = "once_cell" 1360 | version = "1.20.3" 1361 | source = "registry+https://github.com/rust-lang/crates.io-index" 1362 | checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" 1363 | 1364 | [[package]] 1365 | name = "openssl" 1366 | version = "0.10.66" 1367 | source = "registry+https://github.com/rust-lang/crates.io-index" 1368 | checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" 1369 | dependencies = [ 1370 | "bitflags 2.6.0", 1371 | "cfg-if", 1372 | "foreign-types", 1373 | "libc", 1374 | "once_cell", 1375 | "openssl-macros", 1376 | "openssl-sys", 1377 | ] 1378 | 1379 | [[package]] 1380 | name = "openssl-macros" 1381 | version = "0.1.1" 1382 | source = "registry+https://github.com/rust-lang/crates.io-index" 1383 | checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" 1384 | dependencies = [ 1385 | "proc-macro2", 1386 | "quote", 1387 | "syn 2.0.82", 1388 | ] 1389 | 1390 | [[package]] 1391 | name = "openssl-probe" 1392 | version = "0.1.5" 1393 | source = "registry+https://github.com/rust-lang/crates.io-index" 1394 | checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" 1395 | 1396 | [[package]] 1397 | name = "openssl-src" 1398 | version = "300.3.2+3.3.2" 1399 | source = "registry+https://github.com/rust-lang/crates.io-index" 1400 | checksum = "a211a18d945ef7e648cc6e0058f4c548ee46aab922ea203e0d30e966ea23647b" 1401 | dependencies = [ 1402 | "cc", 1403 | ] 1404 | 1405 | [[package]] 1406 | name = "openssl-sys" 1407 | version = "0.9.103" 1408 | source = "registry+https://github.com/rust-lang/crates.io-index" 1409 | checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" 1410 | dependencies = [ 1411 | "cc", 1412 | "libc", 1413 | "openssl-src", 1414 | "pkg-config", 1415 | "vcpkg", 1416 | ] 1417 | 1418 | [[package]] 1419 | name = "os_str_bytes" 1420 | version = "6.6.1" 1421 | source = "registry+https://github.com/rust-lang/crates.io-index" 1422 | checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1" 1423 | 1424 | [[package]] 1425 | name = "parking_lot" 1426 | version = "0.12.3" 1427 | source = "registry+https://github.com/rust-lang/crates.io-index" 1428 | checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" 1429 | dependencies = [ 1430 | "lock_api", 1431 | "parking_lot_core", 1432 | ] 1433 | 1434 | [[package]] 1435 | name = "parking_lot_core" 1436 | version = "0.9.10" 1437 | source = "registry+https://github.com/rust-lang/crates.io-index" 1438 | checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" 1439 | dependencies = [ 1440 | "cfg-if", 1441 | "libc", 1442 | "redox_syscall", 1443 | "smallvec", 1444 | "windows-targets 0.52.6", 1445 | ] 1446 | 1447 | [[package]] 1448 | name = "paste" 1449 | version = "1.0.15" 1450 | source = "registry+https://github.com/rust-lang/crates.io-index" 1451 | checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" 1452 | 1453 | [[package]] 1454 | name = "pathdiff" 1455 | version = "0.2.1" 1456 | source = "registry+https://github.com/rust-lang/crates.io-index" 1457 | checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" 1458 | 1459 | [[package]] 1460 | name = "penguin" 1461 | version = "0.1.0" 1462 | dependencies = [ 1463 | "async-trait", 1464 | "bytes", 1465 | "clap 4.5.37", 1466 | "config", 1467 | "env_logger", 1468 | "hickory-resolver", 1469 | "http", 1470 | "humantime-serde", 1471 | "log", 1472 | "matchit", 1473 | "once_cell", 1474 | "pingora", 1475 | "pingora-limits", 1476 | "regex", 1477 | "serde", 1478 | "serde_with", 1479 | "serde_yaml 0.9.34+deprecated", 1480 | "snafu", 1481 | "tokio", 1482 | "validator", 1483 | ] 1484 | 1485 | [[package]] 1486 | name = "percent-encoding" 1487 | version = "2.3.1" 1488 | source = "registry+https://github.com/rust-lang/crates.io-index" 1489 | checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" 1490 | 1491 | [[package]] 1492 | name = "pin-project-lite" 1493 | version = "0.2.14" 1494 | source = "registry+https://github.com/rust-lang/crates.io-index" 1495 | checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" 1496 | 1497 | [[package]] 1498 | name = "pin-utils" 1499 | version = "0.1.0" 1500 | source = "registry+https://github.com/rust-lang/crates.io-index" 1501 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 1502 | 1503 | [[package]] 1504 | name = "pingora" 1505 | version = "0.4.0" 1506 | source = "registry+https://github.com/rust-lang/crates.io-index" 1507 | checksum = "79c9fc7098dc3e7d09d2d1647921005be9301cf68536826195dc5369e05124bd" 1508 | dependencies = [ 1509 | "pingora-cache", 1510 | "pingora-core", 1511 | "pingora-http", 1512 | "pingora-load-balancing", 1513 | "pingora-proxy", 1514 | "pingora-timeout", 1515 | ] 1516 | 1517 | [[package]] 1518 | name = "pingora-cache" 1519 | version = "0.4.0" 1520 | source = "registry+https://github.com/rust-lang/crates.io-index" 1521 | checksum = "35ee62f28526d8d484621e77f8d6a1807f1bd07558a06ab5a204b4834d6be056" 1522 | dependencies = [ 1523 | "ahash", 1524 | "async-trait", 1525 | "blake2", 1526 | "bytes", 1527 | "hex", 1528 | "http", 1529 | "httparse", 1530 | "httpdate", 1531 | "indexmap 1.9.3", 1532 | "log", 1533 | "lru", 1534 | "once_cell", 1535 | "parking_lot", 1536 | "pingora-core", 1537 | "pingora-error", 1538 | "pingora-header-serde", 1539 | "pingora-http", 1540 | "pingora-lru", 1541 | "pingora-timeout", 1542 | "regex", 1543 | "rmp", 1544 | "rmp-serde", 1545 | "rustracing", 1546 | "rustracing_jaeger", 1547 | "serde", 1548 | "strum", 1549 | "tokio", 1550 | ] 1551 | 1552 | [[package]] 1553 | name = "pingora-core" 1554 | version = "0.4.0" 1555 | source = "registry+https://github.com/rust-lang/crates.io-index" 1556 | checksum = "d123320b69bd06e897fc16bd1dde962a7b488c4d2ae825683fbca0198fad8669" 1557 | dependencies = [ 1558 | "ahash", 1559 | "async-trait", 1560 | "brotli", 1561 | "bytes", 1562 | "chrono", 1563 | "clap 3.2.25", 1564 | "daemonize", 1565 | "flate2", 1566 | "futures", 1567 | "h2", 1568 | "http", 1569 | "httparse", 1570 | "httpdate", 1571 | "libc", 1572 | "log", 1573 | "lru", 1574 | "nix", 1575 | "once_cell", 1576 | "openssl-probe", 1577 | "parking_lot", 1578 | "percent-encoding", 1579 | "pingora-error", 1580 | "pingora-http", 1581 | "pingora-openssl", 1582 | "pingora-pool", 1583 | "pingora-runtime", 1584 | "pingora-timeout", 1585 | "prometheus", 1586 | "rand", 1587 | "regex", 1588 | "serde", 1589 | "serde_yaml 0.8.26", 1590 | "sfv", 1591 | "socket2", 1592 | "strum", 1593 | "strum_macros", 1594 | "thread_local", 1595 | "tokio", 1596 | "tokio-test", 1597 | "unicase", 1598 | "windows-sys 0.59.0", 1599 | "zstd", 1600 | ] 1601 | 1602 | [[package]] 1603 | name = "pingora-error" 1604 | version = "0.4.0" 1605 | source = "registry+https://github.com/rust-lang/crates.io-index" 1606 | checksum = "6389511530152c535a554f592ae4a9691b1246cff20eb4564f2a34fc921195c0" 1607 | 1608 | [[package]] 1609 | name = "pingora-header-serde" 1610 | version = "0.4.0" 1611 | source = "registry+https://github.com/rust-lang/crates.io-index" 1612 | checksum = "bcb3f62d852da015e76ced56e93e6d52941679a9825281c90f2897841129e59d" 1613 | dependencies = [ 1614 | "bytes", 1615 | "http", 1616 | "httparse", 1617 | "pingora-error", 1618 | "pingora-http", 1619 | "thread_local", 1620 | "zstd", 1621 | "zstd-safe", 1622 | ] 1623 | 1624 | [[package]] 1625 | name = "pingora-http" 1626 | version = "0.4.0" 1627 | source = "registry+https://github.com/rust-lang/crates.io-index" 1628 | checksum = "70202f126056f366549afc804741e12dd9f419cfc79a0063ab15653007a0f4c6" 1629 | dependencies = [ 1630 | "bytes", 1631 | "http", 1632 | "pingora-error", 1633 | ] 1634 | 1635 | [[package]] 1636 | name = "pingora-ketama" 1637 | version = "0.4.0" 1638 | source = "registry+https://github.com/rust-lang/crates.io-index" 1639 | checksum = "3c1bb6c2e11823a05ec9140fc8827f112b8380d78b837535f284e0a98f24cc0a" 1640 | dependencies = [ 1641 | "crc32fast", 1642 | ] 1643 | 1644 | [[package]] 1645 | name = "pingora-limits" 1646 | version = "0.4.0" 1647 | source = "registry+https://github.com/rust-lang/crates.io-index" 1648 | checksum = "dfcc8e3afeae5a83bbcd415d8d3bb50bea31d2eda2a91f79220b59abab86dd0f" 1649 | dependencies = [ 1650 | "ahash", 1651 | ] 1652 | 1653 | [[package]] 1654 | name = "pingora-load-balancing" 1655 | version = "0.4.0" 1656 | source = "registry+https://github.com/rust-lang/crates.io-index" 1657 | checksum = "84d558167ecb05cea487a6479700390a67fe414724f203e10c3912584a0f2cb1" 1658 | dependencies = [ 1659 | "arc-swap", 1660 | "async-trait", 1661 | "derivative", 1662 | "fnv", 1663 | "futures", 1664 | "http", 1665 | "log", 1666 | "pingora-core", 1667 | "pingora-error", 1668 | "pingora-http", 1669 | "pingora-ketama", 1670 | "pingora-runtime", 1671 | "rand", 1672 | "tokio", 1673 | ] 1674 | 1675 | [[package]] 1676 | name = "pingora-lru" 1677 | version = "0.4.0" 1678 | source = "registry+https://github.com/rust-lang/crates.io-index" 1679 | checksum = "cb50f65f06c4b81ccb3edcceaa54bb9439608506b0b3b8c048798169a64aad8e" 1680 | dependencies = [ 1681 | "arrayvec", 1682 | "hashbrown 0.14.5", 1683 | "parking_lot", 1684 | "rand", 1685 | ] 1686 | 1687 | [[package]] 1688 | name = "pingora-openssl" 1689 | version = "0.4.0" 1690 | source = "registry+https://github.com/rust-lang/crates.io-index" 1691 | checksum = "4f18158b901a02289f2a2a954a531c96e4d0703c94f7c9291981c9e53fddc6c1" 1692 | dependencies = [ 1693 | "foreign-types", 1694 | "libc", 1695 | "openssl", 1696 | "openssl-src", 1697 | "openssl-sys", 1698 | "tokio-openssl", 1699 | ] 1700 | 1701 | [[package]] 1702 | name = "pingora-pool" 1703 | version = "0.4.0" 1704 | source = "registry+https://github.com/rust-lang/crates.io-index" 1705 | checksum = "bacdd5dbdec690d468856d988b170c8bb4ab62e0edefc0f432ba5e326489f421" 1706 | dependencies = [ 1707 | "crossbeam-queue", 1708 | "log", 1709 | "lru", 1710 | "parking_lot", 1711 | "pingora-timeout", 1712 | "thread_local", 1713 | "tokio", 1714 | ] 1715 | 1716 | [[package]] 1717 | name = "pingora-proxy" 1718 | version = "0.4.0" 1719 | source = "registry+https://github.com/rust-lang/crates.io-index" 1720 | checksum = "5031783d6743bd31e4de7d7c7a19e9eecf369174c3cbd8a57eb52bc6bf882d92" 1721 | dependencies = [ 1722 | "async-trait", 1723 | "bytes", 1724 | "clap 3.2.25", 1725 | "futures", 1726 | "h2", 1727 | "http", 1728 | "log", 1729 | "once_cell", 1730 | "pingora-cache", 1731 | "pingora-core", 1732 | "pingora-error", 1733 | "pingora-http", 1734 | "pingora-timeout", 1735 | "regex", 1736 | "tokio", 1737 | ] 1738 | 1739 | [[package]] 1740 | name = "pingora-runtime" 1741 | version = "0.4.0" 1742 | source = "registry+https://github.com/rust-lang/crates.io-index" 1743 | checksum = "31a7c445ca224630961045684201e3cf8da9af0b01f286ed54ff8b2403aaabff" 1744 | dependencies = [ 1745 | "once_cell", 1746 | "rand", 1747 | "thread_local", 1748 | "tokio", 1749 | ] 1750 | 1751 | [[package]] 1752 | name = "pingora-timeout" 1753 | version = "0.4.0" 1754 | source = "registry+https://github.com/rust-lang/crates.io-index" 1755 | checksum = "685bb8808cc1919c63a06ab14fdac9b84a4887ced49259a5c0adc8bfb2ffe558" 1756 | dependencies = [ 1757 | "once_cell", 1758 | "parking_lot", 1759 | "pin-project-lite", 1760 | "thread_local", 1761 | "tokio", 1762 | ] 1763 | 1764 | [[package]] 1765 | name = "pkg-config" 1766 | version = "0.3.31" 1767 | source = "registry+https://github.com/rust-lang/crates.io-index" 1768 | checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" 1769 | 1770 | [[package]] 1771 | name = "powerfmt" 1772 | version = "0.2.0" 1773 | source = "registry+https://github.com/rust-lang/crates.io-index" 1774 | checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" 1775 | 1776 | [[package]] 1777 | name = "ppv-lite86" 1778 | version = "0.2.20" 1779 | source = "registry+https://github.com/rust-lang/crates.io-index" 1780 | checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" 1781 | dependencies = [ 1782 | "zerocopy", 1783 | ] 1784 | 1785 | [[package]] 1786 | name = "proc-macro-error" 1787 | version = "1.0.4" 1788 | source = "registry+https://github.com/rust-lang/crates.io-index" 1789 | checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" 1790 | dependencies = [ 1791 | "proc-macro-error-attr", 1792 | "proc-macro2", 1793 | "quote", 1794 | "syn 1.0.109", 1795 | "version_check", 1796 | ] 1797 | 1798 | [[package]] 1799 | name = "proc-macro-error-attr" 1800 | version = "1.0.4" 1801 | source = "registry+https://github.com/rust-lang/crates.io-index" 1802 | checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" 1803 | dependencies = [ 1804 | "proc-macro2", 1805 | "quote", 1806 | "version_check", 1807 | ] 1808 | 1809 | [[package]] 1810 | name = "proc-macro-error-attr2" 1811 | version = "2.0.0" 1812 | source = "registry+https://github.com/rust-lang/crates.io-index" 1813 | checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" 1814 | dependencies = [ 1815 | "proc-macro2", 1816 | "quote", 1817 | ] 1818 | 1819 | [[package]] 1820 | name = "proc-macro-error2" 1821 | version = "2.0.1" 1822 | source = "registry+https://github.com/rust-lang/crates.io-index" 1823 | checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" 1824 | dependencies = [ 1825 | "proc-macro-error-attr2", 1826 | "proc-macro2", 1827 | "quote", 1828 | "syn 2.0.82", 1829 | ] 1830 | 1831 | [[package]] 1832 | name = "proc-macro2" 1833 | version = "1.0.86" 1834 | source = "registry+https://github.com/rust-lang/crates.io-index" 1835 | checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" 1836 | dependencies = [ 1837 | "unicode-ident", 1838 | ] 1839 | 1840 | [[package]] 1841 | name = "prometheus" 1842 | version = "0.13.4" 1843 | source = "registry+https://github.com/rust-lang/crates.io-index" 1844 | checksum = "3d33c28a30771f7f96db69893f78b857f7450d7e0237e9c8fc6427a81bae7ed1" 1845 | dependencies = [ 1846 | "cfg-if", 1847 | "fnv", 1848 | "lazy_static", 1849 | "memchr", 1850 | "parking_lot", 1851 | "protobuf", 1852 | "thiserror", 1853 | ] 1854 | 1855 | [[package]] 1856 | name = "protobuf" 1857 | version = "2.28.0" 1858 | source = "registry+https://github.com/rust-lang/crates.io-index" 1859 | checksum = "106dd99e98437432fed6519dedecfade6a06a73bb7b2a1e019fdd2bee5778d94" 1860 | 1861 | [[package]] 1862 | name = "quick-error" 1863 | version = "1.2.3" 1864 | source = "registry+https://github.com/rust-lang/crates.io-index" 1865 | checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" 1866 | 1867 | [[package]] 1868 | name = "quote" 1869 | version = "1.0.37" 1870 | source = "registry+https://github.com/rust-lang/crates.io-index" 1871 | checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" 1872 | dependencies = [ 1873 | "proc-macro2", 1874 | ] 1875 | 1876 | [[package]] 1877 | name = "rand" 1878 | version = "0.8.5" 1879 | source = "registry+https://github.com/rust-lang/crates.io-index" 1880 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 1881 | dependencies = [ 1882 | "libc", 1883 | "rand_chacha", 1884 | "rand_core", 1885 | ] 1886 | 1887 | [[package]] 1888 | name = "rand_chacha" 1889 | version = "0.3.1" 1890 | source = "registry+https://github.com/rust-lang/crates.io-index" 1891 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 1892 | dependencies = [ 1893 | "ppv-lite86", 1894 | "rand_core", 1895 | ] 1896 | 1897 | [[package]] 1898 | name = "rand_core" 1899 | version = "0.6.4" 1900 | source = "registry+https://github.com/rust-lang/crates.io-index" 1901 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 1902 | dependencies = [ 1903 | "getrandom", 1904 | ] 1905 | 1906 | [[package]] 1907 | name = "redox_syscall" 1908 | version = "0.5.6" 1909 | source = "registry+https://github.com/rust-lang/crates.io-index" 1910 | checksum = "355ae415ccd3a04315d3f8246e86d67689ea74d88d915576e1589a351062a13b" 1911 | dependencies = [ 1912 | "bitflags 2.6.0", 1913 | ] 1914 | 1915 | [[package]] 1916 | name = "regex" 1917 | version = "1.11.1" 1918 | source = "registry+https://github.com/rust-lang/crates.io-index" 1919 | checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" 1920 | dependencies = [ 1921 | "aho-corasick", 1922 | "memchr", 1923 | "regex-automata", 1924 | "regex-syntax", 1925 | ] 1926 | 1927 | [[package]] 1928 | name = "regex-automata" 1929 | version = "0.4.8" 1930 | source = "registry+https://github.com/rust-lang/crates.io-index" 1931 | checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" 1932 | dependencies = [ 1933 | "aho-corasick", 1934 | "memchr", 1935 | "regex-syntax", 1936 | ] 1937 | 1938 | [[package]] 1939 | name = "regex-syntax" 1940 | version = "0.8.5" 1941 | source = "registry+https://github.com/rust-lang/crates.io-index" 1942 | checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" 1943 | 1944 | [[package]] 1945 | name = "resolv-conf" 1946 | version = "0.7.0" 1947 | source = "registry+https://github.com/rust-lang/crates.io-index" 1948 | checksum = "52e44394d2086d010551b14b53b1f24e31647570cd1deb0379e2c21b329aba00" 1949 | dependencies = [ 1950 | "hostname", 1951 | "quick-error", 1952 | ] 1953 | 1954 | [[package]] 1955 | name = "rmp" 1956 | version = "0.8.14" 1957 | source = "registry+https://github.com/rust-lang/crates.io-index" 1958 | checksum = "228ed7c16fa39782c3b3468e974aec2795e9089153cd08ee2e9aefb3613334c4" 1959 | dependencies = [ 1960 | "byteorder", 1961 | "num-traits", 1962 | "paste", 1963 | ] 1964 | 1965 | [[package]] 1966 | name = "rmp-serde" 1967 | version = "1.3.0" 1968 | source = "registry+https://github.com/rust-lang/crates.io-index" 1969 | checksum = "52e599a477cf9840e92f2cde9a7189e67b42c57532749bf90aea6ec10facd4db" 1970 | dependencies = [ 1971 | "byteorder", 1972 | "rmp", 1973 | "serde", 1974 | ] 1975 | 1976 | [[package]] 1977 | name = "rust_decimal" 1978 | version = "1.36.0" 1979 | source = "registry+https://github.com/rust-lang/crates.io-index" 1980 | checksum = "b082d80e3e3cc52b2ed634388d436fe1f4de6af5786cc2de9ba9737527bdf555" 1981 | dependencies = [ 1982 | "arrayvec", 1983 | "num-traits", 1984 | ] 1985 | 1986 | [[package]] 1987 | name = "rustc-demangle" 1988 | version = "0.1.24" 1989 | source = "registry+https://github.com/rust-lang/crates.io-index" 1990 | checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" 1991 | 1992 | [[package]] 1993 | name = "rustracing" 1994 | version = "0.5.1" 1995 | source = "registry+https://github.com/rust-lang/crates.io-index" 1996 | checksum = "a44822b10c095e574869de2b891e40c724fef42cadaea040d1cd3bdbb13d36a5" 1997 | dependencies = [ 1998 | "backtrace", 1999 | "crossbeam-channel", 2000 | "rand", 2001 | "trackable 0.2.24", 2002 | ] 2003 | 2004 | [[package]] 2005 | name = "rustracing_jaeger" 2006 | version = "0.7.0" 2007 | source = "registry+https://github.com/rust-lang/crates.io-index" 2008 | checksum = "a6c2fe9411ef5f43ac773f0e84ad735804c55719346a7aad52de2d9162db97c8" 2009 | dependencies = [ 2010 | "crossbeam-channel", 2011 | "hostname", 2012 | "percent-encoding", 2013 | "rand", 2014 | "rustracing", 2015 | "thrift_codec", 2016 | "trackable 0.2.24", 2017 | ] 2018 | 2019 | [[package]] 2020 | name = "rustversion" 2021 | version = "1.0.17" 2022 | source = "registry+https://github.com/rust-lang/crates.io-index" 2023 | checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" 2024 | 2025 | [[package]] 2026 | name = "ryu" 2027 | version = "1.0.18" 2028 | source = "registry+https://github.com/rust-lang/crates.io-index" 2029 | checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" 2030 | 2031 | [[package]] 2032 | name = "scopeguard" 2033 | version = "1.2.0" 2034 | source = "registry+https://github.com/rust-lang/crates.io-index" 2035 | checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 2036 | 2037 | [[package]] 2038 | name = "serde" 2039 | version = "1.0.217" 2040 | source = "registry+https://github.com/rust-lang/crates.io-index" 2041 | checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" 2042 | dependencies = [ 2043 | "serde_derive", 2044 | ] 2045 | 2046 | [[package]] 2047 | name = "serde_derive" 2048 | version = "1.0.217" 2049 | source = "registry+https://github.com/rust-lang/crates.io-index" 2050 | checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" 2051 | dependencies = [ 2052 | "proc-macro2", 2053 | "quote", 2054 | "syn 2.0.82", 2055 | ] 2056 | 2057 | [[package]] 2058 | name = "serde_json" 2059 | version = "1.0.128" 2060 | source = "registry+https://github.com/rust-lang/crates.io-index" 2061 | checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" 2062 | dependencies = [ 2063 | "itoa", 2064 | "memchr", 2065 | "ryu", 2066 | "serde", 2067 | ] 2068 | 2069 | [[package]] 2070 | name = "serde_with" 2071 | version = "3.12.0" 2072 | source = "registry+https://github.com/rust-lang/crates.io-index" 2073 | checksum = "d6b6f7f2fcb69f747921f79f3926bd1e203fce4fef62c268dd3abfb6d86029aa" 2074 | dependencies = [ 2075 | "base64", 2076 | "chrono", 2077 | "hex", 2078 | "indexmap 1.9.3", 2079 | "indexmap 2.5.0", 2080 | "serde", 2081 | "serde_derive", 2082 | "serde_json", 2083 | "serde_with_macros", 2084 | "time", 2085 | ] 2086 | 2087 | [[package]] 2088 | name = "serde_with_macros" 2089 | version = "3.12.0" 2090 | source = "registry+https://github.com/rust-lang/crates.io-index" 2091 | checksum = "8d00caa5193a3c8362ac2b73be6b9e768aa5a4b2f721d8f4b339600c3cb51f8e" 2092 | dependencies = [ 2093 | "darling", 2094 | "proc-macro2", 2095 | "quote", 2096 | "syn 2.0.82", 2097 | ] 2098 | 2099 | [[package]] 2100 | name = "serde_yaml" 2101 | version = "0.8.26" 2102 | source = "registry+https://github.com/rust-lang/crates.io-index" 2103 | checksum = "578a7433b776b56a35785ed5ce9a7e777ac0598aac5a6dd1b4b18a307c7fc71b" 2104 | dependencies = [ 2105 | "indexmap 1.9.3", 2106 | "ryu", 2107 | "serde", 2108 | "yaml-rust", 2109 | ] 2110 | 2111 | [[package]] 2112 | name = "serde_yaml" 2113 | version = "0.9.34+deprecated" 2114 | source = "registry+https://github.com/rust-lang/crates.io-index" 2115 | checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" 2116 | dependencies = [ 2117 | "indexmap 2.5.0", 2118 | "itoa", 2119 | "ryu", 2120 | "serde", 2121 | "unsafe-libyaml", 2122 | ] 2123 | 2124 | [[package]] 2125 | name = "sfv" 2126 | version = "0.9.4" 2127 | source = "registry+https://github.com/rust-lang/crates.io-index" 2128 | checksum = "f27daf6ed3fc7ffd5ea3ce9f684fe351c47e50f2fdbb6236e2bad0b440dbe408" 2129 | dependencies = [ 2130 | "data-encoding", 2131 | "indexmap 2.5.0", 2132 | "rust_decimal", 2133 | ] 2134 | 2135 | [[package]] 2136 | name = "shlex" 2137 | version = "1.3.0" 2138 | source = "registry+https://github.com/rust-lang/crates.io-index" 2139 | checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 2140 | 2141 | [[package]] 2142 | name = "signal-hook-registry" 2143 | version = "1.4.2" 2144 | source = "registry+https://github.com/rust-lang/crates.io-index" 2145 | checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" 2146 | dependencies = [ 2147 | "libc", 2148 | ] 2149 | 2150 | [[package]] 2151 | name = "slab" 2152 | version = "0.4.9" 2153 | source = "registry+https://github.com/rust-lang/crates.io-index" 2154 | checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" 2155 | dependencies = [ 2156 | "autocfg", 2157 | ] 2158 | 2159 | [[package]] 2160 | name = "smallvec" 2161 | version = "1.13.2" 2162 | source = "registry+https://github.com/rust-lang/crates.io-index" 2163 | checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" 2164 | 2165 | [[package]] 2166 | name = "snafu" 2167 | version = "0.8.5" 2168 | source = "registry+https://github.com/rust-lang/crates.io-index" 2169 | checksum = "223891c85e2a29c3fe8fb900c1fae5e69c2e42415e3177752e8718475efa5019" 2170 | dependencies = [ 2171 | "snafu-derive", 2172 | ] 2173 | 2174 | [[package]] 2175 | name = "snafu-derive" 2176 | version = "0.8.5" 2177 | source = "registry+https://github.com/rust-lang/crates.io-index" 2178 | checksum = "03c3c6b7927ffe7ecaa769ee0e3994da3b8cafc8f444578982c83ecb161af917" 2179 | dependencies = [ 2180 | "heck 0.5.0", 2181 | "proc-macro2", 2182 | "quote", 2183 | "syn 2.0.82", 2184 | ] 2185 | 2186 | [[package]] 2187 | name = "socket2" 2188 | version = "0.5.7" 2189 | source = "registry+https://github.com/rust-lang/crates.io-index" 2190 | checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" 2191 | dependencies = [ 2192 | "libc", 2193 | "windows-sys 0.52.0", 2194 | ] 2195 | 2196 | [[package]] 2197 | name = "stable_deref_trait" 2198 | version = "1.2.0" 2199 | source = "registry+https://github.com/rust-lang/crates.io-index" 2200 | checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" 2201 | 2202 | [[package]] 2203 | name = "strsim" 2204 | version = "0.10.0" 2205 | source = "registry+https://github.com/rust-lang/crates.io-index" 2206 | checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" 2207 | 2208 | [[package]] 2209 | name = "strsim" 2210 | version = "0.11.1" 2211 | source = "registry+https://github.com/rust-lang/crates.io-index" 2212 | checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" 2213 | 2214 | [[package]] 2215 | name = "strum" 2216 | version = "0.26.3" 2217 | source = "registry+https://github.com/rust-lang/crates.io-index" 2218 | checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" 2219 | dependencies = [ 2220 | "strum_macros", 2221 | ] 2222 | 2223 | [[package]] 2224 | name = "strum_macros" 2225 | version = "0.26.4" 2226 | source = "registry+https://github.com/rust-lang/crates.io-index" 2227 | checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" 2228 | dependencies = [ 2229 | "heck 0.5.0", 2230 | "proc-macro2", 2231 | "quote", 2232 | "rustversion", 2233 | "syn 2.0.82", 2234 | ] 2235 | 2236 | [[package]] 2237 | name = "subtle" 2238 | version = "2.6.1" 2239 | source = "registry+https://github.com/rust-lang/crates.io-index" 2240 | checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" 2241 | 2242 | [[package]] 2243 | name = "syn" 2244 | version = "1.0.109" 2245 | source = "registry+https://github.com/rust-lang/crates.io-index" 2246 | checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" 2247 | dependencies = [ 2248 | "proc-macro2", 2249 | "quote", 2250 | "unicode-ident", 2251 | ] 2252 | 2253 | [[package]] 2254 | name = "syn" 2255 | version = "2.0.82" 2256 | source = "registry+https://github.com/rust-lang/crates.io-index" 2257 | checksum = "83540f837a8afc019423a8edb95b52a8effe46957ee402287f4292fae35be021" 2258 | dependencies = [ 2259 | "proc-macro2", 2260 | "quote", 2261 | "unicode-ident", 2262 | ] 2263 | 2264 | [[package]] 2265 | name = "synstructure" 2266 | version = "0.13.1" 2267 | source = "registry+https://github.com/rust-lang/crates.io-index" 2268 | checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" 2269 | dependencies = [ 2270 | "proc-macro2", 2271 | "quote", 2272 | "syn 2.0.82", 2273 | ] 2274 | 2275 | [[package]] 2276 | name = "termcolor" 2277 | version = "1.4.1" 2278 | source = "registry+https://github.com/rust-lang/crates.io-index" 2279 | checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" 2280 | dependencies = [ 2281 | "winapi-util", 2282 | ] 2283 | 2284 | [[package]] 2285 | name = "textwrap" 2286 | version = "0.16.1" 2287 | source = "registry+https://github.com/rust-lang/crates.io-index" 2288 | checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9" 2289 | 2290 | [[package]] 2291 | name = "thiserror" 2292 | version = "1.0.64" 2293 | source = "registry+https://github.com/rust-lang/crates.io-index" 2294 | checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" 2295 | dependencies = [ 2296 | "thiserror-impl", 2297 | ] 2298 | 2299 | [[package]] 2300 | name = "thiserror-impl" 2301 | version = "1.0.64" 2302 | source = "registry+https://github.com/rust-lang/crates.io-index" 2303 | checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" 2304 | dependencies = [ 2305 | "proc-macro2", 2306 | "quote", 2307 | "syn 2.0.82", 2308 | ] 2309 | 2310 | [[package]] 2311 | name = "thread_local" 2312 | version = "1.1.8" 2313 | source = "registry+https://github.com/rust-lang/crates.io-index" 2314 | checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" 2315 | dependencies = [ 2316 | "cfg-if", 2317 | "once_cell", 2318 | ] 2319 | 2320 | [[package]] 2321 | name = "thrift_codec" 2322 | version = "0.1.1" 2323 | source = "registry+https://github.com/rust-lang/crates.io-index" 2324 | checksum = "8fb61fb3d0a0af14949f3a6949b2639112e13226647112824f4d081533f9b1a8" 2325 | dependencies = [ 2326 | "byteorder", 2327 | "trackable 0.2.24", 2328 | ] 2329 | 2330 | [[package]] 2331 | name = "time" 2332 | version = "0.3.36" 2333 | source = "registry+https://github.com/rust-lang/crates.io-index" 2334 | checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" 2335 | dependencies = [ 2336 | "deranged", 2337 | "itoa", 2338 | "num-conv", 2339 | "powerfmt", 2340 | "serde", 2341 | "time-core", 2342 | "time-macros", 2343 | ] 2344 | 2345 | [[package]] 2346 | name = "time-core" 2347 | version = "0.1.2" 2348 | source = "registry+https://github.com/rust-lang/crates.io-index" 2349 | checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" 2350 | 2351 | [[package]] 2352 | name = "time-macros" 2353 | version = "0.2.18" 2354 | source = "registry+https://github.com/rust-lang/crates.io-index" 2355 | checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" 2356 | dependencies = [ 2357 | "num-conv", 2358 | "time-core", 2359 | ] 2360 | 2361 | [[package]] 2362 | name = "tinystr" 2363 | version = "0.7.6" 2364 | source = "registry+https://github.com/rust-lang/crates.io-index" 2365 | checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" 2366 | dependencies = [ 2367 | "displaydoc", 2368 | "zerovec", 2369 | ] 2370 | 2371 | [[package]] 2372 | name = "tinyvec" 2373 | version = "1.8.0" 2374 | source = "registry+https://github.com/rust-lang/crates.io-index" 2375 | checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" 2376 | dependencies = [ 2377 | "tinyvec_macros", 2378 | ] 2379 | 2380 | [[package]] 2381 | name = "tinyvec_macros" 2382 | version = "0.1.1" 2383 | source = "registry+https://github.com/rust-lang/crates.io-index" 2384 | checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" 2385 | 2386 | [[package]] 2387 | name = "tokio" 2388 | version = "1.43.0" 2389 | source = "registry+https://github.com/rust-lang/crates.io-index" 2390 | checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e" 2391 | dependencies = [ 2392 | "backtrace", 2393 | "bytes", 2394 | "libc", 2395 | "mio", 2396 | "parking_lot", 2397 | "pin-project-lite", 2398 | "signal-hook-registry", 2399 | "socket2", 2400 | "tokio-macros", 2401 | "windows-sys 0.52.0", 2402 | ] 2403 | 2404 | [[package]] 2405 | name = "tokio-macros" 2406 | version = "2.5.0" 2407 | source = "registry+https://github.com/rust-lang/crates.io-index" 2408 | checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" 2409 | dependencies = [ 2410 | "proc-macro2", 2411 | "quote", 2412 | "syn 2.0.82", 2413 | ] 2414 | 2415 | [[package]] 2416 | name = "tokio-openssl" 2417 | version = "0.6.5" 2418 | source = "registry+https://github.com/rust-lang/crates.io-index" 2419 | checksum = "59df6849caa43bb7567f9a36f863c447d95a11d5903c9cc334ba32576a27eadd" 2420 | dependencies = [ 2421 | "openssl", 2422 | "openssl-sys", 2423 | "tokio", 2424 | ] 2425 | 2426 | [[package]] 2427 | name = "tokio-stream" 2428 | version = "0.1.16" 2429 | source = "registry+https://github.com/rust-lang/crates.io-index" 2430 | checksum = "4f4e6ce100d0eb49a2734f8c0812bcd324cf357d21810932c5df6b96ef2b86f1" 2431 | dependencies = [ 2432 | "futures-core", 2433 | "pin-project-lite", 2434 | "tokio", 2435 | ] 2436 | 2437 | [[package]] 2438 | name = "tokio-test" 2439 | version = "0.4.4" 2440 | source = "registry+https://github.com/rust-lang/crates.io-index" 2441 | checksum = "2468baabc3311435b55dd935f702f42cd1b8abb7e754fb7dfb16bd36aa88f9f7" 2442 | dependencies = [ 2443 | "async-stream", 2444 | "bytes", 2445 | "futures-core", 2446 | "tokio", 2447 | "tokio-stream", 2448 | ] 2449 | 2450 | [[package]] 2451 | name = "tokio-util" 2452 | version = "0.7.12" 2453 | source = "registry+https://github.com/rust-lang/crates.io-index" 2454 | checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" 2455 | dependencies = [ 2456 | "bytes", 2457 | "futures-core", 2458 | "futures-sink", 2459 | "pin-project-lite", 2460 | "tokio", 2461 | ] 2462 | 2463 | [[package]] 2464 | name = "tracing" 2465 | version = "0.1.40" 2466 | source = "registry+https://github.com/rust-lang/crates.io-index" 2467 | checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" 2468 | dependencies = [ 2469 | "pin-project-lite", 2470 | "tracing-attributes", 2471 | "tracing-core", 2472 | ] 2473 | 2474 | [[package]] 2475 | name = "tracing-attributes" 2476 | version = "0.1.27" 2477 | source = "registry+https://github.com/rust-lang/crates.io-index" 2478 | checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" 2479 | dependencies = [ 2480 | "proc-macro2", 2481 | "quote", 2482 | "syn 2.0.82", 2483 | ] 2484 | 2485 | [[package]] 2486 | name = "tracing-core" 2487 | version = "0.1.32" 2488 | source = "registry+https://github.com/rust-lang/crates.io-index" 2489 | checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" 2490 | dependencies = [ 2491 | "once_cell", 2492 | ] 2493 | 2494 | [[package]] 2495 | name = "trackable" 2496 | version = "0.2.24" 2497 | source = "registry+https://github.com/rust-lang/crates.io-index" 2498 | checksum = "b98abb9e7300b9ac902cc04920945a874c1973e08c310627cc4458c04b70dd32" 2499 | dependencies = [ 2500 | "trackable 1.3.0", 2501 | "trackable_derive", 2502 | ] 2503 | 2504 | [[package]] 2505 | name = "trackable" 2506 | version = "1.3.0" 2507 | source = "registry+https://github.com/rust-lang/crates.io-index" 2508 | checksum = "b15bd114abb99ef8cee977e517c8f37aee63f184f2d08e3e6ceca092373369ae" 2509 | dependencies = [ 2510 | "trackable_derive", 2511 | ] 2512 | 2513 | [[package]] 2514 | name = "trackable_derive" 2515 | version = "1.0.0" 2516 | source = "registry+https://github.com/rust-lang/crates.io-index" 2517 | checksum = "ebeb235c5847e2f82cfe0f07eb971d1e5f6804b18dac2ae16349cc604380f82f" 2518 | dependencies = [ 2519 | "quote", 2520 | "syn 1.0.109", 2521 | ] 2522 | 2523 | [[package]] 2524 | name = "typenum" 2525 | version = "1.17.0" 2526 | source = "registry+https://github.com/rust-lang/crates.io-index" 2527 | checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" 2528 | 2529 | [[package]] 2530 | name = "unicase" 2531 | version = "2.7.0" 2532 | source = "registry+https://github.com/rust-lang/crates.io-index" 2533 | checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" 2534 | dependencies = [ 2535 | "version_check", 2536 | ] 2537 | 2538 | [[package]] 2539 | name = "unicode-bidi" 2540 | version = "0.3.15" 2541 | source = "registry+https://github.com/rust-lang/crates.io-index" 2542 | checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" 2543 | 2544 | [[package]] 2545 | name = "unicode-ident" 2546 | version = "1.0.13" 2547 | source = "registry+https://github.com/rust-lang/crates.io-index" 2548 | checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" 2549 | 2550 | [[package]] 2551 | name = "unicode-normalization" 2552 | version = "0.1.24" 2553 | source = "registry+https://github.com/rust-lang/crates.io-index" 2554 | checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" 2555 | dependencies = [ 2556 | "tinyvec", 2557 | ] 2558 | 2559 | [[package]] 2560 | name = "unsafe-libyaml" 2561 | version = "0.2.11" 2562 | source = "registry+https://github.com/rust-lang/crates.io-index" 2563 | checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" 2564 | 2565 | [[package]] 2566 | name = "url" 2567 | version = "2.5.2" 2568 | source = "registry+https://github.com/rust-lang/crates.io-index" 2569 | checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" 2570 | dependencies = [ 2571 | "form_urlencoded", 2572 | "idna 0.5.0", 2573 | "percent-encoding", 2574 | ] 2575 | 2576 | [[package]] 2577 | name = "utf16_iter" 2578 | version = "1.0.5" 2579 | source = "registry+https://github.com/rust-lang/crates.io-index" 2580 | checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" 2581 | 2582 | [[package]] 2583 | name = "utf8_iter" 2584 | version = "1.0.4" 2585 | source = "registry+https://github.com/rust-lang/crates.io-index" 2586 | checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" 2587 | 2588 | [[package]] 2589 | name = "utf8parse" 2590 | version = "0.2.2" 2591 | source = "registry+https://github.com/rust-lang/crates.io-index" 2592 | checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" 2593 | 2594 | [[package]] 2595 | name = "validator" 2596 | version = "0.20.0" 2597 | source = "registry+https://github.com/rust-lang/crates.io-index" 2598 | checksum = "43fb22e1a008ece370ce08a3e9e4447a910e92621bb49b85d6e48a45397e7cfa" 2599 | dependencies = [ 2600 | "idna 1.0.3", 2601 | "once_cell", 2602 | "regex", 2603 | "serde", 2604 | "serde_derive", 2605 | "serde_json", 2606 | "url", 2607 | "validator_derive", 2608 | ] 2609 | 2610 | [[package]] 2611 | name = "validator_derive" 2612 | version = "0.20.0" 2613 | source = "registry+https://github.com/rust-lang/crates.io-index" 2614 | checksum = "b7df16e474ef958526d1205f6dda359fdfab79d9aa6d54bafcb92dcd07673dca" 2615 | dependencies = [ 2616 | "darling", 2617 | "once_cell", 2618 | "proc-macro-error2", 2619 | "proc-macro2", 2620 | "quote", 2621 | "syn 2.0.82", 2622 | ] 2623 | 2624 | [[package]] 2625 | name = "vcpkg" 2626 | version = "0.2.15" 2627 | source = "registry+https://github.com/rust-lang/crates.io-index" 2628 | checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" 2629 | 2630 | [[package]] 2631 | name = "version_check" 2632 | version = "0.9.5" 2633 | source = "registry+https://github.com/rust-lang/crates.io-index" 2634 | checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" 2635 | 2636 | [[package]] 2637 | name = "wasi" 2638 | version = "0.11.0+wasi-snapshot-preview1" 2639 | source = "registry+https://github.com/rust-lang/crates.io-index" 2640 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 2641 | 2642 | [[package]] 2643 | name = "wasm-bindgen" 2644 | version = "0.2.93" 2645 | source = "registry+https://github.com/rust-lang/crates.io-index" 2646 | checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" 2647 | dependencies = [ 2648 | "cfg-if", 2649 | "once_cell", 2650 | "wasm-bindgen-macro", 2651 | ] 2652 | 2653 | [[package]] 2654 | name = "wasm-bindgen-backend" 2655 | version = "0.2.93" 2656 | source = "registry+https://github.com/rust-lang/crates.io-index" 2657 | checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" 2658 | dependencies = [ 2659 | "bumpalo", 2660 | "log", 2661 | "once_cell", 2662 | "proc-macro2", 2663 | "quote", 2664 | "syn 2.0.82", 2665 | "wasm-bindgen-shared", 2666 | ] 2667 | 2668 | [[package]] 2669 | name = "wasm-bindgen-macro" 2670 | version = "0.2.93" 2671 | source = "registry+https://github.com/rust-lang/crates.io-index" 2672 | checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" 2673 | dependencies = [ 2674 | "quote", 2675 | "wasm-bindgen-macro-support", 2676 | ] 2677 | 2678 | [[package]] 2679 | name = "wasm-bindgen-macro-support" 2680 | version = "0.2.93" 2681 | source = "registry+https://github.com/rust-lang/crates.io-index" 2682 | checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" 2683 | dependencies = [ 2684 | "proc-macro2", 2685 | "quote", 2686 | "syn 2.0.82", 2687 | "wasm-bindgen-backend", 2688 | "wasm-bindgen-shared", 2689 | ] 2690 | 2691 | [[package]] 2692 | name = "wasm-bindgen-shared" 2693 | version = "0.2.93" 2694 | source = "registry+https://github.com/rust-lang/crates.io-index" 2695 | checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" 2696 | 2697 | [[package]] 2698 | name = "widestring" 2699 | version = "1.1.0" 2700 | source = "registry+https://github.com/rust-lang/crates.io-index" 2701 | checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311" 2702 | 2703 | [[package]] 2704 | name = "winapi" 2705 | version = "0.3.9" 2706 | source = "registry+https://github.com/rust-lang/crates.io-index" 2707 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 2708 | dependencies = [ 2709 | "winapi-i686-pc-windows-gnu", 2710 | "winapi-x86_64-pc-windows-gnu", 2711 | ] 2712 | 2713 | [[package]] 2714 | name = "winapi-i686-pc-windows-gnu" 2715 | version = "0.4.0" 2716 | source = "registry+https://github.com/rust-lang/crates.io-index" 2717 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 2718 | 2719 | [[package]] 2720 | name = "winapi-util" 2721 | version = "0.1.9" 2722 | source = "registry+https://github.com/rust-lang/crates.io-index" 2723 | checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" 2724 | dependencies = [ 2725 | "windows-sys 0.59.0", 2726 | ] 2727 | 2728 | [[package]] 2729 | name = "winapi-x86_64-pc-windows-gnu" 2730 | version = "0.4.0" 2731 | source = "registry+https://github.com/rust-lang/crates.io-index" 2732 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 2733 | 2734 | [[package]] 2735 | name = "windows-core" 2736 | version = "0.52.0" 2737 | source = "registry+https://github.com/rust-lang/crates.io-index" 2738 | checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" 2739 | dependencies = [ 2740 | "windows-targets 0.52.6", 2741 | ] 2742 | 2743 | [[package]] 2744 | name = "windows-sys" 2745 | version = "0.48.0" 2746 | source = "registry+https://github.com/rust-lang/crates.io-index" 2747 | checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" 2748 | dependencies = [ 2749 | "windows-targets 0.48.5", 2750 | ] 2751 | 2752 | [[package]] 2753 | name = "windows-sys" 2754 | version = "0.52.0" 2755 | source = "registry+https://github.com/rust-lang/crates.io-index" 2756 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 2757 | dependencies = [ 2758 | "windows-targets 0.52.6", 2759 | ] 2760 | 2761 | [[package]] 2762 | name = "windows-sys" 2763 | version = "0.59.0" 2764 | source = "registry+https://github.com/rust-lang/crates.io-index" 2765 | checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" 2766 | dependencies = [ 2767 | "windows-targets 0.52.6", 2768 | ] 2769 | 2770 | [[package]] 2771 | name = "windows-targets" 2772 | version = "0.48.5" 2773 | source = "registry+https://github.com/rust-lang/crates.io-index" 2774 | checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" 2775 | dependencies = [ 2776 | "windows_aarch64_gnullvm 0.48.5", 2777 | "windows_aarch64_msvc 0.48.5", 2778 | "windows_i686_gnu 0.48.5", 2779 | "windows_i686_msvc 0.48.5", 2780 | "windows_x86_64_gnu 0.48.5", 2781 | "windows_x86_64_gnullvm 0.48.5", 2782 | "windows_x86_64_msvc 0.48.5", 2783 | ] 2784 | 2785 | [[package]] 2786 | name = "windows-targets" 2787 | version = "0.52.6" 2788 | source = "registry+https://github.com/rust-lang/crates.io-index" 2789 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 2790 | dependencies = [ 2791 | "windows_aarch64_gnullvm 0.52.6", 2792 | "windows_aarch64_msvc 0.52.6", 2793 | "windows_i686_gnu 0.52.6", 2794 | "windows_i686_gnullvm", 2795 | "windows_i686_msvc 0.52.6", 2796 | "windows_x86_64_gnu 0.52.6", 2797 | "windows_x86_64_gnullvm 0.52.6", 2798 | "windows_x86_64_msvc 0.52.6", 2799 | ] 2800 | 2801 | [[package]] 2802 | name = "windows_aarch64_gnullvm" 2803 | version = "0.48.5" 2804 | source = "registry+https://github.com/rust-lang/crates.io-index" 2805 | checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" 2806 | 2807 | [[package]] 2808 | name = "windows_aarch64_gnullvm" 2809 | version = "0.52.6" 2810 | source = "registry+https://github.com/rust-lang/crates.io-index" 2811 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 2812 | 2813 | [[package]] 2814 | name = "windows_aarch64_msvc" 2815 | version = "0.48.5" 2816 | source = "registry+https://github.com/rust-lang/crates.io-index" 2817 | checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" 2818 | 2819 | [[package]] 2820 | name = "windows_aarch64_msvc" 2821 | version = "0.52.6" 2822 | source = "registry+https://github.com/rust-lang/crates.io-index" 2823 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 2824 | 2825 | [[package]] 2826 | name = "windows_i686_gnu" 2827 | version = "0.48.5" 2828 | source = "registry+https://github.com/rust-lang/crates.io-index" 2829 | checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" 2830 | 2831 | [[package]] 2832 | name = "windows_i686_gnu" 2833 | version = "0.52.6" 2834 | source = "registry+https://github.com/rust-lang/crates.io-index" 2835 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 2836 | 2837 | [[package]] 2838 | name = "windows_i686_gnullvm" 2839 | version = "0.52.6" 2840 | source = "registry+https://github.com/rust-lang/crates.io-index" 2841 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 2842 | 2843 | [[package]] 2844 | name = "windows_i686_msvc" 2845 | version = "0.48.5" 2846 | source = "registry+https://github.com/rust-lang/crates.io-index" 2847 | checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" 2848 | 2849 | [[package]] 2850 | name = "windows_i686_msvc" 2851 | version = "0.52.6" 2852 | source = "registry+https://github.com/rust-lang/crates.io-index" 2853 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 2854 | 2855 | [[package]] 2856 | name = "windows_x86_64_gnu" 2857 | version = "0.48.5" 2858 | source = "registry+https://github.com/rust-lang/crates.io-index" 2859 | checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" 2860 | 2861 | [[package]] 2862 | name = "windows_x86_64_gnu" 2863 | version = "0.52.6" 2864 | source = "registry+https://github.com/rust-lang/crates.io-index" 2865 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 2866 | 2867 | [[package]] 2868 | name = "windows_x86_64_gnullvm" 2869 | version = "0.48.5" 2870 | source = "registry+https://github.com/rust-lang/crates.io-index" 2871 | checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" 2872 | 2873 | [[package]] 2874 | name = "windows_x86_64_gnullvm" 2875 | version = "0.52.6" 2876 | source = "registry+https://github.com/rust-lang/crates.io-index" 2877 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 2878 | 2879 | [[package]] 2880 | name = "windows_x86_64_msvc" 2881 | version = "0.48.5" 2882 | source = "registry+https://github.com/rust-lang/crates.io-index" 2883 | checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" 2884 | 2885 | [[package]] 2886 | name = "windows_x86_64_msvc" 2887 | version = "0.52.6" 2888 | source = "registry+https://github.com/rust-lang/crates.io-index" 2889 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 2890 | 2891 | [[package]] 2892 | name = "winnow" 2893 | version = "0.6.20" 2894 | source = "registry+https://github.com/rust-lang/crates.io-index" 2895 | checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" 2896 | dependencies = [ 2897 | "memchr", 2898 | ] 2899 | 2900 | [[package]] 2901 | name = "winreg" 2902 | version = "0.50.0" 2903 | source = "registry+https://github.com/rust-lang/crates.io-index" 2904 | checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" 2905 | dependencies = [ 2906 | "cfg-if", 2907 | "windows-sys 0.48.0", 2908 | ] 2909 | 2910 | [[package]] 2911 | name = "write16" 2912 | version = "1.0.0" 2913 | source = "registry+https://github.com/rust-lang/crates.io-index" 2914 | checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" 2915 | 2916 | [[package]] 2917 | name = "writeable" 2918 | version = "0.5.5" 2919 | source = "registry+https://github.com/rust-lang/crates.io-index" 2920 | checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" 2921 | 2922 | [[package]] 2923 | name = "yaml-rust" 2924 | version = "0.4.5" 2925 | source = "registry+https://github.com/rust-lang/crates.io-index" 2926 | checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" 2927 | dependencies = [ 2928 | "linked-hash-map", 2929 | ] 2930 | 2931 | [[package]] 2932 | name = "yaml-rust2" 2933 | version = "0.9.0" 2934 | source = "registry+https://github.com/rust-lang/crates.io-index" 2935 | checksum = "2a1a1c0bc9823338a3bdf8c61f994f23ac004c6fa32c08cd152984499b445e8d" 2936 | dependencies = [ 2937 | "arraydeque", 2938 | "encoding_rs", 2939 | "hashlink", 2940 | ] 2941 | 2942 | [[package]] 2943 | name = "yoke" 2944 | version = "0.7.4" 2945 | source = "registry+https://github.com/rust-lang/crates.io-index" 2946 | checksum = "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5" 2947 | dependencies = [ 2948 | "serde", 2949 | "stable_deref_trait", 2950 | "yoke-derive", 2951 | "zerofrom", 2952 | ] 2953 | 2954 | [[package]] 2955 | name = "yoke-derive" 2956 | version = "0.7.4" 2957 | source = "registry+https://github.com/rust-lang/crates.io-index" 2958 | checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95" 2959 | dependencies = [ 2960 | "proc-macro2", 2961 | "quote", 2962 | "syn 2.0.82", 2963 | "synstructure", 2964 | ] 2965 | 2966 | [[package]] 2967 | name = "zerocopy" 2968 | version = "0.7.35" 2969 | source = "registry+https://github.com/rust-lang/crates.io-index" 2970 | checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" 2971 | dependencies = [ 2972 | "byteorder", 2973 | "zerocopy-derive", 2974 | ] 2975 | 2976 | [[package]] 2977 | name = "zerocopy-derive" 2978 | version = "0.7.35" 2979 | source = "registry+https://github.com/rust-lang/crates.io-index" 2980 | checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" 2981 | dependencies = [ 2982 | "proc-macro2", 2983 | "quote", 2984 | "syn 2.0.82", 2985 | ] 2986 | 2987 | [[package]] 2988 | name = "zerofrom" 2989 | version = "0.1.4" 2990 | source = "registry+https://github.com/rust-lang/crates.io-index" 2991 | checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55" 2992 | dependencies = [ 2993 | "zerofrom-derive", 2994 | ] 2995 | 2996 | [[package]] 2997 | name = "zerofrom-derive" 2998 | version = "0.1.4" 2999 | source = "registry+https://github.com/rust-lang/crates.io-index" 3000 | checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5" 3001 | dependencies = [ 3002 | "proc-macro2", 3003 | "quote", 3004 | "syn 2.0.82", 3005 | "synstructure", 3006 | ] 3007 | 3008 | [[package]] 3009 | name = "zerovec" 3010 | version = "0.10.4" 3011 | source = "registry+https://github.com/rust-lang/crates.io-index" 3012 | checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" 3013 | dependencies = [ 3014 | "yoke", 3015 | "zerofrom", 3016 | "zerovec-derive", 3017 | ] 3018 | 3019 | [[package]] 3020 | name = "zerovec-derive" 3021 | version = "0.10.3" 3022 | source = "registry+https://github.com/rust-lang/crates.io-index" 3023 | checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" 3024 | dependencies = [ 3025 | "proc-macro2", 3026 | "quote", 3027 | "syn 2.0.82", 3028 | ] 3029 | 3030 | [[package]] 3031 | name = "zstd" 3032 | version = "0.13.2" 3033 | source = "registry+https://github.com/rust-lang/crates.io-index" 3034 | checksum = "fcf2b778a664581e31e389454a7072dab1647606d44f7feea22cd5abb9c9f3f9" 3035 | dependencies = [ 3036 | "zstd-safe", 3037 | ] 3038 | 3039 | [[package]] 3040 | name = "zstd-safe" 3041 | version = "7.2.1" 3042 | source = "registry+https://github.com/rust-lang/crates.io-index" 3043 | checksum = "54a3ab4db68cea366acc5c897c7b4d4d1b8994a9cd6e6f841f8964566a419059" 3044 | dependencies = [ 3045 | "zstd-sys", 3046 | ] 3047 | 3048 | [[package]] 3049 | name = "zstd-sys" 3050 | version = "2.0.13+zstd.1.5.6" 3051 | source = "registry+https://github.com/rust-lang/crates.io-index" 3052 | checksum = "38ff0f21cfee8f97d94cef41359e0c89aa6113028ab0291aa8ca0038995a95aa" 3053 | dependencies = [ 3054 | "cc", 3055 | "pkg-config", 3056 | ] 3057 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "penguin" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | async-trait = "0.1.85" 8 | bytes = {version = "1.10.0"} 9 | clap = { version = "4.5.37", features = ["derive"] } 10 | config = { version = "0.15.6", default-features = false, features = ["yaml"] } 11 | env_logger = { version = "0.11.6", features = ["unstable-kv"] } 12 | hickory-resolver = "0.24.3" 13 | http = "1.2.0" 14 | humantime-serde = "1.1.1" 15 | log = {version = "0.4.27", features = ["kv"]} 16 | matchit = "0.8.6" 17 | once_cell = "1.20.3" 18 | pingora = { version = "0.4.0", features = ["lb", "openssl"] } 19 | pingora-limits = "0.4.0" 20 | regex = "1.11.1" 21 | serde = { version = "1.0.217", features = ["derive"] } 22 | serde_with = "3.12.0" 23 | serde_yaml = "0.9.34" 24 | snafu = "0.8.5" 25 | tokio = { version = "1.43.0", features = ["full"] } 26 | validator = { version = "0.20.0", features = ["derive"] } 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Penguin API Gateway 2 | 3 | ## Introduction 4 | 5 | In a word: **Penguin is to Pingora what Kong or APISIX is to openresty.** 6 | 7 | Penguin is an API gateway built on top of [Pingora](https://github.com/cloudflare/pingora). 8 | 9 | Pingora is very similar to openresty, it's a **framework** for building applications. It's **not** an out of box solution that end-user can use. This is where penguin comes in. 10 | 11 | Penguin is high performance, extensible, and easy to use. 12 | 13 | 14 | Key features include: 15 | - Concise configuration based on YAML 16 | - Flexible routing matching rules 17 | - composable plugins 18 | - **Easy to extend and customize through plugins** 19 | 20 | ## Quick View 21 | 22 | Here's an example of the configuration file: 23 | 24 | ```yaml 25 | services: 26 | - name: service1 27 | listeners: 28 | - name: listener1 29 | address: 0.0.0.0:8080 30 | protocol: http 31 | routes: 32 | - name: public_route 33 | match: 34 | uri: 35 | regexp: "/public/api/v\\d+.*" 36 | plugins: 37 | - name: cms_rate 38 | config: 39 | count: 3 40 | - name: echo 41 | config: 42 | body: "hello, world" 43 | status_code: 200 44 | headers: 45 | x-custom-header: "123" 46 | foo-bar: "baz" 47 | cluster: public_backend_cluster 48 | - name: qq 49 | match: 50 | uri: 51 | exact: "/param" 52 | cluster: public_backend_cluster 53 | 54 | clusters: 55 | - name: public_backend_cluster 56 | resolver: static 57 | lb_policy: round_robin 58 | config: 59 | endpoints: 60 | - 127.0.0.1:9933 61 | - 127.0.0.1:9934 62 | ``` 63 | 64 | ## Configuration Explanation 65 | 66 | Here's a detailed explanation of the configuration file: 67 | 68 | 1. pingora supports multiple seperated services, each service has its own listeners and logic. Similarity, our configuration is consists of multiple services. 69 | ```yaml 70 | services: 71 | - name: service_1 72 | # ... 73 | - name: service_2 74 | # ... 75 | ``` 76 | 77 | 2. Service Configuration: 78 | ```yaml 79 | services: 80 | - name: service1 # service name, just for user's convenience 81 | listeners: 82 | - name: listener1 83 | address: 0.0.0.0:8443 # address to listen 84 | protocol: https # protocol to listen 85 | ssl_config: # tls config if protocol is https 86 | cert: /path/to/cert 87 | key: /path/to/key 88 | plugins: # plugins that will be applied to all routes in this service 89 | - name: cms_rate # name of the plugin, must match the name in the plugin registry. cms_rate is `count-min-sketch` based rate limiter 90 | config: # plugin specific configuration 91 | total: 100 # 100 requests per ${interval} 92 | interval: 1m 93 | routes: 94 | - name: route_1 95 | match: # match rule is to define how to match incoming requests 96 | uri: # match by request uri 97 | regexp: "/public/api/v\\d+.*" # {regexp, prefix, exact} are supported 98 | plugins: # plugins apply for this route only, order matters, first configured plugin will be applied first 99 | - name: cms_rate # name of the plugin, must match the name of the plugin in the plugin registry. cms_rate is `count-min-sketch` based rate limiter 100 | config: # plugin specific configuration 101 | total: 3 # 3 requests per ${interval} 102 | interval: 5s 103 | cluster: cluster_aa # backend cluster to forward to, use this name to refer to the cluster 104 | - name: route_hello 105 | match: # match rule is to define how to match incoming requests 106 | uri: # match by request uri 107 | prefix: "/hello" # {regexp, prefix, exact} are supported 108 | cluster: cluster_bb # backend cluster to forward to, use this name to refer to the cluster 109 | # other routes 110 | clusters: # set of backend clusters 111 | - name: cluster_aa # name of the cluster 112 | resolver: static # how to resolve ip address of the cluster, currently supported: static, dns (consul, k8s, nacos... are on the roadmap) 113 | lb_policy: round_robin # load balancing policy, currently supported: round_robin, random 114 | config: # cluster specific configuration 115 | endpoints: # for static resolver, just list all backend addresses 116 | - 127.0.0.1:9933 117 | - 127.0.0.1:9934 118 | - name: cluster_bb # name of the cluster 119 | resolver: dns # use dns resolver 120 | lb_policy: random # load balancing policy, currently supported: round_robin, random 121 | config: # cluster specific configuration 122 | host: foo.svc.bar 123 | port: 8500 124 | ``` 125 | 126 | 127 | ## Plugin Development 128 | 129 | Penguin's plugin system is designed to be highly extensible and customizable. You can easily add new plugins or modify existing ones to suit your needs. 130 | 131 | To develop a new plugin, you need to implement the `Plugin` trait and register it in the plugin registry. Here's a simple example of how to create a custom plugin: 132 | 133 | ```rust 134 | use std::{collections::HashMap, sync::Arc}; 135 | use async_trait::async_trait; 136 | // and more others ... 137 | use crate::{ 138 | core::plugin::{Plugin, PluginCtx}, 139 | plugins::{PluginResult,errors::*}, 140 | }; 141 | 142 | pub const ECHO_PLUGIN_NAME: &str = "echo"; 143 | 144 | #[derive(Clone)] 145 | pub struct EchoPlugin { 146 | config: Arc, 147 | } 148 | 149 | #[derive(Debug, Deserialize)] 150 | struct EchoConfig { 151 | body: Bytes, 152 | status_code: StatusCode, 153 | headers: Option>, 154 | } 155 | 156 | // constructor of the plugin 157 | pub fn create_echo_plugin(cfg: Option) -> PluginResult> { 158 | // unmarshal config from yaml 159 | let config: EchoConfig = serde_yaml::from_value(cfg)?; 160 | // ... 161 | 162 | // create plugin instance 163 | Ok(Box::new(EchoPlugin { config: Arc::new(config) })) 164 | } 165 | 166 | #[async_trait] 167 | impl Plugin for EchoPlugin { 168 | async fn request_filter(&self, session: &mut Session, _ctx: &mut PluginCtx) -> Result { 169 | // your logic here 170 | // you can store your state in _ctx 171 | // you can use session to write response directly and return Ok(true) to stop proxying 172 | // return Ok(false) to continue proxying 173 | Ok(false) 174 | } 175 | } 176 | ``` 177 | 178 | Then you need to register the plugin in the plugin registry `src/plugins/mod.rs`: 179 | 180 | ```rust 181 | static PLUGIN_BUILDER_REGISTRY: Lazy> = Lazy::new(|| { 182 | let arr: Vec<(&str, PluginInitFn)> = vec![ 183 | (echo::ECHO_PLUGIN_NAME, Arc::new(echo::create_echo_plugin)), 184 | (cms_rate::CMS_RATE_PLUGIN_NAME, Arc::new(cms_rate::create_cms_rate_limiter)), 185 | // (your_plugin_name, Arc::new(your_plugin_create_func), 186 | // and more others ... 187 | ]; 188 | arr.into_iter().collect() 189 | }); 190 | ``` 191 | 192 | That's it! Your plugin is now registered and can be used in the configuration file. ✿✿ヽ(°▽°)ノ✿ 193 | 194 | examples: 195 | - [cms_rate](./src/plugins/cms_rate/mod.rs) 196 | - [echo](./src/plugins/echo/mod.rs) 197 | 198 | ### Plugin trait 199 | 200 | Plugin trait is a subset of Pingora's ProxyHttp trait, you can refer to Pingora's [official documentation](https://github.com/cloudflare/pingora/blob/main/docs/user_guide/phase.md) for more information. 201 | 202 | ```rust 203 | /// Main trait for plugins, defining various filter methods 204 | #[async_trait] 205 | pub trait Plugin: Send + Sync { 206 | /// Handle the incoming request. 207 | /// 208 | /// In this phase, users can parse, validate, rate limit, perform access control and/or 209 | /// return a response for this request. 210 | /// 211 | /// # Arguments 212 | /// 213 | /// * `_session` - Mutable reference to the current session 214 | /// * `_ctx` - Mutable reference to the plugin context 215 | /// 216 | /// # Returns 217 | /// 218 | /// * `Ok(true)` if a response was sent and the proxy should exit 219 | /// * `Ok(false)` if the proxy should continue to the next phase 220 | async fn request_filter(&self, _session: &mut Session, _ctx: &mut PluginCtx) -> Result { 221 | Ok(false) 222 | } 223 | 224 | /// Handle the incoming request body. 225 | /// 226 | /// This function will be called every time a piece of request body is received. 227 | /// 228 | /// # Arguments 229 | /// 230 | /// * `_session` - Mutable reference to the current session 231 | /// * `_body` - Mutable reference to an optional Bytes containing the body chunk 232 | /// * `_end_of_stream` - Boolean indicating if this is the last chunk 233 | /// * `_ctx` - Mutable reference to the plugin context 234 | async fn request_body_filter( 235 | &self, 236 | _session: &mut Session, 237 | _body: &mut Option, 238 | _end_of_stream: bool, 239 | _ctx: &mut PluginCtx, 240 | ) -> Result<()> { 241 | Ok(()) 242 | } 243 | 244 | /// Modify the request before it is sent to the upstream 245 | /// 246 | /// # Arguments 247 | /// 248 | /// * `_session` - Mutable reference to the current session 249 | /// * `_upstream_request` - Mutable reference to the upstream request header 250 | /// * `_ctx` - Mutable reference to the plugin context 251 | async fn upstream_request_filter( 252 | &self, 253 | _session: &mut Session, 254 | _upstream_request: &mut RequestHeader, 255 | _ctx: &mut PluginCtx, 256 | ) -> Result<()> { 257 | Ok(()) 258 | } 259 | 260 | /// Modify the response header before it is sent to the downstream 261 | /// 262 | /// # Arguments 263 | /// 264 | /// * `_session` - Mutable reference to the current session 265 | /// * `_upstream_response` - Mutable reference to the upstream response header 266 | /// * `_ctx` - Mutable reference to the plugin context 267 | async fn response_filter( 268 | &self, 269 | _session: &mut Session, 270 | _upstream_response: &mut ResponseHeader, 271 | _ctx: &mut PluginCtx, 272 | ) -> Result<()> { 273 | Ok(()) 274 | } 275 | 276 | /// Handle the response body chunks 277 | /// 278 | /// # Arguments 279 | /// 280 | /// * `_session` - Mutable reference to the current session 281 | /// * `_body` - Mutable reference to an optional Bytes containing the body chunk 282 | /// * `_end_of_stream` - Boolean indicating if this is the last chunk 283 | /// * `_ctx` - Mutable reference to the plugin context 284 | fn response_body_filter( 285 | &self, 286 | _session: &mut Session, 287 | _body: &mut Option, 288 | _end_of_stream: bool, 289 | _ctx: &mut PluginCtx, 290 | ) -> Result<()> { 291 | Ok(()) 292 | } 293 | } 294 | ``` 295 | 296 | ## Benchmark 297 | 298 | Test Penguin 299 | ```bash 300 | Running 30s test @ http://penguin:8080/foo/bar 301 | 2 threads and 400 connections 302 | Thread Stats Avg Stdev Max +/- Stdev 303 | Latency 13.00ms 13.87ms 66.50ms 80.90% 304 | Req/Sec 23.74k 2.16k 27.68k 69.67% 305 | 1416929 requests in 30.04s, 186.48MB read 306 | Requests/sec: 47175.79 307 | Transfer/sec: 6.21MB 308 | ``` 309 | 310 | Test Nginx 311 | ```bash 312 | Running 30s test @ http://nginx:80/foo/bar 313 | 2 threads and 400 connections 314 | Thread Stats Avg Stdev Max +/- Stdev 315 | Latency 19.60ms 85.42ms 1.61s 97.40% 316 | Req/Sec 24.87k 0.94k 26.70k 72.50% 317 | 1484571 requests in 30.04s, 227.94MB read 318 | Requests/sec: 49414.34 319 | Transfer/sec: 7.59MB 320 | ``` 321 | Penguin is just a little bit slower than Nginx, but it's still very fast and we have a lot of room to improve. 322 | 323 | See benchmark directory for more details. 324 | 325 | ## TODO 326 | 327 | - resolver: 328 | - [ ] polarismesh 329 | - [ ] consul 330 | - [ ] k8s 331 | - [ ] nacos 332 | - lb_policy: 333 | - [ ] least_conn 334 | - plugin: 335 | - [ ] cors 336 | - [ ] fault injection 337 | - [ ] better rate limiter 338 | - auth system 339 | - better error handling 340 | - nginx like logging 341 | - self monitoring 342 | 343 | 344 | ## Contributing 345 | 346 | We welcome contributions to Penguin! If you have any ideas, suggestions, or bug reports, please feel free to open an issue or submit a pull request. 347 | -------------------------------------------------------------------------------- /benchmark/apisix_conf/apisix.yaml: -------------------------------------------------------------------------------- 1 | routes: 2 | - uri: /foo/bar 3 | plugins: 4 | mocking: 5 | delay: 0 6 | content_type: "text/plain" 7 | response_status: 200 8 | response_example: "hello,world!" 9 | #END -------------------------------------------------------------------------------- /benchmark/apisix_conf/config.yaml: -------------------------------------------------------------------------------- 1 | apisix: 2 | node_listen: 3 | - port: 9080 4 | nginx_config: 5 | error_log_level: warn 6 | http: 7 | enable_access_log: false 8 | 9 | deployment: 10 | role: data_plane 11 | role_data_plane: 12 | config_provider: yaml 13 | -------------------------------------------------------------------------------- /benchmark/docker-compose-apisix.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | apisix: 5 | image: apache/apisix:3.10.0-debian 6 | volumes: 7 | - ./apisix_conf/config.yaml:/usr/local/apisix/conf/config.yaml 8 | - ./apisix_conf/apisix.yaml:/usr/local/apisix/conf/apisix.yaml 9 | environment: 10 | - APISIX_STAND_ALONE=true 11 | expose: 12 | - 9080 13 | user: "${UID}:${GID}" 14 | 15 | wrk: 16 | image: williamyeh/wrk 17 | command: -t2 -c400 -d5m http://apisix:9080/foo/bar 18 | depends_on: 19 | - apisix -------------------------------------------------------------------------------- /benchmark/docker-compose-nginx.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | nginx: 5 | build: ./nginx 6 | expose: 7 | - "80" 8 | 9 | wrk: 10 | image: williamyeh/wrk 11 | command: -t2 -c400 -d30s http://nginx:80/foo/bar 12 | depends_on: 13 | - nginx -------------------------------------------------------------------------------- /benchmark/docker-compose-penguin.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | penguin: 5 | build: 6 | dockerfile: ./benchmark/penguin/Dockerfile 7 | context: .. 8 | expose: 9 | - "8080" 10 | 11 | wrk: 12 | image: williamyeh/wrk 13 | command: -t2 -c400 -d30s http://penguin:8080/foo/bar 14 | depends_on: 15 | - penguin 16 | 17 | apisix: 18 | image: apache/apisix:3.2.1-debian 19 | volumes: 20 | - ./apisix_conf/config.yaml:/usr/local/apisix/conf/config.yaml:ro 21 | - ./apisix_conf/apisix.yaml:/usr/local/apisix/conf/apisix.yaml:ro 22 | environment: 23 | - APISIX_STAND_ALONE=true 24 | ports: 25 | - "9080:9080" 26 | 27 | wrk-apisix: 28 | image: williamyeh/wrk 29 | command: -t2 -c400 -d30s http://apisix:9080/hello 30 | depends_on: 31 | - apisix -------------------------------------------------------------------------------- /benchmark/nginx/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM nginx:alpine 2 | COPY nginx.conf /etc/nginx/nginx.conf -------------------------------------------------------------------------------- /benchmark/nginx/nginx.conf: -------------------------------------------------------------------------------- 1 | events { 2 | worker_connections 1024; 3 | } 4 | 5 | http { 6 | access_log off; # 关闭access log 7 | 8 | server { 9 | listen 80; 10 | location /foo/bar { 11 | return 200 "Hello, World!"; 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /benchmark/penguin/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:24.10 2 | ENV RUST_LOG=error 3 | COPY ./target/release/penguin /usr/local/bin/penguin 4 | COPY ./benchmark/penguin/gateway.yaml /etc/penguin/gateway.yaml 5 | CMD ["penguin", "-c", "/etc/penguin/gateway.yaml", "run"] -------------------------------------------------------------------------------- /benchmark/penguin/gateway.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | - name: service1 3 | listeners: 4 | - name: listener1 5 | address: 0.0.0.0:8080 6 | protocol: http 7 | routes: 8 | - name: public_route 9 | match: 10 | uri: 11 | exact: /foo/bar 12 | plugins: 13 | - name: echo 14 | config: 15 | body: "hello, world" 16 | status_code: 200 17 | cluster: public_backend_cluster 18 | clusters: 19 | - name: public_backend_cluster 20 | resolver: static 21 | lb_policy: round_robin 22 | config: 23 | endpoints: 24 | - 127.0.0.1:9933 25 | - 127.0.0.1:9934 -------------------------------------------------------------------------------- /gateway.yaml: -------------------------------------------------------------------------------- 1 | global_plugins: 2 | - name: cms_rate # count-min-sketch based rate limiter 3 | config: 4 | overall: 10 5 | interval: 1m 6 | 7 | 8 | services: 9 | - name: service1 10 | listeners: 11 | - name: listener1 12 | address: 0.0.0.0:8080 13 | protocol: http 14 | routes: 15 | - name: public_route 16 | match: 17 | uri: 18 | exact: /foo/bar 19 | plugins: 20 | - name: cms_rate 21 | config: 22 | count: 3 23 | - name: echo 24 | config: 25 | body: "hello, world" 26 | status_code: 200 27 | cluster: public_backend_cluster 28 | - name: qq 29 | match: 30 | uri: 31 | exact: "/param" 32 | cluster: public_backend_cluster 33 | 34 | clusters: 35 | - name: public_backend_cluster 36 | resolver: static 37 | lb_policy: round_robin 38 | config: 39 | endpoints: 40 | - 127.0.0.1:9933 41 | - 127.0.0.1:9934 -------------------------------------------------------------------------------- /src/builder/errors.rs: -------------------------------------------------------------------------------- 1 | use matchit::InsertError; 2 | use snafu::Snafu; 3 | 4 | use crate::plugins::errors::PluginError; 5 | 6 | #[derive(Debug, Snafu)] 7 | #[snafu(visibility(pub))] 8 | pub enum BuilderError { 9 | #[snafu(display("Lack config for plugin: {}", name))] 10 | LackConfig { name: String }, 11 | #[snafu(display("Failed to build plugin: {}, error: {:?}", name, source))] 12 | PluginBuild { source: PluginError, name: String }, 13 | #[snafu(display("Lack uri for route: {}", name))] 14 | LackUri { name: String }, 15 | #[snafu(display("Failed to compile regex: {}, error: {:?}", re, source))] 16 | Regexp { source: regex::Error, re: String }, 17 | #[snafu(display("Failed to insert route: {}, error: {:?}", path, source))] 18 | InsertRoute { source: InsertError, path: String }, 19 | } 20 | -------------------------------------------------------------------------------- /src/builder/mod.rs: -------------------------------------------------------------------------------- 1 | use regex::Regex; 2 | use snafu::ResultExt; 3 | use std::collections::HashMap; 4 | use std::sync::Arc; 5 | 6 | use crate::{ 7 | clusters::{discovery::ResolverWrapper, Resolver}, 8 | config::def::{DiscoveryProvider, Plugin, ResolverType, Route, StrMatch}, 9 | core::plugin::Plugin as PluginTrait, 10 | plugins::create_plugin_builder, 11 | proxy::process::{MatchEntry, Pipeline}, 12 | }; 13 | use errors::*; 14 | 15 | pub mod errors; 16 | 17 | pub type BuilderResult = Result; 18 | 19 | pub fn init_discovery_providers( 20 | cfg: &[DiscoveryProvider], 21 | ) -> BuilderResult>> { 22 | let mut providers: HashMap> = HashMap::new(); 23 | for provider in cfg { 24 | if provider.resolver_type == ResolverType::DNS { 25 | let resolver = ResolverWrapper::new(); 26 | providers.insert(ResolverType::DNS, Arc::new(resolver)); 27 | } 28 | } 29 | Ok(providers) 30 | } 31 | 32 | pub fn init_routes(cfg: Vec) -> BuilderResult { 33 | let mut matcher = MatchEntry::new(); 34 | for one_route in cfg { 35 | // build plugins 36 | let ppl = build_pipleline(one_route.plugins, &one_route.cluster)?; 37 | 38 | // build matcher 39 | if let Some(uri) = one_route.matcher.uri { 40 | match uri { 41 | StrMatch::Regexp(re) => { 42 | let re = Regex::new(&re).context(RegexpSnafu { re })?; 43 | matcher.add_regex_route(re, ppl); 44 | } 45 | StrMatch::Prefix(prefix) => { 46 | matcher 47 | .insert_route(revise_prefix(&prefix).as_str(), ppl) 48 | .context(InsertRouteSnafu { path: prefix })?; 49 | } 50 | StrMatch::Exact(exact) => { 51 | matcher 52 | .insert_route(&exact, ppl) 53 | .context(InsertRouteSnafu { path: exact })?; 54 | } 55 | } 56 | } else { 57 | unimplemented!("uri is required for now"); 58 | } 59 | } 60 | Ok(matcher) 61 | } 62 | 63 | /// revise prefix to be a valid path for matchit 64 | /// if it suffix of *, remove * and append {*rest} 65 | /// else append {*rest} 66 | fn revise_prefix(prefix: &str) -> String { 67 | if prefix.ends_with("*") { 68 | prefix.to_string().replace("*", r"{*rest}") 69 | } else { 70 | format!("{}{}", prefix, r"{*rest}") 71 | } 72 | } 73 | 74 | fn build_pipleline(cfg: Option>, cluster: &str) -> BuilderResult> { 75 | let plugin_builder = build_plugin_list(cfg)?; 76 | Ok(Arc::new(Pipeline::new( 77 | Arc::new(plugin_builder), 78 | cluster.to_string(), 79 | ))) 80 | } 81 | 82 | pub fn build_plugin_list(cfg: Option>) -> BuilderResult>> { 83 | let mut plugin_builder = vec![]; 84 | if let Some(plugins) = cfg { 85 | for pl in plugins { 86 | let builder = create_plugin_builder(&pl.name, pl.config) 87 | .context(PluginBuildSnafu { name: pl.name })?; 88 | plugin_builder.push(builder); 89 | } 90 | } 91 | Ok(plugin_builder) 92 | } 93 | -------------------------------------------------------------------------------- /src/clusters/discovery/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::clusters::{errors::*, ClusterResult, Resolver}; 2 | use async_trait::async_trait; 3 | use hickory_resolver::TokioAsyncResolver; 4 | use once_cell::sync::OnceCell; 5 | use pingora::lb::{discovery::ServiceDiscovery, Backend}; 6 | use pingora::prelude::*; 7 | use pingora::protocols::l4::socket::SocketAddr as PingoraSocketAddr; 8 | use serde::Deserialize; 9 | use serde_yaml::Value as YamlValue; 10 | use snafu::ResultExt; 11 | use std::collections::{BTreeSet, HashMap}; 12 | use std::net::{IpAddr, SocketAddr as StdSocketAddr}; 13 | use std::sync::Arc; 14 | use std::vec::IntoIter; 15 | 16 | static GLOBAL_RESOLVER: OnceCell> = OnceCell::new(); 17 | 18 | fn get_global_resolver() -> Arc { 19 | GLOBAL_RESOLVER 20 | .get_or_init(|| Arc::new(TokioAsyncResolver::tokio_from_system_conf().unwrap())) 21 | .clone() 22 | } 23 | 24 | pub struct ResolverWrapper { 25 | resolver: Arc, 26 | } 27 | 28 | impl ResolverWrapper { 29 | pub fn new() -> Self { 30 | Self { 31 | resolver: get_global_resolver(), 32 | } 33 | } 34 | } 35 | 36 | impl Default for ResolverWrapper { 37 | fn default() -> Self { 38 | Self::new() 39 | } 40 | } 41 | 42 | #[async_trait] 43 | impl Resolver for ResolverWrapper { 44 | async fn lookup_ip(&self, name: &str) -> ClusterResult> { 45 | Ok(self 46 | .resolver 47 | .lookup_ip(name) 48 | .await 49 | .context(ResolveIpSnafu { name })? 50 | .iter() 51 | .collect()) 52 | } 53 | } 54 | 55 | pub struct DnsDiscovery { 56 | resolver: Arc, 57 | name: String, 58 | port: u16, 59 | } 60 | 61 | impl DnsDiscovery { 62 | pub fn new(name: String, port: u16, resolver: Arc) -> Self { 63 | Self { 64 | resolver, 65 | name, 66 | port, 67 | } 68 | } 69 | } 70 | 71 | #[async_trait] 72 | impl ServiceDiscovery for DnsDiscovery { 73 | async fn discover(&self) -> Result<(BTreeSet, HashMap)> { 74 | let backends = self 75 | .resolver 76 | .lookup_ip(self.name.as_str()) 77 | .await 78 | .unwrap() 79 | .iter() 80 | .map(|ip| Backend { 81 | addr: PingoraSocketAddr::Inet(StdSocketAddr::new(*ip, self.port)), 82 | weight: 1, 83 | ext: http::Extensions::new(), 84 | }) 85 | .collect(); 86 | Ok((backends, HashMap::new())) 87 | } 88 | } 89 | 90 | #[derive(Debug, Deserialize)] 91 | struct StaticConfig { 92 | endpoints: Vec, 93 | } 94 | 95 | pub struct StaticDiscovery { 96 | pub backends: Vec, 97 | } 98 | 99 | impl StaticDiscovery { 100 | pub fn new(cfg: Option) -> ClusterResult { 101 | let cfg = cfg.ok_or(ClusterError::LackConfig { 102 | name: "static".to_string(), 103 | })?; 104 | let config: StaticConfig = 105 | serde_yaml::from_value(cfg).context(StaticConfigSnafu { name: "static" })?; 106 | Ok(Self { 107 | backends: config.endpoints, 108 | }) 109 | } 110 | } 111 | 112 | impl IntoIterator for StaticDiscovery { 113 | type Item = StdSocketAddr; 114 | type IntoIter = IntoIter; 115 | 116 | fn into_iter(self) -> Self::IntoIter { 117 | self.backends.into_iter() 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/clusters/errors.rs: -------------------------------------------------------------------------------- 1 | use crate::config::def::ResolverType; 2 | use hickory_resolver::error::ResolveError; 3 | use serde_yaml::Error as YamlError; 4 | use snafu::Snafu; 5 | 6 | #[derive(Debug, Snafu)] 7 | #[snafu(visibility(pub))] 8 | pub enum ClusterError { 9 | #[snafu(display("Invalid static config for {}, reason: {}", name, source))] 10 | StaticConfig { source: YamlError, name: String }, 11 | #[snafu(display("Lack config for {}", name))] 12 | LackConfig { name: String }, 13 | #[snafu(display("Failed to build discovery for {} due to {}", name, source))] 14 | DiscoveryConfig { source: YamlError, name: String }, 15 | #[snafu(display("Invalid port {}", port))] 16 | InvalidPort { port: String }, 17 | #[snafu(display("Invalid endpoints {}", ep))] 18 | InvalidEndpoints { ep: String }, 19 | #[snafu(display("Unknown resolver type {:?}", resolver))] 20 | UnknownResolver { resolver: ResolverType }, 21 | #[snafu(display("Failed to resolve ip for {}", name))] 22 | ResolveIp { source: ResolveError, name: String }, 23 | } 24 | -------------------------------------------------------------------------------- /src/clusters/mod.rs: -------------------------------------------------------------------------------- 1 | use std::{collections::HashMap, net::IpAddr, sync::Arc}; 2 | 3 | use crate::{ 4 | clusters::{ 5 | discovery::{DnsDiscovery, StaticDiscovery}, 6 | errors::*, 7 | }, 8 | config::def::{Cluster as ClusterConfig, ResolverType}, 9 | core::lb::LB, 10 | }; 11 | use async_trait::async_trait; 12 | use pingora::lb::{selection::Random, Backends, LoadBalancer}; 13 | use serde::Deserialize; 14 | use snafu::ResultExt; 15 | 16 | pub mod discovery; 17 | pub mod errors; 18 | 19 | pub type ClusterResult = Result; 20 | 21 | #[async_trait] 22 | pub trait Resolver: Send + Sync { 23 | async fn lookup_ip(&self, name: &str) -> ClusterResult>; 24 | } 25 | 26 | pub struct ClusterManager { 27 | clusters: HashMap>, 28 | } 29 | 30 | impl ClusterManager { 31 | pub fn new( 32 | cfgs: Vec, 33 | resolvers: &HashMap>, 34 | ) -> ClusterResult { 35 | let mut clusters: HashMap> = HashMap::new(); 36 | for cfg in cfgs { 37 | match cfg.resolver { 38 | ResolverType::DNS => { 39 | let resolver = resolvers.get(&ResolverType::DNS).cloned().ok_or( 40 | ClusterError::UnknownResolver { 41 | resolver: cfg.resolver, 42 | }, 43 | )?; 44 | let c: DNSConfig = serde_yaml::from_value(cfg.config.unwrap()).context( 45 | errors::DiscoveryConfigSnafu { 46 | name: cfg.name.clone(), 47 | }, 48 | )?; 49 | let discovery = DnsDiscovery::new(c.host, c.port, resolver); 50 | let backends = Backends::new(Box::new(discovery)); 51 | let lb = LoadBalancer::::from_backends(backends); 52 | clusters.insert(cfg.name, Arc::new(lb)); 53 | } 54 | ResolverType::Static => { 55 | let discovery = StaticDiscovery::new(cfg.config)?; 56 | let lb = LoadBalancer::::try_from_iter(discovery).unwrap(); 57 | clusters.insert(cfg.name, Arc::new(lb)); 58 | } 59 | } 60 | } 61 | Ok(Self { clusters }) 62 | } 63 | pub fn get_cluster(&self, name: &str) -> Option> { 64 | self.clusters.get(name).cloned() 65 | } 66 | } 67 | 68 | #[derive(Debug, Deserialize)] 69 | struct DNSConfig { 70 | pub host: String, 71 | pub port: u16, 72 | } 73 | -------------------------------------------------------------------------------- /src/config/args.rs: -------------------------------------------------------------------------------- 1 | use clap::{Parser, Subcommand}; 2 | use std::path::PathBuf; 3 | 4 | #[derive(Parser, Debug)] 5 | #[command(author, version, about, long_about = None)] 6 | pub struct Args { 7 | #[arg(short, long, value_name = "FILE", default_value = "gateway.yaml")] 8 | pub config: PathBuf, 9 | 10 | #[command(subcommand)] 11 | pub command: Command, 12 | } 13 | 14 | #[derive(Subcommand, Debug)] 15 | pub enum Command { 16 | #[command(name = "validate", about = "Validate the configuration file")] 17 | ValidateConfig, 18 | #[command(name = "run", about = "Run the server")] 19 | Run, 20 | } 21 | -------------------------------------------------------------------------------- /src/config/def.rs: -------------------------------------------------------------------------------- 1 | use std::{collections::HashMap, net::SocketAddr, time::Duration}; 2 | 3 | use pingora::server::configuration::ServerConf; 4 | use serde::{Deserialize, Serialize}; 5 | use serde_yaml::Value as YamlValue; 6 | use validator::{Validate, ValidationError}; 7 | 8 | #[derive(Debug, Serialize, Deserialize, Validate)] 9 | pub struct Config { 10 | #[serde(default)] 11 | pub identities: Vec, 12 | #[validate(length(min = 1))] 13 | #[validate(nested)] 14 | pub services: Vec, 15 | #[serde(rename = "resolvers", default)] 16 | pub discovery_providers: Vec, 17 | } 18 | 19 | #[derive(Debug, Serialize, Deserialize)] 20 | pub struct Identity { 21 | pub name: String, 22 | pub basic_auth: Option, 23 | pub hmac_auth: Option, 24 | pub jwt_auth: Option, 25 | } 26 | 27 | #[derive(Debug, Serialize, Deserialize)] 28 | pub struct BasicAuth { 29 | pub username: String, 30 | pub password: String, 31 | } 32 | 33 | #[derive(Debug, Serialize, Deserialize)] 34 | pub struct HmacAuth { 35 | pub access_key: String, 36 | pub secret_key: String, 37 | } 38 | 39 | #[derive(Debug, Serialize, Deserialize)] 40 | pub struct JwtAuth { 41 | pub issuer: String, 42 | pub secret: String, 43 | } 44 | 45 | #[derive(Debug, Serialize, Deserialize, Validate)] 46 | pub struct Service { 47 | pub name: String, 48 | pub server_conf: Option, 49 | #[validate(length(min = 1))] 50 | #[validate(nested)] 51 | pub listeners: Vec, 52 | pub plugins: Option>, 53 | #[validate(length(min = 1))] 54 | pub routes: Vec, 55 | pub clusters: Vec, 56 | } 57 | 58 | #[derive(Debug, Serialize, Deserialize, Validate)] 59 | #[validate(schema(function = "validate_listener"))] 60 | pub struct Listener { 61 | pub name: String, 62 | pub address: SocketAddr, 63 | #[serde(default)] 64 | pub protocol: Protocol, 65 | pub ssl_config: Option, 66 | } 67 | 68 | #[derive(Debug, Serialize, Deserialize, Default)] 69 | pub enum Protocol { 70 | #[default] 71 | HTTP, 72 | HTTPS, 73 | } 74 | 75 | #[derive(Debug, Serialize, Deserialize)] 76 | pub struct SslConfig { 77 | #[serde(rename = "cert")] 78 | pub cert_path: String, 79 | #[serde(rename = "key")] 80 | pub key_path: String, 81 | } 82 | 83 | #[derive(Debug, Serialize, Deserialize)] 84 | pub struct Route { 85 | pub name: String, 86 | #[serde(rename = "match")] 87 | pub matcher: Matcher, 88 | pub auth: Option, 89 | pub plugins: Option>, 90 | pub cluster: String, 91 | } 92 | 93 | #[derive(Debug, Serialize, Deserialize)] 94 | pub struct Matcher { 95 | pub uri: Option, 96 | pub headers: Option>, 97 | } 98 | 99 | #[derive(Debug, Serialize, Deserialize)] 100 | #[serde(rename_all = "lowercase")] 101 | pub enum StrMatch { 102 | Regexp(String), 103 | Prefix(String), 104 | Exact(String), 105 | } 106 | 107 | #[derive(Debug, Serialize, Deserialize)] 108 | pub struct Auth { 109 | #[serde(rename = "type")] 110 | pub auth_type: String, 111 | pub allowed_identities: Option>, 112 | pub config: Option, 113 | } 114 | 115 | #[derive(Debug, Serialize, Deserialize)] 116 | pub struct ForwardAuthConfig { 117 | pub cluster: String, 118 | pub path: String, 119 | pub headers_to_forward: Vec, 120 | } 121 | 122 | #[derive(Debug, Serialize, Deserialize)] 123 | pub struct Plugin { 124 | pub name: String, 125 | pub config: Option, 126 | } 127 | 128 | #[derive(Debug, Serialize, Deserialize)] 129 | pub struct Cluster { 130 | pub name: String, 131 | pub resolver: ResolverType, 132 | pub lb_policy: LbPolicy, 133 | pub config: Option, 134 | pub health_checks: Option>, 135 | } 136 | 137 | #[derive(Debug, Serialize, Deserialize, PartialEq)] 138 | #[serde(rename_all = "snake_case")] 139 | pub enum ClusterType { 140 | StrictDns, 141 | Static, 142 | #[serde(other)] 143 | Unsupported, 144 | } 145 | 146 | #[derive(Debug, Serialize, Deserialize, PartialEq)] 147 | #[serde(rename_all = "snake_case")] 148 | pub enum LbPolicy { 149 | RoundRobin, 150 | LeastConn, 151 | Random, 152 | #[serde(other)] 153 | Unsupported, 154 | } 155 | 156 | #[derive(Debug, Serialize, Deserialize)] 157 | pub struct HealthCheck { 158 | #[serde(with = "humantime_serde")] 159 | pub timeout: Duration, 160 | #[serde(with = "humantime_serde")] 161 | pub interval: Duration, 162 | pub unhealthy_threshold: u32, 163 | pub healthy_threshold: u32, 164 | } 165 | 166 | #[derive(Debug, Serialize, Deserialize)] 167 | pub struct DiscoveryProvider { 168 | pub name: String, 169 | #[serde(rename = "type")] 170 | pub resolver_type: ResolverType, 171 | pub config: Option, 172 | } 173 | 174 | #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Hash, Eq)] 175 | #[serde(rename_all = "lowercase")] 176 | pub enum ResolverType { 177 | DNS, 178 | Static, 179 | } 180 | 181 | fn validate_listener(listener: &Listener) -> Result<(), ValidationError> { 182 | if matches!(listener.protocol, Protocol::HTTPS) && listener.ssl_config.is_none() { 183 | return Err(ValidationError::new( 184 | "ssl_config is required for HTTPS listener", 185 | )); 186 | } 187 | Ok(()) 188 | } 189 | -------------------------------------------------------------------------------- /src/config/errors.rs: -------------------------------------------------------------------------------- 1 | use config::ConfigError as ExternalConfigError; 2 | use snafu::Snafu; 3 | 4 | #[derive(Debug, Snafu)] 5 | #[snafu(visibility(pub))] 6 | pub enum ConfigError { 7 | #[snafu(display("Failed to load config: {}", source))] 8 | Config { 9 | file_name: String, 10 | source: ExternalConfigError, 11 | }, 12 | } 13 | -------------------------------------------------------------------------------- /src/config/mod.rs: -------------------------------------------------------------------------------- 1 | use config::{Config, File, FileFormat}; 2 | use errors::*; 3 | use snafu::ResultExt; 4 | 5 | pub mod args; 6 | pub mod def; 7 | pub mod errors; 8 | 9 | pub fn load_config(file_name: &str) -> Result { 10 | let settings = Config::builder() 11 | .add_source(File::new(file_name, FileFormat::Yaml)) 12 | .build() 13 | .context(ConfigSnafu { 14 | file_name: file_name.to_string(), 15 | })?; 16 | settings.try_deserialize().context(ConfigSnafu { 17 | file_name: file_name.to_string(), 18 | }) 19 | } 20 | -------------------------------------------------------------------------------- /src/core/lb.rs: -------------------------------------------------------------------------------- 1 | use pingora::{ 2 | http::RequestHeader, 3 | lb::{ 4 | selection::{BackendIter, BackendSelection}, 5 | Backend, LoadBalancer, 6 | }, 7 | }; 8 | 9 | /// Trait defining the interface for load balancers 10 | /// 11 | /// This trait should be implemented by types that provide load balancing functionality. 12 | pub trait LB: Send + Sync { 13 | /// Selects a backend based on the given request header 14 | /// 15 | /// # Arguments 16 | /// 17 | /// * `header` - The request header to use for backend selection 18 | /// 19 | /// # Returns 20 | /// 21 | /// An `Option` representing the selected backend, or `None` if no backend is available 22 | fn select_backend(&self, header: &RequestHeader) -> Option; 23 | } 24 | 25 | /// Implementation of the `LB` trait for `LoadBalancer` 26 | /// 27 | /// This implementation allows the Pingora `LoadBalancer` to be used with our `LB` trait. 28 | impl LB for LoadBalancer 29 | where 30 | S: BackendSelection + Send + Sync + 'static, 31 | S::Iter: BackendIter, 32 | { 33 | /// Selects a backend using the Pingora `LoadBalancer` 34 | /// 35 | /// This implementation ignores the request header and uses a default key and TTL. 36 | /// 37 | /// # Arguments 38 | /// 39 | /// * `_header` - The request header (ignored in this implementation) 40 | /// 41 | /// # Returns 42 | /// 43 | /// An `Option` representing the selected backend, or `None` if no backend is available 44 | fn select_backend(&self, _header: &RequestHeader) -> Option { 45 | self.select(b"", 256) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/core/mod.rs: -------------------------------------------------------------------------------- 1 | /// Module for load balancing related functionality 2 | pub mod lb; 3 | 4 | /// Module for plugin system implementation 5 | pub mod plugin; 6 | -------------------------------------------------------------------------------- /src/core/plugin.rs: -------------------------------------------------------------------------------- 1 | use async_trait::async_trait; 2 | use bytes::Bytes; 3 | use matchit::Params; 4 | use pingora::{http::ResponseHeader, prelude::*}; 5 | use regex::Captures; 6 | 7 | /// Context for plugin execution 8 | #[derive(Default)] 9 | pub struct PluginCtx { 10 | pub route_params: Option, 11 | } 12 | 13 | /// Main trait for plugins, defining various filter methods 14 | #[async_trait] 15 | pub trait Plugin: Send + Sync { 16 | /// Handle the incoming request. 17 | /// 18 | /// In this phase, users can parse, validate, rate limit, perform access control and/or 19 | /// return a response for this request. 20 | /// 21 | /// # Arguments 22 | /// 23 | /// * `_session` - Mutable reference to the current session 24 | /// * `_ctx` - Mutable reference to the plugin context 25 | /// 26 | /// # Returns 27 | /// 28 | /// * `Ok(true)` if a response was sent and the proxy should exit 29 | /// * `Ok(false)` if the proxy should continue to the next phase 30 | async fn request_filter(&self, _session: &mut Session, _ctx: &mut PluginCtx) -> Result { 31 | Ok(false) 32 | } 33 | 34 | /// Handle the incoming request body. 35 | /// 36 | /// This function will be called every time a piece of request body is received. 37 | /// 38 | /// # Arguments 39 | /// 40 | /// * `_session` - Mutable reference to the current session 41 | /// * `_body` - Mutable reference to an optional Bytes containing the body chunk 42 | /// * `_end_of_stream` - Boolean indicating if this is the last chunk 43 | /// * `_ctx` - Mutable reference to the plugin context 44 | async fn request_body_filter( 45 | &self, 46 | _session: &mut Session, 47 | _body: &mut Option, 48 | _end_of_stream: bool, 49 | _ctx: &mut PluginCtx, 50 | ) -> Result<()> { 51 | Ok(()) 52 | } 53 | 54 | /// Modify the request before it is sent to the upstream 55 | /// 56 | /// # Arguments 57 | /// 58 | /// * `_session` - Mutable reference to the current session 59 | /// * `_upstream_request` - Mutable reference to the upstream request header 60 | /// * `_ctx` - Mutable reference to the plugin context 61 | async fn upstream_request_filter( 62 | &self, 63 | _session: &mut Session, 64 | _upstream_request: &mut RequestHeader, 65 | _ctx: &mut PluginCtx, 66 | ) -> Result<()> { 67 | Ok(()) 68 | } 69 | 70 | /// Modify the response header before it is sent to the downstream 71 | /// 72 | /// # Arguments 73 | /// 74 | /// * `_session` - Mutable reference to the current session 75 | /// * `_upstream_response` - Mutable reference to the upstream response header 76 | /// * `_ctx` - Mutable reference to the plugin context 77 | async fn response_filter( 78 | &self, 79 | _session: &mut Session, 80 | _upstream_response: &mut ResponseHeader, 81 | _ctx: &mut PluginCtx, 82 | ) -> Result<()> { 83 | Ok(()) 84 | } 85 | 86 | /// Handle the response body chunks 87 | /// 88 | /// # Arguments 89 | /// 90 | /// * `_session` - Mutable reference to the current session 91 | /// * `_body` - Mutable reference to an optional Bytes containing the body chunk 92 | /// * `_end_of_stream` - Boolean indicating if this is the last chunk 93 | /// * `_ctx` - Mutable reference to the plugin context 94 | fn response_body_filter( 95 | &self, 96 | _session: &mut Session, 97 | _body: &mut Option, 98 | _end_of_stream: bool, 99 | _ctx: &mut PluginCtx, 100 | ) -> Result<()> { 101 | Ok(()) 102 | } 103 | } 104 | 105 | /// Represents the parameters extracted from a route match 106 | /// 107 | /// This struct encapsulates the parameters that are extracted when a route is matched, 108 | /// either by a regex pattern or a path matching algorithm. It provides a unified 109 | /// interface to access these parameters, regardless of their source. 110 | #[derive(Debug, Default)] 111 | pub struct RouteParams { 112 | /// A vector of strings containing the captured parameters 113 | /// 114 | /// For regex matches, this includes all captured groups. 115 | /// For path matches, this includes all path segments that were matched as parameters. 116 | params: Vec, 117 | } 118 | 119 | impl RouteParams { 120 | pub fn new_caps(caps: &Captures) -> Self { 121 | Self { 122 | params: caps 123 | .iter() 124 | .filter_map(|s| s.map(|ss| ss.as_str().to_string())) 125 | .collect(), 126 | } 127 | } 128 | 129 | pub fn new_params(params: &Params) -> Self { 130 | Self { 131 | params: params.iter().map(|(_, v)| v.to_string()).collect(), 132 | } 133 | } 134 | 135 | pub fn get(&self, idx: usize) -> Option<&str> { 136 | self.params.get(idx).map(|s| s.as_str()) 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /src/errors/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | builder::errors::BuilderError, clusters::errors::ClusterError, config::errors::ConfigError, 3 | }; 4 | use pingora::BError; 5 | use snafu::Snafu; 6 | use validator::ValidationErrors; 7 | 8 | #[derive(Debug, Snafu)] 9 | #[snafu(visibility(pub))] 10 | pub enum AppError { 11 | #[snafu(display("Cluster error: {}", source))] 12 | Cluster { source: ClusterError }, 13 | #[snafu(display("Config error: {}", source))] 14 | Config { source: ConfigError }, 15 | #[snafu(display("Builder error: {}", source))] 16 | Builder { source: BuilderError }, 17 | #[snafu(display("Pingora error: {}", source))] 18 | Pingora { source: BError }, 19 | #[snafu(display("Validation error: {}", source))] 20 | Validation { source: ValidationErrors }, 21 | } 22 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod builder; 2 | pub mod clusters; 3 | pub mod config; 4 | pub mod core; 5 | pub mod errors; 6 | pub mod plugins; 7 | pub mod proxy; 8 | pub mod utils; 9 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use std::{path::PathBuf, sync::Arc}; 2 | 3 | use clap::Parser; 4 | use penguin::{ 5 | builder::{build_plugin_list, init_discovery_providers, init_routes}, 6 | clusters::ClusterManager, 7 | config::{ 8 | args::{Args, Command}, 9 | def::{Config, Listener, Service as ServiceConf}, 10 | load_config, 11 | }, 12 | errors::*, 13 | proxy::Proxy, 14 | }; 15 | use pingora::{ 16 | prelude::*, proxy::http_proxy_service_with_name, server::configuration::ServerConf, 17 | services::Service as PingoraServiceTrait, 18 | }; 19 | use snafu::ResultExt; 20 | use validator::Validate; 21 | 22 | fn main() -> Result<(), AppError> { 23 | let args = Args::parse(); 24 | let buf_writer = std::io::BufWriter::new(std::io::stdout()); 25 | env_logger::Builder::from_default_env() 26 | .format_timestamp(None) 27 | .target(env_logger::Target::Pipe(Box::new(buf_writer))) 28 | .init(); 29 | match args.command { 30 | Command::ValidateConfig => { 31 | load_and_validate_config(args.config)?; 32 | Ok(()) 33 | } 34 | Command::Run => { 35 | let config = load_and_validate_config(args.config)?; 36 | // init discovery providers 37 | let resolvers = init_discovery_providers(&config.discovery_providers).unwrap(); 38 | 39 | // init pingora server 40 | let mut server = Server::new(None).unwrap(); 41 | 42 | // for each service in config, init its routes, clusters 43 | // combine them into a Proxy object(which is an implementation of Pingora ProxyHttp Trait) 44 | // create a pingora service based on the Proxy object 45 | // add the service to the pingora server 46 | let mut svcs = vec![]; 47 | for ServiceConf { 48 | name, 49 | server_conf, 50 | plugins, 51 | listeners, 52 | routes, 53 | clusters, 54 | } in config.services 55 | { 56 | let routes = init_routes(routes).context(BuilderSnafu)?; 57 | let clusters = ClusterManager::new(clusters, &resolvers).context(ClusterSnafu)?; 58 | let global_plugins = build_plugin_list(plugins).context(BuilderSnafu)?; 59 | let proxy = Proxy::new(routes, clusters, global_plugins); 60 | let svc = 61 | create_service(name, server_conf, listeners, proxy).context(PingoraSnafu)?; 62 | svcs.push(svc); 63 | } 64 | server.add_services(svcs); 65 | // run the server 66 | server.run_forever(); 67 | } 68 | } 69 | } 70 | 71 | fn create_service( 72 | name: String, 73 | server_conf: Option, 74 | listeners: Vec, 75 | proxy: Proxy, 76 | ) -> Result> { 77 | let mut svc = 78 | http_proxy_service_with_name(&Arc::new(server_conf.unwrap_or_default()), proxy, &name); 79 | for listener in listeners { 80 | let addr = listener.address.to_string(); 81 | match listener.ssl_config { 82 | Some(ssl_config) => { 83 | svc.add_tls(&addr, &ssl_config.cert_path, &ssl_config.key_path)?; 84 | } 85 | None => { 86 | svc.add_tcp(&addr); 87 | } 88 | } 89 | } 90 | Ok(Box::new(svc)) 91 | } 92 | 93 | fn load_and_validate_config(path: PathBuf) -> Result { 94 | let config = load_config(path.as_path().to_str().unwrap()).context(ConfigSnafu)?; 95 | config.validate().context(ValidationSnafu)?; 96 | Ok(config) 97 | } 98 | -------------------------------------------------------------------------------- /src/plugins/cms_rate/mod.rs: -------------------------------------------------------------------------------- 1 | use std::{sync::Arc, time::Duration}; 2 | 3 | use async_trait::async_trait; 4 | use http::StatusCode; 5 | use pingora::prelude::*; 6 | use pingora_limits::rate::Rate; 7 | use serde::{Deserialize, Serialize}; 8 | use serde_yaml::Value as YamlValue; 9 | use snafu::ResultExt; 10 | use validator::{Validate, ValidationError}; 11 | 12 | use crate::{ 13 | core::plugin::{Plugin, PluginCtx}, 14 | plugins::{errors::*, PluginResult}, 15 | utils::send_response, 16 | }; 17 | 18 | pub const CMS_RATE_PLUGIN_NAME: &str = "cms_rate"; 19 | 20 | #[derive(Debug, Serialize, Deserialize, Validate)] 21 | pub struct CmsRateConf { 22 | #[validate(range(min = 1))] 23 | pub total: isize, 24 | #[serde(with = "humantime_serde")] 25 | #[validate(custom(function = "atleast_1_second"))] 26 | pub interval: Duration, 27 | } 28 | 29 | fn atleast_1_second(v: &Duration) -> Result<(), ValidationError> { 30 | if v.as_secs() == 0 { 31 | return Err(ValidationError::new("interval must be at least 1 second")); 32 | } 33 | Ok(()) 34 | } 35 | 36 | pub fn create_cms_rate_limiter(config: Option) -> PluginResult> { 37 | let config = config.ok_or(PluginError::LackPluginConfig { 38 | name: CMS_RATE_PLUGIN_NAME.to_string(), 39 | })?; 40 | let cfg: CmsRateConf = serde_yaml::from_value(config).context(YamlErrSnafu { 41 | name: CMS_RATE_PLUGIN_NAME.to_string(), 42 | })?; 43 | cfg.validate().context(ValidateErrSnafu { 44 | name: CMS_RATE_PLUGIN_NAME.to_string(), 45 | })?; 46 | let r = Arc::new(Rate::new(cfg.interval)); 47 | Ok(Box::new(PerRoutePlugin { 48 | total: cfg.total, 49 | r, 50 | })) 51 | } 52 | 53 | #[derive(Clone)] 54 | pub struct PerRoutePlugin { 55 | total: isize, 56 | r: Arc, 57 | } 58 | 59 | #[async_trait] 60 | impl Plugin for PerRoutePlugin { 61 | async fn request_filter(&self, session: &mut Session, _ctx: &mut PluginCtx) -> Result { 62 | let path = session.req_header().uri.path(); 63 | let nc = self.r.observe(&path, 1); 64 | if nc > self.total { 65 | send_response(session, StatusCode::TOO_MANY_REQUESTS, None, None, None).await?; 66 | return Ok(true); 67 | } 68 | Ok(false) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/plugins/echo/mod.rs: -------------------------------------------------------------------------------- 1 | use std::{collections::HashMap, sync::Arc}; 2 | 3 | use async_trait::async_trait; 4 | use bytes::Bytes; 5 | use http::StatusCode; 6 | use pingora::prelude::*; 7 | use serde::Deserialize; 8 | use serde_yaml::Value as YamlValue; 9 | use snafu::prelude::*; 10 | 11 | use crate::{ 12 | core::plugin::{Plugin, PluginCtx}, 13 | plugins::{errors::*, PluginResult}, 14 | utils::send_response, 15 | }; 16 | 17 | pub const ECHO_PLUGIN_NAME: &str = "echo"; 18 | 19 | pub fn create_echo_plugin(cfg: Option) -> PluginResult> { 20 | let cfg = cfg.ok_or(PluginError::LackPluginConfig { 21 | name: ECHO_PLUGIN_NAME.to_string(), 22 | })?; 23 | let mut config: EchoConfigRaw = serde_yaml::from_value(cfg).context(YamlErrSnafu { 24 | name: ECHO_PLUGIN_NAME.to_string(), 25 | })?; 26 | // 把config.headers中的key转换为小写 27 | if let Some(headers) = config.headers.as_mut() { 28 | *headers = std::mem::take(headers) 29 | .into_iter() 30 | .map(|(k, v)| (k.to_lowercase(), v)) 31 | .collect(); 32 | } 33 | let config = EchoConfig { 34 | body: Bytes::from(config.body), 35 | status_code: StatusCode::from_u16(config.status_code.unwrap_or(200)) 36 | .map_err(|e| e.into()) 37 | .context(SpecificErrSnafu { 38 | name: ECHO_PLUGIN_NAME.to_string(), 39 | })?, 40 | headers: config.headers, 41 | }; 42 | Ok(Box::new(EchoPlugin { 43 | config: Arc::new(config), 44 | })) 45 | } 46 | 47 | #[derive(Clone)] 48 | pub struct EchoPlugin { 49 | config: Arc, 50 | } 51 | 52 | #[derive(Debug)] 53 | struct EchoConfig { 54 | body: Bytes, 55 | status_code: StatusCode, 56 | headers: Option>, 57 | } 58 | 59 | #[derive(Debug, Deserialize)] 60 | struct EchoConfigRaw { 61 | body: String, 62 | status_code: Option, 63 | headers: Option>, 64 | } 65 | 66 | #[async_trait] 67 | impl Plugin for EchoPlugin { 68 | async fn request_filter(&self, session: &mut Session, _ctx: &mut PluginCtx) -> Result { 69 | send_response( 70 | session, 71 | self.config.status_code, 72 | None, 73 | Some(self.config.body.clone()), 74 | self.config.headers.clone(), 75 | ) 76 | .await?; 77 | Ok(true) 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/plugins/errors.rs: -------------------------------------------------------------------------------- 1 | use serde_yaml::Error as YamlError; 2 | use snafu::prelude::*; 3 | use validator::ValidationErrors; 4 | 5 | #[derive(Debug, Snafu)] 6 | #[snafu(visibility(pub))] 7 | pub enum PluginError { 8 | #[snafu(display("Unknown plugin: {}", name))] 9 | UnknownPlugin { name: String }, 10 | #[snafu(display("Failed to validate plugin: {}, error: {:?}", name, source))] 11 | ValidateErr { 12 | source: ValidationErrors, 13 | name: String, 14 | }, 15 | #[snafu(display("Failed to parse plugin config: {}, error: {:?}", name, source))] 16 | YamlErr { source: YamlError, name: String }, 17 | #[snafu(display("Lack plugin config: {}", name))] 18 | LackPluginConfig { name: String }, 19 | #[snafu(display("Specific error: {}, name: {}", source, name))] 20 | SpecificErr { 21 | source: Box, 22 | name: String, 23 | }, 24 | } 25 | -------------------------------------------------------------------------------- /src/plugins/mod.rs: -------------------------------------------------------------------------------- 1 | use std::{collections::HashMap, sync::Arc}; 2 | 3 | use crate::core::plugin::Plugin; 4 | use once_cell::sync::Lazy; 5 | use serde_yaml::Value as YamlValue; 6 | 7 | pub mod cms_rate; 8 | pub mod echo; 9 | pub mod errors; 10 | 11 | use errors::*; 12 | 13 | pub type PluginResult = std::result::Result; 14 | 15 | /// Type alias for plugin initialization functions 16 | pub type PluginInitFn = 17 | Arc) -> PluginResult> + Send + Sync>; 18 | 19 | /// Registry of plugin builders 20 | static PLUGIN_BUILDER_REGISTRY: Lazy> = Lazy::new(|| { 21 | let arr: Vec<(&str, PluginInitFn)> = vec![ 22 | (echo::ECHO_PLUGIN_NAME, Arc::new(echo::create_echo_plugin)), 23 | ( 24 | cms_rate::CMS_RATE_PLUGIN_NAME, 25 | Arc::new(cms_rate::create_cms_rate_limiter), 26 | ), 27 | ]; 28 | arr.into_iter().collect() 29 | }); 30 | 31 | pub fn create_plugin_builder(name: &str, cfg: Option) -> PluginResult> { 32 | let builder = PLUGIN_BUILDER_REGISTRY 33 | .get(name) 34 | .ok_or(PluginError::UnknownPlugin { 35 | name: name.to_string(), 36 | })?; 37 | builder(cfg) 38 | } 39 | -------------------------------------------------------------------------------- /src/proxy/errors.rs: -------------------------------------------------------------------------------- 1 | use snafu::Snafu; 2 | 3 | #[derive(Debug, Snafu)] 4 | #[snafu(visibility(pub))] 5 | pub enum ProxyErr { 6 | #[snafu(display("Failed to build matcher: {}", source))] 7 | BuildMatcher { source: Box }, 8 | } 9 | -------------------------------------------------------------------------------- /src/proxy/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod errors; 2 | pub mod process; 3 | 4 | pub type ProxyResult = Result; 5 | pub use process::*; 6 | -------------------------------------------------------------------------------- /src/proxy/process.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Cow; 2 | use std::{sync::Arc, time::Duration}; 3 | 4 | use async_trait::async_trait; 5 | use bytes::Bytes; 6 | use http::StatusCode; 7 | use log::{error, info, log_enabled, Level}; 8 | use matchit::{InsertError, Router}; 9 | use once_cell::sync::Lazy; 10 | use pingora::{http::ResponseHeader, prelude::*, proxy::ProxyHttp}; 11 | use regex::Regex; 12 | 13 | use crate::{ 14 | clusters::ClusterManager, 15 | core::plugin::{Plugin, PluginCtx, RouteParams}, 16 | utils::send_response, 17 | }; 18 | 19 | /// Represents the main proxy structure 20 | pub struct Proxy { 21 | plugins: Vec>, 22 | /// Router for matching requests to pipelines 23 | matcher: MatchEntry, 24 | /// Manager for handling clusters of backends 25 | cluster_manager: ClusterManager, 26 | } 27 | 28 | impl Proxy { 29 | /// Creates a new Proxy instance 30 | /// 31 | /// # Arguments 32 | /// 33 | /// * `matcher` - The MatchEntry for routing requests 34 | /// * `cluster_manager` - The ClusterManager for handling backend clusters 35 | pub fn new( 36 | matcher: MatchEntry, 37 | cluster_manager: ClusterManager, 38 | plugins: Vec>, 39 | ) -> Self { 40 | Self { 41 | matcher, 42 | cluster_manager, 43 | plugins, 44 | } 45 | } 46 | } 47 | 48 | static NOT_FOUND: Lazy = Lazy::new(|| Bytes::from("not found")); 49 | 50 | /// Context for the proxy, holding plugins and other request-specific data 51 | #[derive(Default)] 52 | pub struct ProxyCtx { 53 | /// List of plugins to be applied 54 | plugins: Arc>>, 55 | /// The selected cluster for the request 56 | cluster: Option, 57 | /// Context for plugin execution 58 | plugin_ctx: PluginCtx, 59 | } 60 | 61 | #[async_trait] 62 | impl ProxyHttp for Proxy { 63 | type CTX = ProxyCtx; 64 | 65 | /// Creates a new context for each request 66 | fn new_ctx(&self) -> Self::CTX { 67 | Self::CTX::default() 68 | } 69 | 70 | /// Filters incoming requests 71 | /// 72 | /// This method matches the request to a pipeline, initializes plugins, 73 | /// and applies request filters from each plugin. 74 | async fn request_filter(&self, session: &mut Session, ctx: &mut Self::CTX) -> Result 75 | where 76 | Self::CTX: Send + Sync, 77 | { 78 | // global plugins 79 | for plugin in &self.plugins { 80 | let stop = plugin.request_filter(session, &mut ctx.plugin_ctx).await?; 81 | if stop { 82 | return Ok(true); 83 | } 84 | } 85 | 86 | // Match request to pipeline 87 | if let Some((route_params, ppl)) = self.matcher.match_request(session) { 88 | ctx.cluster = Some(ppl.cluster.clone()); 89 | 90 | // Initialize plugins 91 | ctx.plugins = ppl.plugins.clone(); 92 | ctx.plugin_ctx.route_params = Some(route_params); 93 | 94 | // Apply request filters from each plugin 95 | for plugin in ctx.plugins.iter() { 96 | let should_stop = plugin.request_filter(session, &mut ctx.plugin_ctx).await?; 97 | if should_stop { 98 | return Ok(true); 99 | } 100 | } 101 | } else { 102 | send_response( 103 | session, 104 | StatusCode::NOT_FOUND, 105 | None, 106 | Some(NOT_FOUND.clone()), 107 | None, 108 | ) 109 | .await?; 110 | return Ok(true); 111 | } 112 | 113 | Ok(false) 114 | } 115 | 116 | /// Filters the request body 117 | /// 118 | /// Applies request body filters from each plugin. 119 | async fn request_body_filter( 120 | &self, 121 | session: &mut Session, 122 | body: &mut Option, 123 | end_of_stream: bool, 124 | ctx: &mut Self::CTX, 125 | ) -> Result<()> { 126 | // global plugins 127 | for plugin in &self.plugins { 128 | plugin 129 | .request_body_filter(session, body, end_of_stream, &mut ctx.plugin_ctx) 130 | .await?; 131 | } 132 | for plugin in ctx.plugins.iter() { 133 | plugin 134 | .request_body_filter(session, body, end_of_stream, &mut ctx.plugin_ctx) 135 | .await?; 136 | } 137 | Ok(()) 138 | } 139 | 140 | /// Filters the upstream request 141 | /// 142 | /// Applies upstream request filters from each plugin. 143 | async fn upstream_request_filter( 144 | &self, 145 | session: &mut Session, 146 | upstream_request: &mut RequestHeader, 147 | ctx: &mut Self::CTX, 148 | ) -> Result<()> { 149 | // global plugins 150 | for plugin in &self.plugins { 151 | plugin 152 | .upstream_request_filter(session, upstream_request, &mut ctx.plugin_ctx) 153 | .await?; 154 | } 155 | for plugin in ctx.plugins.iter() { 156 | plugin 157 | .upstream_request_filter(session, upstream_request, &mut ctx.plugin_ctx) 158 | .await?; 159 | } 160 | Ok(()) 161 | } 162 | 163 | /// Filters the response 164 | /// 165 | /// Applies response filters from each plugin. 166 | async fn response_filter( 167 | &self, 168 | session: &mut Session, 169 | upstream_response: &mut ResponseHeader, 170 | ctx: &mut Self::CTX, 171 | ) -> Result<()> { 172 | // global plugins 173 | for plugin in self.plugins.iter() { 174 | plugin 175 | .response_filter(session, upstream_response, &mut ctx.plugin_ctx) 176 | .await?; 177 | } 178 | for plugin in ctx.plugins.iter() { 179 | plugin 180 | .response_filter(session, upstream_response, &mut ctx.plugin_ctx) 181 | .await?; 182 | } 183 | Ok(()) 184 | } 185 | 186 | /// Filters the response body 187 | /// 188 | /// Applies response body filters from each plugin. 189 | fn response_body_filter( 190 | &self, 191 | session: &mut Session, 192 | body: &mut Option, 193 | end_of_stream: bool, 194 | ctx: &mut Self::CTX, 195 | ) -> Result> { 196 | // global plugins 197 | for plugin in self.plugins.iter() { 198 | plugin.response_body_filter(session, body, end_of_stream, &mut ctx.plugin_ctx)?; 199 | } 200 | for plugin in ctx.plugins.iter() { 201 | plugin.response_body_filter(session, body, end_of_stream, &mut ctx.plugin_ctx)?; 202 | } 203 | Ok(None) 204 | } 205 | 206 | /// This filter is called when the entire response is sent to the downstream successfully or 207 | /// there is a fatal error that terminate the request. 208 | /// 209 | /// An error log is already emitted if there is any error. This phase is used for collecting 210 | /// metrics and sending access logs. 211 | async fn logging(&self, session: &mut Session, e: Option<&Error>, _ctx: &mut Self::CTX) 212 | where 213 | Self::CTX: Send + Sync, 214 | { 215 | if log_enabled!(Level::Info) { 216 | let req = session.req_header(); 217 | let resp = session.response_written(); 218 | 219 | let status = resp 220 | .map_or(StatusCode::INTERNAL_SERVER_ERROR, |r| r.status) 221 | .as_u16(); 222 | let body_bytes_sent = session.body_bytes_sent(); 223 | let remote_addr = session 224 | .client_addr() 225 | .map_or(Cow::Borrowed("-"), |ip| Cow::Owned(ip.to_string())); 226 | // 使用类似nginx 的格式打日志 227 | info!( 228 | "{} \"{} {}\" {} {}", 229 | remote_addr, req.method, req.uri, status, body_bytes_sent 230 | ); 231 | } 232 | if let Some(e) = e { 233 | error!(error:? = e; "Error occurred"); 234 | } 235 | } 236 | 237 | /// Selects an upstream peer for the request 238 | /// 239 | /// This method selects a backend from the appropriate cluster for the request. 240 | async fn upstream_peer( 241 | &self, 242 | session: &mut Session, 243 | ctx: &mut Self::CTX, 244 | ) -> Result> { 245 | let cluster = ctx 246 | .cluster 247 | .as_ref() 248 | .ok_or(Error::new(ErrorType::Custom("no cluster")))?; 249 | let lb = self 250 | .cluster_manager 251 | .get_cluster(cluster) 252 | .ok_or(Error::new(ErrorType::ConnectNoRoute))?; 253 | let backend = lb.select_backend(session.req_header()).ok_or( 254 | Error::new(ErrorType::Custom("no backend")) 255 | .more_context(format!("cluster: {}", cluster)), 256 | )?; 257 | Ok(Box::new(HttpPeer::new(backend, false, "a.b.c".to_string()))) 258 | } 259 | } 260 | 261 | /// Represents a pipeline of plugins for a specific route 262 | pub struct Pipeline { 263 | /// List of plugin builders for this pipeline 264 | plugins: Arc>>, 265 | /// The cluster associated with this pipeline 266 | cluster: String, 267 | } 268 | 269 | impl Pipeline { 270 | /// Creates a new Pipeline instance 271 | pub fn new(plugins: Arc>>, cluster: String) -> Self { 272 | Self { plugins, cluster } 273 | } 274 | } 275 | 276 | /// Struct for matching requests to pipelines 277 | pub struct MatchEntry { 278 | /// Router for non-regex URI matching 279 | non_reg_uri: Router>, 280 | /// Vector of regex patterns and associated pipelines 281 | regex_uris: Vec<(Regex, Arc)>, 282 | } 283 | 284 | impl MatchEntry { 285 | /// Creates a new MatchEntry instance 286 | pub fn new() -> Self { 287 | Self { 288 | non_reg_uri: Router::new(), 289 | regex_uris: vec![], 290 | } 291 | } 292 | 293 | /// Inserts a new route into the non-regex router 294 | pub fn insert_route(&mut self, path: &str, ppl: Arc) -> Result<(), InsertError> { 295 | if self.non_reg_uri.at(path).is_ok() { 296 | return Ok(()); 297 | } 298 | self.non_reg_uri.insert(path, ppl) 299 | } 300 | 301 | /// Adds a new regex route 302 | pub fn add_regex_route(&mut self, re: Regex, ppl: Arc) { 303 | self.regex_uris.push((re, ppl)); 304 | } 305 | 306 | /// Matches a request to a pipeline 307 | fn match_request(&self, session: &mut Session) -> Option<(RouteParams, Arc)> { 308 | let uri = session.req_header().uri.path(); 309 | if let Ok(ppl) = self.non_reg_uri.at(uri) { 310 | return Some((RouteParams::new_params(&ppl.params), ppl.value.clone())); 311 | } 312 | 313 | for (re, ppl) in self.regex_uris.iter() { 314 | if let Some(caps) = re.captures(uri) { 315 | return Some((RouteParams::new_caps(&caps), ppl.clone())); 316 | } 317 | } 318 | None 319 | } 320 | } 321 | 322 | impl Default for MatchEntry { 323 | fn default() -> Self { 324 | Self::new() 325 | } 326 | } 327 | -------------------------------------------------------------------------------- /src/utils/mod.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use bytes::Bytes; 4 | use http::{header, Response, StatusCode}; 5 | use pingora::{http::ResponseHeader, prelude::*}; 6 | 7 | pub async fn send_response( 8 | session: &mut Session, 9 | status: StatusCode, 10 | content_type: Option<&'static str>, 11 | body: Option, 12 | headers: Option>, 13 | ) -> Result<()> { 14 | let cl = body.as_ref().map(|b| b.len()).unwrap_or(0); 15 | let mut bd = Response::builder() 16 | .status(status) 17 | .header(header::CONTENT_LENGTH, cl); 18 | if let Some(headers) = headers { 19 | for (key, value) in headers { 20 | bd = bd.header(key, value); 21 | } 22 | } 23 | if let Some(content_type) = content_type { 24 | bd = bd.header(header::CONTENT_TYPE, content_type); 25 | } else { 26 | bd = bd.header(header::CONTENT_TYPE, "text/plain"); 27 | } 28 | 29 | if let Some(body) = body { 30 | let resp = bd.body(body).unwrap(); 31 | let (parts, body) = resp.into_parts(); 32 | let resp_header: ResponseHeader = parts.into(); 33 | session 34 | .write_response_header(Box::new(resp_header), false) 35 | .await?; 36 | session.write_response_body(Some(body), true).await 37 | } else { 38 | let resp = bd.body(()).unwrap(); 39 | let (parts, _) = resp.into_parts(); 40 | let resp_header: ResponseHeader = parts.into(); 41 | session 42 | .write_response_header(Box::new(resp_header), false) 43 | .await?; 44 | session.write_response_body(None, true).await 45 | } 46 | } 47 | --------------------------------------------------------------------------------