diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 68d610f..cb8367f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -65,6 +65,22 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 + - name: Checkout c2pa-rs (0.92.1) + uses: actions/checkout@v4 + with: + repository: contentauth/c2pa-rs + ref: c2pa-v0.92.1 + path: c2pa-rs + + - name: Configure c2pa-rs release source build + shell: bash + run: | + git -C c2pa-rs log -1 --format='c2pa-rs checkout: %H %s' + cargo --version + rustc --version + echo "C2PA_BUILD_FROM_SOURCE=ON" >> "$GITHUB_ENV" + echo "C2PA_RS_PATH=${GITHUB_WORKSPACE//\\//}/c2pa-rs" >> "$GITHUB_ENV" + - name: Check cmake run: cmake --version @@ -82,7 +98,7 @@ jobs: which make make --version - - name: Build and test against c2pa-rs 0.92.0 release + - name: Build and test against c2pa-rs 0.92.1 release run: make test - name: Run tests (debug build, with sanitizers) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0d3b053..49e6652 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,7 +17,7 @@ cmake_minimum_required(VERSION 3.27) project(c2pa-c VERSION 0.27.0) # Set the version of the c2pa_rs library used -set(C2PA_VERSION "0.92.0") +set(C2PA_VERSION "0.92.1") set(CMAKE_POLICY_DEFAULT_CMP0135 NEW) set(CMAKE_C_STANDARD 17) diff --git a/Makefile b/Makefile index 3697a4d..2d1c934 100644 --- a/Makefile +++ b/Makefile @@ -5,6 +5,9 @@ BUILD_DIR = build DEBUG_BUILD_DIR = build/debug RELEASE_BUILD_DIR = build/release +# Use published c2pa-rs binaries by default; source builds remain opt-in. +C2PA_BUILD_FROM_SOURCE ?= OFF + # CMake options (can be overridden via environment) CMAKE_OPTS := ifdef C2PA_BUILD_FROM_SOURCE diff --git a/include/c2pa.hpp b/include/c2pa.hpp index 4feeb05..091796c 100644 --- a/include/c2pa.hpp +++ b/include/c2pa.hpp @@ -401,6 +401,8 @@ namespace c2pa /// @details The callback is invoked synchronously whenever the SDK needs to /// make an HTTP request (remote manifest fetch, OCSP, timestamp, etc.). /// The callback receives a C2paHttpRequest and must fill in a C2paHttpResponse. + /// The SDK cannot interrupt the callback; configure appropriate timeouts + /// on its HTTP client. /// The response body must be allocated with malloc(); the underlying native library will call free(). /// @param user_data Opaque user pointer passed to every callback invocation (may be nullptr). /// @param callback Function pointer matching C2paHttpResolverCallback. diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 53a878d..a401c36 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -88,13 +88,22 @@ if(C2PA_BUILD_FROM_SOURCE) find_program(CARGO_EXECUTABLE cargo PATHS "$ENV{HOME}/.cargo/bin" REQUIRED) message(STATUS "Found cargo at: ${CARGO_EXECUTABLE}") + set(C2PA_RUST_ARTIFACTS "${C2PA_LIB_NAME}" "c2pa.h") + if(WIN32) + list(APPEND C2PA_RUST_ARTIFACTS "${C2PA_IMPLIB_NAME}") + endif() + set(C2PA_RUST_SOURCES ${C2PA_RUST_ARTIFACTS}) + list(TRANSFORM C2PA_RUST_SOURCES PREPEND "${C2PA_RS_PATH}/target/release/") + set(C2PA_RUST_OUTPUTS ${C2PA_RUST_ARTIFACTS}) + list(TRANSFORM C2PA_RUST_OUTPUTS PREPEND "${C2PA_RUST_BUILD_DIR}/") + # Build the Rust c2pa_c_ffi library add_custom_command( - OUTPUT "${C2PA_RUST_BUILD_DIR}/${C2PA_LIB_NAME}" + OUTPUT ${C2PA_RUST_OUTPUTS} COMMAND ${CARGO_EXECUTABLE} build --release --package c2pa-c-ffi --no-default-features --features "rust_native_crypto, file_io, http, add_thumbnails" - COMMAND ${CMAKE_COMMAND} -E copy - "${C2PA_RS_PATH}/target/release/${C2PA_LIB_NAME}" - "${C2PA_RUST_BUILD_DIR}/${C2PA_LIB_NAME}" + COMMAND ${CMAKE_COMMAND} -E copy_if_different + ${C2PA_RUST_SOURCES} + "${C2PA_RUST_BUILD_DIR}" WORKING_DIRECTORY "${C2PA_RS_PATH}" COMMENT "Building c2pa_c from source using Cargo" VERBATIM @@ -102,16 +111,7 @@ if(C2PA_BUILD_FROM_SOURCE) # Create a custom target that depends on the library add_custom_target(build_c2pa_c ALL - DEPENDS "${C2PA_RUST_BUILD_DIR}/${C2PA_LIB_NAME}" - ) - - # Copy the generated c2pa.h header - add_custom_command( - TARGET build_c2pa_c POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_if_different - "${C2PA_RS_PATH}/target/release/c2pa.h" - "${C2PA_RUST_BUILD_DIR}/c2pa.h" - COMMENT "Copying c2pa.h header" + DEPENDS ${C2PA_RUST_OUTPUTS} ) set(C2PA_C_LIB "${C2PA_RUST_BUILD_DIR}/${C2PA_LIB_NAME}") diff --git a/tests/context.test.cpp b/tests/context.test.cpp index 6f0d226..eaed3cc 100644 --- a/tests/context.test.cpp +++ b/tests/context.test.cpp @@ -874,3 +874,40 @@ TEST_F(ContextTest, ContextMoveAssignWithCallbackPreservesInvocation) { EXPECT_EQ(lhs.load(), 0); EXPECT_GT(rhs.load(), 0); } + +#if 0 // TODO: Re-enable when C2paHttpRequest.timeout_ms is published (absent in 0.92.1). +static int capture_timeout(void* user_data, const C2paHttpRequest* request, C2paHttpResponse* response) +{ + static_cast*>(user_data)->store(request->timeout_ms); + response->status = 200; + response->body = nullptr; + response->body_len = 0; + return 0; +} + +TEST(Context, HttpResolverReceivesRemoteManifestTimeout) +{ + for (const auto& [settings_json, expected] : { + std::pair{"{}", 10000}, + std::pair{ + R"({"core":{"http_request_timeout_in_ms":2500}})", 2500}, + }) { + std::atomic seen{0}; + c2pa::Context context = c2pa::Context::ContextBuilder() + .with_json(settings_json) + .with_http_resolver(&seen, capture_timeout) + .create_context(); + + std::ifstream asset(fixture_path("cloud.jpg"), std::ios::binary); + ASSERT_TRUE(asset.is_open()); + try { + c2pa::Reader reader(context, "image/jpeg", asset); + } catch (const c2pa::C2paException&) { + // The empty callback response is not a manifest; only the request matters here. + } + + EXPECT_LE(seen.load(), expected); + EXPECT_GE(seen.load(), expected - 100); + } +} +#endif