feat(lib): replace types with those from http crate
BREAKING CHANGE: `Method`, `Request`, `Response`, `StatusCode`, `Version`, and `Uri` have been replaced with types from the `http` crate. The `hyper::header` module is gone for now. Removed `Client::get`, since it needed to construct a `Request<B>` with an empty body. Just use `Client::request` instead. Removed `compat` cargo feature, and `compat` related API.
This commit is contained in:
@@ -5,13 +5,12 @@ use std::marker::PhantomData;
|
||||
use bytes::Bytes;
|
||||
use futures::{Async, AsyncSink, Poll, StartSend};
|
||||
use futures::task::Task;
|
||||
use http::{Method, Version};
|
||||
use tokio_io::{AsyncRead, AsyncWrite};
|
||||
|
||||
use proto::{Chunk, Decode, Http1Transaction, MessageHead};
|
||||
use super::io::{Cursor, Buffered};
|
||||
use super::{EncodedBuf, Encoder, Decoder};
|
||||
use method::Method;
|
||||
use version::HttpVersion;
|
||||
|
||||
|
||||
/// This handles a connection, which will have been established over an
|
||||
@@ -55,7 +54,7 @@ where I: AsyncRead + AsyncWrite,
|
||||
writing: Writing::Init,
|
||||
// We assume a modern world where the remote speaks HTTP/1.1.
|
||||
// If they tell us otherwise, we'll downgrade in `read_head`.
|
||||
version: Version::Http11,
|
||||
version: Version::HTTP_11,
|
||||
},
|
||||
_marker: PhantomData,
|
||||
}
|
||||
@@ -141,15 +140,16 @@ where I: AsyncRead + AsyncWrite,
|
||||
}
|
||||
};
|
||||
|
||||
self.state.version = match version {
|
||||
HttpVersion::Http10 => Version::Http10,
|
||||
HttpVersion::Http11 => Version::Http11,
|
||||
match version {
|
||||
Version::HTTP_10 |
|
||||
Version::HTTP_11 => {},
|
||||
_ => {
|
||||
error!("unimplemented HTTP Version = {:?}", version);
|
||||
self.state.close_read();
|
||||
return Err(::Error::Version);
|
||||
}
|
||||
};
|
||||
self.state.version = version;
|
||||
|
||||
let decoder = match T::decoder(&head, &mut self.state.method) {
|
||||
Ok(Decode::Normal(d)) => {
|
||||
@@ -448,7 +448,7 @@ where I: AsyncRead + AsyncWrite,
|
||||
// If we know the remote speaks an older version, we try to fix up any messages
|
||||
// to work with our older peer.
|
||||
fn enforce_version(&mut self, head: &mut MessageHead<T::Outgoing>) {
|
||||
use header::Connection;
|
||||
//use header::Connection;
|
||||
|
||||
let wants_keep_alive = if self.state.wants_keep_alive() {
|
||||
let ka = head.should_keep_alive();
|
||||
@@ -459,15 +459,15 @@ where I: AsyncRead + AsyncWrite,
|
||||
};
|
||||
|
||||
match self.state.version {
|
||||
Version::Http10 => {
|
||||
Version::HTTP_10 => {
|
||||
// If the remote only knows HTTP/1.0, we should force ourselves
|
||||
// to do only speak HTTP/1.0 as well.
|
||||
head.version = HttpVersion::Http10;
|
||||
head.version = Version::HTTP_10;
|
||||
if wants_keep_alive {
|
||||
head.headers.set(Connection::keep_alive());
|
||||
//TODO: head.headers.set(Connection::keep_alive());
|
||||
}
|
||||
},
|
||||
Version::Http11 => {
|
||||
_ => {
|
||||
// If the remote speaks HTTP/1.1, then it *should* be fine with
|
||||
// both HTTP/1.0 and HTTP/1.1 from us. So again, we just let
|
||||
// the user's headers be.
|
||||
@@ -789,12 +789,6 @@ impl State {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
enum Version {
|
||||
Http10,
|
||||
Http11,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
//TODO: rewrite these using dispatch
|
||||
mod tests {
|
||||
|
||||
@@ -2,11 +2,11 @@ use std::io;
|
||||
|
||||
use bytes::Bytes;
|
||||
use futures::{Async, AsyncSink, Future, Poll, Stream};
|
||||
use http::{Request, Response, StatusCode};
|
||||
use tokio_io::{AsyncRead, AsyncWrite};
|
||||
use tokio_service::Service;
|
||||
|
||||
use proto::{Body, Conn, Http1Transaction, MessageHead, RequestHead, ResponseHead};
|
||||
use ::StatusCode;
|
||||
use proto::{Body, Conn, Http1Transaction, MessageHead, RequestHead, RequestLine, ResponseHead};
|
||||
|
||||
pub struct Dispatcher<D, Bs, I, B, T> {
|
||||
conn: Conn<I, B, T>,
|
||||
@@ -21,7 +21,7 @@ pub trait Dispatch {
|
||||
type PollBody;
|
||||
type RecvItem;
|
||||
fn poll_msg(&mut self) -> Poll<Option<(Self::PollItem, Option<Self::PollBody>)>, ::Error>;
|
||||
fn recv_msg(&mut self, msg: ::Result<(Self::RecvItem, Option<Body>)>) -> ::Result<()>;
|
||||
fn recv_msg(&mut self, msg: ::Result<(Self::RecvItem, Body)>) -> ::Result<()>;
|
||||
fn poll_ready(&mut self) -> Poll<(), ()>;
|
||||
fn should_poll(&self) -> bool;
|
||||
}
|
||||
@@ -32,13 +32,13 @@ pub struct Server<S: Service> {
|
||||
}
|
||||
|
||||
pub struct Client<B> {
|
||||
callback: Option<::client::dispatch::Callback<ClientMsg<B>, ::Response>>,
|
||||
callback: Option<::client::dispatch::Callback<ClientMsg<B>, Response<Body>>>,
|
||||
rx: ClientRx<B>,
|
||||
}
|
||||
|
||||
pub type ClientMsg<B> = (RequestHead, Option<B>);
|
||||
pub type ClientMsg<B> = Request<B>;
|
||||
|
||||
type ClientRx<B> = ::client::dispatch::Receiver<ClientMsg<B>, ::Response>;
|
||||
type ClientRx<B> = ::client::dispatch::Receiver<ClientMsg<B>, Response<Body>>;
|
||||
|
||||
impl<D, Bs, I, B, T> Dispatcher<D, Bs, I, B, T>
|
||||
where
|
||||
@@ -184,9 +184,9 @@ where
|
||||
let (mut tx, rx) = ::proto::body::channel();
|
||||
let _ = tx.poll_ready(); // register this task if rx is dropped
|
||||
self.body_tx = Some(tx);
|
||||
Some(rx)
|
||||
rx
|
||||
} else {
|
||||
None
|
||||
Body::empty()
|
||||
};
|
||||
self.dispatch.recv_msg(Ok((head, body)))?;
|
||||
Ok(Async::Ready(()))
|
||||
@@ -315,7 +315,7 @@ impl<S> Server<S> where S: Service {
|
||||
|
||||
impl<S, Bs> Dispatch for Server<S>
|
||||
where
|
||||
S: Service<Request=::Request, Response=::Response<Bs>, Error=::Error>,
|
||||
S: Service<Request=Request<Body>, Response=Response<Bs>, Error=::Error>,
|
||||
Bs: Stream<Error=::Error>,
|
||||
Bs::Item: AsRef<[u8]>,
|
||||
{
|
||||
@@ -332,16 +332,25 @@ where
|
||||
return Ok(Async::NotReady);
|
||||
}
|
||||
};
|
||||
let (head, body) = ::proto::response::split(resp);
|
||||
Ok(Async::Ready(Some((head.into(), body))))
|
||||
let (parts, body) = resp.into_parts();
|
||||
let head = MessageHead {
|
||||
version: parts.version,
|
||||
subject: parts.status,
|
||||
headers: parts.headers,
|
||||
};
|
||||
Ok(Async::Ready(Some((head, Some(body)))))
|
||||
} else {
|
||||
unreachable!("poll_msg shouldn't be called if no inflight");
|
||||
}
|
||||
}
|
||||
|
||||
fn recv_msg(&mut self, msg: ::Result<(Self::RecvItem, Option<Body>)>) -> ::Result<()> {
|
||||
fn recv_msg(&mut self, msg: ::Result<(Self::RecvItem, Body)>) -> ::Result<()> {
|
||||
let (msg, body) = msg?;
|
||||
let req = ::proto::request::from_wire(None, msg, body);
|
||||
let mut req = Request::new(body);
|
||||
*req.method_mut() = msg.subject.0;
|
||||
*req.uri_mut() = msg.subject.1;
|
||||
*req.headers_mut() = msg.headers;
|
||||
*req.version_mut() = msg.version;
|
||||
self.in_flight = Some(self.service.call(req));
|
||||
Ok(())
|
||||
}
|
||||
@@ -382,7 +391,7 @@ where
|
||||
|
||||
fn poll_msg(&mut self) -> Poll<Option<(Self::PollItem, Option<Self::PollBody>)>, ::Error> {
|
||||
match self.rx.poll() {
|
||||
Ok(Async::Ready(Some(((head, body), mut cb)))) => {
|
||||
Ok(Async::Ready(Some((req, mut cb)))) => {
|
||||
// check that future hasn't been canceled already
|
||||
match cb.poll_cancel().expect("poll_cancel cannot error") {
|
||||
Async::Ready(()) => {
|
||||
@@ -390,8 +399,14 @@ where
|
||||
Ok(Async::Ready(None))
|
||||
},
|
||||
Async::NotReady => {
|
||||
let (parts, body) = req.into_parts();
|
||||
let head = RequestHead {
|
||||
version: parts.version,
|
||||
subject: RequestLine(parts.method, parts.uri),
|
||||
headers: parts.headers,
|
||||
};
|
||||
self.callback = Some(cb);
|
||||
Ok(Async::Ready(Some((head, body))))
|
||||
Ok(Async::Ready(Some((head, Some(body)))))
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -405,11 +420,14 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
fn recv_msg(&mut self, msg: ::Result<(Self::RecvItem, Option<Body>)>) -> ::Result<()> {
|
||||
fn recv_msg(&mut self, msg: ::Result<(Self::RecvItem, Body)>) -> ::Result<()> {
|
||||
match msg {
|
||||
Ok((msg, body)) => {
|
||||
if let Some(cb) = self.callback.take() {
|
||||
let res = ::proto::response::from_wire(msg, body);
|
||||
let mut res = Response::new(body);
|
||||
*res.status_mut() = msg.subject;
|
||||
*res.headers_mut() = msg.headers;
|
||||
*res.version_mut() = msg.version;
|
||||
let _ = cb.send(Ok(res));
|
||||
Ok(())
|
||||
} else {
|
||||
@@ -469,12 +487,7 @@ mod tests {
|
||||
let conn = Conn::<_, ::Chunk, ClientTransaction>::new(io);
|
||||
let mut dispatcher = Dispatcher::new(Client::new(rx), conn);
|
||||
|
||||
let req = RequestHead {
|
||||
version: ::HttpVersion::Http11,
|
||||
subject: ::proto::RequestLine::default(),
|
||||
headers: Default::default(),
|
||||
};
|
||||
let res_rx = tx.try_send((req, None::<::Body>)).unwrap();
|
||||
let res_rx = tx.try_send(::Request::new(::Body::empty())).unwrap();
|
||||
|
||||
let a1 = dispatcher.poll().expect("error should be sent on channel");
|
||||
assert!(a1.is_ready(), "dispatcher should be closed");
|
||||
|
||||
@@ -1,16 +1,13 @@
|
||||
use std::borrow::Cow;
|
||||
use std::fmt::{self, Write};
|
||||
|
||||
use httparse;
|
||||
use bytes::{BytesMut, Bytes};
|
||||
use http::header::{CONTENT_LENGTH, DATE, HeaderName, HeaderValue, TRANSFER_ENCODING};
|
||||
use http::{HeaderMap, Method, StatusCode, Uri, Version};
|
||||
use httparse;
|
||||
|
||||
use header::{self, Headers, ContentLength, TransferEncoding};
|
||||
use proto::{Decode, MessageHead, RawStatus, Http1Transaction, ParseResult,
|
||||
RequestLine, RequestHead};
|
||||
use headers;
|
||||
use proto::{Decode, MessageHead, Http1Transaction, ParseResult, RequestLine, RequestHead};
|
||||
use proto::h1::{Encoder, Decoder, date};
|
||||
use method::Method;
|
||||
use status::StatusCode;
|
||||
use version::HttpVersion::{Http10, Http11};
|
||||
|
||||
const MAX_HEADERS: usize = 100;
|
||||
const AVERAGE_HEADER_SIZE: usize = 30; // totally scientific
|
||||
@@ -46,13 +43,17 @@ where
|
||||
match try!(req.parse(&buf)) {
|
||||
httparse::Status::Complete(len) => {
|
||||
trace!("Request.parse Complete({})", len);
|
||||
let method = try!(req.method.unwrap().parse());
|
||||
let method = Method::from_bytes(req.method.unwrap().as_bytes())?;
|
||||
let path = req.path.unwrap();
|
||||
let bytes_ptr = buf.as_ref().as_ptr() as usize;
|
||||
let path_start = path.as_ptr() as usize - bytes_ptr;
|
||||
let path_end = path_start + path.len();
|
||||
let path = (path_start, path_end);
|
||||
let version = if req.version.unwrap() == 1 { Http11 } else { Http10 };
|
||||
let version = if req.version.unwrap() == 1 {
|
||||
Version::HTTP_11
|
||||
} else {
|
||||
Version::HTTP_10
|
||||
};
|
||||
|
||||
record_header_indices(buf.as_ref(), &req.headers, &mut headers_indices);
|
||||
let headers_len = req.headers.len();
|
||||
@@ -62,11 +63,11 @@ where
|
||||
}
|
||||
};
|
||||
|
||||
let mut headers = Headers::with_capacity(headers_len);
|
||||
let mut headers = HeaderMap::with_capacity(headers_len);
|
||||
let slice = buf.split_to(len).freeze();
|
||||
let path = slice.slice(path.0, path.1);
|
||||
// path was found to be utf8 by httparse
|
||||
let path = try!(unsafe { ::uri::from_utf8_unchecked(path) });
|
||||
let path = Uri::from_shared(path)?;
|
||||
let subject = RequestLine(
|
||||
method,
|
||||
path,
|
||||
@@ -85,8 +86,6 @@ where
|
||||
}
|
||||
|
||||
fn decoder(head: &MessageHead<Self::Incoming>, method: &mut Option<Method>) -> ::Result<Decode> {
|
||||
use ::header;
|
||||
|
||||
*method = Some(head.subject.0.clone());
|
||||
|
||||
// According to https://tools.ietf.org/html/rfc7230#section-3.3.3
|
||||
@@ -98,24 +97,24 @@ where
|
||||
// 6. Length 0.
|
||||
// 7. (irrelevant to Request)
|
||||
|
||||
if let Some(&header::TransferEncoding(ref encodings)) = head.headers.get() {
|
||||
if head.headers.contains_key(TRANSFER_ENCODING) {
|
||||
// https://tools.ietf.org/html/rfc7230#section-3.3.3
|
||||
// If Transfer-Encoding header is present, and 'chunked' is
|
||||
// not the final encoding, and this is a Request, then it is
|
||||
// mal-formed. A server should respond with 400 Bad Request.
|
||||
if head.version == Http10 {
|
||||
debug!("HTTP/1.0 has Transfer-Encoding header");
|
||||
if head.version == Version::HTTP_10 {
|
||||
debug!("HTTP/1.0 cannot have Transfer-Encoding header");
|
||||
Err(::Error::Header)
|
||||
} else if encodings.last() == Some(&header::Encoding::Chunked) {
|
||||
} else if headers::transfer_encoding_is_chunked(&head.headers) {
|
||||
Ok(Decode::Normal(Decoder::chunked()))
|
||||
} else {
|
||||
debug!("request with transfer-encoding header, but not chunked, bad request");
|
||||
Err(::Error::Header)
|
||||
}
|
||||
} else if let Some(&header::ContentLength(len)) = head.headers.get() {
|
||||
} else if let Some(len) = headers::content_length_parse(&head.headers) {
|
||||
Ok(Decode::Normal(Decoder::length(len)))
|
||||
} else if head.headers.has::<header::ContentLength>() {
|
||||
debug!("illegal Content-Length: {:?}", head.headers.get_raw("Content-Length"));
|
||||
} else if head.headers.contains_key(CONTENT_LENGTH) {
|
||||
debug!("illegal Content-Length header");
|
||||
Err(::Error::Header)
|
||||
} else {
|
||||
Ok(Decode::Normal(Decoder::length(0)))
|
||||
@@ -130,7 +129,7 @@ where
|
||||
// This is because Service only allows returning a single Response, and
|
||||
// so if you try to reply with a e.g. 100 Continue, you have no way of
|
||||
// replying with the latter status code response.
|
||||
let ret = if ::StatusCode::SwitchingProtocols == head.subject {
|
||||
let ret = if StatusCode::SWITCHING_PROTOCOLS == head.subject {
|
||||
T::on_encode_upgrade(&mut head)
|
||||
.map(|_| {
|
||||
let mut enc = Server::set_length(&mut head, has_body, method.as_ref());
|
||||
@@ -140,8 +139,8 @@ where
|
||||
} else if head.subject.is_informational() {
|
||||
error!("response with 1xx status code not supported");
|
||||
head = MessageHead::default();
|
||||
head.subject = ::StatusCode::InternalServerError;
|
||||
head.headers.set(ContentLength(0));
|
||||
head.subject = StatusCode::INTERNAL_SERVER_ERROR;
|
||||
headers::content_length_zero(&mut head.headers);
|
||||
Err(::Error::Status)
|
||||
} else {
|
||||
Ok(Server::set_length(&mut head, has_body, method.as_ref()))
|
||||
@@ -150,15 +149,24 @@ where
|
||||
|
||||
let init_cap = 30 + head.headers.len() * AVERAGE_HEADER_SIZE;
|
||||
dst.reserve(init_cap);
|
||||
if head.version == ::HttpVersion::Http11 && head.subject == ::StatusCode::Ok {
|
||||
if head.version == Version::HTTP_11 && head.subject == StatusCode::OK {
|
||||
extend(dst, b"HTTP/1.1 200 OK\r\n");
|
||||
let _ = write!(FastWrite(dst), "{}", head.headers);
|
||||
} else {
|
||||
let _ = write!(FastWrite(dst), "{} {}\r\n{}", head.version, head.subject, head.headers);
|
||||
match head.version {
|
||||
Version::HTTP_10 => extend(dst, b"HTTP/1.0 "),
|
||||
Version::HTTP_11 => extend(dst, b"HTTP/1.1 "),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
extend(dst, head.subject.as_str().as_bytes());
|
||||
extend(dst, b" ");
|
||||
extend(dst, head.subject.canonical_reason().unwrap_or("<none>").as_bytes());
|
||||
extend(dst, b"\r\n");
|
||||
}
|
||||
write_headers(&head.headers, dst);
|
||||
// using http::h1::date is quite a lot faster than generating a unique Date header each time
|
||||
// like req/s goes up about 10%
|
||||
if !head.headers.has::<header::Date>() {
|
||||
if !head.headers.contains_key(DATE) {
|
||||
dst.reserve(date::DATE_VALUE_LENGTH + 8);
|
||||
extend(dst, b"Date: ");
|
||||
date::extend(dst);
|
||||
@@ -173,12 +181,12 @@ where
|
||||
let status = match err {
|
||||
&::Error::Method |
|
||||
&::Error::Version |
|
||||
&::Error::Header |
|
||||
&::Error::Uri(_) => {
|
||||
StatusCode::BadRequest
|
||||
&::Error::Header /*|
|
||||
&::Error::Uri(_)*/ => {
|
||||
StatusCode::BAD_REQUEST
|
||||
},
|
||||
&::Error::TooLarge => {
|
||||
StatusCode::RequestHeaderFieldsTooLarge
|
||||
StatusCode::REQUEST_HEADER_FIELDS_TOO_LARGE
|
||||
}
|
||||
_ => return None,
|
||||
};
|
||||
@@ -202,8 +210,8 @@ impl Server<()> {
|
||||
fn set_length(head: &mut MessageHead<StatusCode>, has_body: bool, method: Option<&Method>) -> Encoder {
|
||||
// these are here thanks to borrowck
|
||||
// `if method == Some(&Method::Get)` says the RHS doesn't live long enough
|
||||
const HEAD: Option<&'static Method> = Some(&Method::Head);
|
||||
const CONNECT: Option<&'static Method> = Some(&Method::Connect);
|
||||
const HEAD: Option<&'static Method> = Some(&Method::HEAD);
|
||||
const CONNECT: Option<&'static Method> = Some(&Method::CONNECT);
|
||||
|
||||
let can_have_body = {
|
||||
if method == HEAD {
|
||||
@@ -214,20 +222,20 @@ impl Server<()> {
|
||||
match head.subject {
|
||||
// TODO: support for 1xx codes needs improvement everywhere
|
||||
// would be 100...199 => false
|
||||
StatusCode::SwitchingProtocols |
|
||||
StatusCode::NoContent |
|
||||
StatusCode::NotModified => false,
|
||||
StatusCode::SWITCHING_PROTOCOLS |
|
||||
StatusCode::NO_CONTENT |
|
||||
StatusCode::NOT_MODIFIED => false,
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if has_body && can_have_body {
|
||||
set_length(&mut head.headers, head.version == Http11)
|
||||
set_length(&mut head.headers, head.version == Version::HTTP_11)
|
||||
} else {
|
||||
head.headers.remove::<TransferEncoding>();
|
||||
head.headers.remove(TRANSFER_ENCODING);
|
||||
if can_have_body {
|
||||
head.headers.set(ContentLength(0));
|
||||
headers::content_length_zero(&mut head.headers);
|
||||
}
|
||||
Encoder::length(0)
|
||||
}
|
||||
@@ -238,10 +246,10 @@ impl<T> Http1Transaction for Client<T>
|
||||
where
|
||||
T: OnUpgrade,
|
||||
{
|
||||
type Incoming = RawStatus;
|
||||
type Incoming = StatusCode;
|
||||
type Outgoing = RequestLine;
|
||||
|
||||
fn parse(buf: &mut BytesMut) -> ParseResult<RawStatus> {
|
||||
fn parse(buf: &mut BytesMut) -> ParseResult<StatusCode> {
|
||||
if buf.len() == 0 {
|
||||
return Ok(None);
|
||||
}
|
||||
@@ -249,7 +257,7 @@ where
|
||||
name: (0, 0),
|
||||
value: (0, 0)
|
||||
}; MAX_HEADERS];
|
||||
let (len, code, reason, version, headers_len) = {
|
||||
let (len, status, version, headers_len) = {
|
||||
let mut headers = [httparse::EMPTY_HEADER; MAX_HEADERS];
|
||||
trace!("Response.parse([Header; {}], [u8; {}])", headers.len(), buf.len());
|
||||
let mut res = httparse::Response::new(&mut headers);
|
||||
@@ -257,22 +265,21 @@ where
|
||||
match try!(res.parse(bytes)) {
|
||||
httparse::Status::Complete(len) => {
|
||||
trace!("Response.parse Complete({})", len);
|
||||
let code = res.code.unwrap();
|
||||
let status = try!(StatusCode::try_from(code).map_err(|_| ::Error::Status));
|
||||
let reason = match status.canonical_reason() {
|
||||
Some(reason) if reason == res.reason.unwrap() => Cow::Borrowed(reason),
|
||||
_ => Cow::Owned(res.reason.unwrap().to_owned())
|
||||
let status = try!(StatusCode::from_u16(res.code.unwrap()).map_err(|_| ::Error::Status));
|
||||
let version = if res.version.unwrap() == 1 {
|
||||
Version::HTTP_11
|
||||
} else {
|
||||
Version::HTTP_10
|
||||
};
|
||||
let version = if res.version.unwrap() == 1 { Http11 } else { Http10 };
|
||||
record_header_indices(bytes, &res.headers, &mut headers_indices);
|
||||
let headers_len = res.headers.len();
|
||||
(len, code, reason, version, headers_len)
|
||||
(len, status, version, headers_len)
|
||||
},
|
||||
httparse::Status::Partial => return Ok(None),
|
||||
}
|
||||
};
|
||||
|
||||
let mut headers = Headers::with_capacity(headers_len);
|
||||
let mut headers = HeaderMap::with_capacity(headers_len);
|
||||
let slice = buf.split_to(len).freeze();
|
||||
headers.extend(HeadersAsBytesIter {
|
||||
headers: headers_indices[..headers_len].iter(),
|
||||
@@ -280,7 +287,7 @@ where
|
||||
});
|
||||
Ok(Some((MessageHead {
|
||||
version: version,
|
||||
subject: RawStatus(code, reason),
|
||||
subject: status,
|
||||
headers: headers,
|
||||
}, len)))
|
||||
}
|
||||
@@ -295,12 +302,12 @@ where
|
||||
// 6. (irrelevant to Response)
|
||||
// 7. Read till EOF.
|
||||
|
||||
match inc.subject.0 {
|
||||
match inc.subject.as_u16() {
|
||||
101 => {
|
||||
return T::on_decode_upgrade().map(Decode::Final);
|
||||
},
|
||||
100...199 => {
|
||||
trace!("ignoring informational response: {}", inc.subject.0);
|
||||
trace!("ignoring informational response: {}", inc.subject.as_u16());
|
||||
return Ok(Decode::Ignore);
|
||||
},
|
||||
204 |
|
||||
@@ -308,10 +315,10 @@ where
|
||||
_ => (),
|
||||
}
|
||||
match *method {
|
||||
Some(Method::Head) => {
|
||||
Some(Method::HEAD) => {
|
||||
return Ok(Decode::Normal(Decoder::length(0)));
|
||||
}
|
||||
Some(Method::Connect) => match inc.subject.0 {
|
||||
Some(Method::CONNECT) => match inc.subject.as_u16() {
|
||||
200...299 => {
|
||||
return Ok(Decode::Final(Decoder::length(0)));
|
||||
},
|
||||
@@ -323,21 +330,24 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if let Some(&header::TransferEncoding(ref codings)) = inc.headers.get() {
|
||||
if inc.version == Http10 {
|
||||
debug!("HTTP/1.0 has Transfer-Encoding header");
|
||||
if inc.headers.contains_key(TRANSFER_ENCODING) {
|
||||
// https://tools.ietf.org/html/rfc7230#section-3.3.3
|
||||
// If Transfer-Encoding header is present, and 'chunked' is
|
||||
// not the final encoding, and this is a Request, then it is
|
||||
// mal-formed. A server should respond with 400 Bad Request.
|
||||
if inc.version == Version::HTTP_10 {
|
||||
debug!("HTTP/1.0 cannot have Transfer-Encoding header");
|
||||
Err(::Error::Header)
|
||||
} else if codings.last() == Some(&header::Encoding::Chunked) {
|
||||
} else if headers::transfer_encoding_is_chunked(&inc.headers) {
|
||||
Ok(Decode::Normal(Decoder::chunked()))
|
||||
} else {
|
||||
trace!("not chunked. read till eof");
|
||||
trace!("not chunked, read till eof");
|
||||
Ok(Decode::Normal(Decoder::eof()))
|
||||
}
|
||||
} else if let Some(&header::ContentLength(len)) = inc.headers.get() {
|
||||
} else if let Some(len) = headers::content_length_parse(&inc.headers) {
|
||||
Ok(Decode::Normal(Decoder::length(len)))
|
||||
} else if inc.headers.has::<header::ContentLength>() {
|
||||
debug!("illegal Content-Length: {:?}", inc.headers.get_raw("Content-Length"));
|
||||
} else if inc.headers.contains_key(CONTENT_LENGTH) {
|
||||
debug!("illegal Content-Length header");
|
||||
Err(::Error::Header)
|
||||
} else {
|
||||
trace!("neither Transfer-Encoding nor Content-Length");
|
||||
@@ -354,7 +364,22 @@ where
|
||||
|
||||
let init_cap = 30 + head.headers.len() * AVERAGE_HEADER_SIZE;
|
||||
dst.reserve(init_cap);
|
||||
let _ = write!(FastWrite(dst), "{} {}\r\n{}\r\n", head.subject, head.version, head.headers);
|
||||
|
||||
|
||||
extend(dst, head.subject.0.as_str().as_bytes());
|
||||
extend(dst, b" ");
|
||||
//TODO: add API to http::Uri to encode without std::fmt
|
||||
let _ = write!(FastWrite(dst), "{} ", head.subject.1);
|
||||
|
||||
match head.version {
|
||||
Version::HTTP_10 => extend(dst, b"HTTP/1.0"),
|
||||
Version::HTTP_11 => extend(dst, b"HTTP/1.1"),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
extend(dst, b"\r\n");
|
||||
|
||||
write_headers(&head.headers, dst);
|
||||
extend(dst, b"\r\n");
|
||||
|
||||
Ok(body)
|
||||
}
|
||||
@@ -376,41 +401,30 @@ where
|
||||
impl Client<()> {
|
||||
fn set_length(head: &mut RequestHead, has_body: bool) -> Encoder {
|
||||
if has_body {
|
||||
let can_chunked = head.version == Http11
|
||||
&& (head.subject.0 != Method::Head)
|
||||
&& (head.subject.0 != Method::Get)
|
||||
&& (head.subject.0 != Method::Connect);
|
||||
let can_chunked = head.version == Version::HTTP_11
|
||||
&& (head.subject.0 != Method::HEAD)
|
||||
&& (head.subject.0 != Method::GET)
|
||||
&& (head.subject.0 != Method::CONNECT);
|
||||
set_length(&mut head.headers, can_chunked)
|
||||
} else {
|
||||
head.headers.remove::<ContentLength>();
|
||||
head.headers.remove::<TransferEncoding>();
|
||||
head.headers.remove(CONTENT_LENGTH);
|
||||
head.headers.remove(TRANSFER_ENCODING);
|
||||
Encoder::length(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn set_length(headers: &mut Headers, can_chunked: bool) -> Encoder {
|
||||
let len = headers.get::<header::ContentLength>().map(|n| **n);
|
||||
fn set_length(headers: &mut HeaderMap, can_chunked: bool) -> Encoder {
|
||||
let len = headers::content_length_parse(&headers);
|
||||
|
||||
if let Some(len) = len {
|
||||
Encoder::length(len)
|
||||
} else if can_chunked {
|
||||
let encodings = match headers.get_mut::<header::TransferEncoding>() {
|
||||
Some(&mut header::TransferEncoding(ref mut encodings)) => {
|
||||
if encodings.last() != Some(&header::Encoding::Chunked) {
|
||||
encodings.push(header::Encoding::Chunked);
|
||||
}
|
||||
false
|
||||
},
|
||||
None => true
|
||||
};
|
||||
|
||||
if encodings {
|
||||
headers.set(header::TransferEncoding(vec![header::Encoding::Chunked]));
|
||||
}
|
||||
//TODO: maybe not overwrite existing transfer-encoding
|
||||
headers.insert(TRANSFER_ENCODING, HeaderValue::from_static("chunked"));
|
||||
Encoder::chunked()
|
||||
} else {
|
||||
headers.remove::<TransferEncoding>();
|
||||
headers.remove(TRANSFER_ENCODING);
|
||||
Encoder::eof()
|
||||
}
|
||||
}
|
||||
@@ -440,8 +454,8 @@ impl OnUpgrade for NoUpgrades {
|
||||
fn on_encode_upgrade(head: &mut MessageHead<StatusCode>) -> ::Result<()> {
|
||||
error!("response with 101 status code not supported");
|
||||
*head = MessageHead::default();
|
||||
head.subject = ::StatusCode::InternalServerError;
|
||||
head.headers.set(ContentLength(0));
|
||||
head.subject = ::StatusCode::INTERNAL_SERVER_ERROR;
|
||||
headers::content_length_zero(&mut head.headers);
|
||||
Err(::Error::Status)
|
||||
}
|
||||
|
||||
@@ -475,7 +489,7 @@ struct HeadersAsBytesIter<'a> {
|
||||
}
|
||||
|
||||
impl<'a> Iterator for HeadersAsBytesIter<'a> {
|
||||
type Item = (&'a str, Bytes);
|
||||
type Item = (HeaderName, HeaderValue);
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.headers.next().map(|header| {
|
||||
let name = unsafe {
|
||||
@@ -485,11 +499,27 @@ impl<'a> Iterator for HeadersAsBytesIter<'a> {
|
||||
);
|
||||
::std::str::from_utf8_unchecked(bytes)
|
||||
};
|
||||
(name, self.slice.slice(header.value.0, header.value.1))
|
||||
let name = HeaderName::from_bytes(name.as_bytes())
|
||||
.expect("header name already validated");
|
||||
let value = unsafe {
|
||||
HeaderValue::from_shared_unchecked(
|
||||
self.slice.slice(header.value.0, header.value.1)
|
||||
)
|
||||
};
|
||||
(name, value)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn write_headers(headers: &HeaderMap, dst: &mut Vec<u8>) {
|
||||
for (name, value) in headers {
|
||||
extend(dst, name.as_str().as_bytes());
|
||||
extend(dst, b": ");
|
||||
extend(dst, value.as_bytes());
|
||||
extend(dst, b"\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
struct FastWrite<'a>(&'a mut Vec<u8>);
|
||||
|
||||
impl<'a> fmt::Write for FastWrite<'a> {
|
||||
@@ -516,7 +546,6 @@ mod tests {
|
||||
|
||||
use proto::{Decode, MessageHead};
|
||||
use super::{Decoder, Server as S, Client as C, NoUpgrades, Http1Transaction};
|
||||
use header::{ContentLength, TransferEncoding};
|
||||
|
||||
type Server = S<NoUpgrades>;
|
||||
type Client = C<NoUpgrades>;
|
||||
@@ -552,11 +581,11 @@ mod tests {
|
||||
let expected_len = raw.len();
|
||||
let (req, len) = Server::parse(&mut raw).unwrap().unwrap();
|
||||
assert_eq!(len, expected_len);
|
||||
assert_eq!(req.subject.0, ::Method::Get);
|
||||
assert_eq!(req.subject.0, ::Method::GET);
|
||||
assert_eq!(req.subject.1, "/echo");
|
||||
assert_eq!(req.version, ::HttpVersion::Http11);
|
||||
assert_eq!(req.version, ::Version::HTTP_11);
|
||||
assert_eq!(req.headers.len(), 1);
|
||||
assert_eq!(req.headers.get_raw("Host").map(|raw| &raw[0]), Some(b"hyper.rs".as_ref()));
|
||||
assert_eq!(req.headers["Host"], "hyper.rs");
|
||||
}
|
||||
|
||||
|
||||
@@ -568,11 +597,10 @@ mod tests {
|
||||
let expected_len = raw.len();
|
||||
let (req, len) = Client::parse(&mut raw).unwrap().unwrap();
|
||||
assert_eq!(len, expected_len);
|
||||
assert_eq!(req.subject.0, 200);
|
||||
assert_eq!(req.subject.1, "OK");
|
||||
assert_eq!(req.version, ::HttpVersion::Http11);
|
||||
assert_eq!(req.subject, ::StatusCode::OK);
|
||||
assert_eq!(req.version, ::Version::HTTP_11);
|
||||
assert_eq!(req.headers.len(), 1);
|
||||
assert_eq!(req.headers.get_raw("Content-Length").map(|raw| &raw[0]), Some(b"0".as_ref()));
|
||||
assert_eq!(req.headers["Content-Length"], "0");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -581,18 +609,6 @@ mod tests {
|
||||
Server::parse(&mut raw).unwrap_err();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_raw_status() {
|
||||
let mut raw = BytesMut::from(b"HTTP/1.1 200 OK\r\n\r\n".to_vec());
|
||||
let (res, _) = Client::parse(&mut raw).unwrap().unwrap();
|
||||
assert_eq!(res.subject.1, "OK");
|
||||
|
||||
let mut raw = BytesMut::from(b"HTTP/1.1 200 Howdy\r\n\r\n".to_vec());
|
||||
let (res, _) = Client::parse(&mut raw).unwrap().unwrap();
|
||||
assert_eq!(res.subject.1, "Howdy");
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_decoder_request() {
|
||||
use super::Decoder;
|
||||
@@ -600,47 +616,49 @@ mod tests {
|
||||
let method = &mut None;
|
||||
let mut head = MessageHead::<::proto::RequestLine>::default();
|
||||
|
||||
head.subject.0 = ::Method::Get;
|
||||
head.subject.0 = ::Method::GET;
|
||||
assert_eq!(Decoder::length(0), Server::decoder(&head, method).unwrap().normal());
|
||||
assert_eq!(*method, Some(::Method::Get));
|
||||
assert_eq!(*method, Some(::Method::GET));
|
||||
|
||||
head.subject.0 = ::Method::Post;
|
||||
head.subject.0 = ::Method::POST;
|
||||
assert_eq!(Decoder::length(0), Server::decoder(&head, method).unwrap().normal());
|
||||
assert_eq!(*method, Some(::Method::Post));
|
||||
assert_eq!(*method, Some(::Method::POST));
|
||||
|
||||
head.headers.set(TransferEncoding::chunked());
|
||||
head.headers.insert("transfer-encoding", ::http::header::HeaderValue::from_static("chunked"));
|
||||
assert_eq!(Decoder::chunked(), Server::decoder(&head, method).unwrap().normal());
|
||||
// transfer-encoding and content-length = chunked
|
||||
head.headers.set(ContentLength(10));
|
||||
head.headers.insert("content-length", ::http::header::HeaderValue::from_static("10"));
|
||||
assert_eq!(Decoder::chunked(), Server::decoder(&head, method).unwrap().normal());
|
||||
|
||||
head.headers.remove::<TransferEncoding>();
|
||||
head.headers.remove("transfer-encoding");
|
||||
assert_eq!(Decoder::length(10), Server::decoder(&head, method).unwrap().normal());
|
||||
|
||||
head.headers.set_raw("Content-Length", vec![b"5".to_vec(), b"5".to_vec()]);
|
||||
head.headers.insert("content-length", ::http::header::HeaderValue::from_static("5"));
|
||||
head.headers.append("content-length", ::http::header::HeaderValue::from_static("5"));
|
||||
assert_eq!(Decoder::length(5), Server::decoder(&head, method).unwrap().normal());
|
||||
|
||||
head.headers.set_raw("Content-Length", vec![b"10".to_vec(), b"11".to_vec()]);
|
||||
head.headers.insert("content-length", ::http::header::HeaderValue::from_static("5"));
|
||||
head.headers.append("content-length", ::http::header::HeaderValue::from_static("6"));
|
||||
Server::decoder(&head, method).unwrap_err();
|
||||
|
||||
head.headers.remove::<ContentLength>();
|
||||
head.headers.remove("content-length");
|
||||
|
||||
head.headers.set_raw("Transfer-Encoding", "gzip");
|
||||
head.headers.insert("transfer-encoding", ::http::header::HeaderValue::from_static("gzip"));
|
||||
Server::decoder(&head, method).unwrap_err();
|
||||
|
||||
|
||||
// http/1.0
|
||||
head.version = ::HttpVersion::Http10;
|
||||
head.version = ::Version::HTTP_10;
|
||||
head.headers.clear();
|
||||
|
||||
// 1.0 requests can only have bodies if content-length is set
|
||||
assert_eq!(Decoder::length(0), Server::decoder(&head, method).unwrap().normal());
|
||||
|
||||
head.headers.set(TransferEncoding::chunked());
|
||||
head.headers.insert("transfer-encoding", ::http::header::HeaderValue::from_static("chunked"));
|
||||
Server::decoder(&head, method).unwrap_err();
|
||||
head.headers.remove::<TransferEncoding>();
|
||||
head.headers.remove("transfer-encoding");
|
||||
|
||||
head.headers.set(ContentLength(15));
|
||||
head.headers.insert("content-length", ::http::header::HeaderValue::from_static("15"));
|
||||
assert_eq!(Decoder::length(15), Server::decoder(&head, method).unwrap().normal());
|
||||
}
|
||||
|
||||
@@ -648,67 +666,69 @@ mod tests {
|
||||
fn test_decoder_response() {
|
||||
use super::Decoder;
|
||||
|
||||
let method = &mut Some(::Method::Get);
|
||||
let mut head = MessageHead::<::proto::RawStatus>::default();
|
||||
let method = &mut Some(::Method::GET);
|
||||
let mut head = MessageHead::<::StatusCode>::default();
|
||||
|
||||
head.subject.0 = 204;
|
||||
head.subject = ::StatusCode::from_u16(204).unwrap();
|
||||
assert_eq!(Decoder::length(0), Client::decoder(&head, method).unwrap().normal());
|
||||
head.subject.0 = 304;
|
||||
head.subject = ::StatusCode::from_u16(304).unwrap();
|
||||
assert_eq!(Decoder::length(0), Client::decoder(&head, method).unwrap().normal());
|
||||
|
||||
head.subject.0 = 200;
|
||||
head.subject = ::StatusCode::OK;
|
||||
assert_eq!(Decoder::eof(), Client::decoder(&head, method).unwrap().normal());
|
||||
|
||||
*method = Some(::Method::Head);
|
||||
*method = Some(::Method::HEAD);
|
||||
assert_eq!(Decoder::length(0), Client::decoder(&head, method).unwrap().normal());
|
||||
|
||||
*method = Some(::Method::Connect);
|
||||
*method = Some(::Method::CONNECT);
|
||||
assert_eq!(Decoder::length(0), Client::decoder(&head, method).unwrap().final_());
|
||||
|
||||
|
||||
// CONNECT receiving non 200 can have a body
|
||||
head.subject.0 = 404;
|
||||
head.headers.set(ContentLength(10));
|
||||
head.subject = ::StatusCode::NOT_FOUND;
|
||||
head.headers.insert("content-length", ::http::header::HeaderValue::from_static("10"));
|
||||
assert_eq!(Decoder::length(10), Client::decoder(&head, method).unwrap().normal());
|
||||
head.headers.remove::<ContentLength>();
|
||||
head.headers.remove("content-length");
|
||||
|
||||
|
||||
*method = Some(::Method::Get);
|
||||
head.headers.set(TransferEncoding::chunked());
|
||||
*method = Some(::Method::GET);
|
||||
head.headers.insert("transfer-encoding", ::http::header::HeaderValue::from_static("chunked"));
|
||||
assert_eq!(Decoder::chunked(), Client::decoder(&head, method).unwrap().normal());
|
||||
|
||||
// transfer-encoding and content-length = chunked
|
||||
head.headers.set(ContentLength(10));
|
||||
head.headers.insert("content-length", ::http::header::HeaderValue::from_static("10"));
|
||||
assert_eq!(Decoder::chunked(), Client::decoder(&head, method).unwrap().normal());
|
||||
|
||||
head.headers.remove::<TransferEncoding>();
|
||||
head.headers.remove("transfer-encoding");
|
||||
assert_eq!(Decoder::length(10), Client::decoder(&head, method).unwrap().normal());
|
||||
|
||||
head.headers.set_raw("Content-Length", vec![b"5".to_vec(), b"5".to_vec()]);
|
||||
head.headers.insert("content-length", ::http::header::HeaderValue::from_static("5"));
|
||||
head.headers.append("content-length", ::http::header::HeaderValue::from_static("5"));
|
||||
assert_eq!(Decoder::length(5), Client::decoder(&head, method).unwrap().normal());
|
||||
|
||||
head.headers.set_raw("Content-Length", vec![b"10".to_vec(), b"11".to_vec()]);
|
||||
head.headers.insert("content-length", ::http::header::HeaderValue::from_static("5"));
|
||||
head.headers.append("content-length", ::http::header::HeaderValue::from_static("6"));
|
||||
Client::decoder(&head, method).unwrap_err();
|
||||
head.headers.clear();
|
||||
|
||||
// 1xx status codes
|
||||
head.subject.0 = 100;
|
||||
head.subject = ::StatusCode::CONTINUE;
|
||||
Client::decoder(&head, method).unwrap().ignore();
|
||||
|
||||
head.subject.0 = 103;
|
||||
head.subject = ::StatusCode::from_u16(103).unwrap();
|
||||
Client::decoder(&head, method).unwrap().ignore();
|
||||
|
||||
// 101 upgrade not supported yet
|
||||
head.subject.0 = 101;
|
||||
head.subject = ::StatusCode::SWITCHING_PROTOCOLS;
|
||||
Client::decoder(&head, method).unwrap_err();
|
||||
head.subject.0 = 200;
|
||||
head.subject = ::StatusCode::OK;
|
||||
|
||||
// http/1.0
|
||||
head.version = ::HttpVersion::Http10;
|
||||
head.version = ::Version::HTTP_10;
|
||||
|
||||
assert_eq!(Decoder::eof(), Client::decoder(&head, method).unwrap().normal());
|
||||
|
||||
head.headers.set(TransferEncoding::chunked());
|
||||
head.headers.insert("transfer-encoding", ::http::header::HeaderValue::from_static("chunked"));
|
||||
Client::decoder(&head, method).unwrap_err();
|
||||
}
|
||||
|
||||
@@ -757,23 +777,19 @@ mod tests {
|
||||
#[cfg(feature = "nightly")]
|
||||
#[bench]
|
||||
fn bench_server_transaction_encode(b: &mut Bencher) {
|
||||
use header::{Headers, ContentLength, ContentType};
|
||||
use ::{StatusCode, HttpVersion};
|
||||
use http::header::HeaderValue;
|
||||
use proto::BodyLength;
|
||||
|
||||
let len = 108;
|
||||
b.bytes = len as u64;
|
||||
|
||||
let mut head = MessageHead {
|
||||
subject: StatusCode::Ok,
|
||||
headers: Headers::new(),
|
||||
version: HttpVersion::Http11,
|
||||
};
|
||||
head.headers.set(ContentLength(10));
|
||||
head.headers.set(ContentType::json());
|
||||
let mut head = MessageHead::default();
|
||||
head.headers.insert("content-length", HeaderValue::from_static("10"));
|
||||
head.headers.insert("content-type", HeaderValue::from_static("application/json"));
|
||||
|
||||
b.iter(|| {
|
||||
let mut vec = Vec::new();
|
||||
Server::encode(head.clone(), true, &mut None, &mut vec).unwrap();
|
||||
Server::encode(head.clone(), Some(BodyLength::Known(10)), &mut None, &mut vec).unwrap();
|
||||
assert_eq!(vec.len(), len);
|
||||
::test::black_box(vec);
|
||||
})
|
||||
|
||||
130
src/proto/mod.rs
130
src/proto/mod.rs
@@ -1,16 +1,8 @@
|
||||
//! Pieces pertaining to the HTTP message protocol.
|
||||
use std::borrow::Cow;
|
||||
use std::fmt;
|
||||
|
||||
use bytes::BytesMut;
|
||||
use http::{HeaderMap, Method, StatusCode, Uri, Version};
|
||||
|
||||
use header::{Connection, ConnectionOption, Expect};
|
||||
use header::Headers;
|
||||
use method::Method;
|
||||
use status::StatusCode;
|
||||
use uri::Uri;
|
||||
use version::HttpVersion;
|
||||
use version::HttpVersion::{Http10, Http11};
|
||||
use headers;
|
||||
|
||||
pub use self::body::Body;
|
||||
pub use self::chunk::Chunk;
|
||||
@@ -20,19 +12,17 @@ mod body;
|
||||
mod chunk;
|
||||
mod h1;
|
||||
//mod h2;
|
||||
pub mod request;
|
||||
pub mod response;
|
||||
|
||||
|
||||
/// An Incoming Message head. Includes request/status line, and headers.
|
||||
#[derive(Clone, Debug, Default, PartialEq)]
|
||||
pub struct MessageHead<S> {
|
||||
/// HTTP version of the message.
|
||||
pub version: HttpVersion,
|
||||
pub version: Version,
|
||||
/// Subject (request line or status line) of Incoming message.
|
||||
pub subject: S,
|
||||
/// Headers of the Incoming message.
|
||||
pub headers: Headers
|
||||
pub headers: HeaderMap,
|
||||
}
|
||||
|
||||
/// An incoming request message.
|
||||
@@ -41,14 +31,8 @@ pub type RequestHead = MessageHead<RequestLine>;
|
||||
#[derive(Debug, Default, PartialEq)]
|
||||
pub struct RequestLine(pub Method, pub Uri);
|
||||
|
||||
impl fmt::Display for RequestLine {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{} {}", self.0, self.1)
|
||||
}
|
||||
}
|
||||
|
||||
/// An incoming response message.
|
||||
pub type ResponseHead = MessageHead<RawStatus>;
|
||||
pub type ResponseHead = MessageHead<StatusCode>;
|
||||
|
||||
impl<S> MessageHead<S> {
|
||||
pub fn should_keep_alive(&self) -> bool {
|
||||
@@ -60,76 +44,20 @@ impl<S> MessageHead<S> {
|
||||
}
|
||||
}
|
||||
|
||||
impl ResponseHead {
|
||||
/// Converts this head's RawStatus into a StatusCode.
|
||||
#[inline]
|
||||
pub fn status(&self) -> StatusCode {
|
||||
self.subject.status()
|
||||
}
|
||||
}
|
||||
|
||||
/// The raw status code and reason-phrase.
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
pub struct RawStatus(pub u16, pub Cow<'static, str>);
|
||||
|
||||
impl RawStatus {
|
||||
/// Converts this into a StatusCode.
|
||||
#[inline]
|
||||
pub fn status(&self) -> StatusCode {
|
||||
StatusCode::try_from(self.0).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for RawStatus {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{} {}", self.0, self.1)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<StatusCode> for RawStatus {
|
||||
fn from(status: StatusCode) -> RawStatus {
|
||||
RawStatus(status.into(), Cow::Borrowed(status.canonical_reason().unwrap_or("")))
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for RawStatus {
|
||||
fn default() -> RawStatus {
|
||||
RawStatus(200, Cow::Borrowed("OK"))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<MessageHead<::StatusCode>> for MessageHead<RawStatus> {
|
||||
fn from(head: MessageHead<::StatusCode>) -> MessageHead<RawStatus> {
|
||||
MessageHead {
|
||||
subject: head.subject.into(),
|
||||
version: head.version,
|
||||
headers: head.headers,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if a connection should be kept alive.
|
||||
#[inline]
|
||||
pub fn should_keep_alive(version: HttpVersion, headers: &Headers) -> bool {
|
||||
let ret = match (version, headers.get::<Connection>()) {
|
||||
(Http10, None) => false,
|
||||
(Http10, Some(conn)) if !conn.contains(&ConnectionOption::KeepAlive) => false,
|
||||
(Http11, Some(conn)) if conn.contains(&ConnectionOption::Close) => false,
|
||||
_ => true
|
||||
};
|
||||
trace!("should_keep_alive(version={:?}, header={:?}) = {:?}", version, headers.get::<Connection>(), ret);
|
||||
ret
|
||||
pub fn should_keep_alive(version: Version, headers: &HeaderMap) -> bool {
|
||||
if version == Version::HTTP_10 {
|
||||
headers::connection_keep_alive(headers)
|
||||
} else {
|
||||
!headers::connection_close(headers)
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if a connection is expecting a `100 Continue` before sending its body.
|
||||
#[inline]
|
||||
pub fn expecting_continue(version: HttpVersion, headers: &Headers) -> bool {
|
||||
let ret = match (version, headers.get::<Expect>()) {
|
||||
(Http11, Some(&Expect::Continue)) => true,
|
||||
_ => false
|
||||
};
|
||||
trace!("expecting_continue(version={:?}, header={:?}) = {:?}", version, headers.get::<Expect>(), ret);
|
||||
ret
|
||||
pub fn expecting_continue(version: Version, headers: &HeaderMap) -> bool {
|
||||
version == Version::HTTP_11 && headers::expect_continue(headers)
|
||||
}
|
||||
|
||||
pub type ServerTransaction = h1::role::Server<h1::role::YesUpgrades>;
|
||||
@@ -143,7 +71,7 @@ pub trait Http1Transaction {
|
||||
type Incoming;
|
||||
type Outgoing: Default;
|
||||
fn parse(bytes: &mut BytesMut) -> ParseResult<Self::Incoming>;
|
||||
fn decoder(head: &MessageHead<Self::Incoming>, method: &mut Option<::Method>) -> ::Result<Decode>;
|
||||
fn decoder(head: &MessageHead<Self::Incoming>, method: &mut Option<Method>) -> ::Result<Decode>;
|
||||
fn encode(head: MessageHead<Self::Outgoing>, has_body: bool, method: &mut Option<Method>, dst: &mut Vec<u8>) -> ::Result<h1::Encoder>;
|
||||
fn on_error(err: &::Error) -> Option<MessageHead<Self::Outgoing>>;
|
||||
|
||||
@@ -165,28 +93,28 @@ pub enum Decode {
|
||||
|
||||
#[test]
|
||||
fn test_should_keep_alive() {
|
||||
let mut headers = Headers::new();
|
||||
let mut headers = HeaderMap::new();
|
||||
|
||||
assert!(!should_keep_alive(Http10, &headers));
|
||||
assert!(should_keep_alive(Http11, &headers));
|
||||
assert!(!should_keep_alive(Version::HTTP_10, &headers));
|
||||
assert!(should_keep_alive(Version::HTTP_11, &headers));
|
||||
|
||||
headers.set(Connection::close());
|
||||
assert!(!should_keep_alive(Http10, &headers));
|
||||
assert!(!should_keep_alive(Http11, &headers));
|
||||
headers.insert("connection", ::http::header::HeaderValue::from_static("close"));
|
||||
assert!(!should_keep_alive(Version::HTTP_10, &headers));
|
||||
assert!(!should_keep_alive(Version::HTTP_11, &headers));
|
||||
|
||||
headers.set(Connection::keep_alive());
|
||||
assert!(should_keep_alive(Http10, &headers));
|
||||
assert!(should_keep_alive(Http11, &headers));
|
||||
headers.insert("connection", ::http::header::HeaderValue::from_static("keep-alive"));
|
||||
assert!(should_keep_alive(Version::HTTP_10, &headers));
|
||||
assert!(should_keep_alive(Version::HTTP_11, &headers));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_expecting_continue() {
|
||||
let mut headers = Headers::new();
|
||||
let mut headers = HeaderMap::new();
|
||||
|
||||
assert!(!expecting_continue(Http10, &headers));
|
||||
assert!(!expecting_continue(Http11, &headers));
|
||||
assert!(!expecting_continue(Version::HTTP_10, &headers));
|
||||
assert!(!expecting_continue(Version::HTTP_11, &headers));
|
||||
|
||||
headers.set(Expect::Continue);
|
||||
assert!(!expecting_continue(Http10, &headers));
|
||||
assert!(expecting_continue(Http11, &headers));
|
||||
headers.insert("expect", ::http::header::HeaderValue::from_static("100-continue"));
|
||||
assert!(!expecting_continue(Version::HTTP_10, &headers));
|
||||
assert!(expecting_continue(Version::HTTP_11, &headers));
|
||||
}
|
||||
|
||||
@@ -1,322 +0,0 @@
|
||||
use std::fmt;
|
||||
#[cfg(feature = "compat")]
|
||||
use std::mem::replace;
|
||||
use std::net::SocketAddr;
|
||||
|
||||
#[cfg(feature = "compat")]
|
||||
use http;
|
||||
|
||||
use header::Headers;
|
||||
use proto::{Body, MessageHead, RequestHead, RequestLine};
|
||||
use method::Method;
|
||||
use uri::{self, Uri};
|
||||
use version::HttpVersion;
|
||||
|
||||
/// An HTTP Request
|
||||
pub struct Request<B = Body> {
|
||||
method: Method,
|
||||
uri: Uri,
|
||||
version: HttpVersion,
|
||||
headers: Headers,
|
||||
body: Option<B>,
|
||||
is_proxy: bool,
|
||||
remote_addr: Option<SocketAddr>,
|
||||
}
|
||||
|
||||
impl<B> Request<B> {
|
||||
/// Construct a new Request.
|
||||
#[inline]
|
||||
pub fn new(method: Method, uri: Uri) -> Request<B> {
|
||||
Request {
|
||||
method: method,
|
||||
uri: uri,
|
||||
version: HttpVersion::default(),
|
||||
headers: Headers::new(),
|
||||
body: None,
|
||||
is_proxy: false,
|
||||
remote_addr: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Read the Request Uri.
|
||||
#[inline]
|
||||
pub fn uri(&self) -> &Uri { &self.uri }
|
||||
|
||||
/// Read the Request Version.
|
||||
#[inline]
|
||||
pub fn version(&self) -> HttpVersion { self.version }
|
||||
|
||||
/// Read the Request headers.
|
||||
#[inline]
|
||||
pub fn headers(&self) -> &Headers { &self.headers }
|
||||
|
||||
/// Read the Request method.
|
||||
#[inline]
|
||||
pub fn method(&self) -> &Method { &self.method }
|
||||
|
||||
/// Read the Request body.
|
||||
#[inline]
|
||||
pub fn body_ref(&self) -> Option<&B> { self.body.as_ref() }
|
||||
|
||||
/// Get a mutable reference to the Request body.
|
||||
#[inline]
|
||||
pub fn body_mut(&mut self) -> &mut Option<B> { &mut self.body }
|
||||
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
#[deprecated(since="0.11.12", note="This method will be gone in future versions.")]
|
||||
pub fn remote_addr(&self) -> Option<SocketAddr> { self.remote_addr }
|
||||
|
||||
/// The target path of this Request.
|
||||
#[inline]
|
||||
pub fn path(&self) -> &str {
|
||||
self.uri.path()
|
||||
}
|
||||
|
||||
/// The query string of this Request.
|
||||
#[inline]
|
||||
pub fn query(&self) -> Option<&str> {
|
||||
self.uri.query()
|
||||
}
|
||||
|
||||
/// Set the Method of this request.
|
||||
#[inline]
|
||||
pub fn set_method(&mut self, method: Method) { self.method = method; }
|
||||
|
||||
/// Get a mutable reference to the Request headers.
|
||||
#[inline]
|
||||
pub fn headers_mut(&mut self) -> &mut Headers { &mut self.headers }
|
||||
|
||||
/// Set the `Uri` of this request.
|
||||
#[inline]
|
||||
pub fn set_uri(&mut self, uri: Uri) { self.uri = uri; }
|
||||
|
||||
/// Set the `HttpVersion` of this request.
|
||||
#[inline]
|
||||
pub fn set_version(&mut self, version: HttpVersion) { self.version = version; }
|
||||
|
||||
/// Set the body of the request.
|
||||
///
|
||||
/// By default, the body will be sent using `Transfer-Encoding: chunked`. To
|
||||
/// override this behavior, manually set a [`ContentLength`] header with the
|
||||
/// length of `body`.
|
||||
#[inline]
|
||||
pub fn set_body<T: Into<B>>(&mut self, body: T) { self.body = Some(body.into()); }
|
||||
|
||||
/// Set that the URI should use the absolute form.
|
||||
///
|
||||
/// This is only needed when talking to HTTP/1 proxies to URLs not
|
||||
/// protected by TLS.
|
||||
#[inline]
|
||||
pub fn set_proxy(&mut self, is_proxy: bool) { self.is_proxy = is_proxy; }
|
||||
|
||||
pub(crate) fn is_proxy(&self) -> bool { self.is_proxy }
|
||||
}
|
||||
|
||||
impl Request<Body> {
|
||||
/// Deconstruct this Request into its pieces.
|
||||
///
|
||||
/// Modifying these pieces will have no effect on how hyper behaves.
|
||||
#[inline]
|
||||
pub fn deconstruct(self) -> (Method, Uri, HttpVersion, Headers, Body) {
|
||||
(self.method, self.uri, self.version, self.headers, self.body.unwrap_or_default())
|
||||
}
|
||||
|
||||
/// Take the Request body.
|
||||
#[inline]
|
||||
pub fn body(self) -> Body { self.body.unwrap_or_default() }
|
||||
}
|
||||
|
||||
impl<B> fmt::Debug for Request<B> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("Request")
|
||||
.field("method", &self.method)
|
||||
.field("uri", &self.uri)
|
||||
.field("version", &self.version)
|
||||
.field("remote_addr", &self.remote_addr)
|
||||
.field("headers", &self.headers)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "compat")]
|
||||
impl From<Request> for http::Request<Body> {
|
||||
fn from(from_req: Request) -> http::Request<Body> {
|
||||
let (m, u, v, h, b) = from_req.deconstruct();
|
||||
|
||||
let to_req = http::Request::new(());
|
||||
let (mut to_parts, _) = to_req.into_parts();
|
||||
|
||||
to_parts.method = m.into();
|
||||
to_parts.uri = u.into();
|
||||
to_parts.version = v.into();
|
||||
to_parts.headers = h.into();
|
||||
|
||||
http::Request::from_parts(to_parts, b)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "compat")]
|
||||
impl<B> From<http::Request<B>> for Request<B> {
|
||||
fn from(from_req: http::Request<B>) -> Request<B> {
|
||||
let (from_parts, body) = from_req.into_parts();
|
||||
|
||||
let mut to_req = Request::new(from_parts.method.into(), from_parts.uri.into());
|
||||
to_req.set_version(from_parts.version.into());
|
||||
replace(to_req.headers_mut(), from_parts.headers.into());
|
||||
to_req.set_body(body);
|
||||
to_req
|
||||
}
|
||||
}
|
||||
|
||||
/// Constructs a request using a received ResponseHead and optional body
|
||||
pub fn from_wire(addr: Option<SocketAddr>, incoming: RequestHead, body: Option<Body>) -> Request<Body> {
|
||||
Request {
|
||||
remote_addr: addr,
|
||||
..join((incoming, body))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn split<B>(req: Request<B>) -> (RequestHead, Option<B>) {
|
||||
let uri = if req.is_proxy {
|
||||
req.uri
|
||||
} else {
|
||||
uri::origin_form(&req.uri)
|
||||
};
|
||||
let head = RequestHead {
|
||||
subject: ::proto::RequestLine(req.method, uri),
|
||||
headers: req.headers,
|
||||
version: req.version,
|
||||
};
|
||||
(head, req.body)
|
||||
}
|
||||
|
||||
pub fn join<B>((head, body): (RequestHead, Option<B>)) -> Request<B> {
|
||||
let MessageHead { version, subject: RequestLine(method, uri), headers } = head;
|
||||
|
||||
Request {
|
||||
method: method,
|
||||
uri: uri,
|
||||
headers: headers,
|
||||
version: version,
|
||||
remote_addr: None,
|
||||
body: body,
|
||||
is_proxy: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn addr<B>(req: &mut Request<B>, addr: SocketAddr) {
|
||||
req.remote_addr = Some(addr);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
/*
|
||||
use std::io::Write;
|
||||
use std::str::from_utf8;
|
||||
use Url;
|
||||
use method::Method::{Get, Head, Post};
|
||||
use mock::{MockStream, MockConnector};
|
||||
use net::Fresh;
|
||||
use header::{ContentLength,TransferEncoding,Encoding};
|
||||
use url::form_urlencoded;
|
||||
use super::Request;
|
||||
use http::h1::Http11Message;
|
||||
|
||||
fn run_request(req: Request<Fresh>) -> Vec<u8> {
|
||||
let req = req.start().unwrap();
|
||||
let message = req.message;
|
||||
let mut message = message.downcast::<Http11Message>().ok().unwrap();
|
||||
message.flush_outgoing().unwrap();
|
||||
let stream = *message
|
||||
.into_inner().downcast::<MockStream>().ok().unwrap();
|
||||
stream.write
|
||||
}
|
||||
|
||||
fn assert_no_body(s: &str) {
|
||||
assert!(!s.contains("Content-Length:"));
|
||||
assert!(!s.contains("Transfer-Encoding:"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_empty_body() {
|
||||
let req = Request::with_connector(
|
||||
Get, Url::parse("http://example.dom").unwrap(), &mut MockConnector
|
||||
).unwrap();
|
||||
let bytes = run_request(req);
|
||||
let s = from_utf8(&bytes[..]).unwrap();
|
||||
assert_no_body(s);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_head_empty_body() {
|
||||
let req = Request::with_connector(
|
||||
Head, Url::parse("http://example.dom").unwrap(), &mut MockConnector
|
||||
).unwrap();
|
||||
let bytes = run_request(req);
|
||||
let s = from_utf8(&bytes[..]).unwrap();
|
||||
assert_no_body(s);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_url_query() {
|
||||
let url = Url::parse("http://example.dom?q=value").unwrap();
|
||||
let req = Request::with_connector(
|
||||
Get, url, &mut MockConnector
|
||||
).unwrap();
|
||||
let bytes = run_request(req);
|
||||
let s = from_utf8(&bytes[..]).unwrap();
|
||||
assert!(s.contains("?q=value"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_post_content_length() {
|
||||
let url = Url::parse("http://example.dom").unwrap();
|
||||
let mut req = Request::with_connector(
|
||||
Post, url, &mut MockConnector
|
||||
).unwrap();
|
||||
let mut body = String::new();
|
||||
form_urlencoded::Serializer::new(&mut body).append_pair("q", "value");
|
||||
req.headers_mut().set(ContentLength(body.len() as u64));
|
||||
let bytes = run_request(req);
|
||||
let s = from_utf8(&bytes[..]).unwrap();
|
||||
assert!(s.contains("Content-Length:"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_post_chunked() {
|
||||
let url = Url::parse("http://example.dom").unwrap();
|
||||
let req = Request::with_connector(
|
||||
Post, url, &mut MockConnector
|
||||
).unwrap();
|
||||
let bytes = run_request(req);
|
||||
let s = from_utf8(&bytes[..]).unwrap();
|
||||
assert!(!s.contains("Content-Length:"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_host_header() {
|
||||
let url = Url::parse("http://example.dom").unwrap();
|
||||
let req = Request::with_connector(
|
||||
Get, url, &mut MockConnector
|
||||
).unwrap();
|
||||
let bytes = run_request(req);
|
||||
let s = from_utf8(&bytes[..]).unwrap();
|
||||
assert!(s.contains("Host: example.dom"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_proxy() {
|
||||
let url = Url::parse("http://example.dom").unwrap();
|
||||
let mut req = Request::with_connector(
|
||||
Get, url, &mut MockConnector
|
||||
).unwrap();
|
||||
req.message.set_proxied(true);
|
||||
let bytes = run_request(req);
|
||||
let s = from_utf8(&bytes[..]).unwrap();
|
||||
let request_line = "GET http://example.dom/ HTTP/1.1";
|
||||
assert_eq!(&s[..request_line.len()], request_line);
|
||||
assert!(s.contains("Host: example.dom"));
|
||||
}
|
||||
*/
|
||||
}
|
||||
@@ -1,212 +0,0 @@
|
||||
use std::fmt;
|
||||
#[cfg(feature = "compat")]
|
||||
use std::mem::replace;
|
||||
|
||||
#[cfg(feature = "compat")]
|
||||
use http;
|
||||
|
||||
use header::{Header, Headers};
|
||||
use proto::{MessageHead, ResponseHead, Body};
|
||||
use status::StatusCode;
|
||||
use version::HttpVersion;
|
||||
|
||||
/// An HTTP Response
|
||||
pub struct Response<B = Body> {
|
||||
version: HttpVersion,
|
||||
headers: Headers,
|
||||
status: StatusCode,
|
||||
#[cfg(feature = "raw_status")]
|
||||
raw_status: ::proto::RawStatus,
|
||||
body: Option<B>,
|
||||
}
|
||||
|
||||
impl<B> Response<B> {
|
||||
/// Constructs a default response
|
||||
#[inline]
|
||||
pub fn new() -> Response<B> {
|
||||
Response::default()
|
||||
}
|
||||
|
||||
/// Get the HTTP version of this response.
|
||||
#[inline]
|
||||
pub fn version(&self) -> HttpVersion { self.version }
|
||||
|
||||
/// Get the headers from the response.
|
||||
#[inline]
|
||||
pub fn headers(&self) -> &Headers { &self.headers }
|
||||
|
||||
/// Get a mutable reference to the headers.
|
||||
#[inline]
|
||||
pub fn headers_mut(&mut self) -> &mut Headers { &mut self.headers }
|
||||
|
||||
/// Get the status from the server.
|
||||
#[inline]
|
||||
pub fn status(&self) -> StatusCode { self.status }
|
||||
|
||||
/// Get the raw status code and reason.
|
||||
///
|
||||
/// This method is only useful when inspecting the raw subject line from
|
||||
/// a received response.
|
||||
#[inline]
|
||||
#[cfg(feature = "raw_status")]
|
||||
pub fn status_raw(&self) -> &::proto::RawStatus { &self.raw_status }
|
||||
|
||||
/// Set the `StatusCode` for this response.
|
||||
#[inline]
|
||||
pub fn set_status(&mut self, status: StatusCode) {
|
||||
self.status = status;
|
||||
}
|
||||
|
||||
/// Set the status and move the Response.
|
||||
///
|
||||
/// Useful for the "builder-style" pattern.
|
||||
#[inline]
|
||||
pub fn with_status(mut self, status: StatusCode) -> Self {
|
||||
self.set_status(status);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set a header and move the Response.
|
||||
///
|
||||
/// Useful for the "builder-style" pattern.
|
||||
#[inline]
|
||||
pub fn with_header<H: Header>(mut self, header: H) -> Self {
|
||||
self.headers.set(header);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the headers and move the Response.
|
||||
///
|
||||
/// Useful for the "builder-style" pattern.
|
||||
#[inline]
|
||||
pub fn with_headers(mut self, headers: Headers) -> Self {
|
||||
self.headers = headers;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the body.
|
||||
#[inline]
|
||||
pub fn set_body<T: Into<B>>(&mut self, body: T) {
|
||||
self.body = Some(body.into());
|
||||
}
|
||||
|
||||
/// Set the body and move the Response.
|
||||
///
|
||||
/// Useful for the "builder-style" pattern.
|
||||
#[inline]
|
||||
pub fn with_body<T: Into<B>>(mut self, body: T) -> Self {
|
||||
self.set_body(body);
|
||||
self
|
||||
}
|
||||
|
||||
/// Read the body.
|
||||
#[inline]
|
||||
pub fn body_ref(&self) -> Option<&B> { self.body.as_ref() }
|
||||
}
|
||||
|
||||
impl Response<Body> {
|
||||
/// Take the `Body` of this response.
|
||||
#[inline]
|
||||
pub fn body(self) -> Body {
|
||||
self.body.unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "raw_status"))]
|
||||
impl<B> Default for Response<B> {
|
||||
fn default() -> Response<B> {
|
||||
Response::<B> {
|
||||
version: Default::default(),
|
||||
headers: Default::default(),
|
||||
status: Default::default(),
|
||||
body: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "raw_status")]
|
||||
impl<B> Default for Response<B> {
|
||||
fn default() -> Response<B> {
|
||||
Response::<B> {
|
||||
version: Default::default(),
|
||||
headers: Default::default(),
|
||||
status: Default::default(),
|
||||
raw_status: Default::default(),
|
||||
body: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Response {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("Response")
|
||||
.field("status", &self.status)
|
||||
.field("version", &self.version)
|
||||
.field("headers", &self.headers)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "compat")]
|
||||
impl<B> From<http::Response<B>> for Response<B> {
|
||||
fn from(from_res: http::Response<B>) -> Response<B> {
|
||||
let (from_parts, body) = from_res.into_parts();
|
||||
let mut to_res = Response::new();
|
||||
to_res.version = from_parts.version.into();
|
||||
to_res.set_status(from_parts.status.into());
|
||||
replace(to_res.headers_mut(), from_parts.headers.into());
|
||||
to_res.with_body(body)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "compat")]
|
||||
impl From<Response> for http::Response<Body> {
|
||||
fn from(mut from_res: Response) -> http::Response<Body> {
|
||||
let (mut to_parts, ()) = http::Response::new(()).into_parts();
|
||||
to_parts.version = from_res.version().into();
|
||||
to_parts.status = from_res.status().into();
|
||||
let from_headers = replace(from_res.headers_mut(), Headers::new());
|
||||
to_parts.headers = from_headers.into();
|
||||
http::Response::from_parts(to_parts, from_res.body())
|
||||
}
|
||||
}
|
||||
|
||||
/// Constructs a response using a received ResponseHead and optional body
|
||||
#[inline]
|
||||
#[cfg(not(feature = "raw_status"))]
|
||||
pub fn from_wire<B>(incoming: ResponseHead, body: Option<B>) -> Response<B> {
|
||||
let status = incoming.status();
|
||||
|
||||
Response::<B> {
|
||||
status: status,
|
||||
version: incoming.version,
|
||||
headers: incoming.headers,
|
||||
body: body,
|
||||
}
|
||||
}
|
||||
|
||||
/// Constructs a response using a received ResponseHead and optional body
|
||||
#[inline]
|
||||
#[cfg(feature = "raw_status")]
|
||||
pub fn from_wire<B>(incoming: ResponseHead, body: Option<B>) -> Response<B> {
|
||||
let status = incoming.status();
|
||||
|
||||
Response::<B> {
|
||||
status: status,
|
||||
version: incoming.version,
|
||||
headers: incoming.headers,
|
||||
raw_status: incoming.subject,
|
||||
body: body,
|
||||
}
|
||||
}
|
||||
|
||||
/// Splits this response into a `MessageHead<StatusCode>` and its body
|
||||
#[inline]
|
||||
pub fn split<B>(res: Response<B>) -> (MessageHead<StatusCode>, Option<B>) {
|
||||
let head = MessageHead::<StatusCode> {
|
||||
version: res.version,
|
||||
headers: res.headers,
|
||||
subject: res.status
|
||||
};
|
||||
(head, res.body)
|
||||
}
|
||||
Reference in New Issue
Block a user