diff --git a/lib/compute-at-edge-abi/compute-at-edge.witx b/lib/compute-at-edge-abi/compute-at-edge.witx index 3306aac3..5aa0731d 100644 --- a/lib/compute-at-edge-abi/compute-at-edge.witx +++ b/lib/compute-at-edge-abi/compute-at-edge.witx @@ -242,7 +242,14 @@ (param $ja4_max_len (@witx usize)) (param $nwritten_out (@witx pointer (@witx usize))) (result $err (expected (error $fastly_status))) - ) + ) + + (@interface func (export "downstream_compliance_region") + (param $region_out (@witx pointer (@witx char8))) + (param $region_max_len (@witx usize)) + (param $nwritten_out (@witx pointer (@witx usize))) + (result $err (expected (error $fastly_status))) + ) (@interface func (export "new") (result $err (expected $request_handle (error $fastly_status))) diff --git a/lib/src/component/http_req.rs b/lib/src/component/http_req.rs index 9e64fa47..32fb6841 100644 --- a/lib/src/component/http_req.rs +++ b/lib/src/component/http_req.rs @@ -810,9 +810,15 @@ impl http_req::Host for Session { async fn downstream_compliance_region( &mut self, - _max_len: u64, + region_max_len: u64, ) -> Result, types::Error> { - Err(Error::NotAvailable("Client TLS JA4 hash").into()) + let region = Session::downstream_compliance_region(self); + let region_len = region.len(); + + match u64::try_from(region_len) { + Ok(region_len) if region_len <= region_max_len => Ok(region.into()), + too_large => Err(types::Error::BufferLen(too_large.unwrap_or(0))), + } } async fn original_header_names_get( diff --git a/lib/src/session.rs b/lib/src/session.rs index 754d8f2e..8b1b5615 100644 --- a/lib/src/session.rs +++ b/lib/src/session.rs @@ -38,6 +38,8 @@ use { tokio::sync::oneshot::Sender, }; +const REGION_NONE: &[u8] = b"none"; + /// Data specific to an individual request, including any host-side /// allocations on behalf of the guest processing the request. pub struct Session { @@ -45,6 +47,11 @@ pub struct Session { downstream_client_addr: SocketAddr, /// The IP address and port that received this session. downstream_server_addr: SocketAddr, + /// The compliance region that this request was received in. + /// + /// For now this is just always `"none"`, but we place the field in the session + /// to make it easier to implement custom configuration values later on. + downstream_compliance_region: Vec, /// Handle for the downstream request "parts". NB the backing parts data can be mutated /// or even removed from the relevant map. downstream_req_handle: RequestHandle, @@ -165,6 +172,7 @@ impl Session { Session { downstream_server_addr: server_addr, downstream_client_addr: client_addr, + downstream_compliance_region: Vec::from(REGION_NONE), downstream_req_handle, downstream_req_body_handle, downstream_req_original_headers, @@ -204,6 +212,11 @@ impl Session { self.downstream_server_addr.ip() } + /// Retrieve the compliance region that received the request for this session. + pub fn downstream_compliance_region(&self) -> &[u8] { + self.downstream_compliance_region.as_slice() + } + /// Retrieve the handle corresponding to the downstream request. pub fn downstream_request(&self) -> RequestHandle { self.downstream_req_handle diff --git a/lib/src/wiggle_abi/req_impl.rs b/lib/src/wiggle_abi/req_impl.rs index 8af66e2e..23cd1ce8 100644 --- a/lib/src/wiggle_abi/req_impl.rs +++ b/lib/src/wiggle_abi/req_impl.rs @@ -300,6 +300,35 @@ impl FastlyHttpReq for Session { Err(Error::NotAvailable("Client TLS JA4 hash")) } + fn downstream_compliance_region( + &mut self, + memory: &mut GuestMemory<'_>, + // Must be a 16-byte array: + region_out: GuestPtr, + region_max_len: u32, + nwritten_out: GuestPtr, + ) -> Result<(), Error> { + let region = Session::downstream_compliance_region(self); + let region_len = region.len(); + + match u32::try_from(region_len) { + Ok(region_len) if region_len <= region_max_len => { + memory.copy_from_slice(region, region_out.as_array(region_max_len))?; + memory.write(nwritten_out, region_len.try_into().unwrap_or(0))?; + + Ok(()) + } + too_large => { + memory.write(nwritten_out, too_large.unwrap_or(0))?; + + Err(Error::BufferLengthError { + buf: "region_out", + len: "region_max_len", + }) + } + } + } + fn framing_headers_mode_set( &mut self, _memory: &mut GuestMemory<'_>,