Choose semantics before parsing.
Select the Syntax_Kind before parsing. Use URI_Syntax for RFC 3986 ASCII references. Use IRI_Syntax for RFC 3987 references that contain well-formed UTF-8. Use Web_URL_Syntax for browser-style URL parsing and serialization.
URI_Syntax- Validate absolute or relative RFC 3986 references. Percent escapes must contain two hexadecimal digits.
IRI_Syntax- Apply the reference grammar while accepting well-formed UTF-8 in IRI component positions.
Web_URL_Syntax- Normalize special and non-special URLs, IDNA hosts, IPv4 and IPv6 literals, dot segments, default ports, and
file:paths. It can resolve input against a parsed web base URL.
Use web mode for an HTTP or WebSocket endpoint supplied as one URL. Use URI or IRI mode when the value is a protocol-neutral reference and browser URL normalization would be the wrong contract.
Keep one owned serialization and component spans.
Call the ordinary Parse (Input, Syntax) overload for a value with an explicit syntax. The example inspects Scheme, Host, Path, and Query.
with Flyology_IRI;
package IRI renames Flyology_IRI;
URL : constant IRI.Reference := IRI.Parse
("HTTPS://Example.COM:443/catalog/../items?q=ada#result",
IRI.Web_URL_Syntax);
pragma Assert (IRI.Image (URL) =
"https://example.com/items?q=ada#result");
pragma Assert (IRI.Scheme (URL) = "https");
pragma Assert (IRI.Host (URL) = "example.com");
pragma Assert (IRI.Path (URL) = "/items");
pragma Assert (IRI.Has_Query (URL));
pragma Assert (IRI.Query (URL) = "q=ada");Reference owns one normalized serialization and records component offsets. Component getters return strings. Has_Query and Has_Fragment distinguish an absent delimiter from a present empty component.
Web parsing lowercases schemes and domains, applies IDNA/Punycode, canonicalizes IP literals and ports, and percent-encodes required bytes. It preserves the URL distinction between hierarchical and opaque paths. Image returns the canonical serialization, which can differ from the input bytes.
Derive the HTTP origin and target from the same URL.
The HTTP Client accepts one origin during configuration and one origin-form target in each Request. Parse a complete endpoint once. Then use Origin and Target instead of splitting strings by hand.
with Flyology.HTTP;
with Flyology.HTTP.Client;
with Flyology_IRI;
package Client renames Flyology.HTTP.Client;
package IRI renames Flyology_IRI;
Endpoint : constant IRI.Reference := IRI.Parse
("https://api.example.com/v1/items?limit=20#selection",
IRI.Web_URL_Syntax);
HTTP : aliased Client.Client (Capacity => 4);
Request : Client.Request;
Client.Configure
(HTTP,
Flyology.HTTP.Parse_Origin (IRI.Origin (Endpoint)),
Backend'Access,
Client.Default_Pool_Configuration);
Client.Set_Target (Request, IRI.Target (Endpoint));
-- Origin is "https://api.example.com".
-- Target is "/v1/items?limit=20"; URL fragments are not sent in HTTP.
Reply := Client.Execute (HTTP, Request, Timeout => 5.0);If a parsed URL uses http, https, ws, or wss, Origin returns its normalized scheme, host, and optional nondefault port. It omits URL credentials.
Target returns the normalized path and optional query, including a present empty query. It omits the fragment. Both operations raise Malformed_Reference when the reference cannot represent this network URL boundary.
The example passes those values to Flyology.HTTP.Parse_Origin, Client.Configure, and Client.Set_Target. The default retention record is Default_Pool_Configuration; the retained-response Execute overload performs the request.
Use the same adapters for WebSocket handshakes.
The WebSocket_Client package uses a Request with a ws or wss connection origin and a request target. The optional browser Origin header is a separate HTTP(S) application origin. Parse those values separately so their roles stay visible.
with Flyology.HTTP.WebSocket_Client;
with Flyology_IRI;
package WebSockets renames Flyology.HTTP.WebSocket_Client;
package IRI renames Flyology_IRI;
Endpoint : constant IRI.Reference := IRI.Parse
("wss://events.example.com/socket?topic=builds",
IRI.Web_URL_Syntax);
Application : constant IRI.Reference := IRI.Parse
("https://app.example", IRI.Web_URL_Syntax);
WebSockets.Configure
(Socket, WebSockets.Parse_Origin (IRI.Origin (Endpoint)),
Backend'Access);
WebSockets.Set_Target (Request, IRI.Target (Endpoint));
WebSockets.Set_Origin (Request, IRI.Origin (Application));
WebSockets.Connect (Socket, Request, Timeout => 5.0);The example constructs the destination with WebSockets.Parse_Origin, uses the TLS Configure overload, sets the target with Set_Target, and calls Connect. Set_Origin sets the browser-origin handshake field; it does not select the socket destination. Keep it absent for a non-browser client unless server policy requires it. Never copy an endpoint's wss: origin into that HTTP-origin field.
Resolve with the semantics of the selected mode.
Base : constant IRI.Reference := IRI.Parse
("https://example.com/catalog/2026/index.html",
IRI.Web_URL_Syntax);
Next : constant IRI.Reference := IRI.Parse
("../items?q=ada#details", Base);
pragma Assert (IRI.Image (Next) =
"https://example.com/catalog/items?q=ada#details");The Parse (Input, Base) overload applies WHATWG URL resolution, including special-scheme slash handling and file: drive-letter rules. For RFC URI or IRI references, call Resolve (Base, Relative). That operation follows RFC 3986 section 5.2 instead of the browser state machine.
Choose exceptions, diagnostics, or a Boolean fast path.
Value : IRI.Reference;
Error : IRI.Parse_Error;
IRI.Try_Parse
(Input, Value, Error,
Syntax => IRI.Web_URL_Syntax,
Max_Length => 8 * 1_024);
if Error.Kind = IRI.No_Error then
Use (Value);
else
Report (Error.Kind, Error.Offset);
end if;No_Error is the successful diagnostic result. The Kind accessor and Offset field locate a failure. The ordinary Parse overload raises Malformed_Reference. Try_Parse returns a stable Parse_Error and can reuse the destination's owned-string capacity. Diagnose reports the first error and byte offset for URI/IRI grammar checks.
Can_Parse avoids constructing a reference. Common absolute HTTP(S), URI, and IRI validation paths do not allocate. Less common web inputs may allocate during normalization or IDNA handling.
At untrusted boundaries, keep a finite Max_Length. It bounds validation input and the constructed serialization. Normalization can make a serialized URL longer than its source.
Keep conformance and performance claims pinned.
The maintained parser harness compares failure, canonical href, protocol, credentials, host and hostname, port, pathname, search, and hash against all 919 parsing cases in Ada URL 4.0.0's pinned WHATWG data. The exact ada-url/url-dataset benchmark contains 100,025 nonempty inputs; both implementations accept 99,999.
On the recorded Apple M3 Max development run, five independent ten-corpus repetitions produced medians of 20 ns per URL for Flyology validation versus 19 ns for Ada URL, and 103 ns for Flyology parse-plus-href versus 104 ns for Ada URL. These figures establish local parity on that pinned workload, not portable performance or production qualification. Reproduce the revisions and commands from the repository's flyology_iri/BENCHMARKS.md.