diff --git a/lib/compute-at-edge-abi/compute-at-edge.witx b/lib/compute-at-edge-abi/compute-at-edge.witx index 48022a81..554d2d11 100644 --- a/lib/compute-at-edge-abi/compute-at-edge.witx +++ b/lib/compute-at-edge-abi/compute-at-edge.witx @@ -110,6 +110,23 @@ (param $nwritten_out (@witx pointer (@witx usize))) (result $err (expected (error $fastly_status))) ) + + ;;; Returns a u64 body length if the length of a body is known, or `FastlyStatus::None` + ;;; otherwise. + ;;; + ;;; If the length is unknown, it is likely due to the body arising from an HTTP/1.1 message with + ;;; chunked encoding, an HTTP/2 or later message with no `content-length`, or being a streaming + ;;; body. + ;;; + ;;; Note that receiving a length from this function does not guarantee that the full number of + ;;; bytes can actually be read from the body. For example, when proxying a response from a + ;;; backend, this length may reflect the `content-length` promised in the response, but if the + ;;; backend connection is closed prematurely, fewer bytes may be delivered before this body + ;;; handle can no longer be read. + (@interface func (export "known_length") + (param $h $body_handle) + (result $err (expected $body_length (error $fastly_status))) + ) ) (module $fastly_log @@ -214,6 +231,13 @@ (result $err (expected $num_bytes (error $fastly_status))) ) + (@interface func (export "downstream_tls_ja4") + (param $ja4_out (@witx pointer (@witx char8))) + (param $ja4_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/compute-at-edge-abi/typenames.witx b/lib/compute-at-edge-abi/typenames.witx index bec555b0..f3d5752d 100644 --- a/lib/compute-at-edge-abi/typenames.witx +++ b/lib/compute-at-edge-abi/typenames.witx @@ -356,3 +356,5 @@ (typename $rate u32) (typename $count u32) (typename $has u32) + +(typename $body_length u64) diff --git a/lib/src/body.rs b/lib/src/body.rs index 58f6ee2e..42419c5f 100644 --- a/lib/src/body.rs +++ b/lib/src/body.rs @@ -145,6 +145,20 @@ impl Body { self.chunks.push_front(chunk.into()) } } + + pub fn len(&self) -> Option { + let mut len = 0u64; + + for chunk in &self.chunks { + if let Chunk::HttpBody(body) = chunk { + len = len.checked_add(body.size_hint().exact()?)?; + } else { + return None; + } + } + + Some(len) + } } impl> From for Body { diff --git a/lib/src/wiggle_abi/body_impl.rs b/lib/src/wiggle_abi/body_impl.rs index d2d85e16..2c5005d6 100644 --- a/lib/src/wiggle_abi/body_impl.rs +++ b/lib/src/wiggle_abi/body_impl.rs @@ -11,7 +11,9 @@ use { session::Session, wiggle_abi::{ fastly_http_body::FastlyHttpBody, - types::{BodyHandle, BodyWriteEnd, MultiValueCursor, MultiValueCursorResult}, + types::{ + BodyHandle, BodyLength, BodyWriteEnd, MultiValueCursor, MultiValueCursorResult, + }, }, }, http_body::Body as HttpBody, @@ -215,4 +217,14 @@ impl FastlyHttpBody for Session { } Err(Error::Again) } + + fn known_length(&mut self, body_handle: BodyHandle) -> Result { + if self.is_streaming_body(body_handle) { + Err(Error::ValueAbsent) + } else if let Some(len) = self.body_mut(body_handle)?.len() { + Ok(len) + } else { + Err(Error::ValueAbsent) + } + } } diff --git a/lib/src/wiggle_abi/req_impl.rs b/lib/src/wiggle_abi/req_impl.rs index 3d6737a7..29b80a5e 100644 --- a/lib/src/wiggle_abi/req_impl.rs +++ b/lib/src/wiggle_abi/req_impl.rs @@ -226,6 +226,16 @@ impl FastlyHttpReq for Session { Err(Error::NotAvailable("Client TLS JA3 hash")) } + #[allow(unused_variables)] // FIXME UFSM 2024-02-19: Remove this directive once implemented. + fn downstream_tls_ja4( + &mut self, + ja4_out: &GuestPtr, + ja4_max_len: u32, + nwritten_out: &GuestPtr, + ) -> Result<(), Error> { + Err(Error::NotAvailable("Client TLS JA4 hash")) + } + fn framing_headers_mode_set( &mut self, _h: RequestHandle,