🔥 使用 Rust 重构 #5
37
Cargo.lock
generated
37
Cargo.lock
generated
@ -852,6 +852,16 @@ version = "0.3.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
|
||||
|
||||
[[package]]
|
||||
name = "mime_guess"
|
||||
version = "2.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef"
|
||||
dependencies = [
|
||||
"mime",
|
||||
"unicase",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "minimal-lexical"
|
||||
version = "0.2.1"
|
||||
@ -1179,9 +1189,11 @@ dependencies = [
|
||||
"headers",
|
||||
"http",
|
||||
"http-body-util",
|
||||
"httpdate",
|
||||
"hyper",
|
||||
"hyper-util",
|
||||
"mime",
|
||||
"mime_guess",
|
||||
"multer",
|
||||
"nix",
|
||||
"parking_lot",
|
||||
@ -1306,6 +1318,19 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "queryst"
|
||||
version = "3.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1cbeb75ac695daf201ca2d66d9c684f873b135f28af4f2c79952478cab3b9d9"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"percent-encoding",
|
||||
"regex",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quick-xml"
|
||||
version = "0.30.0"
|
||||
@ -1450,8 +1475,11 @@ dependencies = [
|
||||
"http",
|
||||
"hyper-util",
|
||||
"lazy_static",
|
||||
"mime",
|
||||
"percent-encoding",
|
||||
"poem",
|
||||
"poem-openapi",
|
||||
"queryst",
|
||||
"rand",
|
||||
"regex",
|
||||
"reqwest",
|
||||
@ -2126,6 +2154,15 @@ dependencies = [
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicase"
|
||||
version = "2.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89"
|
||||
dependencies = [
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-bidi"
|
||||
version = "0.3.14"
|
||||
|
@ -11,8 +11,11 @@ futures-util = "0.3.30"
|
||||
http = "1.0.0"
|
||||
hyper-util = { version = "0.1.2", features = ["full"] }
|
||||
lazy_static = "1.4.0"
|
||||
poem = { version = "2.0.0", features = ["tokio-metrics", "websocket"] }
|
||||
mime = "0.3.17"
|
||||
percent-encoding = "2.3.1"
|
||||
poem = { version = "2.0.0", features = ["tokio-metrics", "websocket", "static-files"] }
|
||||
poem-openapi = { version = "4.0.0", features = ["swagger-ui"] }
|
||||
queryst = "3.0.0"
|
||||
rand = "0.8.5"
|
||||
regex = "1.10.2"
|
||||
reqwest = { git = "https://github.com/seanmonstar/reqwest.git", branch = "hyper-v1", version = "0.11.23" }
|
||||
|
12
regions/index.html
Normal file
12
regions/index.html
Normal file
@ -0,0 +1,12 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Hello, World!</title>
|
||||
</head>
|
||||
<body>
|
||||
<p>Hello, there!</p>
|
||||
<p>Here's the roadsign benchmarking test data!</p>
|
||||
</body>
|
||||
</html>
|
@ -5,5 +5,5 @@ id = "root"
|
||||
hosts = ["localhost"]
|
||||
paths = ["/"]
|
||||
[[locations.destinations]]
|
||||
id = "echo"
|
||||
uri = "https://postman-echo.com/get"
|
||||
id = "static"
|
||||
uri = "files://regions/index.html"
|
||||
|
1
regions/kokodayo.txt
Normal file
1
regions/kokodayo.txt
Normal file
@ -0,0 +1 @@
|
||||
Ko Ko Da Yo~
|
@ -2,7 +2,7 @@ mod config;
|
||||
mod proxies;
|
||||
mod sideload;
|
||||
|
||||
use poem::{listener::TcpListener, Endpoint, EndpointExt, Route, Server};
|
||||
use poem::{listener::TcpListener, EndpointExt, Route, Server};
|
||||
use poem_openapi::OpenApiService;
|
||||
use tracing::{error, info, Level};
|
||||
|
||||
|
52
src/proxies/browser.rs
Normal file
52
src/proxies/browser.rs
Normal file
@ -0,0 +1,52 @@
|
||||
use std::fmt::Write;
|
||||
|
||||
pub struct DirectoryTemplate<'a> {
|
||||
pub path: &'a str,
|
||||
pub files: Vec<FileRef>,
|
||||
}
|
||||
|
||||
impl<'a> DirectoryTemplate<'a> {
|
||||
pub fn render(&self) -> String {
|
||||
let mut s = format!(
|
||||
r#"
|
||||
<html>
|
||||
<head>
|
||||
<title>Index of {}</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Index of /{}</h1>
|
||||
<ul>"#,
|
||||
self.path, self.path
|
||||
);
|
||||
|
||||
for file in &self.files {
|
||||
if file.is_dir {
|
||||
let _ = write!(
|
||||
s,
|
||||
r#"<li><a href="{}">{}/</a></li>"#,
|
||||
file.url, file.filename
|
||||
);
|
||||
} else {
|
||||
let _ = write!(
|
||||
s,
|
||||
r#"<li><a href="{}">{}</a></li>"#,
|
||||
file.url, file.filename
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
s.push_str(
|
||||
r#"</ul>
|
||||
</body>
|
||||
</html>"#,
|
||||
);
|
||||
|
||||
s
|
||||
}
|
||||
}
|
||||
|
||||
pub struct FileRef {
|
||||
pub url: String,
|
||||
pub filename: String,
|
||||
pub is_dir: bool,
|
||||
}
|
@ -1,6 +1,10 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use queryst::parse;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::json;
|
||||
|
||||
use super::responder::StaticResponderConfig;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Region {
|
||||
@ -15,6 +19,7 @@ pub struct Location {
|
||||
pub paths: Vec<String>,
|
||||
pub headers: Option<HashMap<String, String>>,
|
||||
pub queries: Option<Vec<String>>,
|
||||
pub methods: Option<Vec<String>>,
|
||||
pub destinations: Vec<Destination>,
|
||||
}
|
||||
|
||||
@ -46,15 +51,35 @@ impl Destination {
|
||||
}
|
||||
|
||||
pub fn get_queries(&self) -> &str {
|
||||
self.uri.as_str().splitn(2, "?").collect::<Vec<_>>()[1]
|
||||
self.uri
|
||||
.as_str()
|
||||
.splitn(2, '?')
|
||||
.collect::<Vec<_>>()
|
||||
.get(1)
|
||||
.unwrap_or(&"")
|
||||
}
|
||||
|
||||
pub fn get_host(&self) -> &str {
|
||||
(self.uri.as_str().splitn(2, "://").collect::<Vec<_>>()[1])
|
||||
.splitn(2, "?")
|
||||
(self
|
||||
.uri
|
||||
.as_str()
|
||||
.splitn(2, "://")
|
||||
.collect::<Vec<_>>()
|
||||
.get(1)
|
||||
.unwrap_or(&""))
|
||||
.splitn(2, '?')
|
||||
.collect::<Vec<_>>()[0]
|
||||
}
|
||||
|
||||
pub fn get_websocket_uri(&self) -> Result<String, ()> {
|
||||
let parts = self.uri.as_str().splitn(2, "://").collect::<Vec<_>>();
|
||||
let url = parts.get(1).unwrap_or(&"");
|
||||
match self.get_protocol() {
|
||||
"http" | "https" => Ok(url.replace("http", "ws")),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_hypertext_uri(&self) -> Result<String, ()> {
|
||||
match self.get_protocol() {
|
||||
"http" => Ok("http://".to_string() + self.get_host()),
|
||||
@ -63,10 +88,32 @@ impl Destination {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_websocket_uri(&self) -> Result<String, ()> {
|
||||
let url = self.uri.as_str().splitn(2, "://").collect::<Vec<_>>()[1];
|
||||
pub fn get_static_config(&self) -> Result<StaticResponderConfig, ()> {
|
||||
match self.get_protocol() {
|
||||
"http" | "https" => Ok(url.replace("http", "ws")),
|
||||
"file" | "files" => {
|
||||
let queries = parse(self.get_queries()).unwrap_or(json!({}));
|
||||
Ok(StaticResponderConfig {
|
||||
uri: self.get_host().to_string(),
|
||||
utf8: queries
|
||||
.get("utf8")
|
||||
.and_then(|val| val.as_bool())
|
||||
.unwrap_or(false),
|
||||
with_slash: queries
|
||||
.get("slash")
|
||||
.and_then(|val| val.as_bool())
|
||||
.unwrap_or(false),
|
||||
browse: queries
|
||||
.get("browse")
|
||||
.and_then(|val| val.as_bool())
|
||||
.unwrap_or(false),
|
||||
index: queries
|
||||
.get("index")
|
||||
.and_then(|val| val.as_str().map(str::to_string)),
|
||||
fallback: queries
|
||||
.get("fallback")
|
||||
.and_then(|val| val.as_str().map(str::to_string)),
|
||||
})
|
||||
}
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
use http::Method;
|
||||
use poem::http::{HeaderMap, Uri};
|
||||
use regex::Regex;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@ -5,8 +6,10 @@ use wildmatch::WildMatch;
|
||||
|
||||
use self::config::{Location, Region};
|
||||
|
||||
pub mod browser;
|
||||
pub mod config;
|
||||
pub mod loader;
|
||||
pub mod responder;
|
||||
pub mod route;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
@ -19,7 +22,7 @@ impl Instance {
|
||||
Instance { regions: vec![] }
|
||||
}
|
||||
|
||||
pub fn filter(&self, uri: &Uri, headers: &HeaderMap) -> Option<&Location> {
|
||||
pub fn filter(&self, uri: &Uri, method: Method, headers: &HeaderMap) -> Option<&Location> {
|
||||
self.regions.iter().find_map(|region| {
|
||||
region.locations.iter().find(|location| {
|
||||
let mut hosts = location.hosts.iter();
|
||||
@ -37,6 +40,12 @@ impl Instance {
|
||||
return false;
|
||||
}
|
||||
|
||||
if let Some(val) = location.methods.clone() {
|
||||
if !val.iter().any(|item| *item == method.to_string()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(val) = location.headers.clone() {
|
||||
match !val.keys().all(|item| {
|
||||
headers.get(item).unwrap()
|
||||
|
221
src/proxies/responder.rs
Normal file
221
src/proxies/responder.rs
Normal file
@ -0,0 +1,221 @@
|
||||
use futures_util::{SinkExt, StreamExt};
|
||||
use http::{header, request::Builder, HeaderMap, Method, StatusCode, Uri};
|
||||
use lazy_static::lazy_static;
|
||||
use poem::{
|
||||
web::{websocket::WebSocket, StaticFileRequest},
|
||||
Body, Error, FromRequest, IntoResponse, Request, Response,
|
||||
};
|
||||
use std::{
|
||||
ffi::OsStr,
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
};
|
||||
use tokio::sync::RwLock;
|
||||
use tokio_tungstenite::connect_async;
|
||||
|
||||
use super::browser::{DirectoryTemplate, FileRef};
|
||||
|
||||
lazy_static! {
|
||||
pub static ref CLIENT: reqwest::Client = reqwest::Client::new();
|
||||
}
|
||||
|
||||
pub async fn repond_websocket(req: Builder, ws: WebSocket) -> Response {
|
||||
ws.on_upgrade(move |socket| async move {
|
||||
let (mut clientsink, mut clientstream) = socket.split();
|
||||
|
||||
// Start connection to server
|
||||
let (serversocket, _) = connect_async(req.body(()).unwrap()).await.unwrap();
|
||||
let (mut serversink, mut serverstream) = serversocket.split();
|
||||
|
||||
let client_live = Arc::new(RwLock::new(true));
|
||||
let server_live = client_live.clone();
|
||||
|
||||
tokio::spawn(async move {
|
||||
while let Some(Ok(msg)) = clientstream.next().await {
|
||||
if (serversink.send(msg.into()).await).is_err() {
|
||||
break;
|
||||
};
|
||||
if !*client_live.read().await {
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
*client_live.write().await = false;
|
||||
});
|
||||
|
||||
// Relay server messages to the client
|
||||
tokio::spawn(async move {
|
||||
while let Some(Ok(msg)) = serverstream.next().await {
|
||||
if (clientsink.send(msg.into()).await).is_err() {
|
||||
break;
|
||||
};
|
||||
if !*server_live.read().await {
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
*server_live.write().await = false;
|
||||
});
|
||||
})
|
||||
.into_response()
|
||||
}
|
||||
|
||||
pub async fn respond_hypertext(
|
||||
uri: String,
|
||||
ori: &Uri,
|
||||
method: Method,
|
||||
body: Body,
|
||||
headers: &HeaderMap,
|
||||
) -> Result<Response, Error> {
|
||||
let res = CLIENT
|
||||
.request(method, uri + ori.path() + ori.query().unwrap_or(""))
|
||||
.headers(headers.clone())
|
||||
.body(body.into_bytes().await.unwrap())
|
||||
.send()
|
||||
.await;
|
||||
|
||||
match res {
|
||||
Ok(result) => {
|
||||
let mut res = Response::default();
|
||||
res.extensions().clone_from(&result.extensions());
|
||||
result.headers().iter().for_each(|(key, val)| {
|
||||
res.headers_mut().insert(key, val.to_owned());
|
||||
});
|
||||
res.set_status(result.status());
|
||||
res.set_version(result.version());
|
||||
res.set_body(result.bytes().await.unwrap());
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
Err(error) => Err(Error::from_string(
|
||||
error.to_string(),
|
||||
error.status().unwrap_or(StatusCode::BAD_GATEWAY),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
pub struct StaticResponderConfig {
|
||||
pub uri: String,
|
||||
pub utf8: bool,
|
||||
pub with_slash: bool,
|
||||
pub browse: bool,
|
||||
pub index: Option<String>,
|
||||
pub fallback: Option<String>,
|
||||
}
|
||||
|
||||
pub async fn respond_static(
|
||||
cfg: StaticResponderConfig,
|
||||
method: Method,
|
||||
req: &Request,
|
||||
) -> Result<Response, Error> {
|
||||
if method != Method::GET {
|
||||
return Err(Error::from_string(
|
||||
"This destination only support GET request.",
|
||||
StatusCode::METHOD_NOT_ALLOWED,
|
||||
));
|
||||
}
|
||||
|
||||
let path = req
|
||||
.uri()
|
||||
.path()
|
||||
.trim_start_matches('/')
|
||||
.trim_end_matches('/');
|
||||
|
||||
let path = percent_encoding::percent_decode_str(path)
|
||||
.decode_utf8()
|
||||
.map_err(|_| Error::from_status(StatusCode::NOT_FOUND))?;
|
||||
|
||||
let base_path = cfg.uri.parse::<PathBuf>().unwrap();
|
||||
let mut file_path = base_path.clone();
|
||||
for p in Path::new(&*path) {
|
||||
if p == OsStr::new(".") {
|
||||
continue;
|
||||
} else if p == OsStr::new("..") {
|
||||
file_path.pop();
|
||||
} else {
|
||||
file_path.push(p);
|
||||
}
|
||||
}
|
||||
|
||||
if !file_path.starts_with(cfg.uri) {
|
||||
return Err(Error::from_status(StatusCode::FORBIDDEN));
|
||||
}
|
||||
|
||||
if !file_path.exists() {
|
||||
if let Some(file) = cfg.fallback {
|
||||
let fallback_path = base_path.join(file);
|
||||
if fallback_path.is_file() {
|
||||
return Ok(StaticFileRequest::from_request_without_body(req)
|
||||
.await?
|
||||
.create_response(&fallback_path, cfg.utf8)?
|
||||
.into_response());
|
||||
}
|
||||
}
|
||||
return Err(Error::from_status(StatusCode::NOT_FOUND));
|
||||
}
|
||||
|
||||
if file_path.is_file() {
|
||||
Ok(StaticFileRequest::from_request_without_body(req)
|
||||
.await?
|
||||
.create_response(&file_path, cfg.utf8)?
|
||||
.into_response())
|
||||
} else {
|
||||
if cfg.with_slash
|
||||
&& !req.original_uri().path().ends_with('/')
|
||||
&& (cfg.index.is_some() || cfg.browse)
|
||||
{
|
||||
let redirect_to = format!("{}/", req.original_uri().path());
|
||||
return Ok(Response::builder()
|
||||
.status(StatusCode::FOUND)
|
||||
.header(header::LOCATION, redirect_to)
|
||||
.finish());
|
||||
}
|
||||
|
||||
if let Some(index_file) = &cfg.index {
|
||||
let index_path = file_path.join(index_file);
|
||||
if index_path.is_file() {
|
||||
return Ok(StaticFileRequest::from_request_without_body(req)
|
||||
.await?
|
||||
.create_response(&index_path, cfg.utf8)?
|
||||
.into_response());
|
||||
}
|
||||
}
|
||||
|
||||
if cfg.browse {
|
||||
let read_dir = file_path
|
||||
.read_dir()
|
||||
.map_err(|_| Error::from_status(StatusCode::FORBIDDEN))?;
|
||||
let mut template = DirectoryTemplate {
|
||||
path: &path,
|
||||
files: Vec::new(),
|
||||
};
|
||||
|
||||
for res in read_dir {
|
||||
let entry = res.map_err(|_| Error::from_status(StatusCode::FORBIDDEN))?;
|
||||
|
||||
if let Some(filename) = entry.file_name().to_str() {
|
||||
let mut base_url = req.original_uri().path().to_string();
|
||||
if !base_url.ends_with('/') {
|
||||
base_url.push('/');
|
||||
}
|
||||
let filename_url = percent_encoding::percent_encode(
|
||||
filename.as_bytes(),
|
||||
percent_encoding::NON_ALPHANUMERIC,
|
||||
);
|
||||
template.files.push(FileRef {
|
||||
url: format!("{base_url}{filename_url}"),
|
||||
filename: filename.to_string(),
|
||||
is_dir: entry.path().is_dir(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let html = template.render();
|
||||
Ok(Response::builder()
|
||||
.header(header::CONTENT_TYPE, mime::TEXT_HTML_UTF_8.as_ref())
|
||||
.body(Body::from_string(html)))
|
||||
} else {
|
||||
Err(Error::from_status(StatusCode::NOT_FOUND))
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,3 @@
|
||||
use futures_util::{SinkExt, StreamExt};
|
||||
use poem::{
|
||||
handler,
|
||||
http::{HeaderMap, StatusCode, Uri},
|
||||
@ -8,16 +7,10 @@ use poem::{
|
||||
use rand::seq::SliceRandom;
|
||||
use reqwest::Method;
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::RwLock;
|
||||
use tokio_tungstenite::connect_async;
|
||||
|
||||
use crate::proxies::config::{Destination, DestinationType};
|
||||
|
||||
lazy_static! {
|
||||
pub static ref CLIENT: reqwest::Client = reqwest::Client::new();
|
||||
}
|
||||
use crate::proxies::{
|
||||
config::{Destination, DestinationType},
|
||||
responder,
|
||||
};
|
||||
|
||||
#[handler]
|
||||
pub async fn handle(
|
||||
@ -28,7 +21,7 @@ pub async fn handle(
|
||||
method: Method,
|
||||
body: Body,
|
||||
) -> Result<impl IntoResponse, Error> {
|
||||
let location = match app.filter(uri, headers) {
|
||||
let location = match app.filter(uri, method.clone(), headers) {
|
||||
Some(val) => val,
|
||||
None => {
|
||||
return Err(Error::from_string(
|
||||
@ -50,13 +43,13 @@ pub async fn handle(
|
||||
headers: &HeaderMap,
|
||||
method: Method,
|
||||
body: Body,
|
||||
) -> Result<impl IntoResponse, Error> {
|
||||
) -> Result<Response, Error> {
|
||||
// Handle websocket
|
||||
if let Ok(ws) = WebSocket::from_request_without_body(req).await {
|
||||
// Get uri
|
||||
let Ok(uri) = end.get_websocket_uri() else {
|
||||
return Err(Error::from_string(
|
||||
"Proxy endpoint not configured to support websockets!",
|
||||
"This destination was not support websockets.",
|
||||
StatusCode::NOT_IMPLEMENTED,
|
||||
));
|
||||
};
|
||||
@ -68,47 +61,7 @@ pub async fn handle(
|
||||
}
|
||||
|
||||
// Start the websocket connection
|
||||
return Ok(ws
|
||||
.on_upgrade(move |socket| async move {
|
||||
let (mut clientsink, mut clientstream) = socket.split();
|
||||
|
||||
// Start connection to server
|
||||
let (serversocket, _) = connect_async(ws_req.body(()).unwrap()).await.unwrap();
|
||||
let (mut serversink, mut serverstream) = serversocket.split();
|
||||
|
||||
let client_live = Arc::new(RwLock::new(true));
|
||||
let server_live = client_live.clone();
|
||||
|
||||
tokio::spawn(async move {
|
||||
while let Some(Ok(msg)) = clientstream.next().await {
|
||||
match serversink.send(msg.into()).await {
|
||||
Err(_) => break,
|
||||
_ => {}
|
||||
};
|
||||
if !*client_live.read().await {
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
*client_live.write().await = false;
|
||||
});
|
||||
|
||||
// Relay server messages to the client
|
||||
tokio::spawn(async move {
|
||||
while let Some(Ok(msg)) = serverstream.next().await {
|
||||
match clientsink.send(msg.into()).await {
|
||||
Err(_) => break,
|
||||
_ => {}
|
||||
};
|
||||
if !*server_live.read().await {
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
*server_live.write().await = false;
|
||||
});
|
||||
})
|
||||
.into_response());
|
||||
return Ok(responder::repond_websocket(ws_req, ws).await);
|
||||
}
|
||||
|
||||
// Handle normal web request
|
||||
@ -116,40 +69,29 @@ pub async fn handle(
|
||||
DestinationType::Hypertext => {
|
||||
let Ok(uri) = end.get_hypertext_uri() else {
|
||||
return Err(Error::from_string(
|
||||
"Proxy endpoint not configured to support web requests!",
|
||||
"This destination was not support web requests.",
|
||||
StatusCode::NOT_IMPLEMENTED,
|
||||
));
|
||||
};
|
||||
|
||||
let res = CLIENT
|
||||
.request(method, uri + ori.path() + ori.query().unwrap_or(""))
|
||||
.headers(headers.clone())
|
||||
.body(body.into_bytes().await.unwrap())
|
||||
.send()
|
||||
.await;
|
||||
|
||||
match res {
|
||||
Ok(result) => {
|
||||
let mut res = Response::default();
|
||||
res.extensions().clone_from(&result.extensions());
|
||||
result.headers().iter().for_each(|(key, val)| {
|
||||
res.headers_mut().insert(key, val.to_owned());
|
||||
});
|
||||
res.set_status(result.status());
|
||||
res.set_version(result.version());
|
||||
res.set_body(result.bytes().await.unwrap());
|
||||
Ok(res)
|
||||
responder::respond_hypertext(uri, ori, method, body, headers).await
|
||||
}
|
||||
DestinationType::StaticFiles => {
|
||||
let Ok(cfg) = end.get_static_config() else {
|
||||
return Err(Error::from_string(
|
||||
"This destination was not support static files.",
|
||||
StatusCode::NOT_IMPLEMENTED,
|
||||
));
|
||||
};
|
||||
|
||||
Err(error) => Err(Error::from_string(
|
||||
error.to_string(),
|
||||
error.status().unwrap_or(StatusCode::BAD_GATEWAY),
|
||||
responder::respond_static(cfg, method, req).await
|
||||
}
|
||||
_ => Err(Error::from_string(
|
||||
"Unsupported destination protocol.",
|
||||
StatusCode::NOT_IMPLEMENTED,
|
||||
)),
|
||||
}
|
||||
}
|
||||
_ => Err(Error::from_status(StatusCode::NOT_IMPLEMENTED)),
|
||||
}
|
||||
}
|
||||
|
||||
forward(destination, req, uri, headers, method, body).await
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user