Changelog
View Source0.1.7
Added
#whitecap_req{}carries areceivedfield:os:system_time()in native units, stamped when the first byte of the request arrived. A handler with a deadline (e.g. an RTBtmax) can measure the time already spent on the wire and shed accordingly.handler_timeoutconfig (defaultinfinity). A finite value runs the handler in a monitored process and, if it overruns, kills it and answers504, emitting a[whitecap, handler, timeout]telemetry event.infinitykeeps the handler inline with no per-request process — the right choice when the handler enforces its own deadline.
Changed
- The single
receive_timeoutis split intokeepalive_timeout(idle wait for the next request on a keep-alive connection) andrequest_timeout(per-read wait once a request has started arriving). This allows a short in-request deadline for slowloris protection without dropping healthy idle keep-alive connections.receive_timeout, if set, remains the back-compat default for both; all three still default toinfinity, so behaviour is unchanged unless a finiterequest_timeoutis configured. - A body with a known
Content-Lengthis now read with an exact-lengthgen_tcp:recv, so it lands in one read and one concatenation instead of repeatedly re-buffering partial reads (previously O(n²) on a body fragmented across TCP segments). - Per-connection config (
handler_timeout,keepalive_timeout,max_keepalive,request_timeout) is read once when the connection starts rather than on every request/read.
0.1.6
Changed
- Connection workers now proactively garbage-collect after sending a
response when their heap grows past ~64K words (half of ERTS's
ERTS_POTENTIALLY_LONG_GC_HSIZE). Above that threshold ERTS schedules a process's collection on a dirty CPU scheduler; with thousands of connections that offloading could starve the dirty schedulers, so the collection is forced inline instead.
0.1.5
Fixed
- A non-integer, empty, or negative
Content-Lengthno longer crashes the connection worker.binary_to_integer/1threwbadargon a non-integer value and a negative length caused a later badmatch; both propagated to the acceptor and, repeated within the supervisor restart window, took the whole application down. Such values now return400 Bad Request. - A crashing connection worker can no longer take the acceptor (and
its sibling connections) down with it. Workers are spawned unlinked
with
proc_lib:spawn/3and own their socket viagen_tcp:controlling_process/2, so an abnormal exit closes only that worker's socket. whitecap_protocol:headers/1returns{error, invalid_headers}for a header value that violates the single-space rule instead of raisingcase_clause.- The keep-alive limit served
max_keepalive + 1requests; it now serves exactlymax_keepalive. The forcedConnection: closealso overwrites a handler-setConnectionheader of any casing or iodata form instead of emitting a duplicate.
Changed
bin_patternsis stored infoilrather thanpersistent_term, the same mechanism as the rest of the config. foil's compiled-module lookup is faster and carries the compiled patterns through its constant pool.whitecap:events/0and the README telemetry table now list the[whitecap, connections, send_error]event, which has been emitted since 0.1.4.
0.1.4
Changed
- Listen sockets now set
sndbuf=256KB,send_timeout=50ms, andsend_timeout_close=trueso accepted sockets inherit them. The default kernel sndbuf was too small for multi-segment writes under tight RTB timing, where ~20KB responses could be silently dropped.
Fixed
gen_tcp:sendreturn values are now checked. On error the connection is closed instead of looping back intoparse_requestsagainst a dead socket, and a[whitecap, connections, send_error]telemetry event is emitted with the response size and reason.
0.1.3
Added
whitecap:events/0— returns the list oftelemetry:event_name()values whitecap emits. Consumers can iterate this list to attach handlers programmatically instead of hardcoding event names that drift across releases:[telemetry:attach(handler_id_for(E), E, fun handler/4, []) || E <- whitecap:events()].README gained a "Pipelining and backpressure" section. The per-connection request loop has no concurrent in-flight cap and relies on TCP send buffer pushback to throttle a slow client, which is appropriate for HTTP/1.1 pipelining semantics. The section spells out the contract so callers don't reach for the wrong tool.
0.1.2
Fixed
whitecap:start_listeners/2is now safe to call more than once per node. Listener processes are registered under names that include the configured port, so a second call with a differentportno longer collides onwhitecap_listener_<N>with the first call. Before this fix, the secondstart_listenerscall always failed with{error, {already_started, _}}as soon as its countdown reached anNvalue already used by the first call (e.g. one listener on metrics port 9091 plus sixteen on the main port would crash onwhitecap_listener_1).
0.1.1
Dep bumps + CI bootstrap. No source changes.
Changed
foilbumped from0.1.3to0.1.4(tightererror/0type; internal DRY refactor; behaviour unchanged).metalbumped from0.1.1to0.1.2(infrastructure refresh).telemetrybumped from1.2.1to1.4.2.- Test profile:
buoyswitched from git ref0.2.4to the hex release0.2.6-- buoy itself bumped through to 0.2.6 with the same dep-modernization pass; the hex release transitively pulls inshackle 0.7.1(which replaced granderl with knot, fixing the OTP 27+ build break). - Test profile:
timinggit ref pinned frommasterto tag0.1.3. - Test profile:
fprofxmoved fromransomr/fprofxtolpgauth/fprofx(otp_19branch).
Added
- GitHub Actions CI workflow. Matrix covers OTP 25, 26, 27, 28. (whitecap shipped 0.1.0 without any CI configured.)
Removed
edocprofile (was still configured to useedown; whitecap already usesrebar3_ex_docfor hex docs, so the edown path was unreachable).
0.1.0
Initial release.
Features
- HTTP/1.1 GET, HEAD, POST, PUT.
- Multi-acceptor model using
SO_REUSEPORTon Linux and Darwin. - Configurable handler module via
whitecap:start_listeners/1,2. - Keep-alive with a configurable per-connection request cap (
max_keepalive). - Telemetry events under
[whitecap, connections, ...].
Notes
[whitecap, connections, stats]reportsdurationin microseconds (not seconds).- See README "Non-goals" for deliberate departures from RFC 7230.