@@ -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]
18281825pub 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