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
This commit is contained in:
Nick Gonzales
2017-05-01 12:19:55 -06:00
parent 0de295670f
commit 864d3e27a4
11 changed files with 242 additions and 314 deletions

View File

@@ -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<Option<Body>> for Body {
#[inline]
fn from (body: Option<Body>) -> Body {
body.unwrap_or_default()
}

View File

@@ -24,6 +24,8 @@ mod io;
mod h1;
//mod h2;
mod str;
pub mod request;
pub mod response;
/*
macro_rules! nonblocking {

283
src/http/request.rs Normal file
View File

@@ -0,0 +1,283 @@
use std::fmt;
use header::Headers;
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<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() }
/// 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<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.
#[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; }
}
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()
}
}
struct MaybeAddr<'a>(&'a Option<SocketAddr>);
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<B>(addr: Option<SocketAddr>, incoming: RequestHead, body: B) -> Request<B> {
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::<B> {
method: method,
uri: uri,
headers: headers,
version: version,
remote_addr: addr,
body: Some(body),
is_proxy: false,
}
}
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: ::http::RequestLine(req.method, uri),
headers: req.headers,
version: req.version,
};
(head, req.body)
}
#[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"));
}
*/
}

152
src/http/response.rs Normal file
View File

@@ -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<B = Body> {
version: HttpVersion,
headers: Headers,
status: StatusCode,
raw_status: 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]
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<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
}
}
impl Response<Body> {
/// Take the `Body` of this response.
#[inline]
pub fn body(self) -> Body {
self.body.unwrap_or_default()
}
}
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()
}
}
/// Constructs a response using a received ResponseHead and optional body
#[inline]
pub fn from_wire<B>(incoming: ResponseHead, body: Option<B>) -> Response<B> {
let status = incoming.status();
trace!("Response::new");
debug!("version={:?}, status={:?}", incoming.version, status);
debug!("headers={:?}", incoming.headers);
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)
}