mirror of
https://github.com/tinygo-org/net.git
synced 2026-08-12 15:33:40 +00:00
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:
@@ -1,6 +1,4 @@
|
||||
// TINYGO: The following is copied from Go 1.21.5 official implementation.
|
||||
|
||||
// Copyright 2009 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.
|
||||
|
||||
@@ -73,8 +71,8 @@ func outgoingLength(req *http.Request) int64 {
|
||||
return -1
|
||||
}
|
||||
|
||||
// DumpRequestOut is like DumpRequest but for outgoing client requests. It
|
||||
// includes any headers that the standard http.Transport adds, such as
|
||||
// DumpRequestOut is like [DumpRequest] but for outgoing client requests. It
|
||||
// includes any headers that the standard [http.Transport] adds, such as
|
||||
// User-Agent.
|
||||
func DumpRequestOut(req *http.Request, body bool) ([]byte, error) {
|
||||
save := req.Body
|
||||
@@ -149,7 +147,6 @@ func DumpRequestOut(req *http.Request, body bool) ([]byte, error) {
|
||||
|
||||
req.Body = save
|
||||
if err != nil {
|
||||
pw.Close()
|
||||
dr.err = err
|
||||
close(quitReadCh)
|
||||
return nil, err
|
||||
@@ -205,17 +202,17 @@ var reqWriteExcludeHeaderDump = map[string]bool{
|
||||
// representation. It should only be used by servers to debug client
|
||||
// requests. The returned representation is an approximation only;
|
||||
// some details of the initial request are lost while parsing it into
|
||||
// an http.Request. In particular, the order and case of header field
|
||||
// an [http.Request]. In particular, the order and case of header field
|
||||
// names are lost. The order of values in multi-valued headers is kept
|
||||
// intact. HTTP/2 requests are dumped in HTTP/1.x form, not in their
|
||||
// original binary representations.
|
||||
//
|
||||
// If body is true, DumpRequest also returns the body. To do so, it
|
||||
// consumes req.Body and then replaces it with a new io.ReadCloser
|
||||
// consumes req.Body and then replaces it with a new [io.ReadCloser]
|
||||
// that yields the same bytes. If DumpRequest returns an error,
|
||||
// the state of req is undefined.
|
||||
//
|
||||
// The documentation for http.Request.Write details which fields
|
||||
// The documentation for [http.Request.Write] details which fields
|
||||
// of req are included in the dump.
|
||||
func DumpRequest(req *http.Request, body bool) ([]byte, error) {
|
||||
var err error
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
// TINYGO: The following is copied from Go 1.21.5 official implementation.
|
||||
|
||||
// Copyright 2014 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.
|
||||
|
||||
@@ -15,7 +13,7 @@ import (
|
||||
|
||||
// NewChunkedReader returns a new chunkedReader that translates the data read from r
|
||||
// out of HTTP "chunked" format before returning it.
|
||||
// The chunkedReader returns io.EOF when the final 0-length chunk is read.
|
||||
// The chunkedReader returns [io.EOF] when the final 0-length chunk is read.
|
||||
//
|
||||
// NewChunkedReader is not needed by normal applications. The http package
|
||||
// automatically decodes chunking when reading response bodies.
|
||||
|
||||
+17
-19
@@ -1,6 +1,4 @@
|
||||
// TINYGO: The following is copied from Go 1.21.5 official implementation.
|
||||
|
||||
// Copyright 2009 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.
|
||||
|
||||
@@ -35,7 +33,7 @@ var errClosed = errors.New("i/o operation on closed connection")
|
||||
// It is low-level, old, and unused by Go's current HTTP stack.
|
||||
// We should have deleted it before Go 1.
|
||||
//
|
||||
// Deprecated: Use the Server in package net/http instead.
|
||||
// Deprecated: Use the Server in package [net/http] instead.
|
||||
type ServerConn struct {
|
||||
mu sync.Mutex // read-write protects the following fields
|
||||
c net.Conn
|
||||
@@ -52,7 +50,7 @@ type ServerConn struct {
|
||||
// It is low-level, old, and unused by Go's current HTTP stack.
|
||||
// We should have deleted it before Go 1.
|
||||
//
|
||||
// Deprecated: Use the Server in package net/http instead.
|
||||
// Deprecated: Use the Server in package [net/http] instead.
|
||||
func NewServerConn(c net.Conn, r *bufio.Reader) *ServerConn {
|
||||
if r == nil {
|
||||
r = bufio.NewReader(c)
|
||||
@@ -60,10 +58,10 @@ func NewServerConn(c net.Conn, r *bufio.Reader) *ServerConn {
|
||||
return &ServerConn{c: c, r: r, pipereq: make(map[*http.Request]uint)}
|
||||
}
|
||||
|
||||
// Hijack detaches the ServerConn and returns the underlying connection as well
|
||||
// Hijack detaches the [ServerConn] and returns the underlying connection as well
|
||||
// as the read-side bufio which may have some left over data. Hijack may be
|
||||
// called before Read has signaled the end of the keep-alive logic. The user
|
||||
// should not call Hijack while Read or Write is in progress.
|
||||
// should not call Hijack while [ServerConn.Read] or [ServerConn.Write] is in progress.
|
||||
func (sc *ServerConn) Hijack() (net.Conn, *bufio.Reader) {
|
||||
sc.mu.Lock()
|
||||
defer sc.mu.Unlock()
|
||||
@@ -74,7 +72,7 @@ func (sc *ServerConn) Hijack() (net.Conn, *bufio.Reader) {
|
||||
return c, r
|
||||
}
|
||||
|
||||
// Close calls Hijack and then also closes the underlying connection.
|
||||
// Close calls [ServerConn.Hijack] and then also closes the underlying connection.
|
||||
func (sc *ServerConn) Close() error {
|
||||
c, _ := sc.Hijack()
|
||||
if c != nil {
|
||||
@@ -83,7 +81,7 @@ func (sc *ServerConn) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Read returns the next request on the wire. An ErrPersistEOF is returned if
|
||||
// Read returns the next request on the wire. An [ErrPersistEOF] is returned if
|
||||
// it is gracefully determined that there are no more requests (e.g. after the
|
||||
// first request on an HTTP/1.0 connection, or after a Connection:close on a
|
||||
// HTTP/1.1 connection).
|
||||
@@ -173,7 +171,7 @@ func (sc *ServerConn) Pending() int {
|
||||
|
||||
// Write writes resp in response to req. To close the connection gracefully, set the
|
||||
// Response.Close field to true. Write should be considered operational until
|
||||
// it returns an error, regardless of any errors returned on the Read side.
|
||||
// it returns an error, regardless of any errors returned on the [ServerConn.Read] side.
|
||||
func (sc *ServerConn) Write(req *http.Request, resp *http.Response) error {
|
||||
|
||||
// Retrieve the pipeline ID of this request/response pair
|
||||
@@ -228,7 +226,7 @@ func (sc *ServerConn) Write(req *http.Request, resp *http.Response) error {
|
||||
// It is low-level, old, and unused by Go's current HTTP stack.
|
||||
// We should have deleted it before Go 1.
|
||||
//
|
||||
// Deprecated: Use Client or Transport in package net/http instead.
|
||||
// Deprecated: Use Client or Transport in package [net/http] instead.
|
||||
type ClientConn struct {
|
||||
mu sync.Mutex // read-write protects the following fields
|
||||
c net.Conn
|
||||
@@ -246,7 +244,7 @@ type ClientConn struct {
|
||||
// It is low-level, old, and unused by Go's current HTTP stack.
|
||||
// We should have deleted it before Go 1.
|
||||
//
|
||||
// Deprecated: Use the Client or Transport in package net/http instead.
|
||||
// Deprecated: Use the Client or Transport in package [net/http] instead.
|
||||
func NewClientConn(c net.Conn, r *bufio.Reader) *ClientConn {
|
||||
if r == nil {
|
||||
r = bufio.NewReader(c)
|
||||
@@ -263,17 +261,17 @@ func NewClientConn(c net.Conn, r *bufio.Reader) *ClientConn {
|
||||
// It is low-level, old, and unused by Go's current HTTP stack.
|
||||
// We should have deleted it before Go 1.
|
||||
//
|
||||
// Deprecated: Use the Client or Transport in package net/http instead.
|
||||
// Deprecated: Use the Client or Transport in package [net/http] instead.
|
||||
func NewProxyClientConn(c net.Conn, r *bufio.Reader) *ClientConn {
|
||||
cc := NewClientConn(c, r)
|
||||
cc.writeReq = (*http.Request).WriteProxy
|
||||
return cc
|
||||
}
|
||||
|
||||
// Hijack detaches the ClientConn and returns the underlying connection as well
|
||||
// Hijack detaches the [ClientConn] and returns the underlying connection as well
|
||||
// as the read-side bufio which may have some left over data. Hijack may be
|
||||
// called before the user or Read have signaled the end of the keep-alive
|
||||
// logic. The user should not call Hijack while Read or Write is in progress.
|
||||
// logic. The user should not call Hijack while [ClientConn.Read] or ClientConn.Write is in progress.
|
||||
func (cc *ClientConn) Hijack() (c net.Conn, r *bufio.Reader) {
|
||||
cc.mu.Lock()
|
||||
defer cc.mu.Unlock()
|
||||
@@ -284,7 +282,7 @@ func (cc *ClientConn) Hijack() (c net.Conn, r *bufio.Reader) {
|
||||
return
|
||||
}
|
||||
|
||||
// Close calls Hijack and then also closes the underlying connection.
|
||||
// Close calls [ClientConn.Hijack] and then also closes the underlying connection.
|
||||
func (cc *ClientConn) Close() error {
|
||||
c, _ := cc.Hijack()
|
||||
if c != nil {
|
||||
@@ -293,7 +291,7 @@ func (cc *ClientConn) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Write writes a request. An ErrPersistEOF error is returned if the connection
|
||||
// Write writes a request. An [ErrPersistEOF] error is returned if the connection
|
||||
// has been closed in an HTTP keep-alive sense. If req.Close equals true, the
|
||||
// keep-alive connection is logically closed after this request and the opposing
|
||||
// server is informed. An ErrUnexpectedEOF indicates the remote closed the
|
||||
@@ -359,9 +357,9 @@ func (cc *ClientConn) Pending() int {
|
||||
}
|
||||
|
||||
// Read reads the next response from the wire. A valid response might be
|
||||
// returned together with an ErrPersistEOF, which means that the remote
|
||||
// returned together with an [ErrPersistEOF], which means that the remote
|
||||
// requested that this be the last request serviced. Read can be called
|
||||
// concurrently with Write, but not with another Read.
|
||||
// concurrently with [ClientConn.Write], but not with another Read.
|
||||
func (cc *ClientConn) Read(req *http.Request) (resp *http.Response, err error) {
|
||||
// Retrieve the pipeline ID of this request/response pair
|
||||
cc.mu.Lock()
|
||||
|
||||
+185
-48
@@ -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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user