From d7711c41daf4bf1860475fc6578f14aa6b9c6cef Mon Sep 17 00:00:00 2001 From: Luca Palmieri <20745048+LukeMathWalker@users.noreply.github.com> Date: Sun, 31 Dec 2023 18:37:08 +0000 Subject: [PATCH] Fill in body section. --- .../buffered_body/project-custom_limit.snap | 17 + .../buffered_body/project-extraction.snap | 9 + .../project-granular_limits.snap | 27 + .../buffered_body/project-no_limit.snap | 15 + .../buffered_body/project/.gitignore | 1 + .../buffered_body/project/Cargo.lock | 759 ++++++++++++++++++ .../buffered_body/project/Cargo.toml | 13 + .../project/server_sdk/Cargo.toml | 16 + .../project/server_sdk/blueprint.ron | 347 ++++++++ .../project/server_sdk/src/lib.rs | 81 ++ .../buffered_body/project/src/blueprint.rs | 51 ++ .../project/src/buffered_body/blueprint.rs | 9 + .../project/src/buffered_body/mod.rs | 5 + .../project/src/buffered_body/routes.rs | 7 + .../project/src/custom_limit/blueprint.rs | 20 + .../project/src/custom_limit/mod.rs | 5 + .../project/src/custom_limit/routes.rs | 6 + .../project/src/granular_limits/blueprint.rs | 25 + .../project/src/granular_limits/mod.rs | 5 + .../project/src/granular_limits/routes.rs | 6 + .../buffered_body/project/src/lib.rs | 11 + .../buffered_body/project/src/main.rs | 13 + .../project/src/no_limit/blueprint.rs | 18 + .../buffered_body/project/src/no_limit/mod.rs | 5 + .../project/src/no_limit/routes.rs | 6 + .../request_data/buffered_body/tutorial.yml | 18 + docs/guide/request_data/body/byte_wrappers.md | 52 +- libs/pavex/src/request/body/buffered_body.rs | 11 +- 28 files changed, 1551 insertions(+), 7 deletions(-) create mode 100644 doc_examples/guide/request_data/buffered_body/project-custom_limit.snap create mode 100644 doc_examples/guide/request_data/buffered_body/project-extraction.snap create mode 100644 doc_examples/guide/request_data/buffered_body/project-granular_limits.snap create mode 100644 doc_examples/guide/request_data/buffered_body/project-no_limit.snap create mode 100644 doc_examples/guide/request_data/buffered_body/project/.gitignore create mode 100644 doc_examples/guide/request_data/buffered_body/project/Cargo.lock create mode 100644 doc_examples/guide/request_data/buffered_body/project/Cargo.toml create mode 100644 doc_examples/guide/request_data/buffered_body/project/server_sdk/Cargo.toml create mode 100644 doc_examples/guide/request_data/buffered_body/project/server_sdk/blueprint.ron create mode 100644 doc_examples/guide/request_data/buffered_body/project/server_sdk/src/lib.rs create mode 100644 doc_examples/guide/request_data/buffered_body/project/src/blueprint.rs create mode 100644 doc_examples/guide/request_data/buffered_body/project/src/buffered_body/blueprint.rs create mode 100644 doc_examples/guide/request_data/buffered_body/project/src/buffered_body/mod.rs create mode 100644 doc_examples/guide/request_data/buffered_body/project/src/buffered_body/routes.rs create mode 100644 doc_examples/guide/request_data/buffered_body/project/src/custom_limit/blueprint.rs create mode 100644 doc_examples/guide/request_data/buffered_body/project/src/custom_limit/mod.rs create mode 100644 doc_examples/guide/request_data/buffered_body/project/src/custom_limit/routes.rs create mode 100644 doc_examples/guide/request_data/buffered_body/project/src/granular_limits/blueprint.rs create mode 100644 doc_examples/guide/request_data/buffered_body/project/src/granular_limits/mod.rs create mode 100644 doc_examples/guide/request_data/buffered_body/project/src/granular_limits/routes.rs create mode 100644 doc_examples/guide/request_data/buffered_body/project/src/lib.rs create mode 100644 doc_examples/guide/request_data/buffered_body/project/src/main.rs create mode 100644 doc_examples/guide/request_data/buffered_body/project/src/no_limit/blueprint.rs create mode 100644 doc_examples/guide/request_data/buffered_body/project/src/no_limit/mod.rs create mode 100644 doc_examples/guide/request_data/buffered_body/project/src/no_limit/routes.rs create mode 100644 doc_examples/guide/request_data/buffered_body/tutorial.yml diff --git a/doc_examples/guide/request_data/buffered_body/project-custom_limit.snap b/doc_examples/guide/request_data/buffered_body/project-custom_limit.snap new file mode 100644 index 000000000..6c127a14e --- /dev/null +++ b/doc_examples/guide/request_data/buffered_body/project-custom_limit.snap @@ -0,0 +1,17 @@ +```rust title="src/custom_limit/blueprint.rs" +use pavex::blueprint::{constructor::Lifecycle, Blueprint}; +use pavex::f; +use pavex::request::body::BodySizeLimit; + +pub fn body_size_limit() -> BodySizeLimit { + BodySizeLimit::Enabled { + max_n_bytes: 10_485_760, // 10 MBs + } +} + +pub fn blueprint() -> Blueprint { + let mut bp = Blueprint::new(); + bp.constructor(f!(crate::body_size_limit), Lifecycle::Singleton); + // [...] +} +``` \ No newline at end of file diff --git a/doc_examples/guide/request_data/buffered_body/project-extraction.snap b/doc_examples/guide/request_data/buffered_body/project-extraction.snap new file mode 100644 index 000000000..0cfd60027 --- /dev/null +++ b/doc_examples/guide/request_data/buffered_body/project-extraction.snap @@ -0,0 +1,9 @@ +```rust title="src/buffered_body/routes.rs" hl_lines="4" +use pavex::http::StatusCode; +use pavex::request::body::BufferedBody; + +pub fn handler(body: &BufferedBody) -> StatusCode { + format!("The incoming request contains {} bytes", body.bytes.len()); + // [...] +} +``` \ No newline at end of file diff --git a/doc_examples/guide/request_data/buffered_body/project-granular_limits.snap b/doc_examples/guide/request_data/buffered_body/project-granular_limits.snap new file mode 100644 index 000000000..76291f200 --- /dev/null +++ b/doc_examples/guide/request_data/buffered_body/project-granular_limits.snap @@ -0,0 +1,27 @@ +```rust title="src/granular_limits/blueprint.rs" +use pavex::blueprint::{constructor::Lifecycle, router::POST, Blueprint}; +use pavex::f; +use pavex::request::body::BodySizeLimit; + +pub fn blueprint() -> Blueprint { + let mut bp = Blueprint::new(); + bp.nest(upload_bp()); + // Other routes... + bp +} + +fn upload_bp() -> Blueprint { + let mut bp = Blueprint::new(); + // This limit will only apply to the routes registered + // in this nested blueprint. + bp.constructor(f!(crate::upload_size_limit), Lifecycle::Singleton); + bp.route(POST, "/upload", f!(crate::upload)); + bp +} + +pub fn upload_size_limit() -> BodySizeLimit { + BodySizeLimit::Enabled { + max_n_bytes: 1_073_741_824, // 1GB + } +} +``` \ No newline at end of file diff --git a/doc_examples/guide/request_data/buffered_body/project-no_limit.snap b/doc_examples/guide/request_data/buffered_body/project-no_limit.snap new file mode 100644 index 000000000..e3ee65944 --- /dev/null +++ b/doc_examples/guide/request_data/buffered_body/project-no_limit.snap @@ -0,0 +1,15 @@ +```rust title="src/no_limit/blueprint.rs" +use pavex::blueprint::{constructor::Lifecycle, Blueprint}; +use pavex::f; +use pavex::request::body::BodySizeLimit; + +pub fn body_size_limit() -> BodySizeLimit { + BodySizeLimit::Disabled +} + +pub fn blueprint() -> Blueprint { + let mut bp = Blueprint::new(); + bp.constructor(f!(crate::body_size_limit), Lifecycle::Singleton); + // [...] +} +``` \ No newline at end of file diff --git a/doc_examples/guide/request_data/buffered_body/project/.gitignore b/doc_examples/guide/request_data/buffered_body/project/.gitignore new file mode 100644 index 000000000..c41cc9e35 --- /dev/null +++ b/doc_examples/guide/request_data/buffered_body/project/.gitignore @@ -0,0 +1 @@ +/target \ No newline at end of file diff --git a/doc_examples/guide/request_data/buffered_body/project/Cargo.lock b/doc_examples/guide/request_data/buffered_body/project/Cargo.lock new file mode 100644 index 000000000..c429ace23 --- /dev/null +++ b/doc_examples/guide/request_data/buffered_body/project/Cargo.lock @@ -0,0 +1,759 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "anyhow" +version = "1.0.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59d2a3357dde987206219e78ecfbbb6e8dad06cbb65292758d3270e6254f7355" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "backtrace" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base64" +version = "0.21.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" + +[[package]] +name = "bitflags" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +dependencies = [ + "serde", +] + +[[package]] +name = "buffered_body" +version = "0.1.0" +dependencies = [ + "cargo_px_env", + "pavex", + "pavex_cli_client", + "serde", +] + +[[package]] +name = "buffered_body_server_sdk" +version = "0.1.0" +dependencies = [ + "buffered_body", + "http", + "hyper", + "matchit", + "pavex", + "thiserror", +] + +[[package]] +name = "bytes" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" + +[[package]] +name = "cargo_px_env" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b459425e7bfec0783df9538ecc749961f076cc793a09e91f1d082ee0012c925" + +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "fs-err" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88a41f105fe1d5b6b34b2055e3dc59bb79b46b48b2040b9e6c7b4b5de097aa41" +dependencies = [ + "autocfg", +] + +[[package]] +name = "futures-channel" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" + +[[package]] +name = "futures-macro" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" + +[[package]] +name = "futures-task" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" + +[[package]] +name = "futures-util" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" +dependencies = [ + "futures-core", + "futures-macro", + "futures-task", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + +[[package]] +name = "h2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d308f63daf4181410c242d34c11f928dcb3aa105852019e043c9d1f4e4368a" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" + +[[package]] +name = "http" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b32afd38673a8016f7c9ae69e5af41a58f81b1d31689040f2f1959594ce194ea" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41cb79eb393015dadd30fc252023adb0b2400a0caee0fa2a077e6e21a551e840" +dependencies = [ + "bytes", + "futures-util", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5aa53871fc917b1a9ed87b683a5d86db645e23acb32c2e0785a353e522fb75" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "tokio", + "want", +] + +[[package]] +name = "hyper-util" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdea9aac0dbe5a9240d68cfd9501e2db94222c6dc06843e06640b9e07f0fdc67" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "pin-project-lite", + "socket2", + "tokio", + "tracing", +] + +[[package]] +name = "indexmap" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +dependencies = [ + "equivalent", + "hashbrown", + "serde", +] + +[[package]] +name = "itoa" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" + +[[package]] +name = "libc" +version = "0.2.151" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" + +[[package]] +name = "matchit" +version = "0.7.3" +source = "git+https://github.com/ibraheemdev/matchit?branch=master#7766d457ee20826497b5061581fa6cef682d05b7" + +[[package]] +name = "memchr" +version = "2.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" +dependencies = [ + "libc", + "wasi", + "windows-sys", +] + +[[package]] +name = "object" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + +[[package]] +name = "pavex" +version = "0.1.0" +dependencies = [ + "anyhow", + "bytes", + "fs-err", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "indexmap", + "matchit", + "mime", + "paste", + "pavex_macros", + "percent-encoding", + "pin-project-lite", + "ron", + "serde", + "serde_html_form", + "serde_json", + "serde_path_to_error", + "smallvec", + "socket2", + "thiserror", + "tokio", + "tracing", +] + +[[package]] +name = "pavex_cli_client" +version = "0.1.0" +dependencies = [ + "anyhow", + "pavex", +] + +[[package]] +name = "pavex_macros" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "proc-macro2" +version = "1.0.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "ron" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94" +dependencies = [ + "base64", + "bitflags", + "serde", + "serde_derive", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "ryu" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" + +[[package]] +name = "serde" +version = "1.0.193" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.193" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_html_form" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224e6a14f315852940f3ec103125aa6482f0e224732ed91ed3330ed633077c34" +dependencies = [ + "form_urlencoded", + "indexmap", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_json" +version = "1.0.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_path_to_error" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4beec8bce849d58d06238cb50db2e1c417cfeafa4c63f692b15c82b7c80f8335" +dependencies = [ + "itoa", + "serde", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" + +[[package]] +name = "socket2" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "syn" +version = "2.0.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b7d0a2c048d661a1a59fcd7355baa232f7ed34e0ee4df2eef3c1c1c0d3852d8" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f11c217e1416d6f036b870f14e0413d480dbf28edbee1f877abaf0206af43bb7" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01742297787513b79cf8e29d1056ede1313e2420b7b3b15d0a768b4921f549df" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio" +version = "1.35.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "pin-project-lite", + "socket2", + "windows-sys", +] + +[[package]] +name = "tokio-util" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" diff --git a/doc_examples/guide/request_data/buffered_body/project/Cargo.toml b/doc_examples/guide/request_data/buffered_body/project/Cargo.toml new file mode 100644 index 000000000..17a7aa468 --- /dev/null +++ b/doc_examples/guide/request_data/buffered_body/project/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "buffered_body" +version = "0.1.0" +edition = "2021" + +[dependencies] +pavex = { path = "../../../../../libs/pavex" } +pavex_cli_client = { path = "../../../../../libs/pavex_cli_client" } +serde = { version = "1", features = ["derive"] } +cargo_px_env = "0.1" + +[workspace] +members = [".", "server_sdk"] diff --git a/doc_examples/guide/request_data/buffered_body/project/server_sdk/Cargo.toml b/doc_examples/guide/request_data/buffered_body/project/server_sdk/Cargo.toml new file mode 100644 index 000000000..9251307a8 --- /dev/null +++ b/doc_examples/guide/request_data/buffered_body/project/server_sdk/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "buffered_body_server_sdk" +version = "0.1.0" +edition = "2021" + +[package.metadata.px.generate] +generator_type = "cargo_workspace_binary" +generator_name = "buffered_body" + +[dependencies] +buffered_body = { version = "0.1.0", path = "..", package = "buffered_body" } +http = { version = "1.0.0", package = "http" } +hyper = { version = "1.1.0", package = "hyper" } +matchit = { version = "0.7.3", git = "https://github.com/ibraheemdev/matchit", branch = "master", package = "matchit" } +pavex = { version = "0.1.0", path = "../../../../../../libs/pavex", package = "pavex" } +thiserror = { version = "1.0.51", package = "thiserror" } diff --git a/doc_examples/guide/request_data/buffered_body/project/server_sdk/blueprint.ron b/doc_examples/guide/request_data/buffered_body/project/server_sdk/blueprint.ron new file mode 100644 index 000000000..c372f437b --- /dev/null +++ b/doc_examples/guide/request_data/buffered_body/project/server_sdk/blueprint.ron @@ -0,0 +1,347 @@ +( + creation_location: ( + line: 5, + column: 18, + file: "src/blueprint.rs", + ), + constructors: [], + middlewares: [], + routes: [ + ( + path: "/greet", + method_guard: ( + inner: Some(( + bitset: 256, + extensions: [], + )), + ), + request_handler: ( + callable: ( + registered_at: "cookbook", + import_path: "crate::routes::greet", + ), + location: ( + line: 12, + column: 8, + file: "src/blueprint.rs", + ), + ), + error_handler: None, + ), + ], + fallback_request_handler: None, + nested_blueprints: [ + ( + blueprint: ( + creation_location: ( + line: 6, + column: 18, + file: "src/functions/blueprint.rs", + ), + constructors: [ + ( + constructor: ( + callable: ( + registered_at: "cookbook", + import_path: "crate::functions::extract", + ), + location: ( + line: 7, + column: 8, + file: "src/functions/blueprint.rs", + ), + ), + lifecycle: RequestScoped, + cloning_strategy: None, + error_handler: None, + ), + ], + middlewares: [], + routes: [], + fallback_request_handler: None, + nested_blueprints: [], + ), + path_prefix: None, + nesting_location: ( + line: 6, + column: 8, + file: "src/blueprint.rs", + ), + ), + ( + blueprint: ( + creation_location: ( + line: 6, + column: 18, + file: "src/static_methods/blueprint.rs", + ), + constructors: [ + ( + constructor: ( + callable: ( + registered_at: "cookbook", + import_path: "crate::User::extract", + ), + location: ( + line: 7, + column: 8, + file: "src/static_methods/blueprint.rs", + ), + ), + lifecycle: RequestScoped, + cloning_strategy: None, + error_handler: None, + ), + ], + middlewares: [], + routes: [], + fallback_request_handler: None, + nested_blueprints: [], + ), + path_prefix: None, + nesting_location: ( + line: 7, + column: 8, + file: "src/blueprint.rs", + ), + ), + ( + blueprint: ( + creation_location: ( + line: 6, + column: 18, + file: "src/non_static_methods/blueprint.rs", + ), + constructors: [ + ( + constructor: ( + callable: ( + registered_at: "cookbook", + import_path: "crate::non_static_methods::UserStore::retrieve", + ), + location: ( + line: 7, + column: 8, + file: "src/non_static_methods/blueprint.rs", + ), + ), + lifecycle: RequestScoped, + cloning_strategy: None, + error_handler: None, + ), + ( + constructor: ( + callable: ( + registered_at: "cookbook", + import_path: "crate::non_static_methods::UserStore::new", + ), + location: ( + line: 11, + column: 8, + file: "src/non_static_methods/blueprint.rs", + ), + ), + lifecycle: Singleton, + cloning_strategy: None, + error_handler: None, + ), + ], + middlewares: [], + routes: [], + fallback_request_handler: None, + nested_blueprints: [], + ), + path_prefix: None, + nesting_location: ( + line: 8, + column: 8, + file: "src/blueprint.rs", + ), + ), + ( + blueprint: ( + creation_location: ( + line: 6, + column: 18, + file: "src/trait_methods/blueprint.rs", + ), + constructors: [ + ( + constructor: ( + callable: ( + registered_at: "cookbook", + import_path: "::id", + ), + location: ( + line: 7, + column: 8, + file: "src/trait_methods/blueprint.rs", + ), + ), + lifecycle: RequestScoped, + cloning_strategy: None, + error_handler: None, + ), + ( + constructor: ( + callable: ( + registered_at: "cookbook", + import_path: "crate::functions::extract", + ), + location: ( + line: 11, + column: 8, + file: "src/trait_methods/blueprint.rs", + ), + ), + lifecycle: RequestScoped, + cloning_strategy: None, + error_handler: None, + ), + ], + middlewares: [], + routes: [], + fallback_request_handler: None, + nested_blueprints: [], + ), + path_prefix: None, + nesting_location: ( + line: 9, + column: 8, + file: "src/blueprint.rs", + ), + ), + ( + blueprint: ( + creation_location: ( + line: 6, + column: 18, + file: "src/output/blueprint.rs", + ), + constructors: [ + ( + constructor: ( + callable: ( + registered_at: "cookbook", + import_path: "crate::output::parse", + ), + location: ( + line: 7, + column: 8, + file: "src/output/blueprint.rs", + ), + ), + lifecycle: RequestScoped, + cloning_strategy: None, + error_handler: None, + ), + ( + constructor: ( + callable: ( + registered_at: "cookbook", + import_path: "pavex::request::body::BufferedBody::extract", + ), + location: ( + line: 8, + column: 8, + file: "src/output/blueprint.rs", + ), + ), + lifecycle: RequestScoped, + cloning_strategy: None, + error_handler: Some(( + callable: ( + registered_at: "cookbook", + import_path: "pavex::request::body::errors::ExtractBufferedBodyError::into_response", + ), + location: ( + line: 12, + column: 6, + file: "src/output/blueprint.rs", + ), + )), + ), + ( + constructor: ( + callable: ( + registered_at: "cookbook", + import_path: "::default", + ), + location: ( + line: 15, + column: 8, + file: "src/output/blueprint.rs", + ), + ), + lifecycle: RequestScoped, + cloning_strategy: None, + error_handler: None, + ), + ], + middlewares: [], + routes: [], + fallback_request_handler: None, + nested_blueprints: [], + ), + path_prefix: None, + nesting_location: ( + line: 10, + column: 8, + file: "src/blueprint.rs", + ), + ), + ( + blueprint: ( + creation_location: ( + line: 6, + column: 18, + file: "src/input/blueprint.rs", + ), + constructors: [ + ( + constructor: ( + callable: ( + registered_at: "cookbook", + import_path: "crate::input::length::", + ), + location: ( + line: 7, + column: 8, + file: "src/input/blueprint.rs", + ), + ), + lifecycle: RequestScoped, + cloning_strategy: None, + error_handler: None, + ), + ( + constructor: ( + callable: ( + registered_at: "cookbook", + import_path: "crate::input::json", + ), + location: ( + line: 11, + column: 8, + file: "src/input/blueprint.rs", + ), + ), + lifecycle: RequestScoped, + cloning_strategy: None, + error_handler: None, + ), + ], + middlewares: [], + routes: [], + fallback_request_handler: None, + nested_blueprints: [], + ), + path_prefix: None, + nesting_location: ( + line: 11, + column: 8, + file: "src/blueprint.rs", + ), + ), + ], +) \ No newline at end of file diff --git a/doc_examples/guide/request_data/buffered_body/project/server_sdk/src/lib.rs b/doc_examples/guide/request_data/buffered_body/project/server_sdk/src/lib.rs new file mode 100644 index 000000000..001afbed5 --- /dev/null +++ b/doc_examples/guide/request_data/buffered_body/project/server_sdk/src/lib.rs @@ -0,0 +1,81 @@ +//! Do NOT edit this code. +//! It was automatically generated by Pavex. +//! All manual edits will be lost next time the code is generated. +extern crate alloc; +struct ServerState { + router: matchit::Router, + #[allow(dead_code)] + application_state: ApplicationState, +} +pub struct ApplicationState {} +pub async fn build_application_state() -> crate::ApplicationState { + crate::ApplicationState {} +} +pub fn run( + server_builder: pavex::server::Server, + application_state: ApplicationState, +) -> pavex::server::ServerHandle { + let server_state = std::sync::Arc::new(ServerState { + router: build_router(), + application_state, + }); + server_builder.serve(route_request, server_state) +} +fn build_router() -> matchit::Router { + let mut router = matchit::Router::new(); + router.insert("/greet", 0u32).unwrap(); + router +} +async fn route_request( + request: http::Request, + server_state: std::sync::Arc, +) -> pavex::response::Response { + let (request_head, request_body) = request.into_parts(); + #[allow(unused)] + let request_body = pavex::request::body::RawIncomingBody::from(request_body); + let request_head: pavex::request::RequestHead = request_head.into(); + let matched_route = match server_state.router.at(&request_head.uri.path()) { + Ok(m) => m, + Err(_) => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter( + vec![], + ) + .into(); + return route_1::handler(&allowed_methods).await; + } + }; + let route_id = matched_route.value; + #[allow(unused)] + let url_params: pavex::request::route::RawRouteParams<'_, '_> = matched_route + .params + .into(); + match route_id { + 0u32 => { + match &request_head.method { + &pavex::http::Method::GET => route_0::handler().await, + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_1::handler(&allowed_methods).await + } + } + } + i => unreachable!("Unknown route id: {}", i), + } +} +pub mod route_0 { + pub async fn handler() -> pavex::response::Response { + let v0 = cookbook::routes::greet(); + ::into_response(v0) + } +} +pub mod route_1 { + pub async fn handler( + v0: &pavex::router::AllowedMethods, + ) -> pavex::response::Response { + let v1 = pavex::router::default_fallback(v0).await; + ::into_response(v1) + } +} diff --git a/doc_examples/guide/request_data/buffered_body/project/src/blueprint.rs b/doc_examples/guide/request_data/buffered_body/project/src/blueprint.rs new file mode 100644 index 000000000..b7b301a42 --- /dev/null +++ b/doc_examples/guide/request_data/buffered_body/project/src/blueprint.rs @@ -0,0 +1,51 @@ +use pavex::blueprint::Blueprint; +use pavex::blueprint::constructor::Lifecycle; +use pavex::f; + +pub fn blueprint() -> Blueprint { + let mut bp = Blueprint::new(); + register_common_constructors(&mut bp); + bp.nest(crate::buffered_body::blueprint()); + bp +} + +/// Common constructors used by all routes. +fn register_common_constructors(bp: &mut Blueprint) { + // Query parameters + bp.constructor( + f!(pavex::request::query::QueryParams::extract), + Lifecycle::RequestScoped, + ) + .error_handler(f!( + pavex::request::query::errors::ExtractQueryParamsError::into_response + )); + + // Route parameters + bp.constructor( + f!(pavex::request::route::RouteParams::extract), + Lifecycle::RequestScoped, + ) + .error_handler(f!( + pavex::request::route::errors::ExtractRouteParamsError::into_response + )); + + // Json body + bp.constructor( + f!(pavex::request::body::JsonBody::extract), + Lifecycle::RequestScoped, + ) + .error_handler(f!( + pavex::request::body::errors::ExtractJsonBodyError::into_response + )); + bp.constructor( + f!(pavex::request::body::BufferedBody::extract), + Lifecycle::RequestScoped, + ) + .error_handler(f!( + pavex::request::body::errors::ExtractBufferedBodyError::into_response + )); + bp.constructor( + f!(::default), + Lifecycle::RequestScoped, + ); +} diff --git a/doc_examples/guide/request_data/buffered_body/project/src/buffered_body/blueprint.rs b/doc_examples/guide/request_data/buffered_body/project/src/buffered_body/blueprint.rs new file mode 100644 index 000000000..e8588a97c --- /dev/null +++ b/doc_examples/guide/request_data/buffered_body/project/src/buffered_body/blueprint.rs @@ -0,0 +1,9 @@ +use pavex::blueprint::router::GET; +use pavex::blueprint::Blueprint; +use pavex::f; + +pub fn blueprint() -> Blueprint { + let mut bp = Blueprint::new(); + bp.route(GET, "/buffered", f!(crate::buffered_body::handler)); + bp +} diff --git a/doc_examples/guide/request_data/buffered_body/project/src/buffered_body/mod.rs b/doc_examples/guide/request_data/buffered_body/project/src/buffered_body/mod.rs new file mode 100644 index 000000000..a94b1443b --- /dev/null +++ b/doc_examples/guide/request_data/buffered_body/project/src/buffered_body/mod.rs @@ -0,0 +1,5 @@ +pub use blueprint::blueprint; +pub use routes::handler; + +mod blueprint; +mod routes; diff --git a/doc_examples/guide/request_data/buffered_body/project/src/buffered_body/routes.rs b/doc_examples/guide/request_data/buffered_body/project/src/buffered_body/routes.rs new file mode 100644 index 000000000..8338cac9f --- /dev/null +++ b/doc_examples/guide/request_data/buffered_body/project/src/buffered_body/routes.rs @@ -0,0 +1,7 @@ +use pavex::http::StatusCode; +use pavex::request::body::BufferedBody; + +pub fn handler(body: &BufferedBody) -> StatusCode { + format!("The incoming request contains {} bytes", body.bytes.len()); + StatusCode::OK +} diff --git a/doc_examples/guide/request_data/buffered_body/project/src/custom_limit/blueprint.rs b/doc_examples/guide/request_data/buffered_body/project/src/custom_limit/blueprint.rs new file mode 100644 index 000000000..e2cab8359 --- /dev/null +++ b/doc_examples/guide/request_data/buffered_body/project/src/custom_limit/blueprint.rs @@ -0,0 +1,20 @@ +use pavex::blueprint::{constructor::Lifecycle, Blueprint}; +use pavex::f; +use pavex::request::body::BodySizeLimit; + +pub fn body_size_limit() -> BodySizeLimit { + BodySizeLimit::Enabled { + max_n_bytes: 10_485_760, // 10 MBs + } +} + +pub fn blueprint() -> Blueprint { + let mut bp = Blueprint::new(); + bp.constructor(f!(crate::body_size_limit), Lifecycle::Singleton); + bp.route( + pavex::blueprint::router::GET, + "/custom_limit", + f!(crate::custom_limit::handler), + ); + bp +} diff --git a/doc_examples/guide/request_data/buffered_body/project/src/custom_limit/mod.rs b/doc_examples/guide/request_data/buffered_body/project/src/custom_limit/mod.rs new file mode 100644 index 000000000..a94b1443b --- /dev/null +++ b/doc_examples/guide/request_data/buffered_body/project/src/custom_limit/mod.rs @@ -0,0 +1,5 @@ +pub use blueprint::blueprint; +pub use routes::handler; + +mod blueprint; +mod routes; diff --git a/doc_examples/guide/request_data/buffered_body/project/src/custom_limit/routes.rs b/doc_examples/guide/request_data/buffered_body/project/src/custom_limit/routes.rs new file mode 100644 index 000000000..0aa9f0532 --- /dev/null +++ b/doc_examples/guide/request_data/buffered_body/project/src/custom_limit/routes.rs @@ -0,0 +1,6 @@ +use pavex::http::StatusCode; +use pavex::request::body::BufferedBody; + +pub fn handler(_body: BufferedBody) -> StatusCode { + StatusCode::OK +} diff --git a/doc_examples/guide/request_data/buffered_body/project/src/granular_limits/blueprint.rs b/doc_examples/guide/request_data/buffered_body/project/src/granular_limits/blueprint.rs new file mode 100644 index 000000000..d39449991 --- /dev/null +++ b/doc_examples/guide/request_data/buffered_body/project/src/granular_limits/blueprint.rs @@ -0,0 +1,25 @@ +use pavex::blueprint::{constructor::Lifecycle, router::POST, Blueprint}; +use pavex::f; +use pavex::request::body::BodySizeLimit; + +pub fn blueprint() -> Blueprint { + let mut bp = Blueprint::new(); + bp.nest(upload_bp()); + // Other routes... + bp +} + +fn upload_bp() -> Blueprint { + let mut bp = Blueprint::new(); + // This limit will only apply to the routes registered + // in this nested blueprint. + bp.constructor(f!(crate::upload_size_limit), Lifecycle::Singleton); + bp.route(POST, "/upload", f!(crate::upload)); + bp +} + +pub fn upload_size_limit() -> BodySizeLimit { + BodySizeLimit::Enabled { + max_n_bytes: 1_073_741_824, // 1GB + } +} diff --git a/doc_examples/guide/request_data/buffered_body/project/src/granular_limits/mod.rs b/doc_examples/guide/request_data/buffered_body/project/src/granular_limits/mod.rs new file mode 100644 index 000000000..413b96f93 --- /dev/null +++ b/doc_examples/guide/request_data/buffered_body/project/src/granular_limits/mod.rs @@ -0,0 +1,5 @@ +pub use blueprint::{blueprint, upload_size_limit}; +pub use routes::upload; + +mod blueprint; +mod routes; diff --git a/doc_examples/guide/request_data/buffered_body/project/src/granular_limits/routes.rs b/doc_examples/guide/request_data/buffered_body/project/src/granular_limits/routes.rs new file mode 100644 index 000000000..bbc867980 --- /dev/null +++ b/doc_examples/guide/request_data/buffered_body/project/src/granular_limits/routes.rs @@ -0,0 +1,6 @@ +use pavex::http::StatusCode; +use pavex::request::body::BufferedBody; + +pub fn upload(_body: BufferedBody) -> StatusCode { + StatusCode::OK +} diff --git a/doc_examples/guide/request_data/buffered_body/project/src/lib.rs b/doc_examples/guide/request_data/buffered_body/project/src/lib.rs new file mode 100644 index 000000000..6b0435f4e --- /dev/null +++ b/doc_examples/guide/request_data/buffered_body/project/src/lib.rs @@ -0,0 +1,11 @@ +#![allow(dead_code)] +#![allow(unused_variables)] + +pub use blueprint::blueprint; +pub use granular_limits::{upload, upload_size_limit}; + +mod blueprint; +pub mod buffered_body; +pub mod custom_limit; +pub mod granular_limits; +pub mod no_limit; diff --git a/doc_examples/guide/request_data/buffered_body/project/src/main.rs b/doc_examples/guide/request_data/buffered_body/project/src/main.rs new file mode 100644 index 000000000..df77aade6 --- /dev/null +++ b/doc_examples/guide/request_data/buffered_body/project/src/main.rs @@ -0,0 +1,13 @@ +use std::error::Error; + +use buffered_body::blueprint; +use cargo_px_env::generated_pkg_manifest_path; +use pavex_cli_client::Client; + +fn main() -> Result<(), Box> { + let generated_dir = generated_pkg_manifest_path()?.parent().unwrap().into(); + Client::new() + .generate(blueprint(), generated_dir) + .execute()?; + Ok(()) +} diff --git a/doc_examples/guide/request_data/buffered_body/project/src/no_limit/blueprint.rs b/doc_examples/guide/request_data/buffered_body/project/src/no_limit/blueprint.rs new file mode 100644 index 000000000..909de4596 --- /dev/null +++ b/doc_examples/guide/request_data/buffered_body/project/src/no_limit/blueprint.rs @@ -0,0 +1,18 @@ +use pavex::blueprint::{constructor::Lifecycle, Blueprint}; +use pavex::f; +use pavex::request::body::BodySizeLimit; + +pub fn body_size_limit() -> BodySizeLimit { + BodySizeLimit::Disabled +} + +pub fn blueprint() -> Blueprint { + let mut bp = Blueprint::new(); + bp.constructor(f!(crate::body_size_limit), Lifecycle::Singleton); + bp.route( + pavex::blueprint::router::GET, + "/no_limit", + f!(crate::no_limit::handler), + ); + bp +} diff --git a/doc_examples/guide/request_data/buffered_body/project/src/no_limit/mod.rs b/doc_examples/guide/request_data/buffered_body/project/src/no_limit/mod.rs new file mode 100644 index 000000000..a94b1443b --- /dev/null +++ b/doc_examples/guide/request_data/buffered_body/project/src/no_limit/mod.rs @@ -0,0 +1,5 @@ +pub use blueprint::blueprint; +pub use routes::handler; + +mod blueprint; +mod routes; diff --git a/doc_examples/guide/request_data/buffered_body/project/src/no_limit/routes.rs b/doc_examples/guide/request_data/buffered_body/project/src/no_limit/routes.rs new file mode 100644 index 000000000..0aa9f0532 --- /dev/null +++ b/doc_examples/guide/request_data/buffered_body/project/src/no_limit/routes.rs @@ -0,0 +1,6 @@ +use pavex::http::StatusCode; +use pavex::request::body::BufferedBody; + +pub fn handler(_body: BufferedBody) -> StatusCode { + StatusCode::OK +} diff --git a/doc_examples/guide/request_data/buffered_body/tutorial.yml b/doc_examples/guide/request_data/buffered_body/tutorial.yml new file mode 100644 index 000000000..a5d43f76b --- /dev/null +++ b/doc_examples/guide/request_data/buffered_body/tutorial.yml @@ -0,0 +1,18 @@ +starter_project_folder: "project" +commands: + - command: "cargo px c" + expected_outcome: "success" +snippets: + - name: "extraction" + source_path: "src/buffered_body/routes.rs" + ranges: [ "0..5", "6..7" ] + hl_lines: [ 4 ] + - name: "custom_limit" + source_path: "src/custom_limit/blueprint.rs" + ranges: [ "0..13", "19..20" ] + - name: "no_limit" + source_path: "src/no_limit/blueprint.rs" + ranges: [ "0..11", "17..18" ] + - name: "granular_limits" + source_path: "src/granular_limits/blueprint.rs" + ranges: [ ".." ] diff --git a/docs/guide/request_data/body/byte_wrappers.md b/docs/guide/request_data/body/byte_wrappers.md index c040e3557..93d503ca8 100644 --- a/docs/guide/request_data/body/byte_wrappers.md +++ b/docs/guide/request_data/body/byte_wrappers.md @@ -1,3 +1,53 @@ # Low-level access -[`BufferedBody`](../buffered.md) buffers the raw stream of bytes into a [`Vec`](https://doc.rust-lang.org/std/vec/struct.Vec.html). +[`BufferedBody`][BufferedBody] is Pavex's main interface to work with the bytes of the incoming request body. + +[`BufferedBody`][BufferedBody] consumes the [raw byte stream](../wire_data.md#rawincomingbody) and +buffers the entire body of the incoming request in memory. +At the same time, it takes care of enforcing [sane limits](#body-size-limit) to prevent resource exhaustion attacks. + +## Use cases + +[`BufferedBody`][BufferedBody] is the ideal building block for other extractors that need to have the entire body +available in memory to do their job (e.g. [`JsonBody`][JsonBody]). + +[`BufferedBody`][BufferedBody] is also a good fit if you need to access the raw bytes of the +body ahead of deserialization (e.g. to compute its hash as a step of a signature verification process). +In those scenarios, make sure to inject a shared reference to [`BufferedBody`][BufferedBody] (i.e. `&BufferedBody`) +into your component rather than consuming it (i.e. `BufferedBody`). + +--8<-- "doc_examples/guide/request_data/buffered_body/project-extraction.snap" + +## Body size limit + +[BufferedBody] enforces an upper limit on the body size to prevent [resource exhaustion attacks](https://owasp.org/API-Security/editions/2023/en/0xa4-unrestricted-resource-consumption/). +The default limit is 2 MBs. +[BufferedBody::extract] returns [SizeLimitExceeded] if the limit is exceeded. + +### Custom limit + +You can customize the limit by registering a custom constructor for [BodySizeLimit] in your [Blueprint]: + +--8<-- "doc_examples/guide/request_data/buffered_body/project-custom_limit.snap" + +### No limit + +You can also disable the limit altogether: + +--8<-- "doc_examples/guide/request_data/buffered_body/project-no_limit.snap" + +### Granular limits + +In large applications with many routes it can be hard +(if not impossible) to find a single limit that works for all routes. +You can leverage nesting to define more granular limits. + +--8<-- "doc_examples/guide/request_data/buffered_body/project-granular_limits.snap" + + +[BufferedBody]: ../../../../api_reference/pavex/request/body/struct.BufferedBody.html +[JsonBody]: ../../../../api_reference/pavex/request/body/struct.JsonBody.html +[BufferedBody::extract]: ../../../../api_reference/pavex/request/body/struct.BufferedBody.html#method.extract +[SizeLimitExceeded]: ../../../../api_reference/pavex/request/body/errors/enum.ExtractBufferedBodyError.html#variant.SizeLimitExceeded +[BodySizeLimit]: ../../../../api_reference/pavex/request/body/enum.BodySizeLimit.html +[Blueprint]: ../../../../api_reference/pavex/blueprint/struct.Blueprint.html \ No newline at end of file diff --git a/libs/pavex/src/request/body/buffered_body.rs b/libs/pavex/src/request/body/buffered_body.rs index 276e74ae2..d511ecd66 100644 --- a/libs/pavex/src/request/body/buffered_body.rs +++ b/libs/pavex/src/request/body/buffered_body.rs @@ -28,15 +28,18 @@ use super::{ /// # Example /// /// ```rust +/// use pavex::http::StatusCode; /// use pavex::request::body::BufferedBody; /// /// // The `BufferedBody` extractor consumes the raw request body stream /// // and buffers its entire contents in memory. -/// pub fn handler(body: &BufferedBody) -> String { +/// pub fn handler(body: &BufferedBody) -> StatusCode { /// format!( /// "The incoming request contains {} bytes", /// body.bytes.len(), -/// ) +/// ); +/// // [...] +/// # StatusCode::OK /// } /// ``` /// @@ -239,14 +242,10 @@ impl From for Bytes { #[cfg(test)] mod tests { - use bytes::Bytes; use http::HeaderMap; - use http_body_util::Full; use crate::request::RequestHead; - use super::BufferedBody; - // No headers. fn dummy_request_head() -> RequestHead { RequestHead {