Skip to content

ddn.net.uri

The ddn.net.uri module provides a complete, practical implementation of RFC 3986 (URI Generic Syntax) in D.

It supports parsing and serializing absolute URIs and relative references, component access (scheme, authority, path, query, fragment), normalization, reference resolution (RFC 3986 §5.2), percent-encoding helpers, IDNA/Punycode for internationalized hostnames, and RFC 6570 URI Templates.

Overview

The core type is URI, which represents either:

  • an absolute URI (has a scheme), or
  • a relative reference (no scheme).

For convenience, the module also provides:

  • URL: a wrapper for hierarchical absolute URIs with an authority (scheme://host...)
  • URN: a wrapper for urn: URIs with convenient access to the NSS

Key features

  • RFC 3986 parsing & serialization: absolute URIs and relative references
  • Normalization: scheme/host case-folding, dot-segment removal, percent-encoding normalization, default port elision
  • Reference resolution: RFC 3986 §5.2 base.resolve(reference)
  • Percent-encoding utilities: encode/decode/normalize, per component
  • IDNA/Punycode for hostnames: URI.normalizeIDNA() and hostUnicode()
  • URI Templates (RFC 6570): UriTemplate.parse(...) + expand(...) (Levels 1–4)

Basic usage

Parse and serialize

import ddn.net.uri;

auto u = URI.parse("http://user:pass@example.com:80/a/./b/../c?x=1#frag");

assert(u.scheme == "http");
assert(u.hasAuthority);
assert(u.authority.host == "example.com");
assert(u.authority.port == 80);
assert(u.path == "/a/./b/../c");
assert(u.query == "x=1");
assert(u.fragment == "frag");

assert(u.toString() == "http://user:pass@example.com:80/a/./b/../c?x=1#frag");

Normalize

import ddn.net.uri;

auto u = URI.parse("HTTP://EXAMPLE.COM:80/%7Euser/./a/../b");
auto n = u.normalize();

assert(n.toString() == "http://example.com/~user/b");

Resolve a relative reference

import ddn.net.uri;

auto base = URI.parse("http://a/b/c/d;p?q");
auto ref  = URI.parse("g?y#s");

assert(base.resolve(ref).toString() == "http://a/b/c/g?y#s");

IDNA (internationalized hostnames)

Use normalizeIDNA() to apply RFC 3986 normalization and convert the host (when present and not an IP-literal) to ASCII using IDNA ToASCII.

import ddn.net.uri;

auto u = URI.parse("http://bücher.example/");
auto n = u.normalizeIDNA();

assert(n.toString() == "http://xn--bcher-kva.example/");
assert(n.hostUnicode() == "bücher.example");

Standard compliance

This module focuses on URI/URL/URN parsing, normalization, and related helpers. The table below summarizes which URI-related RFCs are supported and at what level.

RFC Topic Support Notes
RFC 3986 URI Generic Syntax Yes Core parser/serializer/normalizer + RFC 3986 reference resolution (§5.2).
RFC 3987 Internationalized Resource Identifiers (IRIs) Partial Non-ASCII hostnames can be parsed (reg-name) and normalized via normalizeIDNA(), but this is not a full IRI implementation for all components.
RFC 6570 URI Templates Yes Full Level 4 operator support with conformance tests from RFC examples (UriTemplate.parse / expand).
RFC 2141 URN Syntax (obsolete) Partial URNs can be parsed/serialized via URN wrapper, but this module does not implement URN-specific resolution rules.
RFC 8141 URN Syntax (current) Partial URN wrapper is syntax-focused; does not implement the full RFC 8141 processing model.
RFC 3492 Punycode Yes Core Punycode algorithm implemented (punycodeEncodeLabel / punycodeDecodeLabel).
RFC 5890–5895 IDNA2008 Partial Basic ToASCII/ToUnicode + some strict checks and optional limited mappings; does not implement full contextual rules/UTS #46.
RFC 6874 IPv6 Zone Identifiers in URIs Yes Parses/serializes zone identifiers in IP-literals using %25 encoding (e.g. http://[fe80::1%25eth0]/).
RFC 5952 IPv6 Text Representation No The module validates IPv6 but does not fully canonicalize IPv6 text per RFC 5952.

URI Templates (RFC 6570)

RFC 6570 URI Template support is implemented in the same module: ddn.net.uri.

Public entry points:

  • UriTemplate.parse(string)
  • UriTemplate.expand(UriTemplateVars)
import ddn.net.uri;

auto t = UriTemplate.parse("http://example.com{/path}{?q}");

UriTemplateVars vars;
vars.set("path", UriTemplateValue.fromScalar("foo/bar"));
vars.set("q", UriTemplateValue.fromScalar("hello world"));

assert(t.expand(vars) == "http://example.com/foo%2Fbar?q=hello%20world");