From 864d3e27a437d2274f91201cda495887a8ba27e5 Mon Sep 17 00:00:00 2001 From: Nick Gonzales Date: Mon, 1 May 2017 12:19:55 -0600 Subject: [PATCH] refactor(http): merge Request and Response from server and client Request and Response are now visible from: - hyper::{Request, Response} - hyper::server::{Request, Response} - hyper::client::{Request, Response} They truly exist in the http module, but are re-exported to reduce the number of breaking changes. request::new and response::new were renamed to ::from_wire to reduce confusion with Request::new and Response::new. See issue #1126 Request now has an optional Body, because not all requests have bodies. Use body_ref() to determine if a body exists. Use body() to take the body, or construct one if no body exists. Closes #1155 BREAKING CHANGE: Response::body() now consumes the response --- src/client/mod.rs | 12 +-- src/client/response.rs | 65 -------------- src/http/body.rs | 7 +- src/http/mod.rs | 2 + src/{client => http}/request.rs | 75 +++++++++++++++- src/http/response.rs | 152 ++++++++++++++++++++++++++++++++ src/lib.rs | 2 + src/server/mod.rs | 13 ++- src/server/request.rs | 113 ------------------------ src/server/response.rs | 111 ----------------------- tests/server.rs | 4 +- 11 files changed, 242 insertions(+), 314 deletions(-) delete mode 100644 src/client/response.rs rename src/{client => http}/request.rs (74%) create mode 100644 src/http/response.rs delete mode 100644 src/server/request.rs delete mode 100644 src/server/response.rs diff --git a/src/client/mod.rs b/src/client/mod.rs index d8cec11c..2ea07e6a 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -22,19 +22,19 @@ pub use tokio_service::Service; use header::{Headers, Host}; use http::{self, TokioBody}; +use http::response; +use http::request; use method::Method; use self::pool::{Pool, Pooled}; use uri::{self, Uri}; +pub use http::response::Response; +pub use http::request::Request; pub use self::connect::{HttpConnector, Connect}; -pub use self::request::Request; -pub use self::response::Response; mod connect; mod dns; mod pool; -mod request; -mod response; /// A Client to make outgoing HTTP requests. // If the Connector is clone, then the Client can be clone easily. @@ -198,8 +198,8 @@ where C: Connect, }); FutureResponse(Box::new(req.map(|msg| { match msg { - Message::WithoutBody(head) => response::new(head, None), - Message::WithBody(head, body) => response::new(head, Some(body.into())), + Message::WithoutBody(head) => response::from_wire(head, None), + Message::WithBody(head, body) => response::from_wire(head, Some(body.into())), } }))) } diff --git a/src/client/response.rs b/src/client/response.rs deleted file mode 100644 index 3db0ae13..00000000 --- a/src/client/response.rs +++ /dev/null @@ -1,65 +0,0 @@ -use std::fmt; - -use header; -use http::{self, RawStatus, Body}; -use status; -use version; - -pub fn new(incoming: http::ResponseHead, body: Option) -> Response { - trace!("Response::new"); - let status = status::StatusCode::from_u16(incoming.subject.0); - debug!("version={:?}, status={:?}", incoming.version, status); - debug!("headers={:?}", incoming.headers); - - Response { - status: status, - version: incoming.version, - headers: incoming.headers, - status_raw: incoming.subject, - body: body, - } - -} - -/// A response for a client request to a remote server. -pub struct Response { - status: status::StatusCode, - headers: header::Headers, - version: version::HttpVersion, - status_raw: RawStatus, - body: Option, -} - -impl Response { - /// Get the headers from the server. - #[inline] - pub fn headers(&self) -> &header::Headers { &self.headers } - - /// Get the status from the server. - #[inline] - pub fn status(&self) -> status::StatusCode { self.status } - - /// Get the raw status code and reason. - #[inline] - pub fn status_raw(&self) -> &RawStatus { &self.status_raw } - - /// Get the HTTP version of this response from the server. - #[inline] - pub fn version(&self) -> version::HttpVersion { self.version } - - /// Take the `Body` of this response. - #[inline] - pub fn body(mut self) -> Body { - self.body.take().unwrap_or(Body::empty()) - } -} - -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() - } -} diff --git a/src/http/body.rs b/src/http/body.rs index 0ece860e..e454b22b 100644 --- a/src/http/body.rs +++ b/src/http/body.rs @@ -34,12 +34,6 @@ impl Default for Body { } } -impl Default for Body { - fn default() -> Body { - Body::empty() - } -} - impl Stream for Body { type Item = Chunk; type Error = ::Error; @@ -114,6 +108,7 @@ impl From<&'static str> for Body { } impl From> for Body { + #[inline] fn from (body: Option) -> Body { body.unwrap_or_default() } diff --git a/src/http/mod.rs b/src/http/mod.rs index a38f5393..fab8ac75 100644 --- a/src/http/mod.rs +++ b/src/http/mod.rs @@ -24,6 +24,8 @@ mod io; mod h1; //mod h2; mod str; +pub mod request; +pub mod response; /* macro_rules! nonblocking { diff --git a/src/client/request.rs b/src/http/request.rs similarity index 74% rename from src/client/request.rs rename to src/http/request.rs index a17e614f..d92f1556 100644 --- a/src/client/request.rs +++ b/src/http/request.rs @@ -1,10 +1,11 @@ use std::fmt; use header::Headers; -use http::{Body, RequestHead}; +use http::{Body, MessageHead, RequestHead, RequestLine}; use method::Method; use uri::{self, Uri}; use version::HttpVersion; +use std::net::SocketAddr; /// A client request to a remote server. pub struct Request { @@ -14,6 +15,7 @@ pub struct Request { headers: Headers, body: Option, is_proxy: bool, + remote_addr: Option, } impl Request { @@ -27,13 +29,14 @@ impl Request { 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 } @@ -48,8 +51,29 @@ impl Request { /// Read the Request body. #[inline] - pub fn body(&self) -> Option<&B> { self.body.as_ref() } - + pub fn body_ref(&self) -> Option<&B> { self.body.as_ref() } + + /// The remote socket address of this request + /// + /// This is an `Option`, because some underlying transports may not have + /// a socket address, such as Unix Sockets. + /// + /// This field is not used for outgoing requests. + #[inline] + pub fn remote_addr(&self) -> Option { 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; } @@ -78,17 +102,60 @@ impl Request { pub fn set_proxy(&mut self, is_proxy: bool) { self.is_proxy = is_proxy; } } +impl Request { + /// 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 fmt::Debug for Request { 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() } } +struct MaybeAddr<'a>(&'a Option); + +impl<'a> fmt::Display for MaybeAddr<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self.0 { + Some(ref addr) => fmt::Display::fmt(addr, f), + None => f.write_str("None"), + } + } +} + +/// Constructs a request using a received ResponseHead and optional body +pub fn from_wire(addr: Option, incoming: RequestHead, body: B) -> Request { + let MessageHead { version, subject: RequestLine(method, uri), headers } = incoming; + debug!("Request::new: addr={}, req=\"{} {} {}\"", MaybeAddr(&addr), method, uri, version); + debug!("Request::new: headers={:?}", headers); + + Request:: { + method: method, + uri: uri, + headers: headers, + version: version, + remote_addr: addr, + body: Some(body), + is_proxy: false, + } +} + pub fn split(req: Request) -> (RequestHead, Option) { let uri = if req.is_proxy { req.uri diff --git a/src/http/response.rs b/src/http/response.rs new file mode 100644 index 00000000..4f1dd7fd --- /dev/null +++ b/src/http/response.rs @@ -0,0 +1,152 @@ +use std::fmt; + +use header::{Header, Headers}; +use http::{MessageHead, ResponseHead, Body, RawStatus}; +use status::StatusCode; +use version::HttpVersion; + +/// A response for a client request to a remote server. +pub struct Response { + version: HttpVersion, + headers: Headers, + status: StatusCode, + raw_status: RawStatus, + body: Option, +} + +impl Response { + /// Constructs a default response + #[inline] + pub fn new() -> Response { + 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] + pub fn status_raw(&self) -> &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(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>(&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>(mut self, body: T) -> Self { + self.set_body(body); + self + } +} + +impl Response { + /// Take the `Body` of this response. + #[inline] + pub fn body(self) -> Body { + self.body.unwrap_or_default() + } +} + +impl Default for Response { + fn default() -> Response { + Response:: { + 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() + } +} + +/// Constructs a response using a received ResponseHead and optional body +#[inline] +pub fn from_wire(incoming: ResponseHead, body: Option) -> Response { + let status = incoming.status(); + trace!("Response::new"); + debug!("version={:?}, status={:?}", incoming.version, status); + debug!("headers={:?}", incoming.headers); + + Response:: { + status: status, + version: incoming.version, + headers: incoming.headers, + raw_status: incoming.subject, + body: body, + } +} + +/// Splits this response into a MessageHead and its body +#[inline] +pub fn split(res: Response) -> (MessageHead, Option) { + let head = MessageHead:: { + version: res.version, + headers: res.headers, + subject: res.status + }; + (head, res.body) +} diff --git a/src/lib.rs b/src/lib.rs index dfef3cad..0059e9cc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -39,6 +39,8 @@ pub use client::Client; pub use error::{Result, Error}; pub use header::Headers; pub use http::{Body, Chunk}; +pub use http::request::Request; +pub use http::response::Response; pub use method::Method::{self, Get, Head, Post, Put, Delete}; pub use status::StatusCode::{self, Ok, BadRequest, NotFound}; pub use server::Server; diff --git a/src/server/mod.rs b/src/server/mod.rs index 4656ab8f..aba3789d 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -24,13 +24,12 @@ use tokio_proto::streaming::Message; use tokio_proto::streaming::pipeline::{Transport, Frame, ServerProto}; pub use tokio_service::{NewService, Service}; -pub use self::request::Request; -pub use self::response::Response; - use http; +use http::response; +use http::request; -mod request; -mod response; +pub use http::response::Response; +pub use http::request::Request; /// An instance of the HTTP protocol, and implementation of tokio-proto's /// `ServerProto` trait. @@ -284,7 +283,7 @@ impl From> for Request { Message::WithoutBody(head) => (head.0, http::Body::empty()), Message::WithBody(head, body) => (head.0, body.into()), }; - request::new(None, head, body) + request::from_wire(None, head, body) } } @@ -321,7 +320,7 @@ impl Service for HttpService Message::WithoutBody(head) => (head.0, http::Body::empty()), Message::WithBody(head, body) => (head.0, body.into()), }; - let req = request::new(Some(self.remote_addr), head, body); + let req = request::from_wire(Some(self.remote_addr), head, body); self.inner.call(req).map(Into::into) } } diff --git a/src/server/request.rs b/src/server/request.rs deleted file mode 100644 index 136f4714..00000000 --- a/src/server/request.rs +++ /dev/null @@ -1,113 +0,0 @@ -//! Server Requests -//! -//! These are requests that a `hyper::Server` receives, and include its method, -//! target URI, headers, and message body. - -use std::fmt; -use std::net::SocketAddr; - -use version::HttpVersion; -use method::Method; -use header::Headers; -use http::{RequestHead, MessageHead, RequestLine, Body}; -use uri::Uri; - -/// A request bundles several parts of an incoming `NetworkStream`, given to a `Handler`. -pub struct Request { - method: Method, - uri: Uri, - version: HttpVersion, - headers: Headers, - remote_addr: Option, - body: Body, -} - -impl Request { - /// The `Method`, such as `Get`, `Post`, etc. - #[inline] - pub fn method(&self) -> &Method { &self.method } - - /// The headers of the incoming request. - #[inline] - pub fn headers(&self) -> &Headers { &self.headers } - - /// The target request-uri for this request. - #[inline] - pub fn uri(&self) -> &Uri { &self.uri } - - /// The version of HTTP for this request. - #[inline] - pub fn version(&self) -> HttpVersion { self.version } - - /// The remote socket address of this request - /// - /// This is an `Option`, because some underlying transports may not have - /// a socket address, such as Unix Sockets. - #[inline] - pub fn remote_addr(&self) -> Option { 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() - } - - /// Take the `Body` of this `Request`. - #[inline] - pub fn body(self) -> Body { - self.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) - } -} - -impl fmt::Debug for Request { - 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() - } -} - -pub fn new(addr: Option, incoming: RequestHead, body: Body) -> Request { - let MessageHead { version, subject: RequestLine(method, uri), headers } = incoming; - debug!("Request::new: addr={}, req=\"{} {} {}\"", MaybeAddr(&addr), method, uri, version); - debug!("Request::new: headers={:?}", headers); - - Request { - method: method, - uri: uri, - headers: headers, - version: version, - remote_addr: addr, - body: body, - } -} - -struct MaybeAddr<'a>(&'a Option); - -impl<'a> fmt::Display for MaybeAddr<'a> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self.0 { - Some(ref addr) => fmt::Display::fmt(addr, f), - None => f.write_str("None"), - } - } -} - diff --git a/src/server/response.rs b/src/server/response.rs deleted file mode 100644 index 4acb5513..00000000 --- a/src/server/response.rs +++ /dev/null @@ -1,111 +0,0 @@ -use std::fmt; - -use header; -use http::{self, Body}; -use status::StatusCode; -use version; - -/// The Response sent to a client after receiving a Request in a Service. -/// -/// The default `StatusCode` for a `Response` is `200 OK`. -pub struct Response { - head: http::MessageHead, - body: Option, -} - -impl Response { - /// Create a new Response. - #[inline] - pub fn new() -> Response { - Response::default() - } - - /// The headers of this response. - #[inline] - pub fn headers(&self) -> &header::Headers { &self.head.headers } - - /// The status of this response. - #[inline] - pub fn status(&self) -> StatusCode { - self.head.subject - } - - /// The HTTP version of this response. - #[inline] - pub fn version(&self) -> version::HttpVersion { self.head.version } - - /// Get a mutable reference to the Headers. - #[inline] - pub fn headers_mut(&mut self) -> &mut header::Headers { &mut self.head.headers } - - /// Set the `StatusCode` for this response. - #[inline] - pub fn set_status(&mut self, status: StatusCode) { - self.head.subject = status; - } - - /// Set the body. - #[inline] - pub fn set_body>(&mut self, body: T) { - self.body = Some(body.into()); - } - - /// 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(mut self, header: H) -> Self { - self.head.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: header::Headers) -> Self { - self.head.headers = headers; - self - } - - /// Set the body and move the Response. - /// - /// Useful for the "builder-style" pattern. - #[inline] - pub fn with_body>(mut self, body: T) -> Self { - self.set_body(body); - self - } -} - -impl Default for Response { - fn default() -> Response { - Response { - head: 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.head.subject) - .field("version", &self.head.version) - .field("headers", &self.head.headers) - .finish() - } -} - -pub fn split(res: Response) -> (http::MessageHead, Option) { - (res.head, res.body) -} diff --git a/tests/server.rs b/tests/server.rs index 40b75aaf..dae3fca3 100644 --- a/tests/server.rs +++ b/tests/server.rs @@ -503,7 +503,7 @@ fn test_server_disable_keep_alive() { .status(hyper::Ok) .header(hyper::header::ContentLength(quux.len() as u64)) .body(quux); - + let _ = req.write_all(b"\ GET /quux HTTP/1.1\r\n\ Host: example.domain\r\n\ @@ -522,4 +522,4 @@ fn test_server_disable_keep_alive() { panic!("read {} bytes on a disabled keep-alive socket", n); } } -} \ No newline at end of file +}