Implement proxy_all for proxying entire domains
I wanted to add another domain behind a reverse proxy. The existing reverse proxy functionality seemed to only work for subpaths so I implemented this. (I'm happy with another better way if you can point one to me.) I also implemented streaming responses when reverse proxying. That way the responses stay as similar as possible to the upstream server.
This commit is contained in:
committed by
int 80h
parent
cb4ee95620
commit
7f3248e92d
@@ -29,6 +29,8 @@ pub struct Server {
|
||||
pub usrdir: Option<bool>,
|
||||
#[cfg(feature = "proxy")]
|
||||
pub proxy: Option<HashMap<String, String>>,
|
||||
#[cfg(feature = "proxy")]
|
||||
pub proxy_all: Option<String>,
|
||||
pub redirect: Option<HashMap<String, String>>,
|
||||
#[cfg(feature = "scgi")]
|
||||
pub scgi: Option<HashMap<String, String>>,
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
use std::io;
|
||||
use std::marker::Unpin;
|
||||
use std::net::SocketAddr;
|
||||
|
||||
use tokio::net::TcpStream;
|
||||
use tokio::prelude::*;
|
||||
use tokio_openssl::SslStream;
|
||||
@@ -40,4 +42,9 @@ impl Connection {
|
||||
self.stream.flush().await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn send_stream<S: AsyncRead + Unpin>(&mut self, reader: &mut S) -> Result<(), io::Error> {
|
||||
tokio::io::copy(reader, &mut self.stream).await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
19
src/main.rs
19
src/main.rs
@@ -238,6 +238,25 @@ async fn handle_connection(
|
||||
None => {}
|
||||
}
|
||||
|
||||
#[cfg(feature = "proxy")]
|
||||
if let Some(pr) = &srv.server.proxy_all {
|
||||
let host_port: Vec<&str> = pr.splitn(2, ':').collect();
|
||||
let host = host_port[0];
|
||||
let port: Option<u16>;
|
||||
if host_port.len() == 2 {
|
||||
port = host_port[1].parse().ok();
|
||||
} else {
|
||||
port = None;
|
||||
}
|
||||
|
||||
let mut upstream_url = url.clone();
|
||||
upstream_url.set_host(Some(host)).unwrap();
|
||||
upstream_url.set_port(port).unwrap();
|
||||
|
||||
revproxy::proxy_all(pr, upstream_url, con).await?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
#[cfg(feature = "proxy")]
|
||||
match &srv.server.proxy {
|
||||
Some(pr) => match url.path_segments().map(|c| c.collect::<Vec<_>>()) {
|
||||
|
||||
@@ -55,3 +55,39 @@ pub async fn proxy(addr: String, u: url::Url, mut con: conn::Connection) -> Resu
|
||||
con.send_raw(&buf).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn proxy_all(addr: &str, u: url::Url, mut con: conn::Connection) -> Result<(), io::Error> {
|
||||
let mut connector = SslConnector::builder(SslMethod::tls()).unwrap();
|
||||
connector.set_verify(openssl::ssl::SslVerifyMode::NONE);
|
||||
let config = connector.build().configure().unwrap();
|
||||
|
||||
// TCP handshake
|
||||
let stream = match TcpStream::connect(&addr).await {
|
||||
Ok(s) => s,
|
||||
Err(_) => {
|
||||
logger::logger(con.peer_addr, Status::ProxyError, u.as_str());
|
||||
con.send_status(Status::ProxyError, None).await?;
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
let domain = addr.splitn(2, ':').next().unwrap();
|
||||
|
||||
// TLS handshake with SNI
|
||||
let mut stream = match tokio_openssl::connect(config, domain, stream).await {
|
||||
Ok(s) => s,
|
||||
Err(_) => {
|
||||
logger::logger(con.peer_addr, Status::ProxyError, u.as_str());
|
||||
con.send_status(Status::ProxyError, None).await?;
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
|
||||
// send request: URL + CRLF
|
||||
stream.write_all(u.as_ref().as_bytes()).await?;
|
||||
stream.write_all(b"\r\n").await?;
|
||||
stream.flush().await?;
|
||||
|
||||
// stream to client
|
||||
con.send_stream(&mut stream).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user