Revision history for Hyperman

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.
