1
0
mirror of https://github.com/fafhrd91/actix-web synced 2024-11-30 18:44:35 +01:00

Add HttpMessage::readlines()

This commit is contained in:
Dursun Akkurt 2018-06-14 00:19:48 +03:00
parent 1bee528018
commit cb77f7e688
3 changed files with 64 additions and 32 deletions

View File

@ -4,6 +4,8 @@
### Added ### Added
* Add `HttpMessage::readlines()` for reading line by line.
* Add `ClientRequestBuilder::form()` for sending `application/x-www-form-urlencoded` requests. * Add `ClientRequestBuilder::form()` for sending `application/x-www-form-urlencoded` requests.
* Add method to configure custom error handler to Form extractor. * Add method to configure custom error handler to Form extractor.

View File

@ -592,9 +592,13 @@ impl From<JsonError> for JsonPayloadError {
/// Error type returned when reading body as lines. /// Error type returned when reading body as lines.
pub enum ReadlinesError { pub enum ReadlinesError {
/// Error when decoding a line.
EncodingError, EncodingError,
/// Payload error.
PayloadError(PayloadError), PayloadError(PayloadError),
/// Line limit exceeded.
LimitOverflow, LimitOverflow,
/// ContentType error.
ContentTypeError(ContentTypeError), ContentTypeError(ContentTypeError),
} }
@ -604,12 +608,6 @@ impl From<PayloadError> for ReadlinesError {
} }
} }
impl From<Error> for ReadlinesError {
fn from(_: Error) -> Self {
ReadlinesError::EncodingError
}
}
impl From<ContentTypeError> for ReadlinesError { impl From<ContentTypeError> for ReadlinesError {
fn from(err: ContentTypeError) -> Self { fn from(err: ContentTypeError) -> Self {
ReadlinesError::ContentTypeError(err) ReadlinesError::ContentTypeError(err)

View File

@ -13,7 +13,7 @@ use std::str;
use error::{ use error::{
ContentTypeError, HttpRangeError, ParseError, PayloadError, UrlencodedError, ContentTypeError, HttpRangeError, ParseError, PayloadError, UrlencodedError,
Error, ErrorBadRequest, ReadlinesError ReadlinesError
}; };
use header::Header; use header::Header;
use json::JsonBody; use json::JsonBody;
@ -279,6 +279,7 @@ where
req: T, req: T,
buff: BytesMut, buff: BytesMut,
limit: usize, limit: usize,
checked_buff: bool,
} }
impl<T> Readlines<T> impl<T> Readlines<T>
@ -291,6 +292,7 @@ where
req, req,
buff: BytesMut::with_capacity(262_144), buff: BytesMut::with_capacity(262_144),
limit: 262_144, limit: 262_144,
checked_buff: true,
} }
} }
@ -311,6 +313,7 @@ where
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> { fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
let encoding = self.req.encoding()?; let encoding = self.req.encoding()?;
// check if there is a newline in the buffer // check if there is a newline in the buffer
if !self.checked_buff {
let mut found: Option<usize> = None; let mut found: Option<usize> = None;
for (ind, b) in self.buff.iter().enumerate() { for (ind, b) in self.buff.iter().enumerate() {
if *b == '\n' as u8 { if *b == '\n' as u8 {
@ -326,15 +329,17 @@ where
let enc: *const Encoding = encoding as *const Encoding; let enc: *const Encoding = encoding as *const Encoding;
let line = if enc == UTF_8 { let line = if enc == UTF_8 {
str::from_utf8(&self.buff.split_to(ind+1)) str::from_utf8(&self.buff.split_to(ind+1))
.map_err(|_| ErrorBadRequest("Can not decode body"))? .map_err(|_| ReadlinesError::EncodingError)?
.to_owned() .to_owned()
} else { } else {
encoding encoding
.decode(&self.buff.split_to(ind+1), DecoderTrap::Strict) .decode(&self.buff.split_to(ind+1), DecoderTrap::Strict)
.map_err(|_| ErrorBadRequest("Can not decode body"))? .map_err(|_| ReadlinesError::EncodingError)?
}; };
return Ok(Async::Ready(Some(line))); return Ok(Async::Ready(Some(line)));
} }
self.checked_buff = true;
}
// poll req for more bytes // poll req for more bytes
match self.req.poll() { match self.req.poll() {
Ok(Async::Ready(Some(mut bytes))) => { Ok(Async::Ready(Some(mut bytes))) => {
@ -354,15 +359,16 @@ where
let enc: *const Encoding = encoding as *const Encoding; let enc: *const Encoding = encoding as *const Encoding;
let line = if enc == UTF_8 { let line = if enc == UTF_8 {
str::from_utf8(&bytes.split_to(ind+1)) str::from_utf8(&bytes.split_to(ind+1))
.map_err(|_| ErrorBadRequest("Can not decode body"))? .map_err(|_| ReadlinesError::EncodingError)?
.to_owned() .to_owned()
} else { } else {
encoding encoding
.decode(&bytes.split_to(ind+1), DecoderTrap::Strict) .decode(&bytes.split_to(ind+1), DecoderTrap::Strict)
.map_err(|_| ErrorBadRequest("Can not decode body"))? .map_err(|_| ReadlinesError::EncodingError)?
}; };
// extend buffer with rest of the bytes; // extend buffer with rest of the bytes;
self.buff.extend_from_slice(&bytes); self.buff.extend_from_slice(&bytes);
self.checked_buff = false;
return Ok(Async::Ready(Some(line))); return Ok(Async::Ready(Some(line)));
} }
self.buff.extend_from_slice(&bytes); self.buff.extend_from_slice(&bytes);
@ -379,12 +385,12 @@ where
let enc: *const Encoding = encoding as *const Encoding; let enc: *const Encoding = encoding as *const Encoding;
let line = if enc == UTF_8 { let line = if enc == UTF_8 {
str::from_utf8(&self.buff) str::from_utf8(&self.buff)
.map_err(|_| ErrorBadRequest("Can not decode body"))? .map_err(|_| ReadlinesError::EncodingError)?
.to_owned() .to_owned()
} else { } else {
encoding encoding
.decode(&self.buff, DecoderTrap::Strict) .decode(&self.buff, DecoderTrap::Strict)
.map_err(|_| ErrorBadRequest("Can not decode body"))? .map_err(|_| ReadlinesError::EncodingError)?
}; };
self.buff.clear(); self.buff.clear();
return Ok(Async::Ready(Some(line))) return Ok(Async::Ready(Some(line)))
@ -799,4 +805,30 @@ mod tests {
_ => unreachable!("error"), _ => unreachable!("error"),
} }
} }
#[test]
fn test_readlines() {
let mut req = HttpRequest::default();
req.payload_mut().unread_data(Bytes::from_static(
b"Lorem Ipsum is simply dummy text of the printing and typesetting\n\
industry. Lorem Ipsum has been the industry's standard dummy\n\
Contrary to popular belief, Lorem Ipsum is not simply random text."
));
let mut r = Readlines::new(req);
match r.poll().ok().unwrap() {
Async::Ready(Some(s)) => assert_eq!(s,
"Lorem Ipsum is simply dummy text of the printing and typesetting\n"),
_ => unreachable!("error"),
}
match r.poll().ok().unwrap() {
Async::Ready(Some(s)) => assert_eq!(s,
"industry. Lorem Ipsum has been the industry's standard dummy\n"),
_ => unreachable!("error"),
}
match r.poll().ok().unwrap() {
Async::Ready(Some(s)) => assert_eq!(s,
"Contrary to popular belief, Lorem Ipsum is not simply random text."),
_ => unreachable!("error"),
}
}
} }