Skip to content

feat: Allow emscripten users to define HTTP handler#1936

Open
cdmurph32 wants to merge 9 commits intomainfrom
expose_custom_http_handler_c_api
Open

feat: Allow emscripten users to define HTTP handler#1936
cdmurph32 wants to merge 9 commits intomainfrom
expose_custom_http_handler_c_api

Conversation

@cdmurph32
Copy link
Collaborator

Changes in this pull request

Follow-up to the Emscripten build support added in c2pa-rs.

Emscripten does not support reqwest or ureq, the standard http handlers in the Rust SDK. The Rust SDK supports users defining their own http handlers through the context. We need to expose this in the C Bindings.

Checklist

  • This PR represents a single feature, fix, or change.
  • All applicable changes have been documented.
  • Any TO DO items (or similar) have been entered as GitHub issues and the link to that issue has been included in a comment.

@codecov
Copy link

codecov bot commented Mar 16, 2026

Codecov Report

❌ Patch coverage is 0% with 83 lines in your changes missing coverage. Please review.
✅ Project coverage is 76.72%. Comparing base (b6d4ebc) to head (c7e8396).
⚠️ Report is 5 commits behind head on main.

Files with missing lines Patch % Lines
c2pa_c_ffi/src/c_api.rs 0.00% 83 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1936      +/-   ##
==========================================
+ Coverage   76.50%   76.72%   +0.21%     
==========================================
  Files         172      175       +3     
  Lines       41142    42254    +1112     
==========================================
+ Hits        31475    32418     +943     
- Misses       9667     9836     +169     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@codspeed-hq
Copy link

codspeed-hq bot commented Mar 16, 2026

Merging this PR will not alter performance

✅ 16 untouched benchmarks
⏩ 2 skipped benchmarks1


Comparing expose_custom_http_handler_c_api (c7e8396) with main (0e37237)

Open in CodSpeed

Footnotes

  1. 2 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@cdmurph32 cdmurph32 force-pushed the expose_custom_http_handler_c_api branch from 7f658f3 to bafb849 Compare March 16, 2026 15:56
@cdmurph32 cdmurph32 added the check-release Add this label to any PR to invoke a larger suite of tests. label Mar 17, 2026
@cdmurph32 cdmurph32 marked this pull request as ready for review March 17, 2026 17:00
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to add a cpp examples in this repo? This feels more like a c2pa-c example? (cc @gpeacock)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like we might also be able to reuse a lot of structs defined there.


use c2pa::http::HttpResolverError;

let uri_cstr = CString::new(request.uri().to_string())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a blocker here, but it would be clean if impl TryFrom<Request> for C2paHttpRequest and same for response.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll give it a shot.

Comment on lines +338 to +362
let response_body = if c_response.body.is_null() || c_response.body_len == 0 {
Vec::new()
} else {
unsafe { std::slice::from_raw_parts(c_response.body, c_response.body_len).to_vec() }
};

// Release the callback-owned buffer.
if let Some(free_fn) = c_response.body_free {
if !c_response.body.is_null() {
unsafe { free_fn(c_response.body) };
}
}

if rc != 0 {
let msg = CimplError::last_message()
.unwrap_or_else(|| "HTTP callback returned error".to_string());
return Err(HttpResolverError::Other(msg.into()));
}

let response = c2pa::http::http::Response::builder()
.status(c_response.status as u16)
.body(Box::new(std::io::Cursor::new(response_body)) as Box<dyn std::io::Read>)
.map_err(HttpResolverError::Http)?;
Ok(response)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be possible to return a Cursor over c_response.body rather than cloning it into a vec? We can free it through impl Drop for C2paHttpResponse.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes.

@cdmurph32 cdmurph32 force-pushed the expose_custom_http_handler_c_api branch from 5155fbe to 22317c4 Compare March 23, 2026 13:19
Copy link
Contributor

@ok-nick ok-nick left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work, looks great. Approving with the exception that we consider where the C++ example belongs.

///
/// # Safety
/// `self.body` must have been allocated with `malloc()` (or be null).
unsafe fn into_http_response(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can also implement TryFrom here too.

///
/// Use [`as_ffi`](OwnedC2paHttpRequest::as_ffi) to obtain the `#[repr(C)]` view
/// whose pointers borrow from this struct.
struct OwnedC2paHttpRequest {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How come you split OwnedC2paHttpRequest and C2paHttpRequest into separate structs?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

C2paHttpRequest needs *const c_char fields so that cbindgen can generate the bindings.
OwnedC2paHttpRequest owns the CString data (Box<[u8]>).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like we might also be able to reuse a lot of structs defined there.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

check-release Add this label to any PR to invoke a larger suite of tests.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants