routerify_multipart/lib.rs
1//! A `multipart/form-data` parser for [`Routerify`](https://github.com/routerify/routerify).
2//!
3//! It's using [multer](https://github.com/rousan/multer-rs) to parse the `multipart/form-data` content.
4//!
5//! # Examples
6//!
7//! ```no_run
8//! use hyper::{Body, Request, Response, Server, StatusCode};
9//! use routerify::{Error, Router, RouterService};
10//! // Import `RequestMultipartExt` trait.
11//! use routerify_multipart::RequestMultipartExt;
12//! use std::net::SocketAddr;
13//!
14//! // A handler to handle file uploading in `multipart/form-data` content-type.
15//! async fn file_upload_handler(req: Request<Body>) -> Result<Response<Body>, Error> {
16//! // Convert the request into a `Multipart` instance.
17//! let mut multipart = match req.into_multipart() {
18//! Ok(m) => m,
19//! Err(err) => {
20//! return Ok(Response::builder()
21//! .status(StatusCode::BAD_REQUEST)
22//! .body(Body::from(format!("Bad Request: {}", err)))
23//! .unwrap());
24//! }
25//! };
26//!
27//! // Iterate over the fields.
28//! while let Some(mut field) = multipart.next_field().await.map_err(|err| Error::wrap(err))? {
29//! // Get the field name.
30//! let name = field.name();
31//! // Get the field's filename if provided in "Content-Disposition" header.
32//! let file_name = field.file_name();
33//!
34//! println!("Name {:?}, File name: {:?}", name, file_name);
35//!
36//! // Process the field data chunks e.g. store them in a file.
37//! while let Some(chunk) = field.chunk().await.map_err(|err| Error::wrap(err))? {
38//! // Do something with field chunk.
39//! println!("Chunk: {:?}", chunk);
40//! }
41//! }
42//!
43//! Ok(Response::new(Body::from("Success")))
44//! }
45//!
46//! // Create a router.
47//! fn router() -> Router<Body, Error> {
48//! // Register the handlers.
49//! Router::builder().post("/upload", file_upload_handler).build().unwrap()
50//! }
51//!
52//! #[tokio::main]
53//! async fn main() {
54//! let router = router();
55//!
56//! // Create a Service from the router above to handle incoming requests.
57//! let service = RouterService::new(router).unwrap();
58//!
59//! // The address on which the server will be listening.
60//! let addr = SocketAddr::from(([127, 0, 0, 1], 3001));
61//!
62//! // Create a server by passing the created service to `.serve` method.
63//! let server = Server::bind(&addr).serve(service);
64//!
65//! println!("App is running on: {}", addr);
66//! if let Err(err) = server.await {
67//! eprintln!("Server error: {}", err);
68//! }
69//! }
70//! ```
71//!
72//! ## Prevent DDoS Attack
73//!
74//! This crate also provides some APIs to prevent potential `DDoS attack` with fine grained control. It's recommended to add some constraints
75//! on field (specially text field) size to avoid potential `DDoS attack` from attackers running the server out of memory.
76//!
77//! An example:
78//!
79//! ```no_run
80//! use hyper::{Body, Request, Response, Server, StatusCode};
81//! use routerify::{Error, Router, RouterService};
82//! // Import `RequestMultipartExt` trait and other types.
83//! use routerify_multipart::{RequestMultipartExt, Constraints, SizeLimit};
84//! use std::net::SocketAddr;
85//!
86//! // A handler to handle file uploading in `multipart/form-data` content-type.
87//! async fn file_upload_handler(req: Request<Body>) -> Result<Response<Body>, Error> {
88//! // Create some constraints to be applied to the fields to prevent DDoS attack.
89//! let constraints = Constraints::new()
90//! // We only accept `my_text_field` and `my_file_field` fields,
91//! // For any unknown field, we will throw an error.
92//! .allowed_fields(vec!["my_text_field", "my_file_field"])
93//! .size_limit(
94//! SizeLimit::new()
95//! // Set 15mb as size limit for the whole stream body.
96//! .whole_stream(15 * 1024 * 1024)
97//! // Set 10mb as size limit for all fields.
98//! .per_field(10 * 1024 * 1024)
99//! // Set 30kb as size limit for our text field only.
100//! .for_field("my_text_field", 30 * 1024),
101//! );
102//!
103//! // Convert the request into a `Multipart` instance.
104//! let mut multipart = match req.into_multipart_with_constraints(constraints) {
105//! Ok(m) => m,
106//! Err(err) => {
107//! return Ok(Response::builder()
108//! .status(StatusCode::BAD_REQUEST)
109//! .body(Body::from(format!("Bad Request: {}", err)))
110//! .unwrap());
111//! }
112//! };
113//!
114//! // Iterate over the fields.
115//! while let Some(mut field) = multipart.next_field().await.map_err(|err| Error::wrap(err))? {
116//! // Get the field name.
117//! let name = field.name();
118//! // Get the field's filename if provided in "Content-Disposition" header.
119//! let file_name = field.file_name();
120//!
121//! println!("Name {:?}, File name: {:?}", name, file_name);
122//!
123//! // Process the field data chunks e.g. store them in a file.
124//! while let Some(chunk) = field.chunk().await.map_err(|err| Error::wrap(err))? {
125//! // Do something with field chunk.
126//! println!("Chunk: {:?}", chunk);
127//! }
128//! }
129//!
130//! Ok(Response::new(Body::from("Success")))
131//! }
132//! ```
133//!
134//! Please refer [`Constraints`](./struct.Constraints.html) for more info.
135
136pub use self::ext::RequestMultipartExt;
137pub use multer::{Constraints, Error, Field, Multipart, SizeLimit};
138
139mod ext;