Upgrade net package from Go 1.21.4 to Go 1.26.2

Backport upstream Go standard library changes to TinyGo's net package.

Unmodified files (18) replaced directly from Go 1.26.2 source.
Modified files (22) merged via 3-way diff, preserving all TinyGo
netdev adaptations, TINYGO markers, and embedded-device constraints.

Notable upstream changes included:
- net/http: cookie handling improvements, fs enhancements,
  reverse proxy updates, chunked encoding fixes
- net/http: ServeMux routing and pattern matching updates
- net: IP parsing and MAC address handling improvements
- Various doc link syntax modernization (e.g. [Dial], [Buffers])

TinyGo-only files (netdev.go, tlssock.go) unchanged.
All TINYGO comment markers preserved.
This commit is contained in:
deadprogram
2026-04-13 17:36:35 +02:00
committed by Ron Evans
parent a3417d034f
commit 35e809e0e5
46 changed files with 1580 additions and 779 deletions
+185 -48
View File
@@ -1,6 +1,4 @@
// TINYGO: The following is copied from Go 1.21.5 official implementation.
// Copyright 2011 The Go Authors. All rights reserved.
// TINYGO: The following is copied from Go 1.26.2 official implementation.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
@@ -23,12 +21,13 @@ import (
"net/url"
"strings"
"sync"
"sync/atomic"
"time"
"golang.org/x/net/http/httpguts"
)
// A ProxyRequest contains a request to be rewritten by a ReverseProxy.
// A ProxyRequest contains a request to be rewritten by a [ReverseProxy].
type ProxyRequest struct {
// In is the request received by the proxy.
// The Rewrite function must not modify In.
@@ -44,10 +43,12 @@ type ProxyRequest struct {
// SetURL routes the outbound request to the scheme, host, and base path
// provided in target. If the target's path is "/base" and the incoming
// request was for "/dir", the target request will be for "/base/dir".
// To route requests without joining the incoming path,
// set r.Out.URL directly.
//
// SetURL rewrites the outbound Host header to match the target's host.
// To preserve the inbound request's Host header (the default behavior
// of NewSingleHostReverseProxy):
// of [NewSingleHostReverseProxy]):
//
// rewriteFunc := func(r *httputil.ProxyRequest) {
// r.SetURL(url)
@@ -70,7 +71,7 @@ func (r *ProxyRequest) SetURL(target *url.URL) {
// If the outbound request contains an existing X-Forwarded-For header,
// SetXForwarded appends the client IP address to it. To append to the
// inbound request's X-Forwarded-For header (the default behavior of
// ReverseProxy when using a Director function), copy the header
// [ReverseProxy] when using a Director function), copy the header
// from the inbound request before calling SetXForwarded:
//
// rewriteFunc := func(r *httputil.ProxyRequest) {
@@ -102,6 +103,13 @@ func (r *ProxyRequest) SetXForwarded() {
//
// 1xx responses are forwarded to the client if the underlying
// transport supports ClientTrace.Got1xxResponse.
//
// Hop-by-hop headers (see RFC 9110, section 7.6.1), including
// Connection, Proxy-Connection, Keep-Alive, Proxy-Authenticate,
// Proxy-Authorization, TE, Trailer, Transfer-Encoding, and Upgrade,
// are removed from client requests and backend responses.
// The Rewrite function may be used to add hop-by-hop headers to the request,
// and the ModifyResponse function may be used to remove them from the response.
type ReverseProxy struct {
// Rewrite must be a function which modifies
// the request into a new request to be sent
@@ -126,36 +134,6 @@ type ReverseProxy struct {
// At most one of Rewrite or Director may be set.
Rewrite func(*ProxyRequest)
// Director is a function which modifies
// the request into a new request to be sent
// using Transport. Its response is then copied
// back to the original client unmodified.
// Director must not access the provided Request
// after returning.
//
// By default, the X-Forwarded-For header is set to the
// value of the client IP address. If an X-Forwarded-For
// header already exists, the client IP is appended to the
// existing values. As a special case, if the header
// exists in the Request.Header map but has a nil value
// (such as when set by the Director func), the X-Forwarded-For
// header is not modified.
//
// To prevent IP spoofing, be sure to delete any pre-existing
// X-Forwarded-For header coming from the client or
// an untrusted proxy.
//
// Hop-by-hop headers are removed from the request after
// Director returns, which can remove headers added by
// Director. Use a Rewrite function instead to ensure
// modifications to the request are preserved.
//
// Unparsable query parameters are removed from the outbound
// request if Request.Form is set after Director returns.
//
// At most one of Rewrite or Director may be set.
Director func(*http.Request)
// The transport used to perform proxy requests.
// If nil, http.DefaultTransport is used.
Transport http.RoundTripper
@@ -188,6 +166,10 @@ type ReverseProxy struct {
// If the backend is unreachable, the optional ErrorHandler is
// called without any call to ModifyResponse.
//
// Hop-by-hop headers are removed from the response before
// calling ModifyResponse. ModifyResponse may need to remove
// additional headers to fit its deployment model, such as Alt-Svc.
//
// If ModifyResponse returns an error, ErrorHandler is called
// with its error value. If ErrorHandler is nil, its default
// implementation is used.
@@ -199,10 +181,92 @@ type ReverseProxy struct {
// If nil, the default is to log the provided error and return
// a 502 Status Bad Gateway response.
ErrorHandler func(http.ResponseWriter, *http.Request, error)
// Director is deprecated. Use Rewrite instead.
//
// This function is insecure:
//
// - Hop-by-hop headers are removed from the request after Director
// returns, which can remove headers added by Director.
// A client can designate headers as hop-by-hop by listing them
// in the Connection header, so this permits a malicious client
// to remove any headers that may be added by Director.
//
// - X-Forwarded-For, X-Forwarded-Host, and X-Forwarded-Proto
// headers in inbound requests are preserved by default,
// which can permit IP spoofing if the Director function is
// not careful to remove these headers.
//
// Rewrite addresses these issues.
//
// As an example of converting a Director function to Rewrite:
//
// // ReverseProxy with a Director function.
// proxy := &httputil.ReverseProxy{
// Director: func(req *http.Request) {
// req.URL.Scheme = "https"
// req.URL.Host = proxyHost
//
// // A malicious client can remove this header.
// req.Header.Set("Some-Header", "some-header-value")
//
// // X-Forwarded-* headers sent by the client are preserved,
// // since Director did not remove them.
// },
// }
//
// // ReverseProxy with a Rewrite function.
// proxy := &httputil.ReverseProxy{
// Rewrite: func(preq *httputil.ProxyRequest) {
// // See also ProxyRequest.SetURL.
// preq.Out.URL.Scheme = "https"
// preq.Out.URL.Host = proxyHost
//
// // This header cannot be affected by a malicious client.
// preq.Out.Header.Set("Some-Header", "some-header-value")
//
// // X-Forwarded- headers sent by the client have been
// // removed from preq.Out.
// // ProxyRequest.SetXForwarded optionally adds new ones.
// preq.SetXForwarded()
// },
// }
//
// Director is a function which modifies
// the request into a new request to be sent
// using Transport. Its response is then copied
// back to the original client unmodified.
// Director must not access the provided Request
// after returning.
//
// By default, the X-Forwarded-For header is set to the
// value of the client IP address. If an X-Forwarded-For
// header already exists, the client IP is appended to the
// existing values. As a special case, if the header
// exists in the Request.Header map but has a nil value
// (such as when set by the Director func), the X-Forwarded-For
// header is not modified.
//
// To prevent IP spoofing, be sure to delete any pre-existing
// X-Forwarded-For header coming from the client or
// an untrusted proxy.
//
// Hop-by-hop headers are removed from the request after
// Director returns, which can remove headers added by
// Director. Use a Rewrite function instead to ensure
// modifications to the request are preserved.
//
// Unparsable query parameters are removed from the outbound
// request if Request.Form is set after Director returns.
//
// At most one of Rewrite or Director may be set.
//
// Deprecated: Use Rewrite instead.
Director func(*http.Request)
}
// A BufferPool is an interface for getting and returning temporary
// byte slices for use by io.CopyBuffer.
// byte slices for use by [io.CopyBuffer].
type BufferPool interface {
Get() []byte
Put([]byte)
@@ -241,13 +305,17 @@ func joinURLPath(a, b *url.URL) (path, rawpath string) {
return a.Path + b.Path, apath + bpath
}
// NewSingleHostReverseProxy returns a new ReverseProxy that routes
// NewSingleHostReverseProxy returns a new [ReverseProxy] that routes
// URLs to the scheme, host, and base path provided in target. If the
// target's path is "/base" and the incoming request was for "/dir",
// the target request will be for /base/dir.
//
// NewSingleHostReverseProxy does not rewrite the Host header.
//
// For backwards compatibility reasons, NewSingleHostReverseProxy
// returns a ReverseProxy using the deprecated Director function.
// This proxy preserves X-Forwarded-* headers sent by the client.
//
// To customize the ReverseProxy behavior beyond what
// NewSingleHostReverseProxy provides, use ReverseProxy directly
// with a Rewrite function. The ProxyRequest SetURL method
@@ -368,6 +436,18 @@ func (p *ReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
outreq.Body = nil // Issue 16036: nil Body for http.Transport retries
}
if outreq.Body != nil {
// Wrap the body in a reader where Close does nothing. This is done
// because p.Transport.RoundTrip would close the reverse proxy's
// outbound request body if it fails to connect to upstream. If we do
// not wrap the body, when we close the reverse proxy's outbound
// request, it will also close the reverse proxy's inbound request body
// (i.e. the client's outbound request body). This is because
// http.(*Request).Clone creates a shallow copy of the body. This can
// cause an infinite hang in cases where the body is not yet received
// from the client (e.g. 100-continue requests): Close, which
// internally tries to consume the body content, would be called too
// early and would hang.
outreq.Body = &noopCloseReader{readCloser: outreq.Body}
// Reading from the request body after returning from a handler is not
// allowed, and the RoundTrip goroutine that reads the Body can outlive
// this handler. This can lead to a crash if the handler panics (see
@@ -456,23 +536,34 @@ func (p *ReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
outreq.Header.Set("User-Agent", "")
}
var (
roundTripMutex sync.Mutex
roundTripDone bool
)
trace := &httptrace.ClientTrace{
Got1xxResponse: func(code int, header textproto.MIMEHeader) error {
roundTripMutex.Lock()
defer roundTripMutex.Unlock()
if roundTripDone {
// If RoundTrip has returned, don't try to further modify
// the ResponseWriter's header map.
return nil
}
h := rw.Header()
copyHeader(h, http.Header(header))
rw.WriteHeader(code)
// Clear headers, it's not automatically done by ResponseWriter.WriteHeader() for 1xx responses
for k := range h {
delete(h, k)
}
clear(h)
return nil
},
}
outreq = outreq.WithContext(httptrace.WithClientTrace(outreq.Context(), trace))
res, err := transport.RoundTrip(outreq)
roundTripMutex.Lock()
roundTripDone = true
roundTripMutex.Unlock()
if err != nil {
p.getErrorHandler()(rw, outreq, err)
return
@@ -568,7 +659,7 @@ func shouldPanicOnCopyError(req *http.Request) bool {
func removeHopByHopHeaders(h http.Header) {
// RFC 7230, section 6.1: Remove headers listed in the "Connection" header.
for _, f := range h["Connection"] {
for _, sf := range strings.Split(f, ",") {
for sf := range strings.SplitSeq(f, ",") {
if sf = textproto.TrimString(sf); sf != "" {
h.Del(sf)
}
@@ -730,6 +821,7 @@ func (p *ReverseProxy) handleUpgradeResponse(rw http.ResponseWriter, req *http.R
resUpType := upgradeType(res.Header)
if !ascii.IsPrint(resUpType) { // We know reqUpType is ASCII, it's checked by the caller.
p.getErrorHandler()(rw, req, fmt.Errorf("backend tried to switch to invalid protocol %q", resUpType))
return
}
if !ascii.EqualFold(reqUpType, resUpType) {
p.getErrorHandler()(rw, req, fmt.Errorf("backend tried to switch protocol %q when %q was requested", resUpType, reqUpType))
@@ -783,9 +875,17 @@ func (p *ReverseProxy) handleUpgradeResponse(rw http.ResponseWriter, req *http.R
spc := switchProtocolCopier{user: conn, backend: backConn}
go spc.copyToBackend(errc)
go spc.copyFromBackend(errc)
<-errc
// Wait until both copy functions have sent on the error channel,
// or until one fails.
err := <-errc
if err == nil {
err = <-errc
}
}
var errCopyDone = errors.New("hijacked connection copy complete")
// switchProtocolCopier exists so goroutines proxying data back and
// forth have nice names in stacks.
type switchProtocolCopier struct {
@@ -793,13 +893,33 @@ type switchProtocolCopier struct {
}
func (c switchProtocolCopier) copyFromBackend(errc chan<- error) {
_, err := io.Copy(c.user, c.backend)
errc <- err
if _, err := io.Copy(c.user, c.backend); err != nil {
errc <- err
return
}
// backend conn has reached EOF so propogate close write to user conn
if wc, ok := c.user.(interface{ CloseWrite() error }); ok {
errc <- wc.CloseWrite()
return
}
errc <- errCopyDone
}
func (c switchProtocolCopier) copyToBackend(errc chan<- error) {
_, err := io.Copy(c.backend, c.user)
errc <- err
if _, err := io.Copy(c.backend, c.user); err != nil {
errc <- err
return
}
// user conn has reached EOF so propogate close write to backend conn
if wc, ok := c.backend.(interface{ CloseWrite() error }); ok {
errc <- wc.CloseWrite()
return
}
errc <- errCopyDone
}
func cleanQueryParams(s string) string {
@@ -834,3 +954,20 @@ func ishex(c byte) bool {
}
return false
}
type noopCloseReader struct {
readCloser io.ReadCloser
closed atomic.Bool
}
func (ncr *noopCloseReader) Close() error {
ncr.closed.Store(true)
return nil
}
func (ncr *noopCloseReader) Read(p []byte) (int, error) {
if ncr.closed.Load() {
return 0, errors.New("ReverseProxy does an invalid Read on closed Body")
}
return ncr.readCloser.Read(p)
}