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> for Route { type Response = Response; type Error = hyper::http::Error; type Future = Pin>>>; /// This function lets hyper know when the service is ready to process requests. fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { Poll::Ready(Ok(())) } /// Processes incoming requests by proxying them to other hosts. fn call(&mut self, req: Request) -> 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 = 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) } }