Skip to content

Instantly share code, notes, and snippets.

@mockersf
Created May 27, 2017 00:23
Show Gist options
  • Save mockersf/6156d47f52d463ca1601e0aada716459 to your computer and use it in GitHub Desktop.
Save mockersf/6156d47f52d463ca1601e0aada716459 to your computer and use it in GitHub Desktop.
shared client with Hyper
#![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 hyper::StatusCode;
use hyper::header::Host;
use hyper::server::{Http, Service, Request, Response};
use hyper::Client;
#[derive(Clone)]
struct SharedClient {
client: Client<hyper::client::HttpConnector>,
}
unsafe impl Sync for SharedClient {}
unsafe impl Send for SharedClient {}
lazy_static! {
static ref CLIENT: Arc<Mutex<Cell<Option<SharedClient>>>> =
Arc::new(Mutex::new(Cell::new(None)));
}
#[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);
let client = {
let mut lock = CLIENT.lock().unwrap();
lock.get_mut().clone()
}
.unwrap()
.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());
let client = Client::new(&server.handle());
CLIENT
.lock()
.unwrap()
.set(Some(SharedClient { client: client }));
server.run().unwrap();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment