Revision history for Fetch

0.20    2026-08-31
        - A cancelled deadline can no longer fail the next request on
          any backend: the loop names timers by a counter, not by the
          watcher address the allocator recycles.
        - t/09-timeout.t: scale the deadlines off the measured round
          trip, and say what the failure was when one fires early.

0.19    2026-08-23
        - io_uring: cancelling a request deadline could crash the loop.

0.18    2026-08-23
        - t/25-tunnel-starttls.t: the fake STARTTLS server now ignores
          SIGPIPE. LibreSSL writes the TLS 1.3 server flight one record
          per write, so when the client refuses the certificate mid-flight
          the next handshake write hits the reset socket and the signal
          killed the fake

0.17    2026-08-22
        - fetch_abi v3: tunnel_starttls(conn, host, verify) upgrades a
          plaintext tunnel to TLS after the application protocol has
          negotiated it 

0.16    2026-08-20
        - FIX: change abi version checks from == to <=

0.15    2026-08-19
        - HTTP/2 TRAILERS are captured and exposed as $res->trailers.
          They were being discarded.
        - t/24-h2-trailers.t tests this against nghttpd

0.14    2026-08-18
        - fetch_abi v2: on_request, an observer for outbound requests.
        - Fetch->on_request(\&start, \&done): the same registry, reached
          from Perl.

            Fetch->on_request(
                sub { my ($method, $url, $headers) = @_;
                      push @$headers, traceparent => span_id();
                      return { at => time, url => $url } },
                sub { my ($token, $res, $err) = @_;
                      record($token->{url}, time - $token->{at},
                             $err ? "failed: $err" : $res->status) },
            );

0.13    2026-08-15
        - Two agents with no passed loop in one process no longer hang each other. Every
          Fetch->new built its own Standalone loop, but install_await
          writes the process-global $Fetch::Future::AWAIT, so the agent
          constructed last owned the await path for both: awaiting the
          first agent's future pumped the second agent's loop, which has
          no watcher for that socket, and the backend blocked in the
          kernel with no deadline.
        - The implicit loop (no `loop` argument) is now one per process
          rather than one per agent, rebuilt in a child after a fork.
          Agents that did not ask for a specific loop therefore multiplex:
          awaiting on one drives what the others have in flight.
        - Loop adapters gained _ft_await($future), the per-instance form of
          what install_await put in the global. Third-party adapters
          without it keep working through the old global hook.
        - Awaiting on a loop with no watchers and no timers dies with a
          diagnostic instead of blocking forever.

0.12    2026-08-12
        - A keep-alive connection the server closes at the moment it is reused
          no longer fails the request. 

0.11    Date/time
        - $ua->clone(%overrides): another agent over this one's connection
          pool and loop, with the named options replaced. The case it exists
          for is a per-request cookie jar - a jar belongs to the agent, so a
          jar on a long-lived agent is shared by every request that agent
          serves, which is a cross-request leak the moment the cookies
          identify an end user rather than the application. Building a fresh
          agent per request avoids that but throws away the keep-alive pool
          and re-resolves the loop adapter, which is most of what new()
          costs; a clone gives the isolation without the bill.
        - cookie_jar, headers, agent, timeout, tls_verify, max_redirects,
          keep_alive and simple_response are overridable, and
          cookie_jar => undef drops an inherited jar. Headers are copied
          rather than shared, so a clone cannot write into the parent's
          defaults.

0.10    2026-08-05
        - Drive Hyperman loops through Hyperman's public C ABI 
        - Awaiting on a Hyperman loop ($future->get) is also C now: the
          AWAIT hook bridges the Fetch::Future to a Hyperman::Future through
          the ABI and pumps with run_until 

0.09    2026-08-04
        - Security: credential headers (Authorization, Cookie, Proxy-
          Authorization) are no longer sent across a redirect that changes
          origin (scheme, host or port). A token or explicit cookie set for the
          original host is dropped when a 3xx points at a different one; jar
          cookies were already re-scoped per host. t/17-redirect-auth.t.
        - Security: reject a Set-Cookie whose Domain is a bare public suffix /
          single label (e.g. "com", ".com", "localhost") - it must carry an
          interior dot, so a server cannot scope a cookie to a whole TLD. 
        - Security: a response carrying both Content-Length and Transfer-
          Encoding: chunked is now framed strictly by the chunked encoding
          (RFC 7230 3.3.3, TE overrides CL) with the ambiguous Content-Length
          dropped - it cannot truncate the body or desync a keep-alive
          connection. t/18-cl-te.t.
        - Loop watcher callbacks (the per-readiness-event hot path) are now
          dispatched directly: the standalone loop recognises its own C
          closures and calls the XSUB body straight, skipping pp_entersub.

0.08    2026-08-04
        - JSON (the `json =>` request option and Fetch::Response->json) now goes
          through File::Raw::JSON's C ABI
        - Fetch is now an ExtUtils::Depends provider
        - Bodyless responses: 1xx, 204 and 304 never carry a body (RFC 7230
          3.3.3), and a server MUST NOT send Content-Length on 1xx/204, so
          the h1 parser treated the missing length as read-until-close - a
          204 on a keep-alive connection hung until timeout. Those statuses
          now complete at the end of headers.

0.07    2026-08-04
        - Build and run on perl before 5.14. A new include/fetch/ft_compat.h
          supplies fallbacks for perl API the C core used but which predates
          the perls Fetch claims (5.8.3+), all guarded by PERL_VERSION so newer
          perls are untouched:
          - XSPROTO / XS_INTERNAL / XS_EXTERNAL (the closure-callback macros,
            added to XSUB.h at 5.16): without them the build failed to compile
            ("XS_INTERNAL undeclared" / "cv undeclared") before any test ran.
          - mg_findext (5.14), used by the C-closure machinery to fetch a
            closure's captured magic: without it the module built but died at
            runtime ("undefined symbol: mg_findext"), failing most tests.
          - croak_sv (5.13.1), used on a few error paths.

0.06    2026-08-03
        - Build against older OpenSSL. The TLS code uses the OpenSSL 1.1.0 API
          (TLS_client_method, SSL_CTX_set_min_proto_version, SSL_set1_host);
          on libraries that lack it (OpenSSL 1.0.x / 0.9.8 on legacy BSDs,
          LibreSSL before 2.7) the loadable object failed to load with an
          undefined symbol. TLS now compiles to stubs there, as when OpenSSL
          is absent, so the module loads and cleartext HTTP works (https then
          fails cleanly).

0.05    2026-08-02
        - Add an on_headers request option: a coderef fired once, before any
          on_body chunk, with ($status, [k,v,...]). Lets a streaming consumer
          act on the status/headers up front (e.g. open a downstream writer).
          Not fired for a WebSocket upgrade.
        - Add a C ABI so other XS modules can drive Fetch from C without a
          per-request Perl round trip. 

0.04    2026-08-02
        - Fixes for Windows build:
          - Rename the readiness-backend vtable member "wait" to "wait_ev"
          - Add a portable ft_memmem (a GNU/BSD extension MinGW lacks)
          - Define _CRT_RAND_S at the very top of Fetch.xs

0.03    2026-08-02
        - Fetch->new(simple_response => 1): resolve to a plain unblessed hashref
          { status, headers, content } instead of a blessed Fetch::Response, so
          callers read fields directly and skip method dispatch. A lean option
          for consuming very large numbers of responses.
        - Native Windows (MSWin32) port. A new socket/IO shim (ft_win.h) maps
          the ~dozen socket calls onto Winsock: sockets are wrapped in a CRT fd
          via _open_osfhandle so the rest of the core keeps its dense "int fd"
          model (per-fd callback arrays, the backend, TLS all unchanged), with
          the real SOCKET recovered only at the Winsock boundary and WSA errors
          mapped to errno. A select()-based readiness backend (backend_select.c)
          replaces poll (which Windows lacks)
        - Link -lrt when clock_gettime needs it. backend_poll.c calls
          clock_gettime(CLOCK_MONOTONIC), which lives in librt on glibc before
          2.17

0.02    2026-08-01
        - Fix a missing NUL terminator in the URL-scheme lowercaser
          (ft_lc_dup)

0.01    2026-08-01
        First version
