Repository navigation
Added
-
BREAKING (library API).
path_type = "REGEX_PATH"(CLI--path-regex-path,PathRuleKind::REGEX_PATH = 3) matches a regex, anchored at both ends, against the request path up to its first?only, the Gateway API HTTPRouteRegularExpressionpath match.REGEXkeeps matching the path together with its query string, so/api/.+matches/api/?x=1underREGEXbut notREGEX_PATH, and^/api/v2$matches/api/v2?x=1only underREGEX_PATH. It ranks asREGEXdoes. Withrewrite_path, its$PATH[n]captures come from the path alone and the original?queryis appended to a rewritten path that has no?of its own; a redirectLocationfollows it.REGEX,PREFIXandEQUALSare unchanged. Library API:PathRule::from_cli_options(sozu-command-lib) takes a fourthpath_regex_pathargument, andPathRule(sozu-lib) andPathRuleType(sozu-command-lib) gain aRegexPathvariant, which breaks exhaustive matches on them. A worker that predates the kind refuses such a frontend as an invalid path rule, so upgrade every worker before declaring one (#1909). -
An HTTP listener can serve cleartext HTTP/2 by prior knowledge with the new opt-in
h2c_prior_knowledgeoption (HttpListenerConfigfield 38,UpdateHttpListenerConfigfield 44; TOMLh2c_prior_knowledge = true; CLIsozu listener http add --h2c-prior-knowledge,sozu listener http update --h2c-prior-knowledge/--no-h2c-prior-knowledge). A connection whose first 24 bytes are the HTTP/2 connection preface is served as an HTTP/2 frontend, as an HTTPS connection that negotiatedh2through ALPN is, with the listener's H2 flood thresholds and tuning; every other connection stays HTTP/1.x. The first bytes are peeked, never consumed: a strict prefix of the preface waits for more bytes withinrequest_timeout, and a diverging byte decides HTTP/1.x. A client that sends nothing, or only part of the preface, beforerequest_timeoutis closed without the408an HTTP/1-only listener answers. The scheme stayshttp(:scheme,X-Forwarded-Proto,Forwarded: proto), the option composes withexpect_proxy, andUpgrade: h2cremains unsupported. Such a listener is held to the H2 minimumbuffer_size(16393) at configuration load, like an HTTPS listener advertisingh2. Off by default: without it a preface is answered400 Bad Requestas before. The use case is cleartext gRPC, e.g. the Kubernetes Gateway APIGRPCRoute. New metricshttp.h2c.prior_knowledge(connections served as h2c) andhttp.upgrade.h2c.failed. A listener update applies to new connections only (#1911).
Fixed
-
A worker now leaves its event loop when its command channel reports an error, as it already did on a hang-up. The main process closing the channel with a worker answer still unread resets the connection, which the worker used to log and ignore, so it kept running with nothing left to drive it and a caller joining its thread waited forever (#1912).
-
An HTTP/1.1 backend now receives a single
Hostheader when a frontend setsrewrite_host. The router added aHostheader block on top of the rewritten authority, which the HTTP/1.1 converter already writes asHost, so the backend saw two, and servers that refuse a duplicateHost(Go'snet/http) answered400. A frontend request header namedHostnow replaces the request authority instead of adding a second, differentHostline;rewrite_hoststill takes precedence over it, and an HTTP/2 backend now receives that value as:authoritywhere the header was previously dropped (#1908). -
A fast HTTP transfer is no longer cut at a random offset. When the inner loop of
Mux::ready_innerreachedMAX_LOOP_ITERATIONS(10,000) in oneready()call, it closed the session unless output was still queued, even if every iteration had forwarded bytes. When the client and the backend both kept up for that many iterations (about 160 MiB with 16 KiB buffers), the client saw a clean close in the middle of aContent-Lengthbody ("Premature end of Content-Length delimited message body"), withhttp.infinite_loop.errorincremented and, before 2.3.0, no log at the default level. Downloads and uploads over plaintext and TLS HTTP/1.1 were measured exposed, and the listener's ALPN list played no part; HTTP/2 frontends run the same loop. A call that reaches the budget after forwarding at least one stream byte per budgeted iteration to either peer (Context::stream_bytes_out) now yields: it keeps its readiness, counts the newhttp.mux.loop_budget_yieldcounter, and the worker resumes it after its next poll, which does not block while a session is queued (ProxySession::yielded,Server::resume_yielded_sessions). A fast transfer neither is cut nor holds the single-threaded worker past one budget per turn. A call that forwarded nothing keeps the existinghttp.infinite_loop.errorpath. Reproduced deterministically bya_transfer_progressing_at_the_loop_limit_yields_and_resumes_without_a_new_event, and end to end inlarge_response_tests.rs(256 MiB over loopback: 4/40 plaintext and 5/60 TLS downloads, 10/20 TLS uploads cut onmain).ProxySessiongains ayieldedmethod with a defaultfalse, so other implementors are unaffected (#1906). -
A response in progress is no longer cut by the timeout of the peer that is not holding it up.
back_timeoutfired while Sōzu itself had stopped reading the backend because a slow client left response bytes queued, and cut the response (backend_response_timeout);front_timeoutfired while a client that had taken every byte waited on a slow backend, and closed the session silently, because kawa'sis_completed(nothing queued) was read as "response fully proxied". Each timeout now defers to the other while the response waits on the other peer (response_waits_on_client); the two conditions are complementary, so a client that stops reading still meetsfront_timeoutand a silent backend still meetsback_timeout. Reproduced end to end with 4 s pauses against 2 s timeouts, cut at 4.0 and 6.0 of 8 MiB before the fix (#1906).
Upgrade notes
- The command schema gains
PathRuleKind::REGEX_PATH = 3and the optionalh2c_prior_knowledgefields (HttpListenerConfigfield 38,UpdateHttpListenerConfigfield 44). Configuration and saved state written by 2.3.3 load unchanged. Upgrade the main process and every worker before declaring aREGEX_PATHfrontend or enablingh2c_prior_knowledge: a worker that predates them refuses aREGEX_PATHfrontend as an invalid path rule, and ignores the unknownh2c_prior_knowledgefield, so it keeps answering a preface400 Bad Request. A main-only replacement does not qualify the transition. The release gate exercises the published 2.3.3 binary with two workers, saved state and HTTP/HTTPS/TCP/UDP traffic before, during and after replacement. Existing 2.2.1 and 2.3.1 compatibility cases remain separate. - Library API:
PathRule::from_cli_options(sozu-command-lib) takes a fourth argument, andPathRule(sozu-lib) andPathRuleType(sozu-command-lib) gain aRegexPathvariant.ProxySession(sozu-lib) gains ayieldedmethod whose default returnsfalse, so other implementors are unaffected.