use futures::{Future, Stream};
use futures::Async::*;
use std::rc::Rc;
use std::str::Utf8Error;
use std::{fmt, str};
use server::boundary::BoundaryFinder;
use server::{Internal, BodyChunk, StreamError};
use super::{FieldHeaders, FieldData};
use helpers::*;
#[derive(Clone, Debug)]
pub struct TextField {
pub headers: Rc<FieldHeaders>,
pub text: String,
}
#[derive(Default)]
pub struct ReadTextField<S: Stream> {
data: Option<FieldData<S>>,
accum: String,
pub headers: Rc<FieldHeaders>,
pub limit: usize,
}
const DEFAULT_LIMIT: usize = 65536; const MAX_LIMIT: usize = 16_777_216;
pub fn read_text<S: Stream>(data: FieldData<S>) -> ReadTextField<S> {
ReadTextField {
headers: data.headers.clone(), data: Some(data), limit: DEFAULT_LIMIT, accum: String::new()
}
}
impl<S: Stream> ReadTextField<S> {
pub fn limit(self, limit: usize) -> Self {
Self { limit, .. self}
}
pub fn limit_max(self) -> Self {
self.limit(MAX_LIMIT)
}
pub fn take_string(&mut self) -> String {
replace_default(&mut self.accum)
}
pub fn ref_text(&self) -> &str {
&self.accum
}
pub fn into_data(self) -> Option<FieldData<S>> {
self.data
}
}
impl<S: Stream> Future for ReadTextField<S> where S::Item: BodyChunk, S::Error: StreamError {
type Item = TextField;
type Error = S::Error;
fn poll(&mut self) -> Poll<Self::Item, S::Error> {
loop {
let data = match self.data {
Some(ref mut data) => data,
None => return not_ready(),
};
let mut stream = data.stream_mut();
let chunk = match try_ready!(stream.body_chunk()) {
Some(val) => val,
_ => break,
};
if self.accum.len().saturating_add(chunk.len()) > self.limit {
stream.push_chunk(chunk);
ret_err!("Text field {:?} exceeded limit of {} bytes", self.headers, self.limit);
}
let split_idx = match str::from_utf8(chunk.as_slice()) {
Ok(s) => { self.accum.push_str(s); continue },
Err(e) => if should_continue(&e, chunk.as_slice()) {
e.valid_up_to()
} else {
return utf8_err(e);
},
};
let (valid, invalid) = chunk.split_at(split_idx);
self.accum.push_str(str::from_utf8(valid.as_slice())
.expect("a `StreamChunk` was UTF-8 before, now it's not"));
let needed_len = utf8_char_width(invalid.as_slice()[0]) - invalid.len();
let (first, second) = match try_ready!(stream.another_chunk(invalid)) {
Some(pair) => pair,
None => ret_err!("unexpected end of stream while decoding a UTF-8 sequence"),
};
if second.len() < needed_len {
ret_err!("got a chunk smaller than the {} byte(s) needed to finish \
decoding this UTF-8 sequence: {:?}",
needed_len, first.as_slice());
}
if self.accum.len().saturating_add(first.len()).saturating_add(second.len()) > self.limit {
stream.push_chunk(second);
stream.push_chunk(first);
ret_err!("Text field {:?} exceeded limit of {} bytes", self.headers, self.limit);
}
let mut buf = [0u8; 4];
buf[..first.len()].copy_from_slice(first.as_slice());
buf[first.len()..].copy_from_slice(&second.as_slice()[.. needed_len]);
let split_idx = match str::from_utf8(&buf) {
Ok(s) => { self.accum.push_str(s); needed_len },
Err(e) => if should_continue(&e, &buf) {
e.valid_up_to()
} else {
return utf8_err(e);
}
};
let (_, rem) = second.split_at(split_idx);
if !rem.is_empty() {
stream.push_chunk(rem);
}
}
self.data = None;
ready(TextField {
headers: self.headers.clone(),
text: self.take_string(),
})
}
}
impl<S: Stream> fmt::Debug for ReadTextField<S> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("ReadFieldText")
.field("accum", &self.accum)
.field("headers", &self.headers)
.field("limit", &self.limit)
.finish()
}
}
static UTF8_CHAR_WIDTH: [u8; 256] = [
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, 4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0, ];
#[inline]
fn utf8_char_width(b: u8) -> usize {
return UTF8_CHAR_WIDTH[b as usize] as usize;
}
fn should_continue(err: &Utf8Error, buf: &[u8]) -> bool {
let valid_len = err.valid_up_to();
utf8_char_width(buf[valid_len]) > 1 && (
valid_len + 1 == buf.len() || buf[valid_len + 1 ..].iter().all(|&b| b >= 0x80 && b <= 0xBF)
)
}