spiral-contract 0.1.11

Language-neutral service wire values
Documentation
//! Requests bound to a header, for operations whose body is a stream.
//!
//! The request travels as JSON, base64url-encoded without padding, so any JSON value fits
//! the header's visible-ASCII grammar.

use base64::Engine as _;
use base64::engine::general_purpose::URL_SAFE_NO_PAD;
use serde::Serialize;

use crate::strict::{DeserializeStrict, from_slice};

/// The largest encoded header-bound request a server decodes.
pub const MAX_HEADER_REQUEST_BYTES: usize = 64 * 1024;

/// Why a header-bound request was not decoded.
#[derive(Debug)]
pub struct HeaderRequestError;

/// Encode `request` as a header value.
pub fn encode_header_request(request: &impl Serialize) -> Result<String, serde_json::Error> {
    Ok(URL_SAFE_NO_PAD.encode(serde_json::to_vec(request)?))
}

/// Decode a header value strictly, rejecting unknown fields like a JSON request body.
pub fn decode_header_request<T: for<'de> DeserializeStrict<'de>>(
    value: &str,
) -> Result<T, HeaderRequestError> {
    if value.len() > MAX_HEADER_REQUEST_BYTES {
        return Err(HeaderRequestError);
    }
    let json = URL_SAFE_NO_PAD
        .decode(value)
        .map_err(|_| HeaderRequestError)?;
    from_slice(&json).map_err(|_| HeaderRequestError)
}