diff --git a/Cargo.lock b/Cargo.lock index e39238f4..ba9186ac 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -197,7 +197,7 @@ dependencies = [ "futures", "futures-async-stream", "headers", - "http 0.2.9", + "http 1.1.0", "http_client", "humansize", "isolate", @@ -293,7 +293,6 @@ version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "942c7cd7ae39e91bde4820d74132e9862e62c2f386c3aa90ccf55949f5bad63a" dependencies = [ - "brotli", "flate2", "futures-core", "memchr", @@ -309,6 +308,7 @@ version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd066d0b4ef8ecb03a55319dc13aa6910616d0f44008a045bb1835af830abff5" dependencies = [ + "brotli", "flate2", "futures-core", "memchr", @@ -424,6 +424,12 @@ dependencies = [ "num-traits", ] +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + [[package]] name = "atomic-write-file" version = "0.1.2" @@ -464,7 +470,7 @@ dependencies = [ "convex_sync_types", "errors", "futures", - "http 0.2.9", + "http 1.1.0", "keybroker", "metrics", "oauth2", @@ -483,21 +489,21 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "axum" -version = "0.6.20" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" +checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf" dependencies = [ "async-trait", "axum-core", "axum-macros", - "base64 0.21.0", - "bitflags 1.3.2", + "base64 0.21.7", "bytes", "futures-util", - "headers", - "http 0.2.9", - "http-body 0.4.5", - "hyper 0.14.27", + "http 1.1.0", + "http-body 1.0.0", + "http-body-util", + "hyper 1.3.1", + "hyper-util", "itoa", "matchit", "memchr", @@ -511,36 +517,64 @@ dependencies = [ "serde_path_to_error", "serde_urlencoded", "sha1", - "sync_wrapper", + "sync_wrapper 1.0.1", "tokio", "tokio-tungstenite", "tower", "tower-layer", "tower-service", + "tracing", ] [[package]] name = "axum-core" -version = "0.3.4" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c" +checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3" dependencies = [ "async-trait", "bytes", "futures-util", - "http 0.2.9", - "http-body 0.4.5", + "http 1.1.0", + "http-body 1.0.0", + "http-body-util", "mime", + "pin-project-lite", "rustversion", + "sync_wrapper 0.1.2", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-extra" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0be6ea09c9b96cb5076af0de2e383bd2bc0c18f827cf1967bdd353e0b910d733" +dependencies = [ + "axum", + "axum-core", + "bytes", + "futures-util", + "headers", + "http 1.1.0", + "http-body 1.0.0", + "http-body-util", + "mime", + "pin-project-lite", + "serde", + "tower", "tower-layer", "tower-service", + "tracing", ] [[package]] name = "axum-macros" -version = "0.3.8" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdca6a10ecad987bda04e95606ef85a5417dcaac1a78455242d72e031e2b6b62" +checksum = "00c055ee2d014ae5981ce1016374e8213682aa14d9bf40e48ab48b5f3ef20eaa" dependencies = [ "heck 0.4.1", "proc-macro2", @@ -608,9 +642,9 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "base64" -version = "0.21.0" +version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" [[package]] name = "base64" @@ -795,9 +829,9 @@ dependencies = [ [[package]] name = "brotli" -version = "3.4.0" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "516074a47ef4bce09577a3b379392300159ce5b1ba2e501ff1c819950066100f" +checksum = "74f7971dbd9326d58187408ab83117d8ac1bb9c17b085fdacd1cf2f598719b6b" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -806,9 +840,9 @@ dependencies = [ [[package]] name = "brotli-decompressor" -version = "2.5.1" +version = "4.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e2e4afe60d7dd600fdd3de8d0f08c2b7ec039712e3b6137ff98b7004e82de4f" +checksum = "9a45bd2e4095a8b518033b128020dd4a55aab1c0a381ba4404a472630f4bc362" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -868,9 +902,9 @@ dependencies = [ [[package]] name = "cacache" -version = "12.0.0" +version = "13.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "142316461ed3a3dfcba10417317472da5bfd0461e4d276bf7c07b330766d9490" +checksum = "a61ff12b19d89c752c213316b87fdb4a587f073d219b893cc56974b8c9f39bf7" dependencies = [ "digest", "either", @@ -1188,11 +1222,14 @@ dependencies = [ "float_next_after", "futures", "futures-async-stream", + "futures-util", "governor", "headers", "hex", - "http 0.2.9", - "hyper 0.14.27", + "http 1.1.0", + "http-body-util", + "hyper 1.3.1", + "hyper-util", "imbl", "itertools 0.13.0", "jsonschema", @@ -1214,7 +1251,7 @@ dependencies = [ "rand 0.8.5", "rand_chacha 0.3.1", "regex", - "reqwest 0.11.24", + "reqwest 0.12.4", "semver 1.0.23", "sentry", "serde", @@ -1333,7 +1370,7 @@ dependencies = [ "proptest", "prost", "prost-types", - "reqwest 0.11.24", + "reqwest 0.12.4", "tokio", "tonic", "tonic-build", @@ -1365,7 +1402,7 @@ dependencies = [ "proptest-derive", "prost", "prost-types", - "reqwest 0.11.24", + "reqwest 0.12.4", "serde", "serde_json", "thiserror", @@ -1388,13 +1425,14 @@ dependencies = [ "futures", "futures-async-stream", "futures-util", + "headers", "maplit", "proptest", "proptest-derive", "prost", "prost-types", "rand 0.8.5", - "reqwest 0.11.24", + "reqwest 0.12.4", "schemars", "serde", "serde_json", @@ -1438,9 +1476,9 @@ checksum = "147be55d677052dabc6b22252d5dd0fd4c29c8c27aa4f2fbef0f94aa003b406f" [[package]] name = "cookie" -version = "0.17.0" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7efb37c3e1ccb1ff97164ad95ac1606e8ccd35b3fa0a7d99a304c7f4a428cc24" +checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" dependencies = [ "percent-encoding", "time", @@ -2255,7 +2293,7 @@ name = "errors" version = "0.1.0" dependencies = [ "anyhow", - "http 0.2.9", + "http 1.1.0", "metrics", "prometheus", "proptest", @@ -2941,6 +2979,25 @@ dependencies = [ "tracing", ] +[[package]] +name = "h2" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa82e28a107a8cc405f0839610bdc9b15f1e25ec7d696aa5cf173edbcb1486ab" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http 1.1.0", + "indexmap 2.1.0", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "half" version = "1.8.2" @@ -2995,15 +3052,14 @@ dependencies = [ [[package]] name = "headers" -version = "0.3.8" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3e372db8e5c0d213e0cd0b9be18be2aca3d44cf2fe30a9d46a65581cd454584" +checksum = "322106e6bd0cba2d5ead589ddb8150a13d7c4217cf80d7c4f682ca994ccc6aa9" dependencies = [ - "base64 0.13.1", - "bitflags 1.3.2", + "base64 0.21.7", "bytes", "headers-core", - "http 0.2.9", + "http 1.1.0", "httpdate", "mime", "sha1", @@ -3011,11 +3067,11 @@ dependencies = [ [[package]] name = "headers-core" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429" +checksum = "54b4a22553d4242c49fddb9ba998a99962b5cc6f22cb5a3482bec22522403ce4" dependencies = [ - "http 0.2.9", + "http 1.1.0", ] [[package]] @@ -3084,13 +3140,13 @@ dependencies = [ [[package]] name = "hostname" -version = "0.3.1" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" +checksum = "f9c7c7c8ac16c798734b8a24560c1362120597c40d5e1459f09498f8f6c8f2ba" dependencies = [ + "cfg-if", "libc", - "match_cfg", - "winapi", + "windows 0.52.0", ] [[package]] @@ -3112,9 +3168,9 @@ dependencies = [ [[package]] name = "http" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b32afd38673a8016f7c9ae69e5af41a58f81b1d31689040f2f1959594ce194ea" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" dependencies = [ "bytes", "fnv", @@ -3139,18 +3195,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" dependencies = [ "bytes", - "http 1.0.0", + "http 1.1.0", ] [[package]] name = "http-body-util" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d" +checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" dependencies = [ "bytes", - "futures-core", - "http 1.0.0", + "futures-util", + "http 1.1.0", "http-body 1.0.0", "pin-project-lite", ] @@ -3160,12 +3216,26 @@ name = "http-cache" version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b5ab65432bbdfe8490dfde21d0366353a8d39f2bc24aca0146889f931b0b4b5" +dependencies = [ + "async-trait", + "http 0.2.9", + "http-cache-semantics 1.0.1", + "httpdate", + "serde", + "url", +] + +[[package]] +name = "http-cache" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6ffb12b95bb2a369fe47ca8924016c72c2fa0e6059ba98bd1516f558696c5a8" dependencies = [ "async-trait", "bincode", "cacache", - "http 0.2.9", - "http-cache-semantics", + "http 1.1.0", + "http-cache-semantics 2.1.0", "httpdate", "moka", "serde", @@ -3174,19 +3244,18 @@ dependencies = [ [[package]] name = "http-cache-reqwest" -version = "0.13.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8285341ce7e709c56a0f259ff1c789c70edfbaa88acd69d27e4d63980b92dc" +checksum = "be3e27c4e2e510571cbcc601407b639667146aa9a4e818d5cc1d97c8b4b27d61" dependencies = [ "anyhow", "async-trait", - "http 0.2.9", - "http-cache", - "http-cache-semantics", - "reqwest 0.11.24", + "http 1.1.0", + "http-cache 0.19.0", + "http-cache-semantics 2.1.0", + "reqwest 0.12.4", "reqwest-middleware", "serde", - "task-local-extensions", "url", ] @@ -3197,16 +3266,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14246388577086faaaa56fb59f0b94e288800fecfff75918a237813297cdda17" dependencies = [ "http 0.2.9", - "http-serde", + "http-serde 1.1.2", "serde", "time", ] [[package]] -name = "http-range-header" -version = "0.3.0" +name = "http-cache-semantics" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bfe8eed0a9285ef776bb792479ea3834e8b94e13d615c2f66d03dd50a435a29" +checksum = "92baf25cf0b8c9246baecf3a444546360a97b569168fdf92563ee6a47829920c" +dependencies = [ + "http 1.1.0", + "http-serde 2.1.1", + "serde", + "time", +] [[package]] name = "http-serde" @@ -3218,17 +3293,27 @@ dependencies = [ "serde", ] +[[package]] +name = "http-serde" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f056c8559e3757392c8d091e796416e4649d8e49e88b8d76df6c002f05027fd" +dependencies = [ + "http 1.1.0", + "serde", +] + [[package]] name = "http_client" version = "0.1.0" dependencies = [ "anyhow", "futures", - "http-cache", + "http-cache 0.18.0", "http-cache-reqwest", "metrics", "openidconnect", - "reqwest 0.11.24", + "reqwest 0.12.4", "reqwest-middleware", "strum 0.26.2", "thiserror", @@ -3266,7 +3351,7 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "h2", + "h2 0.3.26", "http 0.2.9", "http-body 0.4.5", "httparse", @@ -3289,7 +3374,8 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "http 1.0.0", + "h2 0.4.5", + "http 1.1.0", "http-body 1.0.0", "httparse", "httpdate", @@ -3309,34 +3395,38 @@ dependencies = [ "futures-util", "http 0.2.9", "hyper 0.14.27", - "rustls", + "rustls 0.21.11", "tokio", - "tokio-rustls", + "tokio-rustls 0.24.1", ] [[package]] name = "hyper-timeout" -version = "0.4.1" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" +checksum = "3203a961e5c83b6f5498933e78b6b263e208c197b63e9c6c53cc82ffd3f63793" dependencies = [ - "hyper 0.14.27", + "hyper 1.3.1", + "hyper-util", "pin-project-lite", "tokio", - "tokio-io-timeout", + "tower-service", ] [[package]] name = "hyper-tls" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", - "hyper 0.14.27", + "http-body-util", + "hyper 1.3.1", + "hyper-util", "native-tls", "tokio", "tokio-native-tls", + "tower-service", ] [[package]] @@ -3348,7 +3438,7 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "http 1.0.0", + "http 1.1.0", "http-body 1.0.0", "hyper 1.3.1", "pin-project-lite", @@ -3618,9 +3708,10 @@ dependencies = [ "file_storage", "futures", "headers", - "http 0.2.9", + "http 1.1.0", + "http-body-util", "humansize", - "hyper 0.14.27", + "hyper 1.3.1", "itertools 0.13.0", "keybroker", "maplit", @@ -3675,15 +3766,6 @@ dependencies = [ "either", ] -[[package]] -name = "itertools" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" -dependencies = [ - "either", -] - [[package]] name = "itertools" version = "0.12.1" @@ -3912,6 +3994,7 @@ dependencies = [ "async-trait", "authentication", "axum", + "axum-extra", "base64 0.13.1", "clap", "cmd_util", @@ -3926,8 +4009,10 @@ dependencies = [ "futures", "futures-async-stream", "hex", - "http 0.2.9", - "hyper 0.14.27", + "http 1.1.0", + "http-body-util", + "hyper 1.3.1", + "hyper-util", "isolate", "keybroker", "maplit", @@ -4093,12 +4178,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" -[[package]] -name = "match_cfg" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" - [[package]] name = "matchers" version = "0.1.0" @@ -4265,16 +4344,6 @@ version = "0.1.52" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1a85a5069ebd40e64b1985773cc81addbe9d90d7ecf60e7b5475a57ad584c70" -[[package]] -name = "mime_guess" -version = "2.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" -dependencies = [ - "mime", - "unicase", -] - [[package]] name = "minimal-lexical" version = "0.2.1" @@ -4422,16 +4491,15 @@ dependencies = [ [[package]] name = "multer" -version = "2.1.0" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01acbdc23469fd8fe07ab135923371d5f5a422fbf9c522158677c8eb15bc51c2" +checksum = "83e87776546dc87511aa5ee218730c92b666d7264ab6ed41f9d215af9cd5224b" dependencies = [ "bytes", "encoding_rs", "futures-util", - "http 0.2.9", + "http 1.1.0", "httparse", - "log", "memchr", "mime", "spin 0.9.8", @@ -4522,7 +4590,7 @@ dependencies = [ "errors", "futures", "home", - "http 0.2.9", + "http 1.1.0", "isolate", "keybroker", "maplit", @@ -5462,17 +5530,17 @@ dependencies = [ [[package]] name = "proptest-http" version = "0.1.0" -source = "git+https://github.com/sujayakar/proptest-http?rev=12ef4c4ac2002b050ab0944a9587d83d7ea9fce2#12ef4c4ac2002b050ab0944a9587d83d7ea9fce2" +source = "git+https://github.com/nipunn1313/proptest-http?rev=0e658bd4f6dbb73bdd3be66f0d2c34c00cc3a446#0e658bd4f6dbb73bdd3be66f0d2c34c00cc3a446" dependencies = [ - "http 0.2.9", + "http 1.1.0", "proptest", ] [[package]] name = "prost" -version = "0.12.1" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4fdd22f3b9c31b53c060df4a0613a1c7f062d4115a2b984dd15b1858f7e340d" +checksum = "e13db3d3fde688c61e2446b4d843bc27a7e8af269a69440c0308021dc92333cc" dependencies = [ "bytes", "prost-derive", @@ -5480,13 +5548,13 @@ dependencies = [ [[package]] name = "prost-build" -version = "0.12.1" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bdf592881d821b83d471f8af290226c8d51402259e9bb5be7f9f8bdebbb11ac" +checksum = "5bb182580f71dd070f88d01ce3de9f4da5021db7115d2e1c3605a754153b77c1" dependencies = [ "bytes", - "heck 0.4.1", - "itertools 0.11.0", + "heck 0.5.0", + "itertools 0.13.0", "log", "multimap", "once_cell", @@ -5497,17 +5565,16 @@ dependencies = [ "regex", "syn 2.0.60", "tempfile", - "which 4.4.0", ] [[package]] name = "prost-derive" -version = "0.12.1" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "265baba7fabd416cf5078179f7d2cbeca4ce7a9041111900675ea7c4cb8a4c32" +checksum = "18bec9b0adc4eba778b33684b7ba3e7137789434769ee3ce3930463ef904cfca" dependencies = [ "anyhow", - "itertools 0.11.0", + "itertools 0.13.0", "proc-macro2", "quote", "syn 2.0.60", @@ -5515,9 +5582,9 @@ dependencies = [ [[package]] name = "prost-reflect" -version = "0.13.1" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f5eec97d5d34bdd17ad2db2219aabf46b054c6c41bd5529767c9ce55be5898f" +checksum = "55a6a9143ae25c25fa7b6a48d6cc08b10785372060009c25140a4e7c340e95af" dependencies = [ "logos", "miette 7.2.0", @@ -5528,9 +5595,9 @@ dependencies = [ [[package]] name = "prost-types" -version = "0.12.1" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e081b29f63d83a4bc75cfc9f3fe424f9156cf92d8a4f0c9407cce9a1b67327cf" +checksum = "cee5168b05f49d4b0ca581206eb14a7b22fafd963efe729ac48eb03266e25cc2" dependencies = [ "prost", ] @@ -5877,45 +5944,38 @@ version = "0.11.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6920094eb85afde5e4a138be3f2de8bbdf28000f0029e72c45025a56b042251" dependencies = [ - "async-compression 0.4.11", - "base64 0.21.0", + "base64 0.21.7", "bytes", "encoding_rs", "futures-core", "futures-util", - "h2", + "h2 0.3.26", "http 0.2.9", "http-body 0.4.5", "hyper 0.14.27", "hyper-rustls", - "hyper-tls", "ipnet", "js-sys", "log", "mime", - "mime_guess", - "native-tls", "once_cell", "percent-encoding", "pin-project-lite", - "rustls", - "rustls-pemfile", + "rustls 0.21.11", + "rustls-pemfile 1.0.2", "serde", "serde_json", "serde_urlencoded", - "sync_wrapper", + "sync_wrapper 0.1.2", "system-configuration", "tokio", - "tokio-native-tls", - "tokio-rustls", - "tokio-util", + "tokio-rustls 0.24.1", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", - "wasm-streams", "web-sys", - "webpki-roots", + "webpki-roots 0.25.2", "winreg 0.50.0", ] @@ -5925,49 +5985,59 @@ version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "566cafdd92868e0939d3fb961bd0dc25fcfaaed179291093b3d43e6b3150ea10" dependencies = [ + "async-compression 0.4.11", "base64 0.22.0", "bytes", + "encoding_rs", "futures-channel", "futures-core", "futures-util", - "http 1.0.0", + "h2 0.4.5", + "http 1.1.0", "http-body 1.0.0", "http-body-util", "hyper 1.3.1", + "hyper-tls", "hyper-util", "ipnet", "js-sys", "log", "mime", + "native-tls", "once_cell", "percent-encoding", "pin-project-lite", + "rustls-pemfile 2.1.2", "serde", "serde_json", "serde_urlencoded", - "sync_wrapper", + "sync_wrapper 0.1.2", + "system-configuration", "tokio", + "tokio-native-tls", + "tokio-util", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", + "wasm-streams", "web-sys", "winreg 0.52.0", ] [[package]] name = "reqwest-middleware" -version = "0.2.4" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88a3e86aa6053e59030e7ce2d2a3b258dd08fc2d337d52f73f6cb480f5858690" +checksum = "39346a33ddfe6be00cbc17a34ce996818b97b230b87229f10114693becca1268" dependencies = [ "anyhow", "async-trait", - "http 0.2.9", - "reqwest 0.11.24", + "http 1.1.0", + "reqwest 0.12.4", "serde", - "task-local-extensions", "thiserror", + "tower-service", ] [[package]] @@ -6166,18 +6236,33 @@ checksum = "7fecbfb7b1444f477b345853b1fce097a2c6fb637b2bfb87e6bc5db0f043fae4" dependencies = [ "log", "ring", - "rustls-webpki", + "rustls-webpki 0.101.7", "sct", ] +[[package]] +name = "rustls" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432" +dependencies = [ + "log", + "ring", + "rustls-pki-types", + "rustls-webpki 0.102.3", + "subtle", + "zeroize", +] + [[package]] name = "rustls-native-certs" -version = "0.6.3" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" +checksum = "a88d6d420651b496bdd98684116959239430022a115c1240e6c3993be0b15fba" dependencies = [ "openssl-probe", - "rustls-pemfile", + "rustls-pemfile 2.1.2", + "rustls-pki-types", "schannel", "security-framework", ] @@ -6188,9 +6273,25 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" dependencies = [ - "base64 0.21.0", + "base64 0.21.7", +] + +[[package]] +name = "rustls-pemfile" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d" +dependencies = [ + "base64 0.22.0", + "rustls-pki-types", ] +[[package]] +name = "rustls-pki-types" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "beb461507cee2c2ff151784c52762cf4d9ff6a61f3e80968600ed24fa837fa54" + [[package]] name = "rustls-webpki" version = "0.101.7" @@ -6201,6 +6302,17 @@ dependencies = [ "untrusted", ] +[[package]] +name = "rustls-webpki" +version = "0.102.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3bce581c0dd41bce533ce695a1437fa16a7ab5ac3ccfa99fe1a620a7885eabf" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + [[package]] name = "rustversion" version = "1.0.12" @@ -6500,13 +6612,13 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "sentry" -version = "0.31.0" +version = "0.32.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c3d7f8bf7373e75222452fcdd9347d857452a92d0eec738f941bc4656c5b5df" +checksum = "00421ed8fa0c995f07cde48ba6c89e80f2b312f74ff637326f392fbfd23abe02" dependencies = [ "httpdate", "native-tls", - "reqwest 0.11.24", + "reqwest 0.12.4", "sentry-anyhow", "sentry-backtrace", "sentry-contexts", @@ -6514,15 +6626,16 @@ dependencies = [ "sentry-debug-images", "sentry-panic", "sentry-tower", + "sentry-tracing", "tokio", "ureq", ] [[package]] name = "sentry-anyhow" -version = "0.31.0" +version = "0.32.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ef7f47c57a1146d553b4976f20e8bba370195a88858bdf6945a63c529549236" +checksum = "ddcbce6e6785c2d91e67c55196f60ac561fab5946b6c7d60cc29f498fc126076" dependencies = [ "anyhow", "sentry-backtrace", @@ -6531,9 +6644,9 @@ dependencies = [ [[package]] name = "sentry-backtrace" -version = "0.31.0" +version = "0.32.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03b7cdefbdca51f1146f0f24a3cb4ecb6428951f030ff5c720cfb5c60bd174c0" +checksum = "a79194074f34b0cbe5dd33896e5928bbc6ab63a889bd9df2264af5acb186921e" dependencies = [ "backtrace", "once_cell", @@ -6543,9 +6656,9 @@ dependencies = [ [[package]] name = "sentry-contexts" -version = "0.31.0" +version = "0.32.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6af4cb29066e0e8df0cc3111211eb93543ccb09e1ccbe71de6d88b4bb459a2b1" +checksum = "eba8870c5dba2bfd9db25c75574a11429f6b95957b0a78ac02e2970dd7a5249a" dependencies = [ "hostname", "libc", @@ -6557,9 +6670,9 @@ dependencies = [ [[package]] name = "sentry-core" -version = "0.31.0" +version = "0.32.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e781b55761e47a60d1ff326ae8059de22b0e6b0cee68eab1c5912e4fb199a76" +checksum = "46a75011ea1c0d5c46e9e57df03ce81f5c7f0a9e199086334a1f9c0a541e0826" dependencies = [ "once_cell", "rand 0.8.5", @@ -6570,9 +6683,9 @@ dependencies = [ [[package]] name = "sentry-debug-images" -version = "0.31.0" +version = "0.32.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e758030b31ee2cd97424a980dfa34a12dcd8477424861cf81ae3aa1f9f616a8c" +checksum = "7ec2a486336559414ab66548da610da5e9626863c3c4ffca07d88f7dc71c8de8" dependencies = [ "findshlibs", "once_cell", @@ -6581,9 +6694,9 @@ dependencies = [ [[package]] name = "sentry-panic" -version = "0.31.0" +version = "0.32.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e0b877981990d9e84ae6916df61993d188fdf76afb59521f0aeaf9b8e6d26d0" +checksum = "2eaa3ecfa3c8750c78dcfd4637cfa2598b95b52897ed184b4dc77fcf7d95060d" dependencies = [ "sentry-backtrace", "sentry-core", @@ -6591,11 +6704,11 @@ dependencies = [ [[package]] name = "sentry-tower" -version = "0.31.0" +version = "0.32.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e934c257597464ec8fb9067e44d29bd93d55056ef638931b46c80a3be8793562" +checksum = "df141464944fdf8e2a6f2184eb1d973a20456466f788346b6e3a51791cdaa370" dependencies = [ - "http 0.2.9", + "http 1.1.0", "pin-project", "sentry-core", "tower-layer", @@ -6603,15 +6716,27 @@ dependencies = [ "url", ] +[[package]] +name = "sentry-tracing" +version = "0.32.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f715932bf369a61b7256687c6f0554141b7ce097287e30e3f7ed6e9de82498fe" +dependencies = [ + "sentry-backtrace", + "sentry-core", + "tracing-core", + "tracing-subscriber", +] + [[package]] name = "sentry-types" -version = "0.31.0" +version = "0.32.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d642a04657cc77d8de52ae7c6d93a15cb02284eb219344a89c1e2b26bbaf578c" +checksum = "4519c900ce734f7a0eb7aba0869dfb225a7af8820634a7dd51449e3b093cfb7c" dependencies = [ "debugid", - "getrandom 0.2.12", "hex", + "rand 0.8.5", "serde", "serde_json", "thiserror", @@ -6741,7 +6866,7 @@ version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64cd236ccc1b7a29e7e2739f27c0b2dd199804abc4290e32f59f3b68d6405c23" dependencies = [ - "base64 0.21.0", + "base64 0.21.7", "chrono", "hex", "indexmap 1.9.3", @@ -7188,7 +7313,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e37195395df71fd068f6e2082247891bc11e3289624bbc776a0cdfa1ca7f1ea4" dependencies = [ "atoi", - "base64 0.21.0", + "base64 0.21.7", "bitflags 2.4.2", "byteorder", "bytes", @@ -7232,7 +7357,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6ac0ac3b7ccd10cc96c7ab29791a7dd236bd94021f31eec7ba3d46a74aa1c24" dependencies = [ "atoi", - "base64 0.21.0", + "base64 0.21.7", "bitflags 2.4.2", "byteorder", "chrono", @@ -7297,7 +7422,7 @@ version = "9.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da7a2b3c2bc9693bcb40870c4e9b5bf0d79f9cb46273321bf855ec513e919082" dependencies = [ - "base64 0.21.0", + "base64 0.21.7", "digest", "hex", "miette 5.10.0", @@ -7333,8 +7458,9 @@ dependencies = [ "derive_more", "futures", "futures-async-stream", - "http 0.2.9", - "hyper 0.14.27", + "http 1.1.0", + "http-body-util", + "hyper 1.3.1", "pb", "pin-project", "runtime", @@ -7504,6 +7630,12 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +[[package]] +name = "sync_wrapper" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" + [[package]] name = "sysinfo" version = "0.30.12" @@ -7554,7 +7686,7 @@ dependencies = [ "aho-corasick 0.7.20", "arc-swap", "async-trait", - "base64 0.21.0", + "base64 0.21.7", "bitpacking 0.8.4", "byteorder", "census", @@ -7695,15 +7827,6 @@ dependencies = [ "xattr", ] -[[package]] -name = "task-local-extensions" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba323866e5d033818e3240feeb9f7db2c4296674e4d9e16b97b7bf8f490434e8" -dependencies = [ - "pin-utils", -] - [[package]] name = "tempfile" version = "3.10.1" @@ -7899,16 +8022,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "tokio-io-timeout" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf" -dependencies = [ - "pin-project-lite", - "tokio", -] - [[package]] name = "tokio-macros" version = "2.2.0" @@ -7976,7 +8089,18 @@ version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" dependencies = [ - "rustls", + "rustls 0.21.11", + "tokio", +] + +[[package]] +name = "tokio-rustls" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f" +dependencies = [ + "rustls 0.22.4", + "rustls-pki-types", "tokio", ] @@ -7994,20 +8118,21 @@ dependencies = [ [[package]] name = "tokio-tungstenite" -version = "0.20.1" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "212d5dcb2a1ce06d81107c3d0ffa3121fe974b73f068c8282cb1c32328113b6c" +checksum = "c83b561d025642014097b66e6c1bb422783339e0909e4429cde4749d1990bc38" dependencies = [ "futures-util", "log", "native-tls", - "rustls", + "rustls 0.22.4", "rustls-native-certs", + "rustls-pki-types", "tokio", "tokio-native-tls", - "tokio-rustls", + "tokio-rustls 0.25.0", "tungstenite", - "webpki-roots", + "webpki-roots 0.26.1", ] [[package]] @@ -8043,24 +8168,27 @@ dependencies = [ [[package]] name = "tonic" -version = "0.10.2" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d560933a0de61cf715926b9cac824d4c883c2c43142f787595e48280c40a1d0e" +checksum = "38659f4a91aba8598d27821589f5db7dddd94601e7a01b1e485a50e5484c7401" dependencies = [ "async-stream", "async-trait", "axum", - "base64 0.21.0", + "base64 0.22.0", "bytes", "flate2", - "h2", - "http 0.2.9", - "http-body 0.4.5", - "hyper 0.14.27", + "h2 0.4.5", + "http 1.1.0", + "http-body 1.0.0", + "http-body-util", + "hyper 1.3.1", "hyper-timeout", + "hyper-util", "percent-encoding", "pin-project", "prost", + "socket2 0.5.7", "tokio", "tokio-stream", "tower", @@ -8071,9 +8199,9 @@ dependencies = [ [[package]] name = "tonic-build" -version = "0.10.2" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d021fc044c18582b9a2408cd0dd05b1596e3ecdb5c4df822bb0183545683889" +checksum = "568392c5a2bd0020723e3f387891176aabafe36fd9fcd074ad309dfa0c8eb964" dependencies = [ "prettyplease", "proc-macro2", @@ -8084,9 +8212,9 @@ dependencies = [ [[package]] name = "tonic-health" -version = "0.10.2" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f80db390246dfb46553481f6024f0082ba00178ea495dbb99e70ba9a4fafb5e1" +checksum = "e1e10e6a96ee08b6ce443487d4368442d328d0e746f3681f81127f7dc41b4955" dependencies = [ "async-stream", "prost", @@ -8117,15 +8245,15 @@ dependencies = [ [[package]] name = "tower-cookies" -version = "0.9.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40f38d941a2ffd8402b36e02ae407637a9caceb693aaf2edc910437db0f36984" +checksum = "4fd0118512cf0b3768f7fcccf0bef1ae41d68f2b45edc1e77432b36c97c56c6d" dependencies = [ "async-trait", "axum-core", "cookie", "futures-util", - "http 0.2.9", + "http 1.1.0", "parking_lot", "pin-project-lite", "tower-layer", @@ -8134,18 +8262,17 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.4.0" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d1d42a9b3f3ec46ba828e8d376aec14592ea199f70a06a548587ecd1c4ab658" +checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5" dependencies = [ - "async-compression 0.3.15", - "bitflags 1.3.2", + "async-compression 0.4.11", + "bitflags 2.4.2", "bytes", "futures-core", - "futures-util", - "http 0.2.9", - "http-body 0.4.5", - "http-range-header", + "http 1.1.0", + "http-body 1.0.0", + "http-body-util", "pin-project-lite", "tokio", "tokio-util", @@ -8287,19 +8414,20 @@ checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" [[package]] name = "tungstenite" -version = "0.20.1" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e3dac10fd62eaf6617d3a904ae222845979aec67c615d1c842b4002c7666fb9" +checksum = "9ef1a641ea34f399a848dea702823bbecfb4c486f911735368f1f137cb8257e1" dependencies = [ "byteorder", "bytes", "data-encoding", - "http 0.2.9", + "http 1.1.0", "httparse", "log", "native-tls", "rand 0.8.5", - "rustls", + "rustls 0.22.4", + "rustls-pki-types", "sha1", "thiserror", "url", @@ -8418,9 +8546,9 @@ dependencies = [ [[package]] name = "url" -version = "2.5.0" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" dependencies = [ "form_urlencoded", "idna 0.5.0", @@ -8490,7 +8618,7 @@ dependencies = [ "home", "miniz_oxide", "once_cell", - "which 5.0.0", + "which", ] [[package]] @@ -8771,23 +8899,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14247bb57be4f377dfb94c72830b8ce8fc6beac03cf4bf7b9732eadd414123fc" [[package]] -name = "whatlang" -version = "0.16.4" +name = "webpki-roots" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "471d1c1645d361eb782a1650b1786a8fb58dd625e681a04c09f5ff7c8764a7b0" +checksum = "b3de34ae270483955a94f4b21bdaaeb83d508bb84a01435f393818edb0012009" dependencies = [ - "hashbrown 0.14.2", - "once_cell", + "rustls-pki-types", ] [[package]] -name = "which" -version = "4.4.0" +name = "whatlang" +version = "0.16.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269" +checksum = "471d1c1645d361eb782a1650b1786a8fb58dd625e681a04c09f5ff7c8764a7b0" dependencies = [ - "either", - "libc", + "hashbrown 0.14.2", "once_cell", ] diff --git a/Cargo.toml b/Cargo.toml index 50366cb7..21a5c004 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,8 @@ async_zip = { version = "0.0.9", default-features = false, features = [ "zstd", cbc = { version = "0.1.2" } csv-async = "1.2" atomic_refcell = "0.1.13" -axum = { version = "0.6", features = [ "headers", "ws", "original-uri", "macros", "multipart" ] } +axum = { version = "0.7", features = [ "ws", "original-uri", "macros", "multipart" ] } +axum-extra = { version = "0.9.3", features = [ "typed-header" ] } base32 = "0.4.0" base-62 = { rev = "6f91a3da4b83f20379469835f64195bd52d7b9dd", git = "https://github.com/kryptco/base62.rs" } base64 = "0.13" @@ -49,15 +50,17 @@ futures = "0.3" futures-async-stream = "0.2.11" futures-util = "0.3.30" governor = "0.6.0" -headers = "0.3" +headers = "0.4" hex = "0.4" home = "0.5" enum-iterator = "2.0.0" -http = "0.2.8" +http = "1.0.0" http-cache = { version = "0.18", default-features = false } -http-cache-reqwest = { version = "0.13.0", features = [ "manager-moka" ] } +http-body-util = "0.1.2" +http-cache-reqwest = { version = "0.14.0", features = [ "manager-moka" ] } humansize = { version = "2.1.3", features = [ "impl_style" ] } -hyper = "0.14.16" +hyper = "1.3.1" +hyper-util = { version = "0.1.5", features = [ "server-graceful" ] } proc-macro2 = { version = "1.0" } imbl = "3.0.0" itertools = "0.13" @@ -85,10 +88,10 @@ prometheus = { git = "https://github.com/get-convex/rust-prometheus", rev = "061 prometheus-hyper = "0.2.0" proptest = "1" proptest-derive = "0.4.0" -proptest-http = { git = "https://github.com/sujayakar/proptest-http", rev = "12ef4c4ac2002b050ab0944a9587d83d7ea9fce2" } -prost = "0.12" -prost-types = "0.12" -prost-reflect = { version = "0.13.1", features = [ "text-format", "miette" ] } +proptest-http = { git = "https://github.com/nipunn1313/proptest-http", rev = "0e658bd4f6dbb73bdd3be66f0d2c34c00cc3a446" } +prost = "0.13" +prost-types = "0.13" +prost-reflect = { version = "0.14.0", features = [ "text-format", "miette" ] } pyo3 = "0.20.3" pyo3-asyncio = { version = "0.20.0", features = [ "tokio-runtime" ] } pyo3-build-config = { version = "0.20.3", features = [ "resolve-config" ] } @@ -99,15 +102,15 @@ rand = "0.8" rand_chacha = "0.3.1" ref-cast = "1.0.20" regex = "1" -reqwest = { version = "0.11.24", features = [ "json", "stream", "gzip", "native-tls-vendored" ] } -reqwest-middleware = "0.2.0" +reqwest = { version = "0.12.1", features = [ "json", "stream", "gzip", "native-tls-vendored" ] } +reqwest-middleware = "0.3.2" ring = "0.17.8" rsa = "0.9.6" rusqlite = { version = "0.30", features = [ "bundled" ] } saffron = { git = "https://github.com/get-convex/saffron", rev = "1d842379919fb5c1988ac127cebd6167b1eb9bec", features = [ "std" ] } schemars = { version = "0.8" } semver = { version = "1", features = [ "serde" ] } -sentry = { version = "0.31", features = [ "anyhow", "tower", "tower-http" ] } +sentry = { version = "0.32", features = [ "anyhow", "tower", "tower-http" ] } serde = { version = "1", features = [ "derive" ] } serde_json = { version = "1", features = [ "float_roundtrip", "preserve_order" ] } sha1 = { version = "0.10.5", features = [ "oid" ] } @@ -128,19 +131,19 @@ tokio = { version = "1", features = [ "full" ] } tokio-metrics-collector = { version = "0.2.1" } tokio-process-stream = { version = "0.4.0" } tokio-stream = { version = "0.1", features = [ "io-util", "sync", "signal" ] } -tokio-tungstenite = { version = "0.20.0", features = [ "native-tls-vendored" ] } -tonic = { version = "0.10.2", features = [ "gzip" ] } -tonic-build = "0.10.0" -tonic-health = "0.10.0" +tokio-tungstenite = { version = "0.21.0", features = [ "native-tls-vendored" ] } +tonic = { version = "0.12.0", features = [ "gzip" ] } +tonic-build = "0.12.0" +tonic-health = "0.12.0" tower = { version = "0.4", features = [ "limit", "timeout" ] } -tower-cookies = "0.9" -tower-http = { version = "0.4", features = [ "trace", "cors", "decompression-br" ] } +tower-cookies = "0.10" +tower-http = { version = "0.5", features = [ "trace", "cors", "decompression-br" ] } tracing = "0.1" tracing-appender = { version = "0.2" } tracing-subscriber = { version = "0.3.17", features = [ "env-filter", "json" ] } tracy-client = { version = "0.17.0", default-features = false, features = [ "fibers" ] } -tungstenite = { version = "0.20.0", features = [ "native-tls-vendored" ] } -url = "2" +tungstenite = { version = "0.21.0", features = [ "url", "native-tls-vendored" ] } +url = "2.5.2" urlencoding = "2.1.3" uuid = { version = "1.6", features = [ "serde", "v4" ] } walkdir = "2" diff --git a/crates/authentication/src/lib.rs b/crates/authentication/src/lib.rs index b5e64285..579da7ba 100644 --- a/crates/authentication/src/lib.rs +++ b/crates/authentication/src/lib.rs @@ -19,12 +19,6 @@ use chrono::TimeZone; use common::auth::AuthInfo; use errors::ErrorMetadata; use futures::Future; -use http::{ - header::ACCEPT, - HeaderValue, - Method, - StatusCode, -}; use keybroker::UserIdentity; use oauth2::{ HttpRequest, @@ -36,6 +30,12 @@ use openidconnect::{ CoreIdTokenVerifier, CoreProviderMetadata, }, + http::{ + header::ACCEPT, + HeaderValue, + Method, + StatusCode, + }, ClaimsVerificationError, ClientId, DiscoveryError, @@ -162,9 +162,11 @@ where match e { DiscoveryError::Response(code, body, _) => { let long = format!("{long}: {} {}", code, String::from_utf8_lossy(&body)); - if let Some(em) = - ErrorMetadata::from_http_status_code(code, short, long.clone()) - { + if let Some(em) = ErrorMetadata::from_http_status_code( + code.to_string().parse().unwrap(), + short, + long.clone(), + ) { em } else { ErrorMetadata::bad_request(short, long) @@ -397,7 +399,6 @@ mod tests { Future, FutureExt, }; - use http::StatusCode; use keybroker::testing::TEST_SIGNING_KEY; use openidconnect::{ core::{ @@ -413,6 +414,7 @@ mod tests { CoreResponseType, CoreSubjectIdentifierType, }, + http::StatusCode, AdditionalClaims, Audience, EmptyAdditionalClaims, diff --git a/crates/common/Cargo.toml b/crates/common/Cargo.toml index acefc8f3..55a80e8a 100644 --- a/crates/common/Cargo.toml +++ b/crates/common/Cargo.toml @@ -30,11 +30,14 @@ event-listener = { workspace = true } float_next_after = { workspace = true } futures = { workspace = true } futures-async-stream = { workspace = true } +futures-util = { workspace = true } governor = { workspace = true } headers = { workspace = true } hex = { workspace = true } http = { workspace = true } +http-body-util = { workspace = true } hyper = { workspace = true } +hyper-util = { workspace = true } imbl = { workspace = true } itertools = { workspace = true } maplit = { workspace = true } diff --git a/crates/common/src/grpc/mod.rs b/crates/common/src/grpc/mod.rs index a8ed3bac..a2dd6f87 100644 --- a/crates/common/src/grpc/mod.rs +++ b/crates/common/src/grpc/mod.rs @@ -3,12 +3,11 @@ use std::{ net::SocketAddr, }; -use axum::body::Body; use futures::Future; use sentry::integrations::tower as sentry_tower; use tonic::{ server::NamedService, - transport::server::Routes, + service::Routes, }; use tonic_health::{ server::{ @@ -39,7 +38,7 @@ impl ConvexGrpcService { pub fn add_service(mut self, service: S) -> Self where S: tower::Service< - http::Request, + http::Request, Response = http::Response, Error = Infallible, > + NamedService diff --git a/crates/common/src/http/extract.rs b/crates/common/src/http/extract.rs index 96556cce..81827ab9 100644 --- a/crates/common/src/http/extract.rs +++ b/crates/common/src/http/extract.rs @@ -2,20 +2,16 @@ use std::time::Instant; use async_trait::async_trait; use axum::{ - body::HttpBody, extract::{ FromRequest, FromRequestParts, - }, - http::request::{ - Parts, Request, }, + http::request::Parts, response::{ IntoResponse, Response, }, - BoxError, }; use errors::ErrorMetadata; use serde::{ @@ -90,17 +86,14 @@ pub struct Json(pub T); /// Wrapper type around axum::Json that uses HttpResponseError instead /// of JsonRejection to make sure we get propper logging / error reporting. #[async_trait] -impl FromRequest for Json +impl FromRequest for Json where T: DeserializeOwned, - B: HttpBody + Send + 'static, - B::Data: Send, - B::Error: Into, S: Send + Sync, { type Rejection = HttpResponseError; - async fn from_request(req: Request, state: &S) -> Result { + async fn from_request(req: Request, state: &S) -> Result { #[allow(clippy::disallowed_types)] let t = axum::Json::::from_request(req, state) .await diff --git a/crates/common/src/http/fetch.rs b/crates/common/src/http/fetch.rs index f6f59bb8..73733407 100644 --- a/crates/common/src/http/fetch.rs +++ b/crates/common/src/http/fetch.rs @@ -242,7 +242,7 @@ mod tests { // Ensure it doesn't panic. Regression test for. // https://github.com/seanmonstar/reqwest/issues/668 - assert!(err.to_string().contains("Parsed Url is not a valid Uri")); + assert!(format!("{err:?}").contains("Parsed Url is not a valid Uri")); Ok(()) } diff --git a/crates/common/src/http/fork_of_axum_serve.rs b/crates/common/src/http/fork_of_axum_serve.rs new file mode 100644 index 00000000..1dfa815c --- /dev/null +++ b/crates/common/src/http/fork_of_axum_serve.rs @@ -0,0 +1,723 @@ +//! Forked from axum in order to customize the builder. +//! https://github.com/tokio-rs/axum/blob/main/axum/src/serve.rs +//! https://linear.app/convex/issue/ENG-7171/check-if-axumserve-allows-configurability +//! +//! MIT Licensed https://github.com/tokio-rs/axum?tab=readme-ov-file#license +//! +//! Serve services. + +use std::{ + convert::Infallible, + fmt::Debug, + future::{ + poll_fn, + Future, + IntoFuture, + }, + io, + marker::PhantomData, + net::SocketAddr, + sync::Arc, + task::{ + Context, + Poll, + }, + time::Duration, +}; + +use axum::{ + body::Body, + extract::{ + connect_info::Connected, + Request, + }, + handler::HandlerService, + response::Response, + routing::MethodRouter, + Router, +}; +use futures_util::{ + pin_mut, + FutureExt, +}; +use hyper::body::Incoming; +use hyper_util::{ + rt::{ + TokioExecutor, + TokioIo, + }, + server::conn::auto::Builder, + service::TowerToHyperService, +}; +use tokio::{ + net::{ + TcpListener, + TcpStream, + }, + sync::watch, +}; +use tower::{ + Service, + ServiceExt as _, +}; +use tracing::{ + error, + trace, +}; + +use crate::http::MAX_HTTP2_STREAMS; + +/// Serve the service with the supplied listener. +/// +/// This method of running a service is intentionally simple and doesn't support +/// any configuration. Use hyper or hyper-util if you need configuration. +/// +/// It supports both HTTP/1 as well as HTTP/2. +/// +/// # Examples +/// +/// Serving a [`Router`]: +/// +/// ``` +/// use axum::{Router, routing::get}; +/// +/// # async { +/// let router = Router::new().route("/", get(|| async { "Hello, World!" })); +/// +/// let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); +/// axum::serve(listener, router).await.unwrap(); +/// # }; +/// ``` +/// +/// See also [`Router::into_make_service_with_connect_info`]. +/// +/// Serving a [`MethodRouter`]: +/// +/// ``` +/// use axum::routing::get; +/// +/// # async { +/// let router = get(|| async { "Hello, World!" }); +/// +/// let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); +/// axum::serve(listener, router).await.unwrap(); +/// # }; +/// ``` +/// +/// See also [`MethodRouter::into_make_service_with_connect_info`]. +/// +/// Serving a [`Handler`]: +/// +/// ``` +/// use axum::handler::HandlerWithoutStateExt; +/// +/// # async { +/// async fn handler() -> &'static str { +/// "Hello, World!" +/// } +/// +/// let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); +/// axum::serve(listener, handler.into_make_service()).await.unwrap(); +/// # }; +/// ``` +/// +/// See also [`HandlerWithoutStateExt::into_make_service_with_connect_info`] and +/// [`HandlerService::into_make_service_with_connect_info`]. +/// +/// [`Router`]: crate::Router +/// [`Router::into_make_service_with_connect_info`]: crate::Router::into_make_service_with_connect_info +/// [`MethodRouter`]: crate::routing::MethodRouter +/// [`MethodRouter::into_make_service_with_connect_info`]: crate::routing::MethodRouter::into_make_service_with_connect_info +/// [`Handler`]: crate::handler::Handler +/// [`HandlerWithoutStateExt::into_make_service_with_connect_info`]: crate::handler::HandlerWithoutStateExt::into_make_service_with_connect_info +/// [`HandlerService::into_make_service_with_connect_info`]: crate::handler::HandlerService::into_make_service_with_connect_info +pub fn serve(tcp_listener: TcpListener, make_service: M) -> Serve +where + M: for<'a> Service, Error = Infallible, Response = S>, + S: Service + Clone + Send + 'static, + S::Future: Send, +{ + Serve { + tcp_listener, + make_service, + tcp_nodelay: None, + _marker: PhantomData, + } +} + +/// Future returned by [`serve`]. +#[must_use = "futures must be awaited or polled"] +pub struct Serve { + tcp_listener: TcpListener, + make_service: M, + tcp_nodelay: Option, + _marker: PhantomData, +} + +impl Serve { + /// Prepares a server to handle graceful shutdown when the provided future + /// completes. + /// + /// # Example + /// + /// ``` + /// use axum::{Router, routing::get}; + /// + /// # async { + /// let router = Router::new().route("/", get(|| async { "Hello, World!" })); + /// + /// let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); + /// axum::serve(listener, router) + /// .with_graceful_shutdown(shutdown_signal()) + /// .await + /// .unwrap(); + /// # }; + /// + /// async fn shutdown_signal() { + /// // ... + /// } + /// ``` + pub fn with_graceful_shutdown(self, signal: F) -> WithGracefulShutdown + where + F: Future + Send + 'static, + { + WithGracefulShutdown { + tcp_listener: self.tcp_listener, + make_service: self.make_service, + signal, + tcp_nodelay: self.tcp_nodelay, + _marker: PhantomData, + } + } + + /// Instructs the server to set the value of the `TCP_NODELAY` option on + /// every accepted connection. + /// + /// See also [`TcpStream::set_nodelay`]. + /// + /// # Example + /// ``` + /// use axum::{Router, routing::get}; + /// + /// # async { + /// let router = Router::new().route("/", get(|| async { "Hello, World!" })); + /// + /// let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); + /// axum::serve(listener, router) + /// .tcp_nodelay(true) + /// .await + /// .unwrap(); + /// # }; + /// ``` + pub fn tcp_nodelay(self, nodelay: bool) -> Self { + Self { + tcp_nodelay: Some(nodelay), + ..self + } + } +} + +impl Debug for Serve +where + M: Debug, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let Self { + tcp_listener, + make_service, + tcp_nodelay, + _marker: _, + } = self; + + f.debug_struct("Serve") + .field("tcp_listener", tcp_listener) + .field("make_service", make_service) + .field("tcp_nodelay", tcp_nodelay) + .finish() + } +} + +impl IntoFuture for Serve +where + M: for<'a> Service, Error = Infallible, Response = S> + Send + 'static, + for<'a> >>::Future: Send, + S: Service + Clone + Send + 'static, + S::Future: Send, +{ + type IntoFuture = private::ServeFuture; + type Output = io::Result<()>; + + fn into_future(self) -> Self::IntoFuture { + private::ServeFuture(Box::pin(async move { + let Self { + tcp_listener, + mut make_service, + tcp_nodelay, + _marker: _, + } = self; + + loop { + let (tcp_stream, remote_addr) = match tcp_accept(&tcp_listener).await { + Some(conn) => conn, + None => continue, + }; + + if let Some(nodelay) = tcp_nodelay { + if let Err(err) = tcp_stream.set_nodelay(nodelay) { + trace!("failed to set TCP_NODELAY on incoming connection: {err:#}"); + } + } + + let tcp_stream = TokioIo::new(tcp_stream); + + poll_fn(|cx| make_service.poll_ready(cx)) + .await + .unwrap_or_else(|err| match err {}); + + let tower_service = make_service + .call(IncomingStream { + tcp_stream: &tcp_stream, + remote_addr, + }) + .await + .unwrap_or_else(|err| match err {}) + .map_request(|req: Request| req.map(Body::new)); + + let hyper_service = TowerToHyperService::new(tower_service); + + let mut builder = Builder::new(TokioExecutor::new()); + builder.http2().max_concurrent_streams(MAX_HTTP2_STREAMS); + + tokio::spawn(async move { + match builder + // upgrades needed for websockets + .serve_connection_with_upgrades(tcp_stream, hyper_service) + .await + { + Ok(()) => {}, + Err(_err) => { + // This error only appears when the client doesn't + // send a request and + // terminate the connection. + // + // If client sends one request then terminate + // connection whenever, it doesn't + // appear. + }, + } + }); + } + })) + } +} + +/// Serve future with graceful shutdown enabled. +#[must_use = "futures must be awaited or polled"] +pub struct WithGracefulShutdown { + tcp_listener: TcpListener, + make_service: M, + signal: F, + tcp_nodelay: Option, + _marker: PhantomData, +} + +impl WithGracefulShutdown { + /// Instructs the server to set the value of the `TCP_NODELAY` option on + /// every accepted connection. + /// + /// See also [`TcpStream::set_nodelay`]. + /// + /// # Example + /// ``` + /// use axum::{Router, routing::get}; + /// + /// # async { + /// let router = Router::new().route("/", get(|| async { "Hello, World!" })); + /// + /// let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); + /// axum::serve(listener, router) + /// .with_graceful_shutdown(shutdown_signal()) + /// .tcp_nodelay(true) + /// .await + /// .unwrap(); + /// # }; + /// + /// async fn shutdown_signal() { + /// // ... + /// } + /// ``` + pub fn tcp_nodelay(self, nodelay: bool) -> Self { + Self { + tcp_nodelay: Some(nodelay), + ..self + } + } +} + +impl Debug for WithGracefulShutdown +where + M: Debug, + S: Debug, + F: Debug, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let Self { + tcp_listener, + make_service, + signal, + tcp_nodelay, + _marker: _, + } = self; + + f.debug_struct("WithGracefulShutdown") + .field("tcp_listener", tcp_listener) + .field("make_service", make_service) + .field("signal", signal) + .field("tcp_nodelay", tcp_nodelay) + .finish() + } +} + +impl IntoFuture for WithGracefulShutdown +where + M: for<'a> Service, Error = Infallible, Response = S> + Send + 'static, + for<'a> >>::Future: Send, + S: Service + Clone + Send + 'static, + S::Future: Send, + F: Future + Send + 'static, +{ + type IntoFuture = private::ServeFuture; + type Output = io::Result<()>; + + fn into_future(self) -> Self::IntoFuture { + let Self { + tcp_listener, + mut make_service, + signal, + tcp_nodelay, + _marker: _, + } = self; + + let (signal_tx, signal_rx) = watch::channel(()); + let signal_tx = Arc::new(signal_tx); + tokio::spawn(async move { + signal.await; + trace!("received graceful shutdown signal. Telling tasks to shutdown"); + drop(signal_rx); + }); + + let (close_tx, close_rx) = watch::channel(()); + + private::ServeFuture(Box::pin(async move { + loop { + let (tcp_stream, remote_addr) = tokio::select! { + conn = tcp_accept(&tcp_listener) => { + match conn { + Some(conn) => conn, + None => continue, + } + } + _ = signal_tx.closed() => { + trace!("signal received, not accepting new connections"); + break; + } + }; + + if let Some(nodelay) = tcp_nodelay { + if let Err(err) = tcp_stream.set_nodelay(nodelay) { + trace!("failed to set TCP_NODELAY on incoming connection: {err:#}"); + } + } + + let tcp_stream = TokioIo::new(tcp_stream); + + trace!("connection {remote_addr} accepted"); + + poll_fn(|cx| make_service.poll_ready(cx)) + .await + .unwrap_or_else(|err| match err {}); + + let tower_service = make_service + .call(IncomingStream { + tcp_stream: &tcp_stream, + remote_addr, + }) + .await + .unwrap_or_else(|err| match err {}) + .map_request(|req: Request| req.map(Body::new)); + + let hyper_service = TowerToHyperService::new(tower_service); + + let signal_tx = Arc::clone(&signal_tx); + + let close_rx = close_rx.clone(); + + tokio::spawn(async move { + let mut builder = Builder::new(TokioExecutor::new()); + builder.http2().max_concurrent_streams(MAX_HTTP2_STREAMS); + let conn = builder.serve_connection_with_upgrades(tcp_stream, hyper_service); + pin_mut!(conn); + + let signal_closed = signal_tx.closed().fuse(); + pin_mut!(signal_closed); + + loop { + tokio::select! { + result = conn.as_mut() => { + if let Err(_err) = result { + trace!("failed to serve connection: {_err:#}"); + } + break; + } + _ = &mut signal_closed => { + trace!("signal received in task, starting graceful shutdown"); + conn.as_mut().graceful_shutdown(); + } + } + } + + trace!("connection {remote_addr} closed"); + + drop(close_rx); + }); + } + + drop(close_rx); + drop(tcp_listener); + + trace!( + "waiting for {} task(s) to finish", + close_tx.receiver_count() + ); + close_tx.closed().await; + + Ok(()) + })) + } +} + +fn is_connection_error(e: &io::Error) -> bool { + matches!( + e.kind(), + io::ErrorKind::ConnectionRefused + | io::ErrorKind::ConnectionAborted + | io::ErrorKind::ConnectionReset + ) +} + +async fn tcp_accept(listener: &TcpListener) -> Option<(TcpStream, SocketAddr)> { + match listener.accept().await { + Ok(conn) => Some(conn), + Err(e) => { + if is_connection_error(&e) { + return None; + } + + // [From `hyper::Server` in 0.14](https://github.com/hyperium/hyper/blob/v0.14.27/src/server/tcp.rs#L186) + // + // > 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. If this option is `true`, the error + // > will be logged at the `error` level, since it is still a big deal, + // > and then the listener will sleep for 1 second. + // + // hyper allowed customizing this but axum does not. + error!("accept error: {e}"); + tokio::time::sleep(Duration::from_secs(1)).await; + None + }, + } +} + +mod private { + use std::{ + future::Future, + io, + pin::Pin, + task::{ + Context, + Poll, + }, + }; + + pub struct ServeFuture(pub(super) futures_util::future::BoxFuture<'static, io::Result<()>>); + + impl Future for ServeFuture { + type Output = io::Result<()>; + + #[inline] + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + self.0.as_mut().poll(cx) + } + } + + impl std::fmt::Debug for ServeFuture { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ServeFuture").finish_non_exhaustive() + } + } +} + +/// An incoming stream. +/// +/// Used with [`serve`] and [`IntoMakeServiceWithConnectInfo`]. +/// +/// [`IntoMakeServiceWithConnectInfo`]: crate::extract::connect_info::IntoMakeServiceWithConnectInfo +#[derive(Debug)] +pub struct IncomingStream<'a> { + tcp_stream: &'a TokioIo, + remote_addr: SocketAddr, +} + +impl IncomingStream<'_> { + /// Returns the local address that this stream is bound to. + pub fn local_addr(&self) -> std::io::Result { + self.tcp_stream.inner().local_addr() + } + + /// Returns the remote address that this stream is bound to. + pub fn remote_addr(&self) -> SocketAddr { + self.remote_addr + } +} + +const _: () = { + impl Service> for Router<()> { + type Error = Infallible; + type Future = std::future::Ready>; + type Response = Self; + + fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } + + fn call(&mut self, _req: IncomingStream<'_>) -> Self::Future { + // call `Router::with_state` such that everything is turned into `Route` eagerly + // rather than doing that per request + std::future::ready(Ok(self.clone().with_state(()))) + } + } +}; + +const _: () = { + impl Connected> for SocketAddr { + fn connect_info(target: IncomingStream<'_>) -> Self { + target.remote_addr() + } + } +}; + +const _: () = { + impl Service> for MethodRouter<()> { + type Error = Infallible; + type Future = std::future::Ready>; + type Response = Self; + + fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } + + fn call(&mut self, _req: IncomingStream<'_>) -> Self::Future { + std::future::ready(Ok(self.clone().with_state(()))) + } + } +}; + +const _: () = { + impl Service> for HandlerService + where + H: Clone, + S: Clone, + { + type Error = Infallible; + type Future = std::future::Ready>; + type Response = Self; + + fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } + + fn call(&mut self, _req: IncomingStream<'_>) -> Self::Future { + std::future::ready(Ok(self.clone())) + } + } +}; + +#[cfg(test)] +mod tests { + use axum::{ + handler::{ + Handler, + HandlerWithoutStateExt, + }, + routing::get, + Router, + }; + + use super::*; + + #[allow(dead_code, unused_must_use)] + async fn if_it_compiles_it_works() { + let router: Router = Router::new(); + + let addr = "0.0.0.0:0"; + + // router + serve(TcpListener::bind(addr).await.unwrap(), router.clone()); + serve( + TcpListener::bind(addr).await.unwrap(), + router.clone().into_make_service(), + ); + serve( + TcpListener::bind(addr).await.unwrap(), + router.into_make_service_with_connect_info::(), + ); + + // method router + serve(TcpListener::bind(addr).await.unwrap(), get(handler)); + serve( + TcpListener::bind(addr).await.unwrap(), + get(handler).into_make_service(), + ); + serve( + TcpListener::bind(addr).await.unwrap(), + get(handler).into_make_service_with_connect_info::(), + ); + + // handler + serve( + TcpListener::bind(addr).await.unwrap(), + handler.into_service(), + ); + serve( + TcpListener::bind(addr).await.unwrap(), + handler.with_state(()), + ); + serve( + TcpListener::bind(addr).await.unwrap(), + handler.into_make_service(), + ); + serve( + TcpListener::bind(addr).await.unwrap(), + handler.into_make_service_with_connect_info::(), + ); + + // nodelay + serve( + TcpListener::bind(addr).await.unwrap(), + handler.into_service(), + ) + .tcp_nodelay(true); + + serve( + TcpListener::bind(addr).await.unwrap(), + handler.into_service(), + ) + .with_graceful_shutdown(async { /*...*/ }) + .tcp_nodelay(true); + } + + async fn handler() {} +} diff --git a/crates/common/src/http/mod.rs b/crates/common/src/http/mod.rs index b95466c9..3cb6dbbf 100644 --- a/crates/common/src/http/mod.rs +++ b/crates/common/src/http/mod.rs @@ -25,22 +25,19 @@ use ::metrics::{ use anyhow::Context; use async_trait::async_trait; use axum::{ - body::{ - Body, - BoxBody, - }, + body::Body, error_handling::HandleErrorLayer, extract::{ connect_info::IntoMakeServiceWithConnectInfo, FromRequestParts, Host, + Request, State, }, - http::{ + response::{ + IntoResponse, Response, - StatusCode, }, - response::IntoResponse, routing::get, BoxError, RequestPartsExt, @@ -68,12 +65,12 @@ use http::{ request::Parts, HeaderMap, Method, + StatusCode, Uri, }; -use hyper::server::conn::AddrIncoming; use itertools::Itertools; use maplit::btreemap; -use minitrace::future::FutureExt; +use minitrace::future::FutureExt as _; use prometheus::{ PullingGauge, TextEncoder, @@ -113,6 +110,7 @@ use crate::{ pub mod extract; pub mod fetch; +pub mod fork_of_axum_serve; const MAX_HTTP2_STREAMS: u32 = 1024; @@ -472,7 +470,7 @@ impl HttpError { &self.msg } - pub fn into_response(self) -> Response { + pub fn into_response(self) -> Response { if self.msg.is_empty() && self.error_code.is_empty() { self.status_code.into_response() } else { @@ -498,16 +496,15 @@ impl HttpError { // Tests might parse a response back into a message #[cfg(any(test, feature = "testing"))] - pub async fn from_response(response: Response) -> Self - where - B: axum::body::HttpBody, - B::Error: fmt::Debug, - { + pub async fn from_response(response: Response) -> Self { + use http_body_util::BodyExt; + let (parts, body) = response.into_parts(); let (code, message) = Self::error_message_from_bytes( - hyper::body::to_bytes(body) + body.collect() .await - .expect("Couldn't convert to bytes"), + .expect("Couldn't collect body") + .to_bytes(), ) .await; @@ -543,7 +540,7 @@ struct ResponseErrorMessage { } impl IntoResponse for HttpResponseError { - fn into_response(mut self) -> Response { + fn into_response(mut self) -> Response { // This is the only place we capture errors to sentry because it is the exit // point of the HTTP layer report_error(&mut self.trace); @@ -584,14 +581,14 @@ impl RouteMapper for NoopRouteMapper { /// Router + Middleware for a Convex service pub struct ConvexHttpService { - router: Router<(), Body>, + router: Router, version: String, _concurrency_gauge: Option, } impl ConvexHttpService { pub fn new( - router: Router<(), Body>, + router: Router, service_name: &'static str, version: String, max_concurrency: usize, @@ -635,14 +632,14 @@ impl ConvexHttpService { } /// Routes not handled by the passed-in router. - fn extra_routes(&self) -> Router<(), Body> { + fn extra_routes(&self) -> Router { let version = self.version.clone(); Router::new() .route("/version", get(move || async move { version })) .route("/metrics", get(metrics)) } - pub async fn serve>( + pub async fn serve + Send + 'static>( self, addr: SocketAddr, shutdown: F, @@ -665,13 +662,14 @@ impl ConvexHttpService { /// and `/metrics`. Because the middleware is applied before routing, it is /// allowed to change the request URI and affect which route will be /// matched. - pub async fn serve_with_middleware, Fut, Rejection>( + pub async fn serve_with_middleware( self, addr: SocketAddr, shutdown: F, middleware_fn: impl FnMut(http::Request) -> Fut + Clone + Send + 'static, ) -> anyhow::Result<()> where + F: Future + Send + 'static, Fut: Future, Rejection>> + Send + 'static, Rejection: IntoResponse + Send + 'static, { @@ -692,7 +690,7 @@ impl ConvexHttpService { } #[cfg(any(test, feature = "testing"))] - pub fn new_for_test(router: Router<(), Body>) -> Self { + pub fn new_for_test(router: Router) -> Self { Self { router, version: String::new(), @@ -701,59 +699,47 @@ impl ConvexHttpService { } #[cfg(any(test, feature = "testing"))] - pub fn router(&self) -> Router<(), Body> { + pub fn router(&self) -> Router { self.router.clone() } } /// Serves an HTTP server using the given service. -pub async fn serve_http( - service: IntoMakeServiceWithConnectInfo, +pub async fn serve_http( + make_service: IntoMakeServiceWithConnectInfo, addr: SocketAddr, shutdown: F, ) -> anyhow::Result<()> where - R: Service, Response = Response> + Send + Clone + 'static, - >>::Error: Into>, + R: Service, Response = Response, Error = Infallible> + + Send + + Clone + + 'static, >>::Future: Send, - F: Future, - B: axum::body::HttpBody + Send + 'static, - ::Data: Send + 'static, - ::Error: Into>, + F: Future + Send + 'static, { // Set SO_REUSEADDR and a bounded TCP accept backlog for our server's listening // socket. let socket = TcpSocket::new_v4()?; socket.set_reuseaddr(true)?; + // Set TCP_NODELAY on accepted connections. + socket.set_nodelay(true)?; socket.bind(addr)?; let listener = socket.listen(*HTTP_SERVER_TCP_BACKLOG)?; - let mut incoming_sockets = AddrIncoming::from_listener(listener)?; - // Set TCP_NODELAY on accepted connections. - incoming_sockets.set_nodelay(true); - // This setting is a bit of a `hyper`-specific hack to prevent a DDoS attack - // from taking down the webserver. See https://github.com/hyperium/hyper/issues/1358 and - // https://klau.si/blog/crashing-a-rust-hyper-server-with-a-denial-of-service-attack/ for more - // details. - incoming_sockets.set_sleep_on_errors(true); - let addr = incoming_sockets.local_addr(); - tracing::info!("Listening on http://{}", addr); - hyper::Server::builder(incoming_sockets) - .http2_max_concurrent_streams(MAX_HTTP2_STREAMS) - .serve(service) + + fork_of_axum_serve::serve(listener, make_service) .with_graceful_shutdown(shutdown) .await?; - tracing::info!("HTTP server shutdown complete"); - Ok(()) } async fn client_version_state_middleware( ExtractClientVersion(client_version): ExtractClientVersion, req: http::request::Request, - next: axum::middleware::Next, -) -> Result { + next: axum::middleware::Next, +) -> Result { let version_state = client_version.current_state(); let mut resp = match &version_state { @@ -800,7 +786,7 @@ pub async fn stats_middleware( ExtractResolvedHost(resolved_host): ExtractResolvedHost, ExtractClientVersion(client_version): ExtractClientVersion, req: http::request::Request, - next: axum::middleware::Next, + next: axum::middleware::Next, ) -> Result { let start = Instant::now(); let method = req.method().clone(); @@ -1036,12 +1022,12 @@ where } } -async fn log_middleware( +async fn log_middleware( remote_addr: Option>, ExtractResolvedHost(resolved_host): ExtractResolvedHost, - req: http::request::Request, - next: axum::middleware::Next, -) -> Result { + req: axum::extract::Request, + next: axum::middleware::Next, +) -> Result { let site_id = resolved_host.instance_name; let start = Instant::now(); @@ -1049,13 +1035,13 @@ async fn log_middleware( let method = req.method().clone(); let uri = req.uri().clone(); let version = req.version(); - let get_header = |name: HeaderName| -> Option { + let get_header = |req: &Request, name: HeaderName| -> Option { req.headers() .get(name) .and_then(|h| h.to_str().ok().map(|s| s.to_string())) }; - let referer = get_header(http::header::REFERER); - let user_agent = get_header(http::header::USER_AGENT); + let referer = get_header(&req, http::header::REFERER); + let user_agent = get_header(&req, http::header::USER_AGENT); let resp = next.run(req).await; diff --git a/crates/convex/Cargo.oss.toml b/crates/convex/Cargo.oss.toml index 122b05d8..362baf93 100644 --- a/crates/convex/Cargo.oss.toml +++ b/crates/convex/Cargo.oss.toml @@ -18,7 +18,7 @@ bytes = { version = "1.1.0" } convex_sync_types = { path = "./sync_types", version = "=0.7.0" } futures = { version = "0.3" } imbl = { version = "3.0.0" } -parking_lot = { optional = true, version = "0.12" } +parking_lot = { optional = true, version = "0.12", features = [ "hardware-lock-elision" ] } proptest = { optional = true, version = "1" } proptest-derive = { optional = true, version = "0.4.0" } rand = { version = "0.8" } @@ -26,9 +26,9 @@ serde_json = { features = [ "float_roundtrip", "preserve_order" ], version = "1" thiserror = { version = "1" } tokio = { features = [ "full" ], version = "1" } tokio-stream = { features = [ "io-util", "sync" ], version = "0.1" } -tokio-tungstenite = { version = "0.20.0" } +tokio-tungstenite = { version = "0.21.0", features = [ "native-tls-vendored" ] } tracing = { version = "0.1" } -url = { version = "2" } +url = { version = "2.5.2" } uuid = { features = [ "serde", "v4" ], version = "1.6" } [dev-dependencies] @@ -36,7 +36,7 @@ colored = { version = "2" } convex_sync_types = { path = "./sync_types", version = "=0.7.0", features = [ "testing" ] } dotenvy = { version = "0.15.7" } maplit = { version = "1" } -parking_lot = { version = "0.12" } +parking_lot = { version = "0.12", features = [ "hardware-lock-elision" ] } pretty_assertions = { version = "1" } proptest = { version = "1" } proptest-derive = { version = "0.4.0" } diff --git a/crates/convex/sync_types/Cargo.oss.toml b/crates/convex/sync_types/Cargo.oss.toml index c1845c5e..a334544e 100644 --- a/crates/convex/sync_types/Cargo.oss.toml +++ b/crates/convex/sync_types/Cargo.oss.toml @@ -14,7 +14,7 @@ homepage = "https://www.convex.dev/" anyhow = { version = "1" } base64 = { version = "0.13" } derive_more = { version = "0.99" } -headers = { version = "0.3" } +headers = { version = "0.4" } proptest = { optional = true, version = "1" } proptest-derive = { optional = true, version = "0.4.0" } rand = { version = "0.8" } diff --git a/crates/fivetran_source/Cargo.toml b/crates/fivetran_source/Cargo.toml index 0d80723f..6dd0c2a9 100644 --- a/crates/fivetran_source/Cargo.toml +++ b/crates/fivetran_source/Cargo.toml @@ -18,6 +18,7 @@ convex_fivetran_common = { path = "../fivetran_common" } derive_more = { workspace = true } futures = { workspace = true } futures-async-stream = { workspace = true } +headers = { workspace = true } maplit = { workspace = true } prost = { workspace = true } prost-types = { workspace = true } diff --git a/crates/fivetran_source/src/convex_api.rs b/crates/fivetran_source/src/convex_api.rs index b28ad8a5..b053664a 100644 --- a/crates/fivetran_source/src/convex_api.rs +++ b/crates/fivetran_source/src/convex_api.rs @@ -12,6 +12,10 @@ use derive_more::{ From, Into, }; +use headers::{ + HeaderName, + HeaderValue, +}; use maplit::btreemap; use serde::{ de::DeserializeOwned, @@ -19,10 +23,6 @@ use serde::{ Serialize, }; use serde_json::Value as JsonValue; -use tonic::codegen::http::{ - HeaderName, - HeaderValue, -}; #[allow(clippy::declare_interior_mutable_const)] const CONVEX_CLIENT_HEADER: HeaderName = HeaderName::from_static("convex-client"); diff --git a/crates/http_client/src/lib.rs b/crates/http_client/src/lib.rs index 8ded350b..89ac8e5a 100644 --- a/crates/http_client/src/lib.rs +++ b/crates/http_client/src/lib.rs @@ -66,7 +66,7 @@ async fn cached_http_client_inner( // convert it to a `AsStdError` which does implement `std::error::Error let res: Result = try { let mut request_builder = HTTP_CLIENT - .request(request.method, request.url.as_str()) + .request(request.method.as_str().parse()?, request.url.as_str()) .body(request.body); for (name, value) in &request.headers { request_builder = request_builder.header(name.as_str(), value.as_bytes()); @@ -87,8 +87,16 @@ async fn cached_http_client_inner( let headers = response.headers().to_owned(); let chunks = response.bytes().await?; HttpResponse { - status_code, - headers, + status_code: status_code.as_str().parse()?, + headers: headers + .iter() + .map(|(name, value)| { + Ok(( + openidconnect::http::HeaderName::from_bytes(name.as_ref())?, + openidconnect::http::HeaderValue::from_bytes(value.as_bytes())?, + )) + }) + .collect::>()?, body: chunks.to_vec(), } }; @@ -98,12 +106,14 @@ async fn cached_http_client_inner( #[cfg(test)] mod tests { use http_cache::XCACHE; - use openidconnect::HttpRequest; - use reqwest::{ - header::HeaderValue, - Method, - Url, + use openidconnect::{ + http::{ + header::HeaderValue, + Method, + }, + HttpRequest, }; + use reqwest::Url; use crate::{ cached_http_client_inner, @@ -111,7 +121,7 @@ mod tests { }; #[tokio::test] - async fn test_cached_client() { + async fn test_cached_client() -> anyhow::Result<()> { // Use Google's OpenID configuration, which should never disappear let url = Url::parse("https://accounts.google.com/.well-known/openid-configuration").unwrap(); @@ -119,7 +129,7 @@ mod tests { url: url.clone(), method: Method::GET, headers: vec![( - reqwest::header::ACCEPT, + openidconnect::http::header::ACCEPT, HeaderValue::from_static("application/json"), )] .into_iter() @@ -141,5 +151,6 @@ mod tests { response.headers.get(XCACHE).unwrap().as_bytes(), "HIT".as_bytes() ); + Ok(()) } } diff --git a/crates/isolate/Cargo.toml b/crates/isolate/Cargo.toml index 880bd6fa..69d7a1a3 100644 --- a/crates/isolate/Cargo.toml +++ b/crates/isolate/Cargo.toml @@ -39,6 +39,7 @@ file_storage = { path = "../file_storage" } futures = { workspace = true } headers = { workspace = true } http = { workspace = true } +http-body-util = { workspace = true } humansize = { workspace = true } itertools = { workspace = true } keybroker = { path = "../keybroker" } diff --git a/crates/isolate/src/environment/action/fetch.rs b/crates/isolate/src/environment/action/fetch.rs index 95614e50..6d405a8f 100644 --- a/crates/isolate/src/environment/action/fetch.rs +++ b/crates/isolate/src/environment/action/fetch.rs @@ -46,7 +46,7 @@ impl TaskExecutor { .unbounded_send(TaskResponse::TaskDone { task_id, variant: Err( - ErrorMetadata::bad_request("FetchFailed", e.to_string()).into() + ErrorMetadata::bad_request("FetchFailed", format!("{e:#}")).into() ), }); Self::log_fetch_request(t, origin, Err(()), initial_response_time); diff --git a/crates/isolate/src/environment/action/stream.rs b/crates/isolate/src/environment/action/stream.rs index 97b44439..ebe739bb 100644 --- a/crates/isolate/src/environment/action/stream.rs +++ b/crates/isolate/src/environment/action/stream.rs @@ -70,7 +70,7 @@ impl TaskExecutor { ) -> anyhow::Result> { let request = Request::builder() .header("Content-Type", content_type) - .body(axum::body::Body::wrap_stream(request_stream))?; + .body(axum::body::Body::from_stream(request_stream))?; let mut multipart = axum::extract::Multipart::from_request(request, &()) .await .map_err(|e| ErrorMetadata::bad_request("InvalidMultiPartForm", e.to_string()))?; diff --git a/crates/isolate/src/tests/fetch.rs b/crates/isolate/src/tests/fetch.rs index a273743b..157f74e0 100644 --- a/crates/isolate/src/tests/fetch.rs +++ b/crates/isolate/src/tests/fetch.rs @@ -7,10 +7,7 @@ use std::{ }; use axum::{ - body::{ - Body, - Full, - }, + body::Body, response::Response, routing::{ get, @@ -31,6 +28,7 @@ use http::{ Request, StatusCode, }; +use http_body_util::BodyExt; use itertools::Itertools; use keybroker::Identity; use must_let::must_let; @@ -99,7 +97,7 @@ async fn test_fetch_basic(rt: ProdRuntime) -> anyhow::Result<()> { get(|| async { Response::builder() .header("content-type", "application/json") - .body(Full::from( + .body(Body::from( serde_json::to_string(&json!({ "name": "convex", })) @@ -135,7 +133,7 @@ async fn test_fetch_basic(rt: ProdRuntime) -> anyhow::Result<()> { get(|| async { Response::builder() .status(StatusCode::PROXY_AUTHENTICATION_REQUIRED) - .body(Full::from("Sorry can't do that")) + .body(Body::from("Sorry can't do that")) .expect("invalid response") }), ) @@ -144,7 +142,7 @@ async fn test_fetch_basic(rt: ProdRuntime) -> anyhow::Result<()> { get(|| async { Response::builder() .header("Content-Type", "application/x-www-form-urlencoded") - .body(Full::from("field_1=Hi&field_2=%3CConvex%3E")) + .body(Body::from("field_1=Hi&field_2=%3CConvex%3E")) .expect("invalid response") }), ) @@ -158,7 +156,7 @@ async fn test_fetch_basic(rt: ProdRuntime) -> anyhow::Result<()> { text/javascript\r\n\r\nconsole.log(\"Hi\")\r\n--boundary--\r\nEpilogue"; Response::builder() .header("Content-Type", "multipart/form-data;boundary=boundary") - .body(Full::from(b)) + .body(Body::from(b)) .expect("invalid response") }), ) @@ -175,7 +173,7 @@ async fn test_fetch_basic(rt: ProdRuntime) -> anyhow::Result<()> { "Content-Type", "multipart/form-dataststst;boundary=boundary", ) - .body(Full::from(b)) + .body(Body::from(b)) .expect("invalid response") }), ) @@ -183,7 +181,7 @@ async fn test_fetch_basic(rt: ProdRuntime) -> anyhow::Result<()> { "/echo_multipart_file", post(|req: Request| async { let body = req.into_body(); - let bytes = &hyper::body::to_bytes(body).await.unwrap()[0..]; + let bytes = body.collect().await.unwrap().to_bytes(); let start = b"--boundary\t \r\n\ Content-Disposition: form-data; name=\"field_1\"\r\n\ \r\n\ @@ -194,7 +192,7 @@ async fn test_fetch_basic(rt: ProdRuntime) -> anyhow::Result<()> { Content-Type: application/octet-stream\r\n\ \r\n"; let end = b"\r\n--boundary--\r\n"; - let b = [start as &[u8], bytes, end].concat(); + let b = [start as &[u8], &bytes[..], end].concat(); Response::builder() .header("content-type", "multipart/form-data;boundary=boundary") @@ -207,7 +205,7 @@ async fn test_fetch_basic(rt: ProdRuntime) -> anyhow::Result<()> { "/print_auth", get(|req: Request| async move { Response::builder() - .body(Full::from( + .body(Body::from( serde_json::to_string(&json!({ "auth": match req.headers().get("Authorization") { Some(header) => header.to_str().unwrap(), @@ -222,7 +220,11 @@ async fn test_fetch_basic(rt: ProdRuntime) -> anyhow::Result<()> { rt.spawn("test_router", serve(redirected_router, 4547)); let t = UdfTest::default(rt).await?; - must_let!(let (ConvexValue::String(r), _outcome, log_lines) = t.action_outcome_and_log_lines("fetch", assert_obj!(), Identity::system()).await?); + must_let!(let (ConvexValue::String(r), _outcome, log_lines) = t.action_outcome_and_log_lines( + "fetch", + assert_obj!(), + Identity::system(), + ).await?); assert_eq!(String::from(r), "success".to_string()); assert!(log_lines.is_empty()); @@ -278,7 +280,7 @@ async fn test_fetch_timing(rt: ProdRuntime) -> anyhow::Result<()> { rt_.wait(Duration::from_secs(3)).await; Response::builder() .status(StatusCode::INTERNAL_SERVER_ERROR) - .body(Full::from("timeout")) + .body(Body::from("timeout")) .expect("invalid response") }), ); diff --git a/crates/local_backend/Cargo.toml b/crates/local_backend/Cargo.toml index 6884854b..f106d2a0 100644 --- a/crates/local_backend/Cargo.toml +++ b/crates/local_backend/Cargo.toml @@ -15,6 +15,7 @@ async-broadcast = { workspace = true } async-trait = { workspace = true } authentication = { path = "../authentication" } axum = { workspace = true } +axum-extra = { workspace = true } base64 = { workspace = true } clap = { workspace = true } cmd_util = { path = "../../crates/cmd_util" } @@ -28,7 +29,9 @@ futures = { workspace = true } futures-async-stream = { workspace = true } hex = { workspace = true } http = { workspace = true } +http-body-util = { workspace = true } hyper = { workspace = true } +hyper-util = { workspace = true } isolate = { path = "../../crates/isolate" } keybroker = { path = "../keybroker" } maplit = { workspace = true } diff --git a/crates/local_backend/src/custom_headers.rs b/crates/local_backend/src/custom_headers.rs index e100eb4e..5db322ca 100644 --- a/crates/local_backend/src/custom_headers.rs +++ b/crates/local_backend/src/custom_headers.rs @@ -1,4 +1,4 @@ -use axum::headers::{ +use axum_extra::headers::{ Header, HeaderName, HeaderValue, @@ -13,7 +13,7 @@ impl Header for ContentDispositionAttachment { &CONTENT_DISPOSITION } - fn decode<'i, I>(_values: &mut I) -> Result + fn decode<'i, I>(_values: &mut I) -> Result where I: Iterator, { @@ -26,7 +26,7 @@ impl Header for ContentDispositionAttachment { { let value = format!("attachment; filename={}", self.0); let encoded = HeaderValue::from_str(&value) - .map_err(|_| axum::headers::Error::invalid()) + .map_err(|_| axum_extra::headers::Error::invalid()) .unwrap(); values.extend(std::iter::once(encoded)); } diff --git a/crates/local_backend/src/environment_variables.rs b/crates/local_backend/src/environment_variables.rs index 5eb8824f..1968b500 100644 --- a/crates/local_backend/src/environment_variables.rs +++ b/crates/local_backend/src/environment_variables.rs @@ -87,13 +87,12 @@ fn validate_env_var(name: &String, value: &String) -> anyhow::Result anyhow::Result<()> { let json_body = json!({"changes": changes}); - let body = Body::from(serde_json::to_vec(&json_body)?); + let body = axum::body::Body::from(serde_json::to_vec(&json_body)?); let req = Request::builder() .uri("/api/update_environment_variables") .method("POST") diff --git a/crates/local_backend/src/http_actions.rs b/crates/local_backend/src/http_actions.rs index c8ece969..6a137f6c 100644 --- a/crates/local_backend/src/http_actions.rs +++ b/crates/local_backend/src/http_actions.rs @@ -5,9 +5,8 @@ use application::api::ApplicationApi; use async_trait::async_trait; use axum::{ body::{ - BoxBody, + Body, Bytes, - StreamBody, }, debug_handler, extract::{ @@ -143,7 +142,7 @@ impl FromRequest for ExtractHttpRequestMetadata { url, method, }, - body: Some(Box::pin(body.map_err(|e| e.into()))), + body: Some(Box::pin(body.into_data_stream().map_err(|e| e.into()))), })) } } @@ -250,10 +249,10 @@ pub struct HttpActionResponse { } impl IntoResponse for HttpActionResponse { - fn into_response(self) -> Response { + fn into_response(self) -> Response { let status = self.status; let headers = self.headers; - let body = StreamBody::new(self.body); + let body = Body::from_stream(self.body); (status, headers, body).into_response() } } diff --git a/crates/local_backend/src/import.rs b/crates/local_backend/src/import.rs index 3267d861..c2dee4dd 100644 --- a/crates/local_backend/src/import.rs +++ b/crates/local_backend/src/import.rs @@ -7,11 +7,9 @@ use application::snapshot_import::{ upload_import_file, }; use axum::{ + body::Body, debug_handler, - extract::{ - BodyStream, - State, - }, + extract::State, response::IntoResponse, }; use common::http::{ @@ -133,11 +131,14 @@ pub async fn import( format, mode, }): Query, - stream: BodyStream, + stream: Body, ) -> Result { must_be_admin_with_write_access(&identity)?; let format = parse_format_arg(table_name, format)?; - let body_stream = stream.map_err(anyhow::Error::from).boxed(); + let body_stream = stream + .into_data_stream() + .map_err(anyhow::Error::from) + .boxed(); let num_written = do_import(&st.application, identity, format, mode, body_stream).await?; Ok(Json(ImportResponse { num_written })) } @@ -170,10 +171,11 @@ pub async fn import_upload_part( upload_token, part_number, }): Query, - body_stream: BodyStream, + body_stream: Body, ) -> Result { must_be_admin_with_write_access(&identity)?; let body_bytes = body_stream + .into_data_stream() .map_ok(|chunk| chunk.to_vec()) .try_concat() .await @@ -241,11 +243,14 @@ pub async fn prepare_import( format, mode, }): Query, - stream: BodyStream, + stream: Body, ) -> Result { must_be_admin_with_write_access(&identity)?; let format = parse_format_arg(table_name, format)?; - let body_stream = stream.map_err(anyhow::Error::from).boxed(); + let body_stream = stream + .into_data_stream() + .map_err(anyhow::Error::from) + .boxed(); let import_id = upload_import_file(&st.application, identity, format, mode, body_stream).await?; Ok(Json(PrepareImportResponse { diff --git a/crates/local_backend/src/node_action_callbacks.rs b/crates/local_backend/src/node_action_callbacks.rs index 2630f15a..dbc65bb5 100644 --- a/crates/local_backend/src/node_action_callbacks.rs +++ b/crates/local_backend/src/node_action_callbacks.rs @@ -6,7 +6,6 @@ use std::{ use anyhow::Context; use async_trait::async_trait; use axum::{ - body::Body, debug_handler, extract::{ FromRequestParts, @@ -468,8 +467,8 @@ fn get_encoded_span(headers: &HeaderMap) -> anyhow::Result { pub async fn action_callbacks_middleware( State(st): State, - req: http::request::Request, - next: axum::middleware::Next, + req: axum::extract::Request, + next: axum::middleware::Next, ) -> Result { // Validate we have an valid token in order to call any methods in this // actions_callback router. @@ -582,12 +581,11 @@ impl FromRequestParts for ExtractExecutionContext { #[cfg(test)] mod tests { - use application::test_helpers::ApplicationTestExt; - use axum::headers::authorization::Credentials; + use axum::body::Body; + use axum_extra::headers::authorization::Credentials; use common::runtime::Runtime; use http::Request; - use hyper::Body; use runtime::prod::ProdRuntime; use serde_json::{ json, diff --git a/crates/local_backend/src/proxy.rs b/crates/local_backend/src/proxy.rs index 72a13ca1..001c572a 100644 --- a/crates/local_backend/src/proxy.rs +++ b/crates/local_backend/src/proxy.rs @@ -4,7 +4,10 @@ use std::{ }; use axum::{ - extract::State, + extract::{ + Request, + State, + }, response::IntoResponse, routing::get, Router, @@ -17,8 +20,7 @@ use common::{ }, types::ConvexOrigin, }; -use http::Request; -use hyper::Body; +use hyper_util::rt::TokioExecutor; /// Routes HTTP actions to the main webserver pub async fn dev_site_proxy( @@ -33,11 +35,12 @@ pub async fn dev_site_proxy( async fn proxy_method( State(st): State, - mut request: Request, + mut request: Request, ) -> Result { let new_uri = format!("{}/http{}", st, request.uri()); *request.uri_mut() = new_uri.parse().map_err(anyhow::Error::new)?; - let resp = hyper::Client::new() + let resp = hyper_util::client::legacy::Client::builder(TokioExecutor::new()) + .build_http() .request(request) .await .map_err(anyhow::Error::new)?; diff --git a/crates/local_backend/src/public_api.rs b/crates/local_backend/src/public_api.rs index df3eb9b1..321d37f3 100644 --- a/crates/local_backend/src/public_api.rs +++ b/crates/local_backend/src/public_api.rs @@ -633,11 +633,11 @@ pub async fn public_action_post( #[cfg(test)] mod tests { use application::test_helpers::ApplicationTestExt; + use axum::body::Body; use http::{ Request, StatusCode, }; - use hyper::Body; use runtime::prod::ProdRuntime; use serde_json::{ json, diff --git a/crates/local_backend/src/router.rs b/crates/local_backend/src/router.rs index 510e5a0a..04e06ed3 100644 --- a/crates/local_backend/src/router.rs +++ b/crates/local_backend/src/router.rs @@ -1,4 +1,5 @@ use std::{ + convert::Infallible, sync::Arc, time::Duration, }; @@ -10,7 +11,6 @@ use axum::{ get, post, }, - BoxError, Router, }; use common::{ @@ -151,7 +151,7 @@ pub async fn router(st: LocalAppState) -> Router { .route("/deploy2/start_push", post(deploy_config2::start_push)) .layer( ServiceBuilder::new() - .layer(HandleErrorLayer::new(|_: BoxError| async { + .layer(HandleErrorLayer::new(|_: Infallible| async { StatusCode::INTERNAL_SERVER_ERROR })) .layer(RequestDecompressionLayer::new()) diff --git a/crates/local_backend/src/snapshot_export.rs b/crates/local_backend/src/snapshot_export.rs index 5b7aa8c5..4fd52bf0 100644 --- a/crates/local_backend/src/snapshot_export.rs +++ b/crates/local_backend/src/snapshot_export.rs @@ -2,14 +2,16 @@ use std::time::Duration; use anyhow::Context; use axum::{ - body::StreamBody, + body::Body, debug_handler, extract::State, + response::IntoResponse, +}; +use axum_extra::{ headers::{ CacheControl, ContentLength, }, - response::IntoResponse, TypedHeader, }; use common::http::{ @@ -122,7 +124,7 @@ pub async fn get_export( .with_private() .with_max_age(MAX_CACHE_AGE), ), - StreamBody::new(stream), + Body::from_stream(stream), )) } @@ -161,6 +163,6 @@ pub async fn get_zip_export( .with_private() .with_max_age(MAX_CACHE_AGE), ), - StreamBody::new(stream), + Body::from_stream(stream), )) } diff --git a/crates/local_backend/src/storage.rs b/crates/local_backend/src/storage.rs index 2fd777a1..12a32cb6 100644 --- a/crates/local_backend/src/storage.rs +++ b/crates/local_backend/src/storage.rs @@ -5,17 +5,18 @@ use std::{ use anyhow::Context; use axum::{ - body::StreamBody, + body::Body, debug_handler, extract::{ - rejection::{ - TypedHeaderRejection, - TypedHeaderRejectionReason, - }, - BodyStream, Host, State, }, + response::{ + IntoResponse, + Response, + }, +}; +use axum_extra::{ headers::{ AcceptRanges, CacheControl, @@ -24,9 +25,9 @@ use axum::{ Header, Range, }, - response::{ - IntoResponse, - Response, + typed_header::{ + TypedHeaderRejection, + TypedHeaderRejectionReason, }, TypedHeader, }; @@ -92,7 +93,7 @@ pub async fn storage_upload( ExtractResolvedHost(host): ExtractResolvedHost, Host(original_host): Host, ExtractRequestId(request_id): ExtractRequestId, - body: BodyStream, + body: Body, ) -> Result { let component = st .api @@ -106,7 +107,10 @@ pub async fn storage_upload( let content_length = map_header_err(content_length)?; let content_type = map_header_err(content_type)?; let sha256 = map_header_err(sha256)?.map(|dh| dh.0); - let body = body.map(|r| r.context("Error parsing body")).boxed(); + let body = body + .into_data_stream() + .map(|r| r.context("Error parsing body")) + .boxed(); let origin = original_host.into(); let storage_id = st .api @@ -158,7 +162,13 @@ pub async fn storage_get( // TODO(CX-3065) figure out deterministic repeatable tokens if let Ok(range_header) = range { - let ranges: Vec<(Bound, Bound)> = range_header.iter().collect(); + let ranges: Vec<(Bound, Bound)> = range_header + .satisfiable_ranges( + u64::MAX, /* technically, we should pass in the length of the file to protect + * against inputs that are too large, but it's a + * bit tricky to get at this point */ + ) + .collect(); // Convex only supports a single range because underlying AWS S3 only supports // a single range if ranges.len() != 1 { @@ -189,7 +199,7 @@ pub async fn storage_get( .with_max_age(MAX_CACHE_AGE), ), TypedHeader(AcceptRanges::bytes()), - StreamBody::new(stream), + Body::from_stream(stream), ) .into_response()); } @@ -213,7 +223,7 @@ pub async fn storage_get( .with_max_age(MAX_CACHE_AGE), ), TypedHeader(AcceptRanges::bytes()), - StreamBody::new(stream), + Body::from_stream(stream), ) .into_response()) } diff --git a/crates/local_backend/src/test_helpers.rs b/crates/local_backend/src/test_helpers.rs index f55a64d3..1abcedb7 100644 --- a/crates/local_backend/src/test_helpers.rs +++ b/crates/local_backend/src/test_helpers.rs @@ -4,7 +4,7 @@ use std::{ }; use anyhow::Context; -use axum::headers::Authorization; +use axum_extra::headers::Authorization; use common::{ http::{ ConvexHttpService, @@ -19,6 +19,7 @@ use http::{ Request, StatusCode, }; +use http_body_util::BodyExt; use metrics::SERVER_VERSION_STR; use runtime::prod::ProdRuntime; use serde::de::DeserializeOwned; @@ -75,13 +76,15 @@ pub async fn setup_backend_for_test(runtime: ProdRuntime) -> anyhow::Result( &self, - req: Request, + req: Request, ) -> anyhow::Result { tracing::info!("Sending req {req:?}"); let (parts, body) = self.app.router().clone().oneshot(req).await?.into_parts(); - let bytes = hyper::body::to_bytes(body) + let bytes = body + .collect() .await - .context("Couldn't convert to bytes")?; + .context("Couldn't convert to bytes")? + .to_bytes(); let msg = format!("Got response: {}", String::from_utf8_lossy(&bytes)); tracing::info!("{msg}"); assert_eq!(parts.status, StatusCode::OK, "{msg}"); @@ -91,7 +94,7 @@ impl TestLocalBackend { pub async fn expect_error( &self, - req: Request, + req: Request, expected_code: StatusCode, expected_short_msg: &str, ) -> anyhow::Result<()> { diff --git a/crates/storage/Cargo.toml b/crates/storage/Cargo.toml index be26e24f..c2ce104f 100644 --- a/crates/storage/Cargo.toml +++ b/crates/storage/Cargo.toml @@ -22,6 +22,7 @@ derive_more = { workspace = true } futures = { workspace = true } futures-async-stream = { workspace = true } http = { workspace = true } +http-body-util = { workspace = true } hyper = { workspace = true } pb = { path = "../pb" } pin-project = { workspace = true } diff --git a/crates/storage/src/lib.rs b/crates/storage/src/lib.rs index cee66eb0..d8c058cd 100644 --- a/crates/storage/src/lib.rs +++ b/crates/storage/src/lib.rs @@ -99,13 +99,15 @@ pub struct StorageGetStream { impl StorageGetStream { #[cfg(any(test, feature = "testing"))] pub async fn collect_as_bytes(self) -> anyhow::Result { - use axum::body::StreamBody; + use http_body_util::BodyExt; let Self { content_length, stream, } = self; - let content = hyper::body::to_bytes(StreamBody::new(stream)).await?; + let content = BodyExt::collect(axum::body::Body::from_stream(stream)) + .await? + .to_bytes(); anyhow::ensure!( (content_length as usize) == content.len(), "ContentLength mismatch" diff --git a/npm-packages/udf-tests/convex/fetch.ts b/npm-packages/udf-tests/convex/fetch.ts index fe0e138c..31211a60 100644 --- a/npm-packages/udf-tests/convex/fetch.ts +++ b/npm-packages/udf-tests/convex/fetch.ts @@ -132,7 +132,7 @@ async function fetchProtocolError() { async function fetchDnsError() { await expect(fetch("http://invalid/")).to.be.rejectedWith( - /error trying to connect: dns error/, + /dns error: failed to lookup address information/, ); }