Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 49 additions & 9 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -1,16 +1,56 @@
name: Test
name: CI

on:
push:
branches: [master, develop]
pull_request:
branches: [master, develop]
push:
branches:
- master
- develop

env:
RUSTFLAGS: -Dwarnings

jobs:
build:
runs-on: ubuntu-latest
build_and_test:
name: Build and test
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest]
rust: [stable, nightly]

steps:
- uses: actions/checkout@v2
- name: Run tests
run: cargo test --verbose --features="all"
- uses: actions/checkout@master

- name: Install ${{ matrix.rust }}
uses: actions-rs/toolchain@v1
with:
toolchain: ${{ matrix.rust }}
override: true

- name: check
uses: actions-rs/cargo@v1
with:
command: check
args: --all --bins --examples --features=all

- name: tests
uses: actions-rs/cargo@v1
with:
command: test
args: --all --features=all

check_fmt_and_docs:
name: Checking fmt, clippy, and docs
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master

- name: clippy
run: cargo clippy --tests --examples --bins -- -D warnings

- name: fmt
run: cargo fmt --all -- --check

- name: Docs
run: cargo doc --no-deps
7 changes: 1 addition & 6 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,7 @@ tokio = { version = "0.2", features = ["full"] }
hyper = "0.13"
routerify = "1.1"

[[example]]
name = "test"
path = "examples/test.rs"
required-features = ["json", "reader"]

[[example]]
name = "parse_async_read"
path = "examples/parse_async_read.rs"
required-features = ["reader"]
required-features = ["reader"]
42 changes: 0 additions & 42 deletions examples/test.rs

This file was deleted.

17 changes: 11 additions & 6 deletions src/buffer.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::constants;
use bytes::{Bytes, BytesMut};
use futures::stream::Stream;
use std::fmt;
use std::pin::Pin;
use std::task::{Context, Poll};

Expand All @@ -26,7 +27,7 @@ impl StreamBuffer {
}
}

pub fn poll_stream(&mut self, cx: &mut Context) -> Result<(), crate::Error> {
pub fn poll_stream(&mut self, cx: &mut Context<'_>) -> Result<(), crate::Error> {
if self.eof {
return Ok(());
}
Expand Down Expand Up @@ -116,12 +117,10 @@ impl StreamBuffer {
Err(crate::Error::IncompleteFieldData {
field_name: field_name.map(|s| s.to_owned()),
})
} else if bytes.is_empty() {
Ok(None)
} else {
if bytes.is_empty() {
Ok(None)
} else {
Ok(Some((false, bytes)))
}
Ok(Some((false, bytes)))
}
}
None => {
Expand Down Expand Up @@ -153,3 +152,9 @@ impl StreamBuffer {
self.buf.split_to(self.buf.len()).freeze()
}
}

impl fmt::Debug for StreamBuffer {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("StreamBuffer").finish()
}
}
10 changes: 5 additions & 5 deletions src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ pub(crate) const DEFAULT_WHOLE_STREAM_SIZE_LIMIT: u64 = std::u64::MAX;
pub(crate) const DEFAULT_PER_FIELD_SIZE_LIMIT: u64 = std::u64::MAX;

pub(crate) const MAX_HEADERS: usize = 32;
pub(crate) const BOUNDARY_EXT: &'static str = "--";
pub(crate) const CR: &'static str = "\r";
pub(crate) const BOUNDARY_EXT: &str = "--";
pub(crate) const CR: &str = "\r";
#[allow(dead_code)]
pub(crate) const LF: &'static str = "\n";
pub(crate) const CRLF: &'static str = "\r\n";
pub(crate) const CRLF_CRLF: &'static str = "\r\n\r\n";
pub(crate) const LF: &str = "\n";
pub(crate) const CRLF: &str = "\r\n";
pub(crate) const CRLF_CRLF: &str = "\r\n\r\n";

