Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

http-extract

Extract the signal. Keep trust explicit.

http-extract is a synchronous, framework-independent Rust library for strict HTTP request metadata extraction. Its public functions accept http crate types and return ordinary Rust values such as &str, u64, Mime, Authority, IpAddr, and Vec<IpAddr>.

One small API, one hard boundary

The library deliberately separates two kinds of information:

Request and transport factsRaw Header assertions
URI authority, Content-Type, request ID, socket peerForwarded, X-Forwarded-*, provider client-IP fields
Read through small, direct functionsParsed without silently granting trust

Forwarding fields are never trusted merely because they are present. Errors contain field names, never parser details or raw field values. Authorization credentials, API keys, cookies, bodies, complete query strings, and raw forwarding fields must not be logged.

Start here

  1. Install the crate and run a first extraction.
  2. Understand the client IP trust boundary.
  3. Find the feature and API family you need.
  4. Run the production-oriented Axum example.

The API documentation is the complete type-level reference. The exact protocol boundary is documented in Standards and compatibility.

Framework boundary

The default library core is synchronous and framework-independent. Axum is an optional, non-default dependency used only for the transport-peer extension adapter and the runnable example. The declared compiler baseline is Rust 1.96.0.

Getting started

The crate declares Rust 1.96.0 as its compiler compatibility baseline. Default features provide the complete documented extraction API.

With all common API families enabled:

[dependencies]
http-extract = "0.1"

For a smaller dependency surface, disable defaults and enable only the features you use:

[dependencies]
http-extract = { version = "0.1", default-features = false, features = [
  "authority",
  "content-type",
] }

Header functions contain the parsing logic. Request functions are convenience wrappers that only delegate through request.headers():

#![allow(unused)]
fn main() {
use http_extract::{Request, extract_request_authority, extract_request_content_type};

let request = Request::builder()
    .uri("https://example.com/items")
    .header("content-type", "application/json")
    .body(())?;

assert_eq!(
    extract_request_authority(&request)?.unwrap().as_str(),
    "example.com"
);
assert_eq!(
    extract_request_content_type(&request)?.unwrap().essence_str(),
    "application/json",
);
Ok::<(), Box<dyn std::error::Error>>(())
}

A socket peer is not an HTTP field. Obtain it from the server adapter. If the adapter stores a SocketAddr directly in request extensions, use extract_request_socket_address or extract_request_socket_ip. With the non-default axum feature, extract_axum_socket_address reads an existing ConnectInfo<SocketAddr> request extension, while extract_axum_socket_ip returns its IP component; neither fabricates that fact. extract_socket_ip composes the Axum and direct extension sources without reading Headers.

The Header-based extract_client_ip convenience does not use or authenticate the transport peer, so its result remains untrusted. For an explicitly trusted-proxy deployment, extract_proxy_client_ip checks the default Header order and falls back to extract_socket_ip only when all Headers in CLIENT_IP_HEADERS are absent. It does not verify the proxy trust boundary.

Before deployment, review Standards and compatibility, Features, and the client IP trust boundary.

Client IP trust boundary

The transport peer is the only network fact available independently of HTTP Headers. Forwarded, X-Forwarded-For, and provider-specific client IP fields are caller-controlled assertions until a deployment establishes trust.

extract_client_ip(headers) checks these fields in order:

  1. RFC 7239 Forwarded;
  2. X-Forwarded-For;
  3. X-Real-IP;
  4. CF-Connecting-IP.

This standard-first precedence is a library convention, not an order or trust policy defined by an RFC. Only an absent source falls through. A malformed first-present source returns an error instead of consulting a lower-priority field. Forwarded and X-Forwarded-For contribute the rightmost address, representing the assertion nearest the server.

Use extract_client_ip_with_headers(headers, order) with an ordered ClientIpHeader slice to choose different sources or precedence. The selector also supports the other single-value fields documented in Common client IP fields. Configuration strings can be parsed into ClientIpHeader values with FromStr; unsupported names fail before request extraction.

Neither selector receives the transport peer or trusted CIDRs. The returned IP is therefore raw and untrusted. Before using it for authorization, rate limiting, or audit decisions, restrict the application listener to controlled proxies and ensure that the selected field is overwritten according to the deployment’s policy.

extract_proxy_client_ip(request) composes the default Header selection with peer fallback. It calls extract_socket_ip(request) only when every supported Header is absent; an invalid first-present Header remains an error. The peer helper prefers Axum ConnectInfo<SocketAddr> when the feature is enabled and then a direct SocketAddr extension. Neither helper verifies trusted proxy addresses or CIDRs. Use extract_socket_ip alone when the actual socket peer is the desired fact.

