diff --git a/crates/http-service/src/executor/http.rs b/crates/http-service/src/executor/http.rs index cf20a9e..1db67b1 100644 --- a/crates/http-service/src/executor/http.rs +++ b/crates/http-service/src/executor/http.rs @@ -426,7 +426,7 @@ mod tests { let http_service: HttpService = assert_ok!(ServiceBuilder::new(context).build()); - let res = assert_ok!(http_service.handle_request("1".to_smolstr(), req).await); + let res = assert_ok!(http_service.handle_request(req).await); assert_eq!(StatusCode::OK, res.status()); let headers = res.headers(); assert_eq!(4, headers.len()); @@ -481,7 +481,7 @@ mod tests { let http_service: HttpService = assert_ok!(ServiceBuilder::new(context).build()); - let res = assert_ok!(http_service.handle_request("2".to_smolstr(), req).await); + let res = assert_ok!(http_service.handle_request(req).await); assert_eq!(FASTEDGE_EXECUTION_TIMEOUT, res.status()); let headers = res.headers(); assert_eq!(4, headers.len()); @@ -545,7 +545,7 @@ mod tests { let http_service: HttpService = assert_ok!(ServiceBuilder::new(context).build()); - let res = assert_ok!(http_service.handle_request("3".to_smolstr(), req).await); + let res = assert_ok!(http_service.handle_request(req).await); assert_eq!(FASTEDGE_OUT_OF_MEMORY, res.status()); let headers = res.headers(); assert_eq!(4, headers.len()); @@ -586,7 +586,7 @@ mod tests { let http_service: HttpService = assert_ok!(ServiceBuilder::new(context).build()); - let res = assert_ok!(http_service.handle_request("4".to_smolstr(), req).await); + let res = assert_ok!(http_service.handle_request(req).await); assert_eq!(StatusCode::NOT_FOUND, res.status()); assert_eq!(0, res.headers().len()); } @@ -614,7 +614,7 @@ mod tests { let http_service: HttpService = assert_ok!(ServiceBuilder::new(context).build()); - let res = assert_ok!(http_service.handle_request("5".to_smolstr(), req).await); + let res = assert_ok!(http_service.handle_request(req).await); assert_eq!(StatusCode::NOT_FOUND, res.status()); assert_eq!(0, res.headers().len()); } @@ -642,7 +642,7 @@ mod tests { let http_service: HttpService = assert_ok!(ServiceBuilder::new(context).build()); - let res = assert_ok!(http_service.handle_request("6".to_smolstr(), req).await); + let res = assert_ok!(http_service.handle_request(req).await); assert_eq!(StatusCode::TOO_MANY_REQUESTS, res.status()); assert_eq!(0, res.headers().len()); } @@ -670,7 +670,7 @@ mod tests { let http_service: HttpService = assert_ok!(ServiceBuilder::new(context).build()); - let res = assert_ok!(http_service.handle_request("7".to_smolstr(), req).await); + let res = assert_ok!(http_service.handle_request(req).await); assert_eq!(StatusCode::NOT_ACCEPTABLE, res.status()); assert_eq!(0, res.headers().len()); } @@ -703,7 +703,7 @@ mod tests { let http_service: HttpService = assert_ok!(ServiceBuilder::new(context).build()); - let res = assert_ok!(http_service.handle_request("8".to_smolstr(), req).await); + let res = assert_ok!(http_service.handle_request(req).await); assert_eq!(StatusCode::OK, res.status()); let headers = res.headers(); assert_eq!(4, headers.len()); @@ -742,7 +742,7 @@ mod tests { let http_service: HttpService = assert_ok!(ServiceBuilder::new(context).build()); - let res = assert_ok!(http_service.handle_request("9".to_smolstr(), req).await); + let res = assert_ok!(http_service.handle_request(req).await); // Reaching OK proves we resolved via lookup_by_id (otherwise our mock for // lookup_by_name path would have produced the same status, but we also // verify in `test_fastedge_app_id_invalid_returns_not_found` below that @@ -776,7 +776,7 @@ mod tests { let http_service: HttpService = assert_ok!(ServiceBuilder::new(context).build()); - let res = assert_ok!(http_service.handle_request("10".to_smolstr(), req).await); + let res = assert_ok!(http_service.handle_request(req).await); assert_eq!(StatusCode::NOT_FOUND, res.status()); assert_eq!(0, res.headers().len()); } @@ -807,7 +807,7 @@ mod tests { let http_service: HttpService = assert_ok!(ServiceBuilder::new(context).build()); - let res = assert_ok!(http_service.handle_request("11".to_smolstr(), req).await); + let res = assert_ok!(http_service.handle_request(req).await); assert_eq!(StatusCode::NOT_FOUND, res.status()); assert_eq!(0, res.headers().len()); } @@ -837,7 +837,7 @@ mod tests { let http_service: HttpService = assert_ok!(ServiceBuilder::new(context).build()); - let res = assert_ok!(http_service.handle_request("12".to_smolstr(), req).await); + let res = assert_ok!(http_service.handle_request(req).await); assert_eq!(StatusCode::NOT_FOUND, res.status()); assert_eq!(0, res.headers().len()); } @@ -869,7 +869,7 @@ mod tests { let http_service: HttpService = assert_ok!(ServiceBuilder::new(context).build()); - let res = assert_ok!(http_service.handle_request("13".to_smolstr(), req).await); + let res = assert_ok!(http_service.handle_request(req).await); assert_eq!(StatusCode::NOT_FOUND, res.status()); assert_eq!(0, res.headers().len()); } @@ -895,7 +895,7 @@ mod tests { let http_service: HttpService = assert_ok!(ServiceBuilder::new(context).build()); - let res = assert_ok!(http_service.handle_request("14".to_smolstr(), req).await); + let res = assert_ok!(http_service.handle_request(req).await); assert_eq!(StatusCode::NOT_FOUND, res.status()); assert_eq!(0, res.headers().len()); } diff --git a/crates/http-service/src/executor/wasi_http.rs b/crates/http-service/src/executor/wasi_http.rs index 0c57624..56d56bc 100644 --- a/crates/http-service/src/executor/wasi_http.rs +++ b/crates/http-service/src/executor/wasi_http.rs @@ -7,12 +7,13 @@ use crate::state::HttpState; use ::http::{HeaderMap, Request, Response, Uri, header}; use anyhow::{Context, anyhow, bail}; use async_trait::async_trait; -use http_backend::Backend; +use http_backend::{Backend, SERVER_NAME_HEADER}; use http_body_util::{BodyExt, Full}; use hyper::body::Body; use runtime::util::stats::{StatsTimer, StatsVisitor}; use runtime::{InstancePre, store::StoreBuilder}; use smol_str::SmolStr; +use tracing::Instrument; use wasmtime_wasi_http::bindings::ProxyPre; use wasmtime_wasi_http::bindings::http::types::Scheme; use wasmtime_wasi_http::{WasiHttpView, body::HyperOutgoingBody}; @@ -46,7 +47,19 @@ where let (mut parts, body) = req.into_parts(); const LOCALHOST: SmolStr = SmolStr::new_inline("localhost"); - let backend_hostname = self.backend.hostname().unwrap_or(LOCALHOST); + + // Resolve backend hostname using the following precedence: + // 1. `server_name` request header (if set and valid UTF-8) + // 2. backend's configured hostname + // 3. fallback to "localhost" + let backend_hostname: SmolStr = parts + .headers + .get(SERVER_NAME_HEADER) + .and_then(|v| v.to_str().ok()) + .map(SmolStr::from) + .or_else(|| self.backend.hostname()) + .unwrap_or(LOCALHOST); + let hostname = match backend_hostname.find('.') { None => backend_hostname.as_str(), Some(i) => { @@ -123,25 +136,28 @@ where let proxy = proxy_pre.instantiate_async(&mut store).await?; let task_stats = stats.clone(); - let task = tokio::task::spawn(async move { - let duration = Duration::from_millis(store.data().timeout); - if let Err(e) = tokio::time::timeout( - duration, - proxy - .wasi_http_incoming_handler() - .call_handle(&mut store, req, out), - ) - .await? - { - tracing::warn!(cause=?e, "incoming handler"); - return Err(e); - }; - - drop(stats_timer); // Stop timing for stats - task_stats.memory_used(store.memory_used() as u64); - - Ok(()) - }); + let task = tokio::task::spawn( + async move { + let duration = Duration::from_millis(store.data().timeout); + if let Err(e) = tokio::time::timeout( + duration, + proxy + .wasi_http_incoming_handler() + .call_handle(&mut store, req, out), + ) + .await? + { + tracing::warn!(cause=?e, "incoming handler"); + return Err(e); + }; + + drop(stats_timer); // Stop timing for stats + task_stats.memory_used(store.memory_used() as u64); + + Ok(()) + } + .in_current_span(), + ); match receiver.await { Ok(Ok(response)) => { diff --git a/crates/http-service/src/lib.rs b/crates/http-service/src/lib.rs index b1d5ee3..7eb4bd3 100644 --- a/crates/http-service/src/lib.rs +++ b/crates/http-service/src/lib.rs @@ -150,16 +150,13 @@ where Ok((stream, _)) => { tracing::debug!(remote=?stream.peer_addr(), "new http connection"); let connection = self_.clone(); - use tracing::Instrument; let io = TokioIo::new(stream); let service = service_fn(move |req| { let self_ = connection.clone(); - let request_id = remote_traceparent(&req); async move { self_ - .handle_request(request_id.clone(), req) - .instrument(tracing::debug_span!("http", ?request_id)) + .handle_request(req) .await } }); @@ -269,13 +266,13 @@ where /// handle HTTP request. async fn handle_request( &self, - request_id: SmolStr, mut request: hyper::Request, ) -> Result> where B: BodyExt + Send, ::Data: Send, { + let traceparent = remote_traceparent(&request); request .headers_mut() .extend(app_req_headers(self.context.append_headers())); @@ -285,13 +282,13 @@ where Err(error) => { #[cfg(feature = "metrics")] metrics::metrics(AppResult::UNKNOWN, HTTP_LABEL, None, None); - tracing::info!(cause=?error, "App name not provided"); + tracing::info!(cause=?error, traceparent = %traceparent, "App name not provided"); return not_found(); } Ok(app_name) => app_name, }; - let span = tracing::info_span!("handle", app = %app_name); + let span = tracing::info_span!("http", app = %app_name, traceparent = %traceparent); let _enter = span.enter(); // lookup for application config and binary_id @@ -359,7 +356,7 @@ where } }; - let stats = self.context.new_stats_row(&request_id, &app_name, &cfg); + let stats = self.context.new_stats_row(&traceparent, &app_name, &cfg); let response = match executor .execute(request, stats.clone()) @@ -383,7 +380,7 @@ where let (status_code, fail_reason, msg, internal_code) = map_err(error); stats.status_code(status_code); stats.fail_reason(fail_reason as i32); - tracing::debug!(?fail_reason, ?request_id, "stats"); + tracing::debug!(?fail_reason, ?traceparent, "stats"); #[cfg(feature = "metrics")] metrics::metrics( @@ -487,7 +484,7 @@ fn map_err(error: Error) -> (u16, AppResult, HyperOutgoingBody, u16) { (status_code, fail_reason, msg, internal_code) } -fn remote_traceparent(req: &hyper::Request) -> SmolStr { +fn remote_traceparent(req: &hyper::Request) -> SmolStr { req.headers() .get(TRACEPARENT) .and_then(|v| v.to_str().ok())