Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

simple proxy example #1096

Open
vandenoever opened this issue Mar 19, 2017 · 7 comments
Open

simple proxy example #1096

vandenoever opened this issue Mar 19, 2017 · 7 comments

Comments

@vandenoever
Copy link

@vandenoever vandenoever commented Mar 19, 2017

Hyper has client and server http support and can probably be used to write an HTTP proxy. @fmonjalet claims to have already written one. In Node.JS it's very simple to write such a proxy in a few lines.

Here is my attempt at doing this with Hyper. The return type from fn call is wrong and I've no idea how to fix it. Such a simple proxy would be an useful addition to examples/.

//#![deny(warnings)]
extern crate futures;
extern crate hyper;
extern crate tokio_core;

use futures::Future;
use futures::future::FutureResult;
use hyper::{client, server, Client, Url};

#[derive(Clone, Copy)]
struct Proxy;

fn server_request_to_client_request(req: &server::Request) -> client::Request {
    // todo: handle parse error
    let url = Url::parse(&format!("{}", req.uri())).unwrap();
    let mut client_req = client::Request::new(req.method().clone(), url);
    client_req.set_version(req.version().clone());
    *client_req.headers_mut() = *req.headers();
    client_req
}

impl server::Service for Proxy {
    type Request = server::Request;
    type Response = server::Response;
    type Error = hyper::Error;
    type Future = FutureResult<server::Response, hyper::Error>;

    fn call(&self, req: server::Request) -> Self::Future {
        let mut core = tokio_core::reactor::Core::new().unwrap();
        let handle = core.handle();
        let client = Client::new(&handle);
        let mut client_req = server_request_to_client_request(&req);
        client_req.set_body(req.body());

        client.request(client_req)
            .and_then(|res| {
                let mut resp: server::Response = server::Response::new();
                resp = resp.with_headers(res.headers().clone());
                resp.set_status(res.status().clone());
                resp.set_body(res.body());
                futures::future::ok(resp)
            })
    }
}

fn main() {
    let addr = "127.0.0.1:1337".parse().unwrap();

    let server = server::Http::new().bind(&addr, || Ok(Proxy)).unwrap();
    println!("Listening on http://{} with 1 thread.",
             server.local_addr().unwrap());
    server.run().unwrap();
}

The proxy can be tested with

http_proxy=http://localhost:1337 wget http://w3.org
@vandenoever
Copy link
Author

@vandenoever vandenoever commented Mar 19, 2017

This version compiles with master but does not return any data.
The closure in and_then() is never called.
The error that is printed is

oops, an error Io(Error { repr: Custom(Custom { kind: Other, error: StringError("event loop gone") }) })
extern crate futures;
extern crate hyper;
extern crate tokio_core;

use futures::Future;
use hyper::{client, server, Client, Url};

struct Proxy;

fn server_request_to_client_request(req: &server::Request) -> client::Request {
    // todo: handle parse error
    let url = Url::parse(&format!("{}", req.uri())).unwrap();
    let mut client_req = client::Request::new(req.method().clone(), url);
    client_req.set_version(req.version().clone());
    *client_req.headers_mut() = req.headers().clone();
    client_req
}

impl server::Service for Proxy {
    type Request = server::Request;
    type Response = server::Response;
    type Error = hyper::Error;
    type Future = Box<Future<Item = Self::Response, Error = ::hyper::Error>>;

    fn call(&self, req: server::Request) -> Self::Future {
        let core = tokio_core::reactor::Core::new().unwrap();
        let handle = core.handle().clone();
        let client = Client::new(&handle);
        let mut client_req = server_request_to_client_request(&req);
        println!("got request {}", req.uri());
        client_req.set_body(req.body());

        Box::new(client.request(client_req)
            .map_err(|e| {
                println!("oops, an error {:?}", e);
                e
            })
            .and_then(|res| {
                println!("got response {}", res.headers());
                let mut resp: server::Response = server::Response::new();
                resp = resp.with_headers(res.headers().clone());
                resp.set_status(res.status().clone());
                resp.set_body(res.body());
                futures::future::ok(resp)
            }))
    }
}

fn main() {
    let addr = "127.0.0.1:1337".parse().unwrap();
    let server = server::Http::new().bind(&addr, || Ok(Proxy {})).unwrap();
    println!("Listening on http://{} with 1 thread.",
             server.local_addr().unwrap());
    server.run().unwrap();
}
@vandenoever
Copy link
Author

@vandenoever vandenoever commented Mar 19, 2017

Here is a version that uses a tokio Core per Service. This does not print error but hangs. The reason is probably that it mixes multiple event loops. The code hangs on one loop while another has data.

//#![deny(warnings)]
extern crate futures;
extern crate hyper;
extern crate tokio_core;

use futures::Future;
use hyper::{client, server, Client, Url};

struct Proxy {
    core: tokio_core::reactor::Core,
}

fn server_request_to_client_request(req: &server::Request) -> client::Request {
    // todo: handle parse error
    let url = Url::parse(&format!("{}", req.uri())).unwrap();
    let mut client_req = client::Request::new(req.method().clone(), url);
    client_req.set_version(req.version().clone());
    *client_req.headers_mut() = req.headers().clone();
    client_req
}

impl server::Service for Proxy {
    type Request = server::Request;
    type Response = server::Response;
    type Error = hyper::Error;
    type Future = Box<Future<Item = Self::Response, Error = ::hyper::Error>>;

    fn call(&self, req: server::Request) -> Self::Future {
        let client = Client::new(&self.core.handle());
        let mut client_req = server_request_to_client_request(&req);
        println!("got request {}", req.uri());
        client_req.set_body(req.body());

        Box::new(client.request(client_req)
            .map_err(|e| {
                println!("oops, an error {:?}", e);
                e
            })
            .and_then(|res| {
                println!("got response {}", res.headers());
                let mut resp: server::Response = server::Response::new();
                resp = resp.with_headers(res.headers().clone());
                resp.set_status(res.status().clone());
                resp.set_body(res.body());
                futures::future::ok(resp)
            }))
    }
}

fn main() {
    let addr = "127.0.0.1:1337".parse().unwrap();
    let server = server::Http::new()
        .bind(&addr, || {
            let core = tokio_core::reactor::Core::new().unwrap();
            Ok(Proxy { core: core })
        })
        .unwrap();

    println!("Listening on http://{} with 1 thread.",
             server.local_addr().unwrap());
    server.run().unwrap();
}
@chrmod
Copy link

@chrmod chrmod commented Apr 24, 2017

I would be very much interested in such example code. Found another, apparently also not working approach https://gist.github.com/infinityb/600c22ae549cecf43244 (maybe I'm using wrong version of the libraries)

@meganehouser
Copy link

@meganehouser meganehouser commented Apr 26, 2017

I wrote a simple proxy server with single event loop.
https://gist.github.com/meganehouser/d5e1b47eb2873797ebdc440b0ed482df
I referred to weldr.

@Hoverbear
Copy link

@Hoverbear Hoverbear commented May 17, 2017

Please feel free to submit a PR with such example code! If you need help doing this let me know.

@terry90
Copy link

@terry90 terry90 commented May 23, 2018

I'm currently writing a simple proxy with possibility of custom middlewares.
Since I'm a new rustacean, do not expect the code to be the most opti.
https://github.com/terry90/rs-simple-proxy

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
6 participants
You can’t perform that action at this time.