extract_header_forwarded_for remains deliberately strict: the field must be singular and every element must contain a usable IP for= value. Missing, unknown, obfuscated, and non-IP nodes fail rather than being skipped. Parameters other than for are ignored after quote-aware element splitting; their names and values are not validated. See RFC 7239 Section 5.2 and its Section 8 security considerations.

X-Forwarded-For, X-Real-IP, and the provider-specific fields are de facto or vendor conventions, not IETF standards.

Common client IP fields

The client-ip-headers feature provides one direct function per field:

  • extract_header_cf_connecting_ip / extract_request_cf_connecting_ip;
  • extract_header_cloudfront_viewer_address / matching Request function;
  • extract_header_fly_client_ip / matching Request function;
  • extract_header_true_client_ip / matching Request function;
  • extract_header_x_envoy_external_address / matching Request function;
  • extract_header_x_real_ip / matching Request function.

All return Result<Option<IpAddr>, Error>. Missing fields return None. Fields are singular; duplicates, non-text values, and malformed addresses fail. CloudFront-Viewer-Address accepts IPv4 and IPv6 IP:port forms, including CloudFront’s unbracketed IPv6 representation.

These are vendor or de facto field names, not IETF standards. Extraction does not authenticate the sender or make the value safe for access control, logging, or rate limiting. The default extract_client_ip order includes CF-Connecting-IP and X-Real-IP; the other sources can be selected through extract_client_ip_with_headers. Selection does not authenticate the sender, so applications must apply a deployment-specific trust policy before using a result. See Standards and compatibility for the complete field classification.

Features

The crate declares Rust 1.96.0 as its compiler compatibility baseline. Default features enable the complete common extraction API: api-key, authority, authorization, client-ip, client-ip-headers, content-type, forwarded, request-id, and x-forwarded.

Every extraction feature owns one field family. Header functions contain the parsing implementation; matching Request functions delegate through request.headers(). All public APIs are exposed from the crate root.

Feature and API reference

Cargo featureAddsMain APIsSuccess value
api-keyX-API-Key, then Api-Key extractionextract_header_api_key, extract_request_api_keyOption<&str>
authorityURI authority and strict Host extractionextract_header_authority, extract_request_authorityOption<Authority>
authorizationRaw Authorization and Bearer/Basic scheme routingextract_*_authorization, extract_*_bearer_token, extract_*_basic_credentialsOption<&str>
axumOptional Axum ConnectInfo<SocketAddr> peer adapterextract_axum_socket_address, extract_axum_socket_ipOption<SocketAddr> or Option<IpAddr>
client-ipSocket-peer helpers and default/custom Header selectionextract_socket_ip, extract_client_ip, extract_client_ip_with_headers, extract_proxy_client_ipOption<IpAddr>
client-ip-headersCommon provider and proxy client-IP fieldsone extract_header_* and extract_request_* pair per fieldOption<IpAddr>
content-typeStrict Content-Type parsing and the optional mime dependencyextract_header_content_type, extract_request_content_typeOption<Mime>
forwardedStrict RFC 7239 Forwarded for= IP chainsextract_header_forwarded_for, extract_request_forwarded_forOption<Vec<IpAddr>>
request-idX-Request-Id, then Request-Id precedenceextract_header_request_id, extract_request_request_idOption<&str>
x-forwardedRaw X-Forwarded-For and X-Forwarded-Proto parsingextract_*_x_forwarded_for, extract_*_x_forwarded_protoOption<Vec<IpAddr>> or Option<Vec<String>>

The crate-wide Error and generic header utilities remain available with no default features. header includes the strict singular-field helpers and append_header_value, which appends without replacing existing field lines.

Client IP composition

extract_request_socket_address and extract_request_socket_ip read a SocketAddr stored directly in request extensions. extract_socket_ip prefers Axum ConnectInfo<SocketAddr> when the axum feature is enabled, then falls back to the direct extension. None of these peer helpers inspect Headers.

extract_client_ip uses the documented default Header order, while extract_client_ip_with_headers accepts an explicit ordered slice of ClientIpHeader values. Both return raw, untrusted Header assertions. extract_proxy_client_ip applies the default Header order and uses extract_socket_ip only when every Header in CLIENT_IP_HEADERS is absent.

The non-default axum feature enables client-ip. Its extract_axum_socket_address and extract_axum_socket_ip functions return None when ConnectInfo<SocketAddr> is absent; they never fabricate a peer.

See the client IP trust boundary before using a Header-derived address for authorization, rate limiting, or auditing.

Feature relationships

client-ip enables client-ip-headers, forwarded, and x-forwarded because its selectors use those parsing APIs. content-type enables the optional mime dependency. The normal default dependency tree does not include Axum, Tower, Tokio, tracing, or OpenTelemetry.

Enable the optional Axum adapter and runnable example explicitly:

cargo run --example axum-demo --features axum

