extern crate httparse;
extern crate twoway;
use futures::{Poll, Stream};
use futures::task::{self, Task};
use mime::Mime;
use tempdir::TempDir;
use std::borrow::Borrow;
use std::cell::Cell;
use std::collections::VecDeque;
use std::fs::{self, File};
use std::io::prelude::*;
use std::path::{Path, PathBuf};
use std::rc::Rc;
use std::str::Utf8Error;
use std::{fmt, io, mem, ptr};
use self::boundary::BoundaryFinder;
macro_rules! try_opt (
($expr:expr) => (
match $expr {
Some(val) => val,
None => return None,
}
)
);
macro_rules! ret_err (
($string:expr) => (
return ::helpers::error($string);
);
($string:expr, $($args:tt)*) => (
return ::helpers::error(format!($string, $($args)*));
);
);
mod boundary;
mod field;
use helpers::*;
use self::field::ReadHeaders;
pub use self::field::{Field, FieldHeaders, FieldData, ReadTextField, TextField};
pub struct Multipart<S: Stream> {
internal: Rc<Internal<S>>,
read_hdr: ReadHeaders
}
impl<S: Stream> Multipart<S> where S::Item: BodyChunk, S::Error: StreamError {
pub fn with_body<B: Into<String>>(stream: S, boundary: B) -> Self {
let mut boundary = boundary.into();
boundary.insert_str(0, "--");
debug!("Boundary: {}", boundary);
Multipart {
internal: Rc::new(Internal::new(stream, boundary)),
read_hdr: ReadHeaders::default(),
}
}
}
impl<S: Stream> Stream for Multipart<S> where S::Item: BodyChunk, S::Error: StreamError {
type Item = Field<S>;
type Error = S::Error;
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
if Rc::get_mut(&mut self.internal).is_none() {
self.internal.park_curr_task();
return not_ready();
}
let headers = {
let stream = Rc::get_mut(&mut self.internal).unwrap().stream.get_mut();
match try_ready!(self.read_hdr.read_headers(stream)) {
Some(headers) => headers,
None => return ready(None),
}
};
ready(field::new_field(headers, self.internal.clone()))
}
}
struct Internal<S: Stream> {
stream: Cell<BoundaryFinder<S>>,
waiting_task: Cell<Option<Task>>,
}
impl<S: Stream> Internal<S> {
fn new(stream: S, boundary: String) -> Self {
debug_assert!(boundary.starts_with("--"), "Boundary must start with --");
Internal {
stream: BoundaryFinder::new(stream, boundary).into(),
waiting_task: None.into(),
}
}
fn park_curr_task(&self) {
self.waiting_task.set(Some(task::current()));
}
fn notify_task(&self) {
self.waiting_task.take().map(|t| t.notify());
}
}
pub trait BodyChunk: Sized {
fn split_at(self, idx: usize) -> (Self, Self);
fn as_slice(&self) -> &[u8];
#[inline(always)]
fn len(&self) -> usize {
self.as_slice().len()
}
#[inline(always)]
fn is_empty(&self) -> bool {
self.as_slice().is_empty()
}
#[inline(always)]
fn into_vec(self) -> Vec<u8> {
self.as_slice().to_owned()
}
}
impl BodyChunk for Vec<u8> {
fn split_at(mut self, idx: usize) -> (Self, Self) {
let other = self.split_off(idx);
(self, other)
}
fn as_slice(&self) -> &[u8] {
self
}
fn into_vec(self) -> Vec<u8> { self }
}
impl<'a> BodyChunk for &'a [u8] {
fn split_at(self, idx: usize) -> (Self, Self) {
self.split_at(idx)
}
fn as_slice(&self) -> &[u8] {
self
}
}
pub trait StreamError: From<io::Error> {
fn from_str(str: &'static str) -> Self {
io::Error::new(io::ErrorKind::InvalidData, str).into()
}
fn from_string(string: String) -> Self {
io::Error::new(io::ErrorKind::InvalidData, string).into()
}
fn from_utf8(err: Utf8Error) -> Self {
io::Error::new(io::ErrorKind::InvalidData, err).into()
}
}
impl<E> StreamError for E where E: From<io::Error> {}