cogo_http/
mock.rs

1use std::io::{self, Read, Write, Cursor};
2use std::net::{SocketAddr, Shutdown};
3use std::time::Duration;
4use std::cell::Cell;
5
6use crate::net::{NetworkStream, NetworkConnector, SslClient};
7
8#[derive(Clone, Debug)]
9pub struct MockStream {
10    pub read: Cursor<Vec<u8>>,
11    next_reads: Vec<Vec<u8>>,
12    pub write: Vec<u8>,
13    pub is_closed: bool,
14    pub error_on_write: bool,
15    pub error_on_read: bool,
16    pub read_timeout: Cell<Option<Duration>>,
17    pub write_timeout: Cell<Option<Duration>>,
18    pub id: u64,
19}
20
21impl PartialEq for MockStream {
22    fn eq(&self, other: &MockStream) -> bool {
23        self.read.get_ref() == other.read.get_ref() && self.write == other.write
24    }
25}
26
27impl MockStream {
28    pub fn new() -> MockStream {
29        MockStream::with_input(b"")
30    }
31
32    pub fn with_input(input: &[u8]) -> MockStream {
33        MockStream::with_responses(vec![input])
34    }
35
36    pub fn with_responses(mut responses: Vec<&[u8]>) -> MockStream {
37        MockStream {
38            read: Cursor::new(responses.remove(0).to_vec()),
39            next_reads: responses.into_iter().map(|arr| arr.to_vec()).collect(),
40            write: vec![],
41            is_closed: false,
42            error_on_write: false,
43            error_on_read: false,
44            read_timeout: Cell::new(None),
45            write_timeout: Cell::new(None),
46            id: 0,
47        }
48    }
49}
50
51impl Read for MockStream {
52    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
53        if self.error_on_read {
54            Err(io::Error::new(io::ErrorKind::Other, "mock error"))
55        } else {
56            match self.read.read(buf) {
57                Ok(n) => {
58                    if self.read.position() as usize == self.read.get_ref().len() {
59                        if self.next_reads.len() > 0 {
60                            self.read = Cursor::new(self.next_reads.remove(0));
61                        }
62                    }
63                    Ok(n)
64                }
65                r => r
66            }
67        }
68    }
69}
70
71impl Write for MockStream {
72    fn write(&mut self, msg: &[u8]) -> io::Result<usize> {
73        if self.error_on_write {
74            Err(io::Error::new(io::ErrorKind::Other, "mock error"))
75        } else {
76            Write::write(&mut self.write, msg)
77        }
78    }
79
80    fn flush(&mut self) -> io::Result<()> {
81        Ok(())
82    }
83}
84
85impl NetworkStream for MockStream {
86    fn peer_addr(&mut self) -> io::Result<SocketAddr> {
87        Ok("127.0.0.1:1337".parse().unwrap())
88    }
89
90    fn set_read_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
91        self.read_timeout.set(dur);
92        Ok(())
93    }
94
95    fn set_write_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
96        self.write_timeout.set(dur);
97        Ok(())
98    }
99
100    fn close(&mut self, _how: Shutdown) -> io::Result<()> {
101        self.is_closed = true;
102        Ok(())
103    }
104
105    fn set_nonblocking(&self, b: bool) {}
106
107    fn reset_io(&self) {}
108
109    fn wait_io(&self) {}
110}
111
112pub struct MockConnector;
113
114impl NetworkConnector for MockConnector {
115    type Stream = MockStream;
116
117    fn connect(&self, _host: &str, _port: u16, _scheme: &str) -> crate::Result<MockStream> {
118        Ok(MockStream::new())
119    }
120}
121
122/// new connectors must be created if you wish to intercept requests.
123macro_rules! mock_connector (
124    ($name:ident {
125        $($url:expr => $res:expr)*
126    }) => (
127
128        struct $name;
129
130        impl $crate::net::NetworkConnector for $name {
131            type Stream = crate::mock::MockStream;
132            fn connect(&self, host: &str, port: u16, scheme: &str)
133                    -> $crate::Result<crate::mock::MockStream> {
134                use std::collections::HashMap;
135                debug!("MockStream::connect({:?}, {:?}, {:?})", host, port, scheme);
136                let mut map = HashMap::new();
137                $(map.insert($url, $res);)*
138
139
140                let key = format!("{}://{}", scheme, host);
141                // ignore port for now
142                match map.get(&*key) {
143                    Some(&res) => Ok($crate::mock::MockStream::with_input(res.as_bytes())),
144                    None => panic!("{:?} doesn't know url {}", stringify!($name), key)
145                }
146            }
147        }
148
149    );
150
151    ($name:ident { $($response:expr),+ }) => (
152        struct $name;
153
154        impl $crate::net::NetworkConnector for $name {
155            type Stream = $crate::mock::MockStream;
156            fn connect(&self, _: &str, _: u16, _: &str)
157                    -> $crate::Result<$crate::mock::MockStream> {
158                Ok($crate::mock::MockStream::with_responses(vec![
159                    $($response),+
160                ]))
161            }
162        }
163    );
164);
165
166#[derive(Debug, Default)]
167pub struct MockSsl;
168
169impl<T: NetworkStream + Send + Clone> SslClient<T> for MockSsl {
170    type Stream = T;
171    fn wrap_client(&self, stream: T, _host: &str) -> crate::Result<T> {
172        Ok(stream)
173    }
174}