Useful checks for consumers and contributors:

cargo check --no-default-features
cargo test --no-default-features --features authority,content-type,request-id
cargo test --no-default-features --features client-ip
cargo test --no-default-features --features client-ip-headers

This feature layout does not imply additional runtime compatibility. See Standards and compatibility for the protocol support boundary.

Request IDs

The request ID extractor uses a fixed practical precedence:

  1. X-Request-Id;
  2. Request-Id, only when the preferred field is absent.

The selected value is returned unchanged. An empty X-Request-Id therefore returns Some("") and does not fall back. Duplicate selected fields and non-text values fail without including the value in the error.

#![allow(unused)]
fn main() {
use http_extract::{Request, extract_request_request_id};

let request = Request::builder()
    .header("request-id", "fallback")
    .header("x-request-id", "preferred")
    .body(())?;
assert_eq!(
    extract_request_request_id(&request)?,
    Some("preferred")
);
Ok::<(), Box<dyn std::error::Error>>(())
}

These are crate-configured common field names, not a claim that either is a universal IETF-standard request ID field. The crate does not generate, validate, or propagate identifiers. The distinction from standardized fields is summarized in Standards and compatibility.

Standards and compatibility

http-extract implements narrow extraction behavior derived from the stable RFC versions below. It does not claim complete HTTP, proxy, or authentication protocol conformance.

IETF standards used

  • HTTP Semantics, RFC 9110: Section 5 informs strict field handling; Section 7.2 covers request authority and Host; Section 8.3 covers Content-Type; and Section 11 supplies the Authorization framework context. The crate only extracts these fields; it is not a complete HTTP implementation and does not authenticate.
  • Forwarded, RFC 7239: support is intentionally limited to the field structure in Section 4, the for= parameter in Section 5.2, and IP node forms from Section 6.1. extract_header_forwarded_for returns a continuous IP chain and rejects missing, unknown, obfuscated, or non-IP nodes instead of skipping them. It ignores parameters other than for after quote-aware element splitting without validating their names or values, and does not implement the full Forwarded object model. Parsing and Header-based selection do not establish trust; deployments must account for the Section 8 security considerations independently.
  • Bearer Token Usage, RFC 6750 Section 2.1: the helper performs ASCII case-insensitive scheme routing and returns the raw credential substring. It does not validate a token or authenticate.
  • Basic Authentication, RFC 7617 Section 2: the helper performs ASCII case-insensitive scheme routing and returns the raw credential substring. It does not validate credentials, decode Base64, or authenticate.

Non-standard fields

The following are common vendor or de facto fields, not IETF standards:

  • X-Forwarded-For and X-Forwarded-Proto;
  • CF-Connecting-IP;
  • CloudFront-Viewer-Address;
  • Fly-Client-IP;
  • True-Client-IP;
  • X-Envoy-External-Address;
  • X-Real-IP.

Their APIs only parse raw assertions. Presence does not authenticate a proxy or make a value suitable for access control, rate limiting, or audit use. See the client IP trust boundary for deployment guidance.

Rust and feature compatibility

The declared Rust baseline is Rust 1.96.0, matching Cargo.toml’s rust-version and rust-toolchain.toml. This is a compiler compatibility statement only; the core remains synchronous and framework-independent, and no broader runtime compatibility is implied.

Default features enable all documented extraction API families. Consumers using default-features = false can select only the features they need. The exact feature list, dependency relationships, and copyable configuration are in Features.

Axum integration example

The repository’s axum-demo example demonstrates the library at an Axum request boundary while keeping the core framework-independent. Axum supplies the TCP peer through ConnectInfo<SocketAddr>; extract_axum_socket_address and extract_axum_socket_ip read that request extension, while Header-derived client IP values remain separate raw assertions.

Run the listener:

LISTEN_ADDR=127.0.0.1:3000 cargo run --example axum-demo --features axum

Or run the one-step in-process request demonstration:

cargo test --example axum-demo --features axum \
  one_step_axum_request_demo_returns_complete_safe_json -- --nocapture

The handler passes request.headers() to extract_client_ip. That convenience uses the library’s default Header order and returns a raw, untrusted assertion; it does not authenticate a proxy. A successful request returns and logs one JSON object containing the peer address/IP, selected Header IP/source, authority, request ID, Content-Type, and masked Authorization, API-key, and cookie values. Sensitive strings retain only their first and last two characters (aa***bb); values of four characters or fewer are fully masked. Missing optional values are JSON null.

The event never includes complete Authorization credentials, API-key values, or cookie content. It also omits raw forwarding fields, the complete query string, and body. Malformed selected metadata produces a generic HTTP 400 response.

Review the client IP trust boundary and Standards and compatibility before using a Header-derived IP for a security decision. Complete commands and the output contract are in the example README.