From 152e37409728c2a2d288cd4fb1068136676d311d Mon Sep 17 00:00:00 2001 From: Kian-Meng Ang Date: Fri, 30 Dec 2022 21:29:39 +0800 Subject: [PATCH] Fix typos Found via `codespell -S target,*.txt -L crate` --- README.md | 2 +- examples/README.md | 2 +- examples/hyper_client.rs | 19 ++- examples/hyper_reqbuilder.rs | 2 +- examples/hyper_server.rs | 22 ++-- examples/iron.rs | 27 ++-- examples/iron_intercept.rs | 8 +- examples/nickel.rs | 36 +++--- examples/rocket.rs | 32 +++-- examples/tiny_http.rs | 31 +++-- src/bin/form_test.rs | 13 +- src/client/hyper.rs | 19 +-- src/client/sized.rs | 31 +++-- src/mock.rs | 22 ++-- src/server/boundary.rs | 119 +++++++++++------- src/server/hyper.rs | 68 +++++----- src/server/iron.rs | 126 +++++++++++++------ src/server/mod.rs | 106 +++++++++------- src/server/nickel.rs | 21 ++-- src/server/save.rs | 236 ++++++++++++++++++++++++----------- 20 files changed, 586 insertions(+), 356 deletions(-) diff --git a/README.md b/README.md index a97f805..b77fafd 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ Provides server-side integration with `tiny_http::Request` via `multipart::serve ### [Rocket ![](https://img.shields.io/crates/v/rocket.svg)](https://crates.io/crates/rocket) Direct integration is not provided as the Rocket folks seem to want to handle `multipart/form-data` -behind the scenes which would supercede any integration with `multipart`. However, an example is available +behind the scenes which would supersede any integration with `multipart`. However, an example is available showing how to use `multipart` on a Rocket server: [examples/rocket.rs](examples/rocket.rs) ## ⚡ Powered By ⚡ diff --git a/examples/README.md b/examples/README.md index 580d3bb..64f630d 100644 --- a/examples/README.md +++ b/examples/README.md @@ -102,7 +102,7 @@ Author: [abonander] This example shows how `multipart`'s server API can be used with [Rocket](https://rocket.rs) without explicit support (the Rocket folks seem to want to handle `multipart/form-data` behind the scenes -but haven't gotten around to implementing it yet; this would supercede any integration from `multipart`). +but haven't gotten around to implementing it yet; this would supersede any integration from `multipart`). ``` $ cargo run --example rocket --features "rocket" diff --git a/examples/hyper_client.rs b/examples/hyper_client.rs index 8e968f7..a8bdaad 100644 --- a/examples/hyper_client.rs +++ b/examples/hyper_client.rs @@ -10,23 +10,21 @@ use multipart::client::Multipart; use std::io::Read; fn main() { - let url = "http://localhost:80".parse() - .expect("Failed to parse URL"); + let url = "http://localhost:80".parse().expect("Failed to parse URL"); - let request = Request::new(Method::Post, url) - .expect("Failed to create request"); + let request = Request::new(Method::Post, url).expect("Failed to create request"); - let mut multipart = Multipart::from_request(request) - .expect("Failed to create Multipart"); + let mut multipart = Multipart::from_request(request).expect("Failed to create Multipart"); - write_body(&mut multipart) - .expect("Failed to write multipart body"); + write_body(&mut multipart).expect("Failed to write multipart body"); let mut response = multipart.send().expect("Failed to send multipart request"); if !response.status.is_success() { let mut res = String::new(); - response.read_to_string(&mut res).expect("failed to read response"); + response + .read_to_string(&mut res) + .expect("failed to read response"); println!("response reported unsuccessful: {:?}\n {}", response, res); } @@ -39,6 +37,7 @@ fn write_body(multi: &mut Multipart>) -> hyper::Result<()> { multi.write_text("text", "Hello, world!")?; multi.write_file("file", "lorem_ipsum.txt")?; // &[u8] impl Read - multi.write_stream("binary", &mut binary, None, None) + multi + .write_stream("binary", &mut binary, None, None) .and(Ok(())) } diff --git a/examples/hyper_reqbuilder.rs b/examples/hyper_reqbuilder.rs index 05c5539..06c0bbc 100644 --- a/examples/hyper_reqbuilder.rs +++ b/examples/hyper_reqbuilder.rs @@ -16,4 +16,4 @@ fn main() { // Request is sent here .client_request(&Client::new(), "http://localhost:80") .expect("Error sending multipart request"); -} \ No newline at end of file +} diff --git a/examples/hyper_server.rs b/examples/hyper_server.rs index 56ebc8a..3033332 100644 --- a/examples/hyper_server.rs +++ b/examples/hyper_server.rs @@ -1,13 +1,13 @@ extern crate hyper; extern crate multipart; -use std::io; -use hyper::server::{Handler, Server, Request, Response}; -use hyper::status::StatusCode; use hyper::server::response::Response as HyperResponse; -use multipart::server::hyper::{Switch, MultipartHandler, HyperRequest}; -use multipart::server::{Multipart, Entries, SaveResult}; +use hyper::server::{Handler, Request, Response, Server}; +use hyper::status::StatusCode; use multipart::mock::StdoutTee; +use multipart::server::hyper::{HyperRequest, MultipartHandler, Switch}; +use multipart::server::{Entries, Multipart, SaveResult}; +use std::io; struct NonMultipart; impl Handler for NonMultipart { @@ -28,7 +28,8 @@ impl MultipartHandler for EchoMultipart { } SaveResult::Error(error) => { println!("Errors saving multipart:\n{:?}", error); - res.send(format!("An error occurred {}", error).as_bytes()).unwrap(); + res.send(format!("An error occurred {}", error).as_bytes()) + .unwrap(); } }; } @@ -43,9 +44,8 @@ fn process_entries(res: HyperResponse, entries: Entries) -> io::Result<()> { fn main() { println!("Listening on 0.0.0.0:3333"); - Server::http("0.0.0.0:3333").unwrap().handle( - Switch::new( - NonMultipart, - EchoMultipart - )).unwrap(); + Server::http("0.0.0.0:3333") + .unwrap() + .handle(Switch::new(NonMultipart, EchoMultipart)) + .unwrap(); } diff --git a/examples/iron.rs b/examples/iron.rs index effeef6..8e657bf 100644 --- a/examples/iron.rs +++ b/examples/iron.rs @@ -1,21 +1,23 @@ -extern crate multipart; extern crate iron; +extern crate multipart; extern crate env_logger; -use std::io::{self, Write}; -use multipart::mock::StdoutTee; -use multipart::server::{Multipart, Entries, SaveResult}; use iron::prelude::*; use iron::status; +use multipart::mock::StdoutTee; +use multipart::server::{Entries, Multipart, SaveResult}; +use std::io::{self, Write}; fn main() { env_logger::init(); - Iron::new(process_request).http("localhost:80").expect("Could not bind localhost:80"); + Iron::new(process_request) + .http("localhost:80") + .expect("Could not bind localhost:80"); } -/// Processes a request and returns response or an occured error. +/// Processes a request and returns response or an occurred error. fn process_request(request: &mut Request) -> IronResult { // Getting a multipart reader wrapper match Multipart::from_request(request) { @@ -29,18 +31,19 @@ fn process_request(request: &mut Request) -> IronResult { process_entries(entries.keep_partial())?; Ok(Response::with(( status::BadRequest, - format!("error reading request: {}", reason.unwrap_err()) + format!("error reading request: {}", reason.unwrap_err()), ))) } SaveResult::Error(error) => Ok(Response::with(( status::BadRequest, - format!("error reading request: {}", error) + format!("error reading request: {}", error), ))), } } - Err(_) => { - Ok(Response::with((status::BadRequest, "The request is not multipart"))) - } + Err(_) => Ok(Response::with(( + status::BadRequest, + "The request is not multipart", + ))), } } @@ -55,7 +58,7 @@ fn process_entries(entries: Entries) -> IronResult { entries.write_debug(tee).map_err(|e| { IronError::new( e, - (status::InternalServerError, "Error printing request fields") + (status::InternalServerError, "Error printing request fields"), ) })?; } diff --git a/examples/iron_intercept.rs b/examples/iron_intercept.rs index fa328dd..d9f5ba0 100644 --- a/examples/iron_intercept.rs +++ b/examples/iron_intercept.rs @@ -3,18 +3,18 @@ extern crate multipart; use iron::prelude::*; -use multipart::server::Entries; use multipart::server::iron::Intercept; +use multipart::server::Entries; fn main() { // We start with a basic request handler chain. - let mut chain = Chain::new(|req: &mut Request| + let mut chain = Chain::new(|req: &mut Request| { if let Some(entries) = req.extensions.get::() { Ok(Response::with(format!("{:?}", entries))) } else { Ok(Response::with("Not a multipart request")) } - ); + }); // `Intercept` will read out the entries and place them as an extension in the request. // It has various builder-style methods for changing how it will behave, but has sane settings @@ -22,4 +22,4 @@ fn main() { chain.link_before(Intercept::default()); Iron::new(chain).http("localhost:80").unwrap(); -} \ No newline at end of file +} diff --git a/examples/nickel.rs b/examples/nickel.rs index 71ec8ff..b1a5334 100644 --- a/examples/nickel.rs +++ b/examples/nickel.rs @@ -1,35 +1,33 @@ extern crate multipart; extern crate nickel; -use std::io::{self, Write}; -use nickel::{Action, HttpRouter, MiddlewareResult, Nickel, Request, Response}; use nickel::status::StatusCode; +use nickel::{Action, HttpRouter, MiddlewareResult, Nickel, Request, Response}; +use std::io::{self, Write}; +use multipart::mock::StdoutTee; use multipart::server::nickel::MultipartBody; use multipart::server::{Entries, SaveResult}; -use multipart::mock::StdoutTee; fn handle_multipart<'mw>(req: &mut Request, mut res: Response<'mw>) -> MiddlewareResult<'mw> { match (*req).multipart_body() { - Some(mut multipart) => { - match multipart.save().temp() { - SaveResult::Full(entries) => process_entries(res, entries), - - SaveResult::Partial(entries, e) => { - println!("Partial errors ... {:?}", e); - return process_entries(res, entries.keep_partial()); - }, - - SaveResult::Error(e) => { - println!("There are errors in multipart POSTing ... {:?}", e); - res.set(StatusCode::InternalServerError); - return res.send(format!("Server could not handle multipart POST! {:?}", e)); - }, + Some(mut multipart) => match multipart.save().temp() { + SaveResult::Full(entries) => process_entries(res, entries), + + SaveResult::Partial(entries, e) => { + println!("Partial errors ... {:?}", e); + return process_entries(res, entries.keep_partial()); } - } + + SaveResult::Error(e) => { + println!("There are errors in multipart POSTing ... {:?}", e); + res.set(StatusCode::InternalServerError); + return res.send(format!("Server could not handle multipart POST! {:?}", e)); + } + }, None => { res.set(StatusCode::BadRequest); - return res.send("Request seems not was a multipart request") + return res.send("Request seems not was a multipart request"); } } } diff --git a/examples/rocket.rs b/examples/rocket.rs index cf9e6f7..0cdb795 100644 --- a/examples/rocket.rs +++ b/examples/rocket.rs @@ -10,39 +10,45 @@ extern crate multipart; extern crate rocket; use multipart::mock::StdoutTee; -use multipart::server::Multipart; use multipart::server::save::Entries; use multipart::server::save::SaveResult::*; +use multipart::server::Multipart; -use rocket::Data; use rocket::http::{ContentType, Status}; -use rocket::response::Stream; use rocket::response::status::Custom; +use rocket::response::Stream; +use rocket::Data; use std::io::{self, Cursor, Write}; #[post("/upload", data = "")] // signature requires the request to have a `Content-Type` -fn multipart_upload(cont_type: &ContentType, data: Data) -> Result>>, Custom> { +fn multipart_upload( + cont_type: &ContentType, + data: Data, +) -> Result>>, Custom> { // this and the next check can be implemented as a request guard but it seems like just // more boilerplate than necessary if !cont_type.is_form_data() { return Err(Custom( Status::BadRequest, - "Content-Type not multipart/form-data".into() + "Content-Type not multipart/form-data".into(), )); } - let (_, boundary) = cont_type.params().find(|&(k, _)| k == "boundary").ok_or_else( - || Custom( + let (_, boundary) = cont_type + .params() + .find(|&(k, _)| k == "boundary") + .ok_or_else(|| { + Custom( Status::BadRequest, - "`Content-Type: multipart/form-data` boundary param not provided".into() + "`Content-Type: multipart/form-data` boundary param not provided".into(), ) - )?; + })?; match process_upload(boundary, data) { Ok(resp) => Ok(Stream::from(Cursor::new(resp))), - Err(err) => Err(Custom(Status::InternalServerError, err.to_string())) + Err(err) => Err(Custom(Status::InternalServerError, err.to_string())), } } @@ -61,7 +67,7 @@ fn process_upload(boundary: &str, data: Data) -> io::Result> { } process_entries(partial.entries, &mut out)? - }, + } Error(e) => return Err(e), } @@ -81,5 +87,7 @@ fn process_entries(entries: Entries, mut out: &mut Vec) -> io::Result<()> { } fn main() { - rocket::ignite().mount("/", routes![multipart_upload]).launch(); + rocket::ignite() + .mount("/", routes![multipart_upload]) + .launch(); } diff --git a/examples/tiny_http.rs b/examples/tiny_http.rs index caef109..44dabb0 100644 --- a/examples/tiny_http.rs +++ b/examples/tiny_http.rs @@ -1,10 +1,10 @@ -extern crate tiny_http; extern crate multipart; +extern crate tiny_http; -use std::io::{self, Cursor, Write}; -use multipart::server::{Multipart, Entries, SaveResult}; use multipart::mock::StdoutTee; -use tiny_http::{Response, StatusCode, Request}; +use multipart::server::{Entries, Multipart, SaveResult}; +use std::io::{self, Cursor, Write}; +use tiny_http::{Request, Response, StatusCode}; fn main() { // Starting a server on `localhost:80` let server = tiny_http::Server::http("localhost:80").expect("Could not bind localhost:80"); @@ -12,13 +12,16 @@ fn main() { // This blocks until the next request is received let mut request = server.recv().unwrap(); - // Processes a request and returns response or an occured error + // Processes a request and returns response or an occurred error let result = process_request(&mut request); let resp = match result { Ok(resp) => resp, Err(e) => { - println!("An error has occured during request proccessing: {:?}", e); - build_response(500, "The received data was not correctly proccessed on the server") + println!("An error has occurred during request processing: {:?}", e); + build_response( + 500, + "The received data was not correctly processed on the server", + ) } }; @@ -29,7 +32,7 @@ fn main() { type RespBody = Cursor>; -/// Processes a request and returns response or an occured error. +/// Processes a request and returns response or an occurred error. fn process_request(request: &mut Request) -> io::Result> { // Getting a multipart reader wrapper match Multipart::from_request(request) { @@ -70,9 +73,11 @@ fn process_entries(entries: Entries) -> io::Result> { fn build_response>>(status_code: u16, data: D) -> Response { let data = data.into(); let data_len = data.len(); - Response::new(StatusCode(status_code), - vec![], - Cursor::new(data), - Some(data_len), - None) + Response::new( + StatusCode(status_code), + vec![], + Cursor::new(data), + Some(data_len), + None, + ) } diff --git a/src/bin/form_test.rs b/src/bin/form_test.rs index 8cf1b94..f039e81 100644 --- a/src/bin/form_test.rs +++ b/src/bin/form_test.rs @@ -10,8 +10,10 @@ use std::fs::File; use std::io; fn main() { - let listening = Server::http("127.0.0.1:0").expect("failed to bind socket") - .handle(read_multipart).expect("failed to handle request"); + let listening = Server::http("127.0.0.1:0") + .expect("failed to bind socket") + .handle(read_multipart) + .expect("failed to handle request"); println!("bound socket to: {}", listening.socket); } @@ -23,10 +25,11 @@ fn read_multipart(req: Request, mut resp: Response) { } } - let mut file = File::open("src/bin/test_form.html") - .expect("failed to open src/bind/test_form.html"); + let mut file = + File::open("src/bin/test_form.html").expect("failed to open src/bind/test_form.html"); - resp.headers_mut().set(ContentType("text/html".parse().unwrap())); + resp.headers_mut() + .set(ContentType("text/html".parse().unwrap())); let mut resp = resp.start().expect("failed to open response"); io::copy(&mut file, &mut resp).expect("failed to write response"); diff --git a/src/client/hyper.rs b/src/client/hyper.rs index 675cba6..0d30be2 100644 --- a/src/client/hyper.rs +++ b/src/client/hyper.rs @@ -4,7 +4,7 @@ // http://apache.org/licenses/LICENSE-2.0> or the MIT license , at your option. This file may not be // copied, modified, or distributed except according to those terms. -//! Client-side integration with [Hyper](https://github.com/hyperium/hyper). +//! Client-side integration with [Hyper](https://github.com/hyperium/hyper). //! Enabled with the `hyper` feature (on by default). //! //! Contains `impl HttpRequest for Request` and `impl HttpStream for Request`. @@ -14,13 +14,13 @@ //! (adaptors for `hyper::client::RequestBuilder`). use hyper::client::request::Request; use hyper::client::response::Response; -use hyper::header::{ContentType, ContentLength}; +use hyper::header::{ContentLength, ContentType}; use hyper::method::Method; use hyper::net::{Fresh, Streaming}; use hyper::Error as HyperError; -use hyper::mime::{Mime, TopLevel, SubLevel, Attr, Value}; +use hyper::mime::{Attr, Mime, SubLevel, TopLevel, Value}; use super::{HttpRequest, HttpStream}; @@ -46,10 +46,10 @@ impl HttpRequest for Request { headers.set(ContentType(multipart_mime(boundary))); if let Some(size) = content_len { - headers.set(ContentLength(size)); + headers.set(ContentLength(size)); } - debug!("Hyper headers: {}", headers); + debug!("Hyper headers: {}", headers); true } @@ -57,7 +57,7 @@ impl HttpRequest for Request { fn open_stream(self) -> Result { self.start() } -} +} /// #### Feature: `hyper` impl HttpStream for Request { @@ -77,7 +77,8 @@ pub fn content_type(bound: &str) -> ContentType { fn multipart_mime(bound: &str) -> Mime { Mime( - TopLevel::Multipart, SubLevel::Ext("form-data".into()), - vec![(Attr::Ext("boundary".into()), Value::Ext(bound.into()))] - ) + TopLevel::Multipart, + SubLevel::Ext("form-data".into()), + vec![(Attr::Ext("boundary".into()), Value::Ext(bound.into()))], + ) } diff --git a/src/client/sized.rs b/src/client/sized.rs index 75a3945..278f29f 100644 --- a/src/client/sized.rs +++ b/src/client/sized.rs @@ -14,16 +14,16 @@ use std::io::prelude::*; /// A wrapper around a request object that measures the request body and sets the `Content-Length` /// header to its size in bytes. /// -/// Sized requests are more human-readable and use less bandwidth +/// Sized requests are more human-readable and use less bandwidth /// (as chunking adds [visual noise and overhead][chunked-example]), /// but they must be able to load their entirety, including the contents of all files /// and streams, into memory so the request body can be measured. /// /// You should really only use sized requests if you intend to inspect the data manually on the /// server side, as it will produce a more human-readable request body. Also, of course, if the -/// server doesn't support chunked requests or otherwise rejects them. +/// server doesn't support chunked requests or otherwise rejects them. /// -/// [chunked-example]: http://en.wikipedia.org/wiki/Chunked_transfer_encoding#Example +/// [chunked-example]: http://en.wikipedia.org/wiki/Chunked_transfer_encoding#Example pub struct SizedRequest { inner: R, buffer: Vec, @@ -46,11 +46,15 @@ impl Write for SizedRequest { self.buffer.write(data) } - fn flush(&mut self) -> io::Result<()> { Ok(()) } + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } } -impl HttpRequest for SizedRequest -where ::Error: From { +impl HttpRequest for SizedRequest +where + ::Error: From, +{ type Stream = Self; type Error = R::Error; @@ -67,20 +71,23 @@ where ::Error: From { } } -impl HttpStream for SizedRequest -where ::Error: From { +impl HttpStream for SizedRequest +where + ::Error: From, +{ type Request = Self; type Response = <::Stream as HttpStream>::Response; type Error = <::Stream as HttpStream>::Error; fn finish(mut self) -> Result { let content_len = self.buffer.len() as u64; - + if !self.inner.apply_headers(&self.boundary, Some(content_len)) { return Err(io::Error::new( - io::ErrorKind::Other, - "SizedRequest failed to apply headers to wrapped request." - ).into()); + io::ErrorKind::Other, + "SizedRequest failed to apply headers to wrapped request.", + ) + .into()); } let mut req = self.inner.open_stream()?; diff --git a/src/mock.rs b/src/mock.rs index 2947661..4be7dc6 100644 --- a/src/mock.rs +++ b/src/mock.rs @@ -5,11 +5,11 @@ // http://opensource.org/licenses/MIT>, at your option. This file may not be // copied, modified, or distributed except according to those terms. //! Mocked types for client-side and server-side APIs. -use std::io::{self, Read, Write}; use std::fmt; +use std::io::{self, Read, Write}; -use rand::{self, Rng}; use rand::prelude::ThreadRng; +use rand::{self, Rng}; /// A mock implementation of `client::HttpRequest` which can spawn an `HttpBuffer`. /// @@ -35,13 +35,14 @@ impl ::client::HttpRequest for ClientRequest { /// If `apply_headers()` was not called. fn open_stream(self) -> Result { debug!("ClientRequest::open_stream called! {:?}", self); - let boundary = self.boundary.expect("ClientRequest::set_headers() was not called!"); + let boundary = self + .boundary + .expect("ClientRequest::set_headers() was not called!"); Ok(HttpBuffer::new_empty(boundary, self.content_len)) } } - /// A writable buffer which stores the boundary and content-length, if provided. /// /// Implements `client::HttpStream` if the `client` feature is enabled. @@ -67,7 +68,7 @@ impl HttpBuffer { buf, boundary, content_len, - rng: rand::thread_rng() + rng: rand::thread_rng(), } } @@ -109,7 +110,9 @@ impl ::client::HttpStream for HttpBuffer { type Error = io::Error; /// Returns `Ok(self)`. - fn finish(self) -> Result { Ok(self) } + fn finish(self) -> Result { + Ok(self) + } } impl fmt::Debug for HttpBuffer { @@ -168,7 +171,9 @@ impl<'a> Read for ServerRequest<'a> { impl<'a> ::server::HttpRequest for ServerRequest<'a> { type Body = Self; - fn multipart_boundary(&self) -> Option<&str> { Some(self.boundary) } + fn multipart_boundary(&self) -> Option<&str> { + Some(self.boundary) + } fn body(self) -> Self::Body { self @@ -185,7 +190,8 @@ impl<'s, W> StdoutTee<'s, W> { /// Constructor pub fn new(inner: W, stdout: &'s io::Stdout) -> Self { Self { - inner, stdout: stdout.lock(), + inner, + stdout: stdout.lock(), } } } diff --git a/src/server/boundary.rs b/src/server/boundary.rs index 21f37d1..aaeea3f 100644 --- a/src/server/boundary.rs +++ b/src/server/boundary.rs @@ -7,14 +7,14 @@ //! Boundary parsing for `multipart` requests. -use ::safemem; +use safemem; -use super::buf_redux::BufReader; use super::buf_redux::policy::MinBuffered; +use super::buf_redux::BufReader; use super::twoway; -use std::cmp; use std::borrow::Borrow; +use std::cmp; use std::io; use std::io::prelude::*; @@ -27,7 +27,7 @@ pub const MIN_BUF_SIZE: usize = 1024; enum State { Searching, BoundaryRead, - AtEnd + AtEnd, } /// A struct implementing `Read` and `BufRead` that will yield bytes until it sees a given sequence. @@ -39,7 +39,10 @@ pub struct BoundaryReader { state: State, } -impl BoundaryReader where R: Read { +impl BoundaryReader +where + R: Read, +{ /// Internal API pub fn from_reader>>(reader: R, boundary: B) -> BoundaryReader { let mut boundary = boundary.into(); @@ -59,11 +62,15 @@ impl BoundaryReader where R: Read { trace!("Buf: {:?}", String::from_utf8_lossy(buf)); - debug!("Before search Buf len: {} Search idx: {} State: {:?}", - buf.len(), self.search_idx, self.state); + debug!( + "Before search Buf len: {} Search idx: {} State: {:?}", + buf.len(), + self.search_idx, + self.state + ); if self.state == BoundaryRead || self.state == AtEnd { - return Ok(&buf[..self.search_idx]) + return Ok(&buf[..self.search_idx]); } if self.state == Searching && self.search_idx < buf.len() { @@ -74,22 +81,30 @@ impl BoundaryReader where R: Read { Ok(found_idx) => { self.search_idx += found_idx; self.state = BoundaryRead; - }, + } Err(yield_len) => { self.search_idx += yield_len; } } - } - - debug!("After search Buf len: {} Search idx: {} State: {:?}", - buf.len(), self.search_idx, self.state); + } + + debug!( + "After search Buf len: {} Search idx: {} State: {:?}", + buf.len(), + self.search_idx, + self.state + ); // back up the cursor to before the boundary's preceding CRLF if we haven't already if self.search_idx >= 2 && !buf[self.search_idx..].starts_with(b"\r\n") { - let two_bytes_before = &buf[self.search_idx - 2 .. self.search_idx]; + let two_bytes_before = &buf[self.search_idx - 2..self.search_idx]; - trace!("Two bytes before: {:?} ({:?}) (\"\\r\\n\": {:?})", - String::from_utf8_lossy(two_bytes_before), two_bytes_before, b"\r\n"); + trace!( + "Two bytes before: {:?} ({:?}) (\"\\r\\n\": {:?})", + String::from_utf8_lossy(two_bytes_before), + two_bytes_before, + b"\r\n" + ); if two_bytes_before == *b"\r\n" { debug!("Subtract two!"); @@ -122,8 +137,10 @@ impl BoundaryReader where R: Read { let buf_len = self.read_to_boundary()?.len(); if buf_len == 0 && self.state == Searching { - return Err(io::Error::new(io::ErrorKind::UnexpectedEof, - "unexpected end of request body")); + return Err(io::Error::new( + io::ErrorKind::UnexpectedEof, + "unexpected end of request body", + )); } debug!("Discarding {} bytes", buf_len); @@ -145,7 +162,7 @@ impl BoundaryReader where R: Read { consume_amt += 2; // assert that we've found the boundary after the CRLF - debug_assert_eq!(*self.boundary, bnd_segment[2 .. self.boundary.len() + 2]); + debug_assert_eq!(*self.boundary, bnd_segment[2..self.boundary.len() + 2]); } else { // assert that we've found the boundary debug_assert_eq!(*self.boundary, bnd_segment[..self.boundary.len()]); @@ -155,42 +172,51 @@ impl BoundaryReader where R: Read { consume_amt += 2; if buf.len() < consume_amt { - return Err(io::Error::new(io::ErrorKind::UnexpectedEof, - "not enough bytes to verify boundary")); + return Err(io::Error::new( + io::ErrorKind::UnexpectedEof, + "not enough bytes to verify boundary", + )); } // we have enough bytes to verify self.state = Searching; - let last_two = &buf[consume_amt - 2 .. consume_amt]; + let last_two = &buf[consume_amt - 2..consume_amt]; match last_two { b"\r\n" => self.state = Searching, b"--" => self.state = AtEnd, - _ => return Err(io::Error::new( - io::ErrorKind::InvalidData, - format!("unexpected bytes following multipart boundary: {:X} {:X}", - last_two[0], last_two[1]) - )), + _ => { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + format!( + "unexpected bytes following multipart boundary: {:X} {:X}", + last_two[0], last_two[1] + ), + )) + } } consume_amt }; - trace!("Consuming {} bytes, remaining buf: {:?}", - consume_amt, - String::from_utf8_lossy(self.source.buffer())); + trace!( + "Consuming {} bytes, remaining buf: {:?}", + consume_amt, + String::from_utf8_lossy(self.source.buffer()) + ); self.source.consume(consume_amt); - if cfg!(debug_assertions) { - - } + if cfg!(debug_assertions) {} self.search_idx = 0; - trace!("Consumed boundary (state: {:?}), remaining buf: {:?}", self.state, - String::from_utf8_lossy(self.source.buffer())); + trace!( + "Consumed boundary (state: {:?}), remaining buf: {:?}", + self.state, + String::from_utf8_lossy(self.source.buffer()) + ); Ok(self.state != AtEnd) } @@ -205,7 +231,7 @@ fn find_boundary(buf: &[u8], boundary: &[u8]) -> Result { let search_start = buf.len().saturating_sub(boundary.len()); // search for just the boundary fragment - for i in search_start .. buf.len() { + for i in search_start..buf.len() { if boundary.starts_with(&buf[i..]) { return Err(i); } @@ -234,7 +260,10 @@ impl Borrow for BoundaryReader { } } -impl Read for BoundaryReader where R: Read { +impl Read for BoundaryReader +where + R: Read, +{ fn read(&mut self, out: &mut [u8]) -> io::Result { let read = { let mut buf = self.read_to_boundary()?; @@ -247,7 +276,10 @@ impl Read for BoundaryReader where R: Read { } } -impl BufRead for BoundaryReader where R: Read { +impl BufRead for BoundaryReader +where + R: Read, +{ fn fill_buf(&mut self) -> io::Result<&[u8]> { self.read_to_boundary() } @@ -275,7 +307,7 @@ mod test { --boundary\r\n\ dashed-value-2\r\n\ --boundary--"; - + #[test] fn test_boundary() { ::init_log(); @@ -286,7 +318,7 @@ mod test { let mut reader = BoundaryReader::from_reader(src, BOUNDARY); let mut buf = String::new(); - + test_boundary_reader(&mut reader, &mut buf); } @@ -299,7 +331,7 @@ mod test { fn split(data: &'a [u8], at: usize) -> SplitReader<'a> { let (left, right) = data.split_at(at); - SplitReader { + SplitReader { left: left, right: right, } @@ -329,9 +361,9 @@ mod test { debug!("Testing boundary (split)"); let mut buf = String::new(); - + // Substitute for `.step_by()` being unstable. - for split_at in 0 .. TEST_VAL.len(){ + for split_at in 0..TEST_VAL.len() { debug!("Testing split at: {}", split_at); let src = SplitReader::split(TEST_VAL.as_bytes(), split_at); @@ -405,7 +437,6 @@ mod test { let ref mut buf = String::new(); let mut reader = BoundaryReader::from_reader(&mut body, BOUNDARY); - debug!("Consume 1"); assert_eq!(reader.consume_boundary().unwrap(), true); diff --git a/src/server/hyper.rs b/src/server/hyper.rs index 57022dc..2c9f5e2 100644 --- a/src/server/hyper.rs +++ b/src/server/hyper.rs @@ -9,16 +9,16 @@ //! //! Also contains an implementation of [`HttpRequest`](../trait.HttpRequest.html) //! for `hyper::server::Request` and `&mut hyper::server::Request`. -use hyper::net::Fresh; use hyper::header::ContentType; use hyper::method::Method; +use hyper::net::Fresh; use hyper::server::{Handler, Request, Response}; pub use hyper::server::Request as HyperRequest; -use hyper::mime::{Mime, TopLevel, SubLevel, Attr, Value}; +use hyper::mime::{Attr, Mime, SubLevel, TopLevel, Value}; -use super::{Multipart, HttpRequest}; +use super::{HttpRequest, Multipart}; /// A container that implements `hyper::server::Handler` which will switch /// the handler implementation depending on if the incoming request is multipart or not. @@ -32,7 +32,11 @@ pub struct Switch { multipart: M, } -impl Switch where H: Handler, M: MultipartHandler { +impl Switch +where + H: Handler, + M: MultipartHandler, +{ /// Create a new `Switch` instance where /// `normal` handles normal Hyper requests and `multipart` handles Multipart requests pub fn new(normal: H, multipart: M) -> Switch { @@ -40,7 +44,11 @@ impl Switch where H: Handler, M: MultipartHandler { } } -impl Handler for Switch where H: Handler, M: MultipartHandler { +impl Handler for Switch +where + H: Handler, + M: MultipartHandler, +{ fn handle<'a, 'k>(&'a self, req: Request<'a, 'k>, res: Response<'a, Fresh>) { match Multipart::from_request(req) { Ok(multi) => self.multipart.handle_multipart(multi, res), @@ -55,16 +63,23 @@ impl Handler for Switch where H: Handler, M: MultipartHandler { /// and subsequently static functions. pub trait MultipartHandler: Send + Sync { /// Generate a response from this multipart request. - fn handle_multipart<'a, 'k>(&self, - multipart: Multipart>, - response: Response<'a, Fresh>); + fn handle_multipart<'a, 'k>( + &self, + multipart: Multipart>, + response: Response<'a, Fresh>, + ); } -impl MultipartHandler for F -where F: Fn(Multipart, Response), F: Send + Sync { - fn handle_multipart<'a, 'k>(&self, - multipart: Multipart>, - response: Response<'a, Fresh>) { +impl MultipartHandler for F +where + F: Fn(Multipart, Response), + F: Send + Sync, +{ + fn handle_multipart<'a, 'k>( + &self, + multipart: Multipart>, + response: Response<'a, Fresh>, + ) { (*self)(multipart, response); } } @@ -84,17 +99,16 @@ impl<'a, 'b> HttpRequest for HyperRequest<'a, 'b> { _ => return None, }; - params.iter().find(|&&(ref name, _)| - match *name { + params + .iter() + .find(|&&(ref name, _)| match *name { Attr::Boundary => true, _ => false, - } - ).and_then(|&(_, ref val)| - match *val { + }) + .and_then(|&(_, ref val)| match *val { Value::Ext(ref val) => Some(&**val), _ => None, - } - ) + }) }) } @@ -118,17 +132,16 @@ impl<'r, 'a, 'b> HttpRequest for &'r mut HyperRequest<'a, 'b> { _ => return None, }; - params.iter().find(|&&(ref name, _)| - match *name { + params + .iter() + .find(|&&(ref name, _)| match *name { Attr::Boundary => true, _ => false, - } - ).and_then(|&(_, ref val)| - match *val { + }) + .and_then(|&(_, ref val)| match *val { Value::Ext(ref val) => Some(&**val), _ => None, - } - ) + }) }) } @@ -136,4 +149,3 @@ impl<'r, 'a, 'b> HttpRequest for &'r mut HyperRequest<'a, 'b> { self } } - diff --git a/src/server/iron.rs b/src/server/iron.rs index c4bef57..cd93157 100644 --- a/src/server/iron.rs +++ b/src/server/iron.rs @@ -4,7 +4,7 @@ //! iron::Request`. use iron::headers::ContentType; -use iron::mime::{Mime, TopLevel, SubLevel}; +use iron::mime::{Mime, SubLevel, TopLevel}; use iron::request::{Body as IronBody, Request as IronRequest}; use iron::typemap::Key; use iron::{BeforeMiddleware, IronError, IronResult}; @@ -13,9 +13,9 @@ use std::path::PathBuf; use std::{error, fmt, io}; use tempfile; -use super::{FieldHeaders, HttpRequest, Multipart}; -use super::save::{Entries, PartialReason, TempDir}; use super::save::SaveResult::*; +use super::save::{Entries, PartialReason, TempDir}; +use super::{FieldHeaders, HttpRequest, Multipart}; impl<'r, 'a, 'b> HttpRequest for &'r mut IronRequest<'a, 'b> { type Body = &'r mut IronBody<'a, 'b>; @@ -101,22 +101,34 @@ pub struct Intercept { impl Intercept { /// Set the `temp_dir_path` for this middleware. pub fn temp_dir_path>(self, path: P) -> Self { - Intercept { temp_dir_path: Some(path.into()), .. self } + Intercept { + temp_dir_path: Some(path.into()), + ..self + } } /// Set the `file_size_limit` for this middleware. pub fn file_size_limit(self, limit: u64) -> Self { - Intercept { file_size_limit: limit, .. self } + Intercept { + file_size_limit: limit, + ..self + } } /// Set the `file_count_limit` for this middleware. pub fn file_count_limit(self, limit: u32) -> Self { - Intercept { file_count_limit: limit, .. self } + Intercept { + file_count_limit: limit, + ..self + } } /// Set the `limit_behavior` for this middleware. pub fn limit_behavior(self, behavior: LimitBehavior) -> Self { - Intercept { limit_behavior: behavior, .. self } + Intercept { + limit_behavior: behavior, + ..self + } } fn read_request(&self, req: &mut IronRequest) -> IronResult> { @@ -125,12 +137,18 @@ impl Intercept { Err(_) => return Ok(None), }; - let tempdir = self.temp_dir_path.as_ref() - .map_or_else( - || tempfile::Builder::new().prefix("multipart-iron").tempdir(), - |path| tempfile::Builder::new().prefix("multipart-iron").tempdir_in(path) - ) - .map_err(|e| io_to_iron(e, "Error opening temporary directory for request."))?; + let tempdir = self + .temp_dir_path + .as_ref() + .map_or_else( + || tempfile::Builder::new().prefix("multipart-iron").tempdir(), + |path| { + tempfile::Builder::new() + .prefix("multipart-iron") + .tempdir_in(path) + }, + ) + .map_err(|e| io_to_iron(e, "Error opening temporary directory for request."))?; match self.limit_behavior { LimitBehavior::ThrowError => self.read_request_strict(multipart, tempdir), @@ -138,42 +156,66 @@ impl Intercept { } } - fn read_request_strict(&self, mut multipart: IronMultipart, tempdir: TempDir) -> IronResult> { - match multipart.save().size_limit(self.file_size_limit) - .count_limit(self.file_count_limit) - .with_temp_dir(tempdir) { + fn read_request_strict( + &self, + mut multipart: IronMultipart, + tempdir: TempDir, + ) -> IronResult> { + match multipart + .save() + .size_limit(self.file_size_limit) + .count_limit(self.file_count_limit) + .with_temp_dir(tempdir) + { Full(entries) => Ok(Some(entries)), Partial(_, PartialReason::Utf8Error(_)) => unreachable!(), - Partial(_, PartialReason::IoError(err)) => Err(io_to_iron(err, "Error midway through request")), - Partial(_, PartialReason::CountLimit) => Err(FileCountLimitError(self.file_count_limit).into()), - Partial(partial, PartialReason::SizeLimit) => { + Partial(_, PartialReason::IoError(err)) => { + Err(io_to_iron(err, "Error midway through request")) + } + Partial(_, PartialReason::CountLimit) => { + Err(FileCountLimitError(self.file_count_limit).into()) + } + Partial(partial, PartialReason::SizeLimit) => { let partial = partial.partial.expect(EXPECT_PARTIAL_FILE); - Err( - FileSizeLimitError { - field: partial.source.headers, - }.into() - ) - }, + Err(FileSizeLimitError { + field: partial.source.headers, + } + .into()) + } Error(err) => Err(io_to_iron(err, "Error at start of request")), } } - fn read_request_lenient(&self, mut multipart: IronMultipart, tempdir: TempDir) -> IronResult> { - let mut entries = match multipart.save().size_limit(self.file_size_limit) - .count_limit(self.file_count_limit) - .with_temp_dir(tempdir) { + fn read_request_lenient( + &self, + mut multipart: IronMultipart, + tempdir: TempDir, + ) -> IronResult> { + let mut entries = match multipart + .save() + .size_limit(self.file_size_limit) + .count_limit(self.file_count_limit) + .with_temp_dir(tempdir) + { Full(entries) => return Ok(Some(entries)), - Partial(_, PartialReason::IoError(err)) => return Err(io_to_iron(err, "Error midway through request")), - Partial(partial, _) => partial.keep_partial(), + Partial(_, PartialReason::IoError(err)) => { + return Err(io_to_iron(err, "Error midway through request")) + } + Partial(partial, _) => partial.keep_partial(), Error(err) => return Err(io_to_iron(err, "Error at start of request")), }; loop { - entries = match multipart.save().size_limit(self.file_size_limit) - .count_limit(self.file_count_limit) - .with_entries(entries) { + entries = match multipart + .save() + .size_limit(self.file_size_limit) + .count_limit(self.file_count_limit) + .with_entries(entries) + { Full(entries) => return Ok(Some(entries)), - Partial(_, PartialReason::IoError(err)) => return Err(io_to_iron(err, "Error midway through request")), + Partial(_, PartialReason::IoError(err)) => { + return Err(io_to_iron(err, "Error midway through request")) + } Partial(partial, _) => partial.keep_partial(), Error(err) => return Err(io_to_iron(err, "Error at start of request")), }; @@ -242,8 +284,16 @@ impl error::Error for FileSizeLimitError { impl fmt::Display for FileSizeLimitError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self.field.filename { - Some(ref filename) => write!(f, "File size limit reached for field \"{}\" (filename: \"{}\")", self.field.name, filename), - None => write!(f, "File size limit reached for field \"{}\" (no filename)", self.field.name), + Some(ref filename) => write!( + f, + "File size limit reached for field \"{}\" (filename: \"{}\")", + self.field.name, filename + ), + None => write!( + f, + "File size limit reached for field \"{}\" (no filename)", + self.field.name + ), } } } diff --git a/src/server/mod.rs b/src/server/mod.rs index 8cd01a0..7957371 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -16,14 +16,14 @@ extern crate httparse; extern crate twoway; use std::borrow::Borrow; -use std::io::prelude::*; use std::io; +use std::io::prelude::*; use self::boundary::BoundaryReader; use self::field::PrivReadEntry; -pub use self::field::{FieldHeaders, MultipartField, MultipartData, ReadEntry, ReadEntryResult}; +pub use self::field::{FieldHeaders, MultipartData, MultipartField, ReadEntry, ReadEntryResult}; use self::save::SaveBuilder; @@ -48,12 +48,12 @@ macro_rules! try_opt ( ); macro_rules! try_read_entry { - ($self_:expr; $try:expr) => ( + ($self_:expr; $try:expr) => { match $try { Ok(res) => res, Err(err) => return ::server::ReadEntryResult::Error($self_, err), } - ) + }; } mod boundary; @@ -91,8 +91,8 @@ impl Multipart<()> { None => return Err(req), }; - Ok(Multipart::with_body(req.body(), boundary)) - } + Ok(Multipart::with_body(req.body(), boundary)) + } } impl Multipart { @@ -110,7 +110,7 @@ impl Multipart { info!("Multipart::with_boundary(_, {:?})", boundary); - Multipart { + Multipart { reader: BoundaryReader::from_reader(body, boundary), } } @@ -132,12 +132,15 @@ impl Multipart { } /// Call `f` for each entry in the multipart request. - /// + /// /// This is a substitute for Rust not supporting streaming iterators (where the return value /// from `next()` borrows the iterator for a bound lifetime). /// /// Returns `Ok(())` when all fields have been read, or the first error. - pub fn foreach_entry(&mut self, mut foreach: F) -> io::Result<()> where F: FnMut(MultipartField<&mut Self>) { + pub fn foreach_entry(&mut self, mut foreach: F) -> io::Result<()> + where + F: FnMut(MultipartField<&mut Self>), + { loop { match self.read_entry() { Ok(Some(field)) => foreach(field), @@ -216,7 +219,9 @@ fn issue_104() { let request = Cursor::new(body); let mut multipart = Multipart::with_body(request, "boundary"); - multipart.foreach_entry(|_field| {/* Do nothing */}).unwrap_err(); + multipart + .foreach_entry(|_field| { /* Do nothing */ }) + .unwrap_err(); } #[test] @@ -226,7 +231,9 @@ fn issue_114() { fn consume_all(mut rdr: R) { loop { let consume = rdr.fill_buf().unwrap().len(); - if consume == 0 { return; } + if consume == 0 { + return; + } rdr.consume(consume); } } @@ -249,42 +256,51 @@ fn issue_114() { let mut multipart = Multipart::with_body(request, "------------------------c616e5fded96a3c7"); // one error if you do nothing - multipart.foreach_entry(|_entry| { /* do nothing */}).unwrap(); + multipart + .foreach_entry(|_entry| { /* do nothing */ }) + .unwrap(); // a different error if you skip the first field - multipart.foreach_entry(|entry| if *entry.headers.name != *"key1" { consume_all(entry.data); }) + multipart + .foreach_entry(|entry| { + if *entry.headers.name != *"key1" { + consume_all(entry.data); + } + }) .unwrap(); - - multipart.foreach_entry(|_entry| () /* match entry.headers.name.as_str() { - "file" => { - let mut vec = Vec::new(); - entry.data.read_to_end(&mut vec).expect("can't read"); - // message.file = String::from_utf8(vec).ok(); - println!("key file got"); - } - - "key1" => { - let mut vec = Vec::new(); - entry.data.read_to_end(&mut vec).expect("can't read"); - // message.key1 = String::from_utf8(vec).ok(); - println!("key1 got"); - } - - "key2" => { - let mut vec = Vec::new(); - entry.data.read_to_end(&mut vec).expect("can't read"); - // message.key2 = String::from_utf8(vec).ok(); - println!("key2 got"); - } - - _ => { - // as multipart has a bug https://github.com/abonander/multipart/issues/114 - // we manually do read_to_end here - //let mut _vec = Vec::new(); - //entry.data.read_to_end(&mut _vec).expect("can't read"); - println!("key neglected"); - } - }*/) - .expect("Unable to iterate multipart?") + multipart + .foreach_entry( + |_entry| (), /* match entry.headers.name.as_str() { + "file" => { + let mut vec = Vec::new(); + entry.data.read_to_end(&mut vec).expect("can't read"); + // message.file = String::from_utf8(vec).ok(); + println!("key file got"); + } + + "key1" => { + let mut vec = Vec::new(); + entry.data.read_to_end(&mut vec).expect("can't read"); + // message.key1 = String::from_utf8(vec).ok(); + println!("key1 got"); + } + + "key2" => { + let mut vec = Vec::new(); + entry.data.read_to_end(&mut vec).expect("can't read"); + // message.key2 = String::from_utf8(vec).ok(); + println!("key2 got"); + } + + _ => { + // as multipart has a bug https://github.com/abonander/multipart/issues/114 + // we manually do read_to_end here + //let mut _vec = Vec::new(); + //entry.data.read_to_end(&mut _vec).expect("can't read"); + println!("key neglected"); + } + }*/ + ) + .expect("Unable to iterate multipart?") } diff --git a/src/server/nickel.rs b/src/server/nickel.rs index 0f725a3..5968a6c 100644 --- a/src/server/nickel.rs +++ b/src/server/nickel.rs @@ -1,11 +1,11 @@ //! Support for `multipart/form-data` bodies in [Nickel](https://nickel.rs). pub extern crate nickel; -use self::nickel::hyper; use self::hyper::header::ContentType; +use self::nickel::hyper; -pub use self::nickel::Request as NickelRequest; pub use self::nickel::hyper::server::Request as HyperRequest; +pub use self::nickel::Request as NickelRequest; use server::{HttpRequest, Multipart}; @@ -43,27 +43,34 @@ impl<'mw, 'server, D: 'mw> MultipartBody<'mw, 'server> for NickelRequest<'mw, 's } } -impl<'r, 'mw: 'r, 'server: 'mw, D: 'mw> AsRef<&'r mut NickelRequest<'mw, 'server, D>> for Maybe<'r, 'mw, 'server, D> { +impl<'r, 'mw: 'r, 'server: 'mw, D: 'mw> AsRef<&'r mut NickelRequest<'mw, 'server, D>> + for Maybe<'r, 'mw, 'server, D> +{ fn as_ref(&self) -> &&'r mut NickelRequest<'mw, 'server, D> { &self.0 } } -impl<'r, 'mw: 'r, 'server: 'mw, D: 'mw> AsMut<&'r mut NickelRequest<'mw, 'server, D>> for Maybe<'r, 'mw, 'server, D> { +impl<'r, 'mw: 'r, 'server: 'mw, D: 'mw> AsMut<&'r mut NickelRequest<'mw, 'server, D>> + for Maybe<'r, 'mw, 'server, D> +{ fn as_mut(&mut self) -> &mut &'r mut NickelRequest<'mw, 'server, D> { &mut self.0 } } -impl<'r, 'mw: 'r, 'server: 'mw, D: 'mw> Into<&'r mut NickelRequest<'mw, 'server, D>> for Maybe<'r, 'mw, 'server, D> { +impl<'r, 'mw: 'r, 'server: 'mw, D: 'mw> Into<&'r mut NickelRequest<'mw, 'server, D>> + for Maybe<'r, 'mw, 'server, D> +{ fn into(self) -> &'r mut NickelRequest<'mw, 'server, D> { self.0 } } -impl<'r, 'mw: 'r, 'server: 'mw, D: 'mw> From<&'r mut NickelRequest<'mw, 'server, D>> for Maybe<'r, 'mw, 'server, D> { +impl<'r, 'mw: 'r, 'server: 'mw, D: 'mw> From<&'r mut NickelRequest<'mw, 'server, D>> + for Maybe<'r, 'mw, 'server, D> +{ fn from(req: &'r mut NickelRequest<'mw, 'server, D>) -> Self { Maybe(req) } } - diff --git a/src/server/save.rs b/src/server/save.rs index fa95c9e..b88b19a 100644 --- a/src/server/save.rs +++ b/src/server/save.rs @@ -11,18 +11,18 @@ pub use server::buf_redux::BufReader; pub use tempfile::TempDir; use std::collections::HashMap; -use std::io::prelude::*; use std::fs::{self, File, OpenOptions}; +use std::io::prelude::*; use std::path::{Path, PathBuf}; use std::sync::Arc; use std::{cmp, env, io, mem, str, u32, u64}; use tempfile; -use server::field::{FieldHeaders, MultipartField, MultipartData, ReadEntry, ReadEntryResult}; +use server::field::{FieldHeaders, MultipartData, MultipartField, ReadEntry, ReadEntryResult}; +use self::PartialReason::*; use self::SaveResult::*; use self::TextPolicy::*; -use self::PartialReason::*; const RANDOM_FILENAME_LEN: usize = 12; @@ -65,7 +65,7 @@ enum TextPolicy { /// Attempt to read a text field as text, returning any errors Force, /// Don't try to read text - Ignore + Ignore, } /// A builder for saving a file or files to the local filesystem. @@ -131,7 +131,7 @@ enum TextPolicy { /// misinterpreted by the OS. Such functionality is outside the scope of this crate. #[must_use = "nothing saved to the filesystem yet"] pub struct SaveBuilder { - savable: S, + saveable: S, open_opts: OpenOptions, size_limit: u64, count_limit: u32, @@ -143,12 +143,12 @@ pub struct SaveBuilder { impl SaveBuilder { /// Implementation detail but not problematic to have accessible. #[doc(hidden)] - pub fn new(savable: S) -> SaveBuilder { + pub fn new(saveable: S) -> SaveBuilder { let mut open_opts = OpenOptions::new(); open_opts.write(true).create_new(true); SaveBuilder { - savable, + saveable, open_opts, // 8 MiB, on the conservative end compared to most frameworks size_limit: 8 * 1024 * 1024, @@ -185,7 +185,10 @@ impl SaveBuilder { /// If `0`, forces fields to save directly to the filesystem. /// If `u64::MAX`, effectively forces fields to always save to memory. pub fn memory_threshold(self, memory_threshold: u64) -> Self { - Self { memory_threshold, ..self } + Self { + memory_threshold, + ..self + } } /// When encountering a field that is apparently text, try to read it to a string or fall @@ -196,7 +199,10 @@ impl SaveBuilder { /// /// Has no effect once `memory_threshold` has been reached. pub fn try_text(self) -> Self { - Self { text_policy: TextPolicy::Try, ..self } + Self { + text_policy: TextPolicy::Try, + ..self + } } /// When encountering a field that is apparently text, read it to a string or return an error. @@ -206,17 +212,26 @@ impl SaveBuilder { /// /// (RFC: should this continue to validate UTF-8 when writing to the filesystem?) pub fn force_text(self) -> Self { - Self { text_policy: TextPolicy::Force, ..self} + Self { + text_policy: TextPolicy::Force, + ..self + } } /// Don't try to read or validate any field data as UTF-8. pub fn ignore_text(self) -> Self { - Self { text_policy: TextPolicy::Ignore, ..self } + Self { + text_policy: TextPolicy::Ignore, + ..self + } } } /// Save API for whole multipart requests. -impl SaveBuilder where M: ReadEntry { +impl SaveBuilder +where + M: ReadEntry, +{ /// Set the maximum number of fields to process. /// /// Can be `u32` or `Option`. If `None` or `u32::MAX`, clears the limit. @@ -287,11 +302,15 @@ impl SaveBuilder where M: ReadEntry { /// reaches `u32::MAX`, but this would be an extremely degenerate case. pub fn with_entries(self, mut entries: Entries) -> EntriesSaveResult { let SaveBuilder { - savable, open_opts, count_limit, size_limit, - memory_threshold, text_policy + saveable, + open_opts, + count_limit, + size_limit, + memory_threshold, + text_policy, } = self; - let mut res = ReadEntry::read_entry(savable); + let mut res = ReadEntry::read_entry(saveable); let _ = entries.recount_fields(); @@ -299,8 +318,12 @@ impl SaveBuilder where M: ReadEntry { let text_policy = if field.is_text() { text_policy } else { Ignore }; let mut saver = SaveBuilder { - savable: &mut field.data, open_opts: open_opts.clone(), - count_limit, size_limit, memory_threshold, text_policy + saveable: &mut field.data, + open_opts: open_opts.clone(), + count_limit, + size_limit, + memory_threshold, + text_policy, }; saver.with_dir(entries.save_dir.as_path()) @@ -310,13 +333,15 @@ impl SaveBuilder where M: ReadEntry { let mut field: MultipartField = match res { ReadEntryResult::Entry(field) => field, ReadEntryResult::End(_) => return Full(entries), // normal exit point - ReadEntryResult::Error(_, e) => return Partial ( - PartialEntries { - entries, - partial: None, - }, - e.into(), - ) + ReadEntryResult::Error(_, e) => { + return Partial( + PartialEntries { + entries, + partial: None, + }, + e.into(), + ) + } }; let (dest, reason) = match save_field(&mut field, &entries) { @@ -324,7 +349,7 @@ impl SaveBuilder where M: ReadEntry { entries.push_field(field.headers, saved); res = ReadEntry::read_entry(field.data.into_inner()); continue; - }, + } Partial(saved, reason) => (Some(saved), reason), Error(error) => (None, PartialReason::IoError(error)), }; @@ -337,7 +362,7 @@ impl SaveBuilder where M: ReadEntry { dest, }), }, - reason + reason, ); } @@ -346,13 +371,16 @@ impl SaveBuilder where M: ReadEntry { entries, partial: None, }, - PartialReason::CountLimit + PartialReason::CountLimit, ) } } /// Save API for individual fields. -impl<'m, M: 'm> SaveBuilder<&'m mut MultipartData> where MultipartData: BufRead { +impl<'m, M: 'm> SaveBuilder<&'m mut MultipartData> +where + MultipartData: BufRead, +{ /// Save the field data, potentially using a file with a random name in the /// OS temporary directory. /// @@ -407,7 +435,7 @@ impl<'m, M: 'm> SaveBuilder<&'m mut MultipartData> where MultipartData: Bu match reason { SizeLimit if !self.cmp_size_limit(bytes.len()) => (), - other => return Partial(bytes.into(), other) + other => return Partial(bytes.into(), other), } let path = path.into(); @@ -417,15 +445,14 @@ impl<'m, M: 'm> SaveBuilder<&'m mut MultipartData> where MultipartData: Bu Err(e) => return Error(e), }; - let data = try_full!( - try_write_all(&bytes, &mut file) - .map(move |size| SavedData::File(path, size as u64)) - ); + let data = + try_full!(try_write_all(&bytes, &mut file) + .map(move |size| SavedData::File(path, size as u64))); - self.write_to(file).map(move |written| data.add_size(written)) + self.write_to(file) + .map(move |written| data.add_size(written)) } - /// Write out the field data to `dest`, truncating if a limit was set. /// /// Returns the number of bytes copied, and whether or not the limit was reached @@ -434,39 +461,61 @@ impl<'m, M: 'm> SaveBuilder<&'m mut MultipartData> where MultipartData: Bu /// Retries on interrupts. pub fn write_to(&mut self, mut dest: W) -> SaveResult { if self.size_limit < u64::MAX { - try_copy_limited(&mut self.savable, |buf| try_write_all(buf, &mut dest), self.size_limit) + try_copy_limited( + &mut self.saveable, + |buf| try_write_all(buf, &mut dest), + self.size_limit, + ) } else { - try_read_buf(&mut self.savable, |buf| try_write_all(buf, &mut dest)) + try_read_buf(&mut self.saveable, |buf| try_write_all(buf, &mut dest)) } } fn save_mem(&mut self, mut bytes: Vec) -> SaveResult, Vec> { let pre_read = bytes.len() as u64; - match self.read_mem(|buf| { bytes.extend_from_slice(buf); Full(buf.len()) }, pre_read) { + match self.read_mem( + |buf| { + bytes.extend_from_slice(buf); + Full(buf.len()) + }, + pre_read, + ) { Full(_) => Full(bytes), Partial(_, reason) => Partial(bytes, reason), - Error(e) => if !bytes.is_empty() { Partial(bytes, e.into()) } - else { Error(e) } + Error(e) => { + if !bytes.is_empty() { + Partial(bytes, e.into()) + } else { + Error(e) + } + } } - } fn save_text(&mut self) -> SaveResult { let mut string = String::new(); // incrementally validate UTF-8 to do as much work as possible during network activity - let res = self.read_mem(|buf| { - match str::from_utf8(buf) { - Ok(s) => { string.push_str(s); Full(buf.len()) }, - // buffer should always be bigger - Err(e) => if buf.len() < 4 { - Partial(0, e.into()) - } else { - string.push_str(str::from_utf8(&buf[..e.valid_up_to()]).unwrap()); - Full(e.valid_up_to()) + let res = self.read_mem( + |buf| { + match str::from_utf8(buf) { + Ok(s) => { + string.push_str(s); + Full(buf.len()) } - } - }, 0); + // buffer should always be bigger + Err(e) => { + if buf.len() < 4 { + Partial(0, e.into()) + } else { + string.push_str(str::from_utf8(&buf[..e.valid_up_to()]).unwrap()); + Full(e.valid_up_to()) + } + } + } + }, + 0, + ); match res { Full(_) => Full(string), @@ -475,10 +524,13 @@ impl<'m, M: 'm> SaveBuilder<&'m mut MultipartData> where MultipartData: Bu } } - fn read_mem SaveResult>(&mut self, with_buf: Wb, pre_read: u64) -> SaveResult { - let limit = cmp::min(self.size_limit, self.memory_threshold) - .saturating_sub(pre_read); - try_copy_limited(&mut self.savable, with_buf, limit) + fn read_mem SaveResult>( + &mut self, + with_buf: Wb, + pre_read: u64, + ) -> SaveResult { + let limit = cmp::min(self.size_limit, self.memory_threshold).saturating_sub(pre_read); + try_copy_limited(&mut self.saveable, with_buf, limit) } fn cmp_size_limit(&self, size: usize) -> bool { @@ -549,7 +601,7 @@ impl SavedData { match self { File(path, size) => File(path, size.saturating_add(add)), - other => other + other => other, } } } @@ -616,7 +668,7 @@ pub struct Entries { /// Each vector is guaranteed not to be empty unless externally modified. // Even though individual fields might only have one entry, it's better to limit the // size of a value type in `HashMap` to improve cache efficiency in lookups. - pub fields: HashMap, Vec>, + pub fields: HashMap, Vec>, /// The directory that the entries in `fields` were saved into. pub save_dir: SaveDir, fields_count: u32, @@ -660,12 +712,14 @@ impl Entries { use std::collections::hash_map::Entry::*; match self.fields.entry(headers.name.clone()) { - Vacant(vacant) => { vacant.insert(vec![SavedField { headers, data }]); }, + Vacant(vacant) => { + vacant.insert(vec![SavedField { headers, data }]); + } Occupied(occupied) => { // dedup the field name by reusing the key's `Arc` headers.name = occupied.key().clone(); - occupied.into_mut().push({ SavedField { headers, data }}); - }, + occupied.into_mut().push({ SavedField { headers, data } }); + } } self.fields_count = self.fields_count.saturating_add(1); @@ -686,7 +740,11 @@ impl Entries { for (idx, field) in entries.iter().enumerate() { let mut data = field.data.readable()?; let headers = &field.headers; - writeln!(writer, "{}: {:?} ({:?}):", idx, headers.filename, headers.content_type)?; + writeln!( + writer, + "{}: {:?} ({:?}):", + idx, headers.filename, headers.content_type + )?; io::copy(&mut data, &mut writer)?; } } @@ -907,7 +965,10 @@ impl EntriesSaveResult { } } -impl SaveResult where P: Into { +impl SaveResult +where + P: Into, +{ /// Convert `self` to `Option`; there may still have been an error. pub fn okish(self) -> Option { self.into_opt_both().0 @@ -915,7 +976,10 @@ impl SaveResult where P: Into { /// Map the `Full` or `Partial` values to a new type, retaining the reason /// in the `Partial` case. - pub fn map(self, map: Map) -> SaveResult where Map: FnOnce(S) -> T { + pub fn map(self, map: Map) -> SaveResult + where + Map: FnOnce(S) -> T, + { match self { Full(full) => Full(map(full)), Partial(partial, reason) => Partial(map(partial.into()), reason), @@ -926,7 +990,7 @@ impl SaveResult where P: Into { /// Decompose `self` to `(Option, Option)` pub fn into_opt_both(self) -> (Option, Option) { match self { - Full(full) => (Some(full), None), + Full(full) => (Some(full), None), Partial(partial, IoError(e)) => (Some(partial.into()), Some(e)), Partial(partial, _) => (Some(partial.into()), None), Error(error) => (None, Some(error)), @@ -964,23 +1028,35 @@ fn create_dir_all(path: &Path) -> io::Result<()> { fs::create_dir_all(parent) } else { // RFC: return an error instead? - warn!("Attempting to save file in what looks like a root directory. File path: {:?}", path); + warn!( + "Attempting to save file in what looks like a root directory. File path: {:?}", + path + ); Ok(()) } } -fn try_copy_limited SaveResult>(src: R, mut with_buf: Wb, limit: u64) -> SaveResult { +fn try_copy_limited SaveResult>( + src: R, + mut with_buf: Wb, + limit: u64, +) -> SaveResult { let mut copied = 0u64; try_read_buf(src, |buf| { let new_copied = copied.saturating_add(buf.len() as u64); - if new_copied > limit { return Partial(0, PartialReason::SizeLimit) } + if new_copied > limit { + return Partial(0, PartialReason::SizeLimit); + } copied = new_copied; with_buf(buf) }) } -fn try_read_buf SaveResult>(mut src: R, mut with_buf: Wb) -> SaveResult { +fn try_read_buf SaveResult>( + mut src: R, + mut with_buf: Wb, +) -> SaveResult { let mut total_copied = 0u64; macro_rules! try_here ( @@ -997,16 +1073,22 @@ fn try_read_buf SaveResult>(mut sr loop { let res = { let buf = try_here!(src.fill_buf()); - if buf.is_empty() { break; } + if buf.is_empty() { + break; + } with_buf(buf) }; match res { - Full(copied) => { src.consume(copied); total_copied += copied as u64; } + Full(copied) => { + src.consume(copied); + total_copied += copied as u64; + } Partial(copied, reason) => { - src.consume(copied); total_copied += copied as u64; + src.consume(copied); + total_copied += copied as u64; return Partial(total_copied, reason); - }, + } Error(err) => { return Partial(total_copied, err.into()); } @@ -1032,12 +1114,14 @@ fn try_write_all(mut buf: &[u8], mut dest: W) -> SaveResult try_here!(Err(io::Error::new(io::ErrorKind::WriteZero, - "failed to write whole buffer"))), + 0 => try_here!(Err(io::Error::new( + io::ErrorKind::WriteZero, + "failed to write whole buffer" + ))), copied => { buf = &buf[copied..]; total_copied += copied; - }, + } } }