Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions crates/cli/src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,13 @@ pub struct ServerOptions {
help = "Use mkcert to automatically generate and install local development certificates for HTTPS. This will create certificates for localhost and 127.0.0.1."
)]
pub mkcert: bool,

/// Add X-Torii-Host response header with hostname
#[arg(
long = "http.hostname_header",
help = "When set, adds X-Torii-Host response header with the system hostname to every response."
)]
pub hostname_header: bool,
}

impl Default for ServerOptions {
Expand All @@ -373,6 +380,7 @@ impl Default for ServerOptions {
tls_cert_path: None,
tls_key_path: None,
mkcert: false,
hostname_header: false,
}
}
}
Expand Down
1 change: 1 addition & 0 deletions crates/runner/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -767,6 +767,7 @@ impl Runner {
http2_keepalive_interval: self.args.grpc.http2_keepalive_interval,
http2_keepalive_timeout: self.args.grpc.http2_keepalive_timeout,
},
self.args.server.hostname_header,
);

// Handle mkcert certificate generation
Expand Down
3 changes: 3 additions & 0 deletions crates/server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,6 @@ filetime = "0.2"
tokio-rustls = "0.24.1"
rustls = "0.21.12"
rustls-pemfile = "1.0.4"

# Hostname support
hostname = "0.4"
66 changes: 52 additions & 14 deletions crates/server/src/proxy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ pub struct Proxy<P: Provider + Sync + Send + Debug + 'static> {
tls_config: Option<Arc<ServerConfig>>,
grpc_proxy_client: Arc<ReverseProxy<HttpConnector<GaiResolver>>>,
websocket_proxy_client: Arc<ReverseProxy<HttpConnector<GaiResolver>>>,
hostname: Option<String>,
_provider: std::marker::PhantomData<P>,
}

Expand Down Expand Up @@ -172,6 +173,7 @@ impl<P: Provider + Sync + Send + Debug + 'static> Proxy<P> {
provider: P,
version_spec: String,
proxy_settings: ProxySettings,
hostname_header_enabled: bool,
) -> Self {
// Create proxy clients with configured settings
let grpc_proxy_client = Arc::new(create_grpc_proxy_client(&proxy_settings));
Expand All @@ -192,6 +194,13 @@ impl<P: Provider + Sync + Send + Debug + 'static> Proxy<P> {

let handlers: Arc<RwLock<Vec<Box<dyn Handler>>>> = Arc::new(RwLock::new(handlers));

// Get hostname if the flag is enabled
let hostname = if hostname_header_enabled {
hostname::get().ok().and_then(|h| h.into_string().ok())
} else {
None
};

Self {
addr,
allowed_origins,
Expand All @@ -200,6 +209,7 @@ impl<P: Provider + Sync + Send + Debug + 'static> Proxy<P> {
tls_config: None,
grpc_proxy_client,
websocket_proxy_client,
hostname,
_provider: std::marker::PhantomData,
}
}
Expand Down Expand Up @@ -316,6 +326,7 @@ impl<P: Provider + Sync + Send + Debug + 'static> Proxy<P> {
let handlers = self.handlers.clone();
let version_spec = self.version_spec.clone();
let cors_layer = cors_layer.clone();
let hostname = self.hostname.clone();

tokio::spawn(async move {
match tls_acceptor.accept(stream).await {
Expand All @@ -325,13 +336,13 @@ impl<P: Provider + Sync + Send + Debug + 'static> Proxy<P> {
.service_fn(move |req| {
let handlers = handlers.clone();
let version_spec = version_spec.clone();
let hostname = hostname.clone();
async move {
let handlers = handlers.read().await;
handle(remote_addr.ip(), req, &handlers, &version_spec).await
handle(remote_addr.ip(), req, &handlers, &version_spec, hostname.as_deref()).await
}
});


if let Err(e) = hyper::server::conn::Http::new()
.serve_connection(tls_stream, service)
.with_upgrades() // Enable connection upgrades for WebSocket over TLS
Expand Down Expand Up @@ -365,21 +376,31 @@ impl<P: Provider + Sync + Send + Debug + 'static> Proxy<P> {
} else {
// HTTP server
let cors_layer = self.create_cors_layer();
let hostname = self.hostname.clone();
let make_svc = make_service_fn(move |conn: &AddrStream| {
let remote_addr = conn.remote_addr().ip();
let handlers = self.handlers.clone();
let version_spec = self.version_spec.clone();
let cors_layer = cors_layer.clone();
let hostname = hostname.clone();

let service =
ServiceBuilder::new()
.option_layer(cors_layer)
.service_fn(move |req| {
let handlers = handlers.clone();
let version_spec = version_spec.clone();
let hostname = hostname.clone();
async move {
let handlers = handlers.read().await;
handle(remote_addr, req, &handlers, &version_spec).await
handle(
remote_addr,
req,
&handlers,
&version_spec,
hostname.as_deref(),
)
.await
}
});

Expand All @@ -403,24 +424,41 @@ async fn handle(
req: Request<Body>,
handlers: &[Box<dyn Handler>],
version_spec: &str,
hostname: Option<&str>,
) -> Result<Response<Body>, Infallible> {
let mut response = None;

for handler in handlers.iter() {
if handler.should_handle(&req) {
return Ok(handler.handle(req, client_ip).await);
response = Some(handler.handle(req, client_ip).await);
break;
}
}

// Default response if no handler matches
let json = json!({
"service": "torii",
"version": version_spec,
"success": true,

let mut response = response.unwrap_or_else(|| {
let json = json!({
"service": "torii",
"version": version_spec,
"success": true,
});

Response::builder()
.status(StatusCode::OK)
.header(CONTENT_TYPE, "application/json")
.body(Body::from(json.to_string()))
.unwrap()
});

Ok(Response::builder()
.status(StatusCode::OK)
.header(CONTENT_TYPE, "application/json")
.body(Body::from(json.to_string()))
.unwrap())
// Add hostname header if configured
if let Some(hostname_value) = hostname {
response.headers_mut().insert(
HeaderName::from_static("x-torii-host"),
hostname_value
.parse()
.unwrap_or_else(|_| http::HeaderValue::from_static("unknown")),
);
}

Ok(response)
}
Loading