Skip to content

Commit b78dae2

Browse files
committed
fix: remove panic on invalid content type
If the content type is invalid, an `unwrap` caused the `pactffi_with_body` function to panic. The panic has been replaced by a fallback to log an error, and fallback to auto-detection of the payload based on the body itself. Ref: pact-foundation/pact-js#1754 Ref: PACT-6546 Signed-off-by: JP-Ellis <josh@jpellis.me>
1 parent 03d7ae4 commit b78dae2

1 file changed

Lines changed: 55 additions & 9 deletions

File tree

rust/pact_ffi/src/mock_server/handles.rs

Lines changed: 55 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1795,9 +1795,10 @@ fn process_body(
17951795
/// embedded in the body. See
17961796
/// [IntegrationJson.md](https://github.com/pact-foundation/pact-reference/blob/master/rust/pact_ffi/IntegrationJson.md)
17971797
///
1798-
/// If the `content_type` is determined as follows, whichever is first:
1798+
/// The payload's content type is determined as follows, whichever is first:
17991799
///
1800-
/// - The `content_type` argument to this function
1800+
/// - The `content_type` argument to this function if provided. If the provided
1801+
/// value fails to parse, and error is logged and it will be ignored.
18011802
/// - The `Content-Type` header for HTTP interaction, or `contentType` metadata
18021803
/// entry for message interactions.
18031804
/// - From automatic detection of the body contents.
@@ -1819,11 +1820,7 @@ fn process_body(
18191820
///
18201821
/// # Error Handling
18211822
///
1822-
/// If the contents is a NULL pointer, it will set the body contents as null. If
1823-
/// the content type is a null pointer, or can't be parsed, it will set the
1824-
/// content type as TEXT. Returns false if the interaction or Pact can't be
1825-
/// modified (i.e. the mock server for it has already started) or an error has
1826-
/// occurred.
1823+
/// If the contents is a NULL pointer, it will set the body contents as null.
18271824
#[no_mangle]
18281825
pub extern "C" fn pactffi_with_body(
18291826
interaction: InteractionHandle,
@@ -1838,8 +1835,19 @@ pub extern "C" fn pactffi_with_body(
18381835
content_type,
18391836
body
18401837
);
1841-
let content_type =
1842-
convert_cstr("content_type", content_type).map(|ct| ContentType::parse(ct).unwrap());
1838+
let content_type = convert_cstr("content_type", content_type).and_then(|ct| {
1839+
match ContentType::parse(ct) {
1840+
Ok(parsed) => Some(parsed),
1841+
Err(err) => {
1842+
error!(
1843+
"Failed to parse '{}' as a content type ({}), falling back to auto-detection",
1844+
ct,
1845+
err,
1846+
);
1847+
None
1848+
}
1849+
}
1850+
});
18431851
trace!(?content_type);
18441852
let content_type_header = "Content-Type".to_string();
18451853
let body = convert_cstr("body", body).unwrap_or_default();
@@ -4486,4 +4494,42 @@ mod tests {
44864494
None
44874495
)
44884496
}
4497+
4498+
#[test]
4499+
fn pactffi_with_body_with_invalid_content_type_does_not_panic() {
4500+
let pact_handle = PactHandle::new("WithBodyInvalidCtC", "WithBodyInvalidCtP");
4501+
let description = CString::new("interaction with matcher as content type").unwrap();
4502+
let i_handle = pactffi_new_interaction(pact_handle, description.as_ptr());
4503+
4504+
// Replicates what pact-js passes when a regex matcher is set on Content-Type:
4505+
// contentTypeFromHeaders returned the full matcher JSON blob instead of the
4506+
// example value, and pactffi_with_body would panic trying to parse it as a
4507+
// MIME type. The documented contract says it should fall back gracefully.
4508+
let matcher_blob = CString::new(
4509+
r#"{"pact:matcher:type":"regex","regex":"^application\\/json","value":"application/json"}"#
4510+
).unwrap();
4511+
let body = CString::new(r#"{"id":"123"}"#).unwrap();
4512+
4513+
let result = pactffi_with_body(
4514+
i_handle,
4515+
InteractionPart::Request,
4516+
matcher_blob.as_ptr(),
4517+
body.as_ptr(),
4518+
);
4519+
4520+
let interaction = i_handle
4521+
.with_interaction(&|_, _, inner| inner.as_v4_http().unwrap())
4522+
.unwrap();
4523+
4524+
pactffi_free_pact_handle(pact_handle);
4525+
4526+
// Should not panic; function must return true and fall back gracefully
4527+
expect!(result).to(be_true());
4528+
// Body should be present
4529+
expect!(interaction.request.body.value()).to(be_some());
4530+
// Content-Type should be auto-detected as JSON from the body contents
4531+
let headers = interaction.request.headers.unwrap();
4532+
expect!(headers.get("Content-Type").unwrap().first().unwrap())
4533+
.to(be_equal_to(&JSON.to_string()));
4534+
}
44894535
}

0 commit comments

Comments
 (0)