Revision history for Hyperman

0.32    2026-08-21
        - A worker no longer goes permanently deaf to the bus. The
          wakeup's coalescing flag was cleared before its pipe was
          emptied.
        - bus_gaps reports what the DISPATCHER dropped as well as what
          receive() did. 

0.31    2026-08-21
        - A queue group no longer drops a message because the worker
          that claimed it looked too early.

0.30    2026-08-20
        - A request body over 1MB is written to a temp file as it
          arrives instead of being held in memory, and psgi.input is a
          handle on that file. Measured end to end through a socket, a
          128MB upload costs a worker about 15MB of RSS against roughly
          275MB before.
        - Make one fewer syscall per request. The read loop drained to
          EAGAIN, so an ordinary keep-alive request cost three syscalls -
          a read that returned the request.
        - Header name comparison no longer depends on the operator's
          locale. hm_strncasecmp was the C library's, which folds case
          by LC_CTYPE, and every caller compares an HTTP header name or
          token - ASCII by RFC 9110. Under a Turkish locale the library
          folds 'I' to the dotless form, so "CONTENT-LENGTH" would stop
          matching "Content-Length" and the framing header would go
          unrecognised. 
    
0.29	2026-08-20
        - The Plack handler passes the bus params through. bus_slots,
          bus_slot_size and bus_groups.
        - A queue group registered before the server starts now works.

0.28    2026-08-20
        - A cross worker message bus. One ring in shared memory, mapped
          before the fork, so every worker publishes into and reads from
          the same copy. 

0.27    2026-08-18
        - Hyperman->on_worker_start(sub { ... }): run code in every
          worker, after the fork, before its loop starts turning.
        - Hyperman->deny_add / deny_remove / deny_check / ratelimit_hit:
          the fork-shared arena, as class methods.

0.26    2026-08-18
        - hm_abi v4: on_worker_start. A C consumer registers a callback
          before run(), and the server runs it once in every worker,
          after the fork, in the child, with that child's own loop, and
          before the loop starts turning.

0.25    2026-08-17
        - Response compression: `compress => 1` on run() gzips responses
          on the way out (needs the zlib build; Hyperman->has_compression
          reports it, and without zlib the option is accepted and inert).
        - Hyperman NEVER touches a response that already carries a
          Content-Encoding. That one rule is the whole contract with
          whatever sits above: a framework serving precompressed .gz
          files sets `gzip` and its bytes go out untouched; a route that
          wants no compression sets `identity`, which is honoured and
          stripped. A plain response header, so any PSGI framework can
          use it.
        - Compression is OFF by default, under run() and under
          Plack::Handler::Hyperman alike, and both take the same
          options. Nobody's server starts compressing because they
          upgraded: whether the CPU is worth paying depends on whether
          bandwidth or the processor limits the deployment, which only
          its operator knows.
        - New run() options: compress, compress_min_length (default
          1400, one MTU), compress_level. Plack::Handler::Hyperman
          passes all three through, so `plackup -s Hyperman
          --compress 1` is the same server.

0.24    2026-08-17
	- More windows fixes

0.23    2026-08-16
        - Hyperman attempt to build and serve on native Windows with no 
          behaviour change on any platform it already ran on
        - The socket/IO surface now goes through one shim header
        - A WSAPoll readiness backend (backend_wsapoll.c) behind the
          existing hm_backend vtable, so Windows has the floor that
          poll(2) provides everywhere else
        - The process model is now explicit about what a platform can
          do. The supervisor half - fork, waitpid, the respawn backoff,
          the HUP/USR2 recycle and the POSIX signal plumbing - is
          compiled only where those exist.
        - Fixed a latent bug in the C ABI selftest, which asserted
          arena SEMANTICS (add a denylist entry, expect it to be found)
          without first asking whether this build has an arena. 

0.22    2026-08-16
        - t/27-sendfile.t asserted the worker's ABSOLUTE RSS stayed
          under 60MB after a 16MB download, but a worker's absolute
          size is a property of the perl build - static-libperl and
          threaded smokers idle at 62-83MB and failed the test with
          streaming working perfectly. It now measures the GROWTH
          across the download (baseline RSS before, probe after) and
          asserts it stays under 8MB

