source

flyology_http

0.1.2

HTTP client and server library for Flyology tasks

0.1.2

Changelog excerpt Release notes 0.1.2

Added

  • Added exact 64-bit fixed-length server response streams through the raw and application APIs across HTTP/1.1, HTTP/2, and HTTP/3. Known-length streams emit Content-Length without retaining the whole response, while the existing unknown-length API remains available. (PR #28)

Fixed

  • Reject fixed-response overruns before transport writes, fail underruns at stream completion, isolate failed HTTP/2 and HTTP/3 streams, and preserve the declared representation length while suppressing HEAD response bodies. (PR #28)
  • Accept legal HTTP/2 HEAD responses that complete with an empty DATA frame after advertising the representation's nonzero Content-Length. (PR #29)
See more Show less

0.1.1 - 2026-08-21

Added

  • Added transactional runtime router reconfiguration with atomic publication, stable route and middleware identities, snapshots, stale-update detection, and explicit reclamation of retained generations. (PR #10, PR #10 commits)
  • Exposed the exact request authority, raw target and query, physical header occurrences, and completed request trailers to server applications across HTTP/1.1, HTTP/2, and HTTP/3. (PR #25, commit ca058ef)
  • Extended borrowed request-body streaming and request trailers to HTTP/2 and HTTP/3, with bounded flow control and explicit replay rules. (PR #25, commit ca058ef)

Changed

  • Added the shared 64-bit-safe Flyology.HTTP.Body_Size type and carried it through client and server length, limit, routing, logging, and metrics APIs. The streaming request ceiling is now 50 TB while the HTTP/3 buffered-server profile retains its independent 1 MiB body cap. (PR #24, commit 7a4ec6c)
  • Raised bounded metadata limits for S3-style requests: client request targets to 16 KiB, HTTP/1 request heads to 32 KiB, and QPACK field values to 16 KiB. (PR #25, commit ca058ef)
  • Sealed direct router registration and setters after the first dispatch; running routers must use the transactional update API. (PR #10, PR #10 commits)
  • Restricted the generated API reference to Flyology HTTP units, removing broken links and dependency-internal pages. (PR #23, commit 8210643)
  • Published against the stable dependency set flyology=0.1.0, flyology_cachelines=0.1.0, and flyology_quic=0.1.1. (commit dac7fa9, PR #27, commit 5638f1c)
  • Automated the full SPARK proof and WebSocket qualification campaigns, made proof effort configurable for hosted runners, bounded network-dependent CI steps, and made the HTTP benchmark locate the resolved Flyology runtime. (PR #9, PR #9 commits, PR #19, commit 8cae2c8, commit d99490c)

Fixed

  • Prevented server request framing, limits, byte counters, diagnostics, and metrics from overflowing or narrowing on multi-gigabyte bodies. (PR #24, commit 7a4ec6c)
  • Hardened HTTP/2 and HTTP/3 streamed uploads against zero progress, source overruns and exceptions, cancellation, expired deadlines, early final responses, unsafe connection reuse, and backpressure wake-up races. (PR #25, commit ca058ef)
  • Recovered HTTP/3 handshakes after a lost Initial or Handshake datagram by using QUIC probe timeouts and retransmitted CRYPTO ranges. (PR #21, commit a3866c5)
  • Released HTTP/3 listener capacity promptly when a dual-stack race abandons a handshake or a peer goes silent, using protected handshake closes and a no-progress deadline. (PR #22, PR #22 commits)
  • Kept the raw HTTP/3 integration exchange alive when a full listener discards an Initial datagram. (PR #20, commit 83db8c4)

0.1.0 - 2026-08-14

Added

  • Initial release.

Source documentation

README

View source

Flyology HTTP

Flyology HTTP is an experimental HTTP/1.1 client and server library, with opt-in HTTP/2 and HTTP/3 client and application-server engines, for Flyology tasks. Its synchronous Ada APIs work from native and lightweight tasks. The library includes bounded client pools, an origin-bound WebSocket client, streaming request and response bodies, routing, middleware, server-sent events, WebSocket servers, and plain or TLS transports built on Flyology I/O.

This repository also contains flyology_quic, an independently built Ada-native QUIC transport crate. flyology_http depends on that crate and owns the HTTP/3 and QPACK layers; HTTP semantics do not live in the transport crate. The public Flyology.HTTP.HTTP_3 session API can exchange bounded request and response HEADERS and DATA over live QUIC connections in client and server roles. A routed server adapter also presents HTTP/3 requests through the same Applications.Exchange, routes, middleware, body policies, streaming response, and SSE APIs used by HTTP/1.1 and HTTP/2. The transport and HTTP/3 sessions interoperate in both directions with an aioquic black-box test peer.

Documentation is published at http.flyology.org. The client guide, dedicated HTTP/2 guide, and server guide describe outbound and inbound lifecycles separately.

Build and test

alr build
./scripts/test.sh
./scripts/http2-test.sh prepare
./scripts/http2-test.sh all
# Required aioquic and quic-go black-box HTTP/3 interoperability:
./scripts/test-http3-interop.sh all
# Required published error suite and bounded HTTP/3 resilience campaign:
./scripts/test-http3-h3spec.sh
./scripts/test-http3-stress.sh
# Optional Docker-based server protocol qualification:
./scripts/http2-test.sh h2spec

The test runner prepares a version-matched Flyology runtime, builds the HTTP library as a separate GPR library, and runs its client, server, WebSocket, TLS, policy, and negative lifetime tests. The separate HTTP/2 command uses a pinned python-hyper/h2 peer for ALPN, prior-knowledge, multiplexing, flow-control, and retry interoperability, plus differential HPACK testing. The explicit h2spec target runs the pinned h2spec 2.6.0 image against the cleartext server adapter; it requires Docker and is also part of qualification and nightly. The HTTP/3 interoperability command creates an isolated Python test environment for pinned aioquic and builds a separately pinned quic-go test executable. Both peers exercise Ada client and server roles and are CI gates; neither is a crate or runtime dependency. The separately pinned h3spec command gates published QUIC and HTTP/3 error-case coverage. The stress command checks hostile UDP input, mutated authenticated streams, bounded server churn, and concurrent Ada client use against aioquic. See tests/http3-conformance.md for the exact matrix. Rebuild with FLYOLOGY_QUIC_TRACE=true to emit tagged QUIC and HTTP/3 state failures on standard error. Tracing is compiled out by default.

Use with Alire

Add the Flyology organization index ahead of the community index and depend on the HTTP crate:

alr index --reset-community
alr index --add=git+https://github.com/flyology-ada/alire-index.git \
  --name=flyology --before=community
alr with flyology_http

The flyology_http dependency brings in Flyology. Applications configure and prepare Flyology's version-matched runtime as described in the Flyology guide.

Unix socket clients

On macOS and Linux, a client can keep its HTTP authority separate from a pathname Unix-domain transport. This is suitable for local APIs such as the Docker Engine socket:

Client.Configure
  (HTTP,
   Flyology.HTTP.Parse_Origin ("http://localhost"),
   Client.Unix_Socket ("/var/run/docker.sock"));
Client.Set_Target (Request, "/_ping");
declare
   Response : Client.Response :=
     Client.Execute (HTTP, Request, Timeout => 5.0);
begin
   Consume (Client.Read_All (Response));
end;

The Unix transport supports HTTP/1.1 and cleartext HTTP/2 prior knowledge. The service owns the socket entry and its permissions; the client never removes or modifies it. For a manual request, run ./showcases/run_http_client_cli.sh --unix-socket /var/run/docker.sock http://localhost/version. The automated tests do not require Docker.

Unified HTTP/1.1, HTTP/2, and HTTP/3 server

Register the routes once and call the router's unified Serve overload. It binds TLS/TCP and QUIC/UDP to the same endpoint. TLS ALPN selects HTTP/2 or HTTP/1.1 on TCP; HTTP/3 is served on UDP. Every protocol dispatches through the same router, middleware, body policies, and application context:

type Context is limited null record;
package Routing is new Flyology.HTTP.Server.Routing (Context);

Routes  : aliased Routing.Router
  (Capacity => 1, Slashes => Routing.Strict_Slashes);
State   : aliased Context;
Backend : aliased OpenSSL.OpenSSL_Provider;
Stop    : aliased Flyology.Cancellation.Token;

procedure Hello (State : in out Context; X : in out Applications.Exchange) is
begin
   X.Text (200, "hello " & X.Parameter ("name"));
end Hello;

Routes.Get ("/hello/{name}", Hello'Access, Name => "hello");
OpenSSL.Initialize_Server
  (Backend, "certificate.pem", "private-key.pem",
   Protocols => ALPN."&" (ALPN.Offer ("h2"), "http/1.1"));
Routes.Serve
  (State,
   HTTP_Endpoint  => Sockets.Network_Endpoint (Sockets.Any_IPv4, 80),
   HTTPS_Endpoint => Sockets.Network_Endpoint (Sockets.Any_IPv4, 443),
   HTTPS_Origin   => Flyology.HTTP.Parse_Origin
     ("https://www.example.com"),
   TLS_Backend     => Backend,
   Certificate_DER => Certificate_DER,
   Private_Key     => Private_Key,
   Token           => Stop'Access);

The default cleartext policy returns a method-preserving 308 using the configured HTTPS_Origin; it does not trust the request's Host field. Select Cleartext => Routing.Serve_Cleartext to route cleartext HTTP/1.x through the same application instead. Handlers can inspect X.Request_Scheme independently of X.Request_Protocol. Cleartext direct responses do not advertise HTTP/3, and cleartext, secure TCP, and QUIC each have an independent capacity.

For explicit dual-stack HTTP and HTTPS service, use the four-endpoint overload. The IPv4 and IPv6 cleartext endpoints share one port, the IPv4 and IPv6 secure endpoints share another, and the cleartext, secure TCP, and HTTP/3 capacities are divided independently between address families:

Routes.Serve
  (State,
   IPv4_HTTP_Endpoint  =>
     Sockets.Network_Endpoint (Configured_IPv4, 80),
   IPv6_HTTP_Endpoint  =>
     Sockets.Network_Endpoint (Configured_IPv6, 80),
   IPv4_HTTPS_Endpoint =>
     Sockets.Network_Endpoint (Configured_IPv4, 443),
   IPv6_HTTPS_Endpoint =>
     Sockets.Network_Endpoint (Configured_IPv6, 443),
   HTTPS_Origin        => Flyology.HTTP.Parse_Origin
     ("https://www.example.com"),
   TLS_Backend         => Backend,
   Certificate_DER     => Certificate_DER,
   Private_Key         => Private_Key,
   Cleartext           => Routing.Redirect_To_HTTPS,
   Cleartext_Capacity  => 64,
   TCP_Capacity        => 64,
   HTTP_3_Capacity     => 128,
   Token               => Stop'Access);

A secure-only dual-stack overload remains available when no cleartext listener is wanted. It takes just the IPv4 and IPv6 HTTPS endpoints, on the same port, and serves TLS/TCP plus QUIC/UDP on both families.

The unified server automatically adds Alt-Svc: h3=":443"; ma=86400 to HTTP/1.1 and HTTP/2 responses. The advertised port follows the bound endpoint, and Alt_Svc_Max_Age configures the lifetime. HTTP/3 responses omit this discovery header. The TLS provider must be initialized for server-side ALPN as shown above. Certificate_DER is an Ed25519 certificate and Private_Key is its 32-byte raw private key for QUIC. TLS/TCP may use a distinct certificate, but clients must be able to validate both identities for the requested host.

For local development, the public Flyology.HTTP.Server.Development_Certificates package generates both forms:

package Certificates renames
  Flyology.HTTP.Server.Development_Certificates;

Credentials : Certificates.Identity;

Certificates.Generate (Credentials);
declare
   Certificate_DER : constant Ada.Streams.Stream_Element_Array :=
     Certificates.QUIC_Certificate_DER (Credentials);
   Private_Key : constant Flyology.QUIC.Connections.Ed25519_Private_Key :=
     Certificates.QUIC_Private_Key (Credentials);
begin
   OpenSSL.Initialize_Server
     (Backend,
      Certificates.TLS_Certificate_File (Credentials),
      Certificates.TLS_Private_Key_File (Credentials),
      Protocols => ALPN."&" (ALPN.Offer ("h2"), "http/1.1"));
   Certificates.Discard (Credentials);

   Routes.Serve
     (State, Sockets.Network_Endpoint (Sockets.Any_IPv4, 4_433), Backend,
      Certificate_DER => Certificate_DER,
      Private_Key     => Private_Key,
      Token           => Stop'Access);
end;

The package uses RSA for compatibility with common TLS/TCP clients and the Ed25519 DER certificate and raw key required by the current QUIC profile. It removes any remaining files when the limited identity object finalizes; Discard removes them earlier after the TLS provider and QUIC values have loaded them. Because the certificates are self-signed, development clients must explicitly disable certificate verification or trust them.

The maintained showcases/http3_application_server.adb uses this API when run with no identity arguments:

./showcases/bin/http3_application_server 4433 4080

Passing TLS_CERT.pem TLS_KEY.pem QUIC_CERT.der QUIC_KEY.raw retains the explicit stable-identity form; optional trailing arguments select the HTTPS and HTTP ports. Lower-level Serve_HTTP_3_Listener and single-connection Serve_HTTP_3 adapters remain available when an application owns the UDP listener itself.

Automatic generation requires an OpenSSL command with Ed25519 support. The package recognizes conventional OpenSSL 3 installation paths and the FLYOLOGY_HTTP_OPENSSL environment variable before falling back to PATH. The certificates are self-signed, so use an HTTP/3-enabled curl and explicitly accept them when testing the H3 listener:

curl -i http://127.0.0.1:4080/hello/test
curl --version  # The Features line must include HTTP3.
curl -k --http3-only https://127.0.0.1:4433/hello/test

macOS's system curl currently lacks HTTP/3 support. A normal H2-capable curl will negotiate HTTP/2 instead. Use the IPv4 address because this showcase listener binds Any_IPv4.

HTTP/3 client

The ordinary origin-bound client can learn the unified server's HTTP/3 port without changing request or response code. Configure an ALPN-capable TCP TLS provider together with the exact DER certificate expected from QUIC:

HTTP : aliased Client.Client (Capacity => 2);

OpenSSL.Initialize_Client (Backend);
Client.Configure
  (HTTP,
   Flyology.HTTP.Parse_Origin ("https://api.example.com"),
   Backend'Access,
   Client.Negotiate_HTTP_3,
   HTTP_3_Certificate_DER => Pinned_Certificate_DER,
   Pool =>
     (Max_Idle => 2,
      Idle_Timeout => 30.0,
      Max_Connection_Age => 300.0,
      Max_Requests_Per_Connection => 0));

The first exchange uses authenticated HTTP/2 or HTTP/1.1. A response containing a same-origin Alt-Svc: h3=":port" field records a bounded alternative while healthy TCP transports remain reusable. Later requests prefer HTTP/3 on UDP. When an H3 transport is already busy or connecting, concurrent requests can immediately use retained HTTP/2 or HTTP/1.1 capacity; the client never duplicates one application request across protocols. A failed H3 establishment clears the alternative and makes one TCP attempt inside the original exchange deadline. Negotiated_Protocol reports the protocol used by each response. Set pool capacity and Max_Idle to at least two to keep both protocol stacks warm while idle, as in the example.

When DNS returns both families, the client runs one bounded establishment lane per family under the same filter, cancellation sources, and exchange deadline. The first complete TCP connect or QUIC handshake wins. A losing connected QUIC leg sends an application close before releasing UDP, so server admission is returned immediately.

Use Require_HTTP_3 with the certificate overload that has no TCP provider to send QUIC directly to the HTTPS origin's UDP port. Both modes authenticate the exact peer certificate supplied as 1 through 4,096 DER bytes. The current H3 pool reuses each connection sequentially rather than multiplexing requests; retained bodies and borrowed request sources are emitted incrementally under QUIC flow control. Expect: 100-continue remains unavailable on H3.

Scope

  • HTTP/1.0 response compatibility and HTTP/1.1 client and server messages.
  • Opt-in HTTP/2 clients with ALPN fallback or requirement, cleartext prior knowledge, multiplexed streams, and bounded receive flow control.
  • HTTP/2 application servers over cleartext prior knowledge or an ALPN-negotiated Flyology connection, with multiplexed stream handlers, bounded request and response buffers, routing, middleware, SSE, and flow-controlled streaming bodies.
  • Origin-bound HTTP pools with one monotonic exchange deadline and single-session WebSocket clients with monotonic operation deadlines, including direct or same-origin Alt-Svc-discovered HTTP/3 transports.
  • Fixed-length and chunked request bodies with bounded streaming adapters.
  • Exact 64-bit fixed-length server response streams, plus compatible unknown-length streams, without retaining the whole body.
  • Optional routing, middleware, native offload, SSE, and WebSocket facilities.
  • Provider-neutral TLS integration through Flyology I/O.
  • A low-level HTTP/3 client/server session over the Ada-native flyology_quic transport, with control streams, SETTINGS, a static-table QPACK profile, request and response sequencing, and bounded HEADERS and DATA events.
  • An HTTP/3 routed-server adapter using the protocol-neutral application exchange, including middleware, route parameters, body policies, fixed and streamed responses, and SSE.
  • A unified routed server with a distinct cleartext HTTP/1.x endpoint plus TLS HTTP/1.1 and HTTP/2 and QUIC HTTP/3 on one secure TCP/UDP port. It can route or redirect cleartext requests and advertises HTTP/3 through Alt-Svc only on secure TCP responses.

The HTTP/2 server uses the protocol-neutral Applications.Exchange and the same routes and middleware as HTTP/1.1. The raw HTTP/1.x Server.Connection API is not emulated because HTTP/2 has concurrent stream rather than connection-scoped response state. HTTP/2 does not currently provide h2c Upgrade, server push, extended CONNECT/WebSockets, or HTTP/1.1 fallback inside the HTTP/2 connection adapter. Cleartext deployments can use HTTP/2 prior knowledge, while TLS deployments can use ALPN and route HTTP/1.1 and HTTP/2 through the same Routing.Router. Server push is deliberately not exposed; the client disables it and applications should use ordinary routed responses. Extended CONNECT needs a stream-oriented tunnel API rather than the existing HTTP/1.1 connection-borrowing WebSocket API. On the client, borrowed streaming request sources use bounded flow-control storage on HTTP/1.1, HTTP/2, and HTTP/3; replay still requires an explicit rewindable source. The Expect: 100-continue handshake remains HTTP/1.1-only. HTTP/3 reuses pooled connections sequentially. The library does not provide proxying or content decoding. The managed UDP adapter uses a bounded connection registry and fixed worker set, dispatches request streams synchronously, and buffers each complete request stream before entering the route handler. Each connection retains at most 32 concurrent QUIC stream reassembly buffers and recycles completed request state. The unified server defaults to 128 concurrent HTTP/3 connections alongside 64 TCP connections, serves 100,000 requests per H3 connection by default, and accepts explicit limits up to 256 concurrent H3 connections and 1,000,000 requests per connection. Completed exchanges do not accumulate in the connection, so this lifetime limit controls connection rotation rather than retained request memory. Connection capacity separately controls the fixed worker set. The QPACK profile does not use the dynamic table. It is experimental and does not claim production qualification. HTTP/3 server push is disabled. Because this profile does not advertise MAX_PUSH_ID, received push promises and push streams are rejected as required by RFC 9114.

Flyology HTTP is dual-licensed under MIT or Apache-2.0.