Per RFC 9110 section 15.4.4, a 303 (See Other) response must be followed
by a body-less GET regardless of the original method. Handle 303
explicitly: change any non-GET/HEAD method to GET and always drop the
body, including when the original request was already a GET/HEAD.
Clear every header that frames the dropped body: Content-Length and
Content-Type, plus Transfer-Encoding and Trailer (per RFC 9112 a body is
signaled by Content-Length or Transfer-Encoding, and a Trailer only
applies to a chunked body). 301/302 keep their POST-to-GET behavior, and
307/308 still preserve the method and body.
Fixes#2240
Signed-off-by: Y.Horie <u5.horie@gmail.com>
Clear the response body stream when the client streaming wrapper is closed directly via CloseWithError. This prevents ReleaseResponse from closing the same underlying requestStream again.
compressFileNolock created a ".tmp" file but left it on disk when the
compression copy, os.Chtimes, or os.Rename step failed, leaking partial
files that accumulate over time. Remove the temporary file in each error
path.
Fixes#2239
Signed-off-by: Y.Horie <u5.horie@gmail.com>
RequestCtx.FormFile previously returned (nil, nil) when the parsed
multipart form had a nil File map: the code returned the err variable
from the preceding successful MultipartForm() call, which is always
nil at that point. Callers checking `if err != nil` proceeded with a
nil *multipart.FileHeader, causing nil pointer dereferences downstream.
Return ErrMissingFile instead, matching the existing semantics for the
missing-key case immediately below. Fixes#2235.
Signed-off-by: Y.Horie <u5.horie@gmail.com>
* feat(prefork): add WatchMaster and callback support for child process management
* feat(prefork): add CommandProducer for customizable child process commands
* refactor(prefork): improve comments and parameter order in ListenAndServeTLS
* refactor(prefork): enhance logging message and clarify OnChildRecover callback comment
* fix(prefork): add Windows support to watchMaster
On Windows, os.Getppid() returns a static PID that doesn't change when
the parent exits (no reparenting). Use FindProcess+Wait instead, which
correctly detects parent exit. Also document why masterPID comparison
works for Docker containers (master PID 1 case).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(prefork): extract listenAsChild to eliminate DRY violation
The three ListenAndServe* methods had identical child setup code
(listen, set ln, watch master). Extract to listenAsChild() for
cleaner code. Also add comment for the magic file descriptor number 3.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(prefork): restore upstream ListenAndServeTLS parameter order
Keep upstream's (addr, certKey, certFile) signature to avoid breaking
callers. Fix the doc comment to match the actual parameter order instead.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(prefork): address lint errors and review feedback
Lint fixes:
- Remove unused Reuseport field write in test (govet/unusedwrite)
- Replace fmt.Errorf with errors.New for static errors (perfsprint)
Review feedback (Copilot):
- Validate CommandProducer returns a started command (nil/Process check)
- Clarify ListenAndServeTLS doc: parameter order and internal forwarding
- Use hermetic test binary re-exec instead of external 'go' binary
- Rename misleading test to reflect what it actually asserts
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(prefork): address maintainer review feedback
- watchMaster: log errors from FindProcess/Wait instead of swallowing
- watchMaster: don't call OnMasterDeath if FindProcess fails
- OnChildRecover: change signature to func(pid int), drop unused error return
- OnChildSpawn: add comment clarifying deferred cleanup handles the child
- CommandProducer: improve docs describing contract and use cases
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(prefork): address erikdubbelboer review feedback
- OnChildRecover: signature changed to func(oldPid, newPid int) so
callers can track which process was replaced
- OnChildSpawn: also called for recovered children (a recovered child
is still a spawned child)
- watchMaster: call OnMasterDeath when FindProcess fails (process is
most likely gone)
- CommandProducer: document that FASTHTTP_PREFORK_CHILD=1 must be set
in the child env, and what the default does when nil
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(prefork): avoid zombie processes and replace shallow tests
- Move Wait() goroutine before OnChildSpawn so Kill()+Wait() works
correctly if a callback fails and the deferred cleanup runs
- Add Wait() call in deferred cleanup after Kill() to reap children
- Same fix in recovery loop
- Remove shallow callback tests that only tested Go compiler
- Add Test_Prefork_Lifecycle: runs full prefork with CommandProducer,
verifies callbacks fire in correct order with correct arguments
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(prefork): ensure recovery default stays positive
* test(prefork): isolate lifecycle tests
* fix(prefork): tighten recovery callback flow
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When `StreamResponseBody` is set the user is supposed to limit the reads
they are doing themselves.
Limiting the reads to `MaxResponseBodySize` would break backwards
compatibility.
Match net/http behavior when requests or responses contain both
Content-Length and Transfer-Encoding.
Parse and validate Content-Length even when Transfer-Encoding is present, so
invalid lengths are rejected. For valid Content-Length with chunked
Transfer-Encoding, keep chunked framing as authoritative. Also apply the same
precedence when RequestHeader.DisableSpecialHeader is used.
Reject HTTP/1.1 response Transfer-Encoding values unless they are a single
chunked header, matching net/http's strict transfer parser behavior.
This prevents arbitrary or compound response Transfer-Encoding values from
being silently normalized to chunked and avoids desync/body parsing ambiguity
when parsing upstream responses.
Reject space and tab between the chunk-size and chunk-extension separator while
preserving net/http-compatible trailing OWS before CRLF.
This avoids parser divergence for chunk lines such as "3 ;ext\r\n" without
breaking the existing acceptance of padded chunk-size lines like "3 \r\n".
Add parser regression coverage for both accepted and rejected forms.
* feat: add ExpectHandler for richer Expect: 100-continue handling
ContinueHandler only returns a bool, limiting the server to either
accepting (100) or rejecting with 417. ExpectHandler allows returning
any HTTP status code, and closes the connection on rejection since
the client may have already started sending body data per RFC 9110.
ExpectHandler takes precedence when both handlers are set.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor: use *RequestCtx in ExpectHandler for richer access
Allows callers to inspect RemoteAddr, TLS state, or any other
connection metadata alongside headers, addressing reviewer feedback.
Documents that the response must not be modified by the handler.
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
* Update server.go
Co-authored-by: Erik Dubbelboer <erik@dubbelboer.com>
* Update server.go
Co-authored-by: Erik Dubbelboer <erik@dubbelboer.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Erik Dubbelboer <erik@dubbelboer.com>
Prevent cookie APIs from serializing embedded CR or LF bytes into
Cookie and Set-Cookie header lines.
Route Cookie key, value, domain, and path setters, parsed cookie
fields, and RequestHeader/ResponseHeader SetCookie paths through the
existing newline sanitization. Sanitize paths after normalization so
percent-decoded CR/LF bytes cannot bypass the guard.
Thanks to @vnykmshr for reporting this issue.
When KeepHijackedConns is enabled, the hijacked connection may outlive the
HijackHandler. The wrapper continues reading through the buffered reader
after the handler returns, so returning that reader to the pool can let
another connection reset it while the hijacked connection is still in use.
Keep the buffered reader owned by the escaped hijacked connection in
keep-open mode. Add a regression test that forces reader-pool reuse
and verifies buffered data remains available after the handler returns.
Route RequestCtx.Redirect Location updates through the canonical response
header setter so CR and LF bytes are normalized before serialization.
Add regression coverage for query-only and fragment-only redirects containing
CRLF, and verify the serialized response cannot emit an injected header line.
Reject request header field names with whitespace immediately before the
colon instead of trimming them before special-header handling.
This prevents parser differentials for malformed framing and routing
headers such as Content-Length, Transfer-Encoding, and Host when a frontend
forwards raw invalid request headers.
Keep the existing response and trailer compatibility behavior unchanged, and
add regression coverage for both header-only parsing and full request body
reads.
Validate trailer names added through AddTrailerBytes before storing them
for Trailer header serialization.
Trim OWS around comma-separated trailer names, reject names containing
bytes outside the HTTP field-name token set, and keep the existing
forbidden-trailer filtering in place. This prevents CRLF injection through
dynamic trailer names while preserving valid trailer declarations.
Add request and response regression coverage for invalid trailer names and
tab-trimmed OWS.
On new connections with ReduceMemoryUsage enabled, serveConn could reach
acquireByteReader before installing a read deadline. That left the first
blocking read outside ReadTimeout and allowed silent clients to keep the
connection open until some external timeout closed it.
Apply ReadTimeout before the first read on a new connection, while keeping
the existing idle-timeout behavior for keep-alive requests. Add a regression
test that verifies the server closes a silent ReduceMemoryUsage connection
after the first-byte timeout.
Prevent request and response first-line setters from serializing
embedded CR or LF bytes into the start line.
Route SetMethod, SetRequestURI, SetProtocol, and SetStatusMessage
through the existing newline sanitization used by other header-value
setters. This preserves behavior for valid inputs while preventing
header injection through malformed first-line values.
Thanks to @vnykmshr for reporting this issue.
Reject dial target addresses containing CR or LF before building the
HTTP CONNECT request in httpProxyDial.
This prevents header injection through HTTP proxies when callers pass
unsanitized target addresses via low-level dial paths such as
HostClient.Addr or direct proxy dialer usage.
Reported by https://github.com/OLU-DEVX
Rewritten FS paths were only checked for the "/../" substring, which
allowed leading "../" values to bypass the traversal guard.
Reject any rewritten path containing a ".." path segment before joining
it with FS.Root. This closes the PathRewrite/NewPathPrefixStripper escape
in the default OS-backed handler and keeps rewritten paths within the
intended static root.
This vulnerability was discovered and reported by bugbunny.ai