Skip to content

Instantly share code, notes, and snippets.

@webstrand
Last active August 29, 2015 14:17
Show Gist options
  • Save webstrand/46fb441e52a8663bced0 to your computer and use it in GitHub Desktop.
Save webstrand/46fb441e52a8663bced0 to your computer and use it in GitHub Desktop.
Simple Rust Reverse Proxy
#![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