pub extern crate buf_redux;
extern crate httparse;
extern crate twoway;
use std::borrow::Borrow;
use std::io::prelude::*;
use std::io;
use self::boundary::BoundaryReader;
use self::field::PrivReadEntry;
pub use self::field::{FieldHeaders, MultipartField, MultipartData, ReadEntry, ReadEntryResult};
use self::save::SaveBuilder;
pub use self::save::{Entries, SaveResult, SavedField};
macro_rules! try_opt (
($expr:expr) => (
match $expr {
Some(val) => val,
None => return None,
}
);
($expr:expr, $before_ret:expr) => (
match $expr {
Some(val) => val,
None => {
$before_ret;
return None;
}
}
)
);
macro_rules! try_read_entry {
($self_:expr; $try:expr) => (
match $try {
Ok(res) => res,
Err(err) => return ::server::ReadEntryResult::Error($self_, err),
}
)
}
mod boundary;
mod field;
#[cfg(feature = "hyper")]
pub mod hyper;
#[cfg(feature = "iron")]
pub mod iron;
#[cfg(feature = "tiny_http")]
pub mod tiny_http;
#[cfg(feature = "nickel")]
pub mod nickel;
pub mod save;
pub struct Multipart<R> {
reader: BoundaryReader<R>,
}
impl Multipart<()> {
pub fn from_request<R: HttpRequest>(req: R) -> Result<Multipart<R::Body>, R> {
let boundary = match req.multipart_boundary().map(String::from) {
Some(boundary) => boundary,
None => return Err(req),
};
Ok(Multipart::with_body(req.body(), boundary))
}
}
impl<R: Read> Multipart<R> {
pub fn with_body<Bnd: Into<String>>(body: R, boundary: Bnd) -> Self {
let boundary = boundary.into();
info!("Multipart::with_boundary(_, {:?})", boundary);
Multipart {
reader: BoundaryReader::from_reader(body, boundary),
}
}
pub fn read_entry(&mut self) -> io::Result<Option<MultipartField<&mut Self>>> {
self.read_entry_mut().into_result()
}
pub fn into_entry(self) -> ReadEntryResult<Self> {
self.read_entry()
}
pub fn foreach_entry<F>(&mut self, mut foreach: F) -> io::Result<()> where F: FnMut(MultipartField<&mut Self>) {
loop {
match self.read_entry() {
Ok(Some(field)) => foreach(field),
Ok(None) => return Ok(()),
Err(err) => return Err(err),
}
}
}
pub fn save(&mut self) -> SaveBuilder<&mut Self> {
SaveBuilder::new(self)
}
}
impl<R> Borrow<R> for Multipart<R> {
fn borrow(&self) -> &R {
self.reader.borrow()
}
}
impl<R: Read> PrivReadEntry for Multipart<R> {
type Source = BoundaryReader<R>;
fn source_mut(&mut self) -> &mut BoundaryReader<R> {
&mut self.reader
}
fn set_min_buf_size(&mut self, min_buf_size: usize) {
self.reader.set_min_buf_size(min_buf_size)
}
fn consume_boundary(&mut self) -> io::Result<bool> {
debug!("Consume boundary!");
self.reader.consume_boundary()
}
}
pub trait HttpRequest {
type Body: Read;
fn multipart_boundary(&self) -> Option<&str>;
fn body(self) -> Self::Body;
}
#[test]
fn issue_104() {
::init_log();
use std::io::Cursor;
let body = "\
POST /test.html HTTP/1.1\r\n\
Host: example.org\r\n\
Content-Type: multipart/form-data;boundary=\"boundary\"\r\n\r\n\
Content-Disposition: form-data; name=\"field1\"\r\n\r\n\
value1\r\n\
Content-Disposition: form-data; name=\"field2\"; filename=\"example.txt\"\r\n\r\n\
value2 ";
let request = Cursor::new(body);
let mut multipart = Multipart::with_body(request, "boundary");
multipart.foreach_entry(|_field| {}).unwrap_err();
}
#[test]
fn issue_114() {
::init_log();
fn consume_all<R: BufRead>(mut rdr: R) {
loop {
let consume = rdr.fill_buf().unwrap().len();
if consume == 0 { return; }
rdr.consume(consume);
}
}
use std::io::Cursor;
let body = "\
--------------------------c616e5fded96a3c7\r\n\
Content-Disposition: form-data; name=\"key1\"\r\n\r\n\
v1,\r\n\
--------------------------c616e5fded96a3c7\r\n\
Content-Disposition: form-data; name=\"key2\"\r\n\r\n\
v2,\r\n\
--------------------------c616e5fded96a3c7\r\n\
Content-Disposition: form-data; name=\"key3\"\r\n\r\n\
v3\r\n\
--------------------------c616e5fded96a3c7--\r\n";
let request = Cursor::new(body);
let mut multipart = Multipart::with_body(request, "------------------------c616e5fded96a3c7");
multipart.foreach_entry(|_entry| { }).unwrap();
multipart.foreach_entry(|entry| if *entry.headers.name != *"key1" { consume_all(entry.data); })
.unwrap();
multipart.foreach_entry(|_entry| () )
.expect("Unable to iterate multipart?")
}