0.21    2026-08-16
        - New Hyperman->tls_library: the runtime TLS library banner
          ("OpenSSL 3.0.13 ...", "LibreSSL 3.8.2"), undef without the
          TLS build - the supported way to tell the two stacks apart.
        - t/25-tls-session.t no longer fails on LibreSSL (OpenBSD, from
          CPAN Testers): LibreSSL implements no TLS 1.3 session
          resumption on either side of a connection, so the resumption
          checks could never pass there however correct the ticket key
          was. The ticket-key machinery under test is protocol
          independent, so on a LibreSSL stack those checks now run over
          TLS 1.2, where LibreSSL's tickets work.

0.20    2026-08-16
        - Filehandle and IO-object response bodies stream instead of
          being slurped into one SV. A glob over a regular file lifts to
          (fd, position, remaining) - remaining is the Content-Length
          header when the response carries one.
        - use sendfile(2) on plaintext HTTP/1 file responses

0.19    2026-08-14
        - Fixed a build failure on compilers older than GCC 4.7, which
          took out every perl on FreeBSD 9 (base cc is gcc 4.2.1):
          hm_ratelimit.h probed for __GNUC__ and then used the __atomic
          builtins, which arrived five releases later, so the arena
          added in 0.18 would not compile there at all. It now probes
          for the builtins themselves and falls back to the older
          __sync family, which also has everything the arena needs.

0.18    2026-08-13
        - Abuse controls on a fork-shared arena: an IP denylist enforced
          at accept and fixed-window rate counters, both in one anonymous
          shared-memory region mapped before the workers fork, so a limit
          is exact across the pool rather than per worker. Everything
          fails open.
        - run() gains deny => [ips], deny_capacity and rate_capacity. A
          denylisted peer is dropped at accept, before a connection object
          is built or a byte is read; a new `denied` worker stat counts it.
        - hm_abi.h grows to ABI version 3 (append-only): deny_check,
          deny_add, deny_remove and ratelimit_hit, for consumers such as
          Punk's rate_limit. Older consumers are unaffected.
        - Rate-counter slots are reclaimed on demand: a slot whose window
          has rolled is reused by a new key before a live counter would be
          evicted, so the table drains when the keys that filled it go
          quiet rather than thrashing at capacity.

0.17    2026-08-12
        - TLS session resumption now survives tls_reload. 
        - HTTPS connections no longer pay a wasted SSL_read on every
          writable event. 
        - A TLS connection whose SSL_read blocked wanting a writable
          socket no longer risks spinning the worker at 100% CPU.
        - SSL_MODE_RELEASE_BUFFERS, so OpenSSL's record buffers are handed
          back when they drain instead of being pinned for a connection's
          whole life - a keep-alive server is mostly idle connections.
        - A session id context is now set, without which OpenSSL declines
          to resume any session on a context that verifies peers: every
          mTLS connection paid a full handshake and a full chain
          verification.
        - SNI hostname lookup is a binary search over a sorted, lowercased
          table rather than a strcasecmp walk of every entry, which is a
          per-handshake cost that grew with the number of certificates
          served.
        - close_notify is no longer written for a connection dropped
          mid-handshake, where there is no session to shut down.
        - Tests no longer derive their listening port from $$.
        - A failed fork during a SIGHUP recycle no longer stores -1 in the
          child table, where the shutdown sweep would have turned it into
          kill(-1, SIGTERM) and signalled every process owned by the uid.

0.16    2026-08-12
        - t/18-tls-advanced.t skips, rather than dies, when the host
          cannot mint a certificate. 
        - t/16-http2.t no longer requires curl's --parallel.

0.15    2026-08-10
        - Hyperman->tls_reload(\%sni): replace a worker's TLS
          certificates without replacing the process. 

0.14    2026-08-08
        - An inherited loop's kernel objects are now off-limits to the io
          and timer watch paths, not just to free. On Linux an epoll
          instance is a SHARED kernel object across fork - the fd is
          duplicated but both processes' epoll_ctl calls edit the same
          interest list - so a forked child that "cleaned up" watchers on
          a loop it merely inherited was silently deregistering its
          PARENT's fds.

