Skip to content

Commit

Permalink
Run the request tests as a component (#386)
Browse files Browse the repository at this point in the history
* Run the `request` tests as a component

* Return the length needed for cursor operations as well
  • Loading branch information
elliottt authored Jun 17, 2024
1 parent 6adfdd3 commit d3870da
Show file tree
Hide file tree
Showing 7 changed files with 100 additions and 71 deletions.
10 changes: 10 additions & 0 deletions cli/tests/integration/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,13 @@ async fn request_works() -> TestResult {
assert_eq!(resp.status(), StatusCode::OK);
Ok(())
}

#[tokio::test(flavor = "multi_thread")]
async fn request_works_component() -> TestResult {
let resp = Test::using_fixture("request.wasm")
.adapt_component()
.against_empty()
.await?;
assert_eq!(resp.status(), StatusCode::OK);
Ok(())
}
16 changes: 13 additions & 3 deletions lib/src/component/headers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,35 @@ type MultiValueCursor = u32;
/// Write multiple values out to a single buffer, until the iterator is exhausted, or `max_len`
/// bytes have been written. In the case that there are still values remaining, the second value of
/// the returned tuple will be `Some`.
///
/// If it's not possible to fit a single value inside a buffer of length `max_len`, an error will
/// be returned with the size necessary for the first element of the collection.
pub fn write_values<I, T>(
iter: I,
terminator: u8,
max_len: usize,
mut cursor: MultiValueCursor,
) -> (Vec<u8>, Option<MultiValueCursor>)
cursor_start: MultiValueCursor,
) -> Result<(Vec<u8>, Option<MultiValueCursor>), usize>
where
I: Iterator<Item = T>,
T: AsRef<[u8]>,
{
let mut buf = Vec::with_capacity(max_len);

let mut cursor = cursor_start;
let mut finished = true;
let skip_amt = usize::try_from(cursor).expect("u32 can fit in usize");
for item in iter.skip(skip_amt) {
let bytes = item.as_ref();

let needed = buf.len() + bytes.len() + 1;
if needed > max_len {
// If we haven't written a single entry yet, return an error indicating how much space
// we would need to write a single entry.
if cursor == cursor_start {
return Err(needed);
}

finished = false;
break;
}
Expand All @@ -34,5 +44,5 @@ where

let cursor = if finished { None } else { Some(cursor) };

(buf, cursor)
Ok((buf, cursor))
}
29 changes: 19 additions & 10 deletions lib/src/component/http_body.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,12 +168,17 @@ impl http_body::Host for Session {
b'\0',
usize::try_from(max_len).unwrap(),
cursor,
);
if buf.is_empty() && next.is_none() {
return Ok(None);
}
)
.map_err(|needed| types::Error::BufferLen(u64::try_from(needed).unwrap_or(0)))?;

Ok(Some((buf, next)))
// At this point we know that the buffer being empty will also mean that there are no
// remaining entries to read.
if buf.is_empty() {
debug_assert!(next.is_none());
Ok(None)
} else {
Ok(Some((buf, next)))
}
}

async fn trailer_value_get(
Expand Down Expand Up @@ -241,12 +246,16 @@ impl http_body::Host for Session {
b'\0',
usize::try_from(max_len).unwrap(),
cursor,
);
)
.map_err(|needed| types::Error::BufferLen(u64::try_from(needed).unwrap_or(0)))?;

