hyper_socks/
lib.rs

1//! SOCKS proxy support for Hyper clients
2#![doc(html_root_url="https://docs.rs/hyper-socks/0.4.0")]
3#![warn(missing_docs)]
4
5extern crate socks;
6extern crate hyper;
7
8#[cfg(test)]
9extern crate hyper_openssl;
10
11use hyper::net::{NetworkConnector, HttpStream, HttpsStream, SslClient};
12use socks::{Socks4Stream, Socks5Stream};
13use std::io;
14use std::net::{SocketAddr, ToSocketAddrs};
15
16/// A connector that will produce HttpStreams proxied over a SOCKS4 server.
17#[derive(Debug)]
18pub struct Socks4HttpConnector {
19    addrs: Vec<SocketAddr>,
20    userid: String,
21}
22
23impl Socks4HttpConnector {
24    /// Creates a new `Socks4HttpConnector` which will connect to the specified
25    /// proxy with the specified userid.
26    pub fn new<T: ToSocketAddrs>(proxy: T, userid: &str) -> io::Result<Socks4HttpConnector> {
27        Ok(Socks4HttpConnector {
28            addrs: try!(proxy.to_socket_addrs()).collect(),
29            userid: userid.to_owned(),
30        })
31    }
32}
33
34impl NetworkConnector for Socks4HttpConnector {
35    type Stream = HttpStream;
36
37    fn connect(&self, host: &str, port: u16, scheme: &str) -> hyper::Result<HttpStream> {
38        if scheme != "http" {
39            return Err(io::Error::new(io::ErrorKind::InvalidInput, "invalid scheme for HTTP")
40                           .into());
41        }
42
43        let socket = try!(Socks4Stream::connect(&self.addrs[..], (host, port), &self.userid));
44        Ok(HttpStream(socket.into_inner()))
45    }
46}
47
48/// A connector that will produce HttpsStreams proxied over a SOCKS4 server.
49#[derive(Debug)]
50pub struct Socks4HttpsConnector<S> {
51    addrs: Vec<SocketAddr>,
52    userid: String,
53    ssl: S,
54}
55
56impl<S: SslClient> Socks4HttpsConnector<S> {
57    /// Creates a new `Socks4HttpsConnector` which will connect to the specified
58    /// proxy with the specified userid, and use the provided SSL implementation
59    /// to encrypt the resulting stream.
60    pub fn new<T: ToSocketAddrs>(proxy: T, userid: &str, ssl: S) -> io::Result<Self> {
61        Ok(Socks4HttpsConnector {
62            addrs: try!(proxy.to_socket_addrs()).collect(),
63            userid: userid.to_owned(),
64            ssl: ssl,
65        })
66    }
67}
68
69impl<S: SslClient> NetworkConnector for Socks4HttpsConnector<S> {
70    type Stream = HttpsStream<S::Stream>;
71
72    fn connect(&self, host: &str, port: u16, scheme: &str) -> hyper::Result<Self::Stream> {
73        if scheme != "http" && scheme != "https" {
74            return Err(io::Error::new(io::ErrorKind::InvalidInput, "invalid scheme for HTTPS")
75                           .into());
76        }
77
78        let socket = try!(Socks4Stream::connect(&self.addrs[..], (host, port), &self.userid));
79        let stream = HttpStream(socket.into_inner());
80
81        if scheme == "http" {
82            Ok(HttpsStream::Http(stream))
83        } else {
84            Ok(HttpsStream::Https(try!(self.ssl.wrap_client(stream, host))))
85        }
86    }
87}
88
89/// A connector that will produce HttpStreams proxied over a SOCKS5 server.
90#[derive(Debug)]
91pub struct Socks5HttpConnector {
92    addrs: Vec<SocketAddr>,
93}
94
95impl Socks5HttpConnector {
96    /// Creates a new `Socks5HttpConnector` which will connect to the specified
97    /// proxy.
98    pub fn new<T: ToSocketAddrs>(proxy: T) -> io::Result<Socks5HttpConnector> {
99        Ok(Socks5HttpConnector {
100            addrs: try!(proxy.to_socket_addrs()).collect(),
101        })
102    }
103}
104
105impl NetworkConnector for Socks5HttpConnector {
106    type Stream = HttpStream;
107
108    fn connect(&self, host: &str, port: u16, scheme: &str) -> hyper::Result<HttpStream> {
109        if scheme != "http" {
110            return Err(io::Error::new(io::ErrorKind::InvalidInput, "invalid scheme for HTTP")
111                           .into());
112        }
113
114        let socket = try!(Socks5Stream::connect(&self.addrs[..], (host, port)));
115        Ok(HttpStream(socket.into_inner()))
116    }
117}
118
119/// A connector that will produce HttpsStreams proxied over a SOCKS5 server.
120#[derive(Debug)]
121pub struct Socks5HttpsConnector<S> {
122    addrs: Vec<SocketAddr>,
123    ssl: S,
124}
125
126impl<S: SslClient> Socks5HttpsConnector<S> {
127    /// Creates a new `Socks5HttpsConnector` which will connect to the specified proxy, and use the
128    /// provided SSL implementation to encrypt the resulting stream.
129    pub fn new<T: ToSocketAddrs>(proxy: T, ssl: S) -> io::Result<Self> {
130        Ok(Socks5HttpsConnector {
131            addrs: try!(proxy.to_socket_addrs()).collect(),
132            ssl: ssl,
133        })
134    }
135}
136
137impl<S: SslClient> NetworkConnector for Socks5HttpsConnector<S> {
138    type Stream = HttpsStream<S::Stream>;
139
140    fn connect(&self, host: &str, port: u16, scheme: &str) -> hyper::Result<Self::Stream> {
141        if scheme != "http" && scheme != "https" {
142            return Err(io::Error::new(io::ErrorKind::InvalidInput, "invalid scheme for HTTPS")
143                           .into());
144        }
145
146        let socket = try!(Socks5Stream::connect(&self.addrs[..], (host, port)));
147        let stream = HttpStream(socket.into_inner());
148
149        if scheme == "http" {
150            Ok(HttpsStream::Http(stream))
151        } else {
152            Ok(HttpsStream::Https(try!(self.ssl.wrap_client(stream, host))))
153        }
154    }
155}
156
157#[cfg(test)]
158mod test {
159    use hyper;
160    use hyper_openssl::OpensslClient;
161    use std::io::Read;
162
163    use super::*;
164
165    #[test]
166    fn google() {
167        let connector = Socks4HttpConnector::new("127.0.0.1:8080", "").unwrap();
168        let client = hyper::Client::with_connector(connector);
169        let mut response = client.get("http://www.google.com").send().unwrap();
170
171        assert!(response.status.is_success());
172        let mut body = vec![];
173        response.read_to_end(&mut body).unwrap();
174    }
175
176    #[test]
177    fn google_ssl_http() {
178        let ssl = OpensslClient::new().unwrap();
179        let connector = Socks4HttpsConnector::new("127.0.0.1:8080", "", ssl).unwrap();
180        let client = hyper::Client::with_connector(connector);
181        let mut response = client.get("http://www.google.com").send().unwrap();
182
183        assert!(response.status.is_success());
184        let mut body = vec![];
185        response.read_to_end(&mut body).unwrap();
186    }
187
188    #[test]
189    fn google_ssl_https() {
190        let ssl = OpensslClient::new().unwrap();
191        let connector = Socks4HttpsConnector::new("127.0.0.1:8080", "", ssl).unwrap();
192        let client = hyper::Client::with_connector(connector);
193        let mut response = client.get("https://www.google.com").send().unwrap();
194
195        assert!(response.status.is_success());
196        let mut body = vec![];
197        response.read_to_end(&mut body).unwrap();
198    }
199
200    #[test]
201    fn google_v5() {
202        let connector = Socks5HttpConnector::new("127.0.0.1:8080").unwrap();
203        let client = hyper::Client::with_connector(connector);
204        let mut response = client.get("http://www.google.com").send().unwrap();
205
206        assert!(response.status.is_success());
207        let mut body = vec![];
208        response.read_to_end(&mut body).unwrap();
209    }
210
211    #[test]
212    fn google_ssl_http_v5() {
213        let ssl = OpensslClient::new().unwrap();
214        let connector = Socks5HttpsConnector::new("127.0.0.1:8080", ssl).unwrap();
215        let client = hyper::Client::with_connector(connector);
216        let mut response = client.get("http://www.google.com").send().unwrap();
217
218        assert!(response.status.is_success());
219        let mut body = vec![];
220        response.read_to_end(&mut body).unwrap();
221    }
222
223    #[test]
224    fn google_ssl_https_v5() {
225        let ssl = OpensslClient::new().unwrap();
226        let connector = Socks5HttpsConnector::new("127.0.0.1:8080", ssl).unwrap();
227        let client = hyper::Client::with_connector(connector);
228        let mut response = client.get("https://www.google.com").send().unwrap();
229
230        assert!(response.status.is_success());
231        let mut body = vec![];
232        response.read_to_end(&mut body).unwrap();
233    }
234}