0.13    2026-08-07
        - A loop inherited across a fork no longer closes descriptors that
          are not its own. Every fd in a loop - the backend's own, the
          connection sockets, the access log - belongs to the process that
          created the loop, so a loop the child merely inherited is now
          freed without closing any of them.
        - Pre-size the PSGI env hash. A fresh HV starts with 8 buckets and
          the env carries 25-odd keys before the app adds its own, so
          every request paid two bucket split reallocs and their rehash
          walks. under profile S_hsplit alone was 4% of a worker's
          samples. hv_ksplit(64) at both env build sites (HTTP/1 and
          HTTP/2) removes it outright.
        - Store the common HTTP_* header keys through shared key SVs made
          once at init.
        - Fold header names to their canonical HTTP_ form before matching,
          so the Content-Length / Content-Type / Transfer-Encoding and
          Connection tests are plain memcmp on known-case bytes instead of
          case-blind scans of the wire bytes, per header, per request. The
          fold maps '-' to '_', so the special names also check the wire
          byte at the hyphen position - a literal "Content_Length" header
          stays HTTP_CONTENT_LENGTH rather than aliasing the real one,
          exactly as before.

0.12    2026-08-06
        - io_uring: cancelling a timer could corrupt the heap. ur_del_timer
          freed the ur_timer immediately, but the timeout is already in flight
          in the kernel and its user_data IS that pointer, so the completion
          still arrived, was dereferenced, and freed a second time. 
        - Workers report their own pid again on perl below 5.14. $$ is a plain
          SV that perl only refreshes inside pp_fork, and hm_spawn forks from
          C, so every worker in the pool kept reporting the supervisor's pid -
          and POSIX::getpid is `sub getpid { $$ }`, so that read wrong too.
        - t/15-process.t asserts the invariant directly: no worker may answer
          with the supervisor's pid.
        - t/22-abi.t asserted _abi_ptr returns a positive IV. It returns a raw
          pointer, and Solaris x86-64 maps shared objects high enough to set
          the sign bit, so the assertion was wrong rather than the table.

0.11    2026-08-06
        - Connection detach, the protocol-upgrade seam: Hyperman::detach($env)
          hands a live HTTP/1 socket to the application. The server stops
          watching the fd, forgets the connection and does not close it, so
          the app's own psgix.loop watchers on that fd fire. The app writes
          its own upgrade response and returns a discarded sentinel
          ([101, [], []]). This is what a WebSocket layer needs; before it,
          a hijacked socket kept racing the server's own read watcher.
        - $env->{'psgix.hyperman.conn'} = [ fd, generation id ] - the ticket
          detach works from, and the way an app detects that detaching is
          possible (HTTP/2 streams and other servers do not provide it).
        - Detach is refused, with a specific message, for HTTP/2 and TLS
          connections, a stale ticket, a response still draining, and a
          second detach of the same connection.
        - 1xx and 426 responses now carry their real reason phrase: a 101
          serialised as "HTTP/1.1 101 OK" before this.

0.10    2026-08-05
        - Public C ABI for XS consumers 

