use std::cmp;
use std::time::Duration;
use std::error::Error as ErrorTrait;
use std::net::{SocketAddr, Shutdown};
use std::io::{Error, ErrorKind, Write, Read};
use hyper;
use httparse;
use hyper::net::{NetworkConnector, NetworkStream};
use mock::MockResponseProvider;
use resource::http::HttpRequest;
#[macro_export]
macro_rules! hyper_client {
() => {
if cfg!(test) {
$crate::mock::http::mocked_hyper_client()
} else {
hyper::Client::new()
}
}
}
#[cfg_attr(feature = "clippy", allow(inline_always))]
#[inline(always)]
#[doc(hidden)]
pub fn mocked_hyper_client() -> hyper::Client {
hyper::Client::with_connector(MockConnector)
}
struct MockConnector;
impl NetworkConnector for MockConnector {
type Stream = MockStream;
fn connect(
&self,
host: &str,
port: u16,
_: &str
) -> Result<Self::Stream, hyper::Error> {
Ok(MockStream::new(host, port))
}
}
struct MockStream {
host: String,
port: u16,
request: Vec<u8>,
response: Result<Vec<u8>, Error>,
response_index: usize
}
impl MockStream {
pub fn new(host: &str, port: u16) -> MockStream {
MockStream {
host: host.to_string(),
port: port,
request: Vec::new(),
response: Ok(Vec::new()),
response_index: 0
}
}
}
impl Write for MockStream {
fn write(&mut self, buf: &[u8]) -> Result<usize, Error> {
self.request.extend_from_slice(buf);
Ok(buf.len())
}
fn flush(&mut self) -> Result<(), Error> {
let mut headers = [httparse::EMPTY_HEADER; 16];
let mut req = httparse::Request::new(&mut headers);
match req.parse(&self.request[..]) {
Ok(httparse::Status::Complete(size)) => {
let request = Box::new(HttpRequest::new(
self.host.to_string(),
self.port,
req,
self.request[size..].to_vec().into()
));
match MockResponseProvider::response_from_request(request) {
Ok(response) => {
self.response = response;
Ok(())
},
Err(err) => Err(err)
}
},
_ => unreachable!()
}
}
}
impl Read for MockStream {
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> {
match self.response.as_ref() {
Ok(response) => {
let bytes_available = response.len() - self.response_index;
let bytes_to_read = buf.len();
let bytes_read = cmp::min(bytes_to_read, bytes_available);
let bytes = &response[
self.response_index..self.response_index + bytes_read
];
for (index, b) in bytes.iter().enumerate() {
buf[index] = *b;
}
self.response_index += bytes_read;
Ok(bytes_read)
},
Err(err) => Err(Error::new(err.kind(), err.description()))
}
}
}
impl NetworkStream for MockStream {
fn peer_addr(&mut self) -> Result<SocketAddr, Error> {
Err(Error::new(ErrorKind::NotConnected, "Noir: Address not mocked."))
}
fn set_read_timeout(&self, _: Option<Duration>) -> Result<(), Error> {
Ok(())
}
fn set_write_timeout(&self, _: Option<Duration>) -> Result<(), Error> {
Ok(())
}
fn close(&mut self, _: Shutdown) -> Result<(), Error> {
Ok(())
}
}