diff --git a/Cargo.lock b/Cargo.lock index a09ad24ca7f..ce31c7754e9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -206,6 +206,19 @@ name = "async-compression" version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "942c7cd7ae39e91bde4820d74132e9862e62c2f386c3aa90ccf55949f5bad63a" +dependencies = [ + "flate2", + "futures-core", + "memchr", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "async-compression" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc2d0cfb2a7388d34f590e76686704c494ed7aaceed62ee1ba35cbf363abc2a5" dependencies = [ "brotli", "flate2", @@ -243,21 +256,21 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "axum" -version = "0.6.20" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" +checksum = "202651474fe73c62d9e0a56c6133f7a0ff1dc1c8cf7a5b03381af2a26553ac9d" dependencies = [ "async-trait", "axum-core", "axum-macros", "base64 0.21.0", - "bitflags 1.3.2", "bytes", "futures-util", - "headers", - "http", - "http-body", - "hyper", + "http 1.0.0", + "http-body 1.0.0", + "http-body-util", + "hyper 1.0.1", + "hyper-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "itoa", "matchit", "memchr", @@ -282,17 +295,20 @@ dependencies = [ [[package]] name = "axum-core" -version = "0.3.4" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c" +checksum = "77cb22c689c44d4c07b0ab44ebc25d69d8ae601a2f28fb8d672d344178fa17aa" dependencies = [ "async-trait", "bytes", "futures-util", - "http", - "http-body", + "http 1.0.0", + "http-body 1.0.0", + "http-body-util", "mime", + "pin-project-lite", "rustversion", + "sync_wrapper", "tower-layer", "tower-service", "tracing", @@ -300,9 +316,9 @@ dependencies = [ [[package]] name = "axum-macros" -version = "0.3.8" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdca6a10ecad987bda04e95606ef85a5417dcaac1a78455242d72e031e2b6b62" +checksum = "5a2edad600410b905404c594e2523549f1bcd4bded1e252c8f74524ccce0b867" dependencies = [ "heck", "proc-macro2", @@ -310,21 +326,6 @@ dependencies = [ "syn 2.0.38", ] -[[package]] -name = "axum-server" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bace45b270e36e3c27a190c65883de6dfc9f1d18c829907c127464815dc67b24" -dependencies = [ - "bytes", - "futures-util", - "http", - "http-body", - "hyper", - "tokio", - "tower-service", -] - [[package]] name = "backoff" version = "0.4.0" @@ -507,9 +508,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bytes" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" dependencies = [ "serde", ] @@ -1704,7 +1705,7 @@ dependencies = [ "futures-core", "futures-sink", "gloo-utils 0.1.7", - "http", + "http 0.2.9", "js-sys", "pin-project", "serde", @@ -1725,7 +1726,7 @@ dependencies = [ "futures-core", "futures-sink", "gloo-utils 0.2.0", - "http", + "http 0.2.9", "js-sys", "pin-project", "serde", @@ -1827,7 +1828,7 @@ dependencies = [ "futures-core", "futures-sink", "futures-util", - "http", + "http 0.2.9", "indexmap 1.9.2", "slab", "tokio", @@ -1835,6 +1836,25 @@ dependencies = [ "tracing", ] +[[package]] +name = "h2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d308f63daf4181410c242d34c11f928dcb3aa105852019e043c9d1f4e4368a" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http 1.0.0", + "indexmap 2.0.0", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "half" version = "1.8.2" @@ -1883,31 +1903,6 @@ dependencies = [ "hashbrown 0.12.3", ] -[[package]] -name = "headers" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3e372db8e5c0d213e0cd0b9be18be2aca3d44cf2fe30a9d46a65581cd454584" -dependencies = [ - "base64 0.13.1", - "bitflags 1.3.2", - "bytes", - "headers-core", - "http", - "httpdate", - "mime", - "sha1", -] - -[[package]] -name = "headers-core" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429" -dependencies = [ - "http", -] - [[package]] name = "heck" version = "0.4.1" @@ -1987,6 +1982,17 @@ dependencies = [ "itoa", ] +[[package]] +name = "http" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b32afd38673a8016f7c9ae69e5af41a58f81b1d31689040f2f1959594ce194ea" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + [[package]] name = "http-body" version = "0.4.5" @@ -1994,15 +2000,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" dependencies = [ "bytes", - "http", + "http 0.2.9", "pin-project-lite", ] [[package]] -name = "http-range-header" -version = "0.3.0" +name = "http-body" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" +dependencies = [ + "bytes", + "http 1.0.0", +] + +[[package]] +name = "http-body-util" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bfe8eed0a9285ef776bb792479ea3834e8b94e13d615c2f66d03dd50a435a29" +checksum = "41cb79eb393015dadd30fc252023adb0b2400a0caee0fa2a077e6e21a551e840" +dependencies = [ + "bytes", + "futures-util", + "http 1.0.0", + "http-body 1.0.0", + "pin-project-lite", +] [[package]] name = "httparse" @@ -2032,9 +2055,9 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "h2", - "http", - "http-body", + "h2 0.3.17", + "http 0.2.9", + "http-body 0.4.5", "httparse", "httpdate", "itoa", @@ -2046,6 +2069,25 @@ dependencies = [ "want", ] +[[package]] +name = "hyper" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "403f9214f3e703236b221f1a9cd88ec8b4adfa5296de01ab96216361f4692f56" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "h2 0.4.0", + "http 1.0.0", + "http-body 1.0.0", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "tokio", +] + [[package]] name = "hyper-tls" version = "0.5.0" @@ -2053,12 +2095,51 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" dependencies = [ "bytes", - "hyper", + "hyper 0.14.26", "native-tls", "tokio", "tokio-native-tls", ] +[[package]] +name = "hyper-util" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ca339002caeb0d159cc6e023dff48e199f081e42fa039895c7c6f38b37f2e9d" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http 1.0.0", + "http-body 1.0.0", + "hyper 1.0.1", + "pin-project-lite", + "socket2 0.5.3", + "tokio", + "tower", + "tower-service", + "tracing", +] + +[[package]] +name = "hyper-util" +version = "0.1.1" +source = "git+https://github.com/hyperium/hyper-util?rev=99409f5c4059633b7e2fa8b9c2e6c110b0f2f64b#99409f5c4059633b7e2fa8b9c2e6c110b0f2f64b" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http 1.0.0", + "http-body 1.0.0", + "hyper 1.0.1", + "pin-project-lite", + "socket2 0.5.3", + "tokio", + "tower", + "tower-service", + "tracing", +] + [[package]] name = "iana-time-zone" version = "0.1.53" @@ -2592,7 +2673,7 @@ dependencies = [ "bytes", "encoding_rs", "futures-util", - "http", + "http 0.2.9", "httparse", "log", "memchr", @@ -3494,7 +3575,7 @@ dependencies = [ "once_cell", "parking_lot", "regex", - "sentry-types", + "sentry-types 0.31.8", "serde", "serde_test", ] @@ -3900,7 +3981,6 @@ version = "23.11.2" dependencies = [ "anyhow", "axum", - "axum-server", "backoff", "brotli", "bytecount", @@ -3911,6 +3991,8 @@ dependencies = [ "futures", "hash32", "hashbrown 0.13.2", + "hyper 1.0.1", + "hyper-util 0.1.1 (git+https://github.com/hyperium/hyper-util?rev=99409f5c4059633b7e2fa8b9c2e6c110b0f2f64b)", "insta", "itertools", "json-forensics", @@ -4032,16 +4114,16 @@ version = "0.11.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21eed90ec8570952d53b772ecf8f206aa1ec9a3d76b2521c56c42973f2d91ee9" dependencies = [ - "async-compression", + "async-compression 0.3.15", "base64 0.21.0", "bytes", "encoding_rs", "futures-core", "futures-util", - "h2", - "http", - "http-body", - "hyper", + "h2 0.3.17", + "http 0.2.9", + "http-body 0.4.5", + "hyper 0.14.26", "hyper-tls", "ipnet", "js-sys", @@ -4358,9 +4440,9 @@ checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a" [[package]] name = "sentry" -version = "0.31.7" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0097a48cd1999d983909f07cb03b15241c5af29e5e679379efac1c06296abecc" +checksum = "9b596ee5f4e76638de6063ca96fd3d923675416461fc7f1b77406dc2f32d1979" dependencies = [ "httpdate", "native-tls", @@ -4378,9 +4460,9 @@ dependencies = [ [[package]] name = "sentry-backtrace" -version = "0.31.7" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18a7b80fa1dd6830a348d38a8d3a9761179047757b7dca29aef82db0118b9670" +checksum = "e6510a97162c288989a6310766bcadfc83ec98ad73121674463b055c42189e85" dependencies = [ "backtrace", "once_cell", @@ -4390,9 +4472,9 @@ dependencies = [ [[package]] name = "sentry-contexts" -version = "0.31.7" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7615dc588930f1fd2e721774f25844ae93add2dbe2d3c2f995ce5049af898147" +checksum = "64e2552a4a578aade01bd44691e6805c32bac34fc918f1675739fbbf2add8460" dependencies = [ "hostname", "libc", @@ -4404,22 +4486,22 @@ dependencies = [ [[package]] name = "sentry-core" -version = "0.31.7" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f51264e4013ed9b16558cce43917b983fa38170de2ca480349ceb57d71d6053" +checksum = "ebb7a6ad833035f6b36db3e61e450643eec8a3c5f2839b8e41c74a73e57c6bae" dependencies = [ "once_cell", "rand", - "sentry-types", + "sentry-types 0.32.0", "serde", "serde_json", ] [[package]] name = "sentry-debug-images" -version = "0.31.7" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fe6180fa564d40bb942c9f0084ffb5de691c7357ead6a2b7a3154fae9e401dd" +checksum = "0bcd02214397892a3ec25372cc68c210d858f39314535f5d640bdf41294fd441" dependencies = [ "findshlibs", "once_cell", @@ -4439,9 +4521,9 @@ dependencies = [ [[package]] name = "sentry-panic" -version = "0.31.7" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "323160213bba549f9737317b152af116af35c0410f4468772ee9b606d3d6e0fa" +checksum = "0866e2ba7615fe37e0e485f2373bf9dabbb255f82637b5fe47902095790bbbc9" dependencies = [ "sentry-backtrace", "sentry-core", @@ -4460,12 +4542,12 @@ dependencies = [ [[package]] name = "sentry-tower" -version = "0.31.7" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ffe3ab7bf7f65c9f8ccd20aa136ce5b2140aa6d6a11339e823cd43a7d694a9e" +checksum = "e275f07e9e7d9cf3b85130ab6893a9790c3ab2d8fedca29215aeafad0539c4f4" dependencies = [ "axum", - "http", + "http 1.0.0", "pin-project", "sentry-core", "tower-layer", @@ -4475,9 +4557,9 @@ dependencies = [ [[package]] name = "sentry-tracing" -version = "0.31.7" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38033822128e73f7b6ca74c1631cef8868890c6cb4008a291cf73530f87b4eac" +checksum = "53ef38653386354058f30b3c6d0bf764c59ee6270cd769ac4620a2d2fd60c8fe" dependencies = [ "sentry-backtrace", "sentry-core", @@ -4487,9 +4569,26 @@ dependencies = [ [[package]] name = "sentry-types" -version = "0.31.7" +version = "0.31.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da956cca56e0101998c8688bc65ce1a96f00673a0e58e663664023d4c7911e82" +dependencies = [ + "debugid", + "hex", + "rand", + "serde", + "serde_json", + "thiserror", + "time", + "url", + "uuid", +] + +[[package]] +name = "sentry-types" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e663b3eb62ddfc023c9cf5432daf5f1a4f6acb1df4d78dd80b740b32dd1a740" +checksum = "26342e85c6b3332273b820d5be6b93027fe991ded23a2aa6fb88a5a28c845c40" dependencies = [ "debugid", "hex", @@ -5382,18 +5481,17 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d1d42a9b3f3ec46ba828e8d376aec14592ea199f70a06a548587ecd1c4ab658" +checksum = "09e12e6351354851911bdf8c2b8f2ab15050c567d70a8b9a37ae7b8301a4080d" dependencies = [ - "async-compression", - "bitflags 1.3.2", + "async-compression 0.4.5", + "bitflags 2.4.1", "bytes", - "futures-core", "futures-util", - "http", - "http-body", - "http-range-header", + "http 1.0.0", + "http-body 1.0.0", + "http-body-util", "pin-project-lite", "tokio", "tokio-util", @@ -5551,7 +5649,7 @@ dependencies = [ "byteorder", "bytes", "data-encoding", - "http", + "http 0.2.9", "httparse", "log", "rand", diff --git a/relay-log/Cargo.toml b/relay-log/Cargo.toml index fd7844b6332..5b60745d2eb 100644 --- a/relay-log/Cargo.toml +++ b/relay-log/Cargo.toml @@ -15,12 +15,12 @@ chrono = { workspace = true, features = ["clock"], optional = true } console = { version = "0.15.5", optional = true } once_cell = { version = "1.13.1", optional = true } relay-crash = { path = "../relay-crash", optional = true } -sentry = { version = "0.31.7", features = [ +sentry = { version = "0.32.0", features = [ "debug-images", "tower-axum-matched-path", "tracing", ], optional = true } -sentry-core = { version = "0.31.7" } +sentry-core = { version = "0.32.0" } tokio = { version = "1", default-features = false, features = [ "sync", ], optional = true } diff --git a/relay-server/Cargo.toml b/relay-server/Cargo.toml index 0bd96cec852..3c22a0d735e 100644 --- a/relay-server/Cargo.toml +++ b/relay-server/Cargo.toml @@ -34,24 +34,27 @@ processing = [ [dependencies] anyhow = { workspace = true } -axum = { version = "0.6.20", features = [ - "headers", +axum = { version = "0.7.2", features = [ "macros", "matched-path", "multipart", "tracing", ] } -axum-server = "0.4.7" backoff = "0.4.0" brotli = "3.3.4" bytecount = "0.6.0" -bytes = { version = "1.4.0" } +bytes = { version = "1.5.0" } chrono = { workspace = true, features = ["clock"] } data-encoding = "2.3.3" flate2 = "1.0.19" futures = { workspace = true } hash32 = { workspace = true } hashbrown = { workspace = true } +hyper = { version = "1.0.1", features = ["http1", "http2", "server"] } +hyper-util = { git = "https://github.com/hyperium/hyper-util", rev = "99409f5c4059633b7e2fa8b9c2e6c110b0f2f64b", features = [ + "server-auto", + "tokio", +] } itertools = { workspace = true } json-forensics = { version = "0.1.1" } mime = "0.3.16" @@ -83,6 +86,8 @@ relay-sampling = { path = "../relay-sampling" } relay-spans = { path = "../relay-spans" } relay-statsd = { path = "../relay-statsd" } relay-system = { path = "../relay-system" } +# NOTE: When upgrading to a version of reqwest that depends on hyper/http 1.0, go to +# relay-server/src/endpoints/forward.rs and remove `mod legacy_shims`. reqwest = { version = "0.11.1", features = [ "gzip", "stream", @@ -93,7 +98,7 @@ rmp-serde = "1.1.1" rust-embed = { version = "8.0.0", optional = true } serde = { workspace = true } serde_json = { workspace = true } -smallvec = { workspace = true, features = ["drain_filter"] } +smallvec = { workspace = true, features = ["drain_filter"] } sqlx = { version = "0.7.0", features = [ "macros", "migrate", @@ -107,7 +112,7 @@ symbolic-unreal = { version = "12.1.2", optional = true, default-features = fals thiserror = { workspace = true } tokio = { workspace = true, features = ["rt-multi-thread"] } tower = { version = "0.4.13", default-features = false } -tower-http = { version = "0.4.0", default-features = false, features = [ +tower-http = { version = "0.5.0", default-features = false, features = [ "catch-panic", "cors", "decompression-br", diff --git a/relay-server/src/actors/server.rs b/relay-server/src/actors/server.rs index 1613672e940..43cb723cd7f 100644 --- a/relay-server/src/actors/server.rs +++ b/relay-server/src/actors/server.rs @@ -1,60 +1,110 @@ -use std::net::{SocketAddr, TcpListener}; +use std::io; +use std::net::{SocketAddr, TcpListener as StdTcpListener}; use std::sync::Arc; use std::time::Duration; +use axum::extract::ConnectInfo; use axum::http::{header, HeaderName, HeaderValue}; -use axum::ServiceExt; -use axum_server::{AddrIncomingConfig, Handle, HttpConfig}; +use hyper_util::rt::{TokioExecutor, TokioIo, TokioTimer}; +use hyper_util::server::conn::auto::Builder; +use hyper_util::service::TowerToHyperService; use relay_config::Config; use relay_log::tower::{NewSentryLayer, SentryHttpLayer}; -use relay_system::{Controller, Service, Shutdown}; -use tower::ServiceBuilder; +use relay_system::{Controller, Service}; +use tokio::net::{TcpListener, TcpStream}; +use tokio::sync::{watch, Notify}; +use tower::{Layer, ServiceBuilder}; use tower_http::set_header::SetResponseHeaderLayer; use crate::constants; -use crate::middlewares::{ - self, CatchPanicLayer, HandleErrorLayer, NormalizePathLayer, RequestDecompressionLayer, -}; +use crate::middlewares::{self, CatchPanicLayer, NormalizePath, RequestDecompressionLayer}; use crate::service::ServiceState; use crate::statsd::RelayCounters; +/// Set a timeout for reading client request headers. If a client does not transmit the entire +/// header within this time, the connection is closed. +const CLIENT_HEADER_TIMEOUT: Duration = Duration::from_secs(5); + /// Indicates the type of failure of the server. #[allow(clippy::enum_variant_names)] -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, thiserror::Error)] +#[derive(Debug, thiserror::Error)] pub enum ServerError { + /// Binding failed. + #[error("bind to interface failed")] + BindFailed(#[from] io::Error), + /// TLS support was not compiled in. #[error("SSL is no longer supported by Relay, please use a proxy in front")] TlsNotSupported, } +fn listen(config: &Config) -> Result { + // Inform the user about a removed feature. + if config.tls_listen_addr().is_some() + || config.tls_identity_password().is_some() + || config.tls_identity_path().is_some() + { + return Err(ServerError::TlsNotSupported); + } + + let listener = StdTcpListener::bind(config.listen_addr())?; + listener.set_nonblocking(true)?; + Ok(listener.try_into()?) +} + +type App = NormalizePath; + +/// Build the axum application with all routes and middleware. +fn make_app(service: ServiceState) -> App { + // Build the router middleware into a single service which runs _after_ routing. Service + // builder order defines layers added first will be called first. This means: + // - Requests go from top to bottom + // - Responses go from bottom to top + let middleware = ServiceBuilder::new() + .layer(axum::middleware::from_fn(middlewares::metrics)) + .layer(CatchPanicLayer::custom(middlewares::handle_panic)) + .layer(SetResponseHeaderLayer::overriding( + header::SERVER, + HeaderValue::from_static(constants::SERVER), + )) + .layer(SetResponseHeaderLayer::overriding( + HeaderName::from_static("cross-origin-resource-policy"), + HeaderValue::from_static("cross-origin"), + )) + .layer(NewSentryLayer::new_from_top()) + .layer(SentryHttpLayer::with_transaction()) + .layer(middlewares::trace_http_layer()) + .map_request(middlewares::remove_empty_encoding) + .layer(RequestDecompressionLayer::new()); + + let router = crate::endpoints::routes(service.config()) + .layer(middleware) + .with_state(service); + + // Add middlewares that need to run _before_ routing, which need to wrap the router. This are + // especially middlewares that modify the request path for the router: + NormalizePath::new(router) +} + /// HTTP server service. /// /// This is the main HTTP server of Relay which hosts all [services](ServiceState) and dispatches -/// incoming traffic to them. The server stops when a [`Shutdown`] is triggered. +/// incoming traffic to them. The server stops when a shutdown is triggered. pub struct HttpServer { config: Arc, service: ServiceState, + listener: TcpListener, } -/// Set the number of keep-alive retransmissions to be carried out before declaring that remote end -/// is not available. -const KEEPALIVE_RETRIES: u32 = 5; - -/// Set a timeout for reading client request headers. If a client does not transmit the entire -/// header within this time, the connection is closed. -const CLIENT_HEADER_TIMEOUT: Duration = Duration::from_secs(5); - impl HttpServer { pub fn new(config: Arc, service: ServiceState) -> Result { - // Inform the user about a removed feature. - if config.tls_listen_addr().is_some() - || config.tls_identity_password().is_some() - || config.tls_identity_path().is_some() - { - return Err(ServerError::TlsNotSupported); - } + let listener = listen(&config)?; - Ok(Self { config, service }) + Ok(Self { + config, + service, + listener, + }) } } @@ -62,82 +112,122 @@ impl Service for HttpServer { type Interface = (); fn spawn_handler(self, _rx: relay_system::Receiver) { - let Self { config, service } = self; - - // Build the router middleware into a single service which runs _after_ routing. Service - // builder order defines layers added first will be called first. This means: - // - Requests go from top to bottom - // - Responses go from bottom to top - let middleware = ServiceBuilder::new() - .layer(axum::middleware::from_fn(middlewares::metrics)) - .layer(CatchPanicLayer::custom(middlewares::handle_panic)) - .layer(SetResponseHeaderLayer::overriding( - header::SERVER, - HeaderValue::from_static(constants::SERVER), - )) - .layer(SetResponseHeaderLayer::overriding( - HeaderName::from_static("cross-origin-resource-policy"), - HeaderValue::from_static("cross-origin"), - )) - .layer(NewSentryLayer::new_from_top()) - .layer(SentryHttpLayer::with_transaction()) - .layer(middlewares::trace_http_layer()) - .layer(HandleErrorLayer::new(middlewares::decompression_error)) - .map_request(middlewares::remove_empty_encoding) - .layer(RequestDecompressionLayer::new()); - - let router = crate::endpoints::routes(service.config()) - .layer(middleware) - .with_state(service); - - // Bundle middlewares that need to run _before_ routing, which need to wrap the router. - // ConnectInfo is special as it needs to last. - let app = ServiceBuilder::new() - .layer(NormalizePathLayer::new()) - .service(router) - .into_make_service_with_connect_info::(); - - let http_config = HttpConfig::new() - .http1_half_close(true) - .http1_header_read_timeout(CLIENT_HEADER_TIMEOUT) - .http1_writev(true) - .http2_keep_alive_timeout(config.keepalive_timeout()) - .build(); - - let addr_config = AddrIncomingConfig::new() - .tcp_keepalive(Some(config.keepalive_timeout()).filter(|d| !d.is_zero())) - .tcp_keepalive_interval(Some(config.keepalive_timeout()).filter(|d| !d.is_zero())) - .tcp_keepalive_retries(Some(KEEPALIVE_RETRIES)) - .build(); - - let handle = Handle::new(); - match TcpListener::bind(config.listen_addr()) { - Ok(listener) => { - listener.set_nonblocking(true).ok(); - let server = axum_server::from_tcp(listener) - .http_config(http_config) - .addr_incoming_config(addr_config) - .handle(handle.clone()); - - relay_log::info!("spawning http server"); - relay_log::info!(" listening on http://{}/", config.listen_addr()); - relay_statsd::metric!(counter(RelayCounters::ServerStarting) += 1); - tokio::spawn(server.serve(app)); - } - Err(err) => { - relay_log::error!("Failed to start the HTTP server: {err}"); - std::process::exit(1); - } - } + let Self { + config, + service, + listener, + } = self; - tokio::spawn(async move { - let Shutdown { timeout } = Controller::shutdown_handle().notified().await; - relay_log::info!("Shutting down HTTP server"); + relay_log::info!("spawning http server"); + relay_log::info!(" listening on http://{}/", config.listen_addr()); + relay_statsd::metric!(counter(RelayCounters::ServerStarting) += 1); + + let app = make_app(service); + + tokio::spawn(serve(listener, app, config.clone())); + } +} - match timeout { - Some(timeout) => handle.graceful_shutdown(Some(timeout)), - None => handle.shutdown(), +async fn serve(mut listener: TcpListener, app: App, config: Arc) { + // Create channels to track shutdown and open connections. + let hard_shutdown = monitor_hard_shutdown(); + let mut graceful_shutdown = Controller::shutdown_handle(); + let (connections_tx, connections_rx) = watch::channel(()); + + // Create a connection builder to reuse across connections. + let builder = Arc::new(connection_builder(&config)); + + loop { + let (stream, addr) = tokio::select! { + biased; + result = accept(&mut listener) => result, + _ = graceful_shutdown.notified() => break, // stop connecting on shutdown signal + }; + + // We don't need to call `poll_ready` because `Router` is always ready. + let tower_service = axum::Extension(ConnectInfo(addr)).layer(app.clone()); + let hyper_service = TowerToHyperService::new(tower_service); + + let connection_rx = connections_rx.clone(); + let hard_shutdown = hard_shutdown.clone(); + let connection_builder = builder.clone(); + + tokio::spawn(async move { + tokio::select! { + biased; + _ = connection_builder.serve_connection_with_upgrades(stream, hyper_service) => (), + _ = hard_shutdown.notified() => () // keep polling until we reach hard shutdown } + + drop(connection_rx); }); } + + // Close the listener to stop accepting new connections. + drop(listener); + + // Drop the last rx and wait for all open connections to close (or hard shutdown). + drop(connections_rx); + connections_tx.closed().await; +} + +fn connection_builder(config: &Config) -> Builder { + let mut builder = Builder::new(TokioExecutor::new()); + + builder + .http1() + .timer(TokioTimer::new()) + .half_close(true) + .header_read_timeout(CLIENT_HEADER_TIMEOUT) + .writev(true); + + builder + .http2() + .timer(TokioTimer::new()) + .keep_alive_timeout(config.keepalive_timeout()); + + builder +} + +fn monitor_hard_shutdown() -> Arc { + let shutdown_tx = Arc::new(Notify::new()); + let shutdown_rx = shutdown_tx.clone(); + + // `finished()` is not thread-safe, so we need a dedicated task and a notify. + tokio::spawn(async move { + Controller::shutdown_handle().finished().await; + shutdown_tx.notify_waiters(); + }); + + shutdown_rx +} + +async fn accept(listener: &mut TcpListener) -> (TokioIo, SocketAddr) { + loop { + match listener.accept().await { + Ok((stream, addr)) => return (TokioIo::new(stream), addr), + Err(e) => { + // Connection errors can be ignored directly, continue + // by accepting the next request. + if is_connection_error(&e) { + continue; + } + + // A possible scenario is that the process has hit the max open files allowed, and + // so trying to accept a new connection will fail with `EMFILE`. In some cases, it's + // preferable to just wait for some time, if the application will likely close some + // files (or connections), and try to accept the connection again. + tokio::time::sleep(Duration::from_millis(50)).await + } + } + } +} + +fn is_connection_error(e: &io::Error) -> bool { + matches!( + e.kind(), + io::ErrorKind::ConnectionRefused + | io::ErrorKind::ConnectionAborted + | io::ErrorKind::ConnectionReset + ) } diff --git a/relay-server/src/endpoints/attachments.rs b/relay-server/src/endpoints/attachments.rs index dff9dcdd689..b112ab113fa 100644 --- a/relay-server/src/endpoints/attachments.rs +++ b/relay-server/src/endpoints/attachments.rs @@ -2,7 +2,6 @@ use axum::extract::{DefaultBodyLimit, Multipart, Path}; use axum::http::StatusCode; use axum::response::IntoResponse; use axum::routing::{post, MethodRouter}; -use bytes::Bytes; use relay_config::Config; use relay_event_schema::protocol::EventId; use serde::Deserialize; @@ -45,11 +44,6 @@ async fn handle( Ok(StatusCode::CREATED) } -pub fn route(config: &Config) -> MethodRouter -where - B: axum::body::HttpBody + Send + 'static, - B::Data: Send + Into, - B::Error: Into, -{ +pub fn route(config: &Config) -> MethodRouter { post(handle).route_layer(DefaultBodyLimit::max(config.max_attachments_size())) } diff --git a/relay-server/src/endpoints/envelope.rs b/relay-server/src/endpoints/envelope.rs index 47e38c09547..287b9909c56 100644 --- a/relay-server/src/endpoints/envelope.rs +++ b/relay-server/src/endpoints/envelope.rs @@ -3,8 +3,7 @@ use std::convert::Infallible; use axum::extract::rejection::BytesRejection; -use axum::extract::{DefaultBodyLimit, FromRequest}; -use axum::http::Request; +use axum::extract::{DefaultBodyLimit, FromRequest, Request}; use axum::response::IntoResponse; use axum::routing::{post, MethodRouter}; use axum::{Json, RequestExt}; @@ -71,16 +70,11 @@ impl EnvelopeParams { } #[axum::async_trait] -impl FromRequest for EnvelopeParams -where - B: axum::body::HttpBody + Send + 'static, - B::Data: Send, - B::Error: Into, -{ +impl FromRequest for EnvelopeParams { type Rejection = BadEnvelopeParams; async fn from_request( - mut request: Request, + mut request: Request, state: &ServiceState, ) -> Result { let result = request.extract_parts_with_state(state).await; @@ -125,11 +119,6 @@ async fn handle( Ok(Json(StoreResponse { id })) } -pub fn route(config: &Config) -> MethodRouter -where - B: axum::body::HttpBody + Send + 'static, - B::Data: Send, - B::Error: Into, -{ +pub fn route(config: &Config) -> MethodRouter { post(handle).route_layer(DefaultBodyLimit::max(config.max_envelope_size())) } diff --git a/relay-server/src/endpoints/forward.rs b/relay-server/src/endpoints/forward.rs index 2030022574e..83fbdd07884 100644 --- a/relay-server/src/endpoints/forward.rs +++ b/relay-server/src/endpoints/forward.rs @@ -9,9 +9,9 @@ use std::fmt; use std::future::Future; use std::pin::Pin; -use axum::extract::DefaultBodyLimit; +use axum::extract::{DefaultBodyLimit, Request}; use axum::handler::Handler; -use axum::http::{header, HeaderMap, HeaderName, HeaderValue, Request, StatusCode, Uri}; +use axum::http::{header, HeaderMap, HeaderName, HeaderValue, Method, StatusCode, Uri}; use axum::response::{IntoResponse, Response}; use bytes::Bytes; use once_cell::sync::Lazy; @@ -20,7 +20,7 @@ use relay_config::Config; use tokio::sync::oneshot; use tokio::sync::oneshot::error::RecvError; -use crate::actors::upstream::{Method, SendRequest, UpstreamRequest, UpstreamRequestError}; +use crate::actors::upstream::{SendRequest, UpstreamRequest, UpstreamRequestError}; use crate::extractors::ForwardedFor; use crate::http::{HttpError, RequestBuilder, Response as UpstreamResponse}; use crate::service::ServiceState; @@ -46,6 +46,41 @@ static IGNORED_REQUEST_HEADERS: &[HeaderName] = &[ /// Root path of all API endpoints. const API_PATH: &str = "/api/"; +/// Shims to convert between http 0.2 and http 1.0. This is needed until reqwest is updated to a +/// version that builds on hyper/http 1.0. +mod legacy_shims { + use axum::http::StatusCode as AxumCode; + use reqwest::StatusCode as ReqwestCode; + + use super::HOP_BY_HOP_HEADERS; + + pub fn status_to_1(status: ReqwestCode) -> AxumCode { + AxumCode::from_u16(status.as_u16()).unwrap() + } + + pub fn status_to_1_opt(status: Option) -> Option { + Some(AxumCode::from_u16(status?.as_u16()).unwrap()) + } + + pub fn is_hop_by_hop(header: &reqwest::header::HeaderName) -> bool { + HOP_BY_HOP_HEADERS + .iter() + .any(|h| h.as_str() == header.as_str()) + } + + pub fn header_to_1( + name: &reqwest::header::HeaderName, + value: &reqwest::header::HeaderValue, + ) -> (axum::http::HeaderName, axum::http::HeaderValue) { + ( + name.as_str().parse().unwrap(), + axum::http::HeaderValue::from_bytes(value.as_bytes()).unwrap(), + ) + } +} + +use legacy_shims::*; + /// A wrapper struct that allows conversion of UpstreamRequestError into a `dyn ResponseError`. The /// conversion logic is really only acceptable for blindly forwarded requests. #[derive(Debug, thiserror::Error)] @@ -65,8 +100,7 @@ impl IntoResponse for ForwardError { HttpError::Overflow => StatusCode::PAYLOAD_TOO_LARGE.into_response(), HttpError::Reqwest(error) => { relay_log::error!(error = error as &dyn Error); - error - .status() + status_to_1_opt(error.status()) .unwrap_or(StatusCode::INTERNAL_SERVER_ERROR) .into_response() } @@ -112,8 +146,8 @@ impl fmt::Debug for ForwardRequest { } impl UpstreamRequest for ForwardRequest { - fn method(&self) -> Method { - self.method.clone() + fn method(&self) -> reqwest::Method { + self.method.as_str().parse().unwrap() } fn path(&self) -> Cow<'_, str> { @@ -162,12 +196,12 @@ impl UpstreamRequest for ForwardRequest { Box::pin(async move { let result = match result { Ok(response) => { - let status = response.status(); + let status = status_to_1(response.status()); let headers = response .headers() .iter() - .filter(|(name, _)| !HOP_BY_HOP_HEADERS.contains(name)) - .map(|(name, value)| (name.clone(), value.clone())) + .filter(|(name, _)| !is_hop_by_hop(name)) + .map(|(name, value)| header_to_1(name, value)) .collect(); match response.bytes(self.max_response_size).await { @@ -268,12 +302,7 @@ fn get_limit_for_path(path: &str, config: &Config) -> usize { /// /// - Use it as [`Handler`] directly in router methods when registering this as a route. /// - Call this manually from other request handlers to conditionally forward from other endpoints. -pub fn forward(state: ServiceState, req: Request) -> impl Future -where - B: axum::body::HttpBody + Send + 'static, - B::Data: Send, - B::Error: Into, -{ +pub fn forward(state: ServiceState, req: Request) -> impl Future { let limit = get_limit_for_path(req.uri().path(), state.config()); handle.layer(DefaultBodyLimit::max(limit)).call(req, state) } diff --git a/relay-server/src/endpoints/minidump.rs b/relay-server/src/endpoints/minidump.rs index 0d2d7ea875c..a91f7410723 100644 --- a/relay-server/src/endpoints/minidump.rs +++ b/relay-server/src/endpoints/minidump.rs @@ -1,7 +1,6 @@ use std::convert::Infallible; -use axum::extract::{DefaultBodyLimit, Multipart}; -use axum::http::Request; +use axum::extract::{DefaultBodyLimit, Multipart, Request}; use axum::response::IntoResponse; use axum::routing::{post, MethodRouter}; use axum::RequestExt; @@ -126,17 +125,12 @@ fn extract_raw_minidump(data: Bytes, meta: RequestMeta) -> Result, Ok(envelope) } -async fn handle( +async fn handle( state: ServiceState, meta: RequestMeta, content_type: RawContentType, - request: Request, -) -> axum::response::Result -where - B: axum::body::HttpBody + Send + 'static, - B::Data: Send + Into, - B::Error: Into, -{ + request: Request, +) -> axum::response::Result { // The minidump can either be transmitted as the request body, or as // `upload_file_minidump` in a multipart form-data/ request. // Minidump request payloads do not have the same structure as usual events from other SDKs. The @@ -161,12 +155,7 @@ where Ok(TextResponse(id)) } -pub fn route(config: &Config) -> MethodRouter -where - B: axum::body::HttpBody + Send + 'static, - B::Data: Send + Into, - B::Error: Into, -{ +pub fn route(config: &Config) -> MethodRouter { post(handle).route_layer(DefaultBodyLimit::max(config.max_attachments_size())) } diff --git a/relay-server/src/endpoints/mod.rs b/relay-server/src/endpoints/mod.rs index 462914ad136..a00687e9a3e 100644 --- a/relay-server/src/endpoints/mod.rs +++ b/relay-server/src/endpoints/mod.rs @@ -29,19 +29,13 @@ mod unreal; use axum::extract::DefaultBodyLimit; use axum::routing::{any, get, post, Router}; -use bytes::Bytes; use relay_config::Config; use crate::middlewares; use crate::service::ServiceState; #[rustfmt::skip] -pub fn routes(config: &Config) -> Router -where - B: axum::body::HttpBody + Send + 'static, - B::Data: Send + Into, - B::Error: Into, -{ +pub fn routes(config: &Config) -> Router { #[cfg(feature = "dashboard")] let dashboard = Router::new().route("/dashboard/",get(dashboard::index_handle)) .route("/dashboard/*file", get(dashboard::handle)); diff --git a/relay-server/src/endpoints/monitor.rs b/relay-server/src/endpoints/monitor.rs index 0364cff8307..3c52ee20b01 100644 --- a/relay-server/src/endpoints/monitor.rs +++ b/relay-server/src/endpoints/monitor.rs @@ -1,9 +1,8 @@ -use axum::extract::{DefaultBodyLimit, Path, Query}; -use axum::http::{Request, StatusCode}; +use axum::extract::{DefaultBodyLimit, Path, Query, Request}; +use axum::http::StatusCode; use axum::response::IntoResponse; use axum::routing::{on, MethodFilter, MethodRouter}; use axum::{Json, RequestExt}; -use bytes::Bytes; use relay_config::Config; use relay_event_schema::protocol::EventId; use relay_monitors::{CheckIn, CheckInStatus}; @@ -28,18 +27,13 @@ struct MonitorQuery { duration: Option, } -async fn handle( +async fn handle( state: ServiceState, content_type: RawContentType, meta: RequestMeta, Path(path): Path, - request: Request, -) -> axum::response::Result -where - B: axum::body::HttpBody + Send + 'static, - B::Data: Send + Into, - B::Error: Into, -{ + request: Request, +) -> axum::response::Result { let check_in = if content_type.as_ref().starts_with("application/json") { let Json(mut check_in): Json = request.extract().await?; check_in.monitor_slug = path.monitor_slug; @@ -74,12 +68,7 @@ where Ok(StatusCode::ACCEPTED) } -pub fn route(config: &Config) -> MethodRouter -where - B: axum::body::HttpBody + Send + 'static, - B::Data: Send + Into, - B::Error: Into, -{ - on(MethodFilter::GET | MethodFilter::POST, handle) +pub fn route(config: &Config) -> MethodRouter { + on(MethodFilter::GET.or(MethodFilter::POST), handle) .route_layer(DefaultBodyLimit::max(config.max_event_size())) } diff --git a/relay-server/src/endpoints/nel.rs b/relay-server/src/endpoints/nel.rs index 28aabb4e9ce..dbecf449649 100644 --- a/relay-server/src/endpoints/nel.rs +++ b/relay-server/src/endpoints/nel.rs @@ -59,11 +59,6 @@ async fn handle( Ok(().into_response()) } -pub fn route(config: &Config) -> MethodRouter -where - B: axum::body::HttpBody + Send + 'static, - B::Data: Send, - B::Error: Into, -{ +pub fn route(config: &Config) -> MethodRouter { post(handle).route_layer(DefaultBodyLimit::max(config.max_event_size())) } diff --git a/relay-server/src/endpoints/project_configs.rs b/relay-server/src/endpoints/project_configs.rs index 76901497cc6..0cab73c0590 100644 --- a/relay-server/src/endpoints/project_configs.rs +++ b/relay-server/src/endpoints/project_configs.rs @@ -1,9 +1,8 @@ use std::collections::HashMap; use std::sync::Arc; -use axum::extract::Query; +use axum::extract::{Query, Request}; use axum::handler::Handler; -use axum::http::Request; use axum::response::{IntoResponse, Result}; use axum::{Json, RequestExt}; use futures::future; @@ -196,12 +195,7 @@ fn is_compatible(Query(query): Query) -> bool { /// Relays can drop compatibility with old versions of the project config endpoint, for instance the /// initial version 1. However, Sentry's HTTP endpoint will retain compatibility for much longer to /// support old Relay versions. -pub async fn handle(state: ServiceState, mut req: Request) -> Result -where - B: axum::body::HttpBody + Send + 'static, - B::Data: Send, - B::Error: Into, -{ +pub async fn handle(state: ServiceState, mut req: Request) -> Result { let data = req.extract_parts().await?; Ok(if is_compatible(data) { inner.call(req, state).await diff --git a/relay-server/src/endpoints/security_report.rs b/relay-server/src/endpoints/security_report.rs index e402ca07f33..3bfa52a042f 100644 --- a/relay-server/src/endpoints/security_report.rs +++ b/relay-server/src/endpoints/security_report.rs @@ -87,11 +87,6 @@ async fn handle( Ok(().into_response()) } -pub fn route(config: &Config) -> MethodRouter -where - B: axum::body::HttpBody + Send + 'static, - B::Data: Send, - B::Error: Into, -{ +pub fn route(config: &Config) -> MethodRouter { post(handle).route_layer(DefaultBodyLimit::max(config.max_event_size())) } diff --git a/relay-server/src/endpoints/spans.rs b/relay-server/src/endpoints/spans.rs index ed478bc0ba9..2a677e941ac 100644 --- a/relay-server/src/endpoints/spans.rs +++ b/relay-server/src/endpoints/spans.rs @@ -2,7 +2,6 @@ use axum::extract::{DefaultBodyLimit, Json}; use axum::http::StatusCode; use axum::response::IntoResponse; use axum::routing::{post, MethodRouter}; -use bytes::Bytes; use relay_config::Config; use relay_spans::TracesData; @@ -34,11 +33,6 @@ async fn handle( Ok(StatusCode::ACCEPTED) } -pub fn route(config: &Config) -> MethodRouter -where - B: axum::body::HttpBody + Send + 'static, - B::Data: Send + Into, - B::Error: Into, -{ +pub fn route(config: &Config) -> MethodRouter { post(handle).route_layer(DefaultBodyLimit::max(config.max_span_size())) } diff --git a/relay-server/src/endpoints/store.rs b/relay-server/src/endpoints/store.rs index edf7ffc9e5c..facc5033a71 100644 --- a/relay-server/src/endpoints/store.rs +++ b/relay-server/src/endpoints/store.rs @@ -143,11 +143,6 @@ async fn handle_get( Ok(([(header::CONTENT_TYPE, "image/gif")], PIXEL)) } -pub fn route(config: &Config) -> MethodRouter -where - B: axum::body::HttpBody + Send + 'static, - B::Data: Send, - B::Error: Into, -{ +pub fn route(config: &Config) -> MethodRouter { (post(handle_post).get(handle_get)).route_layer(DefaultBodyLimit::max(config.max_event_size())) } diff --git a/relay-server/src/endpoints/unreal.rs b/relay-server/src/endpoints/unreal.rs index 3207980db6c..0f23fc2ac73 100644 --- a/relay-server/src/endpoints/unreal.rs +++ b/relay-server/src/endpoints/unreal.rs @@ -67,11 +67,6 @@ async fn handle( Ok(TextResponse(id)) } -pub fn route(config: &Config) -> MethodRouter -where - B: axum::body::HttpBody + Send + 'static, - B::Data: Send, - B::Error: Into, -{ +pub fn route(config: &Config) -> MethodRouter { post(handle).route_layer(DefaultBodyLimit::max(config.max_attachments_size())) } diff --git a/relay-server/src/extractors/signed_json.rs b/relay-server/src/extractors/signed_json.rs index 74aa3c7baed..5168d505b69 100644 --- a/relay-server/src/extractors/signed_json.rs +++ b/relay-server/src/extractors/signed_json.rs @@ -1,6 +1,6 @@ use axum::extract::rejection::BytesRejection; -use axum::extract::FromRequest; -use axum::http::{Request, StatusCode}; +use axum::extract::{FromRequest, Request}; +use axum::http::StatusCode; use axum::response::{IntoResponse, Response}; use bytes::Bytes; use relay_auth::{RelayId, UnpackError}; @@ -72,19 +72,13 @@ fn get_header<'a, B>( } #[axum::async_trait] -impl FromRequest for SignedJson +impl FromRequest for SignedJson where T: DeserializeOwned, - B: axum::body::HttpBody + Send + 'static, - B::Data: Send, - B::Error: Into, { type Rejection = SignatureError; - async fn from_request( - request: Request, - state: &ServiceState, - ) -> Result { + async fn from_request(request: Request, state: &ServiceState) -> Result { let relay_id = get_header(&request, "x-sentry-relay-id")? .parse::() .map_err(|_| SignatureError::MalformedHeader("x-sentry-relay-id"))?; diff --git a/relay-server/src/middlewares/decompression.rs b/relay-server/src/middlewares/decompression.rs index 65c5a879258..364793bf311 100644 --- a/relay-server/src/middlewares/decompression.rs +++ b/relay-server/src/middlewares/decompression.rs @@ -1,10 +1,5 @@ -pub use axum::error_handling::HandleErrorLayer; -use axum::http::{header, Request, StatusCode}; -use axum::response::IntoResponse; +use axum::http::{header, Request}; pub use tower_http::decompression::RequestDecompressionLayer; -use tower_http::BoxError; - -use crate::utils::ApiErrorResponse; /// Map request middleware that removes empty content encoding headers. /// @@ -25,13 +20,3 @@ fn should_ignore_encoding(value: &[u8]) -> bool { // sentry.java.android/2.0.0 sends "UTF-8" value == b"" || value.eq_ignore_ascii_case(b"utf-8") } - -/// Error function to be used with [`RequestDecompressionLayer`]. -/// -/// To use decompression in axum, wrap it in a [`HandleErrorLayer`] with this function. -pub async fn decompression_error(error: BoxError) -> impl IntoResponse { - ( - StatusCode::BAD_REQUEST, - ApiErrorResponse::from_error(error.as_ref()), - ) -} diff --git a/relay-server/src/middlewares/metrics.rs b/relay-server/src/middlewares/metrics.rs index 39ebe09e848..4c966c8cc5d 100644 --- a/relay-server/src/middlewares/metrics.rs +++ b/relay-server/src/middlewares/metrics.rs @@ -1,5 +1,4 @@ -use axum::extract::MatchedPath; -use axum::http::Request; +use axum::extract::{MatchedPath, Request}; use axum::middleware::Next; use axum::response::Response; use axum::RequestExt; @@ -10,10 +9,7 @@ use crate::statsd::{RelayCounters, RelayTimers}; /// A middleware that logs web request timings as statsd metrics. /// /// Use this with [`axum::middleware::from_fn`]. -pub async fn metrics(mut request: Request, next: Next) -> Response -where - B: Send + 'static, -{ +pub async fn metrics(mut request: Request, next: Next) -> Response { let start_time = StartTime::now(); request.extensions_mut().insert(start_time); diff --git a/relay-server/src/middlewares/normalize_path.rs b/relay-server/src/middlewares/normalize_path.rs index 268f2c606b7..95793944662 100644 --- a/relay-server/src/middlewares/normalize_path.rs +++ b/relay-server/src/middlewares/normalize_path.rs @@ -1,8 +1,7 @@ use std::borrow::Cow; use std::task::{Context, Poll}; -use axum::http::{Request, Uri}; -use axum::response::Response; +use axum::http::{Request, Response, Uri}; use once_cell::sync::Lazy; use regex::Regex; use tower::{Layer, Service}; @@ -12,6 +11,7 @@ use tower::{Layer, Service}; pub struct NormalizePathLayer; impl NormalizePathLayer { + #[allow(unused)] pub fn new() -> Self { Self } diff --git a/relay-server/src/utils/multipart.rs b/relay-server/src/utils/multipart.rs index 305b45bfba2..02131b57054 100644 --- a/relay-server/src/utils/multipart.rs +++ b/relay-server/src/utils/multipart.rs @@ -217,10 +217,9 @@ where #[cfg(test)] mod tests { - use axum::body::Full; + use axum::body::Body; use axum::extract::FromRequest; use axum::http::Request; - use bytes::Bytes; use super::*; @@ -284,7 +283,7 @@ mod tests { let request = Request::builder() .header("content-type", "multipart/form-data; boundary=X-BOUNDARY") - .body(Full::new(Bytes::from(data))) + .body(Body::from(data)) .unwrap(); let mut multipart = Multipart::from_request(request, &()).await?;