Skip to content

Commit

Permalink
Merge branch 'release/v1.0.2'
Browse files Browse the repository at this point in the history
  • Loading branch information
rousan committed May 10, 2020
2 parents a2038ca + 6f8ccd1 commit 15b8c31
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 5 deletions.
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "multer"
version = "1.0.1"
version = "1.0.2"
description = "An async parser for `multipart/form-data` content-type in Rust."
homepage = "https://github.com/rousan/multer-rs"
repository = "https://github.com/rousan/multer-rs"
Expand Down Expand Up @@ -44,6 +44,7 @@ tokio-util = { version = "0.3.1", features = ["codec"], optional = true}
serde = { version = "1.0", features = ["derive"] }
tokio = { version = "0.2", features = ["full"] }
hyper = "0.13"
routerify = "1.1"

[[example]]
name = "test"
Expand Down
2 changes: 2 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,6 @@ Run an example:

* [`hyper_server_example`](hyper_server_example.rs) - Shows how to use this crate with Rust HTTP server [hyper](https://hyper.rs/).

* [`routerify_example`](routerify_example.rs) - Shows how to use this crate with [hyper](https://hyper.rs/) router implementation [Routerify](https://github.com/routerify/routerify).

* [`parse_async_read`](parse_async_read.rs) - Shows how to parse `multipart/form-data` from an [`AsyncRead`](https://docs.rs/tokio/0.2.20/tokio/io/trait.AsyncRead.html).
107 changes: 107 additions & 0 deletions examples/routerify_example.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
use hyper::{header::CONTENT_TYPE, Body, Request, Response, Server, StatusCode};
// Import the routerify prelude traits.
use routerify::prelude::*;
// Import routerify types.
use routerify::{Middleware, Router, RouterService};
use std::{convert::Infallible, net::SocketAddr};
// Import the multer types.
use multer::Multipart;

// A handler to handle file upload at `/upload` path.
async fn file_upload_handler(req: Request<Body>) -> Result<Response<Body>, 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(Body::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(Body::from(format!("INTERNAL SERVER ERROR: {}", err)))
.unwrap());
}

Ok(Response::new(Body::from("Success")))
}

// Process the request body as multipart/form-data.
async fn process_multipart(body: Body, boundary: String) -> multer::Result<()> {
// Create a Multipart instance from the request body.
let mut multipart = Multipart::new(body, 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(())
}

// A routerify middleware which logs an http request.
async fn logger(req: Request<Body>) -> Result<Request<Body>, Infallible> {
println!("{} {} {}", req.remote_addr(), req.method(), req.uri().path());
Ok(req)
}

// Create a `Router<Body, Infallible>` for response body type `hyper::Body` and for handler error type `Infallible`.
fn router() -> Router<Body, Infallible> {
// Create a router and specify the logger middleware and the handlers.
// Here, "Middleware::pre" means we're adding a pre middleware which will be executed
// before any route handlers.
Router::builder()
.middleware(Middleware::pre(logger))
.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], 3000));

// 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);
}
}
4 changes: 2 additions & 2 deletions src/buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ use std::task::{Context, Poll};
pub(crate) struct StreamBuffer {
pub(crate) eof: bool,
pub(crate) buf: BytesMut,
pub(crate) stream: Pin<Box<dyn Stream<Item = Result<Bytes, crate::Error>> + Send + Sync>>,
pub(crate) stream: Pin<Box<dyn Stream<Item = Result<Bytes, crate::Error>> + Send>>,
}

impl StreamBuffer {
pub fn new<S>(stream: S) -> Self
where
S: Stream<Item = Result<Bytes, crate::Error>> + Send + Sync + 'static,
S: Stream<Item = Result<Bytes, crate::Error>> + Send + 'static,
{
StreamBuffer {
eof: false,
Expand Down
4 changes: 2 additions & 2 deletions src/multipart.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ impl Multipart {
/// Construct a new `Multipart` instance with the given [`Bytes`](https://docs.rs/bytes/0.5.4/bytes/struct.Bytes.html) stream and the boundary.
pub fn new<S, O, E, B>(stream: S, boundary: B) -> Multipart
where
S: Stream<Item = Result<O, E>> + Send + Sync + 'static,
S: Stream<Item = Result<O, E>> + Send + 'static,
O: Into<Bytes> + 'static,
E: Into<Box<dyn std::error::Error + Send + Sync>> + 'static,
B: Into<String>,
Expand Down Expand Up @@ -105,7 +105,7 @@ impl Multipart {
#[cfg(feature = "reader")]
pub fn with_reader<R, B>(reader: R, boundary: B) -> Multipart
where
R: AsyncRead + Send + Sync + 'static,
R: AsyncRead + Send + 'static,
B: Into<String>,
{
let stream = FramedRead::new(reader, BytesCodec::new());
Expand Down

0 comments on commit 15b8c31

Please sign in to comment.