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