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

pub use self::ext::RequestMultipartExt;
pub use multer::{Constraints, Error, Field, Multipart, SizeLimit};

mod ext;