0.09    2026-08-04
        - Chunked request bodies (Transfer-Encoding: chunked) are now decoded
          (HTTP/1). The decoded body is presented as psgi.input with a matching
          CONTENT_LENGTH; chunk extensions and trailers are accepted.
        - Security: harden HTTP/1 request framing against request smuggling
          (RFC 7230 3.3.3). Transfer-Encoding together with Content-Length, a
          repeated Transfer-Encoding, or a duplicate/malformed Content-Length
          (a sign, whitespace, trailing junk, or overflow) is rejected 400; an
          unsupported transfer coding is 501. All close the connection so no
          chunk data can be mis-framed as a following pipelined request.
          t/21-smuggling.t.
        - Security: the built-in redirect_https listener now truncates the
          reflected Host and request-target at the first control/space byte, so
          a bare LF in the Host header cannot inject or split the 301 response.
          Fuzz harness for the request parser under ASan/UBSan in tools/fuzz/.
        - Robustness: allocation helpers hm_xmalloc/hm_xcalloc/hm_xrealloc that
          croak on OOM, so no hot-path allocation dereferences a NULL result.
          tools/cppcheck.sh runs a static-analysis scan (clean).
        - PSGI env: add REMOTE_HOST (the peer address, like REMOTE_ADDR),
          REMOTE_PORT (the peer's port, captured at accept), and
          psgix.input.buffered => 1 (psgi.input is a seekable in-memory :scalar
          handle). Set for both HTTP/1 and HTTP/2.
        - Multiple listeners in one run(). Pass listen => [ {..}, .. ] to bind
          several listeners, each independently plain or TLS - the driving case
          being plain :80 beside HTTPS :443 - or port => [80, 8080] as sugar for
          several plain listeners sharing the top-level options. SERVER_PORT and
          psgi.url_scheme reflect the listener each request arrived on.
        - Built-in https redirect. A listener with redirect_https => $port
          answers every request with a 301 to the same host and target on that
          https port.

0.07    2026-08-03
        - Build on OpenSSL older than 1.0.2. ALPN (SSL_CTX_set_alpn_select_cb),
          used to offer HTTP/2 over TLS, is absent before 1.0.2, so the module
          linked an undefined symbol and failed to load on such smokers. Guard
          it behind a version check.

0.06    2026-08-02
        - Fast default access log. access_log now accepts a file path or an
          open handle in addition to a coderef: Hyperman then formats an
          Apache Combined Log Format line in C and appends it directly, with
          no per-request Perl call on the hot path. 
        - Populate REMOTE_ADDR in the PSGI env (HTTP/1 and HTTP/2), captured
          from the peer at accept. Needed by the access log and expected by
          apps.

0.05    2026-08-02
        - Various fixes to compile on perls older than 5.16.
        - Correct MIN_PERL_VERSION from 5.008003 to 5.010000

0.04    2026-08-02
        - Link -lrt when clock_gettime needs it. backend_poll.c calls
          clock_gettime(CLOCK_MONOTONIC), which on glibc before 2.17 lives in
          librt, not libc.
        - t/12-loop.t: make the persistent-watcher subtest more robust by adding a
          watcher to prevent the test hanging like it has in some cpantesters environments.

0.03    2026-08-01
        - TLS: build against pre-1.1.0 OpenSSL. Older releases lack
          OPENSSL_init_ssl(), the OPENSSL_INIT_LOAD_*_STRINGS constants,
          TLS_server_method(), and SSL_CTX_set_min_proto_version(); provide
          a compatibility shim (SSL_library_init + SSLv23_server_method +
          SSL_OP_NO_* protocol fencing) so hm_tls.h compiles instead of
          failing with undeclared-identifier errors on those hosts. Also map
          SSL_get1_peer_certificate to SSL_get_peer_certificate before 3.0.
        - Makefile.PL: bail out cleanly (NA, not FAIL) on native Windows.
          Hyperman needs fork, POSIX signals, sockets, sys/uio, and a
          kqueue/epoll/io_uring/poll backend, none of which MSWin32/MinGW
          provides (the build previously died on a missing <sys/uio.h>).
        - t/14-prod.t: bound the respawn/recycle/stats polls by wall-clock
          time instead of fixed iteration counts. A -9'd worker is respawned
          with exponential backoff, so on loaded smokers the replacement can
          lag; the fixed windows produced intermittent failures on the
          "request counter advanced" and "supervisor respawned a replacement
          worker" assertions.

0.02    2026-08-01
        - io_uring backend: assign sqe->user_data directly instead of calling
          io_uring_sqe_set_data64(), which only exists in liburing >= 2.2 and
          left the .so with an undefined symbol on hosts with older liburing.
        - Supervisor: close a signal lost-wakeup race. The pool loop now keeps
          TERM/INT/HUP/USR1/USR2/CHLD blocked, reaps with WNOHANG, and waits
          via sigsuspend(), so a USR1 stats dump (or any signal) arriving while
          the pool is idle is serviced immediately instead of stalling until a
          worker happened to exit. Fixes intermittent t/15-process.t failures.

0.01    2026-07-31
        First release.
        - Event-loop PSGI server: prefork supervisor + per-worker XS loop.
          Passes the full Plack::Test::Suite; runs any Plack app via
          `plackup -s Hyperman`.
        - Pluggable readiness backends behind one C vtable: kqueue
          (macOS/BSD), epoll (Linux), portable poll fallback, and an opt-in
          io_uring backend (raw syscalls, poll-mode ring). Selection is
          automatic; force with HYPERMAN_BACKEND.
        - Hyperman::Future: native XS array-slot Future-compatible async
          result. Handlers may return a Future (or any on_ready-compatible
          object, e.g. CPAN Future) of the PSGI response; get/await pump the
          worker's own loop re-entrantly.
        - Async PSGI: psgi.streaming/delayed responder with a streaming
          writer, psgix.io (dup'd client socket), psgix.loop, cancellation
          of the response Future on client disconnect, timer and io_ready
          Futures on the loop.
        - Production: worker respawn with crash-loop throttle, SIGHUP
          zero-downtime worker recycle, graceful TERM/INT drain, idle and
          slow-request timeouts, pipelining fairness cap, request size
          ceiling with 413, opt-in SO_REUSEPORT per-worker listeners
          (Linux), access_log callback, Hyperman->stats.
