Skip to main content

c2pa_http/
lib.rs

1// Copyright 2026 WritersLogic. All rights reserved.
2// Licensed under the Apache License, Version 2.0 or the MIT license,
3// at your option.
4
5//! C2PA manifest discovery over HTTP: the `c2pa-manifest` `Link` header.
6//!
7//! When an asset is served over HTTP, a validator that finds no embedded
8//! Manifest Store should look for a `Link` header carrying
9//! `rel="c2pa-manifest"`. This crate parses and emits that header, and provides
10//! a [Tower] middleware that attaches it to every response.
11//!
12//! # Scope
13//!
14//! - **[`link`]** — parse and serialise the header per {RFC 8288}. No
15//!   dependencies, operates on `&str`, usable under any HTTP stack.
16//! - **[`layer`]** — the Tower [`Layer`](tower_layer::Layer), behind the
17//!   default `tower` feature.
18//!
19//! Retrieving the manifest is left to the caller: this crate performs no
20//! network I/O, so it makes no decisions about timeouts, redirects, or trust.
21//! [`Error::Inaccessible`] exists so a caller that does fetch can report
22//! `manifest.inaccessible` through the same error type.
23//!
24//! # Examples
25//!
26//! Advertise a manifest on every response:
27//!
28//! ```
29//! # #[cfg(feature = "tower")] {
30//! use c2pa_http::ManifestLinkLayer;
31//! use tower::ServiceBuilder;
32//!
33//! let layer = ManifestLinkLayer::new("https://fabrikam.example/m.c2pa").unwrap();
34//! let _service = ServiceBuilder::new().layer(layer);
35//! # }
36//! ```
37//!
38//! Read one from a response you received:
39//!
40//! ```
41//! use c2pa_http::link;
42//!
43//! let header = r#"</style.css>; rel=preload, <https://a.example/m.c2pa>; rel="c2pa-manifest""#;
44//! let found = link::extract([header]).unwrap();
45//! assert_eq!(found.uri, "https://a.example/m.c2pa");
46//! ```
47//!
48//! A target may also name a Manifest Store already embedded in the asset,
49//! through a JUMBF fragment. A reference to a specific manifest *inside* the
50//! store is not permitted, and the `childlabel` portion is discarded:
51//!
52//! ```
53//! use c2pa_http::link;
54//!
55//! let header = r#"<https://a.example/i.jpg#jumbf=c2pa/urn:uuid:1234>; rel="c2pa-manifest""#;
56//! let found = link::extract([header]).unwrap();
57//! assert!(found.is_embedded());
58//! assert_eq!(found.uri, "https://a.example/i.jpg#jumbf=c2pa");
59//! ```
60//!
61//! # Precedence
62//!
63//! For an HTML document that carries both an in-document manifest element and a
64//! `Link` header, the specification gives the header precedence. Discovery
65//! inside the document itself is [`c2pa-html`]'s job.
66//!
67//! [Tower]: https://crates.io/crates/tower
68//! [`c2pa-html`]: https://crates.io/crates/c2pa-html
69
70#![forbid(unsafe_code)]
71#![warn(missing_debug_implementations)]
72
73pub mod error;
74pub mod link;
75
76#[cfg(feature = "tower")]
77pub mod layer;
78
79#[cfg(all(feature = "python", not(target_arch = "wasm32")))]
80mod python;
81
82#[cfg(target_arch = "wasm32")]
83mod wasm;
84
85pub use error::Error;
86pub use link::{ManifestLink, REL};
87
88#[cfg(feature = "tower")]
89pub use layer::{append_to, extract_from, ManifestLinkLayer, ManifestLinkService};