Batch & Async
sign_batch, BatchOptions, sign_pdf_async, and sign_batch_async — parallel batch signing and Tokio wrappers, with the Send/Sync bounds each needs
Two ways to scale beyond one call to sign_pdf: parallel batch signing across a thread pool
(sign_batch), and async wrappers over Tokio's blocking pool (sign_pdf_async /
sign_batch_async, feature tokio). Both share the same underlying sign_pdf — signing itself is
always synchronous, CPU-bound work.
sign_batch
pub fn sign_batch<S>(
inputs: &[Vec<u8>],
signer: &S,
cfg: &SignConfig,
opts: &BatchOptions,
) -> Vec<Result<Vec<u8>>>
where
S: Signer + SyncSigns every PDF in inputs with one signer + cfg, in parallel, across opts.threads worker
threads (work-stealing), returning a Result per input in the same order. A failing document
yields an Err in its slot — the batch never aborts on one error.
use mudrit_keystore::PfxSigner;
use mudrit_pdfsign::{sign_batch, BatchOptions, SignConfig};
let signer = PfxSigner::from_file("samples/ABC12.pfx", "ABC12")?;
let cfg = SignConfig::builder().place("F", [350, 60, 560, 160])?.build();
let inputs: Vec<Vec<u8>> = vec![std::fs::read("a.pdf")?, std::fs::read("b.pdf")?];
let results = sign_batch(&inputs, &signer, &cfg, &BatchOptions::default());
for (i, r) in results.iter().enumerate() {
match r { Ok(pdf) => println!("#{i}: {} bytes", pdf.len()), Err(e) => eprintln!("#{i}: {e}") }
}
# Ok::<(), Box<dyn std::error::Error>>(())Signer must be Sync — and hardware tokens need threads(1)
signer is shared across worker threads, so it must be Sync (e.g. &PfxSigner). For a type-erased
signer, &signer as &(dyn Signer + Sync) also satisfies the bound. Parallel signing is safe for
software signers (RSA/ECDSA signing is reentrant), but a single hardware token has one PKCS#11
session that is not safe for concurrent signing — for one token use BatchOptions::threads(1) (the
work becomes sequential but still returns per-item results). With several tokens, sign each token's
batch on its own.
BatchOptions
Debug + Clone.
Prop
Type
Prop
Type
sign_pdf_async (feature tokio)
pub async fn sign_pdf_async<S>(input: Vec<u8>, signer: Arc<S>, cfg: SignConfig) -> Result<Vec<u8>>
where
S: Signer + Send + Sync + 'staticRuns the blocking sign_pdf on Tokio's blocking thread-pool (spawn_blocking) so it never stalls an
async runtime — the honest, correct shape for an async server (axum / actix / …). The signer is
Arc<S> because the work runs on another thread, so S must be Send + Sync + 'static; for a
type-erased signer use Arc<dyn Signer + Send + Sync>.
use std::sync::Arc;
use mudrit_keystore::PfxSigner;
use mudrit_pdfsign::{sign_pdf_async, SignConfig};
# async fn run() -> Result<(), Box<dyn std::error::Error>> {
let signer = Arc::new(PfxSigner::from_file("samples/ABC12.pfx", "ABC12")?);
let cfg = SignConfig::builder().place("F", [350, 60, 560, 160])?.build();
let signed = sign_pdf_async(std::fs::read("in.pdf")?, signer, cfg).await?;
# Ok(()) }sign_batch_async (feature tokio)
pub async fn sign_batch_async<S>(
inputs: Vec<Vec<u8>>,
signer: Arc<S>,
cfg: SignConfig,
opts: BatchOptions,
) -> Result<Vec<Result<Vec<u8>>>>
where
S: Signer + Send + Sync + 'staticRuns the whole parallel sign_batch on the blocking pool and resolves to the
per-input results. Same Send + Sync + 'static bound on S as sign_pdf_async, for the same reason
— the work is offloaded to another thread.
Enable the feature
sign_pdf_async / sign_batch_async are only compiled with the tokio feature
(mudrit-pdfsign = { version = "…", features = ["tokio"] }, off by default). sign_batch needs no
feature — it uses std::thread directly.