First attempt at writing reverse proxy in hyper
parent
3e7a6a270b
commit
6735bc032b
|
@ -2,6 +2,15 @@
|
|||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "atomic"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b88d82667eca772c4aa12f0f1348b3ae643424c8876448f3f7bd5787032e234c"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.0.1"
|
||||
|
@ -26,6 +35,26 @@ version = "1.0.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "dtoa"
|
||||
version = "0.4.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56899898ce76aaf4a0f24d914c97ea6ed976d42fec6ad33fcbb0a1103e07b2b0"
|
||||
|
||||
[[package]]
|
||||
name = "figment"
|
||||
version = "0.10.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "790b4292c72618abbab50f787a477014fe15634f96291de45672ce46afe122df"
|
||||
dependencies = [
|
||||
"atomic",
|
||||
"pear",
|
||||
"serde",
|
||||
"serde_yaml",
|
||||
"uncased",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
|
@ -173,6 +202,12 @@ dependencies = [
|
|||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "inlinable_string"
|
||||
version = "0.1.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3094308123a0e9fd59659ce45e22de9f53fc1d2ac6e1feb9fef988e4f76cad77"
|
||||
|
||||
[[package]]
|
||||
name = "instant"
|
||||
version = "0.1.12"
|
||||
|
@ -200,6 +235,12 @@ version = "0.2.108"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8521a1b57e76b1ec69af7599e75e38e7b7fad6610f037db8c79b127201b5d119"
|
||||
|
||||
[[package]]
|
||||
name = "linked-hash-map"
|
||||
version = "0.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.5"
|
||||
|
@ -296,6 +337,29 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pear"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "15e44241c5e4c868e3eaa78b7c1848cadd6344ed4f54d029832d32b415a58702"
|
||||
dependencies = [
|
||||
"inlinable_string",
|
||||
"pear_codegen",
|
||||
"yansi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pear_codegen"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "82a5ca643c2303ecb740d506539deba189e16f2754040a42901cd8105d0282d0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"proc-macro2-diagnostics",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.7"
|
||||
|
@ -317,6 +381,19 @@ dependencies = [
|
|||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2-diagnostics"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4bf29726d67464d49fa6224a1d07936a8c08bb3fba727c7493f6cf1616fdaada"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"version_check",
|
||||
"yansi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.10"
|
||||
|
@ -330,8 +407,9 @@ dependencies = [
|
|||
name = "rb-gw_v2"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"http",
|
||||
"figment",
|
||||
"hyper",
|
||||
"serde",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
|
@ -350,6 +428,38 @@ version = "1.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.130"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.130"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_yaml"
|
||||
version = "0.8.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d8c608a35705a5d3cdc9fbe403147647ff34b921f8e833e49306df898f9b20af"
|
||||
dependencies = [
|
||||
"dtoa",
|
||||
"indexmap",
|
||||
"serde",
|
||||
"yaml-rust",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.0"
|
||||
|
@ -469,12 +579,27 @@ version = "0.2.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642"
|
||||
|
||||
[[package]]
|
||||
name = "uncased"
|
||||
version = "0.9.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5baeed7327e25054889b9bd4f975f32e5f4c5d434042d59ab6cd4142c0a76ed0"
|
||||
dependencies = [
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
|
||||
|
||||
[[package]]
|
||||
name = "want"
|
||||
version = "0.3.0"
|
||||
|
@ -506,3 +631,18 @@ name = "winapi-x86_64-pc-windows-gnu"
|
|||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "yaml-rust"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
|
||||
dependencies = [
|
||||
"linked-hash-map",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yansi"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9fc79f4a1e39857fc00c3f662cbf2651c771f00e9c15fe2abc341806bd46bd71"
|
||||
|
|
|
@ -8,4 +8,5 @@ edition = "2018"
|
|||
[dependencies]
|
||||
hyper = { version = "0.14.15", features = [ "full" ] }
|
||||
tokio = { version = "*", features = [ "full" ] }
|
||||
http = "*"
|
||||
figment = { version = "0.10.6", features = [ "yaml", "env" ] }
|
||||
serde = { version = "*", features = [ "derive" ] }
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
debug:
|
||||
address: '0.0.0.0'
|
||||
port: 8000
|
||||
|
||||
services:
|
||||
- route: '/yeet'
|
||||
scheme: 'http'
|
||||
authority: 'localhost:5000'
|
||||
prefix: ''
|
|
@ -0,0 +1,69 @@
|
|||
required_version = "1.4.38"
|
||||
binop_separator = "Front"
|
||||
blank_lines_lower_bound = 0
|
||||
blank_lines_upper_bound = 1
|
||||
# Trying something new
|
||||
brace_style = "AlwaysNextLine"
|
||||
color = "Auto"
|
||||
combine_control_expr = false
|
||||
comment_width = 80
|
||||
condense_wildcard_suffixes = false
|
||||
control_brace_style = "AlwaysSameLine"
|
||||
disable_all_formatting = false
|
||||
edition = "2018"
|
||||
emit_mode = "Files"
|
||||
empty_item_single_line = true
|
||||
enum_discrim_align_threshold = 0
|
||||
error_on_line_overflow = false
|
||||
error_on_unformatted = false
|
||||
fn_args_layout = "Tall"
|
||||
fn_single_line = false
|
||||
force_explicit_abi = true
|
||||
force_multiline_blocks = false
|
||||
format_code_in_doc_comments = false
|
||||
format_macro_bodies = true
|
||||
format_macro_matchers = false
|
||||
format_strings = false
|
||||
group_imports = "StdExternalCrate"
|
||||
hard_tabs = false
|
||||
hide_parse_errors = false
|
||||
ignore = []
|
||||
imports_granularity = "Crate"
|
||||
imports_indent = "Block"
|
||||
imports_layout = "Mixed"
|
||||
indent_style = "Block"
|
||||
inline_attribute_width = 0
|
||||
license_template_path = ""
|
||||
make_backup = false
|
||||
match_arm_blocks = true
|
||||
match_arm_leading_pipes = "Never"
|
||||
match_block_trailing_comma = true
|
||||
max_width = 100
|
||||
merge_derives = true
|
||||
newline_style = "Auto"
|
||||
normalize_comments = false
|
||||
normalize_doc_attributes = false
|
||||
overflow_delimited_expr = false
|
||||
remove_nested_parens = true
|
||||
reorder_impl_items = false
|
||||
reorder_imports = true
|
||||
reorder_modules = true
|
||||
report_fixme = "Always"
|
||||
report_todo = "Always"
|
||||
skip_children = false
|
||||
space_after_colon = true
|
||||
space_before_colon = false
|
||||
spaces_around_ranges = false
|
||||
struct_field_align_threshold = 0
|
||||
struct_lit_single_line = true
|
||||
tab_spaces = 4
|
||||
trailing_comma = "Vertical"
|
||||
trailing_semicolon = true
|
||||
type_punctuation_density = "Wide"
|
||||
unstable_features = false
|
||||
use_field_init_shorthand = false
|
||||
use_small_heuristics = "Default"
|
||||
use_try_shorthand = false
|
||||
version = "One"
|
||||
where_single_line = false
|
||||
wrap_comments = false
|
|
@ -0,0 +1 @@
|
|||
|
|
@ -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 {
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
pub mod route;
|
||||
// pub mod server;
|
||||
|
||||
pub use route::Route;
|
||||
// pub use server::ProxyServer;
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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 {}
|
||||
}
|
Loading…
Reference in New Issue