First attempt at writing reverse proxy in hyper
This commit is contained in:
parent
3e7a6a270b
commit
6735bc032b
10 changed files with 400 additions and 28 deletions
|
|
@ -0,0 +1 @@
|
|||
|
||||
11
src/config.rs
Normal file
11
src/config.rs
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
use std::{net::IpAddr, path::PathBuf};
|
||||
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct GwConfig
|
||||
{
|
||||
address: IpAddr,
|
||||
port: u16,
|
||||
services: Vec<Route>,
|
||||
}
|
||||
54
src/main.rs
54
src/main.rs
|
|
@ -1,29 +1,23 @@
|
|||
use hyper::client::Client;
|
||||
use hyper::client::HttpConnector;
|
||||
use hyper::http::uri::Authority;
|
||||
use hyper::http::uri::Parts;
|
||||
use hyper::http::uri::Scheme;
|
||||
use hyper::service::{make_service_fn, service_fn};
|
||||
use hyper::Uri;
|
||||
use hyper::{Body, Request, Response, Server};
|
||||
use std::convert::Infallible;
|
||||
use std::net::SocketAddr;
|
||||
|
||||
pub mod client;
|
||||
use figment::{
|
||||
providers::{Env, Format, Yaml},
|
||||
Figment,
|
||||
};
|
||||
use hyper::{
|
||||
client::{Client, HttpConnector},
|
||||
http::uri::{Authority, Parts, Scheme},
|
||||
service::{make_service_fn, service_fn},
|
||||
Body, Request, Response, Server, Uri,
|
||||
};
|
||||
use proxy::Route;
|
||||
|
||||
async fn handle(mut req: Request<Body>) -> Result<Response<Body>, hyper::Error> {
|
||||
// We modify the request by changing its URI.
|
||||
// let uri = req.uri();
|
||||
// let scheme = uri.scheme().unwrap_or(&Scheme::HTTP);
|
||||
// let authority = "localhost:5000";
|
||||
// let path_and_query =
|
||||
// println!("{:?}", uri);
|
||||
// let new_uri = Uri::builder()
|
||||
// .scheme(uri.scheme().clone().unwrap_or(&Scheme::HTTP))
|
||||
// .authority("http://localhost:5000")
|
||||
// .path_and_query(uri.path_and_query().unwrap().clone())
|
||||
// .build()
|
||||
// .unwrap();
|
||||
pub mod client;
|
||||
mod config;
|
||||
mod proxy;
|
||||
|
||||
async fn handle(mut req: Request<Body>) -> Result<Response<Body>, hyper::Error>
|
||||
{
|
||||
println!("{:?}", req);
|
||||
let req_parts = req.uri().clone().into_parts();
|
||||
let mut parts = Parts::default();
|
||||
|
|
@ -40,15 +34,23 @@ async fn handle(mut req: Request<Body>) -> Result<Response<Body>, hyper::Error>
|
|||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
async fn main()
|
||||
{
|
||||
// let figment = Figment::new()
|
||||
// .merge(Yaml::file("Rb.yaml").nested())
|
||||
// .merge(Env::prefixed("RB_").global());
|
||||
// let config: config::GwConfig = figment.extract().unwrap();
|
||||
|
||||
// Construct our SocketAddr to listen on...
|
||||
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
|
||||
|
||||
// And a MakeService to handle each connection...
|
||||
let make_service = make_service_fn(|_conn| async { Ok::<_, hyper::Error>(service_fn(handle)) });
|
||||
// let make_service = make_service_fn(|_conn| async { Ok::<_, hyper::Error>(service_fn(handle)) });
|
||||
|
||||
// Then bind and serve...
|
||||
let server = Server::bind(&addr).serve(make_service);
|
||||
let route = Route::new("/".into(), String::from("localhost:8000"), "/".into());
|
||||
let make_svc = make_service_fn(|_| async { Ok::<_, hyper::Error>(route) });
|
||||
let server = Server::bind(&addr).serve(make_svc);
|
||||
|
||||
// And run forever...
|
||||
if let Err(e) = server.await {
|
||||
|
|
|
|||
5
src/proxy/mod.rs
Normal file
5
src/proxy/mod.rs
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
pub mod route;
|
||||
// pub mod server;
|
||||
|
||||
pub use route::Route;
|
||||
// pub use server::ProxyServer;
|
||||
96
src/proxy/route.rs
Normal file
96
src/proxy/route.rs
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
use core::{
|
||||
future::Future,
|
||||
pin::Pin,
|
||||
task::{Context, Poll},
|
||||
};
|
||||
use std::path::PathBuf;
|
||||
|
||||
use hyper::{
|
||||
client::{Client, HttpConnector},
|
||||
http::{
|
||||
uri::{Authority, Parts, Scheme},
|
||||
Request, Response,
|
||||
},
|
||||
service::Service,
|
||||
Body, Uri,
|
||||
};
|
||||
use serde::Deserialize;
|
||||
|
||||
/// Represents a route that a ProxyServer can send requests to. This struct also implements the
|
||||
/// `hyper::service::Service` trait, meaning it can be used as a service for a hyper server to
|
||||
/// consume. This is used in combination with `crate::proxy::ProxyServer` to create a modular proxy
|
||||
/// server.
|
||||
///
|
||||
/// # Fields
|
||||
///
|
||||
/// * `route` - Which prefix of route should be matched. This route prefix will then be replaced by
|
||||
/// the provided prefix when routing.
|
||||
/// * `host` - The host that request should be proxied to. This variable should also include the
|
||||
/// port number if necessary.
|
||||
/// * `prefix` - The prefix with which to replace the routed prefix. In practice, this means that
|
||||
/// the proxy can use a completely different prefix for routes, e.g. `/api/posts` on the proxy site
|
||||
/// & `/v1/api/posts` on the proxied server site.
|
||||
/// * `timeout` - How long the proxy should wait before aborting the request.
|
||||
#[derive(Deserialize)]
|
||||
pub struct Route
|
||||
{
|
||||
route: PathBuf,
|
||||
host: String,
|
||||
prefix: PathBuf,
|
||||
}
|
||||
|
||||
impl Route
|
||||
{
|
||||
pub fn new(route: PathBuf, host: String, prefix: PathBuf) -> Route
|
||||
{
|
||||
Route {
|
||||
route,
|
||||
host,
|
||||
prefix,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Service<Request<Body>> for Route
|
||||
{
|
||||
type Response = Response<Body>;
|
||||
type Error = hyper::http::Error;
|
||||
type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>>>>;
|
||||
|
||||
/// This function lets hyper know when the service is ready to process requests.
|
||||
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>>
|
||||
{
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
/// Processes incoming requests by proxying them to other hosts.
|
||||
fn call(&mut self, req: Request<Body>) -> Self::Future
|
||||
{
|
||||
let fut = async {
|
||||
// Convert the request's uri to match the redirected one
|
||||
let req_parts = req.uri().clone().into_parts();
|
||||
let mut parts = Parts::default();
|
||||
parts.scheme = req_parts.scheme.or_else(|| Some(Scheme::HTTP));
|
||||
parts.authority = Some(Authority::from_static("localhost:5000"));
|
||||
parts.path_and_query = req_parts.path_and_query;
|
||||
|
||||
let new_uri = Uri::from_parts(parts).unwrap();
|
||||
*req.uri_mut() = new_uri;
|
||||
|
||||
// Create a new client that can send the requests
|
||||
let client: Client<HttpConnector, Body> = Client::builder().build_http();
|
||||
|
||||
// match client.request
|
||||
// NOTE: when should we return an Err here?
|
||||
match client.request(req).await {
|
||||
Ok(res) => Ok(res),
|
||||
Err(_) => Ok(hyper::http::response::Builder::new()
|
||||
.status(500)
|
||||
.body(Body::empty())
|
||||
.unwrap()),
|
||||
}
|
||||
};
|
||||
|
||||
Box::pin(fut)
|
||||
}
|
||||
}
|
||||
38
src/proxy/server.rs
Normal file
38
src/proxy/server.rs
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
use core::{
|
||||
future::Future,
|
||||
pin::Pin,
|
||||
task::{Context, Poll},
|
||||
};
|
||||
use std::path::PathBuf;
|
||||
|
||||
use hyper::{
|
||||
client::{Client, HttpConnector},
|
||||
http::{Request, Response},
|
||||
service::Service,
|
||||
Body,
|
||||
};
|
||||
|
||||
use super::route::Route;
|
||||
|
||||
/// A ProxyServer is an implementation of `hyper::service::Service` that proxies requests to a
|
||||
/// given list of routes.
|
||||
pub struct ProxyServer
|
||||
{
|
||||
routes: Vec<Route>,
|
||||
}
|
||||
|
||||
impl Service<Request<Body>> for ProxyServer
|
||||
{
|
||||
type Response = super::Route;
|
||||
type Error = hyper::http::Error;
|
||||
type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>>>>;
|
||||
|
||||
/// This function lets hyper know when the service is ready to process requests.
|
||||
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>>
|
||||
{
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
/// Processes incoming requests by proxying them to other hosts.
|
||||
fn call(&mut self, req: Request<Body>) -> Self::Future {}
|
||||
}
|
||||
Reference in a new issue