From f45ed98c143ed264592ea91830ffc369f61a5fcc Mon Sep 17 00:00:00 2001 From: hasturdev Date: Fri, 1 Nov 2024 18:38:30 -0400 Subject: [PATCH 1/6] changed all the examples in pingora_proxy to work on a fresh linux install --- pingora-proxy/examples/ctx.rs | 56 +- pingora-proxy/examples/example_Cargo.lock | 3340 +++++++++++++++++ pingora-proxy/examples/example_Cargo.toml | 27 + pingora-proxy/examples/gateway.rs | 77 +- ...grpc_web_module.rs => grpc_web_modules.rs} | 30 +- ...{load_balancer.rs => load_loadbalancer.rs} | 39 +- pingora-proxy/examples/modify_response.rs | 58 +- pingora-proxy/examples/multi_lb.rs | 44 +- pingora-proxy/examples/rate_limiter.rs | 101 +- pingora-proxy/examples/test_server.rs | 45 + pingora-proxy/examples/use_module.rs | 65 +- 11 files changed, 3608 insertions(+), 274 deletions(-) create mode 100644 pingora-proxy/examples/example_Cargo.lock create mode 100644 pingora-proxy/examples/example_Cargo.toml rename pingora-proxy/examples/{grpc_web_module.rs => grpc_web_modules.rs} (64%) rename pingora-proxy/examples/{load_balancer.rs => load_loadbalancer.rs} (66%) create mode 100644 pingora-proxy/examples/test_server.rs diff --git a/pingora-proxy/examples/ctx.rs b/pingora-proxy/examples/ctx.rs index 4838391e..fd282c92 100644 --- a/pingora-proxy/examples/ctx.rs +++ b/pingora-proxy/examples/ctx.rs @@ -1,55 +1,42 @@ -// Copyright 2024 Cloudflare, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - use async_trait::async_trait; -use clap::Parser; use log::info; -use std::sync::Mutex; +use std::sync::atomic::{AtomicUsize, Ordering}; use pingora_core::server::configuration::Opt; use pingora_core::server::Server; use pingora_core::upstreams::peer::HttpPeer; use pingora_core::Result; +use pingora_http::RequestHeader; use pingora_proxy::{ProxyHttp, Session}; -// global counter -static REQ_COUNTER: Mutex = Mutex::new(0); +/// Global request counter using `AtomicUsize` for thread-safe atomic operations +static REQ_COUNTER: AtomicUsize = AtomicUsize::new(0); pub struct MyProxy { - // counter for the service - beta_counter: Mutex, // AtomicUsize works too + /// Counter for beta users + beta_counter: AtomicUsize, } pub struct MyCtx { beta_user: bool, } -fn check_beta_user(req: &pingora_http::RequestHeader) -> bool { - // some simple logic to check if user is beta +fn check_beta_user(req: &RequestHeader) -> bool { + // Simple logic to check if user is beta req.headers.get("beta-flag").is_some() } #[async_trait] impl ProxyHttp for MyProxy { type CTX = MyCtx; + fn new_ctx(&self) -> Self::CTX { MyCtx { beta_user: false } } async fn request_filter(&self, session: &mut Session, ctx: &mut Self::CTX) -> Result { ctx.beta_user = check_beta_user(session.req_header()); - Ok(false) + Ok(false) // Continue processing the request } async fn upstream_peer( @@ -57,39 +44,44 @@ impl ProxyHttp for MyProxy { _session: &mut Session, ctx: &mut Self::CTX, ) -> Result> { - let mut req_counter = REQ_COUNTER.lock().unwrap(); - *req_counter += 1; + // Increment the global request counter atomically + let req_counter = REQ_COUNTER.fetch_add(1, Ordering::Relaxed) + 1; let addr = if ctx.beta_user { - let mut beta_count = self.beta_counter.lock().unwrap(); - *beta_count += 1; + // Increment the beta user counter atomically + let beta_count = self.beta_counter.fetch_add(1, Ordering::Relaxed) + 1; info!("I'm a beta user #{beta_count}"); ("1.0.0.1", 443) } else { - info!("I'm an user #{req_counter}"); + info!("I'm a user #{req_counter}"); ("1.1.1.1", 443) }; - let peer = Box::new(HttpPeer::new(addr, true, "one.one.one.one".to_string())); + let peer = Box::new(HttpPeer::new( + addr, + true, + "one.one.one.one".to_string(), + )); Ok(peer) } } +// To run the example: // RUST_LOG=INFO cargo run --example ctx // curl 127.0.0.1:6190 -H "Host: one.one.one.one" // curl 127.0.0.1:6190 -H "Host: one.one.one.one" -H "beta-flag: 1" fn main() { env_logger::init(); - // read command line arguments - let opt = Opt::parse(); + // Read command line arguments + let opt = Opt::parse_args(); let mut my_server = Server::new(Some(opt)).unwrap(); my_server.bootstrap(); let mut my_proxy = pingora_proxy::http_proxy_service( &my_server.configuration, MyProxy { - beta_counter: Mutex::new(0), + beta_counter: AtomicUsize::new(0), }, ); my_proxy.add_tcp("0.0.0.0:6190"); diff --git a/pingora-proxy/examples/example_Cargo.lock b/pingora-proxy/examples/example_Cargo.lock new file mode 100644 index 00000000..5ed728cd --- /dev/null +++ b/pingora-proxy/examples/example_Cargo.lock @@ -0,0 +1,3340 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "actix-codec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f7b0a21988c1bf877cf4759ef5ddaac04c1c9fe808c9142ecb78ba97d97a28a" +dependencies = [ + "bitflags 2.6.0", + "bytes", + "futures-core", + "futures-sink", + "memchr", + "pin-project-lite", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "actix-http" +version = "3.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d48f96fc3003717aeb9856ca3d02a8c7de502667ad76eeacd830b48d2e91fac4" +dependencies = [ + "actix-codec", + "actix-rt", + "actix-service", + "actix-utils", + "ahash", + "base64 0.22.1", + "bitflags 2.6.0", + "brotli 6.0.0", + "bytes", + "bytestring", + "derive_more", + "encoding_rs", + "flate2", + "futures-core", + "h2 0.3.26", + "http 0.2.12", + "httparse", + "httpdate", + "itoa", + "language-tags", + "local-channel", + "mime", + "percent-encoding", + "pin-project-lite", + "rand", + "sha1", + "smallvec", + "tokio", + "tokio-util", + "tracing", + "zstd", +] + +[[package]] +name = "actix-macros" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb" +dependencies = [ + "quote", + "syn 2.0.86", +] + +[[package]] +name = "actix-router" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13d324164c51f63867b57e73ba5936ea151b8a41a1d23d1031eeb9f70d0236f8" +dependencies = [ + "bytestring", + "cfg-if", + "http 0.2.12", + "regex", + "regex-lite", + "serde", + "tracing", +] + +[[package]] +name = "actix-rt" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24eda4e2a6e042aa4e55ac438a2ae052d3b5da0ecf83d7411e1a368946925208" +dependencies = [ + "futures-core", + "tokio", +] + +[[package]] +name = "actix-server" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ca2549781d8dd6d75c40cf6b6051260a2cc2f3c62343d761a969a0640646894" +dependencies = [ + "actix-rt", + "actix-service", + "actix-utils", + "futures-core", + "futures-util", + "mio", + "socket2", + "tokio", + "tracing", +] + +[[package]] +name = "actix-service" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b894941f818cfdc7ccc4b9e60fa7e53b5042a2e8567270f9147d5591893373a" +dependencies = [ + "futures-core", + "paste", + "pin-project-lite", +] + +[[package]] +name = "actix-utils" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88a1dcdff1466e3c2488e1cb5c36a71822750ad43839937f85d2f4d9f8b705d8" +dependencies = [ + "local-waker", + "pin-project-lite", +] + +[[package]] +name = "actix-web" +version = "4.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9180d76e5cc7ccbc4d60a506f2c727730b154010262df5b910eb17dbe4b8cb38" +dependencies = [ + "actix-codec", + "actix-http", + "actix-macros", + "actix-router", + "actix-rt", + "actix-server", + "actix-service", + "actix-utils", + "actix-web-codegen", + "ahash", + "bytes", + "bytestring", + "cfg-if", + "cookie", + "derive_more", + "encoding_rs", + "futures-core", + "futures-util", + "impl-more", + "itoa", + "language-tags", + "log", + "mime", + "once_cell", + "pin-project-lite", + "regex", + "regex-lite", + "serde", + "serde_json", + "serde_urlencoded", + "smallvec", + "socket2", + "time", + "url", +] + +[[package]] +name = "actix-web-codegen" +version = "4.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f591380e2e68490b5dfaf1dd1aa0ebe78d84ba7067078512b4ea6e4492d622b8" +dependencies = [ + "actix-router", + "proc-macro2", + "quote", + "syn 2.0.86", +] + +[[package]] +name = "addr2line" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "getrandom", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "alloc-no-stdlib" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" + +[[package]] +name = "alloc-stdlib" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" +dependencies = [ + "alloc-no-stdlib", +] + +[[package]] +name = "allocator-api2" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" + +[[package]] +name = "anstream" +version = "0.6.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23a1e53f0f5d86382dafe1cf314783b2044280f406e7e1506368220ad11b1338" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" + +[[package]] +name = "anstyle-parse" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" +dependencies = [ + "anstyle", + "windows-sys 0.59.0", +] + +[[package]] +name = "arc-swap" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "async-stream" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.86", +] + +[[package]] +name = "async-trait" +version = "0.1.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.86", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi 0.1.19", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "backtrace" +version = "0.3.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets 0.52.6", +] + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "brotli" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d640d25bc63c50fb1f0b545ffd80207d2e10a4c965530809b40ba3386825c391" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor 2.5.1", +] + +[[package]] +name = "brotli" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74f7971dbd9326d58187408ab83117d8ac1bb9c17b085fdacd1cf2f598719b6b" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor 4.0.1", +] + +[[package]] +name = "brotli-decompressor" +version = "2.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e2e4afe60d7dd600fdd3de8d0f08c2b7ec039712e3b6137ff98b7004e82de4f" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + +[[package]] +name = "brotli-decompressor" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a45bd2e4095a8b518033b128020dd4a55aab1c0a381ba4404a472630f4bc362" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da" + +[[package]] +name = "bytestring" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74d80203ea6b29df88012294f62733de21cfeab47f17b41af3a38bc30a03ee72" +dependencies = [ + "bytes", +] + +[[package]] +name = "cc" +version = "1.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b16803a61b81d9eabb7eae2588776c4c1e584b738ede45fdbb4c972cec1e9945" +dependencies = [ + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +dependencies = [ + "num-traits", +] + +[[package]] +name = "clap" +version = "3.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" +dependencies = [ + "atty", + "bitflags 1.3.2", + "clap_derive", + "clap_lex 0.2.4", + "indexmap 1.9.3", + "once_cell", + "strsim 0.10.0", + "termcolor", + "textwrap", +] + +[[package]] +name = "clap" +version = "4.5.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.5.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54" +dependencies = [ + "anstream", + "anstyle", + "clap_lex 0.7.2", + "strsim 0.11.1", +] + +[[package]] +name = "clap_derive" +version = "3.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae6371b8bdc8b7d3959e9cf7b22d4435ef3e79e138688421ec654acf8c81b008" +dependencies = [ + "heck 0.4.1", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "clap_lex" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" +dependencies = [ + "os_str_bytes", +] + +[[package]] +name = "clap_lex" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" + +[[package]] +name = "cmake" +version = "0.1.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb1e43aa7fd152b1f968787f7dbcdeb306d1867ff373c69955211876c053f91a" +dependencies = [ + "cc", +] + +[[package]] +name = "colorchoice" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "cookie" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb" +dependencies = [ + "percent-encoding", + "time", + "version_check", +] + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cpufeatures" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-queue" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "daemonize" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab8bfdaacb3c887a54d41bdf48d3af8873b3f5566469f8ba21b92057509f116e" +dependencies = [ + "libc", +] + +[[package]] +name = "data-encoding" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" + +[[package]] +name = "debugid" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d" +dependencies = [ + "serde", + "uuid", +] + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_more" +version = "0.99.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn 2.0.86", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", + "subtle", +] + +[[package]] +name = "encoding_rs" +version = "0.8.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "env_filter" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "humantime", + "log", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "flate2" +version = "1.0.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0" +dependencies = [ + "crc32fast", + "libz-ng-sys", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foldhash" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.86", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + +[[package]] +name = "h2" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http 0.2.12", + "indexmap 2.6.0", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "h2" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http 1.1.0", + "indexmap 2.6.0", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hostname" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" +dependencies = [ + "libc", + "match_cfg", + "winapi", +] + +[[package]] +name = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http 0.2.12", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "hyper" +version = "0.14.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c08302e8fa335b151b788c775ff56e7a03ae64ff85c548ee820fecb70356e85" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2 0.3.26", + "http 0.2.12", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" +dependencies = [ + "futures-util", + "http 0.2.12", + "hyper", + "rustls", + "tokio", + "tokio-rustls", +] + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "impl-more" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aae21c3177a27788957044151cc2800043d127acaa460a47ebb9b84dfa2c6aa0" + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", +] + +[[package]] +name = "indexmap" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" +dependencies = [ + "equivalent", + "hashbrown 0.15.0", +] + +[[package]] +name = "ipnet" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "jobserver" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" +dependencies = [ + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "language-tags" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388" + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.159" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" + +[[package]] +name = "libz-ng-sys" +version = "1.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4436751a01da56f1277f323c80d584ffad94a3d14aecd959dd0dff75aa73a438" +dependencies = [ + "cmake", + "libc", +] + +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + +[[package]] +name = "local-channel" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6cbc85e69b8df4b8bb8b89ec634e7189099cea8927a276b7384ce5488e53ec8" +dependencies = [ + "futures-core", + "futures-sink", + "local-waker", +] + +[[package]] +name = "local-waker" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d873d7c67ce09b42110d801813efbc9364414e356be9935700d368351657487" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "lru" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" +dependencies = [ + "hashbrown 0.15.0", +] + +[[package]] +name = "match_cfg" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "miniz_oxide" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +dependencies = [ + "adler2", +] + +[[package]] +name = "mio" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +dependencies = [ + "hermit-abi 0.3.9", + "libc", + "log", + "wasi", + "windows-sys 0.52.0", +] + +[[package]] +name = "nix" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069" +dependencies = [ + "bitflags 1.3.2", + "cfg-if", + "libc", + "memoffset", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "object" +version = "0.36.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" + +[[package]] +name = "openssl" +version = "0.10.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5" +dependencies = [ + "bitflags 2.6.0", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.86", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-src" +version = "300.3.2+3.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a211a18d945ef7e648cc6e0058f4c548ee46aab922ea203e0d30e966ea23647b" +dependencies = [ + "cc", +] + +[[package]] +name = "openssl-sys" +version = "0.9.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741" +dependencies = [ + "cc", + "libc", + "openssl-src", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "os_str_bytes" +version = "6.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1" + +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.52.6", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pingora" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9144f4950d87291365ca24e41b9a149bd38515d562a7464a6fd27ac12ca0874e" +dependencies = [ + "pingora-cache 0.3.0", + "pingora-core 0.3.0", + "pingora-http 0.3.0", + "pingora-load-balancing 0.3.0", + "pingora-proxy 0.3.0", + "pingora-timeout 0.3.0", +] + +[[package]] +name = "pingora-cache" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbe55bddb694583a9db101e5ae5b31f570f2ccce312ac7d64c2e4a430510c4b3" +dependencies = [ + "ahash", + "async-trait", + "blake2", + "bytes", + "hex", + "http 1.1.0", + "httparse", + "httpdate", + "indexmap 1.9.3", + "log", + "lru", + "once_cell", + "parking_lot", + "pingora-core 0.3.0", + "pingora-error 0.3.0", + "pingora-header-serde 0.3.0", + "pingora-http 0.3.0", + "pingora-lru 0.3.0", + "pingora-timeout 0.3.0", + "regex", + "rmp", + "rmp-serde", + "rustracing", + "rustracing_jaeger", + "serde", + "tokio", +] + +[[package]] +name = "pingora-cache" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35ee62f28526d8d484621e77f8d6a1807f1bd07558a06ab5a204b4834d6be056" +dependencies = [ + "ahash", + "async-trait", + "blake2", + "bytes", + "hex", + "http 1.1.0", + "httparse", + "httpdate", + "indexmap 1.9.3", + "log", + "lru", + "once_cell", + "parking_lot", + "pingora-core 0.4.0", + "pingora-error 0.4.0", + "pingora-header-serde 0.4.0", + "pingora-http 0.4.0", + "pingora-lru 0.4.0", + "pingora-timeout 0.4.0", + "regex", + "rmp", + "rmp-serde", + "rustracing", + "rustracing_jaeger", + "serde", + "strum", + "tokio", +] + +[[package]] +name = "pingora-core" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51dbd9509e3bb25a699bee76ba1befbffb4e733694d7e682d4bfe35a1a48cbb4" +dependencies = [ + "ahash", + "async-trait", + "brotli 3.5.0", + "bytes", + "chrono", + "clap 3.2.25", + "daemonize", + "flate2", + "futures", + "h2 0.4.6", + "http 1.1.0", + "httparse", + "httpdate", + "libc", + "log", + "lru", + "nix", + "once_cell", + "openssl-probe", + "parking_lot", + "percent-encoding", + "pingora-error 0.3.0", + "pingora-http 0.3.0", + "pingora-openssl", + "pingora-pool 0.3.0", + "pingora-runtime 0.3.0", + "pingora-timeout 0.3.0", + "prometheus", + "rand", + "regex", + "sentry", + "serde", + "serde_yaml 0.8.26", + "sfv", + "socket2", + "strum", + "strum_macros", + "thread_local", + "tokio", + "tokio-test", + "unicase", + "zstd", +] + +[[package]] +name = "pingora-core" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d123320b69bd06e897fc16bd1dde962a7b488c4d2ae825683fbca0198fad8669" +dependencies = [ + "ahash", + "async-trait", + "brotli 3.5.0", + "bytes", + "chrono", + "clap 3.2.25", + "daemonize", + "flate2", + "futures", + "h2 0.4.6", + "http 1.1.0", + "httparse", + "httpdate", + "libc", + "log", + "lru", + "nix", + "once_cell", + "openssl-probe", + "parking_lot", + "percent-encoding", + "pingora-error 0.4.0", + "pingora-http 0.4.0", + "pingora-pool 0.4.0", + "pingora-runtime 0.4.0", + "pingora-timeout 0.4.0", + "prometheus", + "rand", + "regex", + "serde", + "serde_yaml 0.8.26", + "sfv", + "socket2", + "strum", + "strum_macros", + "thread_local", + "tokio", + "tokio-test", + "unicase", + "windows-sys 0.59.0", + "zstd", +] + +[[package]] +name = "pingora-error" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e39924926e498ddb0e64a642b6c5df56627afc0989b0f7be197eb096f998f0f" + +[[package]] +name = "pingora-error" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6389511530152c535a554f592ae4a9691b1246cff20eb4564f2a34fc921195c0" + +[[package]] +name = "pingora-header-serde" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37a4c91b93ffaf066443aca3ac669909695b7e93049482bb6c32125ba3366571" +dependencies = [ + "bytes", + "http 1.1.0", + "httparse", + "pingora-error 0.3.0", + "pingora-http 0.3.0", + "thread_local", + "zstd", + "zstd-safe", +] + +[[package]] +name = "pingora-header-serde" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcb3f62d852da015e76ced56e93e6d52941679a9825281c90f2897841129e59d" +dependencies = [ + "bytes", + "http 1.1.0", + "httparse", + "pingora-error 0.4.0", + "pingora-http 0.4.0", + "thread_local", + "zstd", + "zstd-safe", +] + +[[package]] +name = "pingora-http" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88243a433347f328442842d9596dfde5b8b171daaee4a3b16cbe74db033ef4cd" +dependencies = [ + "bytes", + "http 1.1.0", + "pingora-error 0.3.0", +] + +[[package]] +name = "pingora-http" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70202f126056f366549afc804741e12dd9f419cfc79a0063ab15653007a0f4c6" +dependencies = [ + "bytes", + "http 1.1.0", + "pingora-error 0.4.0", +] + +[[package]] +name = "pingora-ketama" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "324f14c8f90a534ce55c11dcbc88428f60a2540265f50b5ee7010561f45dd006" +dependencies = [ + "crc32fast", +] + +[[package]] +name = "pingora-ketama" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c1bb6c2e11823a05ec9140fc8827f112b8380d78b837535f284e0a98f24cc0a" +dependencies = [ + "crc32fast", +] + +[[package]] +name = "pingora-limits" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfcc8e3afeae5a83bbcd415d8d3bb50bea31d2eda2a91f79220b59abab86dd0f" +dependencies = [ + "ahash", +] + +[[package]] +name = "pingora-load-balancing" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d58b52ccfec1b55d8d903d3f78d9bb7f96115fae78065f500cb126758cc85f41" +dependencies = [ + "arc-swap", + "async-trait", + "fnv", + "futures", + "log", + "pingora-core 0.3.0", + "pingora-error 0.3.0", + "pingora-http 0.3.0", + "pingora-ketama 0.3.0", + "pingora-runtime 0.3.0", + "rand", + "tokio", +] + +[[package]] +name = "pingora-load-balancing" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84d558167ecb05cea487a6479700390a67fe414724f203e10c3912584a0f2cb1" +dependencies = [ + "arc-swap", + "async-trait", + "derivative", + "fnv", + "futures", + "http 1.1.0", + "log", + "pingora-core 0.4.0", + "pingora-error 0.4.0", + "pingora-http 0.4.0", + "pingora-ketama 0.4.0", + "pingora-runtime 0.4.0", + "rand", + "tokio", +] + +[[package]] +name = "pingora-lru" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bb8f0df84b4b9afd63742c78e6c4b39413554f857e7d41502825e4ff9798e3e" +dependencies = [ + "arrayvec", + "hashbrown 0.15.0", + "parking_lot", + "rand", +] + +[[package]] +name = "pingora-lru" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb50f65f06c4b81ccb3edcceaa54bb9439608506b0b3b8c048798169a64aad8e" +dependencies = [ + "arrayvec", + "hashbrown 0.15.0", + "parking_lot", + "rand", +] + +[[package]] +name = "pingora-openssl" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bbfc0c9d7f066a6e284dfef736e3bbfee07f1cc6e27c7a6bbd2ea6a45862932" +dependencies = [ + "foreign-types", + "libc", + "openssl", + "openssl-src", + "openssl-sys", + "tokio-openssl", +] + +[[package]] +name = "pingora-pool" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d10a7b0e4115c8098e454d77a96d8ac3d89cc9a95910a6ef10f2656160d463a" +dependencies = [ + "crossbeam-queue", + "log", + "lru", + "parking_lot", + "pingora-timeout 0.3.0", + "thread_local", + "tokio", +] + +[[package]] +name = "pingora-pool" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bacdd5dbdec690d468856d988b170c8bb4ab62e0edefc0f432ba5e326489f421" +dependencies = [ + "crossbeam-queue", + "log", + "lru", + "parking_lot", + "pingora-timeout 0.4.0", + "thread_local", + "tokio", +] + +[[package]] +name = "pingora-proxy" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "333b96aff8d7c30f04f21e3e338ba5854d66df00a7cbe437dbb35a857b84e298" +dependencies = [ + "async-trait", + "bytes", + "clap 3.2.25", + "futures", + "h2 0.4.6", + "http 1.1.0", + "log", + "once_cell", + "pingora-cache 0.3.0", + "pingora-core 0.3.0", + "pingora-error 0.3.0", + "pingora-http 0.3.0", + "pingora-timeout 0.3.0", + "regex", + "tokio", +] + +[[package]] +name = "pingora-proxy" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5031783d6743bd31e4de7d7c7a19e9eecf369174c3cbd8a57eb52bc6bf882d92" +dependencies = [ + "async-trait", + "bytes", + "clap 3.2.25", + "futures", + "h2 0.4.6", + "http 1.1.0", + "log", + "once_cell", + "pingora-cache 0.4.0", + "pingora-core 0.4.0", + "pingora-error 0.4.0", + "pingora-http 0.4.0", + "pingora-timeout 0.4.0", + "regex", + "tokio", +] + +[[package]] +name = "pingora-runtime" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f1c3302dec37b0e8916b0f637852f1e56450b7dafe6042f014343bc3cf12995" +dependencies = [ + "once_cell", + "rand", + "thread_local", + "tokio", +] + +[[package]] +name = "pingora-runtime" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31a7c445ca224630961045684201e3cf8da9af0b01f286ed54ff8b2403aaabff" +dependencies = [ + "once_cell", + "rand", + "thread_local", + "tokio", +] + +[[package]] +name = "pingora-stuff" +version = "0.1.0" +dependencies = [ + "actix-web", + "async-trait", + "bytes", + "clap 4.5.20", + "cmake", + "env_logger", + "log", + "once_cell", + "pingora", + "pingora-core 0.4.0", + "pingora-error 0.4.0", + "pingora-http 0.4.0", + "pingora-limits", + "pingora-load-balancing 0.4.0", + "pingora-proxy 0.4.0", + "prometheus", + "serde", + "serde_json", + "serde_yaml 0.9.34+deprecated", +] + +[[package]] +name = "pingora-timeout" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f54daa3e32391201cfabde4dc1c2ecdfa60b4d6695ef47df56f42c55792ee3" +dependencies = [ + "futures", + "once_cell", + "parking_lot", + "pin-project-lite", + "thread_local", + "tokio", +] + +[[package]] +name = "pingora-timeout" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "685bb8808cc1919c63a06ab14fdac9b84a4887ced49259a5c0adc8bfb2ffe558" +dependencies = [ + "once_cell", + "parking_lot", + "pin-project-lite", + "thread_local", + "tokio", +] + +[[package]] +name = "pkg-config" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.88" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c3a7fc5db1e57d5a779a352c8cdb57b29aa4c40cc69c3a68a7fedc815fbf2f9" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "prometheus" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d33c28a30771f7f96db69893f78b857f7450d7e0237e9c8fc6427a81bae7ed1" +dependencies = [ + "cfg-if", + "fnv", + "lazy_static", + "memchr", + "parking_lot", + "protobuf", + "thiserror", +] + +[[package]] +name = "protobuf" +version = "2.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "106dd99e98437432fed6519dedecfade6a06a73bb7b2a1e019fdd2bee5778d94" + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "redox_syscall" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" +dependencies = [ + "bitflags 2.6.0", +] + +[[package]] +name = "regex" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-lite" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53a49587ad06b26609c52e423de037e7f57f20d53535d66e08c695f347df952a" + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "reqwest" +version = "0.11.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" +dependencies = [ + "base64 0.21.7", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2 0.3.26", + "http 0.2.12", + "http-body", + "hyper", + "hyper-rustls", + "ipnet", + "js-sys", + "log", + "mime", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls", + "rustls-pemfile", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "system-configuration", + "tokio", + "tokio-rustls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "webpki-roots", + "winreg", +] + +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if", + "getrandom", + "libc", + "spin", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rmp" +version = "0.8.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "228ed7c16fa39782c3b3468e974aec2795e9089153cd08ee2e9aefb3613334c4" +dependencies = [ + "byteorder", + "num-traits", + "paste", +] + +[[package]] +name = "rmp-serde" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52e599a477cf9840e92f2cde9a7189e67b42c57532749bf90aea6ec10facd4db" +dependencies = [ + "byteorder", + "rmp", + "serde", +] + +[[package]] +name = "rust_decimal" +version = "1.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b082d80e3e3cc52b2ed634388d436fe1f4de6af5786cc2de9ba9737527bdf555" +dependencies = [ + "arrayvec", + "num-traits", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustls" +version = "0.21.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" +dependencies = [ + "log", + "ring", + "rustls-webpki", + "sct", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +dependencies = [ + "base64 0.21.7", +] + +[[package]] +name = "rustls-webpki" +version = "0.101.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "rustracing" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44822b10c095e574869de2b891e40c724fef42cadaea040d1cd3bdbb13d36a5" +dependencies = [ + "backtrace", + "crossbeam-channel", + "rand", + "trackable 0.2.24", +] + +[[package]] +name = "rustracing_jaeger" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6c2fe9411ef5f43ac773f0e84ad735804c55719346a7aad52de2d9162db97c8" +dependencies = [ + "crossbeam-channel", + "hostname", + "percent-encoding", + "rand", + "rustracing", + "thrift_codec", + "trackable 0.2.24", +] + +[[package]] +name = "rustversion" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sct" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "semver" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" + +[[package]] +name = "sentry" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904eca4fb30c6112a1dae60c0a9e29cfb42f42129da4260f1ee20e94151b62e3" +dependencies = [ + "httpdate", + "reqwest", + "sentry-backtrace", + "sentry-contexts", + "sentry-core", + "sentry-panic", + "tokio", +] + +[[package]] +name = "sentry-backtrace" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1671189d1b759879fa4bdde46c50a499abb14332ed81f84fc6f60658f41b2fdb" +dependencies = [ + "backtrace", + "lazy_static", + "regex", + "sentry-core", +] + +[[package]] +name = "sentry-contexts" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db80ceff16bb1a4b2689b8758e5e61e405fc4d8ff9f2d1b5b845b76ce37fa34e" +dependencies = [ + "hostname", + "libc", + "rustc_version", + "sentry-core", + "uname", +] + +[[package]] +name = "sentry-core" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c9f509d3959ed4dbbd80ca42572caad682aaa1cdd92c719e0815d0e87f82c96" +dependencies = [ + "lazy_static", + "rand", + "sentry-types", + "serde", + "serde_json", +] + +[[package]] +name = "sentry-panic" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8b442769cc34115f64393f7bfe4f863c3c38749e0c0b9613a7ae25b37c7ba53" +dependencies = [ + "sentry-backtrace", + "sentry-core", +] + +[[package]] +name = "sentry-types" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "254b600e93e9ef00a48382c9f1e86d27884bd9a5489efa4eb9210c20c72e88a6" +dependencies = [ + "debugid", + "getrandom", + "hex", + "serde", + "serde_json", + "thiserror", + "time", + "url", + "uuid", +] + +[[package]] +name = "serde" +version = "1.0.214" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.214" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.86", +] + +[[package]] +name = "serde_json" +version = "1.0.132" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_yaml" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578a7433b776b56a35785ed5ce9a7e777ac0598aac5a6dd1b4b18a307c7fc71b" +dependencies = [ + "indexmap 1.9.3", + "ryu", + "serde", + "yaml-rust", +] + +[[package]] +name = "serde_yaml" +version = "0.9.34+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" +dependencies = [ + "indexmap 2.6.0", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + +[[package]] +name = "sfv" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f27daf6ed3fc7ffd5ea3ce9f684fe351c47e50f2fdbb6236e2bad0b440dbe408" +dependencies = [ + "data-encoding", + "indexmap 2.6.0", + "rust_decimal", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "socket2" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "strum" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.86", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89275301d38033efb81a6e60e3497e734dfcc62571f2854bf4b16690398824c" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "textwrap" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9" + +[[package]] +name = "thiserror" +version = "1.0.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.86", +] + +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "thrift_codec" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fb61fb3d0a0af14949f3a6949b2639112e13226647112824f4d081533f9b1a8" +dependencies = [ + "byteorder", + "trackable 0.2.24", +] + +[[package]] +name = "time" +version = "0.3.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinyvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.40.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.52.0", +] + +[[package]] +name = "tokio-macros" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.86", +] + +[[package]] +name = "tokio-openssl" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59df6849caa43bb7567f9a36f863c447d95a11d5903c9cc334ba32576a27eadd" +dependencies = [ + "openssl", + "openssl-sys", + "tokio", +] + +[[package]] +name = "tokio-rustls" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-stream" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f4e6ce100d0eb49a2734f8c0812bcd324cf357d21810932c5df6b96ef2b86f1" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-test" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2468baabc3311435b55dd935f702f42cd1b8abb7e754fb7dfb16bd36aa88f9f7" +dependencies = [ + "async-stream", + "bytes", + "futures-core", + "tokio", + "tokio-stream", +] + +[[package]] +name = "tokio-util" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "log", + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", +] + +[[package]] +name = "trackable" +version = "0.2.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98abb9e7300b9ac902cc04920945a874c1973e08c310627cc4458c04b70dd32" +dependencies = [ + "trackable 1.3.0", + "trackable_derive", +] + +[[package]] +name = "trackable" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15bd114abb99ef8cee977e517c8f37aee63f184f2d08e3e6ceca092373369ae" +dependencies = [ + "trackable_derive", +] + +[[package]] +name = "trackable_derive" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebeb235c5847e2f82cfe0f07eb971d1e5f6804b18dac2ae16349cc604380f82f" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "uname" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b72f89f0ca32e4db1c04e2a72f5345d59796d4866a1ee0609084569f73683dc8" +dependencies = [ + "libc", +] + +[[package]] +name = "unicase" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" +dependencies = [ + "version_check", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" + +[[package]] +name = "unicode-ident" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" + +[[package]] +name = "unicode-normalization" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unsafe-libyaml" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "uuid" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" +dependencies = [ + "getrandom", + "serde", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" +dependencies = [ + "cfg-if", + "once_cell", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.86", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7ec4f8827a71586374db3e87abdb5a2bb3a15afed140221307c3ec06b1f63b" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.86", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" + +[[package]] +name = "web-sys" +version = "0.3.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki-roots" +version = "0.25.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + +[[package]] +name = "yaml-rust" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +dependencies = [ + "linked-hash-map", +] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.86", +] + +[[package]] +name = "zstd" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcf2b778a664581e31e389454a7072dab1647606d44f7feea22cd5abb9c9f3f9" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "7.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54a3ab4db68cea366acc5c897c7b4d4d1b8994a9cd6e6f841f8964566a419059" +dependencies = [ + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.13+zstd.1.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38ff0f21cfee8f97d94cef41359e0c89aa6113028ab0291aa8ca0038995a95aa" +dependencies = [ + "cc", + "pkg-config", +] diff --git a/pingora-proxy/examples/example_Cargo.toml b/pingora-proxy/examples/example_Cargo.toml new file mode 100644 index 00000000..924b74a4 --- /dev/null +++ b/pingora-proxy/examples/example_Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "pingora-stuff" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +actix-web = "4.9.0" +async-trait = "0.1.83" +bytes = "1.8.0" +clap = "4.5.20" +cmake = "0.1.51" +env_logger = "0.11.5" +log = "0.4.22" +once_cell = "1.20.2" +pingora = { version = "0.3.0", features = ["pingora-cache", "pingora-load-balancing", "pingora-proxy", "proxy"] } +pingora-core = "0.4.0" +pingora-error = "0.4.0" +pingora-http = "0.4.0" +pingora-limits = "0.4.0" +pingora-load-balancing = "0.4.0" +pingora-proxy = "0.4.0" +prometheus = "0.13.4" +serde = "1.0.214" +serde_json = "1.0.132" +serde_yaml = "0.9.34" diff --git a/pingora-proxy/examples/gateway.rs b/pingora-proxy/examples/gateway.rs index 78f4aae9..4bbb5c72 100644 --- a/pingora-proxy/examples/gateway.rs +++ b/pingora-proxy/examples/gateway.rs @@ -1,49 +1,38 @@ -// Copyright 2024 Cloudflare, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - use async_trait::async_trait; -use clap::Parser; use log::info; -use prometheus::register_int_counter; +use prometheus::{IntCounter, register_int_counter}; -use pingora_core::server::configuration::Opt; use pingora_core::server::Server; use pingora_core::upstreams::peer::HttpPeer; use pingora_core::Result; -use pingora_http::ResponseHeader; +use pingora_http::{RequestHeader, ResponseHeader}; use pingora_proxy::{ProxyHttp, Session}; -fn check_login(req: &pingora_http::RequestHeader) -> bool { - // implement you logic check logic here - req.headers.get("Authorization").map(|v| v.as_bytes()) == Some(b"password") +fn check_login(req: &RequestHeader) -> bool { + // Implement your login check logic here + req.headers.get("Authorization") + .map(|v| v.as_bytes() == b"password") + .unwrap_or(false) } pub struct MyGateway { - req_metric: prometheus::IntCounter, + req_metric: IntCounter, } #[async_trait] impl ProxyHttp for MyGateway { type CTX = (); - fn new_ctx(&self) -> Self::CTX {} + + fn new_ctx(&self) -> Self::CTX { + () + } async fn request_filter(&self, session: &mut Session, _ctx: &mut Self::CTX) -> Result { if session.req_header().uri.path().starts_with("/login") && !check_login(session.req_header()) { let _ = session.respond_error(403).await; - // true: early return as the response is already written + // Return true to indicate early response return Ok(true); } Ok(false) @@ -60,9 +49,13 @@ impl ProxyHttp for MyGateway { ("1.1.1.1", 443) }; - info!("connecting to {addr:?}"); + info!("Connecting to {:?}", addr); - let peer = Box::new(HttpPeer::new(addr, true, "one.one.one.one".to_string())); + let peer = Box::new(HttpPeer::new( + addr, + true, + "one.one.one.one".to_string(), + )); Ok(peer) } @@ -71,15 +64,12 @@ impl ProxyHttp for MyGateway { _session: &mut Session, upstream_response: &mut ResponseHeader, _ctx: &mut Self::CTX, - ) -> Result<()> - where - Self::CTX: Send + Sync, - { - // replace existing header if any + ) -> Result<()> { + // Replace existing header if any upstream_response .insert_header("Server", "MyGateway") .unwrap(); - // because we don't support h3 + // Remove unsupported header upstream_response.remove_header("alt-svc"); Ok(()) @@ -89,39 +79,34 @@ impl ProxyHttp for MyGateway { &self, session: &mut Session, _e: Option<&pingora_core::Error>, - ctx: &mut Self::CTX, + _ctx: &mut Self::CTX, ) { let response_code = session .response_written() .map_or(0, |resp| resp.status.as_u16()); info!( - "{} response code: {response_code}", - self.request_summary(session, ctx) + "Request to {} responded with status code {}", + session.req_header().uri.path(), + response_code ); self.req_metric.inc(); } } -// RUST_LOG=INFO cargo run --example load_balancer -// curl 127.0.0.1:6191 -H "Host: one.one.one.one" -// curl 127.0.0.1:6190/family/ -H "Host: one.one.one.one" -// curl 127.0.0.1:6191/login/ -H "Host: one.one.one.one" -I -H "Authorization: password" -// curl 127.0.0.1:6191/login/ -H "Host: one.one.one.one" -I -H "Authorization: bad" -// For metrics -// curl 127.0.0.1:6192/ fn main() { env_logger::init(); - // read command line arguments - let opt = Opt::parse(); - let mut my_server = Server::new(Some(opt)).unwrap(); + // Create the server without options + let mut my_server = Server::new(None).unwrap(); my_server.bootstrap(); + let req_metric = register_int_counter!("req_counter", "Number of requests").unwrap(); + let mut my_proxy = pingora_proxy::http_proxy_service( &my_server.configuration, MyGateway { - req_metric: register_int_counter!("req_counter", "Number of requests").unwrap(), + req_metric, }, ); my_proxy.add_tcp("0.0.0.0:6191"); diff --git a/pingora-proxy/examples/grpc_web_module.rs b/pingora-proxy/examples/grpc_web_modules.rs similarity index 64% rename from pingora-proxy/examples/grpc_web_module.rs rename to pingora-proxy/examples/grpc_web_modules.rs index ecd11fd1..e72a6bf2 100644 --- a/pingora-proxy/examples/grpc_web_module.rs +++ b/pingora-proxy/examples/grpc_web_modules.rs @@ -1,19 +1,4 @@ -// Copyright 2024 Cloudflare, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - use async_trait::async_trait; -use clap::Parser; use pingora_core::server::Server; use pingora_core::upstreams::peer::HttpPeer; @@ -23,7 +8,6 @@ use pingora_core::{ grpc_web::{GrpcWeb, GrpcWebBridge}, HttpModules, }, - prelude::Opt, }; use pingora_proxy::{ProxyHttp, Session}; @@ -34,7 +18,10 @@ pub struct GrpcWebBridgeProxy; #[async_trait] impl ProxyHttp for GrpcWebBridgeProxy { type CTX = (); - fn new_ctx(&self) -> Self::CTX {} + + fn new_ctx(&self) -> Self::CTX { + () + } fn init_downstream_modules(&self, modules: &mut HttpModules) { // Add the gRPC web module @@ -51,7 +38,7 @@ impl ProxyHttp for GrpcWebBridgeProxy { .get_mut::() .expect("GrpcWebBridge module added"); - // initialize gRPC module for this request + // Initialize gRPC module for this request grpc.init(); Ok(()) } @@ -61,7 +48,7 @@ impl ProxyHttp for GrpcWebBridgeProxy { _session: &mut Session, _ctx: &mut Self::CTX, ) -> Result> { - // this needs to be your gRPC server + // This needs to be your gRPC server let grpc_peer = Box::new(HttpPeer::new( ("1.1.1.1", 443), true, @@ -76,9 +63,8 @@ impl ProxyHttp for GrpcWebBridgeProxy { fn main() { env_logger::init(); - // read command line arguments - let opt = Opt::parse(); - let mut my_server = Server::new(Some(opt)).unwrap(); + // Create server without command line arguments + let mut my_server = Server::new(None).unwrap(); my_server.bootstrap(); let mut my_proxy = diff --git a/pingora-proxy/examples/load_balancer.rs b/pingora-proxy/examples/load_loadbalancer.rs similarity index 66% rename from pingora-proxy/examples/load_balancer.rs rename to pingora-proxy/examples/load_loadbalancer.rs index a428b9b3..94345194 100644 --- a/pingora-proxy/examples/load_balancer.rs +++ b/pingora-proxy/examples/load_loadbalancer.rs @@ -1,38 +1,29 @@ -// Copyright 2024 Cloudflare, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - use async_trait::async_trait; -use clap::Parser; use log::info; use pingora_core::services::background::background_service; use std::{sync::Arc, time::Duration}; -use pingora_core::server::configuration::Opt; use pingora_core::server::Server; use pingora_core::upstreams::peer::HttpPeer; use pingora_core::Result; +use pingora_core::{ + listeners::tls::TlsSettings, +}; use pingora_load_balancing::{health_check, selection::RoundRobin, LoadBalancer}; use pingora_proxy::{ProxyHttp, Session}; +use pingora_http::RequestHeader; pub struct LB(Arc>); #[async_trait] impl ProxyHttp for LB { type CTX = (); - fn new_ctx(&self) -> Self::CTX {} - async fn upstream_peer(&self, _session: &mut Session, _ctx: &mut ()) -> Result> { + fn new_ctx(&self) -> Self::CTX { + () + } + + async fn upstream_peer(&self, _session: &mut Session, _ctx: &mut Self::CTX) -> Result> { let upstream = self .0 .select(b"", 256) // hash doesn't matter @@ -47,7 +38,7 @@ impl ProxyHttp for LB { async fn upstream_request_filter( &self, _session: &mut Session, - upstream_request: &mut pingora_http::RequestHeader, + upstream_request: &mut RequestHeader, _ctx: &mut Self::CTX, ) -> Result<()> { upstream_request @@ -61,12 +52,11 @@ impl ProxyHttp for LB { fn main() { env_logger::init(); - // read command line arguments - let opt = Opt::parse(); - let mut my_server = Server::new(Some(opt)).unwrap(); + // Create the server without command line arguments + let mut my_server = Server::new(None).unwrap(); my_server.bootstrap(); - // 127.0.0.1:343" is just a bad server + // "127.0.0.1:343" is just a bad server let mut upstreams = LoadBalancer::try_from_iter(["1.1.1.1:443", "1.0.0.1:443", "127.0.0.1:343"]).unwrap(); @@ -85,8 +75,7 @@ fn main() { let cert_path = format!("{}/tests/keys/server.crt", env!("CARGO_MANIFEST_DIR")); let key_path = format!("{}/tests/keys/key.pem", env!("CARGO_MANIFEST_DIR")); - let mut tls_settings = - pingora_core::listeners::tls::TlsSettings::intermediate(&cert_path, &key_path).unwrap(); + let mut tls_settings = TlsSettings::intermediate(&cert_path, &key_path).unwrap(); tls_settings.enable_h2(); lb.add_tls_with_settings("0.0.0.0:6189", None, tls_settings); diff --git a/pingora-proxy/examples/modify_response.rs b/pingora-proxy/examples/modify_response.rs index 6730f718..0be9edbe 100644 --- a/pingora-proxy/examples/modify_response.rs +++ b/pingora-proxy/examples/modify_response.rs @@ -1,28 +1,11 @@ -// Copyright 2024 Cloudflare, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - use async_trait::async_trait; use bytes::Bytes; -use clap::Parser; use serde::{Deserialize, Serialize}; -use std::net::ToSocketAddrs; -use pingora_core::server::configuration::Opt; use pingora_core::server::Server; use pingora_core::upstreams::peer::HttpPeer; use pingora_core::Result; -use pingora_http::ResponseHeader; +use pingora_http::{RequestHeader, ResponseHeader}; use pingora_proxy::{ProxyHttp, Session}; const HOST: &str = "ip.jsontest.com"; @@ -43,6 +26,7 @@ pub struct MyCtx { #[async_trait] impl ProxyHttp for Json2Yaml { type CTX = MyCtx; + fn new_ctx(&self) -> Self::CTX { MyCtx { buffer: vec![] } } @@ -59,11 +43,11 @@ impl ProxyHttp for Json2Yaml { async fn upstream_request_filter( &self, _session: &mut Session, - upstream_request: &mut pingora_http::RequestHeader, + upstream_request: &mut RequestHeader, _ctx: &mut Self::CTX, ) -> Result<()> { upstream_request - .insert_header("Host", HOST.to_owned()) + .insert_header("Host", HOST) .unwrap(); Ok(()) } @@ -73,14 +57,11 @@ impl ProxyHttp for Json2Yaml { _session: &mut Session, upstream_response: &mut ResponseHeader, _ctx: &mut Self::CTX, - ) -> Result<()> - where - Self::CTX: Send + Sync, - { - // Remove content-length because the size of the new body is unknown + ) -> Result<()> { + // Remove Content-Length because the size of the new body is unknown upstream_response.remove_header("Content-Length"); upstream_response - .insert_header("Transfer-Encoding", "Chunked") + .insert_header("Transfer-Encoding", "chunked") .unwrap(); Ok(()) } @@ -95,15 +76,14 @@ impl ProxyHttp for Json2Yaml { where Self::CTX: Send + Sync, { - // buffer the data - if let Some(b) = body { - ctx.buffer.extend(&b[..]); - // drop the body - b.clear(); + // Buffer the data + if let Some(b) = body.take() { + ctx.buffer.extend_from_slice(&b); } + if end_of_stream { - // This is the last chunk, we can process the data now - let json_body: Resp = serde_json::de::from_slice(&ctx.buffer).unwrap(); + // This is the last chunk; we can process the data now + let json_body: Resp = serde_json::from_slice(&ctx.buffer).unwrap(); let yaml_body = serde_yaml::to_string(&json_body).unwrap(); *body = Some(Bytes::copy_from_slice(yaml_body.as_bytes())); } @@ -112,24 +92,16 @@ impl ProxyHttp for Json2Yaml { } } -// RUST_LOG=INFO cargo run --example modify_response -// curl 127.0.0.1:6191 fn main() { env_logger::init(); - let opt = Opt::parse(); - let mut my_server = Server::new(Some(opt)).unwrap(); + let mut my_server = Server::new(None).unwrap(); my_server.bootstrap(); let mut my_proxy = pingora_proxy::http_proxy_service( &my_server.configuration, Json2Yaml { - // hardcode the IP of ip.jsontest.com for now - addr: ("142.251.2.121", 80) - .to_socket_addrs() - .unwrap() - .next() - .unwrap(), + addr: "142.251.2.121:80".parse().unwrap(), }, ); diff --git a/pingora-proxy/examples/multi_lb.rs b/pingora-proxy/examples/multi_lb.rs index 1321c207..599e71b6 100644 --- a/pingora-proxy/examples/multi_lb.rs +++ b/pingora-proxy/examples/multi_lb.rs @@ -1,21 +1,8 @@ -// Copyright 2024 Cloudflare, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - use async_trait::async_trait; use std::sync::Arc; use pingora_core::{prelude::*, services::background::GenBackgroundService}; +use pingora_core::upstreams::peer::HttpPeer; use pingora_load_balancing::{ health_check::TcpHealthCheck, selection::{BackendIter, BackendSelection, RoundRobin}, @@ -31,10 +18,19 @@ struct Router { #[async_trait] impl ProxyHttp for Router { type CTX = (); - fn new_ctx(&self) {} - async fn upstream_peer(&self, session: &mut Session, _ctx: &mut ()) -> Result> { - // determine LB cluster based on request uri + // Corrected the `new_ctx` method to return `Self::CTX` + fn new_ctx(&self) -> Self::CTX { + () + } + + // Updated the return type to `Result>` + async fn upstream_peer( + &self, + session: &mut Session, + _ctx: &mut Self::CTX, + ) -> Result> { + // Determine LB cluster based on request URI let cluster = if session.req_header().uri.path().starts_with("/one/") { &self.cluster_one } else { @@ -42,13 +38,17 @@ impl ProxyHttp for Router { }; let upstream = cluster - .select(b"", 256) // hash doesn't matter for round robin + .select(b"", 256) // Hash doesn't matter for round robin .unwrap(); - println!("upstream peer is: {upstream:?}"); + println!("upstream peer is: {:?}", upstream); - // Set SNI to one.one.one.one - let peer = Box::new(HttpPeer::new(upstream, true, "one.one.one.one".to_string())); + // Create a new HttpPeer instance and wrap it in a Box + let peer = Box::new(HttpPeer::new( + upstream, + true, + "one.one.one.one".to_string(), + )); Ok(peer) } } @@ -72,7 +72,7 @@ fn main() { let mut my_server = Server::new(None).unwrap(); my_server.bootstrap(); - // build multiple clusters + // Build multiple clusters let cluster_one = build_cluster_service::(&["1.1.1.1:443", "127.0.0.1:343"]); let cluster_two = build_cluster_service::(&["1.0.0.1:443", "127.0.0.2:343"]); diff --git a/pingora-proxy/examples/rate_limiter.rs b/pingora-proxy/examples/rate_limiter.rs index d2c8b7ec..14e3160c 100644 --- a/pingora-proxy/examples/rate_limiter.rs +++ b/pingora-proxy/examples/rate_limiter.rs @@ -10,21 +10,27 @@ use std::sync::Arc; use std::time::Duration; fn main() { - let mut server = Server::new(Some(Opt::default())).unwrap(); + env_logger::init(); + + let mut server = Server::new(None).unwrap(); server.bootstrap(); - let mut upstreams = LoadBalancer::try_from_iter(["1.1.1.1:443", "1.0.0.1:443"]).unwrap(); + + let mut upstreams = + LoadBalancer::try_from_iter(["1.1.1.1:443", "1.0.0.1:443"]).unwrap(); + // Set health check let hc = TcpHealthCheck::new(); upstreams.set_health_check(hc); upstreams.health_check_frequency = Some(Duration::from_secs(1)); + // Set background service let background = background_service("health check", upstreams); let upstreams = background.task(); + // Set load balancer let mut lb = http_proxy_service(&server.configuration, LB(upstreams)); lb.add_tcp("0.0.0.0:6188"); - // let rate = Rate server.add_service(background); server.add_service(lb); server.run_forever(); @@ -33,78 +39,50 @@ fn main() { pub struct LB(Arc>); impl LB { - pub fn get_request_appid(&self, session: &mut Session) -> Option { - match session + pub fn get_request_appid(&self, session: &Session) -> Option { + session .req_header() .headers .get("appid") - .map(|v| v.to_str()) - { - None => None, - Some(v) => match v { - Ok(v) => Some(v.to_string()), - Err(_) => None, - }, - } + .and_then(|v| v.to_str().ok().map(|s| s.to_string())) } } // Rate limiter static RATE_LIMITER: Lazy = Lazy::new(|| Rate::new(Duration::from_secs(1))); -// max request per second per client -static MAX_REQ_PER_SEC: isize = 1; +// Max requests per second per client +const MAX_REQ_PER_SEC: isize = 1; #[async_trait] impl ProxyHttp for LB { type CTX = (); - fn new_ctx(&self) {} - - async fn upstream_peer( - &self, - _session: &mut Session, - _ctx: &mut Self::CTX, - ) -> Result> { - let upstream = self.0.select(b"", 256).unwrap(); - // Set SNI - let peer = Box::new(HttpPeer::new(upstream, true, "one.one.one.one".to_string())); - Ok(peer) + fn new_ctx(&self) -> Self::CTX { + () } - async fn upstream_request_filter( + async fn request_filter( &self, - _session: &mut Session, - upstream_request: &mut RequestHeader, + session: &mut Session, _ctx: &mut Self::CTX, - ) -> Result<()> - where - Self::CTX: Send + Sync, - { - upstream_request - .insert_header("Host", "one.one.one.one") - .unwrap(); - Ok(()) - } - - async fn request_filter(&self, session: &mut Session, _ctx: &mut Self::CTX) -> Result - where - Self::CTX: Send + Sync, - { + ) -> Result { let appid = match self.get_request_appid(session) { - None => return Ok(false), // no client appid found, skip rate limiting - Some(addr) => addr, + None => return Ok(false), // No client appid found, skip rate limiting + Some(appid) => appid, }; - // retrieve the current window requests + // Retrieve the current window requests let curr_window_requests = RATE_LIMITER.observe(&appid, 1); if curr_window_requests > MAX_REQ_PER_SEC { - // rate limited, return 429 + // Rate limited, return 429 let mut header = ResponseHeader::build(429, None).unwrap(); header .insert_header("X-Rate-Limit-Limit", MAX_REQ_PER_SEC.to_string()) .unwrap(); - header.insert_header("X-Rate-Limit-Remaining", "0").unwrap(); + header + .insert_header("X-Rate-Limit-Remaining", "0") + .unwrap(); header.insert_header("X-Rate-Limit-Reset", "1").unwrap(); session.set_keepalive(None); session @@ -114,4 +92,31 @@ impl ProxyHttp for LB { } Ok(false) } + + async fn upstream_peer( + &self, + _session: &mut Session, + _ctx: &mut Self::CTX, + ) -> Result> { + let upstream = self.0.select(b"", 256).unwrap(); + // Set SNI + let peer = Box::new(HttpPeer::new( + upstream, + true, + "one.one.one.one".to_string(), + )); + Ok(peer) + } + + async fn upstream_request_filter( + &self, + _session: &mut Session, + upstream_request: &mut RequestHeader, + _ctx: &mut Self::CTX, + ) -> Result<()> { + upstream_request + .insert_header("Host", "one.one.one.one") + .unwrap(); + Ok(()) + } } diff --git a/pingora-proxy/examples/test_server.rs b/pingora-proxy/examples/test_server.rs new file mode 100644 index 00000000..3ee544b3 --- /dev/null +++ b/pingora-proxy/examples/test_server.rs @@ -0,0 +1,45 @@ +use std::io::{Read, Write}; +use std::net::{TcpListener, TcpStream}; +use std::thread; + +// Function that starts the test web server +pub fn start_test_server(address: &str) -> std::io::Result<()> { + let listener = TcpListener::bind(address)?; + println!("Test server running on {}", address); + + for stream in listener.incoming() { + match stream { + Ok(stream) => { + thread::spawn(move || { + handle_test_client(stream); + }); + } + Err(e) => eprintln!("Failed to accept connection: {}", e), + } + } + Ok(()) +} + +// Function to handle each client and respond with a "Hello. You made it!" page +fn handle_test_client(mut stream: TcpStream) { + let mut buffer = [0; 512]; + if stream.read(&mut buffer).is_ok() { + // HTTP response with "Hello. You made it!" message + let response = r#"HTTP/1.1 200 OK +Content-Type: text/html + + + +Welcome + +

