1
0
mirror of https://github.com/fafhrd91/actix-web synced 2024-12-02 19:32:24 +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,29 +313,32 @@ 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
let mut found: Option<usize> = None; if !self.checked_buff {
for (ind, b) in self.buff.iter().enumerate() { let mut found: Option<usize> = None;
if *b == '\n' as u8 { for (ind, b) in self.buff.iter().enumerate() {
found = Some(ind); if *b == '\n' as u8 {
break; found = Some(ind);
break;
}
} }
} if let Some(ind) = found {
if let Some(ind) = found { // check if line is longer than limit
// check if line is longer than limit if ind+1 > self.limit {
if ind+1 > self.limit { return Err(ReadlinesError::LimitOverflow);
return Err(ReadlinesError::LimitOverflow); }
let enc: *const Encoding = encoding as *const Encoding;
let line = if enc == UTF_8 {
str::from_utf8(&self.buff.split_to(ind+1))
.map_err(|_| ReadlinesError::EncodingError)?
.to_owned()
} else {
encoding
.decode(&self.buff.split_to(ind+1), DecoderTrap::Strict)
.map_err(|_| ReadlinesError::EncodingError)?
};
return Ok(Async::Ready(Some(line)));
} }
let enc: *const Encoding = encoding as *const Encoding; self.checked_buff = true;
let line = if enc == UTF_8 {
str::from_utf8(&self.buff.split_to(ind+1))
.map_err(|_| ErrorBadRequest("Can not decode body"))?
.to_owned()
} else {
encoding
.decode(&self.buff.split_to(ind+1), DecoderTrap::Strict)
.map_err(|_| ErrorBadRequest("Can not decode body"))?
};
return Ok(Async::Ready(Some(line)));
} }
// poll req for more bytes // poll req for more bytes
match self.req.poll() { match self.req.poll() {
@ -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"),
}
}
} }