Abstract everything but the response from hyper

Because we're using unsized types not for requesting, there's not a
satisfactory way of doing this without boxing the request...

Once unsized stuff lands in rust 1.2/1.3(???) then this should be
implemented with Rc's instead of Box's.

For the time being I'm not sure what else to do.

servo/servo#6727
This commit is contained in:
Sam Gibson 2015-08-09 14:42:31 +10:00
parent 6cba33a50b
commit 7633cd54c2
2 changed files with 57 additions and 44 deletions

View file

@ -23,11 +23,11 @@ use hyper::header::StrictTransportSecurity;
use hyper::header::{AcceptEncoding, Accept, ContentLength, ContentType, Host, Location, qitem, Quality, QualityItem}; use hyper::header::{AcceptEncoding, Accept, ContentLength, ContentType, Host, Location, qitem, Quality, QualityItem};
use hyper::client::{Request, Response}; use hyper::client::{Request, Response};
use hyper::header::{AcceptEncoding, Accept, ContentLength, ContentType, Host, Location, qitem, StrictTransportSecurity}; use hyper::header::{AcceptEncoding, Accept, ContentLength, ContentType, Host, Location, qitem, StrictTransportSecurity};
use hyper::header::{Quality, QualityItem}; use hyper::header::{Quality, QualityItem, Headers};
use hyper::Error as HttpError; use hyper::Error as HttpError;
use hyper::method::Method; use hyper::method::Method;
use hyper::mime::{Mime, TopLevel, SubLevel}; use hyper::mime::{Mime, TopLevel, SubLevel};
use hyper::net::{Fresh, HttpsConnector, Openssl, NetworkConnector, NetworkStream}; use hyper::net::{Fresh, Streaming, HttpsConnector, Openssl, NetworkConnector, NetworkStream};
use hyper::status::{StatusCode, StatusClass}; use hyper::status::{StatusCode, StatusClass};
use ipc_channel::ipc::{self, IpcSender}; use ipc_channel::ipc::{self, IpcSender};
use log; use log;
@ -164,20 +164,52 @@ impl NetworkHttpRequester {
} }
} }
impl HttpRequester for NetworkHttpRequester { pub trait HttpRequest {
fn send(&self, request: Request<Fresh>) -> Result<Response, LoadError> { fn headers(&self) -> &Headers;
unimplemented!() fn headers_mut(&mut self) -> &mut Headers;
fn send(self: Box<Self>) -> Result<Response, LoadError>;
}
struct NetworkHttpRequest {
fresh: Request<Fresh>
}
impl HttpRequest for NetworkHttpRequest {
fn headers(&self) -> &Headers {
self.fresh.headers()
} }
fn build(&self, url: Url, method: Method) -> Result<Request<Fresh>, LoadError> { fn headers_mut(&mut self) -> &mut Headers {
self.fresh.headers_mut()
}
fn send(self: Box<Self>) -> Result<Response, LoadError> {
let connected = match self.fresh.start() {
Ok(streaming) => streaming,
Err(e) => return Err(LoadError::Connection(Url::parse("http://example.com").unwrap(), e.description().to_string()))
};
match connected.send() {
Ok(w) => Ok(w),
Err(e) => return Err(LoadError::Connection(Url::parse("http://example.com").unwrap(), e.description().to_string()))
}
}
}
impl HttpRequester for NetworkHttpRequester {
fn send(&self, request: Box<HttpRequest>) -> Result<Response, LoadError> {
request.send()
}
fn build(&self, url: Url, method: Method) -> Result<Box<HttpRequest>, LoadError> {
let connection = Request::with_connector(method, url.clone(), &self.connector); let connection = Request::with_connector(method, url.clone(), &self.connector);
let ssl_err_string = "Some(OpenSslErrors([UnknownError { library: \"SSL routines\", \ let ssl_err_string = "Some(OpenSslErrors([UnknownError { library: \"SSL routines\", \
function: \"SSL3_GET_SERVER_CERTIFICATE\", \ function: \"SSL3_GET_SERVER_CERTIFICATE\", \
reason: \"certificate verify failed\" }]))"; reason: \"certificate verify failed\" }]))";
match connection { let request = match connection {
Ok(req) => Ok(req), Ok(req) => req,
Err(HttpError::Io(ref io_error)) if ( Err(HttpError::Io(ref io_error)) if (
io_error.kind() == io::ErrorKind::Other && io_error.kind() == io::ErrorKind::Other &&
@ -185,7 +217,7 @@ impl HttpRequester for NetworkHttpRequester {
// FIXME: This incredibly hacky. Make it more robust, and at least test it. // FIXME: This incredibly hacky. Make it more robust, and at least test it.
format!("{:?}", io_error.cause()) == ssl_err_string format!("{:?}", io_error.cause()) == ssl_err_string
) => { ) => {
Err( return Err(
LoadError::Ssl( LoadError::Ssl(
url.clone(), url.clone(),
format!("ssl error {:?}: {:?} {:?}", io_error.kind(), io_error.description(), io_error.cause()) format!("ssl error {:?}: {:?} {:?}", io_error.kind(), io_error.description(), io_error.cause())
@ -193,15 +225,17 @@ impl HttpRequester for NetworkHttpRequester {
) )
}, },
Err(e) => { Err(e) => {
Err(LoadError::Connection(url, e.description().to_string())) return Err(LoadError::Connection(url, e.description().to_string()))
}
} }
};
Ok(Box::new(NetworkHttpRequest { fresh: request }))
} }
} }
pub trait HttpRequester { pub trait HttpRequester {
fn build(&self, url: Url, method: Method) -> Result<Request<Fresh>, LoadError>; fn build(&self, url: Url, method: Method) -> Result<Box<HttpRequest>, LoadError>;
fn send(&self, request: Request<Fresh>) -> Result<Response, LoadError>; fn send(&self, request: Box<HttpRequest>) -> Result<Response, LoadError>;
} }
pub enum LoadError { pub enum LoadError {
@ -321,35 +355,23 @@ pub fn load(mut load_data: LoadData,
// --- Start sending the request // --- Start sending the request
// Avoid automatically sending request body if a redirect has occurred. // Avoid automatically sending request body if a redirect has occurred.
let writer = match load_data.data { let response = match load_data.data {
Some(ref data) if iters == 1 => { Some(ref data) if iters == 1 => {
req.headers_mut().set(ContentLength(data.len() as u64)); req.headers_mut().set(ContentLength(data.len() as u64));
let mut writer = match req.start() { match requester.send(req) {
Ok(w) => w, Ok(w) => w,
Err(e) => { Err(e) => return Err(e)
return Err(LoadError::Connection(url, e.description().to_string()));
} }
};
match writer.write_all(&*data) {
Err(e) => {
return Err(LoadError::Connection(url, e.description().to_string()));
}
_ => {}
};
writer
}, },
_ => { _ => {
match load_data.method { match load_data.method {
Method::Get | Method::Head => (), Method::Get | Method::Head => (),
_ => req.headers_mut().set(ContentLength(0)) _ => req.headers_mut().set(ContentLength(0))
} }
match req.start() { match requester.send(req) {
Ok(w) => w, Ok(w) => w,
Err(e) => { Err(e) => return Err(e)
return Err(LoadError::Connection(url, e.description().to_string()));
}
} }
} }
}; };
@ -368,14 +390,6 @@ pub fn load(mut load_data: LoadData,
net_event))).unwrap(); net_event))).unwrap();
} }
// --- Finish writing the request and read the response
let response = match writer.send() {
Ok(r) => r,
Err(e) => {
return Err(LoadError::Connection(url, e.description().to_string()));
}
};
// Dump headers, but only do the iteration if info!() is enabled. // Dump headers, but only do the iteration if info!() is enabled.
info!("got HTTP response {}, headers:", response.status); info!("got HTTP response {}, headers:", response.status);
if log_enabled!(log::LogLevel::Info) { if log_enabled!(log::LogLevel::Info) {

View file

@ -2,24 +2,23 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use net::http_loader::{load, LoadError, HttpRequester}; use net::http_loader::{load, LoadError, HttpRequester, HttpRequest};
use url::Url; use url::Url;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use ipc_channel::ipc; use ipc_channel::ipc;
use net_traits::LoadData; use net_traits::LoadData;
use net::hsts::HSTSList; use net::hsts::HSTSList;
use hyper::client::{Request, Response}; use hyper::client::Response;
use hyper::net::Fresh;
use hyper::method::Method; use hyper::method::Method;
struct MockHttpRequester; struct MockHttpRequester;
impl HttpRequester for MockHttpRequester { impl HttpRequester for MockHttpRequester {
fn build(&self, url: Url, _: Method) -> Result<Request<Fresh>, LoadError> { fn build(&self, url: Url, _: Method) -> Result<Box<HttpRequest>, LoadError> {
Err(LoadError::Connection(url.clone(), "shouldn't connect".to_string())) Err(LoadError::Connection(url.clone(), "shouldn't connect".to_string()))
} }
fn send(&self, _: Request<Fresh>) -> Result<Response, LoadError> { fn send(&self, _: Box<HttpRequest>) -> Result<Response, LoadError> {
Err(LoadError::Connection(Url::parse("http://example.com").unwrap(), "shouldn't connect".to_string())) Err(LoadError::Connection(Url::parse("http://example.com").unwrap(), "shouldn't connect".to_string()))
} }
} }