mirror of
https://github.com/shadowsocks/shadowsocks-rust.git
synced 2026-11-07 00:13:33 +00:00
feat(http): Http local server builder
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
//! Shadowsocks HTTP Local Server
|
||||
|
||||
pub use self::server::Http;
|
||||
pub use self::server::{Http, HttpBuilder};
|
||||
|
||||
mod client_cache;
|
||||
mod connector;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
use std::{
|
||||
convert::Infallible,
|
||||
io::{self, ErrorKind},
|
||||
net::SocketAddr,
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
@@ -26,36 +27,70 @@ use crate::local::{
|
||||
|
||||
use super::{client_cache::ProxyClientCache, dispatcher::HttpDispatcher};
|
||||
|
||||
/// HTTP Local server builder
|
||||
pub struct HttpBuilder {
|
||||
context: Arc<ServiceContext>,
|
||||
client_config: ServerAddr,
|
||||
balancer: PingBalancer,
|
||||
}
|
||||
|
||||
impl HttpBuilder {
|
||||
/// Create a new HTTP Local server builder
|
||||
pub fn new(client_config: ServerAddr, balancer: PingBalancer) -> HttpBuilder {
|
||||
let context = ServiceContext::new();
|
||||
HttpBuilder::with_context(Arc::new(context), client_config, balancer)
|
||||
}
|
||||
|
||||
/// Create with an existed context
|
||||
pub fn with_context(
|
||||
context: Arc<ServiceContext>,
|
||||
client_config: ServerAddr,
|
||||
balancer: PingBalancer,
|
||||
) -> HttpBuilder {
|
||||
HttpBuilder {
|
||||
context,
|
||||
client_config,
|
||||
balancer,
|
||||
}
|
||||
}
|
||||
|
||||
/// Build HTTP server instance
|
||||
pub async fn build(self) -> io::Result<Http> {
|
||||
let listener = match self.client_config {
|
||||
ServerAddr::SocketAddr(sa) => TcpListener::bind_with_opts(&sa, self.context.accept_opts().clone()).await,
|
||||
ServerAddr::DomainName(ref dname, port) => lookup_then!(self.context.context_ref(), dname, port, |addr| {
|
||||
TcpListener::bind_with_opts(&addr, self.context.accept_opts().clone()).await
|
||||
})
|
||||
.map(|(_, b)| b),
|
||||
}?;
|
||||
|
||||
let proxy_client_cache = Arc::new(ProxyClientCache::new(self.context.clone()));
|
||||
|
||||
Ok(Http {
|
||||
context: self.context,
|
||||
proxy_client_cache,
|
||||
listener,
|
||||
balancer: self.balancer,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// HTTP Local server
|
||||
pub struct Http {
|
||||
context: Arc<ServiceContext>,
|
||||
proxy_client_cache: Arc<ProxyClientCache>,
|
||||
}
|
||||
|
||||
impl Default for Http {
|
||||
fn default() -> Self {
|
||||
Http::new()
|
||||
}
|
||||
listener: TcpListener,
|
||||
balancer: PingBalancer,
|
||||
}
|
||||
|
||||
impl Http {
|
||||
/// Create a new HTTP Local server
|
||||
pub fn new() -> Http {
|
||||
let context = ServiceContext::new();
|
||||
Http::with_context(Arc::new(context))
|
||||
}
|
||||
|
||||
/// Create with an existed context
|
||||
pub fn with_context(context: Arc<ServiceContext>) -> Http {
|
||||
let proxy_client_cache = Arc::new(ProxyClientCache::new(context.clone()));
|
||||
Http {
|
||||
context,
|
||||
proxy_client_cache,
|
||||
}
|
||||
/// Server's local address
|
||||
pub fn local_addr(&self) -> io::Result<SocketAddr> {
|
||||
self.listener.local_addr()
|
||||
}
|
||||
|
||||
/// Run server
|
||||
pub async fn run(self, client_config: &ServerAddr, balancer: PingBalancer) -> io::Result<()> {
|
||||
pub async fn run(self) -> io::Result<()> {
|
||||
let bypass_client = Client::builder()
|
||||
.http1_preserve_header_case(true)
|
||||
.http1_title_case_headers(true)
|
||||
@@ -63,6 +98,7 @@ impl Http {
|
||||
|
||||
let context = self.context.clone();
|
||||
let proxy_client_cache = self.proxy_client_cache.clone();
|
||||
let balancer = self.balancer;
|
||||
let make_service = make_service_fn(|socket: &AddrStream| {
|
||||
let client_addr = socket.remote_addr();
|
||||
let balancer = balancer.clone();
|
||||
@@ -85,27 +121,18 @@ impl Http {
|
||||
}
|
||||
});
|
||||
|
||||
let bind_result = match *client_config {
|
||||
ServerAddr::SocketAddr(sa) => TcpListener::bind_with_opts(&sa, self.context.accept_opts().clone()).await,
|
||||
ServerAddr::DomainName(ref dname, port) => lookup_then!(self.context.context_ref(), dname, port, |addr| {
|
||||
TcpListener::bind_with_opts(&addr, self.context.accept_opts().clone()).await
|
||||
})
|
||||
.map(|(_, b)| b),
|
||||
};
|
||||
let server = {
|
||||
let listener = self.listener.into_inner().into_std()?;
|
||||
let builder = match Server::from_tcp(listener) {
|
||||
Ok(builder) => builder,
|
||||
Err(err) => {
|
||||
error!("hyper server from std::net::TcpListener error: {}", err);
|
||||
let err = io::Error::new(ErrorKind::InvalidInput, err);
|
||||
return Err(err);
|
||||
}
|
||||
};
|
||||
|
||||
let server = match bind_result {
|
||||
Ok(listener) => {
|
||||
let listener = listener.into_inner().into_std()?;
|
||||
let builder = match Server::from_tcp(listener) {
|
||||
Ok(builder) => builder,
|
||||
Err(err) => {
|
||||
error!("hyper server from std::net::TcpListener error: {}", err);
|
||||
let err = io::Error::new(ErrorKind::InvalidInput, err);
|
||||
return Err(err);
|
||||
}
|
||||
};
|
||||
|
||||
builder
|
||||
builder
|
||||
.http1_only(true) // HTTP Proxy protocol only defined in HTTP 1.x
|
||||
.http1_preserve_header_case(true)
|
||||
.http1_title_case_headers(true)
|
||||
@@ -119,12 +146,6 @@ impl Http {
|
||||
)
|
||||
.tcp_nodelay(self.context.accept_opts().tcp.nodelay)
|
||||
.serve(make_service)
|
||||
}
|
||||
Err(err) => {
|
||||
error!("hyper server bind error: {}", err);
|
||||
let err = io::Error::new(ErrorKind::InvalidInput, err);
|
||||
return Err(err);
|
||||
}
|
||||
};
|
||||
|
||||
info!("shadowsocks HTTP listening on {}", server.local_addr());
|
||||
|
||||
@@ -288,17 +288,16 @@ pub async fn create(config: Config) -> io::Result<Server> {
|
||||
}
|
||||
#[cfg(feature = "local-http")]
|
||||
ProtocolType::Http => {
|
||||
use self::http::Http;
|
||||
use self::http::HttpBuilder;
|
||||
|
||||
let client_addr = match local_config.addr {
|
||||
Some(a) => a,
|
||||
None => return Err(io::Error::new(ErrorKind::Other, "http requires local address")),
|
||||
};
|
||||
|
||||
let server = Http::with_context(context.clone());
|
||||
vfut.push(ServerHandle(tokio::spawn(async move {
|
||||
server.run(&client_addr, balancer).await
|
||||
})));
|
||||
let builder = HttpBuilder::with_context(context.clone(), client_addr, balancer);
|
||||
let server = builder.build().await?;
|
||||
vfut.push(ServerHandle(tokio::spawn(async move { server.run().await })));
|
||||
}
|
||||
#[cfg(feature = "local-redir")]
|
||||
ProtocolType::Redir => {
|
||||
|
||||
@@ -11,8 +11,17 @@ use log::{debug, error, trace, warn};
|
||||
use shadowsocks::{
|
||||
config::Mode,
|
||||
relay::socks5::{
|
||||
self, Address, Command, Error as Socks5Error, HandshakeRequest, HandshakeResponse, PasswdAuthRequest,
|
||||
PasswdAuthResponse, Reply, TcpRequestHeader, TcpResponseHeader,
|
||||
self,
|
||||
Address,
|
||||
Command,
|
||||
Error as Socks5Error,
|
||||
HandshakeRequest,
|
||||
HandshakeResponse,
|
||||
PasswdAuthRequest,
|
||||
PasswdAuthResponse,
|
||||
Reply,
|
||||
TcpRequestHeader,
|
||||
TcpResponseHeader,
|
||||
},
|
||||
ServerAddr,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user