Hello. You made it!

+ +"#; + + // Write the response back to the client + if stream.write_all(response.as_bytes()).is_err() { + eprintln!("Failed to send response"); + } + } +} + \ No newline at end of file diff --git a/pingora-proxy/examples/use_module.rs b/pingora-proxy/examples/use_module.rs index 4404768f..38814733 100644 --- a/pingora-proxy/examples/use_module.rs +++ b/pingora-proxy/examples/use_module.rs @@ -1,27 +1,12 @@ -// Copyright 2024 Cloudflare, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - use async_trait::async_trait; -use clap::Parser; use pingora_core::modules::http::HttpModules; -use pingora_core::server::configuration::Opt; use pingora_core::server::Server; use pingora_core::upstreams::peer::HttpPeer; use pingora_core::Result; use pingora_http::RequestHeader; use pingora_proxy::{ProxyHttp, Session}; +use std::any::Any; /// This example shows how to build and import 3rd party modules @@ -29,11 +14,10 @@ use pingora_proxy::{ProxyHttp, Session}; mod my_acl { use super::*; use pingora_core::modules::http::{HttpModule, HttpModuleBuilder, Module}; - use pingora_error::{Error, ErrorType::HTTPStatus}; - use std::any::Any; + use pingora_error::{Error, ErrorType}; // Import Error and ErrorType here - // This is the struct for per request module context - struct MyAclCtx { + // This is the struct for per-request module context + pub struct MyAclCtx { credential_header: String, } @@ -41,18 +25,24 @@ mod my_acl { #[async_trait] impl HttpModule for MyAclCtx { async fn request_header_filter(&mut self, req: &mut RequestHeader) -> Result<()> { - let Some(auth) = req.headers.get(http::header::AUTHORIZATION) else { - return Error::e_explain(HTTPStatus(403), "Auth failed, no auth header"); - }; - - if auth.as_bytes() != self.credential_header.as_bytes() { - Error::e_explain(HTTPStatus(403), "Auth failed, credential mismatch") + if let Some(auth) = req.headers.get("Authorization") { + if auth.as_bytes() != self.credential_header.as_bytes() { + return Error::e_explain( + ErrorType::HTTPStatus(403), + "Auth failed, credential mismatch", + ); + } else { + Ok(()) + } } else { - Ok(()) + Error::e_explain( + ErrorType::HTTPStatus(403), + "Auth failed, no auth header", + ) } } - // boilerplate code for all modules + // Boilerplate code for all modules fn as_any(&self) -> &dyn Any { self } @@ -65,13 +55,13 @@ mod my_acl { pub struct MyAcl { pub credential: String, } + impl HttpModuleBuilder for MyAcl { - // This function defines how to create each Ctx. This function is called when a new request - // arrives + // This function defines how to create each context. This function is called when a new request arrives fn init(&self) -> Module { Box::new(MyAclCtx { // Make it easier to compare header - // We could also store this value in MyAcl and use Arc to share it with every Ctx. + // We could also store this value in MyAcl and use Arc to share it with every context. credential_header: format!("basic {}", self.credential), }) } @@ -83,14 +73,17 @@ pub struct MyProxy; #[async_trait] impl ProxyHttp for MyProxy { type CTX = (); - fn new_ctx(&self) -> Self::CTX {} + + fn new_ctx(&self) -> Self::CTX { + () + } // This function is only called once when the server starts fn init_downstream_modules(&self, modules: &mut HttpModules) { // Add the module to MyProxy modules.add_module(Box::new(my_acl::MyAcl { credential: "testcode".into(), - })) + })); } async fn upstream_peer( @@ -107,6 +100,7 @@ impl ProxyHttp for MyProxy { } } +// To run the example: // RUST_LOG=INFO cargo run --example use_module // curl 127.0.0.1:6193 -H "Host: one.one.one.one" -v // curl 127.0.0.1:6193 -H "Host: one.one.one.one" -H "Authorization: basic testcode" @@ -114,9 +108,8 @@ impl ProxyHttp for MyProxy { fn main() { env_logger::init(); - // read command line arguments - let opt = Opt::parse(); - let mut my_server = Server::new(Some(opt)).unwrap(); + // Create the server without command line arguments + let mut my_server = Server::new(None).unwrap(); my_server.bootstrap(); let mut my_proxy = pingora_proxy::http_proxy_service(&my_server.configuration, MyProxy); From 9900722d56fd1f1f3a52c4308c6a54e595f3d332 Mon Sep 17 00:00:00 2001 From: HasturDev Date: Sat, 9 Nov 2024 18:56:13 -0500 Subject: [PATCH 2/6] added tutorial start --- pingora-proxy/examples/start.md | 215 ++++++++++++++++++++++++++++++++ 1 file changed, 215 insertions(+) create mode 100644 pingora-proxy/examples/start.md diff --git a/pingora-proxy/examples/start.md b/pingora-proxy/examples/start.md new file mode 100644 index 00000000..ffb8805a --- /dev/null +++ b/pingora-proxy/examples/start.md @@ -0,0 +1,215 @@ +### Pingora Tutorial + +## Introduction + +Before we begin building a full proxy server, we need a simple server to test and demonstrate our proxy’s functionality. In this tutorial, we’ll start by setting up a basic test web server in Rust that will display a simple message when accessed. This will help us verify that our proxy server is routing requests correctly. +Step 1: Start the Test Web Server + +## Step 1 creating the server + +To begin, let’s create a basic server function that listens on a specified address and handles incoming client connections. This server will allow us to test the future code we’ll write for the proxy server. +```rust +use std::io::{Read, Write}; +use std::net::{TcpListener, TcpStream}; +use std::thread; + +// Function that starts the test web server +pub fn start_test_server(address: &str) -> std::io::Result<()> { + let listener = TcpListener::bind(address)?; + println!("Test server running on {}", address); + + for stream in listener.incoming() { + match stream { + Ok(stream) => { + thread::spawn(move || { + handle_test_client(stream); + }); + } + Err(e) => eprintln!("Failed to accept connection: {}", e), + } + } + Ok(()) +} +``` +Code: Starting the Test Web Server +```rust +use std::io::{Read, Write}; +use std::net::{TcpListener, TcpStream}; +use std::thread; +``` + +# Imports: + +`std::io::{Read, Write}`: This imports Read and Write, traits that provide reading and writing functionality for streams. We’ll use these to handle data between the client and server. + +`std::net::{TcpListener, TcpStream}`: TcpListener allows us to listen for incoming TCP connections on a specified address, and TcpStream represents a connection between a client and the server. + +`std::thread`: This imports Rust’s threading capabilities, allowing us to spawn new threads for each client connection to handle multiple clients concurrently. + +# Function Declaration: + +```rust +pub fn start_test_server(address: &str) -> std::io::Result<()> { +``` + +start_test_server is a function that takes an address parameter of type &str (a string slice). +It returns a `Result<(), std::io::Error>`. This indicates that the function might return an std::io::Error if there’s a problem, such as if the address is already in use + +# Binding the Listener: + +```rust +let listener = TcpListener::bind(address)?; +``` + +`TcpListener::bind(address)?` creates a TCP listener that binds to the provided address (e.g., "127.0.0.1:9000"). The ? operator automatically returns an error if binding fails. + +This listener will listen for incoming connections on the specified address. + +# Console Output: + +```rust +println!("Test server running on {}", address); +``` + +Prints a message to the console to indicate that the test server has started successfully and is running on the specified address. + +# Handling Incoming Connections: + +```rust +for stream in listener.incoming() { +``` + +`listener.incoming()` is an iterator that yields incoming connections. Each connection is represented as a TcpStream, allowing data transfer with a client. +We use a for loop to handle each incoming connection individually. + +# Match Statement and Threading: + +```rust + match stream { + Ok(stream) => { + thread::spawn(move || { + handle_test_client(stream); + }); + } + Err(e) => eprintln!("Failed to accept connection: {}", e), + } +} +``` + +`match stream`: We check whether each incoming connection (i.e., each stream) is successful (Ok) or encountered an error (Err). + +`Ok(stream)`: If a connection is established successfully, stream represents the TcpStream of that connection. + +`thread::spawn(move || { handle_test_client(stream); })`: Spawns a new thread for each connection, passing the TcpStream to handle_test_client. +This enables handling multiple client connections concurrently. + +`Err(e)`: If there’s an error accepting a connection, eprintln! outputs an error message to the console. + +# Return Value: + +```rust + Ok(()) +} +``` + +Returns `Ok(())`, indicating that the function completed successfully if there were no binding errors. + +## Step 2: Display a Response Message + +To confirm that our server is working correctly, we’ll add a function to respond with a simple HTML message, “Hello. You made it!” This will help us know when we have successfully reached the test server. + +```rust +// Function to handle each client and respond with a "Hello. You made it!" page +fn handle_test_client(mut stream: TcpStream) { + let mut buffer = [0; 512]; + if stream.read(&mut buffer).is_ok() { + // HTTP response with "Hello. You made it!" message + let response = r#"HTTP/1.1 200 OK +Content-Type: text/html + + + +Welcome + +

