routerify-multipart
A multipart/form-data
parser for Routerify
.
It's using multer to parse the multipart/form-data
content.
Docs
Install
Add this to your Cargo.toml
file:
[dependencies]
routerify = "2"
routerify-multipart = "2"
Example
use hyper::{Body, Request, Response, Server, StatusCode};
use routerify::{Error, Router, RouterService};
use routerify_multipart::RequestMultipartExt;
use std::net::SocketAddr;
async fn file_upload_handler(req: Request<Body>) -> Result<Response<Body>, Error> {
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());
}
};
while let Some(mut field) = multipart.next_field().await.map_err(|err| Error::wrap(err))? {
let name = field.name();
let file_name = field.file_name();
println!("Name {:?}, File name: {:?}", name, file_name);
while let Some(chunk) = field.chunk().await.map_err(|err| Error::wrap(err))? {
println!("Chunk: {:?}", chunk);
}
}
Ok(Response::new(Body::from("Success")))
}
fn router() -> Router<Body, Error> {
Router::builder().post("/upload", file_upload_handler).build().unwrap()
}
#[tokio::main]
async fn main() {
let router = router();
let service = RouterService::new(router).unwrap();
let addr = SocketAddr::from(([127, 0, 0, 1], 3001));
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:
use hyper::{Body, Request, Response, Server, StatusCode};
use routerify::{Error, Router, RouterService};
use routerify_multipart::{RequestMultipartExt, Constraints, SizeLimit};
use std::net::SocketAddr;
async fn file_upload_handler(req: Request<Body>) -> Result<Response<Body>, Error> {
let constraints = Constraints::new()
.allowed_fields(vec!["my_text_field", "my_file_field"])
.size_limit(
SizeLimit::new()
.whole_stream(15 * 1024 * 1024)
.per_field(10 * 1024 * 1024)
.for_field("my_text_field", 30 * 1024),
);
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());
}
};
while let Some(mut field) = multipart.next_field().await.map_err(|err| Error::wrap(err))? {
let name = field.name();
let file_name = field.file_name();
println!("Name {:?}, File name: {:?}", name, file_name);
while let Some(chunk) = field.chunk().await.map_err(|err| Error::wrap(err))? {
println!("Chunk: {:?}", chunk);
}
}
Ok(Response::new(Body::from("Success")))
}
Contributing
Your PRs and suggestions are always welcome.