1
0
mirror of https://github.com/fafhrd91/actix-web synced 2024-11-24 16:32:59 +01:00

impl response body support

This commit is contained in:
Nikolay Kim 2018-10-08 15:24:51 -07:00
parent 431e33acb2
commit 805e7a4cd0
4 changed files with 427 additions and 91 deletions

View File

@ -6,7 +6,7 @@ use tokio_codec::{Decoder, Encoder};
use super::decoder::{PayloadDecoder, PayloadItem, RequestDecoder}; use super::decoder::{PayloadDecoder, PayloadItem, RequestDecoder};
use super::encoder::{ResponseEncoder, ResponseLength}; use super::encoder::{ResponseEncoder, ResponseLength};
use body::Body; use body::{Binary, Body};
use config::ServiceConfig; use config::ServiceConfig;
use error::ParseError; use error::ParseError;
use helpers; use helpers;
@ -26,12 +26,13 @@ bitflags! {
const AVERAGE_HEADER_SIZE: usize = 30; const AVERAGE_HEADER_SIZE: usize = 30;
#[derive(Debug)]
/// Http response /// Http response
pub enum OutMessage { pub enum OutMessage {
/// Http response message /// Http response message
Response(Response), Response(Response),
/// Payload chunk /// Payload chunk
Payload(Bytes), Payload(Option<Binary>),
} }
/// Incoming http/1 request /// Incoming http/1 request
@ -151,6 +152,7 @@ impl Codec {
buffer.extend_from_slice(reason); buffer.extend_from_slice(reason);
// content length // content length
let mut len_is_set = true;
match self.te.length { match self.te.length {
ResponseLength::Chunked => { ResponseLength::Chunked => {
buffer.extend_from_slice(b"\r\ntransfer-encoding: chunked\r\n") buffer.extend_from_slice(b"\r\ntransfer-encoding: chunked\r\n")
@ -167,6 +169,10 @@ impl Codec {
buffer.extend_from_slice(b"\r\n"); buffer.extend_from_slice(b"\r\n");
} }
ResponseLength::None => buffer.extend_from_slice(b"\r\n"), ResponseLength::None => buffer.extend_from_slice(b"\r\n"),
ResponseLength::HeaderOrZero => {
len_is_set = false;
buffer.extend_from_slice(b"\r\n")
}
} }
// write headers // write headers
@ -179,6 +185,9 @@ impl Codec {
TRANSFER_ENCODING => continue, TRANSFER_ENCODING => continue,
CONTENT_LENGTH => match self.te.length { CONTENT_LENGTH => match self.te.length {
ResponseLength::None => (), ResponseLength::None => (),
ResponseLength::HeaderOrZero => {
len_is_set = true;
}
_ => continue, _ => continue,
}, },
DATE => { DATE => {
@ -215,11 +224,13 @@ impl Codec {
unsafe { unsafe {
buffer.advance_mut(pos); buffer.advance_mut(pos);
} }
if !len_is_set {
buffer.extend_from_slice(b"content-length: 0\r\n")
}
// optimized date header, set_date writes \r\n // optimized date header, set_date writes \r\n
if !has_date { if !has_date {
self.config.set_date(buffer); self.config.set_date(buffer);
buffer.extend_from_slice(b"\r\n");
} else { } else {
// msg eof // msg eof
buffer.extend_from_slice(b"\r\n"); buffer.extend_from_slice(b"\r\n");
@ -272,8 +283,11 @@ impl Encoder for Codec {
OutMessage::Response(res) => { OutMessage::Response(res) => {
self.encode_response(res, dst)?; self.encode_response(res, dst)?;
} }
OutMessage::Payload(bytes) => { OutMessage::Payload(Some(bytes)) => {
dst.extend_from_slice(&bytes); self.te.encode(bytes.as_ref(), dst)?;
}
OutMessage::Payload(None) => {
self.te.encode_eof(dst)?;
} }
} }
Ok(()) Ok(())

View File

@ -12,7 +12,7 @@ use tokio_timer::Delay;
use error::{ParseError, PayloadError}; use error::{ParseError, PayloadError};
use payload::{Payload, PayloadSender, PayloadStatus, PayloadWriter}; use payload::{Payload, PayloadSender, PayloadStatus, PayloadWriter};
use body::Body; use body::{Body, BodyStream};
use config::ServiceConfig; use config::ServiceConfig;
use error::DispatchError; use error::DispatchError;
use request::Request; use request::Request;
@ -61,9 +61,8 @@ enum Message {
enum State<S: Service> { enum State<S: Service> {
None, None,
ServiceCall(S::Future), ServiceCall(S::Future),
SendResponse(Option<OutMessage>), SendResponse(Option<(OutMessage, Body)>),
SendResponseWithPayload(Option<(OutMessage, Body)>), SendPayload(Option<BodyStream>, Option<OutMessage>),
Payload(Body),
} }
impl<S: Service> State<S> { impl<S: Service> State<S> {
@ -99,6 +98,7 @@ where
}; };
let framed = Framed::new(stream, Codec::new(config.clone())); let framed = Framed::new(stream, Codec::new(config.clone()));
// keep-alive timer
let (ka_expire, ka_timer) = if let Some(delay) = timeout { let (ka_expire, ka_timer) = if let Some(delay) = timeout {
(delay.deadline(), Some(delay)) (delay.deadline(), Some(delay))
} else if let Some(delay) = config.keep_alive_timer() { } else if let Some(delay) = config.keep_alive_timer() {
@ -174,35 +174,32 @@ where
break if let Some(msg) = self.messages.pop_front() { break if let Some(msg) = self.messages.pop_front() {
match msg { match msg {
Message::Item(req) => Some(self.handle_request(req)?), Message::Item(req) => Some(self.handle_request(req)?),
Message::Error(res) => Some(State::SendResponse(Some( Message::Error(res) => Some(State::SendResponse(Some((
OutMessage::Response(res), OutMessage::Response(res),
))), Body::Empty,
)))),
} }
} else { } else {
None None
}; };
}, },
State::Payload(ref mut _body) => unimplemented!(), // call inner service
State::ServiceCall(ref mut fut) => match fut State::ServiceCall(ref mut fut) => {
.poll() match fut.poll().map_err(DispatchError::Service)? {
.map_err(DispatchError::Service)?
{
Async::Ready(mut res) => { Async::Ready(mut res) => {
self.framed.get_codec_mut().prepare_te(&mut res); self.framed.get_codec_mut().prepare_te(&mut res);
let body = res.replace_body(Body::Empty); let body = res.replace_body(Body::Empty);
if body.is_empty() { Some(State::SendResponse(Some((
Some(State::SendResponse(Some(OutMessage::Response(res))))
} else {
Some(State::SendResponseWithPayload(Some((
OutMessage::Response(res), OutMessage::Response(res),
body, body,
)))) ))))
} }
}
Async::NotReady => None, Async::NotReady => None,
}, }
}
// send respons
State::SendResponse(ref mut item) => { State::SendResponse(ref mut item) => {
let msg = item.take().expect("SendResponse is empty"); let (msg, body) = item.take().expect("SendResponse is empty");
match self.framed.start_send(msg) { match self.framed.start_send(msg) {
Ok(AsyncSink::Ready) => { Ok(AsyncSink::Ready) => {
self.flags.set( self.flags.set(
@ -210,32 +207,17 @@ where
self.framed.get_codec().keepalive(), self.framed.get_codec().keepalive(),
); );
self.flags.remove(Flags::FLUSHED); self.flags.remove(Flags::FLUSHED);
Some(State::None) match body {
} Body::Empty => Some(State::None),
Ok(AsyncSink::NotReady(msg)) => { Body::Binary(bin) => Some(State::SendPayload(
*item = Some(msg); None,
return Ok(()); Some(OutMessage::Payload(bin.into())),
} )),
Err(err) => { Body::Streaming(stream) => {
if let Some(mut payload) = self.payload.take() { Some(State::SendPayload(Some(stream), None))
payload.set_error(PayloadError::Incomplete);
}
return Err(DispatchError::Io(err));
} }
} }
} }
State::SendResponseWithPayload(ref mut item) => {
let (msg, body) =
item.take().expect("SendResponseWithPayload is empty");
match self.framed.start_send(msg) {
Ok(AsyncSink::Ready) => {
self.flags.set(
Flags::KEEPALIVE,
self.framed.get_codec().keepalive(),
);
self.flags.remove(Flags::FLUSHED);
Some(State::Payload(body))
}
Ok(AsyncSink::NotReady(msg)) => { Ok(AsyncSink::NotReady(msg)) => {
*item = Some((msg, body)); *item = Some((msg, body));
return Ok(()); return Ok(());
@ -248,6 +230,48 @@ where
} }
} }
} }
// Send payload
State::SendPayload(ref mut stream, ref mut bin) => {
if let Some(item) = bin.take() {
match self.framed.start_send(item) {
Ok(AsyncSink::Ready) => {
self.flags.remove(Flags::FLUSHED);
}
Ok(AsyncSink::NotReady(item)) => {
*bin = Some(item);
return Ok(());
}
Err(err) => return Err(DispatchError::Io(err)),
}
}
if let Some(ref mut stream) = stream {
match stream.poll() {
Ok(Async::Ready(Some(item))) => match self
.framed
.start_send(OutMessage::Payload(Some(item.into())))
{
Ok(AsyncSink::Ready) => {
self.flags.remove(Flags::FLUSHED);
continue;
}
Ok(AsyncSink::NotReady(msg)) => {
*bin = Some(msg);
return Ok(());
}
Err(err) => return Err(DispatchError::Io(err)),
},
Ok(Async::Ready(None)) => Some(State::SendPayload(
None,
Some(OutMessage::Payload(None)),
)),
Ok(Async::NotReady) => return Ok(()),
// Err(err) => return Err(DispatchError::Io(err)),
Err(_) => return Err(DispatchError::Unknown),
}
} else {
Some(State::None)
}
}
}; };
match state { match state {
@ -275,19 +299,13 @@ where
Async::Ready(mut res) => { Async::Ready(mut res) => {
self.framed.get_codec_mut().prepare_te(&mut res); self.framed.get_codec_mut().prepare_te(&mut res);
let body = res.replace_body(Body::Empty); let body = res.replace_body(Body::Empty);
if body.is_empty() { Ok(State::SendResponse(Some((OutMessage::Response(res), body))))
Ok(State::SendResponse(Some(OutMessage::Response(res))))
} else {
Ok(State::SendResponseWithPayload(Some((
OutMessage::Response(res),
body,
))))
}
} }
Async::NotReady => Ok(State::ServiceCall(task)), Async::NotReady => Ok(State::ServiceCall(task)),
} }
} }
/// Process one incoming message
fn one_message(&mut self, msg: InMessage) -> Result<(), DispatchError<S::Error>> { fn one_message(&mut self, msg: InMessage) -> Result<(), DispatchError<S::Error>> {
self.flags.insert(Flags::STARTED); self.flags.insert(Flags::STARTED);
@ -408,9 +426,11 @@ where
// timeout on first request (slow request) return 408 // timeout on first request (slow request) return 408
trace!("Slow request timeout"); trace!("Slow request timeout");
self.flags.insert(Flags::STARTED | Flags::DISCONNECTED); self.flags.insert(Flags::STARTED | Flags::DISCONNECTED);
self.state = self.state = State::SendResponse(Some((
State::SendResponse(Some(OutMessage::Response( OutMessage::Response(
Response::RequestTimeout().finish(), Response::RequestTimeout().finish(),
),
Body::Empty,
))); )));
} }
} else if let Some(deadline) = self.config.keep_alive_expire() { } else if let Some(deadline) = self.config.keep_alive_expire() {

View File

@ -17,9 +17,13 @@ use response::Response;
#[derive(Debug)] #[derive(Debug)]
pub(crate) enum ResponseLength { pub(crate) enum ResponseLength {
Chunked, Chunked,
/// Content length is 0
Zero, Zero,
/// Check if headers contains length or write 0
HeaderOrZero,
Length(usize), Length(usize),
Length64(u64), Length64(u64),
/// Do no set content-length
None, None,
} }
@ -41,6 +45,16 @@ impl Default for ResponseEncoder {
} }
impl ResponseEncoder { impl ResponseEncoder {
/// Encode message
pub fn encode(&mut self, msg: &[u8], buf: &mut BytesMut) -> io::Result<bool> {
self.te.encode(msg, buf)
}
/// Encode eof
pub fn encode_eof(&mut self, buf: &mut BytesMut) -> io::Result<()> {
self.te.encode_eof(buf)
}
pub fn update(&mut self, resp: &mut Response, head: bool, version: Version) { pub fn update(&mut self, resp: &mut Response, head: bool, version: Version) {
self.head = head; self.head = head;
@ -63,17 +77,13 @@ impl ResponseEncoder {
let transfer = match resp.body() { let transfer = match resp.body() {
Body::Empty => { Body::Empty => {
if !self.head {
self.length = match resp.status() { self.length = match resp.status() {
StatusCode::NO_CONTENT StatusCode::NO_CONTENT
| StatusCode::CONTINUE | StatusCode::CONTINUE
| StatusCode::SWITCHING_PROTOCOLS | StatusCode::SWITCHING_PROTOCOLS
| StatusCode::PROCESSING => ResponseLength::None, | StatusCode::PROCESSING => ResponseLength::None,
_ => ResponseLength::Zero, _ => ResponseLength::HeaderOrZero,
}; };
} else {
self.length = ResponseLength::Zero;
}
TransferEncoding::empty() TransferEncoding::empty()
} }
Body::Binary(_) => { Body::Binary(_) => {
@ -253,16 +263,22 @@ impl TransferEncoding {
/// Encode eof. Return `EOF` state of encoder /// Encode eof. Return `EOF` state of encoder
#[inline] #[inline]
pub fn encode_eof(&mut self, buf: &mut BytesMut) -> bool { pub fn encode_eof(&mut self, buf: &mut BytesMut) -> io::Result<()> {
match self.kind { match self.kind {
TransferEncodingKind::Eof => true, TransferEncodingKind::Eof => Ok(()),
TransferEncodingKind::Length(rem) => rem == 0, TransferEncodingKind::Length(rem) => {
if rem != 0 {
Err(io::Error::new(io::ErrorKind::UnexpectedEof, ""))
} else {
Ok(())
}
}
TransferEncodingKind::Chunked(ref mut eof) => { TransferEncodingKind::Chunked(ref mut eof) => {
if !*eof { if !*eof {
*eof = true; *eof = true;
buf.extend_from_slice(b"0\r\n\r\n"); buf.extend_from_slice(b"0\r\n\r\n");
} }
true Ok(())
} }
} }
} }

View File

@ -2,6 +2,7 @@ extern crate actix;
extern crate actix_http; extern crate actix_http;
extern crate actix_net; extern crate actix_net;
extern crate actix_web; extern crate actix_web;
extern crate bytes;
extern crate futures; extern crate futures;
use std::{io::Read, io::Write, net, thread, time}; use std::{io::Read, io::Write, net, thread, time};
@ -9,9 +10,11 @@ use std::{io::Read, io::Write, net, thread, time};
use actix::System; use actix::System;
use actix_net::server::Server; use actix_net::server::Server;
use actix_web::{client, test, HttpMessage}; use actix_web::{client, test, HttpMessage};
use futures::future; use bytes::Bytes;
use futures::future::{self, ok};
use futures::stream::once;
use actix_http::{h1, KeepAlive, Request, Response}; use actix_http::{h1, Body, KeepAlive, Request, Response};
#[test] #[test]
fn test_h1_v2() { fn test_h1_v2() {
@ -33,7 +36,7 @@ fn test_h1_v2() {
let mut sys = System::new("test"); let mut sys = System::new("test");
{ {
let req = client::ClientRequest::get(format!("http://{}/", addr).as_str()) let req = client::ClientRequest::get(format!("http://{}/", addr))
.finish() .finish()
.unwrap(); .unwrap();
let response = sys.block_on(req.send()).unwrap(); let response = sys.block_on(req.send()).unwrap();
@ -68,9 +71,7 @@ fn test_malformed_request() {
thread::spawn(move || { thread::spawn(move || {
Server::new() Server::new()
.bind("test", addr, move || { .bind("test", addr, move || {
h1::H1Service::build() h1::H1Service::new(|_| future::ok::<_, ()>(Response::Ok().finish()))
.client_timeout(100)
.finish(|_| future::ok::<_, ()>(Response::Ok().finish()))
}).unwrap() }).unwrap()
.run(); .run();
}); });
@ -117,8 +118,7 @@ fn test_content_length() {
let mut sys = System::new("test"); let mut sys = System::new("test");
{ {
for i in 0..4 { for i in 0..4 {
let req = let req = client::ClientRequest::get(format!("http://{}/{}", addr, i))
client::ClientRequest::get(format!("http://{}/{}", addr, i).as_str())
.finish() .finish()
.unwrap(); .unwrap();
let response = sys.block_on(req.send()).unwrap(); let response = sys.block_on(req.send()).unwrap();
@ -126,8 +126,7 @@ fn test_content_length() {
} }
for i in 4..6 { for i in 4..6 {
let req = let req = client::ClientRequest::get(format!("http://{}/{}", addr, i))
client::ClientRequest::get(format!("http://{}/{}", addr, i).as_str())
.finish() .finish()
.unwrap(); .unwrap();
let response = sys.block_on(req.send()).unwrap(); let response = sys.block_on(req.send()).unwrap();
@ -135,3 +134,290 @@ fn test_content_length() {
} }
} }
} }
#[test]
fn test_headers() {
let data = STR.repeat(10);
let data2 = data.clone();
let addr = test::TestServer::unused_addr();
thread::spawn(move || {
Server::new()
.bind("test", addr, move || {
let data = data.clone();
h1::H1Service::new(move |_| {
let mut builder = Response::Ok();
for idx in 0..90 {
builder.header(
format!("X-TEST-{}", idx).as_str(),
"TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST ",
);
}
future::ok::<_, ()>(builder.body(data.clone()))
})
})
.unwrap()
.run()
});
thread::sleep(time::Duration::from_millis(400));
let mut sys = System::new("test");
let req = client::ClientRequest::get(format!("http://{}/", addr))
.finish()
.unwrap();
let response = sys.block_on(req.send()).unwrap();
assert!(response.status().is_success());
// read response
let bytes = sys.block_on(response.body()).unwrap();
assert_eq!(bytes, Bytes::from(data2));
}
const STR: &str = "Hello World Hello World Hello World Hello World Hello World \
Hello World Hello World Hello World Hello World Hello World \
Hello World Hello World Hello World Hello World Hello World \
Hello World Hello World Hello World Hello World Hello World \
Hello World Hello World Hello World Hello World Hello World \
Hello World Hello World Hello World Hello World Hello World \
Hello World Hello World Hello World Hello World Hello World \
Hello World Hello World Hello World Hello World Hello World \
Hello World Hello World Hello World Hello World Hello World \
Hello World Hello World Hello World Hello World Hello World \
Hello World Hello World Hello World Hello World Hello World \
Hello World Hello World Hello World Hello World Hello World \
Hello World Hello World Hello World Hello World Hello World \
Hello World Hello World Hello World Hello World Hello World \
Hello World Hello World Hello World Hello World Hello World \
Hello World Hello World Hello World Hello World Hello World \
Hello World Hello World Hello World Hello World Hello World \
Hello World Hello World Hello World Hello World Hello World \
Hello World Hello World Hello World Hello World Hello World \
Hello World Hello World Hello World Hello World Hello World \
Hello World Hello World Hello World Hello World Hello World";
#[test]
fn test_body() {
let addr = test::TestServer::unused_addr();
thread::spawn(move || {
Server::new()
.bind("test", addr, move || {
h1::H1Service::new(|_| future::ok::<_, ()>(Response::Ok().body(STR)))
}).unwrap()
.run();
});
thread::sleep(time::Duration::from_millis(100));
let mut sys = System::new("test");
let req = client::ClientRequest::get(format!("http://{}/", addr))
.finish()
.unwrap();
let response = sys.block_on(req.send()).unwrap();
assert!(response.status().is_success());
// read response
let bytes = sys.block_on(response.body()).unwrap();
assert_eq!(bytes, Bytes::from_static(STR.as_ref()));
}
#[test]
fn test_head_empty() {
let addr = test::TestServer::unused_addr();
thread::spawn(move || {
Server::new()
.bind("test", addr, move || {
h1::H1Service::new(|_| {
ok::<_, ()>(Response::Ok().content_length(STR.len() as u64).finish())
})
}).unwrap()
.run()
});
thread::sleep(time::Duration::from_millis(100));
let mut sys = System::new("test");
let req = client::ClientRequest::head(format!("http://{}/", addr))
.finish()
.unwrap();
let response = sys.block_on(req.send()).unwrap();
assert!(response.status().is_success());
{
println!("RESP: {:?}", response);
let len = response
.headers()
.get(http::header::CONTENT_LENGTH)
.unwrap();
assert_eq!(format!("{}", STR.len()), len.to_str().unwrap());
}
// read response
let bytes = sys.block_on(response.body()).unwrap();
assert!(bytes.is_empty());
}
#[test]
fn test_head_binary() {
let addr = test::TestServer::unused_addr();
thread::spawn(move || {
Server::new()
.bind("test", addr, move || {
h1::H1Service::new(|_| {
ok::<_, ()>(
Response::Ok().content_length(STR.len() as u64).body(STR),
)
})
}).unwrap()
.run()
});
thread::sleep(time::Duration::from_millis(100));
let mut sys = System::new("test");
let req = client::ClientRequest::head(format!("http://{}/", addr))
.finish()
.unwrap();
let response = sys.block_on(req.send()).unwrap();
assert!(response.status().is_success());
{
let len = response
.headers()
.get(http::header::CONTENT_LENGTH)
.unwrap();
assert_eq!(format!("{}", STR.len()), len.to_str().unwrap());
}
// read response
let bytes = sys.block_on(response.body()).unwrap();
assert!(bytes.is_empty());
}
#[test]
fn test_head_binary2() {
let addr = test::TestServer::unused_addr();
thread::spawn(move || {
Server::new()
.bind("test", addr, move || {
h1::H1Service::new(|_| ok::<_, ()>(Response::Ok().body(STR)))
}).unwrap()
.run()
});
thread::sleep(time::Duration::from_millis(100));
let mut sys = System::new("test");
let req = client::ClientRequest::head(format!("http://{}/", addr))
.finish()
.unwrap();
let response = sys.block_on(req.send()).unwrap();
assert!(response.status().is_success());
{
let len = response
.headers()
.get(http::header::CONTENT_LENGTH)
.unwrap();
assert_eq!(format!("{}", STR.len()), len.to_str().unwrap());
}
}
#[test]
fn test_body_length() {
let addr = test::TestServer::unused_addr();
thread::spawn(move || {
Server::new()
.bind("test", addr, move || {
h1::H1Service::new(|_| {
let body = once(Ok(Bytes::from_static(STR.as_ref())));
ok::<_, ()>(
Response::Ok()
.content_length(STR.len() as u64)
.body(Body::Streaming(Box::new(body))),
)
})
}).unwrap()
.run()
});
thread::sleep(time::Duration::from_millis(100));
let mut sys = System::new("test");
let req = client::ClientRequest::get(format!("http://{}/", addr))
.finish()
.unwrap();
let response = sys.block_on(req.send()).unwrap();
assert!(response.status().is_success());
// read response
let bytes = sys.block_on(response.body()).unwrap();
assert_eq!(bytes, Bytes::from_static(STR.as_ref()));
}
#[test]
fn test_body_chunked_explicit() {
let addr = test::TestServer::unused_addr();
thread::spawn(move || {
Server::new()
.bind("test", addr, move || {
h1::H1Service::new(|_| {
let body = once(Ok(Bytes::from_static(STR.as_ref())));
ok::<_, ()>(
Response::Ok()
.chunked()
.body(Body::Streaming(Box::new(body))),
)
})
}).unwrap()
.run()
});
thread::sleep(time::Duration::from_millis(100));
let mut sys = System::new("test");
let req = client::ClientRequest::get(format!("http://{}/", addr))
.finish()
.unwrap();
let response = sys.block_on(req.send()).unwrap();
assert!(response.status().is_success());
// read response
let bytes = sys.block_on(response.body()).unwrap();
// decode
assert_eq!(bytes, Bytes::from_static(STR.as_ref()));
}
#[test]
fn test_body_chunked_implicit() {
let addr = test::TestServer::unused_addr();
thread::spawn(move || {
Server::new()
.bind("test", addr, move || {
h1::H1Service::new(|_| {
let body = once(Ok(Bytes::from_static(STR.as_ref())));
ok::<_, ()>(Response::Ok().body(Body::Streaming(Box::new(body))))
})
}).unwrap()
.run()
});
thread::sleep(time::Duration::from_millis(100));
let mut sys = System::new("test");
let req = client::ClientRequest::get(format!("http://{}/", addr))
.finish()
.unwrap();
let response = sys.block_on(req.send()).unwrap();
assert!(response.status().is_success());
// read response
let bytes = sys.block_on(response.body()).unwrap();
assert_eq!(bytes, Bytes::from_static(STR.as_ref()));
}