Skip to content

Commit 22317c4

Browse files
committed
fix: comment incorporation
* use libc::free instead of custom deallocator * impl TryFrom<Request/Response>
1 parent 471f600 commit 22317c4

File tree

4 files changed

+97
-59
lines changed

4 files changed

+97
-59
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

c2pa_c_ffi/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ c2pa = { path = "../sdk", version = "0.78.4", default-features = false, features
3333
"file_io",
3434
"pdf",
3535
] }
36+
libc = "0.2"
3637
scopeguard = "1.2.0"
3738
serde = { version = "1.0", features = ["derive"] }
3839
serde_json = "1.0"

c2pa_c_ffi/examples/emscripten_example.cpp

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -126,8 +126,6 @@ static std::vector<const char*> parse_headers(const std::string& raw) {
126126
return result;
127127
}
128128

129-
static void free_body(uint8_t* ptr) { free(ptr); }
130-
131129
static int emscripten_http_handler(
132130
void* /*ctx*/, const C2paHttpRequest* req, C2paHttpResponse* resp)
133131
{
@@ -150,7 +148,6 @@ static int emscripten_http_handler(
150148
resp->body_len = static_cast<size_t>(fetch->numBytes);
151149
resp->body = static_cast<uint8_t*>(malloc(resp->body_len));
152150
memcpy(resp->body, fetch->data, resp->body_len);
153-
resp->body_free = free_body;
154151
emscripten_fetch_close(fetch);
155152
return 0;
156153
}

c2pa_c_ffi/src/c_api.rs

Lines changed: 95 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -242,21 +242,102 @@ pub struct C2paHttpRequest {
242242
/// HTTP response filled in by the resolver callback.
243243
///
244244
/// The callback must set `status`, `body`, and `body_len`.
245-
/// `body` must point to callback-allocated memory. Rust will call
246-
/// `body_free(body)` to release it. If `body_free` is NULL, Rust
247-
/// assumes the body is stack- or arena-owned and will not free it
248-
/// (the data is copied before the callback returns).
245+
/// `body` must be allocated with `malloc()`. Rust will call `free()` on it
246+
/// after copying the data.
249247
#[repr(C)]
250248
pub struct C2paHttpResponse {
251249
/// HTTP status code (e.g. 200, 404)
252250
pub status: i32,
253-
/// Response body bytes, allocated by the callback.
251+
/// Response body bytes, allocated by the callback with `malloc()`.
252+
/// Rust takes ownership and will call `free()`.
254253
pub body: *mut c_uchar,
255254
/// Length of `body` in bytes
256255
pub body_len: usize,
257-
/// Deallocator for `body`. Called by Rust after copying the data.
258-
/// May be NULL if `body` does not need to be freed.
259-
pub body_free: Option<unsafe extern "C" fn(*mut c_uchar)>,
256+
}
257+
258+
/// Owns the backing storage for a [`C2paHttpRequest`].
259+
///
260+
/// Use [`as_ffi`](OwnedC2paHttpRequest::as_ffi) to obtain the `#[repr(C)]` view
261+
/// whose pointers borrow from this struct.
262+
struct OwnedC2paHttpRequest {
263+
url: std::ffi::CString,
264+
method: std::ffi::CString,
265+
headers: std::ffi::CString,
266+
body: Vec<u8>,
267+
}
268+
269+
impl TryFrom<c2pa::http::http::Request<Vec<u8>>> for OwnedC2paHttpRequest {
270+
type Error = c2pa::http::HttpResolverError;
271+
272+
fn try_from(request: c2pa::http::http::Request<Vec<u8>>) -> Result<Self, Self::Error> {
273+
use std::ffi::CString;
274+
275+
use c2pa::http::HttpResolverError;
276+
277+
let url = CString::new(request.uri().to_string())
278+
.map_err(|e| HttpResolverError::Other(Box::new(e)))?;
279+
let method = CString::new(request.method().as_str())
280+
.map_err(|e| HttpResolverError::Other(Box::new(e)))?;
281+
let headers_str: String = request
282+
.headers()
283+
.iter()
284+
.filter_map(|(k, v)| v.to_str().ok().map(|v| format!("{k}: {v}\n")))
285+
.collect();
286+
let headers =
287+
CString::new(headers_str).map_err(|e| HttpResolverError::Other(Box::new(e)))?;
288+
let body = request.into_body();
289+
290+
Ok(Self {
291+
url,
292+
method,
293+
headers,
294+
body,
295+
})
296+
}
297+
}
298+
299+
impl OwnedC2paHttpRequest {
300+
/// Returns the `#[repr(C)]` view. The returned struct borrows from `self`
301+
/// and must not outlive it.
302+
fn as_ffi(&self) -> C2paHttpRequest {
303+
let (body, body_len) = if self.body.is_empty() {
304+
(std::ptr::null(), 0)
305+
} else {
306+
(self.body.as_ptr(), self.body.len())
307+
};
308+
C2paHttpRequest {
309+
url: self.url.as_ptr(),
310+
method: self.method.as_ptr(),
311+
headers: self.headers.as_ptr(),
312+
body,
313+
body_len,
314+
}
315+
}
316+
}
317+
318+
impl C2paHttpResponse {
319+
/// Consumes the C-allocated response body and converts it into an
320+
/// `http::Response`. Calls `free()` on the body pointer.
321+
///
322+
/// # Safety
323+
/// `self.body` must have been allocated with `malloc()` (or be null).
324+
unsafe fn into_http_response(
325+
self,
326+
) -> Result<c2pa::http::http::Response<Box<dyn std::io::Read>>, c2pa::http::HttpResolverError>
327+
{
328+
let body_vec = if self.body.is_null() || self.body_len == 0 {
329+
Vec::new()
330+
} else {
331+
let v = std::slice::from_raw_parts(self.body, self.body_len).to_vec();
332+
libc::free(self.body as *mut c_void);
333+
v
334+
};
335+
336+
c2pa::http::http::Response::builder()
337+
.status(self.status as u16)
338+
.body(Box::new(std::io::Cursor::new(body_vec)) as Box<dyn std::io::Read>)
339+
.map_err(c2pa::http::HttpResolverError::Http)
340+
}
260341
}
261342

262343
/// Callback type for custom HTTP request resolution.
@@ -292,73 +373,31 @@ impl c2pa::http::SyncHttpResolver for C2paHttpResolver {
292373
request: c2pa::http::http::Request<Vec<u8>>,
293374
) -> Result<c2pa::http::http::Response<Box<dyn std::io::Read>>, c2pa::http::HttpResolverError>
294375
{
295-
use std::ffi::CString;
296-
297376
use c2pa::http::HttpResolverError;
298377

299-
let uri_cstr = CString::new(request.uri().to_string())
300-
.map_err(|e| HttpResolverError::Other(Box::new(e)))?;
301-
let method_cstr = CString::new(request.method().as_str())
302-
.map_err(|e| HttpResolverError::Other(Box::new(e)))?;
303-
304-
let headers_str: String = request
305-
.headers()
306-
.iter()
307-
.filter_map(|(k, v)| v.to_str().ok().map(|v| format!("{k}: {v}\n")))
308-
.collect();
309-
let headers_cstr =
310-
CString::new(headers_str).map_err(|e| HttpResolverError::Other(Box::new(e)))?;
311-
312-
let body = request.into_body();
313-
let (body_ptr, body_len) = if body.is_empty() {
314-
(std::ptr::null(), 0usize)
315-
} else {
316-
(body.as_ptr(), body.len())
317-
};
318-
319-
let c_request = C2paHttpRequest {
320-
url: uri_cstr.as_ptr(),
321-
method: method_cstr.as_ptr(),
322-
headers: headers_cstr.as_ptr(),
323-
body: body_ptr,
324-
body_len,
325-
};
378+
let owned = OwnedC2paHttpRequest::try_from(request)?;
379+
let c_request = owned.as_ffi();
326380

327381
let mut c_response = C2paHttpResponse {
328382
status: 0,
329383
body: std::ptr::null_mut(),
330384
body_len: 0,
331-
body_free: None,
332385
};
333386

334387
let rc =
335388
unsafe { (self.callback)(self.context as *mut c_void, &c_request, &mut c_response) };
336389

337-
// Copy the body before we potentially free it.
338-
let response_body = if c_response.body.is_null() || c_response.body_len == 0 {
339-
Vec::new()
340-
} else {
341-
unsafe { std::slice::from_raw_parts(c_response.body, c_response.body_len).to_vec() }
342-
};
343-
344-
// Release the callback-owned buffer.
345-
if let Some(free_fn) = c_response.body_free {
390+
if rc != 0 {
391+
// Free any body the callback may have allocated before the error.
346392
if !c_response.body.is_null() {
347-
unsafe { free_fn(c_response.body) };
393+
unsafe { libc::free(c_response.body as *mut c_void) };
348394
}
349-
}
350-
351-
if rc != 0 {
352395
let msg = CimplError::last_message()
353396
.unwrap_or_else(|| "HTTP callback returned error".to_string());
354397
return Err(HttpResolverError::Other(msg.into()));
355398
}
356399

357-
let response = c2pa::http::http::Response::builder()
358-
.status(c_response.status as u16)
359-
.body(Box::new(std::io::Cursor::new(response_body)) as Box<dyn std::io::Read>)
360-
.map_err(HttpResolverError::Http)?;
361-
Ok(response)
400+
unsafe { c_response.into_http_response() }
362401
}
363402
}
364403

0 commit comments

Comments
 (0)