Skip to content

Instantly share code, notes, and snippets.

@mockersf
Created May 27, 2017 15:53
Show Gist options
  • Save mockersf/6ae921598913c3b59799bf2a33546922 to your computer and use it in GitHub Desktop.
Save mockersf/6ae921598913c3b59799bf2a33546922 to your computer and use it in GitHub Desktop.
shared remote with thread local Client
#![deny(warnings)]
extern crate futures;
extern crate tokio_core;
extern crate tokio_proto;
extern crate hyper;
#[macro_use]
extern crate lazy_static;
use std::cell::Cell;
use std::sync::{Arc, Mutex};
use futures::Future;
use tokio_core::reactor::Remote;
use hyper::StatusCode;
use hyper::header::Host;
use hyper::server::{Http, Service, Request, Response};
use hyper::Client;
lazy_static! {
static ref REMOTE: Arc<Mutex<Cell<Option<Remote>>>> = Arc::new(Mutex::new(Cell::new(None)));
}
thread_local! {
static CLIENT: Client<hyper::client::HttpConnector> =
Client::new(&REMOTE.lock().unwrap().get_mut().clone().unwrap().handle().unwrap());
}
#[derive(Clone, Copy)]
struct Config {
target: &'static str,
target_port: u16,
}
#[derive(Clone)]
struct Proxy {
config: Config,
}
impl Proxy {
fn forwarded_request(&self, req: Request) -> Request {
let url = match req.uri().query() {
Some(qp) => {
format!("http://{}:{}{}?{}",
self.config.target,
self.config.target_port,
req.path(),
qp)
}
None => {
format!("http://{}:{}{}",
self.config.target,
self.config.target_port,
req.path())
}
}
.parse::<hyper::Uri>()
.unwrap();
let mut forwarded_request = req;
forwarded_request
.headers_mut()
.set::<Host>(Host::new(self.config.target, self.config.target_port));
forwarded_request.set_uri(url);
forwarded_request
}
}
impl Service for Proxy {
type Request = Request;
type Response = Response;
type Error = hyper::Error;
type Future = Box<Future<Item = Self::Response, Error = hyper::Error>>;
fn call(&self, req: Request) -> Self::Future {
let forwarded_request = self.forwarded_request(req);
CLIENT.with(|client| {
Box::new(client
.request(forwarded_request)
.map(|res| {
let res: Response = res;
if res.status() != StatusCode::Ok {
println!("{:?}", res);
}
res
})
.or_else(|err| {
println!("{:?}", err);
futures::future::ok(Response::new().with_status(StatusCode::ServiceUnavailable))
}))
})
}
}
fn main() {
let addr = "127.0.0.1:3000".parse().unwrap();
let config = Config {
target: "localhost",
target_port: 8080,
};
let server = Http::new()
.bind(&addr, move || Ok(Proxy { config }))
.unwrap();
println!("Listening on http://{} with 1 thread.",
server.local_addr().unwrap());
REMOTE
.lock()
.unwrap()
.set(Some(server.handle().remote().clone()));
server.run().unwrap();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment