mirror of
https://github.com/actix/actix-web.git
synced 2024-11-26 19:41:12 +00:00
Add HttpMessage::readlines()
This commit is contained in:
parent
1bee528018
commit
cb77f7e688
3 changed files with 64 additions and 32 deletions
|
@ -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.
|
||||||
|
|
10
src/error.rs
10
src/error.rs
|
@ -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)
|
||||||
|
|
|
@ -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"),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue