From b9559922ab556d8d47680298e0d32d073f05be61 Mon Sep 17 00:00:00 2001 From: Rousan Ali Date: Mon, 11 May 2020 01:07:47 +0530 Subject: [PATCH 1/2] Remove Sync constraint from Stream type --- Cargo.toml | 1 + examples/README.md | 2 + examples/routerify_example.rs | 107 ++++++++++++++++++++++++++++++++++ src/buffer.rs | 4 +- src/multipart.rs | 4 +- 5 files changed, 114 insertions(+), 4 deletions(-) create mode 100644 examples/routerify_example.rs diff --git a/Cargo.toml b/Cargo.toml index 051fcef..842737f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" diff --git a/examples/README.md b/examples/README.md index 16b63b1..08aeee3 100644 --- a/examples/README.md +++ b/examples/README.md @@ -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). \ No newline at end of file diff --git a/examples/routerify_example.rs b/examples/routerify_example.rs new file mode 100644 index 0000000..e004d15 --- /dev/null +++ b/examples/routerify_example.rs @@ -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) -> Result, 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) -> Result, Infallible> { + println!("{} {} {}", req.remote_addr(), req.method(), req.uri().path()); + Ok(req) +} + +// Create a `Router` for response body type `hyper::Body` and for handler error type `Infallible`. +fn router() -> Router { + // 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); + } +} diff --git a/src/buffer.rs b/src/buffer.rs index 1eaa059..3c07e0c 100644 --- a/src/buffer.rs +++ b/src/buffer.rs @@ -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> + Send + Sync>>, + pub(crate) stream: Pin> + Send>>, } impl StreamBuffer { pub fn new(stream: S) -> Self where - S: Stream> + Send + Sync + 'static, + S: Stream> + Send + 'static, { StreamBuffer { eof: false, diff --git a/src/multipart.rs b/src/multipart.rs index 49830e0..6742918 100644 --- a/src/multipart.rs +++ b/src/multipart.rs @@ -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(stream: S, boundary: B) -> Multipart where - S: Stream> + Send + Sync + 'static, + S: Stream> + Send + 'static, O: Into + 'static, E: Into> + 'static, B: Into, @@ -105,7 +105,7 @@ impl Multipart { #[cfg(feature = "reader")] pub fn with_reader(reader: R, boundary: B) -> Multipart where - R: AsyncRead + Send + Sync + 'static, + R: AsyncRead + Send + 'static, B: Into, { let stream = FramedRead::new(reader, BytesCodec::new()); From 6f8ccd1e70afc3bc63ff65e274cf6f898367c790 Mon Sep 17 00:00:00 2001 From: Rousan Ali Date: Mon, 11 May 2020 01:10:17 +0530 Subject: [PATCH 2/2] Bump version --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 842737f..03ce6c5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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"