Last active
August 29, 2015 14:17
-
-
Save webstrand/46fb441e52a8663bced0 to your computer and use it in GitHub Desktop.
Simple Rust Reverse Proxy
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#![feature(io)] | |
#![feature(net)] | |
#![feature(core)] | |
extern crate hyper; | |
extern crate url; | |
static HOST: &'static str = "www.google.com"; | |
macro_rules! ret_err( | |
($e:expr) => {{ | |
match $e { | |
Ok(v) => v, | |
Err(e) => { println!("Line {}: {}", line!(), e); return; } | |
} | |
}} | |
); | |
/// Rewrite input from an object implementing `std::io::Read` to an object | |
/// implementing `std::io::Write`. | |
fn rewrite_io(input: &mut std::io::Read, output: &mut std::io::Write) -> Result<(), std::io::Error> { | |
let mut buffer: [u8; 4096] = [0; 4096]; | |
loop { | |
match input.read(&mut buffer) { | |
Ok(0) => break, | |
Ok(len) => try!(output.write_all(&buffer[.. len])), | |
Err(e) => return Err(e) | |
} | |
} | |
return Ok(()); | |
} | |
/// Given a `hyper::uri::RequestUri`, rewrite it to a `Url` substituting `HOST` | |
/// for the domain. | |
fn create_proxy_url(uri: hyper::uri::RequestUri, host: &str) -> Result<url::Url, url::ParseError> { | |
use hyper::uri::RequestUri::*; | |
match uri { | |
AbsolutePath(val) => url::Url::parse(&format!("http://{}{}", host, val)), | |
AbsoluteUri(_) => Err(url::ParseError::InvalidScheme), //todo: rewrite uri | |
_ => Err(url::ParseError::InvalidScheme) | |
} | |
} | |
//todo move mut to the type | |
fn proxy_request(mut request: hyper::server::Request, host: &str) -> Result<hyper::client::Response, hyper::HttpError> { | |
use hyper::header::Host; | |
let mut client = hyper::Client::new(); | |
// Read in the request body. | |
let mut request_body: Vec<u8> = Vec::new(); | |
try!(rewrite_io(&mut request, &mut request_body)); | |
// The host header must be changed for compatibility with v-hosts. | |
let mut headers = request.headers; | |
headers.set(Host { | |
hostname: host.to_string(), | |
port: None | |
}); | |
// Rewrite the target url from the client's request. | |
let url = try!(create_proxy_url(request.uri, host)); | |
// Build and send the proxy's request. | |
let proxy_response = try!( | |
client.request(request.method, url) | |
.headers(headers) | |
.body(request_body.as_slice()) | |
.send()); | |
return Ok(proxy_response); | |
} | |
fn handler(request: hyper::server::Request, mut response: hyper::server::Response<hyper::net::Fresh>) -> () { | |
let mut proxy_response = ret_err!(proxy_request(request, HOST)); | |
// Copy the proxy's response headers verbatim into the server's response | |
// headers. | |
*response.status_mut() = proxy_response.status.clone(); | |
*response.headers_mut() = proxy_response.headers.clone(); | |
// Write the headers and rewrite the proxy's response body to the client. | |
let mut response = ret_err!(response.start()); | |
ret_err!(rewrite_io(&mut proxy_response, &mut response)); | |
ret_err!(response.end()); | |
} | |
fn main() { | |
let server = hyper::Server::http(handler); | |
ret_err!(server.listen(std::net::IpAddr::new_v4(127, 0, 0, 1), 3000)); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment