mirror of
https://github.com/fafhrd91/actix-web
synced 2024-11-24 08:22:59 +01:00
impl response body support
This commit is contained in:
parent
431e33acb2
commit
805e7a4cd0
@ -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(())
|
||||||
|
@ -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,59 +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) => {
|
||||||
{
|
self.framed.get_codec_mut().prepare_te(&mut res);
|
||||||
Async::Ready(mut res) => {
|
let body = res.replace_body(Body::Empty);
|
||||||
self.framed.get_codec_mut().prepare_te(&mut res);
|
Some(State::SendResponse(Some((
|
||||||
let body = res.replace_body(Body::Empty);
|
|
||||||
if body.is_empty() {
|
|
||||||
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,
|
|
||||||
},
|
|
||||||
State::SendResponse(ref mut item) => {
|
|
||||||
let msg = item.take().expect("SendResponse 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::None)
|
|
||||||
}
|
|
||||||
Ok(AsyncSink::NotReady(msg)) => {
|
|
||||||
*item = Some(msg);
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
if let Some(mut payload) = self.payload.take() {
|
|
||||||
payload.set_error(PayloadError::Incomplete);
|
|
||||||
}
|
|
||||||
return Err(DispatchError::Io(err));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
State::SendResponseWithPayload(ref mut item) => {
|
// send respons
|
||||||
let (msg, body) =
|
State::SendResponse(ref mut item) => {
|
||||||
item.take().expect("SendResponseWithPayload 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(
|
||||||
@ -234,7 +207,16 @@ where
|
|||||||
self.framed.get_codec().keepalive(),
|
self.framed.get_codec().keepalive(),
|
||||||
);
|
);
|
||||||
self.flags.remove(Flags::FLUSHED);
|
self.flags.remove(Flags::FLUSHED);
|
||||||
Some(State::Payload(body))
|
match body {
|
||||||
|
Body::Empty => Some(State::None),
|
||||||
|
Body::Binary(bin) => Some(State::SendPayload(
|
||||||
|
None,
|
||||||
|
Some(OutMessage::Payload(bin.into())),
|
||||||
|
)),
|
||||||
|
Body::Streaming(stream) => {
|
||||||
|
Some(State::SendPayload(Some(stream), None))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(AsyncSink::NotReady(msg)) => {
|
Ok(AsyncSink::NotReady(msg)) => {
|
||||||
*item = Some((msg, body));
|
*item = Some((msg, body));
|
||||||
@ -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,10 +426,12 @@ 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() {
|
||||||
timer.reset(deadline)
|
timer.reset(deadline)
|
||||||
|
@ -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::HeaderOrZero,
|
||||||
_ => ResponseLength::Zero,
|
};
|
||||||
};
|
|
||||||
} 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(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,21 +118,306 @@ 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();
|
||||||
assert_eq!(response.headers().get(&header), None);
|
assert_eq!(response.headers().get(&header), None);
|
||||||
}
|
}
|
||||||
|
|
||||||
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();
|
||||||
assert_eq!(response.headers().get(&header), Some(&value));
|
assert_eq!(response.headers().get(&header), Some(&value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[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()));
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user