Vendor dependencies

This commit is contained in:
2026-08-01 16:11:49 +03:00
parent 7f139a0241
commit 6b5e7f0f8b
29706 changed files with 9575646 additions and 0 deletions
+19
View File
@@ -0,0 +1,19 @@
# Examples of using multer-rs
These examples show of how to do common tasks using `multer-rs`.
Please visit: [Docs](https://docs.rs/multer) for the documentation.
Run an example:
```sh
cargo run --example example_name
```
* [`simple_example`](simple_example.rs) - A basic example using `multer`.
* [`hyper_server_example`](hyper_server_example.rs) - Shows how to use this crate with Rust HTTP server [hyper](https://hyper.rs/).
* [`parse_async_read`](parse_async_read.rs) - Shows how to parse `multipart/form-data` from an [`AsyncRead`](https://docs.rs/tokio/1/tokio/io/trait.AsyncRead.html).
* [`prevent_dos_attack`](prevent_dos_attack.rs) - Shows how to apply some rules to prevent potential DoS attacks while handling `multipart/form-data`.
@@ -0,0 +1,97 @@
use std::{convert::Infallible, net::SocketAddr};
use bytes::Bytes;
use futures_util::StreamExt;
use http_body_util::{BodyStream, Full};
use hyper::{body::Incoming, header::CONTENT_TYPE, Request, Response, StatusCode};
// Import the multer types.
use multer::Multipart;
// A handler for incoming requests.
async fn handle(req: Request<Incoming>) -> Result<Response<Full<Bytes>>, Infallible> {
// Extract the `multipart/form-data` boundary from the headers.
let boundary = req
.headers()
.get(CONTENT_TYPE)
.and_then(|ct| ct.to_str().ok())
.and_then(|ct| multer::parse_boundary(ct).ok());
// Send `BAD_REQUEST` status if the content-type is not multipart/form-data.
if boundary.is_none() {
return Ok(Response::builder()
.status(StatusCode::BAD_REQUEST)
.body(Full::from("BAD REQUEST"))
.unwrap());
}
// Process the multipart e.g. you can store them in files.
if let Err(err) = process_multipart(req.into_body(), boundary.unwrap()).await {
return Ok(Response::builder()
.status(StatusCode::INTERNAL_SERVER_ERROR)
.body(Full::from(format!("INTERNAL SERVER ERROR: {}", err)))
.unwrap());
}
Ok(Response::new(Full::from("Success")))
}
// Process the request body as multipart/form-data.
async fn process_multipart(body: Incoming, boundary: String) -> multer::Result<()> {
// Convert the body into a stream of data frames.
let body_stream = BodyStream::new(body)
.filter_map(|result| async move { result.map(|frame| frame.into_data().ok()).transpose() });
// Create a Multipart instance from the request body.
let mut multipart = Multipart::new(body_stream, boundary);
// Iterate over the fields, `next_field` method will return the next field if
// available.
while let Some(mut field) = multipart.next_field().await? {
// 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();
// Get the "Content-Type" header as `mime::Mime` type.
let content_type = field.content_type();
println!(
"Name: {:?}, FileName: {:?}, Content-Type: {:?}",
name, file_name, content_type
);
// Process the field data chunks e.g. store them in a file.
let mut field_bytes_len = 0;
while let Some(field_chunk) = field.chunk().await? {
// Do something with field chunk.
field_bytes_len += field_chunk.len();
}
println!("Field Bytes Length: {:?}", field_bytes_len);
}
Ok(())
}
#[tokio::main]
async fn main() {
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
let listener = tokio::net::TcpListener::bind(addr).await.unwrap();
println!("Server running at: {}", addr);
let service = hyper::service::service_fn(handle);
loop {
let (socket, _remote_addr) = listener.accept().await.unwrap();
let socket = hyper_util::rt::TokioIo::new(socket);
tokio::spawn(async move {
if let Err(e) = hyper::server::conn::http1::Builder::new()
.serve_connection(socket, service)
.await
{
eprintln!("server error: {}", e);
}
});
}
}
@@ -0,0 +1,41 @@
use multer::Multipart;
use tokio::io::AsyncRead;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Generate an `AsyncRead` and the boundary from somewhere e.g. server request
// body.
let (reader, boundary) = get_async_reader_from_somewhere().await;
// Create a `Multipart` instance from that async reader and the boundary.
let mut multipart = Multipart::with_reader(reader, boundary);
// Iterate over the fields, use `next_field()` to get the next field.
while let Some(mut field) = multipart.next_field().await? {
// Get 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.
let mut field_bytes_len = 0;
while let Some(field_chunk) = field.chunk().await? {
// Do something with field chunk.
field_bytes_len += field_chunk.len();
}
println!("Field Bytes Length: {:?}", field_bytes_len);
}
Ok(())
}
// Generate an `AsyncRead` and the boundary from somewhere e.g. server request
// body.
async fn get_async_reader_from_somewhere() -> (impl AsyncRead, &'static str) {
let data = "--X-BOUNDARY\r\nContent-Disposition: form-data; name=\"my_text_field\"\r\n\r\nabcd\r\n--X-BOUNDARY\r\nContent-Disposition: form-data; name=\"my_file_field\"; filename=\"a-text-file.txt\"\r\nContent-Type: text/plain\r\n\r\nHello world\nHello\r\nWorld\rAgain\r\n--X-BOUNDARY--\r\n";
(data.as_bytes(), "X-BOUNDARY")
}
@@ -0,0 +1,60 @@
use std::convert::Infallible;
use bytes::Bytes;
use futures_util::stream::Stream;
// Import multer types.
use multer::{Constraints, Multipart, SizeLimit};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Generate a byte stream and the boundary from somewhere e.g. server request
// body.
let (stream, boundary) = get_byte_stream_from_somewhere().await;
// Create some constraints to be applied to the fields to prevent DoS attacks.
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),
);
// Create a `Multipart` instance from that byte stream and the constraints.
let mut multipart = Multipart::with_constraints(stream, boundary, constraints);
// Iterate over the fields, use `next_field()` to get the next field.
while let Some(field) = multipart.next_field().await? {
// Get 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);
// Read field content as text.
let content = field.text().await?;
println!("Content: {:?}", content);
}
Ok(())
}
// Generate a byte stream and the boundary from somewhere e.g. server request
// body.
async fn get_byte_stream_from_somewhere() -> (impl Stream<Item = Result<Bytes, Infallible>>, &'static str) {
let data = "--X-BOUNDARY\r\nContent-Disposition: form-data; name=\"my_text_field\"\r\n\r\nabcd\r\n--X-BOUNDARY\r\nContent-Disposition: form-data; name=\"my_file_field\"; filename=\"a-text-file.txt\"\r\nContent-Type: text/plain\r\n\r\nHello world\nHello\r\nWorld\rAgain\r\n--X-BOUNDARY--\r\n";
let stream = futures_util::stream::iter(
data.chars()
.map(|ch| ch.to_string())
.map(|part| Ok(Bytes::copy_from_slice(part.as_bytes()))),
);
(stream, "X-BOUNDARY")
}
+45
View File
@@ -0,0 +1,45 @@
use std::convert::Infallible;
use bytes::Bytes;
use futures_util::stream::Stream;
// Import multer types.
use multer::Multipart;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Generate a byte stream and the boundary from somewhere e.g. server request
// body.
let (stream, boundary) = get_byte_stream_from_somewhere().await;
// Create a `Multipart` instance from that byte stream and the boundary.
let mut multipart = Multipart::new(stream, boundary);
// Iterate over the fields, use `next_field()` to get the next field.
while let Some(field) = multipart.next_field().await? {
// Get 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);
// Read field content as text.
let content = field.text().await?;
println!("Content: {:?}", content);
}
Ok(())
}
// Generate a byte stream and the boundary from somewhere e.g. server request
// body.
async fn get_byte_stream_from_somewhere() -> (impl Stream<Item = Result<Bytes, Infallible>>, &'static str) {
let data = "--X-BOUNDARY\r\nContent-Disposition: form-data; name=\"my_text_field\"\r\n\r\nabcd\r\n--X-BOUNDARY\r\nContent-Disposition: form-data; name=\"my_file_field\"; filename=\"a-text-file.txt\"\r\nContent-Type: text/plain\r\n\r\nHello world\nHello\r\nWorld\rAgain\r\n--X-BOUNDARY--\r\n";
let stream = futures_util::stream::iter(
data.chars()
.map(|ch| ch.to_string())
.map(|part| Ok(Bytes::copy_from_slice(part.as_bytes()))),
);
(stream, "X-BOUNDARY")
}