scion_http3/lib.rs
1// Copyright 2026 Anapaya Systems
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! # SCION HTTP/3 client
16//!
17//! A high-level HTTP/3-over-SCION client: give it a URL, get a response.
18//!
19//! ## Features
20//!
21//! - SCION connectivity, discovered through an endhost API
22//! - TSAR DNS resolution (SCION addresses from DNS TXT records)
23//! - Per-origin connection pooling
24//! - Staggered multi-candidate connection establishment ([RFC 8305], Happy Eyeballs)
25//! - Connect, request, and idle-connection timeouts
26//! - Recovery from network changes
27//!
28//! The transport engine underneath is [`scion_quic::h3::client::Http3Client`]. It stays
29//! available for callers that need transport-level control.
30//!
31//! ## Usage
32//!
33//! Issue a GET request with the [`Client::get`] shorthand (see also [`Client::post`]):
34//!
35//! ```no_run
36//! # async fn example() -> Result<(), Box<dyn std::error::Error>> {
37//! use scion_http3::{Client, Config};
38//!
39//! /// Cap on a collected response body.
40//! const MAX_BODY_SIZE: usize = 1 << 20;
41//!
42//! let client = Client::new(
43//! Config::new("https://endhost-api.example.org".parse()?).with_auth_token("token"),
44//! );
45//! let response = client.get("https://chat.example.org/rooms").await?;
46//! let (rooms, _trailers) = response.text(Some(MAX_BODY_SIZE)).await?;
47//! # Ok(())
48//! # }
49//! ```
50//!
51//! Or build a [`Request`] from scratch and issue it with [`Client::request`]:
52//!
53//! ```no_run
54//! # async fn example(client: scion_http3::Client) -> Result<(), Box<dyn std::error::Error>> {
55//! use scion_http3::Request;
56//!
57//! let request = Request::post("https://chat.example.org/messages")
58//! .header("content-type", "application/json")
59//! .body(r#"{"room":"general","text":"hi"}"#)
60//! .build()?;
61//! let response = client.request(request).await?;
62//! # Ok(())
63//! # }
64//! ```
65//!
66//! ## Connection pooling and lifecycle
67//!
68//! A [`Client`] holds a connection pool, so create one client per application and share it.
69//!
70//! Construction does no I/O: connections are established lazily on first use and then reused
71//! per origin. Use [`Client::warm_up`] to establish a connection before the first request.
72//!
73//! [`Client::close`] closes all pooled connections and frees their resources. Calling it
74//! before dropping the client is optional.
75//!
76//! ## Network changes
77//!
78//! Call [`Client::reset`] when the network below the client changes, for example when the
79//! platform reports a switch from Wi-Fi to mobile data. The call never blocks: it marks the
80//! current connections stale, and the next request tears them down and rebuilds connectivity.
81//!
82//! ## Error handling
83//!
84//! Every request-path failure surfaces as one error type, [`Error`]. To decide whether to
85//! retry a failed request, use [`Error::is_retryable`] instead of matching variants.
86//! A retryable error does not imply the request never reached the server, so retry
87//! non-idempotent requests with care.
88//!
89//! [RFC 8305]: https://datatracker.ietf.org/doc/html/rfc8305
90
91// Linter baseline for the published API surface.
92#![warn(clippy::pedantic)]
93#![allow(
94 // The module path deliberately echoes type names where a module has one main type.
95 clippy::module_name_repetitions,
96 // `#[must_use]` is applied deliberately where it matters rather than everywhere.
97 clippy::must_use_candidate,
98 // `# Errors` / `# Panics` sections are added where they aid the caller, not mechanically.
99 clippy::missing_errors_doc,
100 clippy::missing_panics_doc
101)]
102
103mod client;
104mod config;
105mod epoch;
106mod error;
107mod establish;
108mod origin;
109mod pool;
110mod request;
111mod response;
112#[cfg(test)]
113mod test_support;
114
115// Re-exported dependencies
116//
117// These crates appear in `scion-http3`'s public API by deliberate choice. They are re-exported
118// here so a client can name and construct the types our signatures require without adding
119// its own direct dependency.
120/// Body bytes ([`bytes::Bytes`]) used for request bodies and collected response bodies.
121pub use bytes;
122pub use client::Client;
123pub use config::{
124 Config, DEFAULT_CONNECT_TIMEOUT, DEFAULT_CONNECTION_ATTEMPT_DELAY,
125 DEFAULT_IDLE_CONNECTION_TIMEOUT, DEFAULT_MAX_ORIGINS, DEFAULT_REQUEST_TIMEOUT,
126};
127pub use error::{BuildRequestError, Error, TimeoutPhase};
128/// HTTP vocabulary types ([`http::Method`], [`http::HeaderMap`], [`http::StatusCode`]) used by
129/// requests and responses.
130pub use http;
131pub use request::{IntoUrl, Request, RequestBuilder};
132pub use response::Response;
133/// The transport engine. Its types appear in this API as the QUIC configuration
134/// ([`scion_quic::quic::config::QuicConfig`], see [`Config::with_quic_config`]).
135pub use scion_quic;
136/// The SCION stack. Its types appear in this API as the builder passthroughs
137/// ([`Config::with_preferred_underlay`], [`Config::with_stack_customizer`]) and the resolver
138/// escape hatch ([`Config::with_resolver`]).
139pub use scion_stack;
140/// Authentication token plumbing for [`Config::with_auth_token_source`]: implement
141/// [`TokenSource`] to supply tokens that expire; [`StaticTokenSource`] wraps a fixed one.
142pub use scion_stack::reqwest_connect_rpc::token_source::{
143 TokenSource, TokenSourceError, TokenSourceWatch, static_token::StaticTokenSource,
144};
145/// SCION address types ([`sciparse::address::ip_addr::ScionIpAddr`]) used by the
146/// [`RequestBuilder::target`] / [`RequestBuilder::targets`] escape hatches.
147pub use sciparse;
148/// URL type used for request URLs and the endhost API address.
149pub use url;