lazy_static! {
pub(crate) static ref CONTENT_DISPOSITION_FIELD_NAME_RE: Regex = Regex::new(r#"(?-u)name="([^"]+)""#).unwrap();
Expand Down
1 change: 1 addition & 0 deletions src/constraints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ use crate::size_limit::SizeLimit;
/// # }
/// # tokio::runtime::Runtime::new().unwrap().block_on(run());
/// ```
#[derive(Debug)]
pub struct Constraints {
pub(crate) size_limit: SizeLimit,
pub(crate) allowed_fields: Option<Vec<String>>,
Expand Down
1 change: 1 addition & 0 deletions src/content_disposition.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::constants;
use http::header::{self, HeaderMap};

#[derive(Debug)]
pub(crate) struct ContentDisposition {
pub(crate) field_name: Option<String>,
pub(crate) file_name: Option<String>,
Expand Down
4 changes: 1 addition & 3 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ type BoxError = Box<dyn std::error::Error + Send + Sync>;
/// A set of errors that can occur during parsing multipart stream and in other operations.
#[derive(Display)]
#[display(fmt = "multer: {}")]
#[non_exhaustive]
pub enum Error {
/// An unknown field is detected when multipart [`constraints`](./struct.Constraints.html#method.allowed_fields) are added.
#[display(
Expand Down Expand Up @@ -77,9 +78,6 @@ pub enum Error {
#[cfg(feature = "json")]
#[display(fmt = "Failed to decode the field data as JSON: {}", _0)]
DecodeJson(BoxError),

#[doc(hidden)]
__Nonexhaustive,
}

impl Debug for Error {
Expand Down
24 changes: 8 additions & 16 deletions src/field.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ use futures::stream::{Stream, TryStreamExt};
use http::header::HeaderMap;
#[cfg(feature = "json")]
use serde::de::DeserializeOwned;
#[cfg(feature = "json")]
use serde_json;
use std::borrow::Cow;
use std::ops::DerefMut;
use std::pin::Pin;
Expand Down Expand Up @@ -50,13 +48,15 @@ use std::task::{Context, Poll};
/// then the parent [`Multipart`](./struct.Multipart.html) will never be able to yield the next field in the stream.
/// The task waiting on the [`Multipart`](./struct.Multipart.html) will also never be notified, which, depending on the executor implementation,
/// may cause a deadlock.
#[derive(Debug)]
pub struct Field {
state: Arc<Mutex<MultipartState>>,
headers: HeaderMap,
done: bool,
meta: FieldMeta,
}

#[derive(Debug)]
struct FieldMeta {
content_disposition: ContentDisposition,
content_type: Option<mime::Mime>,
Expand Down Expand Up @@ -86,20 +86,12 @@ impl Field {

/// The field name found in the [`Content-Disposition`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition) header.
pub fn name(&self) -> Option<&str> {
self.meta
.content_disposition
.field_name
.as_ref()
.map(|name| name.as_str())
self.meta.content_disposition.field_name.as_deref()
}

/// The file name found in the [`Content-Disposition`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition) header.
pub fn file_name(&self) -> Option<&str> {
self.meta
.content_disposition
.file_name
.as_ref()
.map(|file_name| file_name.as_str())
self.meta.content_disposition.file_name.as_deref()
}

/// Get the content type of the field.
Expand Down Expand Up @@ -238,7 +230,7 @@ impl Field {
/// let stream = once(async move { Result::<Bytes, Infallible>::Ok(Bytes::from(data)) });
/// let mut multipart = Multipart::new(stream, "X-BOUNDARY");
///
/// while let Some(mut field) = multipart.next_field().await.unwrap() {
/// while let Some(field) = multipart.next_field().await.unwrap() {
/// let content = field.text().await.unwrap();
/// assert_eq!(content, "abcd");
/// }
Expand Down Expand Up @@ -268,7 +260,7 @@ impl Field {
/// let stream = once(async move { Result::<Bytes, Infallible>::Ok(Bytes::from(data)) });
/// let mut multipart = Multipart::new(stream, "X-BOUNDARY");
///
/// while let Some(mut field) = multipart.next_field().await.unwrap() {
/// while let Some(field) = multipart.next_field().await.unwrap() {
/// let content = field.text_with_charset("utf-8").await.unwrap();
/// assert_eq!(content, "abcd");
/// }
Expand Down Expand Up @@ -324,7 +316,7 @@ impl Field {
impl Stream for Field {
type Item = Result<Bytes, crate::Error>;

fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Option<Self::Item>> {
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
if self.done {
return Poll::Ready(None);
}
Expand Down Expand Up @@ -391,7 +383,7 @@ impl Drop for Field {
state.is_prev_field_consumed = true;

if let Some(waker) = state.next_field_waker.take() {
waker.clone().wake();
waker.wake();
}
}
}
2 changes: 1 addition & 1 deletion src/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use http::header::{self, HeaderMap, HeaderName, HeaderValue};
use httparse::Header;
use std::convert::TryFrom;

pub(crate) fn convert_raw_headers_to_header_map(raw_headers: &[Header]) -> crate::Result<HeaderMap> {
pub(crate) fn convert_raw_headers_to_header_map(raw_headers: &[Header<'_>]) -> crate::Result<HeaderMap> {
let mut headers = HeaderMap::with_capacity(raw_headers.len());

for raw_header in raw_headers {
Expand Down
15 changes: 12 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,17 @@
//!
//! For more examples, please visit [examples](https://github.com/rousan/multer-rs/tree/master/examples).

pub use bytes;
#![forbid(unsafe_code, future_incompatible)]
#![warn(
missing_debug_implementations,
rust_2018_idioms,
trivial_casts,
unused_qualifications
)]
#![doc(test(attr(deny(rust_2018_idioms, warnings))))]
#![doc(test(attr(allow(unused_extern_crates, unused_variables))))]

pub use bytes;
pub use constraints::Constraints;
pub use error::Error;
pub use field::Field;
Expand Down Expand Up @@ -133,7 +142,7 @@ pub type Result<T> = std::result::Result<T, Error>;
/// # }
/// # run();
/// ```
pub fn parse_boundary<T: AsRef<str>>(content_type: T) -> crate::Result<String> {
pub fn parse_boundary<T: AsRef<str>>(content_type: T) -> Result<String> {
let m = content_type
.as_ref()
.parse::<mime::Mime>()
Expand All @@ -145,7 +154,7 @@ pub fn parse_boundary<T: AsRef<str>>(content_type: T) -> crate::Result<String> {

m.get_param(mime::BOUNDARY)
.map(|name| name.as_str().to_owned())
.ok_or_else(|| crate::Error::NoBoundary)
.ok_or(crate::Error::NoBoundary)
}

#[cfg(test)]
Expand Down
Loading