v1.76.0 strengthens HTTP message framing, static-file isolation, and server connection handling. It also improves HTTP compatibility, avoids quadratic work when parsing large trailer sets, and adds configurable TCP keep-alives to TCPDialer.
Security fixes and hardening
- Multipart parsing consumes the entire declared body. Bytes after the closing multipart boundary are drained before the connection is reused, preventing a multipart epilogue from being interpreted as another request. #2336
- Outgoing messages preserve framing. Raw
Content-Lengthheaders are synchronized with the outgoing body, duplicate or conflicting framing headers are rejected, and fixed-length request and response streams cannot write beyond their declared size.FSandSendFilealso stop at the advertisedContent-Lengthwhen a file grows during serving. #2470, #2462 - Requests with both
Content-LengthandTransfer-Encodingclose the connection. The connection is no longer reused after a request containing both framing headers. #2476 - Virtual-host file serving preserves directory isolation.
NewVHostPathRewriteravoids decoding the host and path twice and handles invalid hosts safely, preventing crafted requests from escaping the virtual-host directory and accessing files directly underFS.Root. This affectsFShandlers usingNewVHostPathRewriter. #2487 - Rejected TLS connections no longer block the accept loop. Connections rejected by
MaxConnsPerIPorConcurrencyare closed without attempting an HTTP error response, which could otherwise trigger a TLS handshake and stall while waiting for aClientHello. IPv6 clients are now also counted againstMaxConnsPerIP. #2486, #2468
Performance improvements
- Trailer parsing avoids quadratic work. Large sets of trailer names use indexed membership checks instead of repeatedly scanning all previously recorded names. This reduces CPU amplification from crafted
Trailerheaders and chunked trailer sections while preserving trailer order. #2465
HTTP compatibility and features
- Static files handle range and conditional requests more accurately. Unsupported range units and multiple ranges are ignored instead of returning
416; invalid single byte ranges still return416.Rangeis also ignored whenIf-Rangedoes not match, and bothRangeandIf-Modified-Sinceare ignored for methods other than GET and HEAD. #2467, #2473, #2474, #2475 - Pipeline clients handle HEAD responses without reading a body.
PipelineClientskips the response body for HEAD requests. #2469 - HTTP parsing accepts valid legacy formats and matches tokens correctly.
ParseHTTPDateaccepts obsolete HTTP-date formats, cookies with an invalidExpiresattribute remain parseable,Expect: 100-continueis matched case-insensitively, andAccept-Encodingmatches complete tokens. Asterisk-form request targets are rejected for methods other than OPTIONS. #2466, #2481, #2482, #2479, #2463 - TCP keep-alives are configurable. The new
TCPDialer.KeepAlivefield sets the keep-alive period for connections created by the dialer; a negative value disables keep-alives. #2484
What's Changed
Security and hardening
- consume the rest of the declared body in readMultipartForm by @alhudz in #2336
- fix: validate outgoing request framing by @erikdubbelboer in #2470
- stop FS and SendFile at Content-Length when the file has grown by @alhudz in #2462
- fix: close the connection after a request with both Content-Length and Transfer-Encoding by @LuisFigueroaG in #2476
- fix(fs): preserve vhost isolation during path rewriting by @koyokr in #2487
- Fix TLS connection rejection blocking the accept loop by @erikdubbelboer in #2486
- count IPv6 clients against MaxConnsPerIP by @alhudz in #2468
Performance
Features
Fixes
- fix(header): reject asterisk-form request-target for non-OPTIONS methods by @aadieng100 in #2463
- fix: accept the obsolete HTTP-date formats in ParseHTTPDate by @LuisFigueroaG in #2466
- fix(fs): ignore Range headers FS can't serve instead of replying 416 by @LuisFigueroaG in #2467
- skip the response body for HEAD requests in PipelineClient by @alhudz in #2469
- fix(fs): ignore If-Modified-Since for methods other than GET and HEAD by @LuisFigueroaG in #2474
- fix(fs): ignore Range when the If-Range validator doesn't match by @LuisFigueroaG in #2473
- fix(fs): ignore Range for methods other than GET and HEAD by @LuisFigueroaG in #2475
- fix(cookie): ignore an invalid Expires attribute instead of failing to parse by @LuisFigueroaG in #2481
- fix: match the Expect: 100-continue value case-insensitively by @LuisFigueroaG in #2482
- fix: match complete Accept-Encoding tokens by @jabrailkhalil in #2479
Documentation
Dependency updates
- chore(deps): bump github.com/molecule-man/go-brrr from 1.1.1 to 1.2.0 by @dependabot[bot] in #2464
- chore(deps): bump github.com/molecule-man/go-brrr from 1.2.0 to 1.3.0 by @dependabot[bot] in #2483
- chore(deps): bump golang.org/x/sys from 0.48.0 to 0.49.0 by @dependabot[bot] in #2477
- chore(deps): bump golang.org/x/net from 0.59.0 to 0.61.0 by @dependabot[bot] in #2478
- chore(deps): bump github.com/klauspost/compress from 1.20.1 to 1.20.3 by @dependabot[bot] in #2485
New Contributors
- @aadieng100 made their first contribution in #2463
- @LuisFigueroaG made their first contribution in #2466
- @oozan made their first contribution in #2355
- @jabrailkhalil made their first contribution in #2479
- @koyokr made their first contribution in #2487
Full Changelog: v1.75.0...v1.76.0