if buf.is_empty() && next.is_none() {
return Ok(None);
// At this point we know that the buffer being empty will also mean that there are no
// remaining entries to read.
if buf.is_empty() {
debug_assert!(next.is_none());
Ok(None)
} else {
Ok(Some((buf, next)))
}

Ok(Some((buf, next)))
}
}
50 changes: 25 additions & 25 deletions lib/src/component/http_req.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,9 @@ impl http_req::Host for Session {
let req_method = &req.method;

if req_method.as_str().len() > usize::try_from(max_len).unwrap() {
return Err(Error::BufferLengthError {
buf: "method",
len: "method_max_len",
}
.into());
return Err(types::Error::BufferLen(
u64::try_from(req_method.as_str().len()).unwrap(),
));
}

Ok(req_method.to_string())
Expand All @@ -79,11 +77,7 @@ impl http_req::Host for Session {
let res = req_uri.to_string();

if res.len() > usize::try_from(max_len).unwrap() {
return Err(Error::BufferLengthError {
buf: "reqid_out",
len: "reqid_max_len",
}
.into());
return Err(types::Error::BufferLen(u64::try_from(res.len()).unwrap()));
}

Ok(res)
Expand Down Expand Up @@ -185,13 +179,17 @@ impl http_req::Host for Session {
b'\0',
usize::try_from(max_len).unwrap(),
cursor,
);
)
.map_err(|needed| types::Error::BufferLen(u64::try_from(needed).unwrap_or(0)))?;

if buf.is_empty() && next.is_none() {
return Ok(None);
// At this point we know that the buffer being empty will also mean that there are no
// remaining entries to read.
if buf.is_empty() {
debug_assert!(next.is_none());
Ok(None)
} else {
Ok(Some((buf, next)))
}

Ok(Some((buf, next)))
}

async fn header_value_get(
Expand Down Expand Up @@ -234,13 +232,17 @@ impl http_req::Host for Session {
b'\0',
usize::try_from(max_len).unwrap(),
cursor,
);
)
.map_err(|needed| types::Error::BufferLen(u64::try_from(needed).unwrap_or(0)))?;

if buf.is_empty() && next.is_none() {
return Ok(None);
// At this point we know that the buffer being empty will also mean that there are no
// remaining entries to read.
if buf.is_empty() {
debug_assert!(next.is_none());
Ok(None)
} else {
Ok(Some((buf, next)))
}

Ok(Some((buf, next)))
}

async fn header_values_set(
Expand Down Expand Up @@ -759,11 +761,9 @@ impl http_req::Host for Session {
let result = format!("{:032x}", self.req_id());

if result.len() > usize::try_from(max_len).unwrap() {
return Err(Error::BufferLengthError {
buf: "reqid_out",
len: "reqid_max_len",
}
.into());
return Err(types::Error::BufferLen(
u64::try_from(result.len()).unwrap(),
));
}

Ok(result)
Expand Down
42 changes: 16 additions & 26 deletions lib/src/component/http_resp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,22 +87,17 @@ impl http_resp::Host for Session {
b'\0',
usize::try_from(max_len).unwrap(),
cursor,
);
)
.map_err(|needed| types::Error::BufferLen(u64::try_from(needed).unwrap_or(0)))?;

// At this point we know that the buffer being empty will also mean that there are no
// remaining entries to read.
if buf.is_empty() {
if next.is_none() {
return Ok(None);
} else {
// It's an error if we couldn't write even a single value.
return Err(Error::BufferLengthError {
buf: "buf",
len: "buf.len()",
}
.into());
}
debug_assert!(next.is_none());
Ok(None)
} else {
Ok(Some((buf, next)))
}

Ok(Some((buf, next)))
}

async fn header_value_get(
Expand Down Expand Up @@ -155,22 +150,17 @@ impl http_resp::Host for Session {
b'\0',
usize::try_from(max_len).unwrap(),
cursor,
);
)
.map_err(|needed| types::Error::BufferLen(u64::try_from(needed).unwrap_or(0)))?;

// At this point we know that the buffer being empty will also mean that there are no
// remaining entries to read.
if buf.is_empty() {
if next.is_none() {
return Ok(None);
} else {
// It's an error if we couldn't write even a single value.
return Err(Error::BufferLengthError {
buf: "buf",
len: "buf.len()",
}
.into());
}
debug_assert!(next.is_none());
Ok(None)
} else {
Ok(Some((buf, next)))
}

Ok(Some((buf, next)))
}
}
}
Expand Down
6 changes: 6 additions & 0 deletions lib/src/component/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ impl types::Host for Session {
}
}

impl From<types::Error> for TrappableError {
fn from(e: types::Error) -> Self {
Self::Error(e)
}
}

impl From<HandleError> for TrappableError {
fn from(_: HandleError) -> Self {
Self::Error(types::Error::BadHandle)
Expand Down
18 changes: 11 additions & 7 deletions test-fixtures/src/bin/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -235,11 +235,13 @@ fn test_header_value_get_and_insert() {

// Test that an attempt to get a too-long header name fails.
nwritten = 0;
let long_header =
Vec::from_iter(hdr_name.iter().cycle().take(HEADER_LEN_TOO_LONG).copied());
assert_eq!(
header_value_get(
req,
hdr_name.as_ptr(),
HEADER_LEN_TOO_LONG,
long_header.as_ptr(),
long_header.len(),
good_buffer.as_mut_ptr(),
good_max,
&mut nwritten
Expand All @@ -252,8 +254,8 @@ fn test_header_value_get_and_insert() {
assert_eq!(
header_insert(
req,
hdr_name.as_ptr(),
HEADER_LEN_TOO_LONG,
long_header.as_ptr(),
long_header.len(),
hdr_val.as_ptr(),
hdr_val.len(),
),
Expand Down Expand Up @@ -299,11 +301,13 @@ fn test_header_append_and_remove() {
);

// Test that an attempt to append a too-long header name fails.
let long_header =
Vec::from_iter(hdr_name.iter().cycle().take(HEADER_LEN_TOO_LONG).copied());
assert_eq!(
header_append(
req,
hdr_name.as_ptr(),
HEADER_LEN_TOO_LONG,
long_header.as_ptr(),
long_header.len(),
hdr_val.as_ptr(),
hdr_val.len(),
),
Expand All @@ -312,7 +316,7 @@ fn test_header_append_and_remove() {

// Test that an attempt to remove a too-long header name fails.
assert_eq!(
header_remove(req, hdr_name.as_ptr(), HEADER_LEN_TOO_LONG,),
header_remove(req, long_header.as_ptr(), long_header.len()),
FastlyStatus::INVAL
);

Expand Down

0 comments on commit d3870da

Please sign in to comment.