Hello. You made it!

+ +"#; + + // Write the response back to the client + if stream.write_all(response.as_bytes()).is_err() { + eprintln!("Failed to send response"); + } + } +} +``` + +# Code: Displaying a Response Message + +```rust +// Function to handle each client and respond with a "Hello. You made it!" page +fn handle_test_client(mut stream: TcpStream) { +``` + +# Function Declaration: + +`handle_test_client` takes a mutable TcpStream called stream as input, representing an open connection with a client. +This function will read the client’s request and write back a simple HTML response. + +```rust +let mut buffer = [0; 512]; +``` + +# Buffer Initialization: +`let mut buffer = [0; 512];` creates a buffer, a fixed-size array of 512 bytes, to temporarily hold data read from the client. +512 bytes is generally enough to capture an HTTP request from the client. +```rust + if stream.read(&mut buffer).is_ok() { +``` +# Reading from the Client: + +`stream.read(&mut buffer).is_ok()`: Reads data from the client into buffer. The is_ok() check ensures that reading was successful. +We’re not inspecting the content of the request in this example; we simply need to know the client connected to respond with our message. + +# HTTP Response: + +```rust + let response = r#"HTTP/1.1 200 OK +Content-Type: text/html + + + +Welcome + +

Hello. You made it!

+ +"#; +``` + +`let response = ...`: Defines an HTTP response as a raw string (r#"...#"). +The response includes: + - `Status Line: HTTP/1.1 200 OK` indicates a successful HTTP response. + - `Headers: Content-Type: text/html` informs the client that the response is HTML. + - `HTML Content:` Displays a simple message, `

