1use hyper::Body;
87use std::sync::Arc;
88use std::io;
89use hyper::client::HttpConnector;
90use std::net::IpAddr;
91use std::str::FromStr;
92use tokio::net::TcpStream;
93use hyper::header::{HeaderMap, HeaderValue};
94use hyper::client::connect::{Connect, Connected, Destination};
95use hyper::{Request, Response, Client, Uri, StatusCode};
96use futures::future::{self, err, Future};
97use tokio_tls::{TlsConnector, TlsStream};
98use lazy_static::lazy_static;
99
100type BoxFut = Box<Future<Item=Response<Body>, Error=hyper::Error> + Send>;
101
102fn is_hop_header(name: &str) -> bool {
103 use unicase::Ascii;
104
105 lazy_static! {
109 static ref HOP_HEADERS: Vec<Ascii<&'static str>> = vec![
110 Ascii::new("Connection"),
111 Ascii::new("Keep-Alive"),
112 Ascii::new("Proxy-Authenticate"),
113 Ascii::new("Proxy-Authorization"),
114 Ascii::new("Te"),
115 Ascii::new("Trailers"),
116 Ascii::new("Transfer-Encoding"),
117 Ascii::new("Upgrade"),
118 ];
119 }
120
121 HOP_HEADERS.iter().any(|h| h == &name)
122}
123
124fn remove_hop_headers(headers: &HeaderMap<HeaderValue>) -> HeaderMap<HeaderValue> {
128 let mut result = HeaderMap::new();
129 for (k, v) in headers.iter() {
130 if !is_hop_header(k.as_str()) {
131 result.insert(k.clone(), v.clone());
132 }
133 }
134 result
135}
136
137fn create_proxied_response<B>(mut response: Response<B>) -> Response<B> {
138 *response.headers_mut() = remove_hop_headers(response.headers());
139 response
140}
141
142fn forward_uri<B>(forward_url: &str, req: &Request<B>) -> Uri {
143 let forward_uri = match req.uri().query() {
144 Some(query) => format!("{}{}?{}", forward_url, req.uri().path(), query),
145 None => format!("{}{}", forward_url, req.uri().path()),
146 };
147
148 Uri::from_str(forward_uri.as_str()).unwrap()
149}
150
151fn create_proxied_request<B>(client_ip: IpAddr, forward_url: &str, mut request: Request<B>) -> Request<B> {
152 *request.headers_mut() = remove_hop_headers(request.headers());
153 *request.uri_mut() = forward_uri(forward_url, &request);
154
155 let x_forwarded_for_header_name = "x-forwarded-for";
156
157 match request.headers_mut().entry(x_forwarded_for_header_name) {
159
160 Ok(header_entry) => {
161 match header_entry {
162 hyper::header::Entry::Vacant(entry) => {
163 let addr = format!("{}", client_ip);
164 entry.insert(addr.parse().unwrap());
165 },
166
167 hyper::header::Entry::Occupied(mut entry) => {
168 let addr = format!("{}, {}", entry.get().to_str().unwrap(), client_ip);
169 entry.insert(addr.parse().unwrap());
170 }
171 }
172 }
173
174 Err(_) => panic!("Invalid header name: {}", x_forwarded_for_header_name),
176 }
177
178 request
179}
180
181pub fn call(client_ip: IpAddr, forward_uri: &str, request: Request<Body>) -> BoxFut {
182
183 let proxied_request = create_proxied_request(client_ip, forward_uri, request);
184 let tls_cx = native_tls::TlsConnector::builder().build().expect("Failed to build TLS connector");
185 let mut connector = HttpsConnector {
186 tls: Arc::new(tls_cx.into()),
187 http: HttpConnector::new(2),
188 };
189 connector.http.enforce_http(false);
190
191 let client = Client::builder().build(connector);
192 let response = client.request(proxied_request).then(|response| {
193
194 let proxied_response = match response {
195 Ok(response) => create_proxied_response(response),
196 Err(error) => {
197 println!("Error: {}", error); Response::builder()
199 .status(StatusCode::INTERNAL_SERVER_ERROR)
200 .body(Body::empty())
201 .unwrap()
202 },
203 };
204
205
206 future::ok(proxied_response)
207 });
208
209 Box::new(response)
210}
211
212
213struct HttpsConnector {
214 tls: Arc<TlsConnector>,
215 http: HttpConnector,
216}
217
218impl Connect for HttpsConnector {
219 type Transport = TlsStream<TcpStream>;
220 type Error = io::Error;
221 type Future = Box<Future<Item = (Self::Transport, Connected), Error = Self::Error> + Send>;
222
223 fn connect(&self, dst: Destination) -> Self::Future {
224
225 if dst.scheme() != "https" {
226 return Box::new(err(io::Error::new(io::ErrorKind::Other,
227 "only works with https")))
228 }
229
230 let host = format!("{}{}", dst.host(), dst.port().map(|p| format!(":{}",p)).unwrap_or("".into()));
231
232 let tls_cx = self.tls.clone();
233 Box::new(self.http.connect(dst).and_then(move |(tcp, connected)| {
234 tls_cx.connect(&host, tcp)
235 .map(|s| (s, connected))
236 .map_err(|e| io::Error::new(io::ErrorKind::Other, e))
237 }))
238
239 }
240
241
242}