Hello. You made it!

`, in an HTML page structure. + +This message will be displayed in the client’s browser when they successfully reach the server + +# Writing the Response: + +```rust +if stream.write_all(response.as_bytes()).is_err() { + eprintln!("Failed to send response"); + } + } +} +``` + +`stream.write_all(response.as_bytes())`: Writes the response back to the client as bytes. + +`response.as_bytes()` converts the response string into a byte slice for sending over the network. + +`is_err()`: If an error occurs while sending the response, an error message is printed to the console with eprintln!. + +## Onto the next step +you have now created a rust server that will listen and respond to connections. So lets take this server and use it, as well as copies of it, to explore the rest of pingora + +``` Some link here getting you to the next step ``` \ No newline at end of file From d0833e110c50b1650aa09fae01cdb1f6a6bafe2b Mon Sep 17 00:00:00 2001 From: HasturDev Date: Sat, 9 Nov 2024 19:08:09 -0500 Subject: [PATCH 3/6] feature/full-tutorial created pingora_tutorial folder and start tutorial readme.md file --- pingora-proxy/examples/ctx.rs | 56 ++++---- pingora-proxy/examples/gateway.rs | 77 ++++++----- pingora-proxy/examples/grpc_web_module.rs | 90 +++++++++++++ pingora-proxy/examples/load_balancer.rs | 96 ++++++++++++++ pingora-proxy/examples/modify_response.rs | 58 ++++++--- pingora-proxy/examples/multi_lb.rs | 44 +++---- pingora-proxy/examples/rate_limiter.rs | 101 +++++++-------- pingora-proxy/examples/use_module.rs | 65 +++++----- pingora_tutorial/src/ctx.rs | 91 +++++++++++++ pingora_tutorial/src/gateway.rs | 121 +++++++++++++++++ .../src}/grpc_web_modules.rs | 0 .../src}/load_loadbalancer.rs | 0 pingora_tutorial/src/modify_response.rs | 112 ++++++++++++++++ pingora_tutorial/src/multi_lb.rs | 91 +++++++++++++ pingora_tutorial/src/rate_limiter.rs | 122 ++++++++++++++++++ .../src}/test_server.rs | 0 pingora_tutorial/src/use_module.rs | 120 +++++++++++++++++ .../examples => pingora_tutorial}/start.md | 12 +- .../tutorial_Cargo.lock | 0 .../tutorial_Cargo.toml | 0 20 files changed, 1079 insertions(+), 177 deletions(-) create mode 100644 pingora-proxy/examples/grpc_web_module.rs create mode 100644 pingora-proxy/examples/load_balancer.rs create mode 100644 pingora_tutorial/src/ctx.rs create mode 100644 pingora_tutorial/src/gateway.rs rename {pingora-proxy/examples => pingora_tutorial/src}/grpc_web_modules.rs (100%) rename {pingora-proxy/examples => pingora_tutorial/src}/load_loadbalancer.rs (100%) create mode 100644 pingora_tutorial/src/modify_response.rs create mode 100644 pingora_tutorial/src/multi_lb.rs create mode 100644 pingora_tutorial/src/rate_limiter.rs rename {pingora-proxy/examples => pingora_tutorial/src}/test_server.rs (100%) create mode 100644 pingora_tutorial/src/use_module.rs rename {pingora-proxy/examples => pingora_tutorial}/start.md (89%) rename pingora-proxy/examples/example_Cargo.lock => pingora_tutorial/tutorial_Cargo.lock (100%) rename pingora-proxy/examples/example_Cargo.toml => pingora_tutorial/tutorial_Cargo.toml (100%) diff --git a/pingora-proxy/examples/ctx.rs b/pingora-proxy/examples/ctx.rs index fd282c92..4838391e 100644 --- a/pingora-proxy/examples/ctx.rs +++ b/pingora-proxy/examples/ctx.rs @@ -1,42 +1,55 @@ +// Copyright 2024 Cloudflare, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + use async_trait::async_trait; +use clap::Parser; use log::info; -use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::Mutex; use pingora_core::server::configuration::Opt; use pingora_core::server::Server; use pingora_core::upstreams::peer::HttpPeer; use pingora_core::Result; -use pingora_http::RequestHeader; use pingora_proxy::{ProxyHttp, Session}; -/// Global request counter using `AtomicUsize` for thread-safe atomic operations -static REQ_COUNTER: AtomicUsize = AtomicUsize::new(0); +// global counter +static REQ_COUNTER: Mutex = Mutex::new(0); pub struct MyProxy { - /// Counter for beta users - beta_counter: AtomicUsize, + // counter for the service + beta_counter: Mutex, // AtomicUsize works too } pub struct MyCtx { beta_user: bool, } -fn check_beta_user(req: &RequestHeader) -> bool { - // Simple logic to check if user is beta +fn check_beta_user(req: &pingora_http::RequestHeader) -> bool { + // some simple logic to check if user is beta req.headers.get("beta-flag").is_some() } #[async_trait] impl ProxyHttp for MyProxy { type CTX = MyCtx; - fn new_ctx(&self) -> Self::CTX { MyCtx { beta_user: false } } async fn request_filter(&self, session: &mut Session, ctx: &mut Self::CTX) -> Result { ctx.beta_user = check_beta_user(session.req_header()); - Ok(false) // Continue processing the request + Ok(false) } async fn upstream_peer( @@ -44,44 +57,39 @@ impl ProxyHttp for MyProxy { _session: &mut Session, ctx: &mut Self::CTX, ) -> Result> { - // Increment the global request counter atomically - let req_counter = REQ_COUNTER.fetch_add(1, Ordering::Relaxed) + 1; + let mut req_counter = REQ_COUNTER.lock().unwrap(); + *req_counter += 1; let addr = if ctx.beta_user { - // Increment the beta user counter atomically - let beta_count = self.beta_counter.fetch_add(1, Ordering::Relaxed) + 1; + let mut beta_count = self.beta_counter.lock().unwrap(); + *beta_count += 1; info!("I'm a beta user #{beta_count}"); ("1.0.0.1", 443) } else { - info!("I'm a user #{req_counter}"); + info!("I'm an user #{req_counter}"); ("1.1.1.1", 443) }; - let peer = Box::new(HttpPeer::new( - addr, - true, - "one.one.one.one".to_string(), - )); + let peer = Box::new(HttpPeer::new(addr, true, "one.one.one.one".to_string())); Ok(peer) } } -// To run the example: // RUST_LOG=INFO cargo run --example ctx // curl 127.0.0.1:6190 -H "Host: one.one.one.one" // curl 127.0.0.1:6190 -H "Host: one.one.one.one" -H "beta-flag: 1" fn main() { env_logger::init(); - // Read command line arguments - let opt = Opt::parse_args(); + // read command line arguments + let opt = Opt::parse(); let mut my_server = Server::new(Some(opt)).unwrap(); my_server.bootstrap(); let mut my_proxy = pingora_proxy::http_proxy_service( &my_server.configuration, MyProxy { - beta_counter: AtomicUsize::new(0), + beta_counter: Mutex::new(0), }, ); my_proxy.add_tcp("0.0.0.0:6190"); diff --git a/pingora-proxy/examples/gateway.rs b/pingora-proxy/examples/gateway.rs index 4bbb5c72..78f4aae9 100644 --- a/pingora-proxy/examples/gateway.rs +++ b/pingora-proxy/examples/gateway.rs @@ -1,38 +1,49 @@ +// Copyright 2024 Cloudflare, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + use async_trait::async_trait; +use clap::Parser; use log::info; -use prometheus::{IntCounter, register_int_counter}; +use prometheus::register_int_counter; +use pingora_core::server::configuration::Opt; use pingora_core::server::Server; use pingora_core::upstreams::peer::HttpPeer; use pingora_core::Result; -use pingora_http::{RequestHeader, ResponseHeader}; +use pingora_http::ResponseHeader; use pingora_proxy::{ProxyHttp, Session}; -fn check_login(req: &RequestHeader) -> bool { - // Implement your login check logic here - req.headers.get("Authorization") - .map(|v| v.as_bytes() == b"password") - .unwrap_or(false) +fn check_login(req: &pingora_http::RequestHeader) -> bool { + // implement you logic check logic here + req.headers.get("Authorization").map(|v| v.as_bytes()) == Some(b"password") } pub struct MyGateway { - req_metric: IntCounter, + req_metric: prometheus::IntCounter, } #[async_trait] impl ProxyHttp for MyGateway { type CTX = (); - - fn new_ctx(&self) -> Self::CTX { - () - } + fn new_ctx(&self) -> Self::CTX {} async fn request_filter(&self, session: &mut Session, _ctx: &mut Self::CTX) -> Result { if session.req_header().uri.path().starts_with("/login") && !check_login(session.req_header()) { let _ = session.respond_error(403).await; - // Return true to indicate early response + // true: early return as the response is already written return Ok(true); } Ok(false) @@ -49,13 +60,9 @@ impl ProxyHttp for MyGateway { ("1.1.1.1", 443) }; - info!("Connecting to {:?}", addr); + info!("connecting to {addr:?}"); - let peer = Box::new(HttpPeer::new( - addr, - true, - "one.one.one.one".to_string(), - )); + let peer = Box::new(HttpPeer::new(addr, true, "one.one.one.one".to_string())); Ok(peer) } @@ -64,12 +71,15 @@ impl ProxyHttp for MyGateway { _session: &mut Session, upstream_response: &mut ResponseHeader, _ctx: &mut Self::CTX, - ) -> Result<()> { - // Replace existing header if any + ) -> Result<()> + where + Self::CTX: Send + Sync, + { + // replace existing header if any upstream_response .insert_header("Server", "MyGateway") .unwrap(); - // Remove unsupported header + // because we don't support h3 upstream_response.remove_header("alt-svc"); Ok(()) @@ -79,34 +89,39 @@ impl ProxyHttp for MyGateway { &self, session: &mut Session, _e: Option<&pingora_core::Error>, - _ctx: &mut Self::CTX, + ctx: &mut Self::CTX, ) { let response_code = session .response_written() .map_or(0, |resp| resp.status.as_u16()); info!( - "Request to {} responded with status code {}", - session.req_header().uri.path(), - response_code + "{} response code: {response_code}", + self.request_summary(session, ctx) ); self.req_metric.inc(); } } +// RUST_LOG=INFO cargo run --example load_balancer +// curl 127.0.0.1:6191 -H "Host: one.one.one.one" +// curl 127.0.0.1:6190/family/ -H "Host: one.one.one.one" +// curl 127.0.0.1:6191/login/ -H "Host: one.one.one.one" -I -H "Authorization: password" +// curl 127.0.0.1:6191/login/ -H "Host: one.one.one.one" -I -H "Authorization: bad" +// For metrics +// curl 127.0.0.1:6192/ fn main() { env_logger::init(); - // Create the server without options - let mut my_server = Server::new(None).unwrap(); + // read command line arguments + let opt = Opt::parse(); + let mut my_server = Server::new(Some(opt)).unwrap(); my_server.bootstrap(); - let req_metric = register_int_counter!("req_counter", "Number of requests").unwrap(); - let mut my_proxy = pingora_proxy::http_proxy_service( &my_server.configuration, MyGateway { - req_metric, + req_metric: register_int_counter!("req_counter", "Number of requests").unwrap(), }, ); my_proxy.add_tcp("0.0.0.0:6191"); diff --git a/pingora-proxy/examples/grpc_web_module.rs b/pingora-proxy/examples/grpc_web_module.rs new file mode 100644 index 00000000..ecd11fd1 --- /dev/null +++ b/pingora-proxy/examples/grpc_web_module.rs @@ -0,0 +1,90 @@ +// Copyright 2024 Cloudflare, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use async_trait::async_trait; +use clap::Parser; + +use pingora_core::server::Server; +use pingora_core::upstreams::peer::HttpPeer; +use pingora_core::Result; +use pingora_core::{ + modules::http::{ + grpc_web::{GrpcWeb, GrpcWebBridge}, + HttpModules, + }, + prelude::Opt, +}; +use pingora_proxy::{ProxyHttp, Session}; + +/// This example shows how to use the gRPC-web bridge module + +pub struct GrpcWebBridgeProxy; + +#[async_trait] +impl ProxyHttp for GrpcWebBridgeProxy { + type CTX = (); + fn new_ctx(&self) -> Self::CTX {} + + fn init_downstream_modules(&self, modules: &mut HttpModules) { + // Add the gRPC web module + modules.add_module(Box::new(GrpcWeb)) + } + + async fn early_request_filter( + &self, + session: &mut Session, + _ctx: &mut Self::CTX, + ) -> Result<()> { + let grpc = session + .downstream_modules_ctx + .get_mut::() + .expect("GrpcWebBridge module added"); + + // initialize gRPC module for this request + grpc.init(); + Ok(()) + } + + async fn upstream_peer( + &self, + _session: &mut Session, + _ctx: &mut Self::CTX, + ) -> Result> { + // this needs to be your gRPC server + let grpc_peer = Box::new(HttpPeer::new( + ("1.1.1.1", 443), + true, + "one.one.one.one".to_string(), + )); + Ok(grpc_peer) + } +} + +// RUST_LOG=INFO cargo run --example grpc_web_module + +fn main() { + env_logger::init(); + + // read command line arguments + let opt = Opt::parse(); + let mut my_server = Server::new(Some(opt)).unwrap(); + my_server.bootstrap(); + + let mut my_proxy = + pingora_proxy::http_proxy_service(&my_server.configuration, GrpcWebBridgeProxy); + my_proxy.add_tcp("0.0.0.0:6194"); + + my_server.add_service(my_proxy); + my_server.run_forever(); +} diff --git a/pingora-proxy/examples/load_balancer.rs b/pingora-proxy/examples/load_balancer.rs new file mode 100644 index 00000000..a428b9b3 --- /dev/null +++ b/pingora-proxy/examples/load_balancer.rs @@ -0,0 +1,96 @@ +// Copyright 2024 Cloudflare, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use async_trait::async_trait; +use clap::Parser; +use log::info; +use pingora_core::services::background::background_service; +use std::{sync::Arc, time::Duration}; + +use pingora_core::server::configuration::Opt; +use pingora_core::server::Server; +use pingora_core::upstreams::peer::HttpPeer; +use pingora_core::Result; +use pingora_load_balancing::{health_check, selection::RoundRobin, LoadBalancer}; +use pingora_proxy::{ProxyHttp, Session}; + +pub struct LB(Arc>); + +#[async_trait] +impl ProxyHttp for LB { + type CTX = (); + fn new_ctx(&self) -> Self::CTX {} + + async fn upstream_peer(&self, _session: &mut Session, _ctx: &mut ()) -> Result> { + let upstream = self + .0 + .select(b"", 256) // hash doesn't matter + .unwrap(); + + info!("upstream peer is: {:?}", upstream); + + let peer = Box::new(HttpPeer::new(upstream, true, "one.one.one.one".to_string())); + Ok(peer) + } + + async fn upstream_request_filter( + &self, + _session: &mut Session, + upstream_request: &mut pingora_http::RequestHeader, + _ctx: &mut Self::CTX, + ) -> Result<()> { + upstream_request + .insert_header("Host", "one.one.one.one") + .unwrap(); + Ok(()) + } +} + +// RUST_LOG=INFO cargo run --example load_balancer +fn main() { + env_logger::init(); + + // read command line arguments + let opt = Opt::parse(); + let mut my_server = Server::new(Some(opt)).unwrap(); + my_server.bootstrap(); + + // 127.0.0.1:343" is just a bad server + let mut upstreams = + LoadBalancer::try_from_iter(["1.1.1.1:443", "1.0.0.1:443", "127.0.0.1:343"]).unwrap(); + + // We add health check in the background so that the bad server is never selected. + let hc = health_check::TcpHealthCheck::new(); + upstreams.set_health_check(hc); + upstreams.health_check_frequency = Some(Duration::from_secs(1)); + + let background = background_service("health check", upstreams); + + let upstreams = background.task(); + + let mut lb = pingora_proxy::http_proxy_service(&my_server.configuration, LB(upstreams)); + lb.add_tcp("0.0.0.0:6188"); + + let cert_path = format!("{}/tests/keys/server.crt", env!("CARGO_MANIFEST_DIR")); + let key_path = format!("{}/tests/keys/key.pem", env!("CARGO_MANIFEST_DIR")); + + let mut tls_settings = + pingora_core::listeners::tls::TlsSettings::intermediate(&cert_path, &key_path).unwrap(); + tls_settings.enable_h2(); + lb.add_tls_with_settings("0.0.0.0:6189", None, tls_settings); + + my_server.add_service(lb); + my_server.add_service(background); + my_server.run_forever(); +} diff --git a/pingora-proxy/examples/modify_response.rs b/pingora-proxy/examples/modify_response.rs index 0be9edbe..6730f718 100644 --- a/pingora-proxy/examples/modify_response.rs +++ b/pingora-proxy/examples/modify_response.rs @@ -1,11 +1,28 @@ +// Copyright 2024 Cloudflare, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + use async_trait::async_trait; use bytes::Bytes; +use clap::Parser; use serde::{Deserialize, Serialize}; +use std::net::ToSocketAddrs; +use pingora_core::server::configuration::Opt; use pingora_core::server::Server; use pingora_core::upstreams::peer::HttpPeer; use pingora_core::Result; -use pingora_http::{RequestHeader, ResponseHeader}; +use pingora_http::ResponseHeader; use pingora_proxy::{ProxyHttp, Session}; const HOST: &str = "ip.jsontest.com"; @@ -26,7 +43,6 @@ pub struct MyCtx { #[async_trait] impl ProxyHttp for Json2Yaml { type CTX = MyCtx; - fn new_ctx(&self) -> Self::CTX { MyCtx { buffer: vec![] } } @@ -43,11 +59,11 @@ impl ProxyHttp for Json2Yaml { async fn upstream_request_filter( &self, _session: &mut Session, - upstream_request: &mut RequestHeader, + upstream_request: &mut pingora_http::RequestHeader, _ctx: &mut Self::CTX, ) -> Result<()> { upstream_request - .insert_header("Host", HOST) + .insert_header("Host", HOST.to_owned()) .unwrap(); Ok(()) } @@ -57,11 +73,14 @@ impl ProxyHttp for Json2Yaml { _session: &mut Session, upstream_response: &mut ResponseHeader, _ctx: &mut Self::CTX, - ) -> Result<()> { - // Remove Content-Length because the size of the new body is unknown + ) -> Result<()> + where + Self::CTX: Send + Sync, + { + // Remove content-length because the size of the new body is unknown upstream_response.remove_header("Content-Length"); upstream_response - .insert_header("Transfer-Encoding", "chunked") + .insert_header("Transfer-Encoding", "Chunked") .unwrap(); Ok(()) } @@ -76,14 +95,15 @@ impl ProxyHttp for Json2Yaml { where Self::CTX: Send + Sync, { - // Buffer the data - if let Some(b) = body.take() { - ctx.buffer.extend_from_slice(&b); + // buffer the data + if let Some(b) = body { + ctx.buffer.extend(&b[..]); + // drop the body + b.clear(); } - if end_of_stream { - // This is the last chunk; we can process the data now - let json_body: Resp = serde_json::from_slice(&ctx.buffer).unwrap(); + // This is the last chunk, we can process the data now + let json_body: Resp = serde_json::de::from_slice(&ctx.buffer).unwrap(); let yaml_body = serde_yaml::to_string(&json_body).unwrap(); *body = Some(Bytes::copy_from_slice(yaml_body.as_bytes())); } @@ -92,16 +112,24 @@ impl ProxyHttp for Json2Yaml { } } +// RUST_LOG=INFO cargo run --example modify_response +// curl 127.0.0.1:6191 fn main() { env_logger::init(); - let mut my_server = Server::new(None).unwrap(); + let opt = Opt::parse(); + let mut my_server = Server::new(Some(opt)).unwrap(); my_server.bootstrap(); let mut my_proxy = pingora_proxy::http_proxy_service( &my_server.configuration, Json2Yaml { - addr: "142.251.2.121:80".parse().unwrap(), + // hardcode the IP of ip.jsontest.com for now + addr: ("142.251.2.121", 80) + .to_socket_addrs() + .unwrap() + .next() + .unwrap(), }, ); diff --git a/pingora-proxy/examples/multi_lb.rs b/pingora-proxy/examples/multi_lb.rs index 599e71b6..1321c207 100644 --- a/pingora-proxy/examples/multi_lb.rs +++ b/pingora-proxy/examples/multi_lb.rs @@ -1,8 +1,21 @@ +// Copyright 2024 Cloudflare, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + use async_trait::async_trait; use std::sync::Arc; use pingora_core::{prelude::*, services::background::GenBackgroundService}; -use pingora_core::upstreams::peer::HttpPeer; use pingora_load_balancing::{ health_check::TcpHealthCheck, selection::{BackendIter, BackendSelection, RoundRobin}, @@ -18,19 +31,10 @@ struct Router { #[async_trait] impl ProxyHttp for Router { type CTX = (); + fn new_ctx(&self) {} - // Corrected the `new_ctx` method to return `Self::CTX` - fn new_ctx(&self) -> Self::CTX { - () - } - - // Updated the return type to `Result>` - async fn upstream_peer( - &self, - session: &mut Session, - _ctx: &mut Self::CTX, - ) -> Result> { - // Determine LB cluster based on request URI + async fn upstream_peer(&self, session: &mut Session, _ctx: &mut ()) -> Result> { + // determine LB cluster based on request uri let cluster = if session.req_header().uri.path().starts_with("/one/") { &self.cluster_one } else { @@ -38,17 +42,13 @@ impl ProxyHttp for Router { }; let upstream = cluster - .select(b"", 256) // Hash doesn't matter for round robin + .select(b"", 256) // hash doesn't matter for round robin .unwrap(); - println!("upstream peer is: {:?}", upstream); + println!("upstream peer is: {upstream:?}"); - // Create a new HttpPeer instance and wrap it in a Box - let peer = Box::new(HttpPeer::new( - upstream, - true, - "one.one.one.one".to_string(), - )); + // Set SNI to one.one.one.one + let peer = Box::new(HttpPeer::new(upstream, true, "one.one.one.one".to_string())); Ok(peer) } } @@ -72,7 +72,7 @@ fn main() { let mut my_server = Server::new(None).unwrap(); my_server.bootstrap(); - // Build multiple clusters + // build multiple clusters let cluster_one = build_cluster_service::(&["1.1.1.1:443", "127.0.0.1:343"]); let cluster_two = build_cluster_service::(&["1.0.0.1:443", "127.0.0.2:343"]); diff --git a/pingora-proxy/examples/rate_limiter.rs b/pingora-proxy/examples/rate_limiter.rs index 14e3160c..d2c8b7ec 100644 --- a/pingora-proxy/examples/rate_limiter.rs +++ b/pingora-proxy/examples/rate_limiter.rs @@ -10,27 +10,21 @@ use std::sync::Arc; use std::time::Duration; fn main() { - env_logger::init(); - - let mut server = Server::new(None).unwrap(); + let mut server = Server::new(Some(Opt::default())).unwrap(); server.bootstrap(); - - let mut upstreams = - LoadBalancer::try_from_iter(["1.1.1.1:443", "1.0.0.1:443"]).unwrap(); - + let mut upstreams = LoadBalancer::try_from_iter(["1.1.1.1:443", "1.0.0.1:443"]).unwrap(); // Set health check let hc = TcpHealthCheck::new(); upstreams.set_health_check(hc); upstreams.health_check_frequency = Some(Duration::from_secs(1)); - // Set background service let background = background_service("health check", upstreams); let upstreams = background.task(); - // Set load balancer let mut lb = http_proxy_service(&server.configuration, LB(upstreams)); lb.add_tcp("0.0.0.0:6188"); + // let rate = Rate server.add_service(background); server.add_service(lb); server.run_forever(); @@ -39,50 +33,78 @@ fn main() { pub struct LB(Arc>); impl LB { - pub fn get_request_appid(&self, session: &Session) -> Option { - session + pub fn get_request_appid(&self, session: &mut Session) -> Option { + match session .req_header() .headers .get("appid") - .and_then(|v| v.to_str().ok().map(|s| s.to_string())) + .map(|v| v.to_str()) + { + None => None, + Some(v) => match v { + Ok(v) => Some(v.to_string()), + Err(_) => None, + }, + } } } // Rate limiter static RATE_LIMITER: Lazy = Lazy::new(|| Rate::new(Duration::from_secs(1))); -// Max requests per second per client -const MAX_REQ_PER_SEC: isize = 1; +// max request per second per client +static MAX_REQ_PER_SEC: isize = 1; #[async_trait] impl ProxyHttp for LB { type CTX = (); - fn new_ctx(&self) -> Self::CTX { - () + fn new_ctx(&self) {} + + async fn upstream_peer( + &self, + _session: &mut Session, + _ctx: &mut Self::CTX, + ) -> Result> { + let upstream = self.0.select(b"", 256).unwrap(); + // Set SNI + let peer = Box::new(HttpPeer::new(upstream, true, "one.one.one.one".to_string())); + Ok(peer) } - async fn request_filter( + async fn upstream_request_filter( &self, - session: &mut Session, + _session: &mut Session, + upstream_request: &mut RequestHeader, _ctx: &mut Self::CTX, - ) -> Result { + ) -> Result<()> + where + Self::CTX: Send + Sync, + { + upstream_request + .insert_header("Host", "one.one.one.one") + .unwrap(); + Ok(()) + } + + async fn request_filter(&self, session: &mut Session, _ctx: &mut Self::CTX) -> Result + where + Self::CTX: Send + Sync, + { let appid = match self.get_request_appid(session) { - None => return Ok(false), // No client appid found, skip rate limiting - Some(appid) => appid, + None => return Ok(false), // no client appid found, skip rate limiting + Some(addr) => addr, }; - // Retrieve the current window requests + // retrieve the current window requests let curr_window_requests = RATE_LIMITER.observe(&appid, 1); if curr_window_requests > MAX_REQ_PER_SEC { - // Rate limited, return 429 + // rate limited, return 429 let mut header = ResponseHeader::build(429, None).unwrap(); header .insert_header("X-Rate-Limit-Limit", MAX_REQ_PER_SEC.to_string()) .unwrap(); - header - .insert_header("X-Rate-Limit-Remaining", "0") - .unwrap(); + header.insert_header("X-Rate-Limit-Remaining", "0").unwrap(); header.insert_header("X-Rate-Limit-Reset", "1").unwrap(); session.set_keepalive(None); session @@ -92,31 +114,4 @@ impl ProxyHttp for LB { } Ok(false) } - - async fn upstream_peer( - &self, - _session: &mut Session, - _ctx: &mut Self::CTX, - ) -> Result> { - let upstream = self.0.select(b"", 256).unwrap(); - // Set SNI - let peer = Box::new(HttpPeer::new( - upstream, - true, - "one.one.one.one".to_string(), - )); - Ok(peer) - } - - async fn upstream_request_filter( - &self, - _session: &mut Session, - upstream_request: &mut RequestHeader, - _ctx: &mut Self::CTX, - ) -> Result<()> { - upstream_request - .insert_header("Host", "one.one.one.one") - .unwrap(); - Ok(()) - } } diff --git a/pingora-proxy/examples/use_module.rs b/pingora-proxy/examples/use_module.rs index 38814733..4404768f 100644 --- a/pingora-proxy/examples/use_module.rs +++ b/pingora-proxy/examples/use_module.rs @@ -1,12 +1,27 @@ +// Copyright 2024 Cloudflare, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + use async_trait::async_trait; +use clap::Parser; use pingora_core::modules::http::HttpModules; +use pingora_core::server::configuration::Opt; use pingora_core::server::Server; use pingora_core::upstreams::peer::HttpPeer; use pingora_core::Result; use pingora_http::RequestHeader; use pingora_proxy::{ProxyHttp, Session}; -use std::any::Any; /// This example shows how to build and import 3rd party modules @@ -14,10 +29,11 @@ use std::any::Any; mod my_acl { use super::*; use pingora_core::modules::http::{HttpModule, HttpModuleBuilder, Module}; - use pingora_error::{Error, ErrorType}; // Import Error and ErrorType here + use pingora_error::{Error, ErrorType::HTTPStatus}; + use std::any::Any; - // This is the struct for per-request module context - pub struct MyAclCtx { + // This is the struct for per request module context + struct MyAclCtx { credential_header: String, } @@ -25,24 +41,18 @@ mod my_acl { #[async_trait] impl HttpModule for MyAclCtx { async fn request_header_filter(&mut self, req: &mut RequestHeader) -> Result<()> { - if let Some(auth) = req.headers.get("Authorization") { - if auth.as_bytes() != self.credential_header.as_bytes() { - return Error::e_explain( - ErrorType::HTTPStatus(403), - "Auth failed, credential mismatch", - ); - } else { - Ok(()) - } + let Some(auth) = req.headers.get(http::header::AUTHORIZATION) else { + return Error::e_explain(HTTPStatus(403), "Auth failed, no auth header"); + }; + + if auth.as_bytes() != self.credential_header.as_bytes() { + Error::e_explain(HTTPStatus(403), "Auth failed, credential mismatch") } else { - Error::e_explain( - ErrorType::HTTPStatus(403), - "Auth failed, no auth header", - ) + Ok(()) } } - // Boilerplate code for all modules + // boilerplate code for all modules fn as_any(&self) -> &dyn Any { self } @@ -55,13 +65,13 @@ mod my_acl { pub struct MyAcl { pub credential: String, } - impl HttpModuleBuilder for MyAcl { - // This function defines how to create each context. This function is called when a new request arrives + // This function defines how to create each Ctx. This function is called when a new request + // arrives fn init(&self) -> Module { Box::new(MyAclCtx { // Make it easier to compare header - // We could also store this value in MyAcl and use Arc to share it with every context. + // We could also store this value in MyAcl and use Arc to share it with every Ctx. credential_header: format!("basic {}", self.credential), }) } @@ -73,17 +83,14 @@ pub struct MyProxy; #[async_trait] impl ProxyHttp for MyProxy { type CTX = (); - - fn new_ctx(&self) -> Self::CTX { - () - } + fn new_ctx(&self) -> Self::CTX {} // This function is only called once when the server starts fn init_downstream_modules(&self, modules: &mut HttpModules) { // Add the module to MyProxy modules.add_module(Box::new(my_acl::MyAcl { credential: "testcode".into(), - })); + })) } async fn upstream_peer( @@ -100,7 +107,6 @@ impl ProxyHttp for MyProxy { } } -// To run the example: // RUST_LOG=INFO cargo run --example use_module // curl 127.0.0.1:6193 -H "Host: one.one.one.one" -v // curl 127.0.0.1:6193 -H "Host: one.one.one.one" -H "Authorization: basic testcode" @@ -108,8 +114,9 @@ impl ProxyHttp for MyProxy { fn main() { env_logger::init(); - // Create the server without command line arguments - let mut my_server = Server::new(None).unwrap(); + // read command line arguments + let opt = Opt::parse(); + let mut my_server = Server::new(Some(opt)).unwrap(); my_server.bootstrap(); let mut my_proxy = pingora_proxy::http_proxy_service(&my_server.configuration, MyProxy); diff --git a/pingora_tutorial/src/ctx.rs b/pingora_tutorial/src/ctx.rs new file mode 100644 index 00000000..fd282c92 --- /dev/null +++ b/pingora_tutorial/src/ctx.rs @@ -0,0 +1,91 @@ +use async_trait::async_trait; +use log::info; +use std::sync::atomic::{AtomicUsize, Ordering}; + +use pingora_core::server::configuration::Opt; +use pingora_core::server::Server; +use pingora_core::upstreams::peer::HttpPeer; +use pingora_core::Result; +use pingora_http::RequestHeader; +use pingora_proxy::{ProxyHttp, Session}; + +/// Global request counter using `AtomicUsize` for thread-safe atomic operations +static REQ_COUNTER: AtomicUsize = AtomicUsize::new(0); + +pub struct MyProxy { + /// Counter for beta users + beta_counter: AtomicUsize, +} + +pub struct MyCtx { + beta_user: bool, +} + +fn check_beta_user(req: &RequestHeader) -> bool { + // Simple logic to check if user is beta + req.headers.get("beta-flag").is_some() +} + +#[async_trait] +impl ProxyHttp for MyProxy { + type CTX = MyCtx; + + fn new_ctx(&self) -> Self::CTX { + MyCtx { beta_user: false } + } + + async fn request_filter(&self, session: &mut Session, ctx: &mut Self::CTX) -> Result { + ctx.beta_user = check_beta_user(session.req_header()); + Ok(false) // Continue processing the request + } + + async fn upstream_peer( + &self, + _session: &mut Session, + ctx: &mut Self::CTX, + ) -> Result> { + // Increment the global request counter atomically + let req_counter = REQ_COUNTER.fetch_add(1, Ordering::Relaxed) + 1; + + let addr = if ctx.beta_user { + // Increment the beta user counter atomically + let beta_count = self.beta_counter.fetch_add(1, Ordering::Relaxed) + 1; + info!("I'm a beta user #{beta_count}"); + ("1.0.0.1", 443) + } else { + info!("I'm a user #{req_counter}"); + ("1.1.1.1", 443) + }; + + let peer = Box::new(HttpPeer::new( + addr, + true, + "one.one.one.one".to_string(), + )); + Ok(peer) + } +} + +// To run the example: +// RUST_LOG=INFO cargo run --example ctx +// curl 127.0.0.1:6190 -H "Host: one.one.one.one" +// curl 127.0.0.1:6190 -H "Host: one.one.one.one" -H "beta-flag: 1" +fn main() { + env_logger::init(); + + // Read command line arguments + let opt = Opt::parse_args(); + let mut my_server = Server::new(Some(opt)).unwrap(); + my_server.bootstrap(); + + let mut my_proxy = pingora_proxy::http_proxy_service( + &my_server.configuration, + MyProxy { + beta_counter: AtomicUsize::new(0), + }, + ); + my_proxy.add_tcp("0.0.0.0:6190"); + + my_server.add_service(my_proxy); + my_server.run_forever(); +} diff --git a/pingora_tutorial/src/gateway.rs b/pingora_tutorial/src/gateway.rs new file mode 100644 index 00000000..4bbb5c72 --- /dev/null +++ b/pingora_tutorial/src/gateway.rs @@ -0,0 +1,121 @@ +use async_trait::async_trait; +use log::info; +use prometheus::{IntCounter, register_int_counter}; + +use pingora_core::server::Server; +use pingora_core::upstreams::peer::HttpPeer; +use pingora_core::Result; +use pingora_http::{RequestHeader, ResponseHeader}; +use pingora_proxy::{ProxyHttp, Session}; + +fn check_login(req: &RequestHeader) -> bool { + // Implement your login check logic here + req.headers.get("Authorization") + .map(|v| v.as_bytes() == b"password") + .unwrap_or(false) +} + +pub struct MyGateway { + req_metric: IntCounter, +} + +#[async_trait] +impl ProxyHttp for MyGateway { + type CTX = (); + + fn new_ctx(&self) -> Self::CTX { + () + } + + async fn request_filter(&self, session: &mut Session, _ctx: &mut Self::CTX) -> Result { + if session.req_header().uri.path().starts_with("/login") + && !check_login(session.req_header()) + { + let _ = session.respond_error(403).await; + // Return true to indicate early response + return Ok(true); + } + Ok(false) + } + + async fn upstream_peer( + &self, + session: &mut Session, + _ctx: &mut Self::CTX, + ) -> Result> { + let addr = if session.req_header().uri.path().starts_with("/family") { + ("1.0.0.1", 443) + } else { + ("1.1.1.1", 443) + }; + + info!("Connecting to {:?}", addr); + + let peer = Box::new(HttpPeer::new( + addr, + true, + "one.one.one.one".to_string(), + )); + Ok(peer) + } + + async fn response_filter( + &self, + _session: &mut Session, + upstream_response: &mut ResponseHeader, + _ctx: &mut Self::CTX, + ) -> Result<()> { + // Replace existing header if any + upstream_response + .insert_header("Server", "MyGateway") + .unwrap(); + // Remove unsupported header + upstream_response.remove_header("alt-svc"); + + Ok(()) + } + + async fn logging( + &self, + session: &mut Session, + _e: Option<&pingora_core::Error>, + _ctx: &mut Self::CTX, + ) { + let response_code = session + .response_written() + .map_or(0, |resp| resp.status.as_u16()); + info!( + "Request to {} responded with status code {}", + session.req_header().uri.path(), + response_code + ); + + self.req_metric.inc(); + } +} + +fn main() { + env_logger::init(); + + // Create the server without options + let mut my_server = Server::new(None).unwrap(); + my_server.bootstrap(); + + let req_metric = register_int_counter!("req_counter", "Number of requests").unwrap(); + + let mut my_proxy = pingora_proxy::http_proxy_service( + &my_server.configuration, + MyGateway { + req_metric, + }, + ); + my_proxy.add_tcp("0.0.0.0:6191"); + my_server.add_service(my_proxy); + + let mut prometheus_service_http = + pingora_core::services::listening::Service::prometheus_http_service(); + prometheus_service_http.add_tcp("127.0.0.1:6192"); + my_server.add_service(prometheus_service_http); + + my_server.run_forever(); +} diff --git a/pingora-proxy/examples/grpc_web_modules.rs b/pingora_tutorial/src/grpc_web_modules.rs similarity index 100% rename from pingora-proxy/examples/grpc_web_modules.rs rename to pingora_tutorial/src/grpc_web_modules.rs diff --git a/pingora-proxy/examples/load_loadbalancer.rs b/pingora_tutorial/src/load_loadbalancer.rs similarity index 100% rename from pingora-proxy/examples/load_loadbalancer.rs rename to pingora_tutorial/src/load_loadbalancer.rs diff --git a/pingora_tutorial/src/modify_response.rs b/pingora_tutorial/src/modify_response.rs new file mode 100644 index 00000000..0be9edbe --- /dev/null +++ b/pingora_tutorial/src/modify_response.rs @@ -0,0 +1,112 @@ +use async_trait::async_trait; +use bytes::Bytes; +use serde::{Deserialize, Serialize}; + +use pingora_core::server::Server; +use pingora_core::upstreams::peer::HttpPeer; +use pingora_core::Result; +use pingora_http::{RequestHeader, ResponseHeader}; +use pingora_proxy::{ProxyHttp, Session}; + +const HOST: &str = "ip.jsontest.com"; + +#[derive(Serialize, Deserialize)] +pub struct Resp { + ip: String, +} + +pub struct Json2Yaml { + addr: std::net::SocketAddr, +} + +pub struct MyCtx { + buffer: Vec, +} + +#[async_trait] +impl ProxyHttp for Json2Yaml { + type CTX = MyCtx; + + fn new_ctx(&self) -> Self::CTX { + MyCtx { buffer: vec![] } + } + + async fn upstream_peer( + &self, + _session: &mut Session, + _ctx: &mut Self::CTX, + ) -> Result> { + let peer = Box::new(HttpPeer::new(self.addr, false, HOST.to_owned())); + Ok(peer) + } + + async fn upstream_request_filter( + &self, + _session: &mut Session, + upstream_request: &mut RequestHeader, + _ctx: &mut Self::CTX, + ) -> Result<()> { + upstream_request + .insert_header("Host", HOST) + .unwrap(); + Ok(()) + } + + async fn response_filter( + &self, + _session: &mut Session, + upstream_response: &mut ResponseHeader, + _ctx: &mut Self::CTX, + ) -> Result<()> { + // Remove Content-Length because the size of the new body is unknown + upstream_response.remove_header("Content-Length"); + upstream_response + .insert_header("Transfer-Encoding", "chunked") + .unwrap(); + Ok(()) + } + + fn response_body_filter( + &self, + _session: &mut Session, + body: &mut Option, + end_of_stream: bool, + ctx: &mut Self::CTX, + ) -> Result> + where + Self::CTX: Send + Sync, + { + // Buffer the data + if let Some(b) = body.take() { + ctx.buffer.extend_from_slice(&b); + } + + if end_of_stream { + // This is the last chunk; we can process the data now + let json_body: Resp = serde_json::from_slice(&ctx.buffer).unwrap(); + let yaml_body = serde_yaml::to_string(&json_body).unwrap(); + *body = Some(Bytes::copy_from_slice(yaml_body.as_bytes())); + } + + Ok(None) + } +} + +fn main() { + env_logger::init(); + + let mut my_server = Server::new(None).unwrap(); + my_server.bootstrap(); + + let mut my_proxy = pingora_proxy::http_proxy_service( + &my_server.configuration, + Json2Yaml { + addr: "142.251.2.121:80".parse().unwrap(), + }, + ); + + my_proxy.add_tcp("127.0.0.1:6191"); + + my_server.add_service(my_proxy); + my_server.run_forever(); +} diff --git a/pingora_tutorial/src/multi_lb.rs b/pingora_tutorial/src/multi_lb.rs new file mode 100644 index 00000000..599e71b6 --- /dev/null +++ b/pingora_tutorial/src/multi_lb.rs @@ -0,0 +1,91 @@ +use async_trait::async_trait; +use std::sync::Arc; + +use pingora_core::{prelude::*, services::background::GenBackgroundService}; +use pingora_core::upstreams::peer::HttpPeer; +use pingora_load_balancing::{ + health_check::TcpHealthCheck, + selection::{BackendIter, BackendSelection, RoundRobin}, + LoadBalancer, +}; +use pingora_proxy::{http_proxy_service, ProxyHttp, Session}; + +struct Router { + cluster_one: Arc>, + cluster_two: Arc>, +} + +#[async_trait] +impl ProxyHttp for Router { + type CTX = (); + + // Corrected the `new_ctx` method to return `Self::CTX` + fn new_ctx(&self) -> Self::CTX { + () + } + + // Updated the return type to `Result>` + async fn upstream_peer( + &self, + session: &mut Session, + _ctx: &mut Self::CTX, + ) -> Result> { + // Determine LB cluster based on request URI + let cluster = if session.req_header().uri.path().starts_with("/one/") { + &self.cluster_one + } else { + &self.cluster_two + }; + + let upstream = cluster + .select(b"", 256) // Hash doesn't matter for round robin + .unwrap(); + + println!("upstream peer is: {:?}", upstream); + + // Create a new HttpPeer instance and wrap it in a Box + let peer = Box::new(HttpPeer::new( + upstream, + true, + "one.one.one.one".to_string(), + )); + Ok(peer) + } +} + +fn build_cluster_service(upstreams: &[&str]) -> GenBackgroundService> +where + S: BackendSelection + 'static, + S::Iter: BackendIter, +{ + let mut cluster = LoadBalancer::try_from_iter(upstreams).unwrap(); + cluster.set_health_check(TcpHealthCheck::new()); + cluster.health_check_frequency = Some(std::time::Duration::from_secs(1)); + + background_service("cluster health check", cluster) +} + +// RUST_LOG=INFO cargo run --example multi_lb +// curl 127.0.0.1:6188/one/ +// curl 127.0.0.1:6188/two/ +fn main() { + let mut my_server = Server::new(None).unwrap(); + my_server.bootstrap(); + + // Build multiple clusters + let cluster_one = build_cluster_service::(&["1.1.1.1:443", "127.0.0.1:343"]); + let cluster_two = build_cluster_service::(&["1.0.0.1:443", "127.0.0.2:343"]); + + let router = Router { + cluster_one: cluster_one.task(), + cluster_two: cluster_two.task(), + }; + let mut router_service = http_proxy_service(&my_server.configuration, router); + router_service.add_tcp("0.0.0.0:6188"); + + my_server.add_service(router_service); + my_server.add_service(cluster_one); + my_server.add_service(cluster_two); + + my_server.run_forever(); +} diff --git a/pingora_tutorial/src/rate_limiter.rs b/pingora_tutorial/src/rate_limiter.rs new file mode 100644 index 00000000..14e3160c --- /dev/null +++ b/pingora_tutorial/src/rate_limiter.rs @@ -0,0 +1,122 @@ +use async_trait::async_trait; +use once_cell::sync::Lazy; +use pingora_core::prelude::*; +use pingora_http::{RequestHeader, ResponseHeader}; +use pingora_limits::rate::Rate; +use pingora_load_balancing::prelude::{RoundRobin, TcpHealthCheck}; +use pingora_load_balancing::LoadBalancer; +use pingora_proxy::{http_proxy_service, ProxyHttp, Session}; +use std::sync::Arc; +use std::time::Duration; + +fn main() { + env_logger::init(); + + let mut server = Server::new(None).unwrap(); + server.bootstrap(); + + let mut upstreams = + LoadBalancer::try_from_iter(["1.1.1.1:443", "1.0.0.1:443"]).unwrap(); + + // Set health check + let hc = TcpHealthCheck::new(); + upstreams.set_health_check(hc); + upstreams.health_check_frequency = Some(Duration::from_secs(1)); + + // Set background service + let background = background_service("health check", upstreams); + let upstreams = background.task(); + + // Set load balancer + let mut lb = http_proxy_service(&server.configuration, LB(upstreams)); + lb.add_tcp("0.0.0.0:6188"); + + server.add_service(background); + server.add_service(lb); + server.run_forever(); +} + +pub struct LB(Arc>); + +impl LB { + pub fn get_request_appid(&self, session: &Session) -> Option { + session + .req_header() + .headers + .get("appid") + .and_then(|v| v.to_str().ok().map(|s| s.to_string())) + } +} + +// Rate limiter +static RATE_LIMITER: Lazy = Lazy::new(|| Rate::new(Duration::from_secs(1))); + +// Max requests per second per client +const MAX_REQ_PER_SEC: isize = 1; + +#[async_trait] +impl ProxyHttp for LB { + type CTX = (); + + fn new_ctx(&self) -> Self::CTX { + () + } + + async fn request_filter( + &self, + session: &mut Session, + _ctx: &mut Self::CTX, + ) -> Result { + let appid = match self.get_request_appid(session) { + None => return Ok(false), // No client appid found, skip rate limiting + Some(appid) => appid, + }; + + // Retrieve the current window requests + let curr_window_requests = RATE_LIMITER.observe(&appid, 1); + if curr_window_requests > MAX_REQ_PER_SEC { + // Rate limited, return 429 + let mut header = ResponseHeader::build(429, None).unwrap(); + header + .insert_header("X-Rate-Limit-Limit", MAX_REQ_PER_SEC.to_string()) + .unwrap(); + header + .insert_header("X-Rate-Limit-Remaining", "0") + .unwrap(); + header.insert_header("X-Rate-Limit-Reset", "1").unwrap(); + session.set_keepalive(None); + session + .write_response_header(Box::new(header), true) + .await?; + return Ok(true); + } + Ok(false) + } + + async fn upstream_peer( + &self, + _session: &mut Session, + _ctx: &mut Self::CTX, + ) -> Result> { + let upstream = self.0.select(b"", 256).unwrap(); + // Set SNI + let peer = Box::new(HttpPeer::new( + upstream, + true, + "one.one.one.one".to_string(), + )); + Ok(peer) + } + + async fn upstream_request_filter( + &self, + _session: &mut Session, + upstream_request: &mut RequestHeader, + _ctx: &mut Self::CTX, + ) -> Result<()> { + upstream_request + .insert_header("Host", "one.one.one.one") + .unwrap(); + Ok(()) + } +} diff --git a/pingora-proxy/examples/test_server.rs b/pingora_tutorial/src/test_server.rs similarity index 100% rename from pingora-proxy/examples/test_server.rs rename to pingora_tutorial/src/test_server.rs diff --git a/pingora_tutorial/src/use_module.rs b/pingora_tutorial/src/use_module.rs new file mode 100644 index 00000000..38814733 --- /dev/null +++ b/pingora_tutorial/src/use_module.rs @@ -0,0 +1,120 @@ +use async_trait::async_trait; + +use pingora_core::modules::http::HttpModules; +use pingora_core::server::Server; +use pingora_core::upstreams::peer::HttpPeer; +use pingora_core::Result; +use pingora_http::RequestHeader; +use pingora_proxy::{ProxyHttp, Session}; +use std::any::Any; + +/// This example shows how to build and import 3rd party modules + +/// A simple ACL to check "Authorization: basic $credential" header +mod my_acl { + use super::*; + use pingora_core::modules::http::{HttpModule, HttpModuleBuilder, Module}; + use pingora_error::{Error, ErrorType}; // Import Error and ErrorType here + + // This is the struct for per-request module context + pub struct MyAclCtx { + credential_header: String, + } + + // Implement how the module would consume and/or modify request and/or response + #[async_trait] + impl HttpModule for MyAclCtx { + async fn request_header_filter(&mut self, req: &mut RequestHeader) -> Result<()> { + if let Some(auth) = req.headers.get("Authorization") { + if auth.as_bytes() != self.credential_header.as_bytes() { + return Error::e_explain( + ErrorType::HTTPStatus(403), + "Auth failed, credential mismatch", + ); + } else { + Ok(()) + } + } else { + Error::e_explain( + ErrorType::HTTPStatus(403), + "Auth failed, no auth header", + ) + } + } + + // Boilerplate code for all modules + fn as_any(&self) -> &dyn Any { + self + } + fn as_any_mut(&mut self) -> &mut dyn Any { + self + } + } + + // This is the singleton object which will be attached to the server + pub struct MyAcl { + pub credential: String, + } + + impl HttpModuleBuilder for MyAcl { + // This function defines how to create each context. This function is called when a new request arrives + fn init(&self) -> Module { + Box::new(MyAclCtx { + // Make it easier to compare header + // We could also store this value in MyAcl and use Arc to share it with every context. + credential_header: format!("basic {}", self.credential), + }) + } + } +} + +pub struct MyProxy; + +#[async_trait] +impl ProxyHttp for MyProxy { + type CTX = (); + + fn new_ctx(&self) -> Self::CTX { + () + } + + // This function is only called once when the server starts + fn init_downstream_modules(&self, modules: &mut HttpModules) { + // Add the module to MyProxy + modules.add_module(Box::new(my_acl::MyAcl { + credential: "testcode".into(), + })); + } + + async fn upstream_peer( + &self, + _session: &mut Session, + _ctx: &mut Self::CTX, + ) -> Result> { + let peer = Box::new(HttpPeer::new( + ("1.1.1.1", 443), + true, + "one.one.one.one".to_string(), + )); + Ok(peer) + } +} + +// To run the example: +// RUST_LOG=INFO cargo run --example use_module +// curl 127.0.0.1:6193 -H "Host: one.one.one.one" -v +// curl 127.0.0.1:6193 -H "Host: one.one.one.one" -H "Authorization: basic testcode" +// curl 127.0.0.1:6193 -H "Host: one.one.one.one" -H "Authorization: basic wrong" -v +fn main() { + env_logger::init(); + + // Create the server without command line arguments + let mut my_server = Server::new(None).unwrap(); + my_server.bootstrap(); + + let mut my_proxy = pingora_proxy::http_proxy_service(&my_server.configuration, MyProxy); + my_proxy.add_tcp("0.0.0.0:6193"); + + my_server.add_service(my_proxy); + my_server.run_forever(); +} diff --git a/pingora-proxy/examples/start.md b/pingora_tutorial/start.md similarity index 89% rename from pingora-proxy/examples/start.md rename to pingora_tutorial/start.md index ffb8805a..da194601 100644 --- a/pingora-proxy/examples/start.md +++ b/pingora_tutorial/start.md @@ -1,13 +1,19 @@ ### Pingora Tutorial +The Cargo.lock and Cargo.toml are provided in the Pingora crate if you so choose to use them. Both have been labeled with the prefix of `tutorial_` so you will need to change their names before starting this tutorial. Having that will prevent you from needing to cargo add any crates that may be required to start up any of this code. If you don't want to use that then we will also make sure to point out the exact imports that we're using in each section. + +So with that out of the way, lets begin our Pingora tutorial. + ## Introduction -Before we begin building a full proxy server, we need a simple server to test and demonstrate our proxy’s functionality. In this tutorial, we’ll start by setting up a basic test web server in Rust that will display a simple message when accessed. This will help us verify that our proxy server is routing requests correctly. -Step 1: Start the Test Web Server +Before we begin building a full proxy server, we need a simple server to test and demonstrate our proxy’s functionality. In this tutorial, we’ll start by setting up a basic test web server in Rust that will display a simple message when accessed. This will help us verify that our proxy server is routing requests correctly. The server has alreadydy been built in it's entirety inside of the pingora_tutorial/src/test_server.rs, but this tutorial will explain the code inside that folder so you better understand it and are able to run or modify the code as you see fit. -## Step 1 creating the server +Thank you for using pingora + +## Step 1: Start the Test Web Server To begin, let’s create a basic server function that listens on a specified address and handles incoming client connections. This server will allow us to test the future code we’ll write for the proxy server. + ```rust use std::io::{Read, Write}; use std::net::{TcpListener, TcpStream}; diff --git a/pingora-proxy/examples/example_Cargo.lock b/pingora_tutorial/tutorial_Cargo.lock similarity index 100% rename from pingora-proxy/examples/example_Cargo.lock rename to pingora_tutorial/tutorial_Cargo.lock diff --git a/pingora-proxy/examples/example_Cargo.toml b/pingora_tutorial/tutorial_Cargo.toml similarity index 100% rename from pingora-proxy/examples/example_Cargo.toml rename to pingora_tutorial/tutorial_Cargo.toml From 4d79fbade6f4bbbba47cf41ab9446cce3fb93cac Mon Sep 17 00:00:00 2001 From: HasturDev Date: Sat, 9 Nov 2024 19:50:29 -0500 Subject: [PATCH 4/6] feature/full-tutorial cleaned 1 Billion mistakes --- pingora_tutorial/start.md | 74 +++++++++++++++++++-------------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/pingora_tutorial/start.md b/pingora_tutorial/start.md index da194601..cba4cb01 100644 --- a/pingora_tutorial/start.md +++ b/pingora_tutorial/start.md @@ -1,14 +1,14 @@ -### Pingora Tutorial +# Pingora Tutorial -The Cargo.lock and Cargo.toml are provided in the Pingora crate if you so choose to use them. Both have been labeled with the prefix of `tutorial_` so you will need to change their names before starting this tutorial. Having that will prevent you from needing to cargo add any crates that may be required to start up any of this code. If you don't want to use that then we will also make sure to point out the exact imports that we're using in each section. +The Cargo.lock and Cargo.toml are provided in the Pingora crate if you so choose to use them. Both have been labeled with the prefix of `tutorial_` so you will need to change their names before starting this tutorial. Having that will prevent you from needing to cargo add any crates that may be required to start up any of this code. If you don't want to use that then we will also make sure to point out the exact imports that we're using in each section so that you can install them yourself if you decide. So with that out of the way, lets begin our Pingora tutorial. ## Introduction -Before we begin building a full proxy server, we need a simple server to test and demonstrate our proxy’s functionality. In this tutorial, we’ll start by setting up a basic test web server in Rust that will display a simple message when accessed. This will help us verify that our proxy server is routing requests correctly. The server has alreadydy been built in it's entirety inside of the pingora_tutorial/src/test_server.rs, but this tutorial will explain the code inside that folder so you better understand it and are able to run or modify the code as you see fit. +Before we begin building a full proxy server, we need a simple server to test and demonstrate our proxy’s functionality. In this tutorial, we’ll start by setting up a basic test web server in Rust that will display a simple message when accessed. This will help us verify that our proxy server is routing requests correctly. The server has already been built in it's entirety inside of the `pingora_tutorial/src/test_server.rs`. This tutorial will explain the code inside that folder so you better understand it and are able to run or modify the code as you see fit. -Thank you for using pingora +Thank you for using Pingora ## Step 1: Start the Test Web Server @@ -37,41 +37,41 @@ pub fn start_test_server(address: &str) -> std::io::Result<()> { Ok(()) } ``` -Code: Starting the Test Web Server + +## Imports: + ```rust use std::io::{Read, Write}; use std::net::{TcpListener, TcpStream}; use std::thread; ``` -# Imports: - `std::io::{Read, Write}`: This imports Read and Write, traits that provide reading and writing functionality for streams. We’ll use these to handle data between the client and server. -`std::net::{TcpListener, TcpStream}`: TcpListener allows us to listen for incoming TCP connections on a specified address, and TcpStream represents a connection between a client and the server. +`std::net::{TcpListener, TcpStream}`: `TcpListener` allows us to listen for incoming TCP connections on a specified address. `TcpStream` represents a connection between a client and the server. `std::thread`: This imports Rust’s threading capabilities, allowing us to spawn new threads for each client connection to handle multiple clients concurrently. -# Function Declaration: +## Function Declaration: ```rust pub fn start_test_server(address: &str) -> std::io::Result<()> { ``` -start_test_server is a function that takes an address parameter of type &str (a string slice). -It returns a `Result<(), std::io::Error>`. This indicates that the function might return an std::io::Error if there’s a problem, such as if the address is already in use +`start_test_server` is a function that takes an address parameter of type `&str` (a string slice). +It returns a `Result<(), std::io::Error>`. This indicates that the function might return an `std::io::Error` if there’s a problem, such as if the address is already in use. -# Binding the Listener: +## Binding the Listener: ```rust let listener = TcpListener::bind(address)?; ``` -`TcpListener::bind(address)?` creates a TCP listener that binds to the provided address (e.g., "127.0.0.1:9000"). The ? operator automatically returns an error if binding fails. +`TcpListener::bind(address)?` creates a TCP listener that binds to the provided address (e.g., "127.0.0.1:9000"). The `?` operator automatically returns an error if binding fails. This listener will listen for incoming connections on the specified address. -# Console Output: +## Console Output: ```rust println!("Test server running on {}", address); @@ -79,16 +79,16 @@ println!("Test server running on {}", address); Prints a message to the console to indicate that the test server has started successfully and is running on the specified address. -# Handling Incoming Connections: +## Handling Incoming Connections: ```rust for stream in listener.incoming() { ``` -`listener.incoming()` is an iterator that yields incoming connections. Each connection is represented as a TcpStream, allowing data transfer with a client. -We use a for loop to handle each incoming connection individually. +`listener.incoming()` is an iterator that yields incoming connections. Each connection is represented as a `TcpStream`, allowing data transfer with a client. +We use a `for` loop to handle each incoming connection individually. -# Match Statement and Threading: +## Match Statement and Threading: ```rust match stream { @@ -104,14 +104,14 @@ We use a for loop to handle each incoming connection individually. `match stream`: We check whether each incoming connection (i.e., each stream) is successful (Ok) or encountered an error (Err). -`Ok(stream)`: If a connection is established successfully, stream represents the TcpStream of that connection. +`Ok(stream)`: If a connection is established successfully, stream represents the `TcpStream` of that connection. -`thread::spawn(move || { handle_test_client(stream); })`: Spawns a new thread for each connection, passing the TcpStream to handle_test_client. +`thread::spawn(move || { handle_test_client(stream); })`: Spawns a new thread for each connection, passing the `TcpStream` to `handle_test_client`. This enables handling multiple client connections concurrently. -`Err(e)`: If there’s an error accepting a connection, eprintln! outputs an error message to the console. +`Err(e)`: If there’s an error accepting a connection, `eprintln!` outputs an error message to the console. -# Return Value: +## Return Value: ```rust Ok(()) @@ -120,7 +120,7 @@ This enables handling multiple client connections concurrently. Returns `Ok(())`, indicating that the function completed successfully if there were no binding errors. -## Step 2: Display a Response Message +# Step 2: Display a Response Message To confirm that our server is working correctly, we’ll add a function to respond with a simple HTML message, “Hello. You made it!” This will help us know when we have successfully reached the test server. @@ -149,34 +149,34 @@ Content-Type: text/html } ``` -# Code: Displaying a Response Message +## Code: Displaying a Response Message ```rust // Function to handle each client and respond with a "Hello. You made it!" page fn handle_test_client(mut stream: TcpStream) { ``` -# Function Declaration: +## Function Declaration: -`handle_test_client` takes a mutable TcpStream called stream as input, representing an open connection with a client. +`handle_test_client` takes a mutable `TcpStream` called stream as input, representing an open connection with a client. This function will read the client’s request and write back a simple HTML response. ```rust let mut buffer = [0; 512]; ``` -# Buffer Initialization: +## Buffer Initialization: `let mut buffer = [0; 512];` creates a buffer, a fixed-size array of 512 bytes, to temporarily hold data read from the client. 512 bytes is generally enough to capture an HTTP request from the client. ```rust if stream.read(&mut buffer).is_ok() { ``` -# Reading from the Client: +## Reading from the Client: -`stream.read(&mut buffer).is_ok()`: Reads data from the client into buffer. The is_ok() check ensures that reading was successful. +`stream.read(&mut buffer).is_ok()`: Reads data from the client into buffer. The `is_ok()` check ensures that reading was successful. We’re not inspecting the content of the request in this example; we simply need to know the client connected to respond with our message. -# HTTP Response: +## HTTP Response: ```rust let response = r#"HTTP/1.1 200 OK @@ -193,13 +193,13 @@ Content-Type: text/html `let response = ...`: Defines an HTTP response as a raw string (r#"...#"). The response includes: - - `Status Line: HTTP/1.1 200 OK` indicates a successful HTTP response. - - `Headers: Content-Type: text/html` informs the client that the response is HTML. - - `HTML Content:` Displays a simple message, `

Hello. You made it!

`, in an HTML page structure. +`Status Line: HTTP/1.1 200 OK` indicates a successful HTTP response. +`Headers: Content-Type: text/html` informs the client that the response is HTML. +`HTML Content:` Displays a simple message, `

Hello. You made it!

`, in an HTML page structure. -This message will be displayed in the client’s browser when they successfully reach the server +This message will be displayed in the client’s browser when they successfully reach the server. -# Writing the Response: +## Writing the Response: ```rust if stream.write_all(response.as_bytes()).is_err() { @@ -213,9 +213,9 @@ if stream.write_all(response.as_bytes()).is_err() { `response.as_bytes()` converts the response string into a byte slice for sending over the network. -`is_err()`: If an error occurs while sending the response, an error message is printed to the console with eprintln!. +`is_err()`: If an error occurs while sending the response, an error message is printed to the console with `eprintln!`. ## Onto the next step -you have now created a rust server that will listen and respond to connections. So lets take this server and use it, as well as copies of it, to explore the rest of pingora +You have now created a rust server that will listen and respond to connections. So lets take this server and use it, as well as copies of it, to explore the rest of Pingora. ``` Some link here getting you to the next step ``` \ No newline at end of file From 659f4f5d63e3d3f53a1346e6324af6f1f79e381a Mon Sep 17 00:00:00 2001 From: HasturDev Date: Sat, 9 Nov 2024 19:57:50 -0500 Subject: [PATCH 5/6] feature/full-tutorial: changed the sizing of each beginning statement to have the emphasis be correct and no lower statement is larger than the statement that it's explicitely supposed to be inside of --- pingora_tutorial/start.md | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/pingora_tutorial/start.md b/pingora_tutorial/start.md index cba4cb01..ed47d1f4 100644 --- a/pingora_tutorial/start.md +++ b/pingora_tutorial/start.md @@ -4,7 +4,7 @@ The Cargo.lock and Cargo.toml are provided in the Pingora crate if you so choose So with that out of the way, lets begin our Pingora tutorial. -## Introduction +### Introduction Before we begin building a full proxy server, we need a simple server to test and demonstrate our proxy’s functionality. In this tutorial, we’ll start by setting up a basic test web server in Rust that will display a simple message when accessed. This will help us verify that our proxy server is routing requests correctly. The server has already been built in it's entirety inside of the `pingora_tutorial/src/test_server.rs`. This tutorial will explain the code inside that folder so you better understand it and are able to run or modify the code as you see fit. @@ -38,7 +38,7 @@ pub fn start_test_server(address: &str) -> std::io::Result<()> { } ``` -## Imports: +### Imports: ```rust use std::io::{Read, Write}; @@ -52,7 +52,7 @@ use std::thread; `std::thread`: This imports Rust’s threading capabilities, allowing us to spawn new threads for each client connection to handle multiple clients concurrently. -## Function Declaration: +### Function Declaration: ```rust pub fn start_test_server(address: &str) -> std::io::Result<()> { @@ -61,7 +61,7 @@ pub fn start_test_server(address: &str) -> std::io::Result<()> { `start_test_server` is a function that takes an address parameter of type `&str` (a string slice). It returns a `Result<(), std::io::Error>`. This indicates that the function might return an `std::io::Error` if there’s a problem, such as if the address is already in use. -## Binding the Listener: +### Binding the Listener: ```rust let listener = TcpListener::bind(address)?; @@ -71,7 +71,7 @@ let listener = TcpListener::bind(address)?; This listener will listen for incoming connections on the specified address. -## Console Output: +### Console Output: ```rust println!("Test server running on {}", address); @@ -79,7 +79,7 @@ println!("Test server running on {}", address); Prints a message to the console to indicate that the test server has started successfully and is running on the specified address. -## Handling Incoming Connections: +### Handling Incoming Connections: ```rust for stream in listener.incoming() { @@ -88,7 +88,7 @@ for stream in listener.incoming() { `listener.incoming()` is an iterator that yields incoming connections. Each connection is represented as a `TcpStream`, allowing data transfer with a client. We use a `for` loop to handle each incoming connection individually. -## Match Statement and Threading: +### Match Statement and Threading: ```rust match stream { @@ -111,7 +111,7 @@ This enables handling multiple client connections concurrently. `Err(e)`: If there’s an error accepting a connection, `eprintln!` outputs an error message to the console. -## Return Value: +### Return Value: ```rust Ok(()) @@ -120,7 +120,7 @@ This enables handling multiple client connections concurrently. Returns `Ok(())`, indicating that the function completed successfully if there were no binding errors. -# Step 2: Display a Response Message +## Step 2: Display a Response Message To confirm that our server is working correctly, we’ll add a function to respond with a simple HTML message, “Hello. You made it!” This will help us know when we have successfully reached the test server. @@ -149,14 +149,14 @@ Content-Type: text/html } ``` -## Code: Displaying a Response Message +### Code: Displaying a Response Message ```rust // Function to handle each client and respond with a "Hello. You made it!" page fn handle_test_client(mut stream: TcpStream) { ``` -## Function Declaration: +### Function Declaration: `handle_test_client` takes a mutable `TcpStream` called stream as input, representing an open connection with a client. This function will read the client’s request and write back a simple HTML response. @@ -165,18 +165,18 @@ This function will read the client’s request and write back a simple HTML resp let mut buffer = [0; 512]; ``` -## Buffer Initialization: +### Buffer Initialization: `let mut buffer = [0; 512];` creates a buffer, a fixed-size array of 512 bytes, to temporarily hold data read from the client. 512 bytes is generally enough to capture an HTTP request from the client. ```rust if stream.read(&mut buffer).is_ok() { ``` -## Reading from the Client: +### Reading from the Client: `stream.read(&mut buffer).is_ok()`: Reads data from the client into buffer. The `is_ok()` check ensures that reading was successful. We’re not inspecting the content of the request in this example; we simply need to know the client connected to respond with our message. -## HTTP Response: +### HTTP Response: ```rust let response = r#"HTTP/1.1 200 OK @@ -199,7 +199,7 @@ The response includes: This message will be displayed in the client’s browser when they successfully reach the server. -## Writing the Response: +### Writing the Response: ```rust if stream.write_all(response.as_bytes()).is_err() { From 75862f2657ababd7ed50d524a853352b3dad431b Mon Sep 17 00:00:00 2001 From: Ian Mizer <42318215+HasturDev@users.noreply.github.com> Date: Fri, 15 Nov 2024 15:37:07 -0500 Subject: [PATCH 6/6] Update start.md moved code to it's correct location within the start.md file --- pingora_tutorial/start.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/pingora_tutorial/start.md b/pingora_tutorial/start.md index ed47d1f4..67bef635 100644 --- a/pingora_tutorial/start.md +++ b/pingora_tutorial/start.md @@ -161,18 +161,21 @@ fn handle_test_client(mut stream: TcpStream) { `handle_test_client` takes a mutable `TcpStream` called stream as input, representing an open connection with a client. This function will read the client’s request and write back a simple HTML response. +### Buffer Initialization: + ```rust let mut buffer = [0; 512]; ``` -### Buffer Initialization: `let mut buffer = [0; 512];` creates a buffer, a fixed-size array of 512 bytes, to temporarily hold data read from the client. 512 bytes is generally enough to capture an HTTP request from the client. + +### Reading from the Client: + ```rust if stream.read(&mut buffer).is_ok() { ``` -### Reading from the Client: - + `stream.read(&mut buffer).is_ok()`: Reads data from the client into buffer. The `is_ok()` check ensures that reading was successful. We’re not inspecting the content of the request in this example; we simply need to know the client connected to respond with our message. @@ -218,4 +221,4 @@ if stream.write_all(response.as_bytes()).is_err() { ## Onto the next step You have now created a rust server that will listen and respond to connections. So lets take this server and use it, as well as copies of it, to explore the rest of Pingora. -``` Some link here getting you to the next step ``` \ No newline at end of file +``` Some link here getting you to the next step ```