| package main |
| |
| var src = ` |
| // Code generated by golang.org/x/tools/cmd/bundle. DO NOT EDIT. |
| // $ bundle net/http |
| |
| // Package http provides HTTP client and server implementations. |
| // |
| // [Get], [Head], [Post], and [PostForm] make HTTP (or HTTPS) requests: |
| // |
| // resp, err := http.Get("http://example.com/") |
| // ... |
| // resp, err := http.Post("http://example.com/upload", "image/jpeg", &buf) |
| // ... |
| // resp, err := http.PostForm("http://example.com/form", |
| // url.Values{"key": {"Value"}, "id": {"123"}}) |
| // |
| // The caller must close the response body when finished with it: |
| // |
| // resp, err := http.Get("http://example.com/") |
| // if err != nil { |
| // // handle error |
| // } |
| // defer resp.Body.Close() |
| // body, err := io.ReadAll(resp.Body) |
| // // ... |
| // |
| // # Clients and Transports |
| // |
| // For control over HTTP client headers, redirect policy, and other |
| // settings, create a [Client]: |
| // |
| // client := &http.Client{ |
| // CheckRedirect: redirectPolicyFunc, |
| // } |
| // |
| // resp, err := client.Get("http://example.com") |
| // // ... |
| // |
| // req, err := http.NewRequest("GET", "http://example.com", nil) |
| // // ... |
| // req.Header.Add("If-None-Match", ` + "`" + `W/"wyzzy"` + "`" + `) |
| // resp, err := client.Do(req) |
| // // ... |
| // |
| // For control over proxies, TLS configuration, keep-alives, |
| // compression, and other settings, create a [Transport]: |
| // |
| // tr := &http.Transport{ |
| // MaxIdleConns: 10, |
| // IdleConnTimeout: 30 * time.Second, |
| // DisableCompression: true, |
| // } |
| // client := &http.Client{Transport: tr} |
| // resp, err := client.Get("https://example.com") |
| // |
| // Clients and Transports are safe for concurrent use by multiple |
| // goroutines and for efficiency should only be created once and re-used. |
| // |
| // # Servers |
| // |
| // ListenAndServe starts an HTTP server with a given address and handler. |
| // The handler is usually nil, which means to use [DefaultServeMux]. |
| // [Handle] and [HandleFunc] add handlers to [DefaultServeMux]: |
| // |
| // http.Handle("/foo", fooHandler) |
| // |
| // http.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request) { |
| // fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path)) |
| // }) |
| // |
| // log.Fatal(http.ListenAndServe(":8080", nil)) |
| // |
| // More control over the server's behavior is available by creating a |
| // custom Server: |
| // |
| // s := &http.Server{ |
| // Addr: ":8080", |
| // Handler: myHandler, |
| // ReadTimeout: 10 * time.Second, |
| // WriteTimeout: 10 * time.Second, |
| // MaxHeaderBytes: 1 << 20, |
| // } |
| // log.Fatal(s.ListenAndServe()) |
| // |
| // # HTTP/2 |
| // |
| // The http package has transparent support for the HTTP/2 protocol. |
| // |
| // [Server] and [DefaultTransport] automatically enable HTTP/2 support |
| // when using HTTPS. [Transport] does not enable HTTP/2 by default. |
| // |
| // To enable or disable support for HTTP/1, HTTP/2, and/or unencrypted HTTP/2, |
| // see the [Server.Protocols] and [Transport.Protocols] configuration fields. |
| // |
| // To configure advanced HTTP/2 features, see the [Server.HTTP2] and |
| // [Transport.HTTP2] configuration fields. |
| // |
| // Alternatively, the following GODEBUG settings are currently supported: |
| // |
| // GODEBUG=http2client=0 # disable HTTP/2 client support |
| // GODEBUG=http2server=0 # disable HTTP/2 server support |
| // GODEBUG=http2debug=1 # enable verbose HTTP/2 debug logs |
| // GODEBUG=http2debug=2 # ... even more verbose, with frame dumps |
| // |
| // The "omithttp2" build tag may be used to disable the HTTP/2 implementation |
| // contained in the http package. |
| // |
| |
| package main |
| |
| import ( |
| "bufio" |
| "bytes" |
| "compress/flate" |
| "compress/gzip" |
| "container/list" |
| "context" |
| "crypto/tls" |
| "encoding/base64" |
| "errors" |
| "fmt" |
| "internal/godebug" |
| "io" |
| "io/fs" |
| "log" |
| "maps" |
| "math" |
| "math/rand/v2" |
| "mime" |
| "mime/multipart" |
| "net" |
| "net/http/httptrace" |
| "net/http/internal" |
| "net/http/internal/ascii" |
| "net/http/internal/http2" |
| "net/textproto" |
| "net/url" |
| urlpkg "net/url" |
| "os" |
| "path" |
| "path/filepath" |
| "reflect" |
| "runtime" |
| "slices" |
| "sort" |
| "strconv" |
| "strings" |
| "sync" |
| "sync/atomic" |
| "time" |
| "unicode" |
| "unicode/utf8" |
| _ "unsafe" |
| |
| "golang.org/x/net/http/httpguts" |
| "golang.org/x/net/http/httpproxy" |
| "golang.org/x/net/idna" |
| ) |
| |
| // A Client is an HTTP client. Its zero value ([DefaultClient]) is a |
| // usable client that uses [DefaultTransport]. |
| // |
| // The [Client.Transport] typically has internal state (cached TCP |
| // connections), so Clients should be reused instead of created as |
| // needed. Clients are safe for concurrent use by multiple goroutines. |
| // |
| // A Client is higher-level than a [RoundTripper] (such as [Transport]) |
| // and additionally handles HTTP details such as cookies and |
| // redirects. |
| // |
| // When following redirects, the Client will forward all headers set on the |
| // initial [Request] except: |
| // |
| // - when forwarding sensitive headers like "Authorization", |
| // "WWW-Authenticate", and "Cookie" to untrusted targets. |
| // These headers will be ignored when following a redirect to a domain |
| // that is not a subdomain match or exact match of the initial domain. |
| // For example, a redirect from "foo.com" to either "foo.com" or "sub.foo.com" |
| // will forward the sensitive headers, but a redirect to "bar.com" will not. |
| // - when forwarding the "Cookie" header with a non-nil cookie Jar. |
| // Since each redirect may mutate the state of the cookie jar, |
| // a redirect may possibly alter a cookie set in the initial request. |
| // When forwarding the "Cookie" header, any mutated cookies will be omitted, |
| // with the expectation that the Jar will insert those mutated cookies |
| // with the updated values (assuming the origin matches). |
| // If Jar is nil, the initial cookies are forwarded without change. |
| type http_Client struct { |
| // Transport specifies the mechanism by which individual |
| // HTTP requests are made. |
| // If nil, DefaultTransport is used. |
| Transport http_RoundTripper |
| |
| // CheckRedirect specifies the policy for handling redirects. |
| // If CheckRedirect is not nil, the client calls it before |
| // following an HTTP redirect. The arguments req and via are |
| // the upcoming request and the requests made already, oldest |
| // first. If CheckRedirect returns an error, the Client's Get |
| // method returns both the previous Response (with its Body |
| // closed) and CheckRedirect's error (wrapped in a url.Error) |
| // instead of issuing the Request req. |
| // As a special case, if CheckRedirect returns ErrUseLastResponse, |
| // then the most recent response is returned with its body |
| // unclosed, along with a nil error. |
| // |
| // If CheckRedirect is nil, the Client uses its default policy, |
| // which is to stop after 10 consecutive requests. |
| CheckRedirect func(req *http_Request, via []*http_Request) error |
| |
| // Jar specifies the cookie jar. |
| // |
| // The Jar is used to insert relevant cookies into every |
| // outbound Request and is updated with the cookie values |
| // of every inbound Response. The Jar is consulted for every |
| // redirect that the Client follows. |
| // |
| // If Jar is nil, cookies are only sent if they are explicitly |
| // set on the Request. |
| Jar http_CookieJar |
| |
| // Timeout specifies a time limit for requests made by this |
| // Client. The timeout includes connection time, any |
| // redirects, and reading the response body. The timer remains |
| // running after Get, Head, Post, or Do return and will |
| // interrupt reading of the Response.Body. |
| // |
| // A Timeout of zero means no timeout. |
| // |
| // The Client cancels requests to the underlying Transport |
| // as if the Request's Context ended. |
| // |
| // For compatibility, the Client will also use the deprecated |
| // CancelRequest method on Transport if found. New |
| // RoundTripper implementations should use the Request's Context |
| // for cancellation instead of implementing CancelRequest. |
| Timeout time.Duration |
| } |
| |
| // DefaultClient is the default [Client] and is used by [Get], [Head], and [Post]. |
| var http_DefaultClient = &http_Client{} |
| |
| // RoundTripper is an interface representing the ability to execute a |
| // single HTTP transaction, obtaining the [Response] for a given [Request]. |
| // |
| // A RoundTripper must be safe for concurrent use by multiple |
| // goroutines. |
| type http_RoundTripper interface { |
| // RoundTrip executes a single HTTP transaction, returning |
| // a Response for the provided Request. |
| // |
| // RoundTrip should not attempt to interpret the response. In |
| // particular, RoundTrip must return err == nil if it obtained |
| // a response, regardless of the response's HTTP status code. |
| // A non-nil err should be reserved for failure to obtain a |
| // response. Similarly, RoundTrip should not attempt to |
| // handle higher-level protocol details such as redirects, |
| // authentication, or cookies. |
| // |
| // RoundTrip should not modify the request, except for |
| // consuming and closing the Request's Body. RoundTrip may |
| // read fields of the request in a separate goroutine. Callers |
| // should not mutate or reuse the request until the Response's |
| // Body has been closed. |
| // |
| // RoundTrip must always close the body, including on errors, |
| // but depending on the implementation may do so in a separate |
| // goroutine even after RoundTrip returns. This means that |
| // callers wanting to reuse the body for subsequent requests |
| // must arrange to wait for the Close call before doing so. |
| // |
| // The Request's URL and Header fields must be initialized. |
| RoundTrip(*http_Request) (*http_Response, error) |
| } |
| |
| // refererForURL returns a referer without any authentication info or |
| // an empty string if lastReq scheme is https and newReq scheme is http. |
| // If the referer was explicitly set, then it will continue to be used. |
| func http_refererForURL(lastReq, newReq *url.URL, explicitRef string) string { |
| // https://tools.ietf.org/html/rfc7231#section-5.5.2 |
| // "Clients SHOULD NOT include a Referer header field in a |
| // (non-secure) HTTP request if the referring page was |
| // transferred with a secure protocol." |
| if lastReq.Scheme == "https" && newReq.Scheme == "http" { |
| return "" |
| } |
| if explicitRef != "" { |
| return explicitRef |
| } |
| |
| referer := lastReq.String() |
| if lastReq.User != nil { |
| // This is not very efficient, but is the best we can |
| // do without: |
| // - introducing a new method on URL |
| // - creating a race condition |
| // - copying the URL struct manually, which would cause |
| // maintenance problems down the line |
| auth := lastReq.User.String() + "@" |
| referer = strings.Replace(referer, auth, "", 1) |
| } |
| return referer |
| } |
| |
| // didTimeout is non-nil only if err != nil. |
| func (c *http_Client) send(req *http_Request, deadline time.Time) (resp *http_Response, didTimeout func() bool, err error) { |
| cookieURL := req.URL |
| if req.Host != "" { |
| cookieURL = http_cloneURL(cookieURL) |
| cookieURL.Host = req.Host |
| } |
| if c.Jar != nil { |
| for _, cookie := range c.Jar.Cookies(cookieURL) { |
| req.AddCookie(cookie) |
| } |
| } |
| resp, didTimeout, err = http_send(req, c.transport(), deadline) |
| if err != nil { |
| return nil, didTimeout, err |
| } |
| if c.Jar != nil { |
| if rc := resp.Cookies(); len(rc) > 0 { |
| c.Jar.SetCookies(cookieURL, rc) |
| } |
| } |
| return resp, nil, nil |
| } |
| |
| func (c *http_Client) deadline() time.Time { |
| if c.Timeout > 0 { |
| return time.Now().Add(c.Timeout) |
| } |
| return time.Time{} |
| } |
| |
| func (c *http_Client) transport() http_RoundTripper { |
| if c.Transport != nil { |
| return c.Transport |
| } |
| return http_DefaultTransport |
| } |
| |
| // ErrSchemeMismatch is returned when a server returns an HTTP response to an HTTPS client. |
| var http_ErrSchemeMismatch = errors.New("http: server gave HTTP response to HTTPS client") |
| |
| // send issues an HTTP request. |
| // Caller should close resp.Body when done reading from it. |
| func http_send(ireq *http_Request, rt http_RoundTripper, deadline time.Time) (resp *http_Response, didTimeout func() bool, err error) { |
| req := ireq // req is either the original request, or a modified fork |
| |
| if rt == nil { |
| req.closeBody() |
| return nil, http_alwaysFalse, errors.New("http: no Client.Transport or DefaultTransport") |
| } |
| |
| if req.URL == nil { |
| req.closeBody() |
| return nil, http_alwaysFalse, errors.New("http: nil Request.URL") |
| } |
| |
| if req.RequestURI != "" { |
| req.closeBody() |
| return nil, http_alwaysFalse, errors.New("http: Request.RequestURI can't be set in client requests") |
| } |
| |
| // forkReq forks req into a shallow clone of ireq the first |
| // time it's called. |
| forkReq := func() { |
| if ireq == req { |
| req = new(http_Request) |
| *req = *ireq // shallow clone |
| } |
| } |
| |
| // Most the callers of send (Get, Post, et al) don't need |
| // Headers, leaving it uninitialized. We guarantee to the |
| // Transport that this has been initialized, though. |
| if req.Header == nil { |
| forkReq() |
| req.Header = make(http_Header) |
| } |
| |
| if u := req.URL.User; u != nil && req.Header.Get("Authorization") == "" { |
| username := u.Username() |
| password, _ := u.Password() |
| forkReq() |
| req.Header = http_cloneOrMakeHeader(ireq.Header) |
| req.Header.Set("Authorization", "Basic "+http_basicAuth(username, password)) |
| } |
| |
| if !deadline.IsZero() { |
| forkReq() |
| } |
| stopTimer, didTimeout := http_setRequestCancel(req, rt, deadline) |
| |
| resp, err = rt.RoundTrip(req) |
| if err != nil { |
| stopTimer() |
| if resp != nil { |
| log.Printf("RoundTripper returned a response & error; ignoring response") |
| } |
| if tlsErr, ok := err.(tls.RecordHeaderError); ok { |
| // If we get a bad TLS record header, check to see if the |
| // response looks like HTTP and give a more helpful error. |
| // See golang.org/issue/11111. |
| if string(tlsErr.RecordHeader[:]) == "HTTP/" { |
| err = http_ErrSchemeMismatch |
| } |
| } |
| return nil, didTimeout, err |
| } |
| if resp == nil { |
| return nil, didTimeout, fmt.Errorf("http: RoundTripper implementation (%T) returned a nil *Response with a nil error", rt) |
| } |
| if resp.Body == nil { |
| // The documentation on the Body field says “The http Client and Transport |
| // guarantee that Body is always non-nil, even on responses without a body |
| // or responses with a zero-length body.” Unfortunately, we didn't document |
| // that same constraint for arbitrary RoundTripper implementations, and |
| // RoundTripper implementations in the wild (mostly in tests) assume that |
| // they can use a nil Body to mean an empty one (similar to Request.Body). |
| // (See https://golang.org/issue/38095.) |
| // |
| // If the ContentLength allows the Body to be empty, fill in an empty one |
| // here to ensure that it is non-nil. |
| if resp.ContentLength > 0 && req.Method != "HEAD" { |
| return nil, didTimeout, fmt.Errorf("http: RoundTripper implementation (%T) returned a *Response with content length %d but a nil Body", rt, resp.ContentLength) |
| } |
| resp.Body = io.NopCloser(strings.NewReader("")) |
| } |
| if !deadline.IsZero() { |
| resp.Body = &http_cancelTimerBody{ |
| stop: stopTimer, |
| rc: resp.Body, |
| reqDidTimeout: didTimeout, |
| } |
| } |
| return resp, nil, nil |
| } |
| |
| // timeBeforeContextDeadline reports whether the non-zero Time t is |
| // before ctx's deadline, if any. If ctx does not have a deadline, it |
| // always reports true (the deadline is considered infinite). |
| func http_timeBeforeContextDeadline(t time.Time, ctx context.Context) bool { |
| d, ok := ctx.Deadline() |
| if !ok { |
| return true |
| } |
| return t.Before(d) |
| } |
| |
| // knownRoundTripperImpl reports whether rt is a RoundTripper that's |
| // maintained by the Go team and known to implement the latest |
| // optional semantics (notably contexts). The Request is used |
| // to check whether this particular request is using an alternate protocol, |
| // in which case we need to check the RoundTripper for that protocol. |
| func http_knownRoundTripperImpl(rt http_RoundTripper, req *http_Request) bool { |
| switch t := rt.(type) { |
| case *http_Transport: |
| if altRT := t.alternateRoundTripper(req); altRT != nil { |
| return http_knownRoundTripperImpl(altRT, req) |
| } |
| return true |
| case http_http2RoundTripper: |
| return true |
| } |
| // There's a very minor chance of a false positive with this. |
| // Instead of detecting our golang.org/x/net/http2.Transport, |
| // it might detect a Transport type in a different http2 |
| // package. But I know of none, and the only problem would be |
| // some temporarily leaked goroutines if the transport didn't |
| // support contexts. So this is a good enough heuristic: |
| if reflect.TypeOf(rt).String() == "*http2.Transport" { |
| return true |
| } |
| return false |
| } |
| |
| // setRequestCancel sets req.Cancel and adds a deadline context to req |
| // if deadline is non-zero. The RoundTripper's type is used to |
| // determine whether the legacy CancelRequest behavior should be used. |
| // |
| // As background, there are three ways to cancel a request: |
| // First was Transport.CancelRequest. (deprecated) |
| // Second was Request.Cancel. |
| // Third was Request.Context. |
| // This function populates the second and third, and uses the first if it really needs to. |
| func http_setRequestCancel(req *http_Request, rt http_RoundTripper, deadline time.Time) (stopTimer func(), didTimeout func() bool) { |
| if deadline.IsZero() { |
| return http_nop, http_alwaysFalse |
| } |
| knownTransport := http_knownRoundTripperImpl(rt, req) |
| oldCtx := req.Context() |
| |
| if req.Cancel == nil && knownTransport { |
| // If they already had a Request.Context that's |
| // expiring sooner, do nothing: |
| if !http_timeBeforeContextDeadline(deadline, oldCtx) { |
| return http_nop, http_alwaysFalse |
| } |
| |
| var cancelCtx func() |
| req.ctx, cancelCtx = context.WithDeadline(oldCtx, deadline) |
| return cancelCtx, func() bool { return time.Now().After(deadline) } |
| } |
| initialReqCancel := req.Cancel // the user's original Request.Cancel, if any |
| |
| var cancelCtx func() |
| if http_timeBeforeContextDeadline(deadline, oldCtx) { |
| req.ctx, cancelCtx = context.WithDeadline(oldCtx, deadline) |
| } |
| |
| cancel := make(chan struct{}) |
| req.Cancel = cancel |
| |
| doCancel := func() { |
| // The second way in the func comment above: |
| close(cancel) |
| // The first way, used only for RoundTripper |
| // implementations written before Go 1.5 or Go 1.6. |
| type canceler interface{ CancelRequest(*http_Request) } |
| if v, ok := rt.(canceler); ok { |
| v.CancelRequest(req) |
| } |
| } |
| |
| stopTimerCh := make(chan struct{}) |
| stopTimer = sync.OnceFunc(func() { |
| close(stopTimerCh) |
| if cancelCtx != nil { |
| cancelCtx() |
| } |
| }) |
| |
| timer := time.NewTimer(time.Until(deadline)) |
| var timedOut atomic.Bool |
| |
| go func() { |
| select { |
| case <-initialReqCancel: |
| doCancel() |
| timer.Stop() |
| case <-timer.C: |
| timedOut.Store(true) |
| doCancel() |
| case <-stopTimerCh: |
| timer.Stop() |
| } |
| }() |
| |
| return stopTimer, timedOut.Load |
| } |
| |
| // See 2 (end of page 4) https://www.ietf.org/rfc/rfc2617.txt |
| // "To receive authorization, the client sends the userid and password, |
| // separated by a single colon (":") character, within a base64 |
| // encoded string in the credentials." |
| // It is not meant to be urlencoded. |
| func http_basicAuth(username, password string) string { |
| auth := username + ":" + password |
| return base64.StdEncoding.EncodeToString([]byte(auth)) |
| } |
| |
| // Get issues a GET to the specified URL. If the response is one of |
| // the following redirect codes, Get follows the redirect, up to a |
| // maximum of 10 redirects: |
| // |
| // 301 (Moved Permanently) |
| // 302 (Found) |
| // 303 (See Other) |
| // 307 (Temporary Redirect) |
| // 308 (Permanent Redirect) |
| // |
| // An error is returned if there were too many redirects or if there |
| // was an HTTP protocol error. A non-2xx response doesn't cause an |
| // error. Any returned error will be of type [*url.Error]. The url.Error |
| // value's Timeout method will report true if the request timed out. |
| // |
| // When err is nil, resp always contains a non-nil resp.Body. |
| // Caller should close resp.Body when done reading from it. |
| // |
| // Get is a wrapper around DefaultClient.Get. |
| // |
| // To make a request with custom headers, use [NewRequest] and |
| // DefaultClient.Do. |
| // |
| // To make a request with a specified context.Context, use [NewRequestWithContext] |
| // and DefaultClient.Do. |
| func http_Get(url string) (resp *http_Response, err error) { |
| return http_DefaultClient.Get(url) |
| } |
| |
| // Get issues a GET to the specified URL. If the response is one of the |
| // following redirect codes, Get follows the redirect after calling the |
| // [Client.CheckRedirect] function: |
| // |
| // 301 (Moved Permanently) |
| // 302 (Found) |
| // 303 (See Other) |
| // 307 (Temporary Redirect) |
| // 308 (Permanent Redirect) |
| // |
| // An error is returned if the [Client.CheckRedirect] function fails |
| // or if there was an HTTP protocol error. A non-2xx response doesn't |
| // cause an error. Any returned error will be of type [*url.Error]. The |
| // url.Error value's Timeout method will report true if the request |
| // timed out. |
| // |
| // When err is nil, resp always contains a non-nil resp.Body. |
| // Caller should close resp.Body when done reading from it. |
| // |
| // To make a request with custom headers, use [NewRequest] and [Client.Do]. |
| // |
| // To make a request with a specified context.Context, use [NewRequestWithContext] |
| // and Client.Do. |
| func (c *http_Client) Get(url string) (resp *http_Response, err error) { |
| req, err := http_NewRequest("GET", url, nil) |
| if err != nil { |
| return nil, err |
| } |
| return c.Do(req) |
| } |
| |
| func http_alwaysFalse() bool { return false } |
| |
| // ErrUseLastResponse can be returned by Client.CheckRedirect hooks to |
| // control how redirects are processed. If returned, the next request |
| // is not sent and the most recent response is returned with its body |
| // unclosed. |
| var http_ErrUseLastResponse = errors.New("net/http: use last response") |
| |
| // checkRedirect calls either the user's configured CheckRedirect |
| // function, or the default. |
| func (c *http_Client) checkRedirect(req *http_Request, via []*http_Request) error { |
| fn := c.CheckRedirect |
| if fn == nil { |
| fn = http_defaultCheckRedirect |
| } |
| return fn(req, via) |
| } |
| |
| // redirectBehavior describes what should happen when the |
| // client encounters a 3xx status code from the server. |
| func http_redirectBehavior(reqMethod string, resp *http_Response, ireq *http_Request) (redirectMethod string, shouldRedirect, includeBody bool) { |
| switch resp.StatusCode { |
| case 301, 302, 303: |
| redirectMethod = reqMethod |
| shouldRedirect = true |
| includeBody = false |
| |
| // RFC 2616 allowed automatic redirection only with GET and |
| // HEAD requests. RFC 7231 lifts this restriction, but we still |
| // restrict other methods to GET to maintain compatibility. |
| // See Issue 18570. |
| if reqMethod != "GET" && reqMethod != "HEAD" { |
| redirectMethod = "GET" |
| } |
| case 307, 308: |
| redirectMethod = reqMethod |
| shouldRedirect = true |
| includeBody = true |
| |
| if ireq.GetBody == nil && ireq.outgoingLength() != 0 { |
| // We had a request body, and 307/308 require |
| // re-sending it, but GetBody is not defined. So just |
| // return this response to the user instead of an |
| // error, like we did in Go 1.7 and earlier. |
| shouldRedirect = false |
| } |
| } |
| return redirectMethod, shouldRedirect, includeBody |
| } |
| |
| // urlErrorOp returns the (*url.Error).Op value to use for the |
| // provided (*Request).Method value. |
| func http_urlErrorOp(method string) string { |
| if method == "" { |
| return "Get" |
| } |
| if lowerMethod, ok := ascii.ToLower(method); ok { |
| return method[:1] + lowerMethod[1:] |
| } |
| return method |
| } |
| |
| // Do sends an HTTP request and returns an HTTP response, following |
| // policy (such as redirects, cookies, auth) as configured on the |
| // client. |
| // |
| // An error is returned if caused by client policy (such as |
| // CheckRedirect), or failure to speak HTTP (such as a network |
| // connectivity problem). A non-2xx status code doesn't cause an |
| // error. |
| // |
| // If the returned error is nil, the [Response] will contain a non-nil |
| // Body which the user is expected to close. If the Body is not both |
| // read to EOF and closed, the [Client]'s underlying [RoundTripper] |
| // (typically [Transport]) may not be able to re-use a persistent TCP |
| // connection to the server for a subsequent "keep-alive" request. |
| // Note, however, that [Transport] will automatically try to read a |
| // [Response] Body to EOF asynchronously up to a conservative limit |
| // when a Body is closed. |
| // |
| // The request Body, if non-nil, will be closed by the underlying |
| // Transport, even on errors. The Body may be closed asynchronously after |
| // Do returns. |
| // |
| // On error, any Response can be ignored. A non-nil Response with a |
| // non-nil error only occurs when CheckRedirect fails, and even then |
| // the returned [Response.Body] is already closed. |
| // |
| // Generally [Get], [Post], or [PostForm] will be used instead of Do. |
| // |
| // If the server replies with a redirect, the Client first uses the |
| // CheckRedirect function to determine whether the redirect should be |
| // followed. If permitted, a 301, 302, or 303 redirect causes |
| // subsequent requests to use HTTP method GET |
| // (or HEAD if the original request was HEAD), with no body. |
| // A 307 or 308 redirect preserves the original HTTP method and body, |
| // provided that the [Request.GetBody] function is defined. |
| // The [NewRequest] function automatically sets GetBody for common |
| // standard library body types. |
| // |
| // Note that the [Client] redirect behavior does not follow the WHATWG |
| // Fetch standard. This is because it was written before established |
| // standards existed. As such, by modern standards, [Client] has a |
| // rather permissive behavior. For example, sensitive headers are |
| // retained on redirect to a subdomain or to a different scheme on the |
| // same host. |
| // |
| // Any returned error will be of type [*url.Error]. The url.Error |
| // value's Timeout method will report true if the request timed out. |
| func (c *http_Client) Do(req *http_Request) (*http_Response, error) { |
| return c.do(req) |
| } |
| |
| var http_testHookClientDoResult func(retres *http_Response, reterr error) |
| |
| func (c *http_Client) do(req *http_Request) (retres *http_Response, reterr error) { |
| if http_testHookClientDoResult != nil { |
| defer func() { http_testHookClientDoResult(retres, reterr) }() |
| } |
| if req.URL == nil { |
| req.closeBody() |
| return nil, &url.Error{ |
| Op: http_urlErrorOp(req.Method), |
| Err: errors.New("http: nil Request.URL"), |
| } |
| } |
| _ = *c // panic early if c is nil; see go.dev/issue/53521 |
| |
| var ( |
| deadline = c.deadline() |
| reqs []*http_Request |
| resp *http_Response |
| copyHeaders = c.makeHeadersCopier(req) |
| reqBodyClosed = false // have we closed the current req.Body? |
| |
| // Redirect behavior: |
| redirectMethod string |
| includeBody = true |
| stripSensitiveHeaders = false |
| ) |
| uerr := func(err error) error { |
| // the body may have been closed already by c.send() |
| if !reqBodyClosed { |
| req.closeBody() |
| } |
| var urlStr string |
| if resp != nil && resp.Request != nil { |
| urlStr = http_stripPassword(resp.Request.URL) |
| } else { |
| urlStr = http_stripPassword(req.URL) |
| } |
| return &url.Error{ |
| Op: http_urlErrorOp(reqs[0].Method), |
| URL: urlStr, |
| Err: err, |
| } |
| } |
| for { |
| // For all but the first request, create the next |
| // request hop and replace req. |
| if len(reqs) > 0 { |
| loc := resp.Header.Get("Location") |
| if loc == "" { |
| // While most 3xx responses include a Location, it is not |
| // required and 3xx responses without a Location have been |
| // observed in the wild. See issues #17773 and #49281. |
| return resp, nil |
| } |
| u, err := req.URL.Parse(loc) |
| if err != nil { |
| resp.closeBody() |
| return nil, uerr(fmt.Errorf("failed to parse Location header %q: %v", loc, err)) |
| } |
| host := "" |
| if req.Host != "" && req.Host != req.URL.Host { |
| // If the caller specified a custom Host header and the |
| // redirect location is relative, preserve the Host header |
| // through the redirect. See issue #22233. |
| if u, _ := url.Parse(loc); u != nil && !u.IsAbs() { |
| host = req.Host |
| } |
| } |
| ireq := reqs[0] |
| req = &http_Request{ |
| Method: redirectMethod, |
| Response: resp, |
| URL: u, |
| Header: make(http_Header), |
| Host: host, |
| Cancel: ireq.Cancel, |
| ctx: ireq.ctx, |
| } |
| if includeBody && ireq.GetBody != nil { |
| req.Body, err = ireq.GetBody() |
| if err != nil { |
| resp.closeBody() |
| return nil, uerr(err) |
| } |
| req.GetBody = ireq.GetBody |
| req.ContentLength = ireq.ContentLength |
| } |
| |
| // Copy original headers before setting the Referer, |
| // in case the user set Referer on their first request. |
| // If they really want to override, they can do it in |
| // their CheckRedirect func. |
| if !stripSensitiveHeaders && reqs[0].URL.Host != req.URL.Host { |
| if !http_shouldCopyHeaderOnRedirect(reqs[0].URL, req.URL) { |
| stripSensitiveHeaders = true |
| } |
| } |
| copyHeaders(req, stripSensitiveHeaders, !includeBody) |
| // Add the Referer header from the most recent |
| // request URL to the new one, if it's not https->http: |
| if ref := http_refererForURL(reqs[len(reqs)-1].URL, req.URL, req.Header.Get("Referer")); ref != "" { |
| req.Header.Set("Referer", ref) |
| } |
| err = c.checkRedirect(req, reqs) |
| |
| // Sentinel error to let users select the |
| // previous response, without closing its |
| // body. See Issue 10069. |
| if err == http_ErrUseLastResponse { |
| return resp, nil |
| } |
| |
| // Close the previous response's body. But |
| // read at least some of the body so if it's |
| // small the underlying TCP connection will be |
| // re-used. No need to check for errors: if it |
| // fails, the Transport won't reuse it anyway. |
| const maxBodySlurpSize = 2 << 10 |
| if resp.ContentLength == -1 || resp.ContentLength <= maxBodySlurpSize { |
| io.CopyN(io.Discard, resp.Body, maxBodySlurpSize) |
| } |
| resp.Body.Close() |
| |
| if err != nil { |
| // Special case for Go 1 compatibility: return both the response |
| // and an error if the CheckRedirect function failed. |
| // See https://golang.org/issue/3795 |
| // The resp.Body has already been closed. |
| ue := uerr(err) |
| ue.(*url.Error).URL = loc |
| return resp, ue |
| } |
| } |
| |
| reqs = append(reqs, req) |
| var err error |
| var didTimeout func() bool |
| if resp, didTimeout, err = c.send(req, deadline); err != nil { |
| // c.send() always closes req.Body |
| reqBodyClosed = true |
| if !deadline.IsZero() && didTimeout() { |
| err = &http_timeoutError{err.Error() + " (Client.Timeout exceeded while awaiting headers)"} |
| } |
| return nil, uerr(err) |
| } |
| |
| var shouldRedirect, includeBodyOnHop bool |
| redirectMethod, shouldRedirect, includeBodyOnHop = http_redirectBehavior(req.Method, resp, reqs[0]) |
| if !shouldRedirect { |
| return resp, nil |
| } |
| if !includeBodyOnHop { |
| // Once a hop drops the body, we never send it again |
| // (because we're now handling a redirect for a request with no body). |
| includeBody = false |
| } |
| |
| req.closeBody() |
| } |
| } |
| |
| // makeHeadersCopier makes a function that copies headers from the |
| // initial Request, ireq. For every redirect, this function must be called |
| // so that it can copy headers into the upcoming Request. |
| func (c *http_Client) makeHeadersCopier(ireq *http_Request) func(req *http_Request, stripSensitiveHeaders, stripBodyHeaders bool) { |
| // The headers to copy are from the very initial request. |
| // We use a closured callback to keep a reference to these original headers. |
| var ( |
| ireqhdr = http_cloneOrMakeHeader(ireq.Header) |
| icookies map[string][]*http_Cookie |
| ) |
| if c.Jar != nil && ireq.Header.Get("Cookie") != "" { |
| icookies = make(map[string][]*http_Cookie) |
| for _, c := range ireq.Cookies() { |
| icookies[c.Name] = append(icookies[c.Name], c) |
| } |
| } |
| |
| return func(req *http_Request, stripSensitiveHeaders, stripBodyHeaders bool) { |
| // If Jar is present and there was some initial cookies provided |
| // via the request header, then we may need to alter the initial |
| // cookies as we follow redirects since each redirect may end up |
| // modifying a pre-existing cookie. |
| // |
| // Since cookies already set in the request header do not contain |
| // information about the original domain and path, the logic below |
| // assumes any new set cookies override the original cookie |
| // regardless of domain or path. |
| // |
| // See https://golang.org/issue/17494 |
| if c.Jar != nil && icookies != nil { |
| var changed bool |
| resp := req.Response // The response that caused the upcoming redirect |
| for _, c := range resp.Cookies() { |
| if _, ok := icookies[c.Name]; ok { |
| delete(icookies, c.Name) |
| changed = true |
| } |
| } |
| if changed { |
| ireqhdr.Del("Cookie") |
| var ss []string |
| for _, cs := range icookies { |
| for _, c := range cs { |
| ss = append(ss, c.Name+"="+c.Value) |
| } |
| } |
| slices.Sort(ss) // Ensure deterministic headers |
| ireqhdr.Set("Cookie", strings.Join(ss, "; ")) |
| } |
| } |
| |
| // Copy the initial request's Header values |
| // (at least the safe ones). |
| for k, vv := range ireqhdr { |
| sensitive := false |
| body := false |
| switch http_CanonicalHeaderKey(k) { |
| case "Authorization", "Www-Authenticate", "Cookie", "Cookie2", |
| "Proxy-Authorization", "Proxy-Authenticate": |
| sensitive = true |
| |
| case "Content-Encoding", "Content-Language", "Content-Location", |
| "Content-Type": |
| // Headers relating to the body which is removed for |
| // POST to GET redirects |
| // https://fetch.spec.whatwg.org/#http-redirect-fetch |
| body = true |
| |
| } |
| if !(sensitive && stripSensitiveHeaders) && !(body && stripBodyHeaders) { |
| req.Header[k] = vv |
| } |
| } |
| } |
| } |
| |
| func http_defaultCheckRedirect(req *http_Request, via []*http_Request) error { |
| if len(via) >= 10 { |
| return errors.New("stopped after 10 redirects") |
| } |
| return nil |
| } |
| |
| // Post issues a POST to the specified URL. |
| // |
| // Caller should close resp.Body when done reading from it. |
| // |
| // If the provided body is an [io.Closer], it is closed after the |
| // request. |
| // |
| // Post is a wrapper around DefaultClient.Post. |
| // |
| // To set custom headers, use [NewRequest] and DefaultClient.Do. |
| // |
| // See the [Client.Do] method documentation for details on how redirects |
| // are handled. |
| // |
| // To make a request with a specified context.Context, use [NewRequestWithContext] |
| // and DefaultClient.Do. |
| func http_Post(url, contentType string, body io.Reader) (resp *http_Response, err error) { |
| return http_DefaultClient.Post(url, contentType, body) |
| } |
| |
| // Post issues a POST to the specified URL. |
| // |
| // Caller should close resp.Body when done reading from it. |
| // |
| // If the provided body is an [io.Closer], it is closed after the |
| // request. |
| // |
| // To set custom headers, use [NewRequest] and [Client.Do]. |
| // |
| // To make a request with a specified context.Context, use [NewRequestWithContext] |
| // and [Client.Do]. |
| // |
| // See the [Client.Do] method documentation for details on how redirects |
| // are handled. |
| func (c *http_Client) Post(url, contentType string, body io.Reader) (resp *http_Response, err error) { |
| req, err := http_NewRequest("POST", url, body) |
| if err != nil { |
| return nil, err |
| } |
| req.Header.Set("Content-Type", contentType) |
| return c.Do(req) |
| } |
| |
| // PostForm issues a POST to the specified URL, with data's keys and |
| // values URL-encoded as the request body. |
| // |
| // The Content-Type header is set to application/x-www-form-urlencoded. |
| // To set other headers, use [NewRequest] and DefaultClient.Do. |
| // |
| // When err is nil, resp always contains a non-nil resp.Body. |
| // Caller should close resp.Body when done reading from it. |
| // |
| // PostForm is a wrapper around DefaultClient.PostForm. |
| // |
| // See the [Client.Do] method documentation for details on how redirects |
| // are handled. |
| // |
| // To make a request with a specified [context.Context], use [NewRequestWithContext] |
| // and DefaultClient.Do. |
| func http_PostForm(url string, data url.Values) (resp *http_Response, err error) { |
| return http_DefaultClient.PostForm(url, data) |
| } |
| |
| // PostForm issues a POST to the specified URL, |
| // with data's keys and values URL-encoded as the request body. |
| // |
| // The Content-Type header is set to application/x-www-form-urlencoded. |
| // To set other headers, use [NewRequest] and [Client.Do]. |
| // |
| // When err is nil, resp always contains a non-nil resp.Body. |
| // Caller should close resp.Body when done reading from it. |
| // |
| // See the [Client.Do] method documentation for details on how redirects |
| // are handled. |
| // |
| // To make a request with a specified context.Context, use [NewRequestWithContext] |
| // and Client.Do. |
| func (c *http_Client) PostForm(url string, data url.Values) (resp *http_Response, err error) { |
| return c.Post(url, "application/x-www-form-urlencoded", strings.NewReader(data.Encode())) |
| } |
| |
| // Head issues a HEAD to the specified URL. If the response is one of |
| // the following redirect codes, Head follows the redirect, up to a |
| // maximum of 10 redirects: |
| // |
| // 301 (Moved Permanently) |
| // 302 (Found) |
| // 303 (See Other) |
| // 307 (Temporary Redirect) |
| // 308 (Permanent Redirect) |
| // |
| // Head is a wrapper around DefaultClient.Head. |
| // |
| // To make a request with a specified [context.Context], use [NewRequestWithContext] |
| // and DefaultClient.Do. |
| func http_Head(url string) (resp *http_Response, err error) { |
| return http_DefaultClient.Head(url) |
| } |
| |
| // Head issues a HEAD to the specified URL. If the response is one of the |
| // following redirect codes, Head follows the redirect after calling the |
| // [Client.CheckRedirect] function: |
| // |
| // 301 (Moved Permanently) |
| // 302 (Found) |
| // 303 (See Other) |
| // 307 (Temporary Redirect) |
| // 308 (Permanent Redirect) |
| // |
| // To make a request with a specified [context.Context], use [NewRequestWithContext] |
| // and [Client.Do]. |
| func (c *http_Client) Head(url string) (resp *http_Response, err error) { |
| req, err := http_NewRequest("HEAD", url, nil) |
| if err != nil { |
| return nil, err |
| } |
| return c.Do(req) |
| } |
| |
| // CloseIdleConnections closes any connections on its [Transport] which |
| // were previously connected from previous requests but are now |
| // sitting idle in a "keep-alive" state. It does not interrupt any |
| // connections currently in use. |
| // |
| // If [Client.Transport] does not have a [Client.CloseIdleConnections] method |
| // then this method does nothing. |
| func (c *http_Client) CloseIdleConnections() { |
| type closeIdler interface { |
| CloseIdleConnections() |
| } |
| if tr, ok := c.transport().(closeIdler); ok { |
| tr.CloseIdleConnections() |
| } |
| } |
| |
| // cancelTimerBody is an io.ReadCloser that wraps rc with two features: |
| // 1. On Read error or close, the stop func is called. |
| // 2. On Read failure, if reqDidTimeout is true, the error is wrapped and |
| // marked as net.Error that hit its timeout. |
| type http_cancelTimerBody struct { |
| stop func() // stops the time.Timer waiting to cancel the request |
| rc io.ReadCloser |
| reqDidTimeout func() bool |
| } |
| |
| func (b *http_cancelTimerBody) Read(p []byte) (n int, err error) { |
| n, err = b.rc.Read(p) |
| if err == nil { |
| return n, nil |
| } |
| if err == io.EOF { |
| return n, err |
| } |
| if b.reqDidTimeout() { |
| err = &http_timeoutError{err.Error() + " (Client.Timeout or context cancellation while reading body)"} |
| } |
| return n, err |
| } |
| |
| func (b *http_cancelTimerBody) Close() error { |
| err := b.rc.Close() |
| b.stop() |
| return err |
| } |
| |
| func http_shouldCopyHeaderOnRedirect(initial, dest *url.URL) bool { |
| // Permit sending auth/cookie headers from "foo.com" |
| // to "sub.foo.com". |
| |
| // Note that we don't send all cookies to subdomains |
| // automatically. This function is only used for |
| // Cookies set explicitly on the initial outgoing |
| // client request. Cookies automatically added via the |
| // CookieJar mechanism continue to follow each |
| // cookie's scope as set by Set-Cookie. But for |
| // outgoing requests with the Cookie header set |
| // directly, we don't know their scope, so we assume |
| // it's for *.domain.com. |
| |
| ihost, err1 := httpguts.PunycodeHostPort(initial.Hostname()) |
| dhost, err2 := httpguts.PunycodeHostPort(dest.Hostname()) |
| if err1 != nil || err2 != nil { |
| return false |
| } |
| ihost, ok1 := ascii.ToLower(ihost) |
| dhost, ok2 := ascii.ToLower(dhost) |
| return ok1 && ok2 && http_isDomainOrSubdomain(dhost, ihost) |
| } |
| |
| // isDomainOrSubdomain reports whether sub is a subdomain (or exact |
| // match) of the parent domain. |
| // |
| // Both domains must already be in canonical form. |
| func http_isDomainOrSubdomain(sub, parent string) bool { |
| if sub == parent { |
| return true |
| } |
| // If sub contains a :, it's probably an IPv6 address (and is definitely not a hostname). |
| // Don't check the suffix in this case, to avoid matching the contents of a IPv6 zone. |
| // For example, "::1%.www.example.com" is not a subdomain of "www.example.com". |
| if strings.ContainsAny(sub, ":%") { |
| return false |
| } |
| // If sub is "foo.example.com" and parent is "example.com", |
| // that means sub must end in "."+parent. |
| // Do it without allocating. |
| if !strings.HasSuffix(sub, parent) { |
| return false |
| } |
| return sub[len(sub)-len(parent)-1] == '.' |
| } |
| |
| func http_stripPassword(u *url.URL) string { |
| _, passSet := u.User.Password() |
| if passSet { |
| return strings.Replace(u.String(), u.User.String()+"@", u.User.Username()+":***@", 1) |
| } |
| return u.String() |
| } |
| |
| // A ClientConn is a client connection to an HTTP server. |
| // |
| // Unlike a [Transport], a ClientConn represents a single connection. |
| // Most users should use a Transport rather than creating client connections directly. |
| type http_ClientConn struct { |
| cc http_genericClientConn |
| |
| stateHookMu sync.Mutex |
| userStateHook func(*http_ClientConn) |
| stateHookRunning bool |
| lastAvailable int |
| lastInFlight int |
| lastClosed bool |
| } |
| |
| // newClientConner is the interface implemented by HTTP/2 transports to create new client conns. |
| // |
| // The http package (this package) needs a way to ask the http2 package to |
| // create a client connection. |
| // |
| // Transport.TLSNextProto["h2"] contains a function which appears to do this, |
| // but for historical reasons it does not: The TLSNextProto function adds a |
| // *tls.Conn to the http2.Transport's connection pool and returns a RoundTripper |
| // which is backed by that connection pool. NewClientConn needs a way to get a |
| // single client connection out of the http2 package. |
| // |
| // The http2 package registers a RoundTripper with Transport.RegisterProtocol. |
| // If this RoundTripper implements newClientConner, then Transport.NewClientConn will use |
| // it to create new HTTP/2 client connections. |
| type http_newClientConner interface { |
| // NewClientConn creates a new client connection from a net.Conn. |
| // |
| // The RoundTripper returned by NewClientConn must implement genericClientConn. |
| // (We don't define NewClientConn as returning genericClientConn, |
| // because either we'd need to make genericClientConn an exported type |
| // or define it as a type alias. Neither is particularly appealing.) |
| // |
| // The state hook passed here is the internal state hook |
| // (ClientConn.maybeRunStateHook). The internal state hook calls |
| // the user state hook (if any), which is set by the user with |
| // ClientConn.SetStateHook. |
| // |
| // The client connection should arrange to call the internal state hook |
| // when the connection closes, when requests complete, and when the |
| // connection concurrency limit changes. |
| // |
| // The client connection must call the internal state hook when the connection state |
| // changes asynchronously, such as when a request completes. |
| // |
| // The internal state hook need not be called after synchronous changes to the state: |
| // Close, Reserve, Release, and RoundTrip calls which don't start a request |
| // do not need to call the hook. |
| // |
| // The general idea is that if we call (for example) Close, |
| // we know that the connection state has probably changed and we |
| // don't need the state hook to tell us that. |
| // However, if the connection closes asynchronously |
| // (because, for example, the other end of the conn closed it), |
| // the state hook needs to inform us. |
| NewClientConn(nc net.Conn, internalStateHook func()) (http_RoundTripper, error) |
| } |
| |
| // genericClientConn is an interface implemented by HTTP/2 client conns |
| // returned from newClientConner.NewClientConn. |
| // |
| // See the newClientConner doc comment for more information. |
| type http_genericClientConn interface { |
| Close() error |
| Err() error |
| RoundTrip(req *http_Request) (*http_Response, error) |
| Reserve() error |
| Release() |
| Available() int |
| InFlight() int |
| } |
| |
| // NewClientConn creates a new client connection to the given address. |
| // |
| // If scheme is "http", the connection is unencrypted. |
| // If scheme is "https", the connection uses TLS. |
| // |
| // The protocol used for the new connection is determined by the scheme, |
| // Transport.Protocols configuration field, and protocols supported by the |
| // server. See Transport.Protocols for more details. |
| // |
| // If Transport.Proxy is set and indicates that a request sent to the given |
| // address should use a proxy, the new connection uses that proxy. |
| // |
| // NewClientConn always creates a new connection, |
| // even if the Transport has an existing cached connection to the given host. |
| // |
| // The new connection is not added to the Transport's connection cache, |
| // and will not be used by [Transport.RoundTrip]. |
| // It does not count against the MaxIdleConns and MaxConnsPerHost limits. |
| // |
| // The caller is responsible for closing the new connection. |
| func (t *http_Transport) NewClientConn(ctx context.Context, scheme, address string) (*http_ClientConn, error) { |
| t.nextProtoOnce.Do(t.onceSetNextProtoDefaults) |
| |
| if t.h2Config != nil { |
| // Handle x/net/http2.Transport.NewClientConn passing us a net.Conn |
| // to create a ClientConn from. |
| if cc, err := t.http2NewClientConnFromContext(ctx); err != errors.ErrUnsupported { |
| return cc, err |
| } |
| } |
| |
| switch scheme { |
| case "http", "https": |
| default: |
| return nil, fmt.Errorf("net/http: invalid scheme %q", scheme) |
| } |
| |
| host, port, err := net.SplitHostPort(address) |
| if err != nil { |
| return nil, err |
| } |
| if port == "" { |
| port = http_schemePort(scheme) |
| } |
| |
| var proxyURL *url.URL |
| if t.Proxy != nil { |
| // Transport.Proxy takes a *Request, so create a fake one to pass it. |
| req := &http_Request{ |
| ctx: ctx, |
| Method: "GET", |
| URL: &url.URL{ |
| Scheme: scheme, |
| Host: host, |
| Path: "/", |
| }, |
| Proto: "HTTP/1.1", |
| ProtoMajor: 1, |
| ProtoMinor: 1, |
| Header: make(http_Header), |
| Body: http_NoBody, |
| Host: host, |
| } |
| var err error |
| proxyURL, err = t.Proxy(req) |
| if err != nil { |
| return nil, err |
| } |
| } |
| |
| cm := http_connectMethod{ |
| targetScheme: scheme, |
| targetAddr: net.JoinHostPort(host, port), |
| proxyURL: proxyURL, |
| } |
| |
| // The state hook is a bit tricky: |
| // The persistConn has a state hook which calls ClientConn.maybeRunStateHook, |
| // which in turn calls the user-provided state hook (if any). |
| // |
| // ClientConn.maybeRunStateHook handles debouncing hook calls for both |
| // HTTP/1 and HTTP/2. |
| // |
| // Since there's no need to change the persistConn's hook, we set it at creation time. |
| cc := &http_ClientConn{} |
| const isClientConn = true |
| pconn, err := t.dialConn(ctx, cm, isClientConn, cc.maybeRunStateHook) |
| if err != nil { |
| return nil, err |
| } |
| |
| // Note that cc.maybeRunStateHook may have been called |
| // in the short window between dialConn and now. |
| // This is fine. |
| cc.stateHookMu.Lock() |
| defer cc.stateHookMu.Unlock() |
| if pconn.alt != nil { |
| // If pconn.alt is set, this is a connection implemented in another package |
| // (probably x/net/http2) or the bundled copy in h2_bundle.go. |
| gc, ok := pconn.alt.(http_genericClientConn) |
| if !ok { |
| return nil, errors.New("http: NewClientConn returned something that is not a ClientConn") |
| } |
| cc.cc = gc |
| cc.lastAvailable = gc.Available() |
| } else { |
| // This is an HTTP/1 connection. |
| pconn.availch = make(chan struct{}, 1) |
| pconn.availch <- struct{}{} |
| cc.cc = http_http1ClientConn{pconn} |
| cc.lastAvailable = 1 |
| } |
| return cc, nil |
| } |
| |
| // Close closes the connection. |
| // Outstanding RoundTrip calls are interrupted. |
| func (cc *http_ClientConn) Close() error { |
| defer cc.maybeRunStateHook() |
| return cc.cc.Close() |
| } |
| |
| // Err reports any fatal connection errors. |
| // It returns nil if the connection is usable. |
| // If it returns non-nil, the connection can no longer be used. |
| func (cc *http_ClientConn) Err() error { |
| return cc.cc.Err() |
| } |
| |
| func http_validateClientConnRequest(req *http_Request) error { |
| if req.URL == nil { |
| return errors.New("http: nil Request.URL") |
| } |
| if req.Header == nil { |
| return errors.New("http: nil Request.Header") |
| } |
| // Validate the outgoing headers. |
| if err := http_validateHeaders(req.Header); err != "" { |
| return fmt.Errorf("http: invalid header %s", err) |
| } |
| // Validate the outgoing trailers too. |
| if err := http_validateHeaders(req.Trailer); err != "" { |
| return fmt.Errorf("http: invalid trailer %s", err) |
| } |
| if req.Method != "" && !http_validMethod(req.Method) { |
| return fmt.Errorf("http: invalid method %q", req.Method) |
| } |
| if req.URL.Host == "" { |
| return errors.New("http: no Host in request URL") |
| } |
| return nil |
| } |
| |
| // RoundTrip implements the [RoundTripper] interface. |
| // |
| // The request is sent on the client connection, |
| // regardless of the URL being requested or any proxy settings. |
| // |
| // If the connection is at its concurrency limit, |
| // RoundTrip waits for the connection to become available |
| // before sending the request. |
| func (cc *http_ClientConn) RoundTrip(req *http_Request) (*http_Response, error) { |
| defer cc.maybeRunStateHook() |
| if req.URL == nil && req.Method == ":ping" { |
| // Undocumented feature for sending a PING frame to a HTTP/2 connection, |
| // included to support x/net/http2.ClientConn.Ping. |
| pinger, ok := cc.cc.(interface { |
| Ping(context.Context) error |
| }) |
| if !ok { |
| return nil, errors.New("http: ClientConn does not support PING") |
| } |
| return nil, pinger.Ping(req.Context()) |
| } |
| if err := http_validateClientConnRequest(req); err != nil { |
| cc.Release() |
| return nil, err |
| } |
| return cc.cc.RoundTrip(req) |
| } |
| |
| // Available reports the number of requests that may be sent |
| // to the connection without blocking. |
| // It returns 0 if the connection is closed. |
| func (cc *http_ClientConn) Available() int { |
| return cc.cc.Available() |
| } |
| |
| // InFlight reports the number of requests in flight, |
| // including reserved requests. |
| // It returns 0 if the connection is closed. |
| func (cc *http_ClientConn) InFlight() int { |
| return cc.cc.InFlight() |
| } |
| |
| // Reserve reserves a concurrency slot on the connection. |
| // If Reserve returns nil, one additional RoundTrip call may be made |
| // without waiting for an existing request to complete. |
| // |
| // The reserved concurrency slot is accounted as an in-flight request. |
| // A successful call to RoundTrip will decrement the Available count |
| // and increment the InFlight count. |
| // |
| // Each successful call to Reserve should be followed by exactly one call |
| // to RoundTrip or Release, which will consume or release the reservation. |
| // |
| // If the connection is closed or at its concurrency limit, |
| // Reserve returns an error. |
| func (cc *http_ClientConn) Reserve() error { |
| defer cc.maybeRunStateHook() |
| return cc.cc.Reserve() |
| } |
| |
| // Release releases an unused concurrency slot reserved by Reserve. |
| // If there are no reserved concurrency slots, it has no effect. |
| func (cc *http_ClientConn) Release() { |
| defer cc.maybeRunStateHook() |
| cc.cc.Release() |
| } |
| |
| // shouldRunStateHook returns the user's state hook if we should call it, |
| // or nil if we don't need to call it at this time. |
| func (cc *http_ClientConn) shouldRunStateHook(stopRunning bool) func(*http_ClientConn) { |
| cc.stateHookMu.Lock() |
| defer cc.stateHookMu.Unlock() |
| if cc.cc == nil { |
| return nil |
| } |
| if stopRunning { |
| cc.stateHookRunning = false |
| } |
| if cc.userStateHook == nil { |
| return nil |
| } |
| if cc.stateHookRunning { |
| return nil |
| } |
| var ( |
| available = cc.Available() |
| inFlight = cc.InFlight() |
| closed = cc.Err() != nil |
| ) |
| var hook func(*http_ClientConn) |
| if available > cc.lastAvailable || inFlight < cc.lastInFlight || closed != cc.lastClosed { |
| hook = cc.userStateHook |
| cc.stateHookRunning = true |
| } |
| cc.lastAvailable = available |
| cc.lastInFlight = inFlight |
| cc.lastClosed = closed |
| return hook |
| } |
| |
| func (cc *http_ClientConn) maybeRunStateHook() { |
| hook := cc.shouldRunStateHook(false) |
| if hook == nil { |
| return |
| } |
| // Run the hook synchronously. |
| // |
| // This means that if, for example, the user calls resp.Body.Close to finish a request, |
| // the Close call will synchronously run the hook, giving the hook the chance to |
| // return the ClientConn to a connection pool before the next request is made. |
| hook(cc) |
| // The connection state may have changed while the hook was running, |
| // in which case we need to run it again. |
| // |
| // If we do need to run the hook again, do so in a new goroutine to avoid blocking |
| // the current goroutine indefinitely. |
| hook = cc.shouldRunStateHook(true) |
| if hook != nil { |
| go func() { |
| for hook != nil { |
| hook(cc) |
| hook = cc.shouldRunStateHook(true) |
| } |
| }() |
| } |
| } |
| |
| // SetStateHook arranges for f to be called when the state of the connection changes. |
| // At most one call to f is made at a time. |
| // If the connection's state has changed since it was created, |
| // f is called immediately in a separate goroutine. |
| // f may be called synchronously from RoundTrip or Response.Body.Close. |
| // |
| // If SetStateHook is called multiple times, the new hook replaces the old one. |
| // If f is nil, no further calls will be made to f after SetStateHook returns. |
| // |
| // f is called when Available increases (more requests may be sent on the connection), |
| // InFlight decreases (existing requests complete), or Err begins returning non-nil |
| // (the connection is no longer usable). |
| func (cc *http_ClientConn) SetStateHook(f func(*http_ClientConn)) { |
| cc.stateHookMu.Lock() |
| cc.userStateHook = f |
| cc.stateHookMu.Unlock() |
| cc.maybeRunStateHook() |
| } |
| |
| // http1ClientConn is a genericClientConn implementation backed by |
| // an HTTP/1 *persistConn (pconn.alt is nil). |
| type http_http1ClientConn struct { |
| pconn *http_persistConn |
| } |
| |
| func (cc http_http1ClientConn) RoundTrip(req *http_Request) (*http_Response, error) { |
| ctx := req.Context() |
| trace := httptrace.ContextClientTrace(ctx) |
| |
| // Convert Request.Cancel into context cancellation. |
| ctx, cancel := context.WithCancelCause(req.Context()) |
| if req.Cancel != nil { |
| go http_awaitLegacyCancel(ctx, cancel, req) |
| } |
| |
| treq := &http_transportRequest{http_Request: req, trace: trace, ctx: ctx, cancel: cancel} |
| resp, err := cc.pconn.roundTrip(treq) |
| if err != nil { |
| return nil, err |
| } |
| resp.Request = req |
| return resp, nil |
| } |
| |
| func (cc http_http1ClientConn) Close() error { |
| cc.pconn.close(errors.New("ClientConn closed")) |
| return nil |
| } |
| |
| func (cc http_http1ClientConn) Err() error { |
| select { |
| case <-cc.pconn.closech: |
| return cc.pconn.closed |
| default: |
| return nil |
| } |
| } |
| |
| func (cc http_http1ClientConn) Available() int { |
| cc.pconn.mu.Lock() |
| defer cc.pconn.mu.Unlock() |
| if cc.pconn.closed != nil || cc.pconn.reserved || cc.pconn.inFlight { |
| return 0 |
| } |
| return 1 |
| } |
| |
| func (cc http_http1ClientConn) InFlight() int { |
| cc.pconn.mu.Lock() |
| defer cc.pconn.mu.Unlock() |
| if cc.pconn.closed == nil && (cc.pconn.reserved || cc.pconn.inFlight) { |
| return 1 |
| } |
| return 0 |
| } |
| |
| func (cc http_http1ClientConn) Reserve() error { |
| cc.pconn.mu.Lock() |
| defer cc.pconn.mu.Unlock() |
| if cc.pconn.closed != nil { |
| return cc.pconn.closed |
| } |
| select { |
| case <-cc.pconn.availch: |
| default: |
| return errors.New("connection is unavailable") |
| } |
| cc.pconn.reserved = true |
| return nil |
| } |
| |
| func (cc http_http1ClientConn) Release() { |
| cc.pconn.mu.Lock() |
| defer cc.pconn.mu.Unlock() |
| if cc.pconn.reserved { |
| select { |
| case cc.pconn.availch <- struct{}{}: |
| default: |
| panic("cannot release reservation") |
| } |
| cc.pconn.reserved = false |
| } |
| } |
| |
| // cloneURLValues should be an internal detail, |
| // but widely used packages access it using linkname. |
| // Notable members of the hall of shame include: |
| // - github.com/searKing/golang |
| // |
| // Do not remove or change the type signature. |
| // See go.dev/issue/67401. |
| // |
| //go:linkname cloneURLValues |
| func http_cloneURLValues(v url.Values) url.Values { |
| if v == nil { |
| return nil |
| } |
| // http.Header and url.Values have the same representation, so temporarily |
| // treat it like http.Header, which does have a clone: |
| return url.Values(http_Header(v).Clone()) |
| } |
| |
| // cloneURL should be an internal detail, |
| // but widely used packages access it using linkname. |
| // Notable members of the hall of shame include: |
| // - github.com/searKing/golang |
| // |
| // Do not remove or change the type signature. |
| // See go.dev/issue/67401. |
| // |
| //go:linkname cloneURL |
| func http_cloneURL(u *url.URL) *url.URL { |
| return u.Clone() |
| } |
| |
| // cloneMultipartForm should be an internal detail, |
| // but widely used packages access it using linkname. |
| // Notable members of the hall of shame include: |
| // - github.com/searKing/golang |
| // |
| // Do not remove or change the type signature. |
| // See go.dev/issue/67401. |
| // |
| //go:linkname cloneMultipartForm |
| func http_cloneMultipartForm(f *multipart.Form) *multipart.Form { |
| if f == nil { |
| return nil |
| } |
| f2 := &multipart.Form{ |
| Value: (map[string][]string)(http_Header(f.Value).Clone()), |
| } |
| if f.File != nil { |
| m := make(map[string][]*multipart.FileHeader, len(f.File)) |
| for k, vv := range f.File { |
| vv2 := make([]*multipart.FileHeader, len(vv)) |
| for i, v := range vv { |
| vv2[i] = http_cloneMultipartFileHeader(v) |
| } |
| m[k] = vv2 |
| } |
| f2.File = m |
| } |
| return f2 |
| } |
| |
| // cloneMultipartFileHeader should be an internal detail, |
| // but widely used packages access it using linkname. |
| // Notable members of the hall of shame include: |
| // - github.com/searKing/golang |
| // |
| // Do not remove or change the type signature. |
| // See go.dev/issue/67401. |
| // |
| //go:linkname cloneMultipartFileHeader |
| func http_cloneMultipartFileHeader(fh *multipart.FileHeader) *multipart.FileHeader { |
| if fh == nil { |
| return nil |
| } |
| fh2 := new(multipart.FileHeader) |
| *fh2 = *fh |
| fh2.Header = textproto.MIMEHeader(http_Header(fh.Header).Clone()) |
| return fh2 |
| } |
| |
| // cloneOrMakeHeader invokes Header.Clone but if the |
| // result is nil, it'll instead make and return a non-nil Header. |
| // |
| // cloneOrMakeHeader should be an internal detail, |
| // but widely used packages access it using linkname. |
| // Notable members of the hall of shame include: |
| // - github.com/searKing/golang |
| // |
| // Do not remove or change the type signature. |
| // See go.dev/issue/67401. |
| // |
| //go:linkname cloneOrMakeHeader |
| func http_cloneOrMakeHeader(hdr http_Header) http_Header { |
| clone := hdr.Clone() |
| if clone == nil { |
| clone = make(http_Header) |
| } |
| return clone |
| } |
| |
| var http_httpcookiemaxnum = godebug.New("httpcookiemaxnum") |
| |
| // A Cookie represents an HTTP cookie as sent in the Set-Cookie header of an |
| // HTTP response or the Cookie header of an HTTP request. |
| // |
| // See https://tools.ietf.org/html/rfc6265 for details. |
| type http_Cookie struct { |
| Name string |
| Value string |
| Quoted bool // indicates whether the Value was originally quoted |
| |
| Path string // optional |
| Domain string // optional |
| Expires time.Time // optional |
| RawExpires string // for reading cookies only |
| |
| // MaxAge=0 means no 'Max-Age' attribute specified. |
| // MaxAge<0 means delete cookie now, equivalently 'Max-Age: 0' |
| // MaxAge>0 means Max-Age attribute present and given in seconds |
| MaxAge int |
| Secure bool |
| HttpOnly bool |
| SameSite http_SameSite |
| Partitioned bool |
| Raw string |
| Unparsed []string // Raw text of unparsed attribute-value pairs |
| } |
| |
| // SameSite allows a server to define a cookie attribute making it impossible for |
| // the browser to send this cookie along with cross-site requests. The main |
| // goal is to mitigate the risk of cross-origin information leakage, and provide |
| // some protection against cross-site request forgery attacks. |
| // |
| // See https://tools.ietf.org/html/draft-ietf-httpbis-cookie-same-site-00 for details. |
| type http_SameSite int |
| |
| const ( |
| http_SameSiteDefaultMode http_SameSite = iota + 1 |
| http_SameSiteLaxMode |
| http_SameSiteStrictMode |
| http_SameSiteNoneMode |
| ) |
| |
| var ( |
| http_errBlankCookie = errors.New("http: blank cookie") |
| http_errEqualNotFoundInCookie = errors.New("http: '=' not found in cookie") |
| http_errInvalidCookieName = errors.New("http: invalid cookie name") |
| http_errInvalidCookieValue = errors.New("http: invalid cookie value") |
| http_errCookieNumLimitExceeded = errors.New("http: number of cookies exceeded limit") |
| ) |
| |
| const http_defaultCookieMaxNum = 3000 |
| |
| func http_cookieNumWithinMax(cookieNum int) bool { |
| withinDefaultMax := cookieNum <= http_defaultCookieMaxNum |
| if http_httpcookiemaxnum.Value() == "" { |
| return withinDefaultMax |
| } |
| if customMax, err := strconv.Atoi(http_httpcookiemaxnum.Value()); err == nil { |
| withinCustomMax := customMax == 0 || cookieNum <= customMax |
| if withinDefaultMax != withinCustomMax { |
| http_httpcookiemaxnum.IncNonDefault() |
| } |
| return withinCustomMax |
| } |
| return withinDefaultMax |
| } |
| |
| // ParseCookie parses a Cookie header value and returns all the cookies |
| // which were set in it. Since the same cookie name can appear multiple times |
| // the returned Values can contain more than one value for a given key. |
| func http_ParseCookie(line string) ([]*http_Cookie, error) { |
| nparts := strings.Count(line, ";") + 1 |
| if !http_cookieNumWithinMax(nparts) { |
| return nil, http_errCookieNumLimitExceeded |
| } else if nparts == 1 && textproto.TrimString(line) == "" { |
| return nil, http_errBlankCookie |
| } |
| cookies := make([]*http_Cookie, 0, nparts) |
| for s := range strings.SplitSeq(line, ";") { |
| s = textproto.TrimString(s) |
| name, value, found := strings.Cut(s, "=") |
| if !found { |
| return nil, http_errEqualNotFoundInCookie |
| } |
| if !http_isToken(name) { |
| return nil, http_errInvalidCookieName |
| } |
| value, quoted, found := http_parseCookieValue(value, true) |
| if !found { |
| return nil, http_errInvalidCookieValue |
| } |
| cookies = append(cookies, &http_Cookie{Name: name, Value: value, Quoted: quoted}) |
| } |
| return cookies, nil |
| } |
| |
| // ParseSetCookie parses a Set-Cookie header value and returns a cookie. |
| // It returns an error on syntax error. |
| func http_ParseSetCookie(line string) (*http_Cookie, error) { |
| parts := strings.Split(textproto.TrimString(line), ";") |
| if len(parts) == 1 && parts[0] == "" { |
| return nil, http_errBlankCookie |
| } |
| parts[0] = textproto.TrimString(parts[0]) |
| name, value, ok := strings.Cut(parts[0], "=") |
| if !ok { |
| return nil, http_errEqualNotFoundInCookie |
| } |
| name = textproto.TrimString(name) |
| if !http_isToken(name) { |
| return nil, http_errInvalidCookieName |
| } |
| value, quoted, ok := http_parseCookieValue(value, true) |
| if !ok { |
| return nil, http_errInvalidCookieValue |
| } |
| c := &http_Cookie{ |
| Name: name, |
| Value: value, |
| Quoted: quoted, |
| Raw: line, |
| } |
| for i := 1; i < len(parts); i++ { |
| parts[i] = textproto.TrimString(parts[i]) |
| if len(parts[i]) == 0 { |
| continue |
| } |
| |
| attr, val, _ := strings.Cut(parts[i], "=") |
| lowerAttr, isASCII := ascii.ToLower(attr) |
| if !isASCII { |
| continue |
| } |
| val, _, ok = http_parseCookieValue(val, false) |
| if !ok { |
| c.Unparsed = append(c.Unparsed, parts[i]) |
| continue |
| } |
| |
| switch lowerAttr { |
| case "samesite": |
| lowerVal, ascii := ascii.ToLower(val) |
| if !ascii { |
| c.SameSite = http_SameSiteDefaultMode |
| continue |
| } |
| switch lowerVal { |
| case "lax": |
| c.SameSite = http_SameSiteLaxMode |
| case "strict": |
| c.SameSite = http_SameSiteStrictMode |
| case "none": |
| c.SameSite = http_SameSiteNoneMode |
| default: |
| c.SameSite = http_SameSiteDefaultMode |
| } |
| continue |
| case "secure": |
| c.Secure = true |
| continue |
| case "httponly": |
| c.HttpOnly = true |
| continue |
| case "domain": |
| c.Domain = val |
| continue |
| case "max-age": |
| secs, err := strconv.Atoi(val) |
| if err != nil || secs != 0 && val[0] == '0' { |
| break |
| } |
| if secs <= 0 { |
| secs = -1 |
| } |
| c.MaxAge = secs |
| continue |
| case "expires": |
| c.RawExpires = val |
| exptime, err := time.Parse(time.RFC1123, val) |
| if err != nil { |
| exptime, err = time.Parse("Mon, 02-Jan-2006 15:04:05 MST", val) |
| if err != nil { |
| c.Expires = time.Time{} |
| break |
| } |
| } |
| c.Expires = exptime.UTC() |
| continue |
| case "path": |
| c.Path = val |
| continue |
| case "partitioned": |
| c.Partitioned = true |
| continue |
| } |
| c.Unparsed = append(c.Unparsed, parts[i]) |
| } |
| return c, nil |
| } |
| |
| // readSetCookies parses all "Set-Cookie" values from |
| // the header h and returns the successfully parsed Cookies. |
| // |
| // If the amount of cookies exceeds CookieNumLimit, and httpcookielimitnum |
| // GODEBUG option is not explicitly turned off, this function will silently |
| // fail and return an empty slice. |
| func http_readSetCookies(h http_Header) []*http_Cookie { |
| cookieCount := len(h["Set-Cookie"]) |
| if cookieCount == 0 { |
| return []*http_Cookie{} |
| } |
| // Cookie limit was unfortunately introduced at a later point in time. |
| // As such, we can only fail by returning an empty slice rather than |
| // explicit error. |
| if !http_cookieNumWithinMax(cookieCount) { |
| return []*http_Cookie{} |
| } |
| cookies := make([]*http_Cookie, 0, cookieCount) |
| for _, line := range h["Set-Cookie"] { |
| if cookie, err := http_ParseSetCookie(line); err == nil { |
| cookies = append(cookies, cookie) |
| } |
| } |
| return cookies |
| } |
| |
| // SetCookie adds a Set-Cookie header to the provided [ResponseWriter]'s headers. |
| // The provided cookie must have a valid Name. Invalid cookies may be |
| // silently dropped. |
| func http_SetCookie(w http_ResponseWriter, cookie *http_Cookie) { |
| if v := cookie.String(); v != "" { |
| w.Header().Add("Set-Cookie", v) |
| } |
| } |
| |
| // String returns the serialization of the cookie for use in a [Cookie] |
| // header (if only Name and Value are set) or a Set-Cookie response |
| // header (if other fields are set). |
| // If c is nil or c.Name is invalid, the empty string is returned. |
| func (c *http_Cookie) String() string { |
| if c == nil || !http_isToken(c.Name) { |
| return "" |
| } |
| // extraCookieLength derived from typical length of cookie attributes |
| // see RFC 6265 Sec 4.1. |
| const extraCookieLength = 110 |
| var b strings.Builder |
| b.Grow(len(c.Name) + len(c.Value) + len(c.Domain) + len(c.Path) + extraCookieLength) |
| b.WriteString(c.Name) |
| b.WriteRune('=') |
| b.WriteString(http_sanitizeCookieValue(c.Value, c.Quoted)) |
| |
| if len(c.Path) > 0 { |
| b.WriteString("; Path=") |
| b.WriteString(http_sanitizeCookiePath(c.Path)) |
| } |
| if len(c.Domain) > 0 { |
| if http_validCookieDomain(c.Domain) { |
| // A c.Domain containing illegal characters is not |
| // sanitized but simply dropped which turns the cookie |
| // into a host-only cookie. A leading dot is okay |
| // but won't be sent. |
| d := c.Domain |
| if d[0] == '.' { |
| d = d[1:] |
| } |
| b.WriteString("; Domain=") |
| b.WriteString(d) |
| } else { |
| log.Printf("net/http: invalid Cookie.Domain %q; dropping domain attribute", c.Domain) |
| } |
| } |
| var buf [len(http_TimeFormat)]byte |
| if http_validCookieExpires(c.Expires) { |
| b.WriteString("; Expires=") |
| b.Write(c.Expires.UTC().AppendFormat(buf[:0], http_TimeFormat)) |
| } |
| if c.MaxAge > 0 { |
| b.WriteString("; Max-Age=") |
| b.Write(strconv.AppendInt(buf[:0], int64(c.MaxAge), 10)) |
| } else if c.MaxAge < 0 { |
| b.WriteString("; Max-Age=0") |
| } |
| if c.HttpOnly { |
| b.WriteString("; HttpOnly") |
| } |
| if c.Secure { |
| b.WriteString("; Secure") |
| } |
| switch c.SameSite { |
| case http_SameSiteDefaultMode: |
| // Skip, default mode is obtained by not emitting the attribute. |
| case http_SameSiteNoneMode: |
| b.WriteString("; SameSite=None") |
| case http_SameSiteLaxMode: |
| b.WriteString("; SameSite=Lax") |
| case http_SameSiteStrictMode: |
| b.WriteString("; SameSite=Strict") |
| } |
| if c.Partitioned { |
| b.WriteString("; Partitioned") |
| } |
| return b.String() |
| } |
| |
| // Valid reports whether the cookie is valid. |
| func (c *http_Cookie) Valid() error { |
| if c == nil { |
| return errors.New("http: nil Cookie") |
| } |
| if !http_isToken(c.Name) { |
| return errors.New("http: invalid Cookie.Name") |
| } |
| if !c.Expires.IsZero() && !http_validCookieExpires(c.Expires) { |
| return errors.New("http: invalid Cookie.Expires") |
| } |
| for i := 0; i < len(c.Value); i++ { |
| if !http_validCookieValueByte(c.Value[i]) { |
| return fmt.Errorf("http: invalid byte %q in Cookie.Value", c.Value[i]) |
| } |
| } |
| if len(c.Path) > 0 { |
| for i := 0; i < len(c.Path); i++ { |
| if !http_validCookiePathByte(c.Path[i]) { |
| return fmt.Errorf("http: invalid byte %q in Cookie.Path", c.Path[i]) |
| } |
| } |
| } |
| if len(c.Domain) > 0 { |
| if !http_validCookieDomain(c.Domain) { |
| return errors.New("http: invalid Cookie.Domain") |
| } |
| } |
| if c.Partitioned { |
| if !c.Secure { |
| return errors.New("http: partitioned cookies must be set with Secure") |
| } |
| } |
| return nil |
| } |
| |
| // readCookies parses all "Cookie" values from the header h and |
| // returns the successfully parsed Cookies. |
| // |
| // If filter isn't empty, only cookies of that name are returned. |
| // |
| // If the amount of cookies exceeds CookieNumLimit, and httpcookielimitnum |
| // GODEBUG option is not explicitly turned off, this function will silently |
| // fail and return an empty slice. |
| func http_readCookies(h http_Header, filter string) []*http_Cookie { |
| lines := h["Cookie"] |
| if len(lines) == 0 { |
| return []*http_Cookie{} |
| } |
| |
| // Cookie limit was unfortunately introduced at a later point in time. |
| // As such, we can only fail by returning an empty slice rather than |
| // explicit error. |
| cookieCount := 0 |
| for _, line := range lines { |
| cookieCount += strings.Count(line, ";") + 1 |
| } |
| if !http_cookieNumWithinMax(cookieCount) { |
| return []*http_Cookie{} |
| } |
| |
| cookies := make([]*http_Cookie, 0, len(lines)+strings.Count(lines[0], ";")) |
| for _, line := range lines { |
| line = textproto.TrimString(line) |
| |
| var part string |
| for len(line) > 0 { // continue since we have rest |
| part, line, _ = strings.Cut(line, ";") |
| part = textproto.TrimString(part) |
| if part == "" { |
| continue |
| } |
| name, val, _ := strings.Cut(part, "=") |
| name = textproto.TrimString(name) |
| if !http_isToken(name) { |
| continue |
| } |
| if filter != "" && filter != name { |
| continue |
| } |
| val, quoted, ok := http_parseCookieValue(val, true) |
| if !ok { |
| continue |
| } |
| cookies = append(cookies, &http_Cookie{Name: name, Value: val, Quoted: quoted}) |
| } |
| } |
| return cookies |
| } |
| |
| // validCookieDomain reports whether v is a valid cookie domain-value. |
| func http_validCookieDomain(v string) bool { |
| if http_isCookieDomainName(v) { |
| return true |
| } |
| if net.ParseIP(v) != nil && !strings.Contains(v, ":") { |
| return true |
| } |
| return false |
| } |
| |
| // validCookieExpires reports whether v is a valid cookie expires-value. |
| func http_validCookieExpires(t time.Time) bool { |
| // IETF RFC 6265 Section 5.1.1.5, the year must not be less than 1601 |
| return t.Year() >= 1601 |
| } |
| |
| // isCookieDomainName reports whether s is a valid domain name or a valid |
| // domain name with a leading dot '.'. It is almost a direct copy of |
| // package net's isDomainName. |
| func http_isCookieDomainName(s string) bool { |
| if len(s) == 0 { |
| return false |
| } |
| if len(s) > 255 { |
| return false |
| } |
| |
| if s[0] == '.' { |
| // A cookie domain attribute may start with a leading dot. |
| // Per RFC 6265 section 5.2.3, a leading dot is ignored. |
| s = s[1:] |
| } |
| last := byte('.') |
| ok := false // Ok once we've seen a letter. |
| partlen := 0 |
| for i := 0; i < len(s); i++ { |
| c := s[i] |
| switch { |
| default: |
| return false |
| case 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z': |
| // No '_' allowed here (in contrast to package net). |
| ok = true |
| partlen++ |
| case '0' <= c && c <= '9': |
| // fine |
| partlen++ |
| case c == '-': |
| // Byte before dash cannot be dot. |
| if last == '.' { |
| return false |
| } |
| partlen++ |
| case c == '.': |
| // Byte before dot cannot be dot, dash. |
| if last == '.' || last == '-' { |
| return false |
| } |
| if partlen > 63 || partlen == 0 { |
| return false |
| } |
| partlen = 0 |
| } |
| last = c |
| } |
| if last == '-' || partlen > 63 { |
| return false |
| } |
| |
| return ok |
| } |
| |
| var http_cookieNameSanitizer = strings.NewReplacer("\n", "-", "\r", "-") |
| |
| func http_sanitizeCookieName(n string) string { |
| return http_cookieNameSanitizer.Replace(n) |
| } |
| |
| // sanitizeCookieValue produces a suitable cookie-value from v. |
| // It receives a quoted bool indicating whether the value was originally |
| // quoted. |
| // https://tools.ietf.org/html/rfc6265#section-4.1.1 |
| // |
| // cookie-value = *cookie-octet / ( DQUOTE *cookie-octet DQUOTE ) |
| // cookie-octet = %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E |
| // ; US-ASCII characters excluding CTLs, |
| // ; whitespace DQUOTE, comma, semicolon, |
| // ; and backslash |
| // |
| // We loosen this as spaces and commas are common in cookie values |
| // thus we produce a quoted cookie-value if v contains commas or spaces. |
| // See https://golang.org/issue/7243 for the discussion. |
| func http_sanitizeCookieValue(v string, quoted bool) string { |
| v = http_sanitizeOrWarn("Cookie.Value", http_validCookieValueByte, v) |
| if strings.ContainsAny(v, " ,") || quoted { |
| return ` + "`" + `"` + "`" + ` + v + ` + "`" + `"` + "`" + ` |
| } |
| return v |
| } |
| |
| func http_validCookieValueByte(b byte) bool { |
| return 0x20 <= b && b < 0x7f && b != '"' && b != ';' && b != '\\' |
| } |
| |
| // path-av = "Path=" path-value |
| // path-value = <any CHAR except CTLs or ";"> |
| func http_sanitizeCookiePath(v string) string { |
| return http_sanitizeOrWarn("Cookie.Path", http_validCookiePathByte, v) |
| } |
| |
| func http_validCookiePathByte(b byte) bool { |
| return 0x20 <= b && b < 0x7f && b != ';' |
| } |
| |
| func http_sanitizeOrWarn(fieldName string, valid func(byte) bool, v string) string { |
| ok := true |
| for i := 0; i < len(v); i++ { |
| if valid(v[i]) { |
| continue |
| } |
| log.Printf("net/http: invalid byte %q in %s; dropping invalid bytes", v[i], fieldName) |
| ok = false |
| break |
| } |
| if ok { |
| return v |
| } |
| buf := make([]byte, 0, len(v)) |
| for i := 0; i < len(v); i++ { |
| if b := v[i]; valid(b) { |
| buf = append(buf, b) |
| } |
| } |
| return string(buf) |
| } |
| |
| // parseCookieValue parses a cookie value according to RFC 6265. |
| // If allowDoubleQuote is true, parseCookieValue will consider that it |
| // is parsing the cookie-value; |
| // otherwise, it will consider that it is parsing a cookie-av value |
| // (cookie attribute-value). |
| // |
| // It returns the parsed cookie value, a boolean indicating whether the |
| // parsing was successful, and a boolean indicating whether the parsed |
| // value was enclosed in double quotes. |
| func http_parseCookieValue(raw string, allowDoubleQuote bool) (value string, quoted, ok bool) { |
| // Strip the quotes, if present. |
| if allowDoubleQuote && len(raw) > 1 && raw[0] == '"' && raw[len(raw)-1] == '"' { |
| raw = raw[1 : len(raw)-1] |
| quoted = true |
| } |
| for i := 0; i < len(raw); i++ { |
| if !http_validCookieValueByte(raw[i]) { |
| return "", quoted, false |
| } |
| } |
| return raw, quoted, true |
| } |
| |
| // CrossOriginProtection implements protections against [Cross-Site Request |
| // Forgery (CSRF)] by rejecting non-safe cross-origin browser requests. |
| // |
| // Cross-origin requests are currently detected with the [Sec-Fetch-Site] |
| // header, available in all browsers since 2023, or by comparing the hostname of |
| // the [Origin] header with the Host header. |
| // |
| // The GET, HEAD, and OPTIONS methods are [safe methods] and are always allowed. |
| // It's important that applications do not perform any state changing actions |
| // due to requests with safe methods. |
| // |
| // Requests without Sec-Fetch-Site or Origin headers are currently assumed to be |
| // either same-origin or non-browser requests, and are allowed. |
| // |
| // The zero value of CrossOriginProtection is valid and has no trusted origins |
| // or bypass patterns. |
| // |
| // [Sec-Fetch-Site]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Sec-Fetch-Site |
| // [Origin]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Origin |
| // [Cross-Site Request Forgery (CSRF)]: https://developer.mozilla.org/en-US/docs/Web/Security/Attacks/CSRF |
| // [safe methods]: https://developer.mozilla.org/en-US/docs/Glossary/Safe/HTTP |
| type http_CrossOriginProtection struct { |
| bypass atomic.Pointer[http_ServeMux] |
| trustedMu sync.RWMutex |
| trusted map[string]bool |
| deny atomic.Pointer[http_Handler] |
| } |
| |
| // NewCrossOriginProtection returns a new [CrossOriginProtection] value. |
| func http_NewCrossOriginProtection() *http_CrossOriginProtection { |
| return &http_CrossOriginProtection{} |
| } |
| |
| // AddTrustedOrigin allows all requests with an [Origin] header |
| // which exactly matches the given value. |
| // |
| // Origin header values are of the form "scheme://host[:port]". |
| // |
| // AddTrustedOrigin can be called concurrently with other methods |
| // or request handling, and applies to future requests. |
| // |
| // [Origin]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Origin |
| func (c *http_CrossOriginProtection) AddTrustedOrigin(origin string) error { |
| u, err := url.Parse(origin) |
| if err != nil { |
| return fmt.Errorf("invalid origin %q: %w", origin, err) |
| } |
| if u.Scheme == "" { |
| return fmt.Errorf("invalid origin %q: scheme is required", origin) |
| } |
| if u.Host == "" { |
| return fmt.Errorf("invalid origin %q: host is required", origin) |
| } |
| if u.User != nil || u.Path != "" || u.RawQuery != "" || u.Fragment != "" { |
| return fmt.Errorf("invalid origin %q: userinfo, path, query, and fragment are not allowed", origin) |
| } |
| c.trustedMu.Lock() |
| defer c.trustedMu.Unlock() |
| if c.trusted == nil { |
| c.trusted = make(map[string]bool) |
| } |
| c.trusted[origin] = true |
| return nil |
| } |
| |
| type http_noopHandler struct{} |
| |
| func (http_noopHandler) ServeHTTP(http_ResponseWriter, *http_Request) {} |
| |
| var http_sentinelHandler http_Handler = &http_noopHandler{} |
| |
| // AddInsecureBypassPattern permits all requests that match the given pattern. |
| // |
| // The pattern syntax and precedence rules are the same as [ServeMux]. Only |
| // requests that match the pattern directly are permitted. Those that ServeMux |
| // would redirect to a pattern (e.g. after cleaning the path or adding a |
| // trailing slash) are not. |
| // |
| // AddInsecureBypassPattern panics if the pattern conflicts with one already |
| // registered, or if the pattern is syntactically invalid (for example, an |
| // improperly formed wildcard). |
| // |
| // AddInsecureBypassPattern can be called concurrently with other methods or |
| // request handling, and applies to future requests. |
| func (c *http_CrossOriginProtection) AddInsecureBypassPattern(pattern string) { |
| var bypass *http_ServeMux |
| |
| // Lazily initialize c.bypass |
| for { |
| bypass = c.bypass.Load() |
| if bypass != nil { |
| break |
| } |
| bypass = http_NewServeMux() |
| if c.bypass.CompareAndSwap(nil, bypass) { |
| break |
| } |
| } |
| |
| bypass.Handle(pattern, http_sentinelHandler) |
| } |
| |
| // SetDenyHandler sets a handler to invoke when a request is rejected. |
| // The default error handler responds with a 403 Forbidden status. |
| // |
| // SetDenyHandler can be called concurrently with other methods |
| // or request handling, and applies to future requests. |
| // |
| // Check does not call the error handler. |
| func (c *http_CrossOriginProtection) SetDenyHandler(h http_Handler) { |
| if h == nil { |
| c.deny.Store(nil) |
| return |
| } |
| c.deny.Store(&h) |
| } |
| |
| // Check applies cross-origin checks to a request. |
| // It returns an error if the request should be rejected. |
| func (c *http_CrossOriginProtection) Check(req *http_Request) error { |
| switch req.Method { |
| case "GET", "HEAD", "OPTIONS": |
| // Safe methods are always allowed. |
| return nil |
| } |
| |
| switch req.Header.Get("Sec-Fetch-Site") { |
| case "": |
| // No Sec-Fetch-Site header is present. |
| // Fallthrough to check the Origin header. |
| case "same-origin", "none": |
| return nil |
| default: |
| if c.isRequestExempt(req) { |
| return nil |
| } |
| return http_errCrossOriginRequest |
| } |
| |
| origin := req.Header.Get("Origin") |
| if origin == "" { |
| // Neither Sec-Fetch-Site nor Origin headers are present. |
| // Either the request is same-origin or not a browser request. |
| return nil |
| } |
| |
| if o, err := url.Parse(origin); err == nil && o.Host == req.Host { |
| // The Origin header matches the Host header. Note that the Host header |
| // doesn't include the scheme, so we don't know if this might be an |
| // HTTP→HTTPS cross-origin request. We fail open, since all modern |
| // browsers support Sec-Fetch-Site since 2023, and running an older |
| // browser makes a clear security trade-off already. Sites can mitigate |
| // this with HTTP Strict Transport Security (HSTS). |
| return nil |
| } |
| |
| if c.isRequestExempt(req) { |
| return nil |
| } |
| return http_errCrossOriginRequestFromOldBrowser |
| } |
| |
| var ( |
| http_errCrossOriginRequest = errors.New("cross-origin request detected from Sec-Fetch-Site header") |
| http_errCrossOriginRequestFromOldBrowser = errors.New("cross-origin request detected, and/or browser is out of date: " + |
| "Sec-Fetch-Site is missing, and Origin does not match Host") |
| ) |
| |
| // isRequestExempt checks the bypasses which require taking a lock, and should |
| // be deferred until the last moment. |
| func (c *http_CrossOriginProtection) isRequestExempt(req *http_Request) bool { |
| if bypass := c.bypass.Load(); bypass != nil { |
| if h, _ := bypass.Handler(req); h == http_sentinelHandler { |
| // The request matches a bypass pattern. |
| return true |
| } |
| } |
| |
| c.trustedMu.RLock() |
| defer c.trustedMu.RUnlock() |
| origin := req.Header.Get("Origin") |
| // The request matches a trusted origin. |
| return origin != "" && c.trusted[origin] |
| } |
| |
| // Handler returns a handler that applies cross-origin checks |
| // before invoking the handler h. |
| // |
| // If a request fails cross-origin checks, the request is rejected |
| // with a 403 Forbidden status or handled with the handler passed |
| // to [CrossOriginProtection.SetDenyHandler]. |
| func (c *http_CrossOriginProtection) Handler(h http_Handler) http_Handler { |
| return http_HandlerFunc(func(w http_ResponseWriter, r *http_Request) { |
| if err := c.Check(r); err != nil { |
| if deny := c.deny.Load(); deny != nil { |
| (*deny).ServeHTTP(w, r) |
| return |
| } |
| http_Error(w, err.Error(), http_StatusForbidden) |
| return |
| } |
| h.ServeHTTP(w, r) |
| }) |
| } |
| |
| // fileTransport implements RoundTripper for the 'file' protocol. |
| type http_fileTransport struct { |
| fh http_fileHandler |
| } |
| |
| // NewFileTransport returns a new [RoundTripper], serving the provided |
| // [FileSystem]. The returned RoundTripper ignores the URL host in its |
| // incoming requests, as well as most other properties of the |
| // request. |
| // |
| // The typical use case for NewFileTransport is to register the "file" |
| // protocol with a [Transport], as in: |
| // |
| // t := &http.Transport{} |
| // t.RegisterProtocol("file", http.NewFileTransport(http.Dir("/"))) |
| // c := &http.Client{Transport: t} |
| // res, err := c.Get("file:///etc/passwd") |
| // ... |
| func http_NewFileTransport(fs http_FileSystem) http_RoundTripper { |
| return http_fileTransport{http_fileHandler{fs}} |
| } |
| |
| // NewFileTransportFS returns a new [RoundTripper], serving the provided |
| // file system fsys. The returned RoundTripper ignores the URL host in its |
| // incoming requests, as well as most other properties of the |
| // request. The files provided by fsys must implement [io.Seeker]. |
| // |
| // The typical use case for NewFileTransportFS is to register the "file" |
| // protocol with a [Transport], as in: |
| // |
| // fsys := os.DirFS("/") |
| // t := &http.Transport{} |
| // t.RegisterProtocol("file", http.NewFileTransportFS(fsys)) |
| // c := &http.Client{Transport: t} |
| // res, err := c.Get("file:///etc/passwd") |
| // ... |
| func http_NewFileTransportFS(fsys fs.FS) http_RoundTripper { |
| return http_NewFileTransport(http_FS(fsys)) |
| } |
| |
| func (t http_fileTransport) RoundTrip(req *http_Request) (resp *http_Response, err error) { |
| // We start ServeHTTP in a goroutine, which may take a long |
| // time if the file is large. The newPopulateResponseWriter |
| // call returns a channel which either ServeHTTP or finish() |
| // sends our *Response on, once the *Response itself has been |
| // populated (even if the body itself is still being |
| // written to the res.Body, a pipe) |
| rw, resc := http_newPopulateResponseWriter(req) |
| go func() { |
| t.fh.ServeHTTP(rw, req) |
| rw.finish() |
| }() |
| return <-resc, nil |
| } |
| |
| func http_newPopulateResponseWriter(req *http_Request) (*http_populateResponse, <-chan *http_Response) { |
| pr, pw := io.Pipe() |
| rw := &http_populateResponse{ |
| ch: make(chan *http_Response), |
| pw: pw, |
| res: &http_Response{ |
| Proto: "HTTP/1.0", |
| ProtoMajor: 1, |
| Header: make(http_Header), |
| Close: true, |
| Body: pr, |
| Request: req, |
| }, |
| } |
| return rw, rw.ch |
| } |
| |
| // populateResponse is a ResponseWriter that populates the *Response |
| // in res, and writes its body to a pipe connected to the response |
| // body. Once writes begin or finish() is called, the response is sent |
| // on ch. |
| type http_populateResponse struct { |
| res *http_Response |
| ch chan *http_Response |
| wroteHeader bool |
| hasContent bool |
| sentResponse bool |
| pw *io.PipeWriter |
| } |
| |
| func (pr *http_populateResponse) finish() { |
| if !pr.wroteHeader { |
| pr.WriteHeader(500) |
| } |
| if !pr.sentResponse { |
| pr.sendResponse() |
| } |
| pr.pw.Close() |
| } |
| |
| func (pr *http_populateResponse) sendResponse() { |
| if pr.sentResponse { |
| return |
| } |
| pr.sentResponse = true |
| |
| if pr.hasContent { |
| pr.res.ContentLength = -1 |
| } |
| pr.ch <- pr.res |
| } |
| |
| func (pr *http_populateResponse) Header() http_Header { |
| return pr.res.Header |
| } |
| |
| func (pr *http_populateResponse) WriteHeader(code int) { |
| if pr.wroteHeader { |
| return |
| } |
| pr.wroteHeader = true |
| |
| pr.res.StatusCode = code |
| pr.res.Status = fmt.Sprintf("%d %s", code, http_StatusText(code)) |
| } |
| |
| func (pr *http_populateResponse) Write(p []byte) (n int, err error) { |
| if !pr.wroteHeader { |
| pr.WriteHeader(http_StatusOK) |
| } |
| pr.hasContent = true |
| if !pr.sentResponse { |
| pr.sendResponse() |
| } |
| return pr.pw.Write(p) |
| } |
| |
| // A Dir implements [FileSystem] using the native file system restricted to a |
| // specific directory tree. |
| // |
| // While the [FileSystem.Open] method takes '/'-separated paths, a Dir's string |
| // value is a directory path on the native file system, not a URL, so it is separated |
| // by [filepath.Separator], which isn't necessarily '/'. |
| // |
| // Note that Dir could expose sensitive files and directories. Dir will follow |
| // symlinks pointing out of the directory tree, which can be especially dangerous |
| // if serving from a directory in which users are able to create arbitrary symlinks. |
| // Dir will also allow access to files and directories starting with a period, |
| // which could expose sensitive directories like .git or sensitive files like |
| // .htpasswd. To exclude files with a leading period, remove the files/directories |
| // from the server or create a custom FileSystem implementation. |
| // |
| // An empty Dir is treated as ".". |
| type http_Dir string |
| |
| // mapOpenError maps the provided non-nil error from opening name |
| // to a possibly better non-nil error. In particular, it turns OS-specific errors |
| // about opening files in non-directories into fs.ErrNotExist. See Issues 18984 and 49552. |
| func http_mapOpenError(originalErr error, name string, sep rune, stat func(string) (fs.FileInfo, error)) error { |
| if errors.Is(originalErr, fs.ErrNotExist) || errors.Is(originalErr, fs.ErrPermission) { |
| return originalErr |
| } |
| |
| parts := strings.Split(name, string(sep)) |
| for i := range parts { |
| if parts[i] == "" { |
| continue |
| } |
| fi, err := stat(strings.Join(parts[:i+1], string(sep))) |
| if err != nil { |
| return originalErr |
| } |
| if !fi.IsDir() { |
| return fs.ErrNotExist |
| } |
| } |
| return originalErr |
| } |
| |
| // errInvalidUnsafePath is returned by Dir.Open when the call to |
| // filepath.Localize fails. filepath.Localize returns an error if the path |
| // cannot be represented by the operating system. |
| var http_errInvalidUnsafePath = errors.New("http: invalid or unsafe file path") |
| |
| // Open implements [FileSystem] using [os.Open], opening files for reading rooted |
| // and relative to the directory d. |
| func (d http_Dir) Open(name string) (http_File, error) { |
| path := path.Clean("/" + name)[1:] |
| if path == "" { |
| path = "." |
| } |
| path, err := filepath.Localize(path) |
| if err != nil { |
| return nil, http_errInvalidUnsafePath |
| } |
| dir := string(d) |
| if dir == "" { |
| dir = "." |
| } |
| fullName := filepath.Join(dir, path) |
| f, err := os.Open(fullName) |
| if err != nil { |
| return nil, http_mapOpenError(err, fullName, filepath.Separator, os.Stat) |
| } |
| return f, nil |
| } |
| |
| // A FileSystem implements access to a collection of named files. |
| // The elements in a file path are separated by slash ('/', U+002F) |
| // characters, regardless of host operating system convention. |
| // See the [FileServer] function to convert a FileSystem to a [Handler]. |
| // |
| // This interface predates the [fs.FS] interface, which can be used instead: |
| // the [FS] adapter function converts an fs.FS to a FileSystem. |
| type http_FileSystem interface { |
| Open(name string) (http_File, error) |
| } |
| |
| // A File is returned by a [FileSystem]'s Open method and can be |
| // served by the [FileServer] implementation. |
| // |
| // The methods should behave the same as those on an [*os.File]. |
| type http_File interface { |
| io.Closer |
| io.Reader |
| io.Seeker |
| Readdir(count int) ([]fs.FileInfo, error) |
| Stat() (fs.FileInfo, error) |
| } |
| |
| type http_anyDirs interface { |
| len() int |
| name(i int) string |
| isDir(i int) bool |
| } |
| |
| type http_fileInfoDirs []fs.FileInfo |
| |
| func (d http_fileInfoDirs) len() int { return len(d) } |
| |
| func (d http_fileInfoDirs) isDir(i int) bool { return d[i].IsDir() } |
| |
| func (d http_fileInfoDirs) name(i int) string { return d[i].Name() } |
| |
| type http_dirEntryDirs []fs.DirEntry |
| |
| func (d http_dirEntryDirs) len() int { return len(d) } |
| |
| func (d http_dirEntryDirs) isDir(i int) bool { return d[i].IsDir() } |
| |
| func (d http_dirEntryDirs) name(i int) string { return d[i].Name() } |
| |
| func http_dirList(w http_ResponseWriter, r *http_Request, f http_File) { |
| // Prefer to use ReadDir instead of Readdir, |
| // because the former doesn't require calling |
| // Stat on every entry of a directory on Unix. |
| var dirs http_anyDirs |
| var err error |
| if d, ok := f.(fs.ReadDirFile); ok { |
| var list http_dirEntryDirs |
| list, err = d.ReadDir(-1) |
| dirs = list |
| } else { |
| var list http_fileInfoDirs |
| list, err = f.Readdir(-1) |
| dirs = list |
| } |
| |
| if err != nil { |
| http_logf(r, "http: error reading directory: %v", err) |
| http_Error(w, "Error reading directory", http_StatusInternalServerError) |
| return |
| } |
| sort.Slice(dirs, func(i, j int) bool { return dirs.name(i) < dirs.name(j) }) |
| |
| w.Header().Set("Content-Type", "text/html; charset=utf-8") |
| fmt.Fprintf(w, "<!doctype html>\n") |
| fmt.Fprintf(w, "<meta name=\"viewport\" content=\"width=device-width\">\n") |
| fmt.Fprintf(w, "<pre>\n") |
| for i, n := 0, dirs.len(); i < n; i++ { |
| name := dirs.name(i) |
| if dirs.isDir(i) { |
| name += "/" |
| } |
| // name may contain '?' or '#', which must be escaped to remain |
| // part of the URL path, and not indicate the start of a query |
| // string or fragment. |
| url := url.URL{Path: name} |
| fmt.Fprintf(w, "<a href=\"%s\">%s</a>\n", url.String(), http_htmlReplacer.Replace(name)) |
| } |
| fmt.Fprintf(w, "</pre>\n") |
| } |
| |
| // GODEBUG=httpservecontentkeepheaders=1 restores the pre-1.23 behavior of not deleting |
| // Cache-Control, Content-Encoding, Etag, or Last-Modified headers on ServeContent errors. |
| var http_httpservecontentkeepheaders = godebug.New("httpservecontentkeepheaders") |
| |
| // serveError serves an error from ServeFile, ServeFileFS, and ServeContent. |
| // Because those can all be configured by the caller by setting headers like |
| // Etag, Last-Modified, and Cache-Control to send on a successful response, |
| // the error path needs to clear them, since they may not be meant for errors. |
| func http_serveError(w http_ResponseWriter, text string, code int) { |
| h := w.Header() |
| |
| nonDefault := false |
| for _, k := range []string{ |
| "Cache-Control", |
| "Content-Encoding", |
| "Etag", |
| "Last-Modified", |
| } { |
| if !h.has(k) { |
| continue |
| } |
| if http_httpservecontentkeepheaders.Value() == "1" { |
| nonDefault = true |
| } else { |
| h.Del(k) |
| } |
| } |
| if nonDefault { |
| http_httpservecontentkeepheaders.IncNonDefault() |
| } |
| |
| http_Error(w, text, code) |
| } |
| |
| // ServeContent replies to the request using the content in the |
| // provided ReadSeeker. The main benefit of ServeContent over [io.Copy] |
| // is that it handles Range requests properly, sets the MIME type, and |
| // handles If-Match, If-Unmodified-Since, If-None-Match, If-Modified-Since, |
| // and If-Range requests. |
| // |
| // If the response's Content-Type header is not set, ServeContent |
| // first tries to deduce the type from name's file extension and, |
| // if that fails, falls back to reading the first block of the content |
| // and passing it to [DetectContentType]. |
| // The name is otherwise unused; in particular it can be empty and is |
| // never sent in the response. |
| // |
| // If modtime is not the zero time or Unix epoch, ServeContent |
| // includes it in a Last-Modified header in the response. If the |
| // request includes an If-Modified-Since header, ServeContent uses |
| // modtime to decide whether the content needs to be sent at all. |
| // |
| // The content's Seek method must work: ServeContent uses |
| // a seek to the end of the content to determine its size. |
| // Note that [*os.File] implements the [io.ReadSeeker] interface. |
| // |
| // If the caller has set w's ETag header formatted per RFC 7232, section 2.3, |
| // ServeContent uses it to handle requests using If-Match, If-None-Match, or If-Range. |
| // |
| // If an error occurs when serving the request (for example, when |
| // handling an invalid range request), ServeContent responds with an |
| // error message. By default, ServeContent strips the Cache-Control, |
| // Content-Encoding, ETag, and Last-Modified headers from error responses. |
| // The GODEBUG setting httpservecontentkeepheaders=1 causes ServeContent |
| // to preserve these headers. |
| func http_ServeContent(w http_ResponseWriter, req *http_Request, name string, modtime time.Time, content io.ReadSeeker) { |
| sizeFunc := func() (int64, error) { |
| size, err := content.Seek(0, io.SeekEnd) |
| if err != nil { |
| return 0, http_errSeeker |
| } |
| _, err = content.Seek(0, io.SeekStart) |
| if err != nil { |
| return 0, http_errSeeker |
| } |
| return size, nil |
| } |
| http_serveContent(w, req, name, modtime, sizeFunc, content) |
| } |
| |
| // errSeeker is returned by ServeContent's sizeFunc when the content |
| // doesn't seek properly. The underlying Seeker's error text isn't |
| // included in the sizeFunc reply so it's not sent over HTTP to end |
| // users. |
| var http_errSeeker = errors.New("seeker can't seek") |
| |
| // errNoOverlap is returned by serveContent's parseRange if first-byte-pos of |
| // all of the byte-range-spec values is greater than the content size. |
| var http_errNoOverlap = errors.New("invalid range: failed to overlap") |
| |
| // if name is empty, filename is unknown. (used for mime type, before sniffing) |
| // if modtime.IsZero(), modtime is unknown. |
| // content must be seeked to the beginning of the file. |
| // The sizeFunc is called at most once. Its error, if any, is sent in the HTTP response. |
| func http_serveContent(w http_ResponseWriter, r *http_Request, name string, modtime time.Time, sizeFunc func() (int64, error), content io.ReadSeeker) { |
| http_setLastModified(w, modtime) |
| done, rangeReq := http_checkPreconditions(w, r, modtime) |
| if done { |
| return |
| } |
| |
| code := http_StatusOK |
| |
| // If Content-Type isn't set, use the file's extension to find it, but |
| // if the Content-Type is unset explicitly, do not sniff the type. |
| ctypes, haveType := w.Header()["Content-Type"] |
| var ctype string |
| if !haveType { |
| ctype = mime.TypeByExtension(filepath.Ext(name)) |
| if ctype == "" { |
| // read a chunk to decide between utf-8 text and binary |
| var buf [internal.SniffLen]byte |
| n, _ := io.ReadFull(content, buf[:]) |
| ctype = http_DetectContentType(buf[:n]) |
| _, err := content.Seek(0, io.SeekStart) // rewind to output whole file |
| if err != nil { |
| http_serveError(w, "seeker can't seek", http_StatusInternalServerError) |
| return |
| } |
| } |
| w.Header().Set("Content-Type", ctype) |
| } else if len(ctypes) > 0 { |
| ctype = ctypes[0] |
| } |
| |
| size, err := sizeFunc() |
| if err != nil { |
| http_serveError(w, err.Error(), http_StatusInternalServerError) |
| return |
| } |
| if size < 0 { |
| // Should never happen but just to be sure |
| http_serveError(w, "negative content size computed", http_StatusInternalServerError) |
| return |
| } |
| |
| // handle Content-Range header. |
| sendSize := size |
| var sendContent io.Reader = content |
| ranges, err := http_parseRange(rangeReq, size) |
| switch err { |
| case nil: |
| case http_errNoOverlap: |
| if size == 0 { |
| // Some clients add a Range header to all requests to |
| // limit the size of the response. If the file is empty, |
| // ignore the range header and respond with a 200 rather |
| // than a 416. |
| ranges = nil |
| break |
| } |
| w.Header().Set("Content-Range", fmt.Sprintf("bytes */%d", size)) |
| fallthrough |
| default: |
| http_serveError(w, err.Error(), http_StatusRequestedRangeNotSatisfiable) |
| return |
| } |
| |
| if http_sumRangesSize(ranges) > size { |
| // The total number of bytes in all the ranges |
| // is larger than the size of the file by |
| // itself, so this is probably an attack, or a |
| // dumb client. Ignore the range request. |
| ranges = nil |
| } |
| switch { |
| case len(ranges) == 1: |
| // RFC 7233, Section 4.1: |
| // "If a single part is being transferred, the server |
| // generating the 206 response MUST generate a |
| // Content-Range header field, describing what range |
| // of the selected representation is enclosed, and a |
| // payload consisting of the range. |
| // ... |
| // A server MUST NOT generate a multipart response to |
| // a request for a single range, since a client that |
| // does not request multiple parts might not support |
| // multipart responses." |
| ra := ranges[0] |
| if _, err := content.Seek(ra.start, io.SeekStart); err != nil { |
| http_serveError(w, err.Error(), http_StatusRequestedRangeNotSatisfiable) |
| return |
| } |
| sendSize = ra.length |
| code = http_StatusPartialContent |
| w.Header().Set("Content-Range", ra.contentRange(size)) |
| case len(ranges) > 1: |
| sendSize = http_rangesMIMESize(ranges, ctype, size) |
| code = http_StatusPartialContent |
| |
| pr, pw := io.Pipe() |
| mw := multipart.NewWriter(pw) |
| w.Header().Set("Content-Type", "multipart/byteranges; boundary="+mw.Boundary()) |
| sendContent = pr |
| defer pr.Close() // cause writing goroutine to fail and exit if CopyN doesn't finish. |
| go func() { |
| for _, ra := range ranges { |
| part, err := mw.CreatePart(ra.mimeHeader(ctype, size)) |
| if err != nil { |
| pw.CloseWithError(err) |
| return |
| } |
| if _, err := content.Seek(ra.start, io.SeekStart); err != nil { |
| pw.CloseWithError(err) |
| return |
| } |
| if _, err := io.CopyN(part, content, ra.length); err != nil { |
| pw.CloseWithError(err) |
| return |
| } |
| } |
| mw.Close() |
| pw.Close() |
| }() |
| } |
| |
| w.Header().Set("Accept-Ranges", "bytes") |
| |
| // We should be able to unconditionally set the Content-Length here. |
| // |
| // However, there is a pattern observed in the wild that this breaks: |
| // The user wraps the ResponseWriter in one which gzips data written to it, |
| // and sets "Content-Encoding: gzip". |
| // |
| // The user shouldn't be doing this; the serveContent path here depends |
| // on serving seekable data with a known length. If you want to compress |
| // on the fly, then you shouldn't be using ServeFile/ServeContent, or |
| // you should compress the entire file up-front and provide a seekable |
| // view of the compressed data. |
| // |
| // However, since we've observed this pattern in the wild, and since |
| // setting Content-Length here breaks code that mostly-works today, |
| // skip setting Content-Length if the user set Content-Encoding. |
| // |
| // If this is a range request, always set Content-Length. |
| // If the user isn't changing the bytes sent in the ResponseWrite, |
| // the Content-Length will be correct. |
| // If the user is changing the bytes sent, then the range request wasn't |
| // going to work properly anyway and we aren't worse off. |
| // |
| // A possible future improvement on this might be to look at the type |
| // of the ResponseWriter, and always set Content-Length if it's one |
| // that we recognize. |
| if len(ranges) > 0 || w.Header().Get("Content-Encoding") == "" { |
| w.Header().Set("Content-Length", strconv.FormatInt(sendSize, 10)) |
| } |
| w.WriteHeader(code) |
| |
| if r.Method != "HEAD" { |
| io.CopyN(w, sendContent, sendSize) |
| } |
| } |
| |
| // scanETag determines if a syntactically valid ETag is present at s. If so, |
| // the ETag and remaining text after consuming ETag is returned. Otherwise, |
| // it returns "", "". |
| func http_scanETag(s string) (etag string, remain string) { |
| s = textproto.TrimString(s) |
| start := 0 |
| if strings.HasPrefix(s, "W/") { |
| start = 2 |
| } |
| if len(s[start:]) < 2 || s[start] != '"' { |
| return "", "" |
| } |
| // ETag is either W/"text" or "text". |
| // See RFC 7232 2.3. |
| for i := start + 1; i < len(s); i++ { |
| c := s[i] |
| switch { |
| // Character values allowed in ETags. |
| case c == 0x21 || c >= 0x23 && c <= 0x7E || c >= 0x80: |
| case c == '"': |
| return s[:i+1], s[i+1:] |
| default: |
| return "", "" |
| } |
| } |
| return "", "" |
| } |
| |
| // etagStrongMatch reports whether a and b match using strong ETag comparison. |
| // Assumes a and b are valid ETags. |
| func http_etagStrongMatch(a, b string) bool { |
| return a == b && a != "" && a[0] == '"' |
| } |
| |
| // etagWeakMatch reports whether a and b match using weak ETag comparison. |
| // Assumes a and b are valid ETags. |
| func http_etagWeakMatch(a, b string) bool { |
| return strings.TrimPrefix(a, "W/") == strings.TrimPrefix(b, "W/") |
| } |
| |
| // condResult is the result of an HTTP request precondition check. |
| // See https://tools.ietf.org/html/rfc7232 section 3. |
| type http_condResult int |
| |
| const ( |
| http_condNone http_condResult = iota |
| http_condTrue |
| http_condFalse |
| ) |
| |
| func http_checkIfMatch(w http_ResponseWriter, r *http_Request) http_condResult { |
| im := r.Header.Get("If-Match") |
| if im == "" { |
| return http_condNone |
| } |
| for { |
| im = textproto.TrimString(im) |
| if len(im) == 0 { |
| break |
| } |
| if im[0] == ',' { |
| im = im[1:] |
| continue |
| } |
| if im[0] == '*' { |
| return http_condTrue |
| } |
| etag, remain := http_scanETag(im) |
| if etag == "" { |
| break |
| } |
| if http_etagStrongMatch(etag, w.Header().get("Etag")) { |
| return http_condTrue |
| } |
| im = remain |
| } |
| |
| return http_condFalse |
| } |
| |
| func http_checkIfUnmodifiedSince(r *http_Request, modtime time.Time) http_condResult { |
| ius := r.Header.Get("If-Unmodified-Since") |
| if ius == "" || http_isZeroTime(modtime) { |
| return http_condNone |
| } |
| t, err := http_ParseTime(ius) |
| if err != nil { |
| return http_condNone |
| } |
| |
| // The Last-Modified header truncates sub-second precision so |
| // the modtime needs to be truncated too. |
| modtime = modtime.Truncate(time.Second) |
| if ret := modtime.Compare(t); ret <= 0 { |
| return http_condTrue |
| } |
| return http_condFalse |
| } |
| |
| func http_checkIfNoneMatch(w http_ResponseWriter, r *http_Request) http_condResult { |
| inm := r.Header.get("If-None-Match") |
| if inm == "" { |
| return http_condNone |
| } |
| buf := inm |
| for { |
| buf = textproto.TrimString(buf) |
| if len(buf) == 0 { |
| break |
| } |
| if buf[0] == ',' { |
| buf = buf[1:] |
| continue |
| } |
| if buf[0] == '*' { |
| return http_condFalse |
| } |
| etag, remain := http_scanETag(buf) |
| if etag == "" { |
| break |
| } |
| if http_etagWeakMatch(etag, w.Header().get("Etag")) { |
| return http_condFalse |
| } |
| buf = remain |
| } |
| return http_condTrue |
| } |
| |
| func http_checkIfModifiedSince(r *http_Request, modtime time.Time) http_condResult { |
| if r.Method != "GET" && r.Method != "HEAD" { |
| return http_condNone |
| } |
| ims := r.Header.Get("If-Modified-Since") |
| if ims == "" || http_isZeroTime(modtime) { |
| return http_condNone |
| } |
| t, err := http_ParseTime(ims) |
| if err != nil { |
| return http_condNone |
| } |
| // The Last-Modified header truncates sub-second precision so |
| // the modtime needs to be truncated too. |
| modtime = modtime.Truncate(time.Second) |
| if ret := modtime.Compare(t); ret <= 0 { |
| return http_condFalse |
| } |
| return http_condTrue |
| } |
| |
| func http_checkIfRange(w http_ResponseWriter, r *http_Request, modtime time.Time) http_condResult { |
| if r.Method != "GET" && r.Method != "HEAD" { |
| return http_condNone |
| } |
| ir := r.Header.get("If-Range") |
| if ir == "" { |
| return http_condNone |
| } |
| etag, _ := http_scanETag(ir) |
| if etag != "" { |
| if http_etagStrongMatch(etag, w.Header().Get("Etag")) { |
| return http_condTrue |
| } else { |
| return http_condFalse |
| } |
| } |
| // The If-Range value is typically the ETag value, but it may also be |
| // the modtime date. See golang.org/issue/8367. |
| if modtime.IsZero() { |
| return http_condFalse |
| } |
| t, err := http_ParseTime(ir) |
| if err != nil { |
| return http_condFalse |
| } |
| if t.Unix() == modtime.Unix() { |
| return http_condTrue |
| } |
| return http_condFalse |
| } |
| |
| var http_unixEpochTime = time.Unix(0, 0) |
| |
| // isZeroTime reports whether t is obviously unspecified (either zero or Unix()=0). |
| func http_isZeroTime(t time.Time) bool { |
| return t.IsZero() || t.Equal(http_unixEpochTime) |
| } |
| |
| func http_setLastModified(w http_ResponseWriter, modtime time.Time) { |
| if !http_isZeroTime(modtime) { |
| w.Header().Set("Last-Modified", modtime.UTC().Format(http_TimeFormat)) |
| } |
| } |
| |
| func http_writeNotModified(w http_ResponseWriter) { |
| // RFC 7232 section 4.1: |
| // a sender SHOULD NOT generate representation metadata other than the |
| // above listed fields unless said metadata exists for the purpose of |
| // guiding cache updates (e.g., Last-Modified might be useful if the |
| // response does not have an ETag field). |
| h := w.Header() |
| delete(h, "Content-Type") |
| delete(h, "Content-Length") |
| delete(h, "Content-Encoding") |
| if h.Get("Etag") != "" { |
| delete(h, "Last-Modified") |
| } |
| w.WriteHeader(http_StatusNotModified) |
| } |
| |
| // checkPreconditions evaluates request preconditions and reports whether a precondition |
| // resulted in sending StatusNotModified or StatusPreconditionFailed. |
| func http_checkPreconditions(w http_ResponseWriter, r *http_Request, modtime time.Time) (done bool, rangeHeader string) { |
| // This function carefully follows RFC 7232 section 6. |
| ch := http_checkIfMatch(w, r) |
| if ch == http_condNone { |
| ch = http_checkIfUnmodifiedSince(r, modtime) |
| } |
| if ch == http_condFalse { |
| w.WriteHeader(http_StatusPreconditionFailed) |
| return true, "" |
| } |
| switch http_checkIfNoneMatch(w, r) { |
| case http_condFalse: |
| if r.Method == "GET" || r.Method == "HEAD" { |
| http_writeNotModified(w) |
| return true, "" |
| } else { |
| w.WriteHeader(http_StatusPreconditionFailed) |
| return true, "" |
| } |
| case http_condNone: |
| if http_checkIfModifiedSince(r, modtime) == http_condFalse { |
| http_writeNotModified(w) |
| return true, "" |
| } |
| } |
| |
| rangeHeader = r.Header.get("Range") |
| if rangeHeader != "" && http_checkIfRange(w, r, modtime) == http_condFalse { |
| rangeHeader = "" |
| } |
| return false, rangeHeader |
| } |
| |
| // name is '/'-separated, not filepath.Separator. |
| func http_serveFile(w http_ResponseWriter, r *http_Request, fs http_FileSystem, name string, redirect bool) { |
| const indexPage = "/index.html" |
| |
| // redirect .../index.html to .../ |
| // can't use Redirect() because that would make the path absolute, |
| // which would be a problem running under StripPrefix |
| if strings.HasSuffix(r.URL.Path, indexPage) { |
| http_localRedirect(w, r, "./") |
| return |
| } |
| |
| f, err := fs.Open(name) |
| if err != nil { |
| msg, code := http_toHTTPError(err) |
| http_serveError(w, msg, code) |
| return |
| } |
| defer f.Close() |
| |
| d, err := f.Stat() |
| if err != nil { |
| msg, code := http_toHTTPError(err) |
| http_serveError(w, msg, code) |
| return |
| } |
| |
| if redirect { |
| // redirect to canonical path: / at end of directory url |
| // r.URL.Path always begins with / |
| url := r.URL.Path |
| if d.IsDir() { |
| if url[len(url)-1] != '/' { |
| http_localRedirect(w, r, path.Base(url)+"/") |
| return |
| } |
| } else if url[len(url)-1] == '/' { |
| base := path.Base(url) |
| if base == "/" || base == "." { |
| // The FileSystem maps a path like "/" or "/./" to a file instead of a directory. |
| msg := "http: attempting to traverse a non-directory" |
| http_serveError(w, msg, http_StatusInternalServerError) |
| return |
| } |
| http_localRedirect(w, r, "../"+base) |
| return |
| } |
| } |
| |
| if d.IsDir() { |
| url := r.URL.Path |
| // redirect if the directory name doesn't end in a slash |
| if url == "" || url[len(url)-1] != '/' { |
| http_localRedirect(w, r, path.Base(url)+"/") |
| return |
| } |
| |
| // use contents of index.html for directory, if present |
| index := strings.TrimSuffix(name, "/") + indexPage |
| ff, err := fs.Open(index) |
| if err == nil { |
| defer ff.Close() |
| dd, err := ff.Stat() |
| if err == nil { |
| d = dd |
| f = ff |
| } |
| } |
| } |
| |
| // Still a directory? (we didn't find an index.html file) |
| if d.IsDir() { |
| if http_checkIfModifiedSince(r, d.ModTime()) == http_condFalse { |
| http_writeNotModified(w) |
| return |
| } |
| http_setLastModified(w, d.ModTime()) |
| http_dirList(w, r, f) |
| return |
| } |
| |
| // serveContent will check modification time |
| sizeFunc := func() (int64, error) { return d.Size(), nil } |
| http_serveContent(w, r, d.Name(), d.ModTime(), sizeFunc, f) |
| } |
| |
| // toHTTPError returns a non-specific HTTP error message and status code |
| // for a given non-nil error value. It's important that toHTTPError does not |
| // actually return err.Error(), since msg and httpStatus are returned to users, |
| // and historically Go's ServeContent always returned just "404 Not Found" for |
| // all errors. We don't want to start leaking information in error messages. |
| func http_toHTTPError(err error) (msg string, httpStatus int) { |
| if errors.Is(err, fs.ErrNotExist) { |
| return "404 page not found", http_StatusNotFound |
| } |
| if errors.Is(err, fs.ErrPermission) { |
| return "403 Forbidden", http_StatusForbidden |
| } |
| if errors.Is(err, http_errInvalidUnsafePath) { |
| return "404 page not found", http_StatusNotFound |
| } |
| // Default: |
| return "500 Internal Server Error", http_StatusInternalServerError |
| } |
| |
| // localRedirect gives a Moved Permanently response. |
| // It does not convert relative paths to absolute paths like Redirect does. |
| func http_localRedirect(w http_ResponseWriter, r *http_Request, newPath string) { |
| // There is no reliable way for us to redirect correctly when the path has |
| // escaped slashes, since StripPrefix might be in use. Just return 404. |
| if p := r.URL.EscapedPath(); strings.Contains(p, "%2f") || strings.Contains(p, "%2F") { |
| http_NotFound(w, r) |
| return |
| } |
| if q := r.URL.RawQuery; q != "" { |
| newPath += "?" + q |
| } |
| w.Header().Set("Location", newPath) |
| w.WriteHeader(http_StatusMovedPermanently) |
| } |
| |
| // ServeFile replies to the request with the contents of the named |
| // file or directory. |
| // |
| // If the provided file or directory name is a relative path, it is |
| // interpreted relative to the current directory and may ascend to |
| // parent directories. If the provided name is constructed from user |
| // input, it should be sanitized before calling [ServeFile]. |
| // |
| // As a precaution, ServeFile will reject requests where r.URL.Path |
| // contains a ".." path element; this protects against callers who |
| // might unsafely use [filepath.Join] on r.URL.Path without sanitizing |
| // it and then use that filepath.Join result as the name argument. |
| // |
| // As another special case, ServeFile redirects any request where r.URL.Path |
| // ends in "/index.html" to the same path, without the final |
| // "index.html". To avoid such redirects either modify the path or |
| // use [ServeContent]. |
| // |
| // Outside of those two special cases, ServeFile does not use |
| // r.URL.Path for selecting the file or directory to serve; only the |
| // file or directory provided in the name argument is used. |
| func http_ServeFile(w http_ResponseWriter, r *http_Request, name string) { |
| if http_containsDotDot(r.URL.Path) { |
| // Too many programs use r.URL.Path to construct the argument to |
| // serveFile. Reject the request under the assumption that happened |
| // here and ".." may not be wanted. |
| // Note that name might not contain "..", for example if code (still |
| // incorrectly) used filepath.Join(myDir, r.URL.Path). |
| http_serveError(w, "invalid URL path", http_StatusBadRequest) |
| return |
| } |
| dir, file := filepath.Split(name) |
| http_serveFile(w, r, http_Dir(dir), file, false) |
| } |
| |
| // ServeFileFS replies to the request with the contents |
| // of the named file or directory from the file system fsys. |
| // The files provided by fsys must implement [io.Seeker]. |
| // |
| // If the provided name is constructed from user input, it should be |
| // sanitized before calling [ServeFileFS]. |
| // |
| // As a precaution, ServeFileFS will reject requests where r.URL.Path |
| // contains a ".." path element; this protects against callers who |
| // might unsafely use [filepath.Join] on r.URL.Path without sanitizing |
| // it and then use that filepath.Join result as the name argument. |
| // |
| // As another special case, ServeFileFS redirects any request where r.URL.Path |
| // ends in "/index.html" to the same path, without the final |
| // "index.html". To avoid such redirects either modify the path or |
| // use [ServeContent]. |
| // |
| // Outside of those two special cases, ServeFileFS does not use |
| // r.URL.Path for selecting the file or directory to serve; only the |
| // file or directory provided in the name argument is used. |
| func http_ServeFileFS(w http_ResponseWriter, r *http_Request, fsys fs.FS, name string) { |
| if http_containsDotDot(r.URL.Path) { |
| // Too many programs use r.URL.Path to construct the argument to |
| // serveFile. Reject the request under the assumption that happened |
| // here and ".." may not be wanted. |
| // Note that name might not contain "..", for example if code (still |
| // incorrectly) used filepath.Join(myDir, r.URL.Path). |
| http_serveError(w, "invalid URL path", http_StatusBadRequest) |
| return |
| } |
| http_serveFile(w, r, http_FS(fsys), name, false) |
| } |
| |
| func http_containsDotDot(v string) bool { |
| if !strings.Contains(v, "..") { |
| return false |
| } |
| for ent := range strings.FieldsFuncSeq(v, http_isSlashRune) { |
| if ent == ".." { |
| return true |
| } |
| } |
| return false |
| } |
| |
| func http_isSlashRune(r rune) bool { return r == '/' || r == '\\' } |
| |
| type http_fileHandler struct { |
| root http_FileSystem |
| } |
| |
| type http_ioFS struct { |
| fsys fs.FS |
| } |
| |
| type http_ioFile struct { |
| file fs.File |
| } |
| |
| func (f http_ioFS) Open(name string) (http_File, error) { |
| if name == "/" { |
| name = "." |
| } else { |
| name = strings.TrimPrefix(name, "/") |
| } |
| file, err := f.fsys.Open(name) |
| if err != nil { |
| return nil, http_mapOpenError(err, name, '/', func(path string) (fs.FileInfo, error) { |
| return fs.Stat(f.fsys, path) |
| }) |
| } |
| return http_ioFile{file}, nil |
| } |
| |
| func (f http_ioFile) Close() error { return f.file.Close() } |
| |
| func (f http_ioFile) Read(b []byte) (int, error) { return f.file.Read(b) } |
| |
| func (f http_ioFile) Stat() (fs.FileInfo, error) { return f.file.Stat() } |
| |
| var http_errMissingSeek = errors.New("io.File missing Seek method") |
| |
| var http_errMissingReadDir = errors.New("io.File directory missing ReadDir method") |
| |
| func (f http_ioFile) Seek(offset int64, whence int) (int64, error) { |
| s, ok := f.file.(io.Seeker) |
| if !ok { |
| return 0, http_errMissingSeek |
| } |
| return s.Seek(offset, whence) |
| } |
| |
| func (f http_ioFile) ReadDir(count int) ([]fs.DirEntry, error) { |
| d, ok := f.file.(fs.ReadDirFile) |
| if !ok { |
| return nil, http_errMissingReadDir |
| } |
| return d.ReadDir(count) |
| } |
| |
| func (f http_ioFile) Readdir(count int) ([]fs.FileInfo, error) { |
| d, ok := f.file.(fs.ReadDirFile) |
| if !ok { |
| return nil, http_errMissingReadDir |
| } |
| var list []fs.FileInfo |
| for { |
| dirs, err := d.ReadDir(count - len(list)) |
| for _, dir := range dirs { |
| info, err := dir.Info() |
| if err != nil { |
| // Pretend it doesn't exist, like (*os.File).Readdir does. |
| continue |
| } |
| list = append(list, info) |
| } |
| if err != nil { |
| return list, err |
| } |
| if count < 0 || len(list) >= count { |
| break |
| } |
| } |
| return list, nil |
| } |
| |
| // FS converts fsys to a [FileSystem] implementation, |
| // for use with [FileServer] and [NewFileTransport]. |
| // The files provided by fsys must implement [io.Seeker]. |
| func http_FS(fsys fs.FS) http_FileSystem { |
| return http_ioFS{fsys} |
| } |
| |
| // FileServer returns a handler that serves HTTP requests |
| // with the contents of the file system rooted at root. |
| // |
| // As a special case, the returned file server redirects any request |
| // ending in "/index.html" to the same path, without the final |
| // "index.html". |
| // |
| // To use the operating system's file system implementation, |
| // use [http.Dir]: |
| // |
| // http.Handle("/", http.FileServer(http.Dir("/tmp"))) |
| // |
| // To use an [fs.FS] implementation, use [http.FileServerFS] instead. |
| func http_FileServer(root http_FileSystem) http_Handler { |
| return &http_fileHandler{root} |
| } |
| |
| // FileServerFS returns a handler that serves HTTP requests |
| // with the contents of the file system fsys. |
| // The files provided by fsys must implement [io.Seeker]. |
| // |
| // As a special case, the returned file server redirects any request |
| // ending in "/index.html" to the same path, without the final |
| // "index.html". |
| // |
| // http.Handle("/", http.FileServerFS(fsys)) |
| func http_FileServerFS(root fs.FS) http_Handler { |
| return http_FileServer(http_FS(root)) |
| } |
| |
| func (f *http_fileHandler) ServeHTTP(w http_ResponseWriter, r *http_Request) { |
| upath := r.URL.Path |
| if !strings.HasPrefix(upath, "/") { |
| upath = "/" + upath |
| r.URL.Path = upath |
| } |
| http_serveFile(w, r, f.root, path.Clean(upath), true) |
| } |
| |
| // httpRange specifies the byte range to be sent to the client. |
| type http_httpRange struct { |
| start, length int64 |
| } |
| |
| func (r http_httpRange) contentRange(size int64) string { |
| return fmt.Sprintf("bytes %d-%d/%d", r.start, r.start+r.length-1, size) |
| } |
| |
| func (r http_httpRange) mimeHeader(contentType string, size int64) textproto.MIMEHeader { |
| return textproto.MIMEHeader{ |
| "Content-Range": {r.contentRange(size)}, |
| "Content-Type": {contentType}, |
| } |
| } |
| |
| // parseRange parses a Range header string as per RFC 7233. |
| // errNoOverlap is returned if none of the ranges overlap. |
| func http_parseRange(s string, size int64) ([]http_httpRange, error) { |
| if s == "" { |
| return nil, nil // header not present |
| } |
| const b = "bytes=" |
| if !strings.HasPrefix(s, b) { |
| return nil, errors.New("invalid range") |
| } |
| var ranges []http_httpRange |
| noOverlap := false |
| for ra := range strings.SplitSeq(s[len(b):], ",") { |
| ra = textproto.TrimString(ra) |
| if ra == "" { |
| continue |
| } |
| start, end, ok := strings.Cut(ra, "-") |
| if !ok { |
| return nil, errors.New("invalid range") |
| } |
| start, end = textproto.TrimString(start), textproto.TrimString(end) |
| var r http_httpRange |
| if start == "" { |
| // If no start is specified, end specifies the |
| // range start relative to the end of the file, |
| // and we are dealing with <suffix-length> |
| // which has to be a non-negative integer as per |
| // RFC 7233 Section 2.1 "Byte-Ranges". |
| if end == "" || end[0] == '-' { |
| return nil, errors.New("invalid range") |
| } |
| i, err := strconv.ParseInt(end, 10, 64) |
| if i < 0 || err != nil { |
| return nil, errors.New("invalid range") |
| } |
| if i > size { |
| i = size |
| } |
| r.start = size - i |
| r.length = size - r.start |
| } else { |
| i, err := strconv.ParseInt(start, 10, 64) |
| if err != nil || i < 0 { |
| return nil, errors.New("invalid range") |
| } |
| if i >= size { |
| // If the range begins after the size of the content, |
| // then it does not overlap. |
| noOverlap = true |
| continue |
| } |
| r.start = i |
| if end == "" { |
| // If no end is specified, range extends to end of the file. |
| r.length = size - r.start |
| } else { |
| i, err := strconv.ParseInt(end, 10, 64) |
| if err != nil || r.start > i { |
| return nil, errors.New("invalid range") |
| } |
| if i >= size { |
| i = size - 1 |
| } |
| r.length = i - r.start + 1 |
| } |
| } |
| ranges = append(ranges, r) |
| } |
| if noOverlap && len(ranges) == 0 { |
| // The specified ranges did not overlap with the content. |
| return nil, http_errNoOverlap |
| } |
| return ranges, nil |
| } |
| |
| // countingWriter counts how many bytes have been written to it. |
| type http_countingWriter int64 |
| |
| func (w *http_countingWriter) Write(p []byte) (n int, err error) { |
| *w += http_countingWriter(len(p)) |
| return len(p), nil |
| } |
| |
| // rangesMIMESize returns the number of bytes it takes to encode the |
| // provided ranges as a multipart response. |
| func http_rangesMIMESize(ranges []http_httpRange, contentType string, contentSize int64) (encSize int64) { |
| var w http_countingWriter |
| mw := multipart.NewWriter(&w) |
| for _, ra := range ranges { |
| mw.CreatePart(ra.mimeHeader(contentType, contentSize)) |
| encSize += ra.length |
| } |
| mw.Close() |
| encSize += int64(w) |
| return |
| } |
| |
| func http_sumRangesSize(ranges []http_httpRange) (size int64) { |
| for _, ra := range ranges { |
| size += ra.length |
| } |
| return |
| } |
| |
| // A Header represents the key-value pairs in an HTTP header. |
| // |
| // The keys should be in canonical form, as returned by |
| // [CanonicalHeaderKey]. |
| type http_Header map[string][]string |
| |
| // Add adds the key, value pair to the header. |
| // It appends to any existing values associated with key. |
| // The key is case insensitive; it is canonicalized by |
| // [CanonicalHeaderKey]. |
| func (h http_Header) Add(key, value string) { |
| textproto.MIMEHeader(h).Add(key, value) |
| } |
| |
| // Set sets the header entries associated with key to the |
| // single element value. It replaces any existing values |
| // associated with key. The key is case insensitive; it is |
| // canonicalized by [textproto.CanonicalMIMEHeaderKey]. |
| // To use non-canonical keys, assign to the map directly. |
| func (h http_Header) Set(key, value string) { |
| textproto.MIMEHeader(h).Set(key, value) |
| } |
| |
| // Get gets the first value associated with the given key. If |
| // there are no values associated with the key, Get returns "". |
| // It is case insensitive; [textproto.CanonicalMIMEHeaderKey] is |
| // used to canonicalize the provided key. Get assumes that all |
| // keys are stored in canonical form. To use non-canonical keys, |
| // access the map directly. |
| func (h http_Header) Get(key string) string { |
| return textproto.MIMEHeader(h).Get(key) |
| } |
| |
| // Values returns all values associated with the given key. |
| // It is case insensitive; [textproto.CanonicalMIMEHeaderKey] is |
| // used to canonicalize the provided key. To use non-canonical |
| // keys, access the map directly. |
| // The returned slice is not a copy. |
| func (h http_Header) Values(key string) []string { |
| return textproto.MIMEHeader(h).Values(key) |
| } |
| |
| // get is like Get, but key must already be in CanonicalHeaderKey form. |
| func (h http_Header) get(key string) string { |
| if v := h[key]; len(v) > 0 { |
| return v[0] |
| } |
| return "" |
| } |
| |
| // has reports whether h has the provided key defined, even if it's |
| // set to 0-length slice. |
| func (h http_Header) has(key string) bool { |
| _, ok := h[key] |
| return ok |
| } |
| |
| // Del deletes the values associated with key. |
| // The key is case insensitive; it is canonicalized by |
| // [CanonicalHeaderKey]. |
| func (h http_Header) Del(key string) { |
| textproto.MIMEHeader(h).Del(key) |
| } |
| |
| // Write writes a header in wire format. |
| func (h http_Header) Write(w io.Writer) error { |
| return h.write(w, nil) |
| } |
| |
| func (h http_Header) write(w io.Writer, trace *httptrace.ClientTrace) error { |
| return h.writeSubset(w, nil, trace) |
| } |
| |
| // Clone returns a copy of h or nil if h is nil. |
| func (h http_Header) Clone() http_Header { |
| if h == nil { |
| return nil |
| } |
| |
| // Find total number of values. |
| nv := 0 |
| for _, vv := range h { |
| nv += len(vv) |
| } |
| sv := make([]string, nv) // shared backing array for headers' values |
| h2 := make(http_Header, len(h)) |
| for k, vv := range h { |
| if vv == nil { |
| // Preserve nil values. ReverseProxy distinguishes |
| // between nil and zero-length header values. |
| h2[k] = nil |
| continue |
| } |
| n := copy(sv, vv) |
| h2[k] = sv[:n:n] |
| sv = sv[n:] |
| } |
| return h2 |
| } |
| |
| var http_timeFormats = []string{ |
| http_TimeFormat, |
| time.RFC850, |
| time.ANSIC, |
| } |
| |
| // ParseTime parses a time header (such as the Date: header), |
| // trying each of the three formats allowed by HTTP/1.1: |
| // [TimeFormat], [time.RFC850], and [time.ANSIC]. |
| func http_ParseTime(text string) (t time.Time, err error) { |
| for _, layout := range http_timeFormats { |
| t, err = time.Parse(layout, text) |
| if err == nil { |
| return |
| } |
| } |
| return |
| } |
| |
| var http_headerNewlineToSpace = strings.NewReplacer("\n", " ", "\r", " ") |
| |
| // stringWriter implements WriteString on a Writer. |
| type http_stringWriter struct { |
| w io.Writer |
| } |
| |
| func (w http_stringWriter) WriteString(s string) (n int, err error) { |
| return w.w.Write([]byte(s)) |
| } |
| |
| type http_keyValues struct { |
| key string |
| values []string |
| } |
| |
| // headerSorter contains a slice of keyValues sorted by keyValues.key. |
| type http_headerSorter struct { |
| kvs []http_keyValues |
| } |
| |
| var http_headerSorterPool = sync.Pool{ |
| New: func() any { return new(http_headerSorter) }, |
| } |
| |
| // sortedKeyValues returns h's keys sorted in the returned kvs |
| // slice. The headerSorter used to sort is also returned, for possible |
| // return to headerSorterCache. |
| func (h http_Header) sortedKeyValues(exclude map[string]bool) (kvs []http_keyValues, hs *http_headerSorter) { |
| hs = http_headerSorterPool.Get().(*http_headerSorter) |
| if cap(hs.kvs) < len(h) { |
| hs.kvs = make([]http_keyValues, 0, len(h)) |
| } |
| kvs = hs.kvs[:0] |
| for k, vv := range h { |
| if !exclude[k] { |
| kvs = append(kvs, http_keyValues{k, vv}) |
| } |
| } |
| hs.kvs = kvs |
| slices.SortFunc(hs.kvs, func(a, b http_keyValues) int { return strings.Compare(a.key, b.key) }) |
| return kvs, hs |
| } |
| |
| // WriteSubset writes a header in wire format. |
| // If exclude is not nil, keys where exclude[key] == true are not written. |
| // Keys are not canonicalized before checking the exclude map. |
| func (h http_Header) WriteSubset(w io.Writer, exclude map[string]bool) error { |
| return h.writeSubset(w, exclude, nil) |
| } |
| |
| func (h http_Header) writeSubset(w io.Writer, exclude map[string]bool, trace *httptrace.ClientTrace) error { |
| ws, ok := w.(io.StringWriter) |
| if !ok { |
| ws = http_stringWriter{w} |
| } |
| kvs, sorter := h.sortedKeyValues(exclude) |
| var formattedVals []string |
| for _, kv := range kvs { |
| if !httpguts.ValidHeaderFieldName(kv.key) { |
| // This could be an error. In the common case of |
| // writing response headers, however, we have no good |
| // way to provide the error back to the server |
| // handler, so just drop invalid headers instead. |
| continue |
| } |
| for _, v := range kv.values { |
| v = http_headerNewlineToSpace.Replace(v) |
| v = textproto.TrimString(v) |
| for _, s := range []string{kv.key, ": ", v, "\r\n"} { |
| if _, err := ws.WriteString(s); err != nil { |
| http_headerSorterPool.Put(sorter) |
| return err |
| } |
| } |
| if trace != nil && trace.WroteHeaderField != nil { |
| formattedVals = append(formattedVals, v) |
| } |
| } |
| if trace != nil && trace.WroteHeaderField != nil { |
| trace.WroteHeaderField(kv.key, formattedVals) |
| formattedVals = nil |
| } |
| } |
| http_headerSorterPool.Put(sorter) |
| return nil |
| } |
| |
| // CanonicalHeaderKey returns the canonical format of the |
| // header key s. The canonicalization converts the first |
| // letter and any letter following a hyphen to upper case; |
| // the rest are converted to lowercase. For example, the |
| // canonical key for "accept-encoding" is "Accept-Encoding". |
| // If s contains a space or invalid header field bytes, it is |
| // returned without modifications. |
| func http_CanonicalHeaderKey(s string) string { return textproto.CanonicalMIMEHeaderKey(s) } |
| |
| // hasToken reports whether token appears with v, ASCII |
| // case-insensitive, with space or comma boundaries. |
| // token must be all lowercase. |
| // v may contain mixed cased. |
| func http_hasToken(v, token string) bool { |
| if len(token) > len(v) || token == "" { |
| return false |
| } |
| if v == token { |
| return true |
| } |
| for sp := 0; sp <= len(v)-len(token); sp++ { |
| // Check that first character is good. |
| // The token is ASCII, so checking only a single byte |
| // is sufficient. We skip this potential starting |
| // position if both the first byte and its potential |
| // ASCII uppercase equivalent (b|0x20) don't match. |
| // False positives ('^' => '~') are caught by EqualFold. |
| if b := v[sp]; b != token[0] && b|0x20 != token[0] { |
| continue |
| } |
| // Check that start pos is on a valid token boundary. |
| if sp > 0 && !http_isTokenBoundary(v[sp-1]) { |
| continue |
| } |
| // Check that end pos is on a valid token boundary. |
| if endPos := sp + len(token); endPos != len(v) && !http_isTokenBoundary(v[endPos]) { |
| continue |
| } |
| if ascii.EqualFold(v[sp:sp+len(token)], token) { |
| return true |
| } |
| } |
| return false |
| } |
| |
| func http_isTokenBoundary(b byte) bool { |
| return b == ' ' || b == ',' || b == '\t' |
| } |
| |
| // Protocols is a set of HTTP protocols. |
| // The zero value is an empty set of protocols. |
| // |
| // The supported protocols are: |
| // |
| // - HTTP1 is the HTTP/1.0 and HTTP/1.1 protocols. |
| // HTTP1 is supported on both unsecured TCP and secured TLS connections. |
| // |
| // - HTTP2 is the HTTP/2 protocol over a TLS connection. |
| // |
| // - UnencryptedHTTP2 is the HTTP/2 protocol over an unsecured TCP connection. |
| type http_Protocols struct { |
| bits uint8 |
| } |
| |
| const ( |
| http_protoHTTP1 = 1 << iota |
| http_protoHTTP2 |
| http_protoUnencryptedHTTP2 |
| http_protoHTTP3 |
| ) |
| |
| // HTTP1 reports whether p includes HTTP/1. |
| func (p http_Protocols) HTTP1() bool { return p.bits&http_protoHTTP1 != 0 } |
| |
| // SetHTTP1 adds or removes HTTP/1 from p. |
| func (p *http_Protocols) SetHTTP1(ok bool) { p.setBit(http_protoHTTP1, ok) } |
| |
| // HTTP2 reports whether p includes HTTP/2. |
| func (p http_Protocols) HTTP2() bool { return p.bits&http_protoHTTP2 != 0 } |
| |
| // SetHTTP2 adds or removes HTTP/2 from p. |
| func (p *http_Protocols) SetHTTP2(ok bool) { p.setBit(http_protoHTTP2, ok) } |
| |
| // UnencryptedHTTP2 reports whether p includes unencrypted HTTP/2. |
| func (p http_Protocols) UnencryptedHTTP2() bool { return p.bits&http_protoUnencryptedHTTP2 != 0 } |
| |
| // SetUnencryptedHTTP2 adds or removes unencrypted HTTP/2 from p. |
| func (p *http_Protocols) SetUnencryptedHTTP2(ok bool) { p.setBit(http_protoUnencryptedHTTP2, ok) } |
| |
| // http3 reports whether p includes HTTP/3. |
| func (p http_Protocols) http3() bool { return p.bits&http_protoHTTP3 != 0 } |
| |
| // setHTTP3 adds or removes HTTP/3 from p. |
| func (p *http_Protocols) setHTTP3(ok bool) { p.setBit(http_protoHTTP3, ok) } |
| |
| //go:linkname protocolSetHTTP3 golang.org/x/net/internal/http3_test.protocolSetHTTP3 |
| func http_protocolSetHTTP3(p *http_Protocols) { p.setHTTP3(true) } |
| |
| func (p *http_Protocols) setBit(bit uint8, ok bool) { |
| if ok { |
| p.bits |= bit |
| } else { |
| p.bits &^= bit |
| } |
| } |
| |
| // empty returns true if p has no protocol set at all. |
| func (p http_Protocols) empty() bool { |
| return p.bits == 0 |
| } |
| |
| func (p http_Protocols) String() string { |
| var s []string |
| if p.HTTP1() { |
| s = append(s, "HTTP1") |
| } |
| if p.HTTP2() { |
| s = append(s, "HTTP2") |
| } |
| if p.UnencryptedHTTP2() { |
| s = append(s, "UnencryptedHTTP2") |
| } |
| if p.http3() { |
| s = append(s, "HTTP3") |
| } |
| return "{" + strings.Join(s, ",") + "}" |
| } |
| |
| // incomparable is a zero-width, non-comparable type. Adding it to a struct |
| // makes that struct also non-comparable, and generally doesn't add |
| // any size (as long as it's first). |
| type http_incomparable [0]func() |
| |
| // maxInt64 is the effective "infinite" value for the Server and |
| // Transport's byte-limiting readers. |
| const http_maxInt64 = 1<<63 - 1 |
| |
| // aLongTimeAgo is a non-zero time, far in the past, used for |
| // immediate cancellation of network operations. |
| var http_aLongTimeAgo = time.Unix(1, 0) |
| |
| // omitBundledHTTP2 is set by omithttp2.go when the nethttpomithttp2 |
| // build tag is set. That means h2_bundle.go isn't compiled in and we |
| // shouldn't try to use it. |
| var http_omitBundledHTTP2 bool |
| |
| // TODO(bradfitz): move common stuff here. The other files have accumulated |
| // generic http stuff in random places. |
| |
| // contextKey is a value for use with context.WithValue. It's used as |
| // a pointer so it fits in an interface{} without allocation. |
| type http_contextKey struct { |
| name string |
| } |
| |
| func (k *http_contextKey) String() string { return "net/http context value " + k.name } |
| |
| // removePort strips the port while correctly handling IPv6. |
| func http_removePort(host string) string { |
| for i := len(host) - 1; i >= 0; i-- { |
| switch host[i] { |
| case ':': |
| return host[:i] |
| case ']': |
| return host |
| } |
| } |
| return host |
| } |
| |
| // isToken reports whether v is a valid token (https://www.rfc-editor.org/rfc/rfc2616#section-2.2). |
| func http_isToken(v string) bool { |
| // For historical reasons, this function is called ValidHeaderFieldName (see issue #67031). |
| return httpguts.ValidHeaderFieldName(v) |
| } |
| |
| // stringContainsCTLByte reports whether s contains any ASCII control character. |
| func http_stringContainsCTLByte(s string) bool { |
| for i := 0; i < len(s); i++ { |
| b := s[i] |
| if b < ' ' || b == 0x7f { |
| return true |
| } |
| } |
| return false |
| } |
| |
| func http_hexEscapeNonASCII(s string) string { |
| newLen := 0 |
| for i := 0; i < len(s); i++ { |
| if s[i] >= utf8.RuneSelf { |
| newLen += 3 |
| } else { |
| newLen++ |
| } |
| } |
| if newLen == len(s) { |
| return s |
| } |
| b := make([]byte, 0, newLen) |
| var pos int |
| for i := 0; i < len(s); i++ { |
| if s[i] >= utf8.RuneSelf { |
| if pos < i { |
| b = append(b, s[pos:i]...) |
| } |
| b = append(b, '%') |
| b = strconv.AppendInt(b, int64(s[i]), 16) |
| pos = i + 1 |
| } |
| } |
| if pos < len(s) { |
| b = append(b, s[pos:]...) |
| } |
| return string(b) |
| } |
| |
| // NoBody is an [io.ReadCloser] with no bytes. Read always returns EOF |
| // and Close always returns nil. It can be used in an outgoing client |
| // request to explicitly signal that a request has zero bytes. |
| // An alternative, however, is to simply set [Request.Body] to nil. |
| var http_NoBody = http_noBody{} |
| |
| type http_noBody struct{} |
| |
| func (http_noBody) Read([]byte) (int, error) { return 0, io.EOF } |
| |
| func (http_noBody) Close() error { return nil } |
| |
| func (http_noBody) WriteTo(io.Writer) (int64, error) { return 0, nil } |
| |
| var ( |
| // verify that an io.Copy from NoBody won't require a buffer: |
| _ io.WriterTo = http_NoBody |
| _ io.ReadCloser = http_NoBody |
| ) |
| |
| // PushOptions describes options for [Pusher.Push]. |
| type http_PushOptions struct { |
| // Method specifies the HTTP method for the promised request. |
| // If set, it must be "GET" or "HEAD". Empty means "GET". |
| Method string |
| |
| // Header specifies additional promised request headers. This cannot |
| // include HTTP/2 pseudo header fields like ":path" and ":scheme", |
| // which will be added automatically. |
| Header http_Header |
| } |
| |
| // Pusher is the interface implemented by ResponseWriters that support |
| // HTTP/2 server push. For more background, see |
| // https://tools.ietf.org/html/rfc7540#section-8.2. |
| type http_Pusher interface { |
| // Push initiates an HTTP/2 server push. This constructs a synthetic |
| // request using the given target and options, serializes that request |
| // into a PUSH_PROMISE frame, then dispatches that request using the |
| // server's request handler. If opts is nil, default options are used. |
| // |
| // The target must either be an absolute path (like "/path") or an absolute |
| // URL that contains a valid host and the same scheme as the parent request. |
| // If the target is a path, it will inherit the scheme and host of the |
| // parent request. |
| // |
| // The HTTP/2 spec disallows recursive pushes and cross-authority pushes. |
| // Push may or may not detect these invalid pushes; however, invalid |
| // pushes will be detected and canceled by conforming clients. |
| // |
| // Handlers that wish to push URL X should call Push before sending any |
| // data that may trigger a request for URL X. This avoids a race where the |
| // client issues requests for X before receiving the PUSH_PROMISE for X. |
| // |
| // Push will run in a separate goroutine making the order of arrival |
| // non-deterministic. Any required synchronization needs to be implemented |
| // by the caller. |
| // |
| // Push returns ErrNotSupported if the client has disabled push or if push |
| // is not supported on the underlying connection. |
| Push(target string, opts *http_PushOptions) error |
| } |
| |
| // HTTP2Config defines HTTP/2 configuration parameters common to |
| // both [Transport] and [Server]. |
| type http_HTTP2Config struct { |
| // MaxConcurrentStreams optionally specifies the number of |
| // concurrent streams that a client may have open at a time. |
| // If zero, MaxConcurrentStreams defaults to at least 100. |
| // |
| // This parameter only applies to Servers. |
| MaxConcurrentStreams int |
| |
| // StrictMaxConcurrentRequests controls whether an HTTP/2 server's |
| // concurrency limit should be respected across all connections |
| // to that server. |
| // If true, new requests sent when a connection's concurrency limit |
| // has been exceeded will block until an existing request completes. |
| // If false, an additional connection will be opened if all |
| // existing connections are at their limit. |
| // |
| // This parameter only applies to Transports. |
| StrictMaxConcurrentRequests bool |
| |
| // MaxDecoderHeaderTableSize optionally specifies an upper limit for the |
| // size of the header compression table used for decoding headers sent |
| // by the peer. |
| // A valid value is less than 4MiB. |
| // If zero or invalid, a default value is used. |
| MaxDecoderHeaderTableSize int |
| |
| // MaxEncoderHeaderTableSize optionally specifies an upper limit for the |
| // header compression table used for sending headers to the peer. |
| // A valid value is less than 4MiB. |
| // If zero or invalid, a default value is used. |
| MaxEncoderHeaderTableSize int |
| |
| // MaxReadFrameSize optionally specifies the largest frame |
| // this endpoint is willing to read. |
| // A valid value is between 16KiB and 16MiB, inclusive. |
| // If zero or invalid, a default value is used. |
| MaxReadFrameSize int |
| |
| // MaxReceiveBufferPerConnection is the maximum size of the |
| // flow control window for data received on a connection. |
| // A valid value is at least 64KiB and less than 4MiB. |
| // If invalid, a default value is used. |
| MaxReceiveBufferPerConnection int |
| |
| // MaxReceiveBufferPerStream is the maximum size of |
| // the flow control window for data received on a stream (request). |
| // A valid value is less than 4MiB. |
| // If zero or invalid, a default value is used. |
| MaxReceiveBufferPerStream int |
| |
| // SendPingTimeout is the timeout after which a health check using a ping |
| // frame will be carried out if no frame is received on a connection. |
| // If zero, no health check is performed. |
| SendPingTimeout time.Duration |
| |
| // PingTimeout is the timeout after which a connection will be closed |
| // if a response to a ping is not received. |
| // If zero, a default of 15 seconds is used. |
| PingTimeout time.Duration |
| |
| // WriteByteTimeout is the timeout after which a connection will be |
| // closed if no data can be written to it. The timeout begins when data is |
| // available to write, and is extended whenever any bytes are written. |
| WriteByteTimeout time.Duration |
| |
| // PermitProhibitedCipherSuites, if true, permits the use of |
| // cipher suites prohibited by the HTTP/2 spec. |
| PermitProhibitedCipherSuites bool |
| |
| // CountError, if non-nil, is called on HTTP/2 errors. |
| // It is intended to increment a metric for monitoring. |
| // The errType contains only lowercase letters, digits, and underscores |
| // (a-z, 0-9, _). |
| CountError func(errType string) |
| } |
| |
| // net/http supports HTTP/2 by default, but this support is removed when |
| // the nethttpomithttp2 build tag is set. |
| // |
| // HTTP/2 support is provided by the net/http/internal/http2 package. |
| // |
| // This file (http2.go) connects net/http to the http2 package. |
| // Since http imports http2, to avoid an import cycle we need to |
| // translate http package types (e.g., Request) into the equivalent |
| // http2 package types (e.g., http2.ClientRequest). |
| // |
| // The golang.org/x/net/http2 package is the original source of truth for |
| // the HTTP/2 implementation. At this time, users may still import that |
| // package and register its implementation on a net/http Transport or Server. |
| // However, the x/net package is no longer synchronized with std. |
| |
| func init() { |
| // NoBody and LocalAddrContextKey need to have the same value |
| // in the http and http2 packages. |
| // |
| // We can't define these values in net/http/internal, |
| // because their concrete types are part of the net/http API and |
| // moving them causes API checker failures. |
| // Override the http2 package versions at init time instead. |
| http2.LocalAddrContextKey = http_LocalAddrContextKey |
| http2.NoBody = http_NoBody |
| } |
| |
| type http_http2Server = http2.Server |
| |
| type http_http2Transport = http2.Transport |
| |
| func (s *http_Server) configureHTTP2() { |
| h2srv := &http2.Server{} |
| |
| // Historically, we've configured the HTTP/2 idle timeout in this fashion: |
| // Set once at configuration time. |
| if s.IdleTimeout != 0 { |
| s.h2IdleTimeout = s.IdleTimeout |
| } else { |
| s.h2IdleTimeout = s.ReadTimeout |
| } |
| |
| if s.TLSConfig == nil { |
| s.TLSConfig = &tls.Config{} |
| } |
| s.nextProtoErr = h2srv.Configure(http_http2ServerConfig{s}, s.TLSConfig) |
| if s.nextProtoErr != nil { |
| return |
| } |
| |
| s.RegisterOnShutdown(h2srv.GracefulShutdown) |
| |
| if s.TLSNextProto == nil { |
| s.TLSNextProto = make(map[string]func(*http_Server, *tls.Conn, http_Handler)) |
| } |
| // Historically, the presence of a TLSNextProto["h2"] key has been the signal to |
| // enable/disable HTTP/2 support. Set a value in the map, but we'll never use it. |
| s.TLSNextProto["h2"] = func(hs *http_Server, c *tls.Conn, h http_Handler) { |
| c.Close() |
| } |
| |
| s.h2 = h2srv |
| } |
| |
| func (s *http_Server) setHTTP2Config(conf http_http2ExternalServerConfig) { |
| if s.h2Config != nil { |
| panic("http: HTTP/2 Server already registered") |
| } |
| s.h2Config = conf |
| s.h2Config.ServeConnFunc(s.serveHTTP2Conn) |
| } |
| |
| func (s *http_Server) serveHTTP2Conn(ctx context.Context, nc net.Conn, h http_Handler, sawClientPreface bool, upgradeReq *http_Request, settings []byte) { |
| s.setupHTTP2_ServeTLS() |
| var serverUpgradeReq *http2.ServerRequest |
| if upgradeReq != nil { |
| serverUpgradeReq = http_http2ServerRequestFromRequest(upgradeReq) |
| } |
| s.h2.ServeConn(nc, &http2.ServeConnOpts{ |
| Context: ctx, |
| Handler: http_http2Handler{h}, |
| BaseConfig: http_http2ServerConfig{s}, |
| SawClientPreface: sawClientPreface, |
| UpgradeRequest: serverUpgradeReq, |
| Settings: settings, |
| }) |
| } |
| |
| func http_http2ServerRequestFromRequest(req *http_Request) *http2.ServerRequest { |
| return &http2.ServerRequest{ |
| Context: req.Context(), |
| Proto: req.Proto, |
| ProtoMajor: req.ProtoMajor, |
| ProtoMinor: req.ProtoMinor, |
| Method: req.Method, |
| URL: req.URL, |
| Header: http2.Header(req.Header), |
| Trailer: http2.Header(req.Trailer), |
| Body: req.Body, |
| Host: req.Host, |
| ContentLength: req.ContentLength, |
| RemoteAddr: req.RemoteAddr, |
| RequestURI: req.RequestURI, |
| TLS: req.TLS, |
| MultipartForm: req.MultipartForm, |
| } |
| } |
| |
| type http_http2Handler struct { |
| h http_Handler |
| } |
| |
| func (h http_http2Handler) ServeHTTP(w *http2.ResponseWriter, req *http2.ServerRequest) { |
| h.h.ServeHTTP(http_http2ResponseWriter{w}, &http_Request{ |
| ctx: req.Context, |
| Proto: "HTTP/2.0", |
| ProtoMajor: 2, |
| ProtoMinor: 0, |
| Method: req.Method, |
| URL: req.URL, |
| Header: http_Header(req.Header), |
| RequestURI: req.RequestURI, |
| Trailer: http_Header(req.Trailer), |
| Body: req.Body, |
| Host: req.Host, |
| ContentLength: req.ContentLength, |
| RemoteAddr: req.RemoteAddr, |
| TLS: req.TLS, |
| MultipartForm: req.MultipartForm, |
| }) |
| } |
| |
| type http_http2ResponseWriter struct { |
| *http2.ResponseWriter |
| } |
| |
| // Optional http.ResponseWriter interfaces implemented. |
| var ( |
| _ http_CloseNotifier = http_http2ResponseWriter{} |
| _ http_Flusher = http_http2ResponseWriter{} |
| _ io.StringWriter = http_http2ResponseWriter{} |
| ) |
| |
| func (w http_http2ResponseWriter) Flush() { w.ResponseWriter.FlushError() } |
| |
| func (w http_http2ResponseWriter) FlushError() error { return w.ResponseWriter.FlushError() } |
| |
| func (w http_http2ResponseWriter) Header() http_Header { return http_Header(w.ResponseWriter.Header()) } |
| |
| func (w http_http2ResponseWriter) Push(target string, opts *http_PushOptions) error { |
| var ( |
| method string |
| header http2.Header |
| ) |
| if opts != nil { |
| method = opts.Method |
| header = http2.Header(opts.Header) |
| } |
| err := w.ResponseWriter.Push(target, method, header) |
| if err == http2.ErrNotSupported { |
| err = http_ErrNotSupported |
| } |
| return err |
| } |
| |
| type http_http2ServerConfig struct { |
| s *http_Server |
| } |
| |
| func (s http_http2ServerConfig) MaxHeaderBytes() int { return s.s.MaxHeaderBytes } |
| |
| func (s http_http2ServerConfig) MaxHeaderValueCount() int { return s.s.maxHeaderValueCount() } |
| |
| func (s http_http2ServerConfig) ConnState(c net.Conn, st http2.ConnState) { |
| if s.s.ConnState != nil { |
| s.s.ConnState(c, http_ConnState(st)) |
| } |
| } |
| |
| func (s http_http2ServerConfig) DoKeepAlives() bool { return s.s.doKeepAlives() } |
| |
| func (s http_http2ServerConfig) WriteTimeout() time.Duration { return s.s.WriteTimeout } |
| |
| func (s http_http2ServerConfig) SendPingTimeout() time.Duration { return s.s.ReadTimeout } |
| |
| func (s http_http2ServerConfig) ErrorLog() *log.Logger { return s.s.ErrorLog } |
| |
| func (s http_http2ServerConfig) ReadTimeout() time.Duration { return s.s.ReadTimeout } |
| |
| func (s http_http2ServerConfig) DisableClientPriority() bool { return s.s.DisableClientPriority } |
| |
| func (s http_http2ServerConfig) IdleTimeout() time.Duration { |
| if s.s.h2Config != nil { |
| return s.s.h2Config.IdleTimeout() |
| } |
| return s.s.h2IdleTimeout |
| } |
| |
| func (s http_http2ServerConfig) HTTP2Config() http2.Config { |
| return http_mergeHTTP2Config(s.s.HTTP2, s.s.h2Config) |
| } |
| |
| // http2ExternalServerConfig is an HTTP/2 configuration provided by x/net/http2. |
| // |
| // When a x/net/http2.Server wraps a net/http.Server, we need to support the user |
| // setting configuration settings on the x/net Server: |
| // |
| // s1 := &http.Server{} |
| // s2 := &http2.Server{} |
| // http2.ConfigureServer(s1, s2) |
| // |
| // // This setting needs to affect s1: |
| // s2.MaxReadFrameSize = 10000 |
| // |
| // We handle this by having http2.ConfigureServer pass us an http2ExternalServerConfig |
| // (see http.Server.Serve) which we can use to query the current state of the http2.Server. |
| type http_http2ExternalServerConfig interface { |
| // Various configuration settings: |
| HTTP2Config() http_HTTP2Config |
| IdleTimeout() time.Duration |
| |
| // ServeConnFunc provides a function to the x/net/http2.Server which it |
| // can use to serve a new connection. |
| ServeConnFunc(func(ctx context.Context, nc net.Conn, h http_Handler, sawClientPreface bool, upgradeReq *http_Request, settings []byte)) |
| } |
| |
| // http2ExternalTransportConfig is an HTTP/2 configuration provided by x/net/http2. |
| // |
| // When a x/net/http2.Transport wraps a net/http.Transport, we need to support the user |
| // setting configuration settings on the x/net Transport: |
| // |
| // tr1 := &http.Transport{} |
| // tr2 := http2.ConfigureTransports(t1) |
| // |
| // // This setting needs to affect tr1: |
| // tr2.MaxHeaderListSize = 10000 |
| // |
| // We handle this by having http2.ConfigureTransports pass us an http2ExternalTransportConfig, |
| // which we can use to query the current state of the http2.Transport. |
| type http_http2ExternalTransportConfig interface { |
| // Various configuration settings: |
| HTTP2Config() http_HTTP2Config |
| DisableCompression() bool |
| MaxHeaderListSize() int64 |
| IdleConnTimeout() time.Duration |
| |
| // ConnFromContext is used to pass a net.Conn to Transport.NewClientConn |
| // via a context value. See Transport.http2NewClientConnFromContext. |
| ConnFromContext(context.Context) net.Conn |
| |
| // DialFromContext is used to dial new connections, overriding Transport.DialContext etc. |
| // This is used when the user calls x/net/http2.Transport.RoundTrip directly, |
| // in which case the historical behavior is to use the http2.Transport's dial functions. |
| DialFromContext(ctx context.Context, network, addr string) (net.Conn, error) |
| |
| // ExternalRoundTrip reports whether Transport.RoundTrip should call the |
| // external transport's RoundTrip. This is used when x/net/http2.Transport.ConnPool |
| // is set, in which case the user-provided ClientConnPool has taken responsibility |
| // for picking a connection to use. |
| ExternalRoundTrip() bool |
| |
| // RoundTrip performs a round trip. |
| // It should only be used when ExternalRoundTrip requests it. |
| RoundTrip(*http_Request) (*http_Response, error) |
| |
| // Registered is called to report successful registration of the config. |
| Registered(*http_Transport) |
| } |
| |
| func (t *http_Transport) configureHTTP2(protocols http_Protocols) { |
| if t.TLSClientConfig == nil { |
| t.TLSClientConfig = &tls.Config{} |
| } |
| if t.HTTP2 == nil { |
| t.HTTP2 = &http_HTTP2Config{} |
| } |
| t2 := http2.NewTransport(http_transportConfig{t}) |
| t.h2Transport = t2 |
| |
| t.registerProtocol("https", http_http2RoundTripper{t2, true}) |
| if t.TLSNextProto == nil { |
| t.TLSNextProto = make(map[string]func(authority string, c *tls.Conn) http_RoundTripper) |
| } |
| // Historically, the presence of a TLSNextProto["h2"] key has been the signal to |
| // enable/disable HTTP/2 support. Set a value in the map, but we'll never use it. |
| t.TLSNextProto["h2"] = func(authority string, c *tls.Conn) http_RoundTripper { |
| return http_http2ErringRoundTripper{ |
| errors.New("unexpected use of stub RoundTripper"), |
| } |
| } |
| |
| // Server.ServeTLS clones the tls.Config before modifying it. |
| // Transport doesn't. We may want to make the two consistent some day. |
| // |
| // http2configureTransport will have already set NextProtos, but adjust it again |
| // here to remove HTTP/1.1 if the user has disabled it. |
| t.TLSClientConfig.NextProtos = http_adjustNextProtos(t.TLSClientConfig.NextProtos, protocols) |
| } |
| |
| type http_http2ErringRoundTripper struct{ err error } |
| |
| func (rt http_http2ErringRoundTripper) RoundTripErr() error { return rt.err } |
| |
| func (rt http_http2ErringRoundTripper) RoundTrip(*http_Request) (*http_Response, error) { |
| return nil, rt.err |
| } |
| |
| func http_http2RoundTrip(req *http_Request, rt func(*http2.ClientRequest) (*http2.ClientResponse, error)) (*http_Response, error) { |
| resp := &http_Response{} |
| cresp, err := rt(&http2.ClientRequest{ |
| Context: req.Context(), |
| Method: req.Method, |
| URL: req.URL, |
| Header: http2.Header(req.Header), |
| Trailer: http2.Header(req.Trailer), |
| Body: req.Body, |
| Host: req.Host, |
| GetBody: req.GetBody, |
| ContentLength: req.ContentLength, |
| Cancel: req.Cancel, |
| Close: req.Close, |
| ResTrailer: (*http2.Header)(&resp.Trailer), |
| }) |
| if err != nil { |
| return nil, err |
| } |
| resp.Status = cresp.Status + " " + http_StatusText(cresp.StatusCode) |
| resp.StatusCode = cresp.StatusCode |
| resp.Proto = "HTTP/2.0" |
| resp.ProtoMajor = 2 |
| resp.ProtoMinor = 0 |
| resp.ContentLength = cresp.ContentLength |
| resp.Uncompressed = cresp.Uncompressed |
| resp.Header = http_Header(cresp.Header) |
| resp.Trailer = http_Header(cresp.Trailer) |
| resp.Body = cresp.Body |
| resp.TLS = cresp.TLS |
| resp.Request = req |
| return resp, nil |
| } |
| |
| // http2AddConn adds nc to the HTTP/2 connection pool. |
| func (t *http_Transport) http2AddConn(scheme, authority string, nc net.Conn) (http_RoundTripper, error) { |
| if t.h2Transport == nil { |
| return nil, errors.ErrUnsupported |
| } |
| err := t.h2Transport.AddConn(scheme, authority, nc) |
| if err != nil { |
| return nil, err |
| } |
| return http_http2RoundTripper{t.h2Transport, false}, nil |
| } |
| |
| // http2NewClientConn creates an HTTP/2 genericClientConn (used to implement ClientConn) from nc. |
| // The connection is not added to the HTTP/2 connection pool. |
| func (t *http_Transport) http2NewClientConn(nc net.Conn, internalStateHook func()) (http_genericClientConn, error) { |
| if t.h2Transport == nil { |
| return nil, errors.ErrUnsupported |
| } |
| cc, err := t.h2Transport.NewClientConn(nc, internalStateHook) |
| if err != nil { |
| return nil, err |
| } |
| return http_http2ClientConn{cc}, nil |
| } |
| |
| // http2NewClientConnFromContext creates a *ClientConn from a net.Conn. |
| // |
| // Transport.NewClientConn takes an address and dials a new net.Conn. |
| // We don't currently provide a simple way for the user to provide a net.Conn and get a |
| // *ClientConn out of it (although we do let the user provide their own Transport.DialContext, |
| // which can be used to effectively do this). |
| // |
| // x/net/http2.Transport.NewClientConn, in contrast, requires the user to provide a net.Conn. |
| // To support implementing the x/net/http2 NewClientConn in terms of a net/http.Transport, |
| // we permit x/net/http2 to pass us a net.Conn via a context key. |
| // |
| // http2NewClientConnFromContext handles extracting the net.Conn from the Context |
| // (when present) and creating a *ClientConn from it. |
| func (t *http_Transport) http2NewClientConnFromContext(ctx context.Context) (*http_ClientConn, error) { |
| if t.h2Config == nil { |
| return nil, errors.ErrUnsupported |
| } |
| nc := t.h2Config.ConnFromContext(ctx) |
| if nc == nil { |
| return nil, errors.ErrUnsupported |
| } |
| if t.h2Transport == nil { |
| return nil, errors.New("http: Transport does not support HTTP/2") |
| } |
| cc := &http_ClientConn{} |
| gc, err := t.http2NewClientConn(nc, cc.maybeRunStateHook) |
| if err != nil { |
| return nil, err |
| } |
| cc.stateHookMu.Lock() |
| defer cc.stateHookMu.Unlock() |
| cc.cc = gc |
| cc.lastAvailable = gc.Available() |
| return cc, nil |
| } |
| |
| // http2ExternalDial creates a new HTTP/2 connection, |
| // using the x/net/http2.Transport's dial functions. |
| // |
| // This is used when the user has called x/net/http2.Transport.RoundTrip. |
| // If the RoundTrip needs to create a new connection, |
| // the historical behavior is for it to use the http2.Transport's DialTLS or DialTLSContext |
| // functions, and not any dial functions on the http.Transport. |
| func (t *http_Transport) http2ExternalDial(ctx context.Context, cm http_connectMethod) (http_RoundTripper, error) { |
| if t.h2Config == nil { |
| return nil, errors.ErrUnsupported |
| } |
| nc, err := t.h2Config.DialFromContext(ctx, "tcp", cm.targetAddr) |
| if err != nil { |
| return nil, err |
| } |
| return t.http2AddConn(cm.targetScheme, cm.targetAddr, nc) |
| } |
| |
| type http_http2RoundTripper struct { |
| t *http2.Transport |
| mapCachedConnErr bool |
| } |
| |
| func (rt http_http2RoundTripper) RoundTrip(req *http_Request) (*http_Response, error) { |
| resp, err := http_http2RoundTrip(req, rt.t.RoundTrip) |
| if err != nil { |
| if rt.mapCachedConnErr && http_http2isNoCachedConnError(err) { |
| err = http_ErrSkipAltProtocol |
| } |
| return nil, err |
| } |
| return resp, nil |
| } |
| |
| type http_http2ClientConn struct { |
| http2.NetHTTPClientConn |
| } |
| |
| func (cc http_http2ClientConn) RoundTrip(req *http_Request) (*http_Response, error) { |
| return http_http2RoundTrip(req, cc.NetHTTPClientConn.RoundTrip) |
| } |
| |
| // transportConfig implements the http2.TransportConfig interface, |
| // providing the net/http Transport's configuration to the HTTP/2 implementation. |
| // |
| // When an x/net/http2 Transport has provided a configuration (see http2ExternalTransportConfig), |
| // the transportConfig merges the x/net/http2 and net/http Transport configurations. |
| type http_transportConfig struct { |
| t *http_Transport |
| } |
| |
| func (t http_transportConfig) MaxResponseHeaderBytes() int64 { return t.t.MaxResponseHeaderBytes } |
| |
| func (t http_transportConfig) DisableKeepAlives() bool { return t.t.DisableKeepAlives } |
| |
| func (t http_transportConfig) ExpectContinueTimeout() time.Duration { return t.t.ExpectContinueTimeout } |
| |
| func (t http_transportConfig) ResponseHeaderTimeout() time.Duration { return t.t.ResponseHeaderTimeout } |
| |
| func (t http_transportConfig) MaxHeaderListSize() int64 { |
| if t.t.h2Config != nil { |
| return t.t.h2Config.MaxHeaderListSize() |
| } |
| return 0 |
| } |
| |
| func (t http_transportConfig) DisableCompression() bool { |
| if t.t.h2Config != nil && t.t.h2Config.DisableCompression() { |
| return true |
| } |
| return t.t.DisableCompression |
| } |
| |
| func (t http_transportConfig) IdleConnTimeout() time.Duration { |
| // Unlike most config settings, historically IdleConnTimeout prefers the |
| // http2.Transport's setting over the http.Transport. |
| if t.t.h2Config != nil { |
| if timeout := t.t.h2Config.IdleConnTimeout(); timeout != 0 { |
| return timeout |
| } |
| } |
| return t.t.IdleConnTimeout |
| } |
| |
| type http_http2Configer interface { |
| HTTP2Config() http_HTTP2Config |
| } |
| |
| func http_mergeHTTP2Config(c1 *http_HTTP2Config, confer http_http2Configer) http2.Config { |
| if c1 == nil && confer == nil { |
| return http2.Config{} |
| } |
| var c http2.Config |
| if c1 != nil { |
| c = (http2.Config)(*c1) |
| } |
| var c2 http_HTTP2Config |
| if confer != nil { |
| c2 = confer.HTTP2Config() |
| } |
| if c.MaxConcurrentStreams == 0 { |
| c.MaxConcurrentStreams = c2.MaxConcurrentStreams |
| } |
| if c2.StrictMaxConcurrentRequests { |
| c.StrictMaxConcurrentRequests = true |
| } |
| if c.MaxDecoderHeaderTableSize == 0 { |
| c.MaxDecoderHeaderTableSize = c2.MaxDecoderHeaderTableSize |
| } |
| if c.MaxEncoderHeaderTableSize == 0 { |
| c.MaxEncoderHeaderTableSize = c2.MaxEncoderHeaderTableSize |
| } |
| if c.MaxReadFrameSize == 0 { |
| c.MaxReadFrameSize = c2.MaxReadFrameSize |
| } |
| if c.MaxReceiveBufferPerConnection == 0 { |
| c.MaxReceiveBufferPerConnection = c2.MaxReceiveBufferPerConnection |
| } |
| if c.MaxReceiveBufferPerStream == 0 { |
| c.MaxReceiveBufferPerStream = c2.MaxReceiveBufferPerStream |
| } |
| if c.SendPingTimeout == 0 { |
| c.SendPingTimeout = c2.SendPingTimeout |
| } |
| if c.PingTimeout == 0 { |
| c.PingTimeout = c2.PingTimeout |
| } |
| if c.WriteByteTimeout == 0 { |
| c.WriteByteTimeout = c2.WriteByteTimeout |
| } |
| if c2.PermitProhibitedCipherSuites { |
| c.PermitProhibitedCipherSuites = true |
| } |
| if c.CountError == nil { |
| c.CountError = c2.CountError |
| } |
| return c |
| } |
| |
| func (t http_transportConfig) HTTP2Config() http2.Config { |
| return http_mergeHTTP2Config(t.t.HTTP2, t.t.h2Config) |
| } |
| |
| // transportFromH1Transport provides a way for HTTP/2 tests to extract |
| // the http2.Transport from an http.Transport. |
| // |
| //go:linkname transportFromH1Transport net/http/internal/http2_test.transportFromH1Transport |
| func http_transportFromH1Transport(t *http_Transport) any { |
| t.nextProtoOnce.Do(t.onceSetNextProtoDefaults) |
| return t.h2Transport |
| } |
| |
| // A CookieJar manages storage and use of cookies in HTTP requests. |
| // |
| // Implementations of CookieJar must be safe for concurrent use by multiple |
| // goroutines. |
| // |
| // The net/http/cookiejar package provides a CookieJar implementation. |
| type http_CookieJar interface { |
| // SetCookies handles the receipt of the cookies in a reply for the |
| // given URL. It may or may not choose to save the cookies, depending |
| // on the jar's policy and implementation. |
| SetCookies(u *url.URL, cookies []*http_Cookie) |
| |
| // Cookies returns the cookies to send in a request for the given URL. |
| // It is up to the implementation to honor the standard cookie use |
| // restrictions such as in RFC 6265. |
| Cookies(u *url.URL) []*http_Cookie |
| } |
| |
| // A mapping is a collection of key-value pairs where the keys are unique. |
| // A zero mapping is empty and ready to use. |
| // A mapping tries to pick a representation that makes [mapping.find] most efficient. |
| type http_mapping[K comparable, V any] struct { |
| s []http_entry[K, V] // for few pairs |
| m map[K]V // for many pairs |
| } |
| |
| type http_entry[K comparable, V any] struct { |
| key K |
| value V |
| } |
| |
| // maxSlice is the maximum number of pairs for which a slice is used. |
| // It is a variable for benchmarking. |
| var http_maxSlice int = 8 |
| |
| // add adds a key-value pair to the mapping. |
| func (h *http_mapping[K, V]) add(k K, v V) { |
| if h.m == nil && len(h.s) < http_maxSlice { |
| h.s = append(h.s, http_entry[K, V]{k, v}) |
| } else { |
| if h.m == nil { |
| h.m = map[K]V{} |
| for _, e := range h.s { |
| h.m[e.key] = e.value |
| } |
| h.s = nil |
| } |
| h.m[k] = v |
| } |
| } |
| |
| // find returns the value corresponding to the given key. |
| // The second return value is false if there is no value |
| // with that key. |
| func (h *http_mapping[K, V]) find(k K) (v V, found bool) { |
| if h == nil { |
| return v, false |
| } |
| if h.m != nil { |
| v, found = h.m[k] |
| return v, found |
| } |
| for _, e := range h.s { |
| if e.key == k { |
| return e.value, true |
| } |
| } |
| return v, false |
| } |
| |
| // eachPair calls f for each pair in the mapping. |
| // If f returns false, pairs returns immediately. |
| func (h *http_mapping[K, V]) eachPair(f func(k K, v V) bool) { |
| if h == nil { |
| return |
| } |
| if h.m != nil { |
| for k, v := range h.m { |
| if !f(k, v) { |
| return |
| } |
| } |
| } else { |
| for _, e := range h.s { |
| if !f(e.key, e.value) { |
| return |
| } |
| } |
| } |
| } |
| |
| // Common HTTP methods. |
| // |
| // Unless otherwise noted, these are defined in RFC 7231 section 4.3. |
| const ( |
| http_MethodGet = "GET" |
| http_MethodHead = "HEAD" |
| http_MethodPost = "POST" |
| http_MethodPut = "PUT" |
| http_MethodPatch = "PATCH" // RFC 5789 |
| http_MethodDelete = "DELETE" |
| http_MethodConnect = "CONNECT" |
| http_MethodOptions = "OPTIONS" |
| http_MethodTrace = "TRACE" |
| ) |
| |
| // A pattern is something that can be matched against an HTTP request. |
| // It has an optional method, an optional host, and a path. |
| type http_pattern struct { |
| str string // original string |
| method string |
| host string |
| // The representation of a path differs from the surface syntax, which |
| // simplifies most algorithms. |
| // |
| // Paths ending in '/' are represented with an anonymous "..." wildcard. |
| // For example, the path "a/" is represented as a literal segment "a" followed |
| // by a segment with multi==true. |
| // |
| // Paths ending in "{$}" are represented with the literal segment "/". |
| // For example, the path "a/{$}" is represented as a literal segment "a" followed |
| // by a literal segment "/". |
| segments []http_segment |
| loc string // source location of registering call, for helpful messages |
| } |
| |
| func (p *http_pattern) String() string { return p.str } |
| |
| func (p *http_pattern) lastSegment() http_segment { |
| return p.segments[len(p.segments)-1] |
| } |
| |
| // A segment is a pattern piece that matches one or more path segments, or |
| // a trailing slash. |
| // |
| // If wild is false, it matches a literal segment, or, if s == "/", a trailing slash. |
| // Examples: |
| // |
| // "a" => segment{s: "a"} |
| // "/{$}" => segment{s: "/"} |
| // |
| // If wild is true and multi is false, it matches a single path segment. |
| // Example: |
| // |
| // "{x}" => segment{s: "x", wild: true} |
| // |
| // If both wild and multi are true, it matches all remaining path segments. |
| // Example: |
| // |
| // "{rest...}" => segment{s: "rest", wild: true, multi: true} |
| type http_segment struct { |
| s string // literal or wildcard name or "/" for "/{$}". |
| wild bool |
| multi bool // "..." wildcard |
| } |
| |
| // parsePattern parses a string into a Pattern. |
| // The string's syntax is |
| // |
| // [METHOD] [HOST]/[PATH] |
| // |
| // where: |
| // - METHOD is an HTTP method |
| // - HOST is a hostname |
| // - PATH consists of slash-separated segments, where each segment is either |
| // a literal or a wildcard of the form "{name}", "{name...}", or "{$}". |
| // |
| // METHOD, HOST and PATH are all optional; that is, the string can be "/". |
| // If METHOD is present, it must be followed by at least one space or tab. |
| // Wildcard names must be valid Go identifiers. |
| // The "{$}" and "{name...}" wildcard must occur at the end of PATH. |
| // PATH may end with a '/'. |
| // Wildcard names in a path must be distinct. |
| func http_parsePattern(s string) (_ *http_pattern, err error) { |
| if len(s) == 0 { |
| return nil, errors.New("empty pattern") |
| } |
| off := 0 // offset into string |
| defer func() { |
| if err != nil { |
| err = fmt.Errorf("at offset %d: %w", off, err) |
| } |
| }() |
| |
| method, rest, found := s, "", false |
| if i := strings.IndexAny(s, " \t"); i >= 0 { |
| method, rest, found = s[:i], strings.TrimLeft(s[i+1:], " \t"), true |
| } |
| if !found { |
| rest = method |
| method = "" |
| } |
| if method != "" && !http_validMethod(method) { |
| return nil, fmt.Errorf("invalid method %q", method) |
| } |
| p := &http_pattern{str: s, method: method} |
| |
| if found { |
| off = len(method) + 1 |
| } |
| i := strings.IndexByte(rest, '/') |
| if i < 0 { |
| return nil, errors.New("host/path missing /") |
| } |
| p.host = rest[:i] |
| rest = rest[i:] |
| if j := strings.IndexByte(p.host, '{'); j >= 0 { |
| off += j |
| return nil, errors.New("host contains '{' (missing initial '/'?)") |
| } |
| // At this point, rest is the path. |
| off += i |
| |
| // An unclean path with a method that is not CONNECT can never match, |
| // because paths are cleaned before matching. |
| if method != "" && method != "CONNECT" && rest != http_cleanPath(rest) { |
| return nil, errors.New("non-CONNECT pattern with unclean path can never match") |
| } |
| |
| seenNames := map[string]bool{} // remember wildcard names to catch dups |
| for len(rest) > 0 { |
| // Invariant: rest[0] == '/'. |
| rest = rest[1:] |
| off = len(s) - len(rest) |
| if len(rest) == 0 { |
| // Trailing slash. |
| p.segments = append(p.segments, http_segment{wild: true, multi: true}) |
| break |
| } |
| i := strings.IndexByte(rest, '/') |
| if i < 0 { |
| i = len(rest) |
| } |
| var seg string |
| seg, rest = rest[:i], rest[i:] |
| if i := strings.IndexByte(seg, '{'); i < 0 { |
| // Literal. |
| seg = http_pathUnescape(seg) |
| p.segments = append(p.segments, http_segment{s: seg}) |
| } else { |
| // Wildcard. |
| if i != 0 { |
| return nil, errors.New("bad wildcard segment (must start with '{')") |
| } |
| if seg[len(seg)-1] != '}' { |
| return nil, errors.New("bad wildcard segment (must end with '}')") |
| } |
| name := seg[1 : len(seg)-1] |
| if name == "$" { |
| if len(rest) != 0 { |
| return nil, errors.New("{$} not at end") |
| } |
| p.segments = append(p.segments, http_segment{s: "/"}) |
| break |
| } |
| name, multi := strings.CutSuffix(name, "...") |
| if multi && len(rest) != 0 { |
| return nil, errors.New("{...} wildcard not at end") |
| } |
| if name == "" { |
| return nil, errors.New("empty wildcard") |
| } |
| if !http_isValidWildcardName(name) { |
| return nil, fmt.Errorf("bad wildcard name %q", name) |
| } |
| if seenNames[name] { |
| return nil, fmt.Errorf("duplicate wildcard name %q", name) |
| } |
| seenNames[name] = true |
| p.segments = append(p.segments, http_segment{s: name, wild: true, multi: multi}) |
| } |
| } |
| return p, nil |
| } |
| |
| func http_isValidWildcardName(s string) bool { |
| if s == "" { |
| return false |
| } |
| // Valid Go identifier. |
| for i, c := range s { |
| if !unicode.IsLetter(c) && c != '_' && (i == 0 || !unicode.IsDigit(c)) { |
| return false |
| } |
| } |
| return true |
| } |
| |
| func http_pathUnescape(path string) string { |
| u, err := url.PathUnescape(path) |
| if err != nil { |
| // Invalidly escaped path; use the original |
| return path |
| } |
| return u |
| } |
| |
| // relationship is a relationship between two patterns, p1 and p2. |
| type http_relationship string |
| |
| const ( |
| http_equivalent http_relationship = "equivalent" // both match the same requests |
| http_moreGeneral http_relationship = "moreGeneral" // p1 matches everything p2 does & more |
| http_moreSpecific http_relationship = "moreSpecific" // p2 matches everything p1 does & more |
| http_disjoint http_relationship = "disjoint" // there is no request that both match |
| http_overlaps http_relationship = "overlaps" // there is a request that both match, but neither is more specific |
| ) |
| |
| // conflictsWith reports whether p1 conflicts with p2, that is, whether |
| // there is a request that both match but where neither is higher precedence |
| // than the other. |
| // |
| // Precedence is defined by two rules: |
| // 1. Patterns with a host win over patterns without a host. |
| // 2. Patterns whose method and path is more specific win. One pattern is more |
| // specific than another if the second matches all the (method, path) pairs |
| // of the first and more. |
| // |
| // If rule 1 doesn't apply, then two patterns conflict if their relationship |
| // is either equivalence (they match the same set of requests) or overlap |
| // (they both match some requests, but neither is more specific than the other). |
| func (p1 *http_pattern) conflictsWith(p2 *http_pattern) bool { |
| if p1.host != p2.host { |
| // Either one host is empty and the other isn't, in which case the |
| // one with the host wins by rule 1, or neither host is empty |
| // and they differ, so they won't match the same paths. |
| return false |
| } |
| rel := p1.comparePathsAndMethods(p2) |
| return rel == http_equivalent || rel == http_overlaps |
| } |
| |
| func (p1 *http_pattern) comparePathsAndMethods(p2 *http_pattern) http_relationship { |
| mrel := p1.compareMethods(p2) |
| // Optimization: avoid a call to comparePaths. |
| if mrel == http_disjoint { |
| return http_disjoint |
| } |
| prel := p1.comparePaths(p2) |
| return http_combineRelationships(mrel, prel) |
| } |
| |
| // compareMethods determines the relationship between the method |
| // part of patterns p1 and p2. |
| // |
| // A method can either be empty, "GET", or something else. |
| // The empty string matches any method, so it is the most general. |
| // "GET" matches both GET and HEAD. |
| // Anything else matches only itself. |
| func (p1 *http_pattern) compareMethods(p2 *http_pattern) http_relationship { |
| if p1.method == p2.method { |
| return http_equivalent |
| } |
| if p1.method == "" { |
| // p1 matches any method, but p2 does not, so p1 is more general. |
| return http_moreGeneral |
| } |
| if p2.method == "" { |
| return http_moreSpecific |
| } |
| if p1.method == "GET" && p2.method == "HEAD" { |
| // p1 matches GET and HEAD; p2 matches only HEAD. |
| return http_moreGeneral |
| } |
| if p2.method == "GET" && p1.method == "HEAD" { |
| return http_moreSpecific |
| } |
| return http_disjoint |
| } |
| |
| // comparePaths determines the relationship between the path |
| // part of two patterns. |
| func (p1 *http_pattern) comparePaths(p2 *http_pattern) http_relationship { |
| // Optimization: if a path pattern doesn't end in a multi ("...") wildcard, then it |
| // can only match paths with the same number of segments. |
| if len(p1.segments) != len(p2.segments) && !p1.lastSegment().multi && !p2.lastSegment().multi { |
| return http_disjoint |
| } |
| |
| // Consider corresponding segments in the two path patterns. |
| var segs1, segs2 []http_segment |
| rel := http_equivalent |
| for segs1, segs2 = p1.segments, p2.segments; len(segs1) > 0 && len(segs2) > 0; segs1, segs2 = segs1[1:], segs2[1:] { |
| rel = http_combineRelationships(rel, http_compareSegments(segs1[0], segs2[0])) |
| if rel == http_disjoint { |
| return rel |
| } |
| } |
| // We've reached the end of the corresponding segments of the patterns. |
| // If they have the same number of segments, then we've already determined |
| // their relationship. |
| if len(segs1) == 0 && len(segs2) == 0 { |
| return rel |
| } |
| // Otherwise, the only way they could fail to be disjoint is if the shorter |
| // pattern ends in a multi. In that case, that multi is more general |
| // than the remainder of the longer pattern, so combine those two relationships. |
| if len(segs1) < len(segs2) && p1.lastSegment().multi { |
| return http_combineRelationships(rel, http_moreGeneral) |
| } |
| if len(segs2) < len(segs1) && p2.lastSegment().multi { |
| return http_combineRelationships(rel, http_moreSpecific) |
| } |
| return http_disjoint |
| } |
| |
| // compareSegments determines the relationship between two segments. |
| func http_compareSegments(s1, s2 http_segment) http_relationship { |
| if s1.multi && s2.multi { |
| return http_equivalent |
| } |
| if s1.multi { |
| return http_moreGeneral |
| } |
| if s2.multi { |
| return http_moreSpecific |
| } |
| if s1.wild && s2.wild { |
| return http_equivalent |
| } |
| if s1.wild { |
| if s2.s == "/" { |
| // A single wildcard doesn't match a trailing slash. |
| return http_disjoint |
| } |
| return http_moreGeneral |
| } |
| if s2.wild { |
| if s1.s == "/" { |
| return http_disjoint |
| } |
| return http_moreSpecific |
| } |
| // Both literals. |
| if s1.s == s2.s { |
| return http_equivalent |
| } |
| return http_disjoint |
| } |
| |
| // combineRelationships determines the overall relationship of two patterns |
| // given the relationships of a partition of the patterns into two parts. |
| // |
| // For example, if p1 is more general than p2 in one way but equivalent |
| // in the other, then it is more general overall. |
| // |
| // Or if p1 is more general in one way and more specific in the other, then |
| // they overlap. |
| func http_combineRelationships(r1, r2 http_relationship) http_relationship { |
| switch r1 { |
| case http_equivalent: |
| return r2 |
| case http_disjoint: |
| return http_disjoint |
| case http_overlaps: |
| if r2 == http_disjoint { |
| return http_disjoint |
| } |
| return http_overlaps |
| case http_moreGeneral, http_moreSpecific: |
| switch r2 { |
| case http_equivalent: |
| return r1 |
| case http_inverseRelationship(r1): |
| return http_overlaps |
| default: |
| return r2 |
| } |
| default: |
| panic(fmt.Sprintf("unknown relationship %q", r1)) |
| } |
| } |
| |
| // If p1 has relationship ` + "`" + `r` + "`" + ` to p2, then |
| // p2 has inverseRelationship(r) to p1. |
| func http_inverseRelationship(r http_relationship) http_relationship { |
| switch r { |
| case http_moreSpecific: |
| return http_moreGeneral |
| case http_moreGeneral: |
| return http_moreSpecific |
| default: |
| return r |
| } |
| } |
| |
| // describeConflict returns an explanation of why two patterns conflict. |
| func http_describeConflict(p1, p2 *http_pattern) string { |
| mrel := p1.compareMethods(p2) |
| prel := p1.comparePaths(p2) |
| rel := http_combineRelationships(mrel, prel) |
| if rel == http_equivalent { |
| return fmt.Sprintf("%s matches the same requests as %s", p1, p2) |
| } |
| if rel != http_overlaps { |
| panic("describeConflict called with non-conflicting patterns") |
| } |
| if prel == http_overlaps { |
| return fmt.Sprintf(` + "`" + `%[1]s and %[2]s both match some paths, like %[3]q. |
| But neither is more specific than the other. |
| %[1]s matches %[4]q, but %[2]s doesn't. |
| %[2]s matches %[5]q, but %[1]s doesn't.` + "`" + `, |
| p1, p2, http_commonPath(p1, p2), http_differencePath(p1, p2), http_differencePath(p2, p1)) |
| } |
| if mrel == http_moreGeneral && prel == http_moreSpecific { |
| return fmt.Sprintf("%s matches more methods than %s, but has a more specific path pattern", p1, p2) |
| } |
| if mrel == http_moreSpecific && prel == http_moreGeneral { |
| return fmt.Sprintf("%s matches fewer methods than %s, but has a more general path pattern", p1, p2) |
| } |
| return fmt.Sprintf("bug: unexpected way for two patterns %s and %s to conflict: methods %s, paths %s", p1, p2, mrel, prel) |
| } |
| |
| // writeMatchingPath writes to b a path that matches the segments. |
| func http_writeMatchingPath(b *strings.Builder, segs []http_segment) { |
| for _, s := range segs { |
| http_writeSegment(b, s) |
| } |
| } |
| |
| func http_writeSegment(b *strings.Builder, s http_segment) { |
| b.WriteByte('/') |
| if !s.multi && s.s != "/" { |
| b.WriteString(s.s) |
| } |
| } |
| |
| // commonPath returns a path that both p1 and p2 match. |
| // It assumes there is such a path. |
| func http_commonPath(p1, p2 *http_pattern) string { |
| var b strings.Builder |
| var segs1, segs2 []http_segment |
| for segs1, segs2 = p1.segments, p2.segments; len(segs1) > 0 && len(segs2) > 0; segs1, segs2 = segs1[1:], segs2[1:] { |
| if s1 := segs1[0]; s1.wild { |
| http_writeSegment(&b, segs2[0]) |
| } else { |
| http_writeSegment(&b, s1) |
| } |
| } |
| if len(segs1) > 0 { |
| http_writeMatchingPath(&b, segs1) |
| } else if len(segs2) > 0 { |
| http_writeMatchingPath(&b, segs2) |
| } |
| return b.String() |
| } |
| |
| // differencePath returns a path that p1 matches and p2 doesn't. |
| // It assumes there is such a path. |
| func http_differencePath(p1, p2 *http_pattern) string { |
| var b strings.Builder |
| |
| var segs1, segs2 []http_segment |
| for segs1, segs2 = p1.segments, p2.segments; len(segs1) > 0 && len(segs2) > 0; segs1, segs2 = segs1[1:], segs2[1:] { |
| s1 := segs1[0] |
| s2 := segs2[0] |
| if s1.multi && s2.multi { |
| // From here the patterns match the same paths, so we must have found a difference earlier. |
| b.WriteByte('/') |
| return b.String() |
| |
| } |
| if s1.multi && !s2.multi { |
| // s1 ends in a "..." wildcard but s2 does not. |
| // A trailing slash will distinguish them, unless s2 ends in "{$}", |
| // in which case any segment will do; prefer the wildcard name if |
| // it has one. |
| b.WriteByte('/') |
| if s2.s == "/" { |
| if s1.s != "" { |
| b.WriteString(s1.s) |
| } else { |
| b.WriteString("x") |
| } |
| } |
| return b.String() |
| } |
| if !s1.multi && s2.multi { |
| http_writeSegment(&b, s1) |
| } else if s1.wild && s2.wild { |
| // Both patterns will match whatever we put here; use |
| // the first wildcard name. |
| http_writeSegment(&b, s1) |
| } else if s1.wild && !s2.wild { |
| // s1 is a wildcard, s2 is a literal. |
| // Any segment other than s2.s will work. |
| // Prefer the wildcard name, but if it's the same as the literal, |
| // tweak the literal. |
| if s1.s != s2.s { |
| http_writeSegment(&b, s1) |
| } else { |
| b.WriteByte('/') |
| b.WriteString(s2.s + "x") |
| } |
| } else if !s1.wild && s2.wild { |
| http_writeSegment(&b, s1) |
| } else { |
| // Both are literals. A precondition of this function is that the |
| // patterns overlap, so they must be the same literal. Use it. |
| if s1.s != s2.s { |
| panic(fmt.Sprintf("literals differ: %q and %q", s1.s, s2.s)) |
| } |
| http_writeSegment(&b, s1) |
| } |
| } |
| if len(segs1) > 0 { |
| // p1 is longer than p2, and p2 does not end in a multi. |
| // Anything that matches the rest of p1 will do. |
| http_writeMatchingPath(&b, segs1) |
| } else if len(segs2) > 0 { |
| http_writeMatchingPath(&b, segs2) |
| } |
| return b.String() |
| } |
| |
| const ( |
| http_defaultMaxMemory = 32 << 20 // 32 MB |
| ) |
| |
| // ErrMissingFile is returned by FormFile when the provided file field name |
| // is either not present in the request or not a file field. |
| var http_ErrMissingFile = errors.New("http: no such file") |
| |
| // ProtocolError represents an HTTP protocol error. |
| // |
| // Deprecated: Not all errors in the http package related to protocol errors |
| // are of type ProtocolError. |
| type http_ProtocolError struct { |
| ErrorString string |
| } |
| |
| func (pe *http_ProtocolError) Error() string { return pe.ErrorString } |
| |
| // Is lets http.ErrNotSupported match errors.ErrUnsupported. |
| func (pe *http_ProtocolError) Is(err error) bool { |
| return pe == http_ErrNotSupported && err == errors.ErrUnsupported |
| } |
| |
| var ( |
| // ErrNotSupported indicates that a feature is not supported. |
| // |
| // It is returned by ResponseController methods to indicate that |
| // the handler does not support the method, and by the Push method |
| // of Pusher implementations to indicate that HTTP/2 Push support |
| // is not available. |
| http_ErrNotSupported = &http_ProtocolError{"feature not supported"} |
| |
| // Deprecated: ErrUnexpectedTrailer is no longer returned by |
| // anything in the net/http package. Callers should not |
| // compare errors against this variable. |
| http_ErrUnexpectedTrailer = &http_ProtocolError{"trailer header without chunked transfer encoding"} |
| |
| // ErrMissingBoundary is returned by Request.MultipartReader when the |
| // request's Content-Type does not include a "boundary" parameter. |
| http_ErrMissingBoundary = &http_ProtocolError{"no multipart boundary param in Content-Type"} |
| |
| // ErrNotMultipart is returned by Request.MultipartReader when the |
| // request's Content-Type is not multipart/form-data. |
| http_ErrNotMultipart = &http_ProtocolError{"request Content-Type isn't multipart/form-data"} |
| |
| // Deprecated: ErrHeaderTooLong is no longer returned by |
| // anything in the net/http package. Callers should not |
| // compare errors against this variable. |
| http_ErrHeaderTooLong = &http_ProtocolError{"header too long"} |
| |
| // Deprecated: ErrShortBody is no longer returned by |
| // anything in the net/http package. Callers should not |
| // compare errors against this variable. |
| http_ErrShortBody = &http_ProtocolError{"entity body too short"} |
| |
| // Deprecated: ErrMissingContentLength is no longer returned by |
| // anything in the net/http package. Callers should not |
| // compare errors against this variable. |
| http_ErrMissingContentLength = &http_ProtocolError{"missing ContentLength in HEAD response"} |
| ) |
| |
| func http_badStringError(what, val string) error { return fmt.Errorf("%s %q", what, val) } |
| |
| // Headers that Request.Write handles itself and should be skipped. |
| var http_reqWriteExcludeHeader = map[string]bool{ |
| "Host": true, // not in Header map anyway |
| "User-Agent": true, |
| "Content-Length": true, |
| "Transfer-Encoding": true, |
| "Trailer": true, |
| } |
| |
| // A Request represents an HTTP request received by a server |
| // or to be sent by a client. |
| // |
| // The field semantics differ slightly between client and server |
| // usage. In addition to the notes on the fields below, see the |
| // documentation for [Request.Write] and [RoundTripper]. |
| type http_Request struct { |
| // Method specifies the HTTP method (GET, POST, PUT, etc.). |
| // For client requests, an empty string means GET. |
| Method string |
| |
| // URL specifies either the URI being requested (for server |
| // requests) or the URL to access (for client requests). |
| // |
| // For server requests, the URL is parsed from the URI |
| // supplied on the Request-Line as stored in RequestURI. For |
| // most requests, fields other than Path and RawQuery will be |
| // empty. (See RFC 7230, Section 5.3) |
| // |
| // For client requests, the URL's Host specifies the server to |
| // connect to, while the Request's Host field optionally |
| // specifies the Host header value to send in the HTTP |
| // request. |
| URL *url.URL |
| |
| // The protocol version for incoming server requests. |
| // |
| // For client requests, these fields are ignored. The HTTP |
| // client code always uses either HTTP/1.1 or HTTP/2. |
| // See the docs on Transport for details. |
| Proto string // "HTTP/1.0" |
| ProtoMajor int // 1 |
| ProtoMinor int // 0 |
| |
| // Header contains the request header fields either received |
| // by the server or to be sent by the client. |
| // |
| // If a server received a request with header lines, |
| // |
| // Host: example.com |
| // accept-encoding: gzip, deflate |
| // Accept-Language: en-us |
| // fOO: Bar |
| // foo: two |
| // |
| // then |
| // |
| // Header = map[string][]string{ |
| // "Accept-Encoding": {"gzip, deflate"}, |
| // "Accept-Language": {"en-us"}, |
| // "Foo": {"Bar", "two"}, |
| // } |
| // |
| // For incoming requests, the Host header is promoted to the |
| // Request.Host field and removed from the Header map. |
| // |
| // HTTP defines that header names are case-insensitive. The |
| // request parser implements this by using CanonicalHeaderKey, |
| // making the first character and any characters following a |
| // hyphen uppercase and the rest lowercase. |
| // |
| // For client requests, certain headers such as Content-Length |
| // and Connection are automatically written when needed and |
| // values in Header may be ignored. See the documentation |
| // for the Request.Write method. |
| Header http_Header |
| |
| // Body is the request's body. |
| // |
| // For client requests, a nil body means the request has no |
| // body, such as a GET request. The HTTP Client's Transport |
| // is responsible for calling the Close method. |
| // |
| // For server requests, the Request Body is always non-nil |
| // but will return EOF immediately when no body is present. |
| // The Server will close the request body. The ServeHTTP |
| // Handler does not need to. |
| // |
| // Body must allow Read to be called concurrently with Close. |
| // In particular, calling Close should unblock a Read waiting |
| // for input. |
| Body io.ReadCloser |
| |
| // GetBody defines an optional func to return a new copy of |
| // Body. It is used for client requests when a redirect requires |
| // reading the body more than once. Use of GetBody still |
| // requires setting Body. |
| // |
| // For server requests, it is unused. |
| GetBody func() (io.ReadCloser, error) |
| |
| // ContentLength records the length of the associated content. |
| // The value -1 indicates that the length is unknown. |
| // Values >= 0 indicate that the given number of bytes may |
| // be read from Body. |
| // |
| // For client requests, a value of 0 with a non-nil Body is |
| // also treated as unknown. |
| ContentLength int64 |
| |
| // TransferEncoding lists the transfer encodings from outermost to |
| // innermost. An empty list denotes the "identity" encoding. |
| // TransferEncoding can usually be ignored; chunked encoding is |
| // automatically added and removed as necessary when sending and |
| // receiving requests. |
| TransferEncoding []string |
| |
| // Close indicates whether to close the connection after |
| // replying to this request (for servers) or after sending this |
| // request and reading its response (for clients). |
| // |
| // For server requests, the HTTP server handles this automatically |
| // and this field is not needed by Handlers. |
| // |
| // For client requests, setting this field prevents re-use of |
| // TCP connections between requests to the same hosts, as if |
| // Transport.DisableKeepAlives were set. |
| Close bool |
| |
| // For server requests, Host specifies the host on which the |
| // URL is sought. For HTTP/1 (per RFC 7230, section 5.4), this |
| // is either the value of the "Host" header or the host name |
| // given in the URL itself. For HTTP/2, it is the value of the |
| // ":authority" pseudo-header field. |
| // It may be of the form "host:port". For international domain |
| // names, Host may be in Punycode or Unicode form. Use |
| // golang.org/x/net/idna to convert it to either format if |
| // needed. |
| // To prevent DNS rebinding attacks, server Handlers should |
| // validate that the Host header has a value for which the |
| // Handler considers itself authoritative. The included |
| // ServeMux supports patterns registered to particular host |
| // names and thus protects its registered Handlers. |
| // |
| // For client requests, Host optionally overrides the Host |
| // header to send. If empty, the Request.Write method uses |
| // the value of URL.Host. Host may contain an international |
| // domain name. |
| Host string |
| |
| // Form contains the parsed form data, including both the URL |
| // field's query parameters and the PATCH, POST, or PUT form data. |
| // This field is only available after ParseForm is called. |
| // The HTTP client ignores Form and uses Body instead. |
| Form url.Values |
| |
| // PostForm contains the parsed form data from PATCH, POST |
| // or PUT body parameters. |
| // |
| // This field is only available after ParseForm is called. |
| // The HTTP client ignores PostForm and uses Body instead. |
| PostForm url.Values |
| |
| // MultipartForm is the parsed multipart form, including file uploads. |
| // This field is only available after ParseMultipartForm is called. |
| // The HTTP client ignores MultipartForm and uses Body instead. |
| MultipartForm *multipart.Form |
| |
| // Trailer specifies additional headers that are sent after the request |
| // body. |
| // |
| // For server requests, the Trailer map initially contains only the |
| // trailer keys, with nil values. (The client declares which trailers it |
| // will later send.) While the handler is reading from Body, it must |
| // not reference Trailer. After reading from Body returns EOF, Trailer |
| // can be read again and will contain non-nil values, if they were sent |
| // by the client. |
| // |
| // For client requests, Trailer must be initialized to a map containing |
| // the trailer keys to later send. The values may be nil or their final |
| // values. The ContentLength must be 0 or -1, to send a chunked request. |
| // After the HTTP request is sent the map values can be updated while |
| // the request body is read. Once the body returns EOF, the caller must |
| // not mutate Trailer. |
| // |
| // Writing a request whose Trailer contains a key with invalid bytes |
| // (such as CR or LF), or such a value present when Write begins, |
| // returns an error. |
| // |
| // Few HTTP clients, servers, or proxies support HTTP trailers. |
| Trailer http_Header |
| |
| // RemoteAddr allows HTTP servers and other software to record |
| // the network address that sent the request, usually for |
| // logging. This field is not filled in by ReadRequest and |
| // has no defined format. The HTTP server in this package |
| // sets RemoteAddr to an "IP:port" address before invoking a |
| // handler. |
| // This field is ignored by the HTTP client. |
| RemoteAddr string |
| |
| // RequestURI is the unmodified request-target of the |
| // Request-Line (RFC 7230, Section 3.1.1) as sent by the client |
| // to a server. Usually the URL field should be used instead. |
| // It is an error to set this field in an HTTP client request. |
| RequestURI string |
| |
| // TLS allows HTTP servers and other software to record |
| // information about the TLS connection on which the request |
| // was received. This field is not filled in by ReadRequest. |
| // The HTTP server in this package sets the field for |
| // TLS-enabled connections before invoking a handler; |
| // otherwise it leaves the field nil. |
| // This field is ignored by the HTTP client. |
| TLS *tls.ConnectionState |
| |
| // Cancel is an optional channel whose closure indicates that the client |
| // request should be regarded as canceled. Not all implementations of |
| // RoundTripper may support Cancel. |
| // |
| // For server requests, this field is not applicable. |
| // |
| // Deprecated: Set the Request's context with NewRequestWithContext |
| // instead. If a Request's Cancel field and context are both |
| // set, it is undefined whether Cancel is respected. |
| Cancel <-chan struct{} |
| |
| // Response is the redirect response which caused this request |
| // to be created. This field is only populated during client |
| // redirects. |
| Response *http_Response |
| |
| // Pattern is the [ServeMux] pattern that matched the request. |
| // It is empty if the request was not matched against a pattern. |
| Pattern string |
| |
| // ctx is either the client or server context. It should only |
| // be modified via copying the whole Request using Clone or WithContext. |
| // It is unexported to prevent people from using Context wrong |
| // and mutating the contexts held by callers of the same request. |
| ctx context.Context |
| |
| // The following fields are for requests matched by ServeMux. |
| pat *http_pattern // the pattern that matched |
| matches []string // values for the matching wildcards in pat |
| otherValues map[string]string // for calls to SetPathValue that don't match a wildcard |
| } |
| |
| // Context returns the request's context. To change the context, use |
| // [Request.Clone] or [Request.WithContext]. |
| // |
| // The returned context is always non-nil; it defaults to the |
| // background context. |
| // |
| // For outgoing client requests, the context controls cancellation. |
| // |
| // For incoming server requests, the context is canceled when the |
| // client's connection closes, the request is canceled (with HTTP/2), |
| // or when the ServeHTTP method returns. |
| func (r *http_Request) Context() context.Context { |
| if r.ctx != nil { |
| return r.ctx |
| } |
| return context.Background() |
| } |
| |
| // WithContext returns a shallow copy of r with its context changed |
| // to ctx. The provided ctx must be non-nil. |
| // |
| // For outgoing client request, the context controls the entire |
| // lifetime of a request and its response: obtaining a connection, |
| // sending the request, and reading the response headers and body. |
| // |
| // To create a new request with a context, use [NewRequestWithContext]. |
| // To make a deep copy of a request with a new context, use [Request.Clone]. |
| func (r *http_Request) WithContext(ctx context.Context) *http_Request { |
| if ctx == nil { |
| panic("nil context") |
| } |
| r2 := new(http_Request) |
| *r2 = *r |
| r2.ctx = ctx |
| return r2 |
| } |
| |
| // Clone returns a deep copy of r with its context changed to ctx. |
| // The provided ctx must be non-nil. |
| // |
| // Clone only makes a shallow copy of the Body field. |
| // |
| // For an outgoing client request, the context controls the entire |
| // lifetime of a request and its response: obtaining a connection, |
| // sending the request, and reading the response headers and body. |
| func (r *http_Request) Clone(ctx context.Context) *http_Request { |
| if ctx == nil { |
| panic("nil context") |
| } |
| r2 := new(http_Request) |
| *r2 = *r |
| r2.ctx = ctx |
| r2.URL = http_cloneURL(r.URL) |
| r2.Header = r.Header.Clone() |
| r2.Trailer = r.Trailer.Clone() |
| if s := r.TransferEncoding; s != nil { |
| s2 := make([]string, len(s)) |
| copy(s2, s) |
| r2.TransferEncoding = s2 |
| } |
| r2.Form = http_cloneURLValues(r.Form) |
| r2.PostForm = http_cloneURLValues(r.PostForm) |
| r2.MultipartForm = http_cloneMultipartForm(r.MultipartForm) |
| |
| // Copy matches and otherValues. See issue 61410. |
| if s := r.matches; s != nil { |
| s2 := make([]string, len(s)) |
| copy(s2, s) |
| r2.matches = s2 |
| } |
| r2.otherValues = maps.Clone(r.otherValues) |
| return r2 |
| } |
| |
| // ProtoAtLeast reports whether the HTTP protocol used |
| // in the request is at least major.minor. |
| func (r *http_Request) ProtoAtLeast(major, minor int) bool { |
| return r.ProtoMajor > major || |
| r.ProtoMajor == major && r.ProtoMinor >= minor |
| } |
| |
| // UserAgent returns the client's User-Agent, if sent in the request. |
| func (r *http_Request) UserAgent() string { |
| return r.Header.Get("User-Agent") |
| } |
| |
| // Cookies parses and returns the HTTP cookies sent with the request. |
| func (r *http_Request) Cookies() []*http_Cookie { |
| return http_readCookies(r.Header, "") |
| } |
| |
| // CookiesNamed parses and returns the named HTTP cookies sent with the request |
| // or an empty slice if none matched. |
| func (r *http_Request) CookiesNamed(name string) []*http_Cookie { |
| if name == "" { |
| return []*http_Cookie{} |
| } |
| return http_readCookies(r.Header, name) |
| } |
| |
| // ErrNoCookie is returned by Request's Cookie method when a cookie is not found. |
| var http_ErrNoCookie = errors.New("http: named cookie not present") |
| |
| // Cookie returns the named cookie provided in the request or |
| // [ErrNoCookie] if not found. |
| // If multiple cookies match the given name, only one cookie will |
| // be returned. |
| func (r *http_Request) Cookie(name string) (*http_Cookie, error) { |
| if name == "" { |
| return nil, http_ErrNoCookie |
| } |
| for _, c := range http_readCookies(r.Header, name) { |
| return c, nil |
| } |
| return nil, http_ErrNoCookie |
| } |
| |
| // AddCookie adds a cookie to the request. Per RFC 6265 section 5.4, |
| // AddCookie does not attach more than one [Cookie] header field. That |
| // means all cookies, if any, are written into the same line, |
| // separated by semicolon. |
| // AddCookie only sanitizes c's name and value, and does not sanitize |
| // a Cookie header already present in the request. |
| func (r *http_Request) AddCookie(c *http_Cookie) { |
| s := fmt.Sprintf("%s=%s", http_sanitizeCookieName(c.Name), http_sanitizeCookieValue(c.Value, c.Quoted)) |
| if c := r.Header.Get("Cookie"); c != "" { |
| r.Header.Set("Cookie", c+"; "+s) |
| } else { |
| r.Header.Set("Cookie", s) |
| } |
| } |
| |
| // Referer returns the referring URL, if sent in the request. |
| // |
| // Referer is misspelled as in the request itself, a mistake from the |
| // earliest days of HTTP. This value can also be fetched from the |
| // [Header] map as Header["Referer"]; the benefit of making it available |
| // as a method is that the compiler can diagnose programs that use the |
| // alternate (correct English) spelling req.Referrer() but cannot |
| // diagnose programs that use Header["Referrer"]. |
| func (r *http_Request) Referer() string { |
| return r.Header.Get("Referer") |
| } |
| |
| // multipartByReader is a sentinel value. |
| // Its presence in Request.MultipartForm indicates that parsing of the request |
| // body has been handed off to a MultipartReader instead of ParseMultipartForm. |
| var http_multipartByReader = &multipart.Form{ |
| Value: make(map[string][]string), |
| File: make(map[string][]*multipart.FileHeader), |
| } |
| |
| // MultipartReader returns a MIME multipart reader if this is a |
| // multipart/form-data or a multipart/mixed POST request, else returns nil and an error. |
| // Use this function instead of [Request.ParseMultipartForm] to |
| // process the request body as a stream. |
| func (r *http_Request) MultipartReader() (*multipart.Reader, error) { |
| if r.MultipartForm == http_multipartByReader { |
| return nil, errors.New("http: MultipartReader called twice") |
| } |
| if r.MultipartForm != nil { |
| return nil, errors.New("http: multipart handled by ParseMultipartForm") |
| } |
| r.MultipartForm = http_multipartByReader |
| return r.multipartReader(true) |
| } |
| |
| func (r *http_Request) multipartReader(allowMixed bool) (*multipart.Reader, error) { |
| v := r.Header.Get("Content-Type") |
| if v == "" { |
| return nil, http_ErrNotMultipart |
| } |
| if r.Body == nil { |
| return nil, errors.New("missing form body") |
| } |
| d, params, err := mime.ParseMediaType(v) |
| if err != nil || !(d == "multipart/form-data" || allowMixed && d == "multipart/mixed") { |
| return nil, http_ErrNotMultipart |
| } |
| boundary, ok := params["boundary"] |
| if !ok { |
| return nil, http_ErrMissingBoundary |
| } |
| return multipart.NewReader(r.Body, boundary), nil |
| } |
| |
| // isH2Upgrade reports whether r represents the http2 "client preface" |
| // magic string. |
| func (r *http_Request) isH2Upgrade() bool { |
| return r.Method == "PRI" && len(r.Header) == 0 && r.URL.Path == "*" && r.Proto == "HTTP/2.0" |
| } |
| |
| // Return value if nonempty, def otherwise. |
| func http_valueOrDefault(value, def string) string { |
| if value != "" { |
| return value |
| } |
| return def |
| } |
| |
| // NOTE: This is not intended to reflect the actual Go version being used. |
| // It was changed at the time of Go 1.1 release because the former User-Agent |
| // had ended up blocked by some intrusion detection systems. |
| // See https://codereview.appspot.com/7532043. |
| const http_defaultUserAgent = "Go-http-client/1.1" |
| |
| // Write writes an HTTP/1.1 request, which is the header and body, in wire format. |
| // This method consults the following fields of the request: |
| // |
| // Host |
| // URL |
| // Method (defaults to "GET") |
| // Header |
| // ContentLength |
| // TransferEncoding |
| // Body |
| // |
| // If Body is present, Content-Length is <= 0 and [Request.TransferEncoding] |
| // hasn't been set to "identity", Write adds "Transfer-Encoding: |
| // chunked" to the header. Body is closed after it is sent. |
| // |
| // Header values for Host, Content-Length, Transfer-Encoding, |
| // and Trailer are not used; these are derived from other Request fields. |
| // If the Header does not contain a User-Agent value, Write uses |
| // "Go-http-client/1.1". |
| func (r *http_Request) Write(w io.Writer) error { |
| return r.write(w, false, nil, nil) |
| } |
| |
| // WriteProxy is like [Request.Write] but writes the request in the form |
| // expected by an HTTP proxy. In particular, [Request.WriteProxy] writes the |
| // initial Request-URI line of the request with an absolute URI, per |
| // section 5.3 of RFC 7230, including the scheme and host. |
| // In either case, WriteProxy also writes a Host header, using |
| // either r.Host or r.URL.Host. |
| func (r *http_Request) WriteProxy(w io.Writer) error { |
| return r.write(w, true, nil, nil) |
| } |
| |
| // errMissingHost is returned by Write when there is no Host or URL present in |
| // the Request. |
| var http_errMissingHost = errors.New("http: Request.Write on Request with no Host or URL set") |
| |
| // extraHeaders may be nil |
| // waitForContinue may be nil |
| // always closes body |
| func (r *http_Request) write(w io.Writer, usingProxy bool, extraHeaders http_Header, waitForContinue func() bool) (err error) { |
| trace := httptrace.ContextClientTrace(r.Context()) |
| if trace != nil && trace.WroteRequest != nil { |
| defer func() { |
| trace.WroteRequest(httptrace.WroteRequestInfo{ |
| Err: err, |
| }) |
| }() |
| } |
| closed := false |
| defer func() { |
| if closed { |
| return |
| } |
| if closeErr := r.closeBody(); closeErr != nil && err == nil { |
| err = closeErr |
| } |
| }() |
| |
| // Find the target host. Prefer the Host: header, but if that |
| // is not given, use the host from the request URL. |
| // |
| // Clean the host, in case it arrives with unexpected stuff in it. |
| host := r.Host |
| if host == "" { |
| if r.URL == nil { |
| return http_errMissingHost |
| } |
| host = r.URL.Host |
| } |
| host, err = httpguts.PunycodeHostPort(host) |
| if err != nil { |
| return err |
| } |
| // Validate that the Host header is a valid header in general, |
| // but don't validate the host itself. This is sufficient to avoid |
| // header or request smuggling via the Host field. |
| // The server can (and will, if it's a net/http server) reject |
| // the request if it doesn't consider the host valid. |
| if !httpguts.ValidHostHeader(host) { |
| // Historically, we would truncate the Host header after '/' or ' '. |
| // Some users have relied on this truncation to convert a network |
| // address such as Unix domain socket path into a valid, ignored |
| // Host header (see https://go.dev/issue/61431). |
| // |
| // We don't preserve the truncation, because sending an altered |
| // header field opens a smuggling vector. Instead, zero out the |
| // Host header entirely if it isn't valid. (An empty Host is valid; |
| // see RFC 9112 Section 3.2.) |
| // |
| // Return an error if we're sending to a proxy, since the proxy |
| // probably can't do anything useful with an empty Host header. |
| if !usingProxy { |
| host = "" |
| } else { |
| return errors.New("http: invalid Host header") |
| } |
| } |
| |
| // According to RFC 6874, an HTTP client, proxy, or other |
| // intermediary must remove any IPv6 zone identifier attached |
| // to an outgoing URI. |
| host = http_removeZone(host) |
| |
| ruri := r.URL.RequestURI() |
| if usingProxy && r.URL.Scheme != "" && r.URL.Opaque == "" { |
| ruri = r.URL.Scheme + "://" + host + ruri |
| } else if r.Method == "CONNECT" && r.URL.Path == "" { |
| // CONNECT requests normally give just the host and port, not a full URL. |
| ruri = host |
| if r.URL.Opaque != "" { |
| ruri = r.URL.Opaque |
| } |
| } |
| if http_stringContainsCTLByte(ruri) { |
| return errors.New("net/http: can't write control character in Request.URL") |
| } |
| // TODO: validate r.Method too? At least it's less likely to |
| // come from an attacker (more likely to be a constant in |
| // code). |
| |
| // Wrap the writer in a bufio Writer if it's not already buffered. |
| // Don't always call NewWriter, as that forces a bytes.Buffer |
| // and other small bufio Writers to have a minimum 4k buffer |
| // size. |
| var bw *bufio.Writer |
| if _, ok := w.(io.ByteWriter); !ok { |
| bw = bufio.NewWriter(w) |
| w = bw |
| } |
| |
| _, err = fmt.Fprintf(w, "%s %s HTTP/1.1\r\n", http_valueOrDefault(r.Method, "GET"), ruri) |
| if err != nil { |
| return err |
| } |
| |
| // Header lines |
| _, err = fmt.Fprintf(w, "Host: %s\r\n", host) |
| if err != nil { |
| return err |
| } |
| if trace != nil && trace.WroteHeaderField != nil { |
| trace.WroteHeaderField("Host", []string{host}) |
| } |
| |
| // Use the defaultUserAgent unless the Header contains one, which |
| // may be blank to not send the header. |
| userAgent := http_defaultUserAgent |
| if r.Header.has("User-Agent") { |
| userAgent = r.Header.Get("User-Agent") |
| } |
| if userAgent != "" { |
| userAgent = http_headerNewlineToSpace.Replace(userAgent) |
| userAgent = textproto.TrimString(userAgent) |
| _, err = fmt.Fprintf(w, "User-Agent: %s\r\n", userAgent) |
| if err != nil { |
| return err |
| } |
| if trace != nil && trace.WroteHeaderField != nil { |
| trace.WroteHeaderField("User-Agent", []string{userAgent}) |
| } |
| } |
| |
| // Process Body,ContentLength,Close,Trailer |
| tw, err := http_newTransferWriter(r) |
| if err != nil { |
| return err |
| } |
| err = tw.writeHeader(w, trace) |
| if err != nil { |
| return err |
| } |
| |
| err = r.Header.writeSubset(w, http_reqWriteExcludeHeader, trace) |
| if err != nil { |
| return err |
| } |
| |
| if extraHeaders != nil { |
| err = extraHeaders.write(w, trace) |
| if err != nil { |
| return err |
| } |
| } |
| |
| _, err = io.WriteString(w, "\r\n") |
| if err != nil { |
| return err |
| } |
| |
| if trace != nil && trace.WroteHeaders != nil { |
| trace.WroteHeaders() |
| } |
| |
| // Flush and wait for 100-continue if expected. |
| if waitForContinue != nil { |
| if bw, ok := w.(*bufio.Writer); ok { |
| err = bw.Flush() |
| if err != nil { |
| return err |
| } |
| } |
| if trace != nil && trace.Wait100Continue != nil { |
| trace.Wait100Continue() |
| } |
| if !waitForContinue() { |
| closed = true |
| r.closeBody() |
| return nil |
| } |
| } |
| |
| if bw, ok := w.(*bufio.Writer); ok && tw.FlushHeaders { |
| if err := bw.Flush(); err != nil { |
| return err |
| } |
| } |
| |
| // Write body and trailer |
| closed = true |
| err = tw.writeBody(w) |
| if err != nil { |
| if tw.bodyReadError == err { |
| err = http_requestBodyReadError{err} |
| } |
| return err |
| } |
| |
| if bw != nil { |
| return bw.Flush() |
| } |
| return nil |
| } |
| |
| // requestBodyReadError wraps an error from (*Request).write to indicate |
| // that the error came from a Read call on the Request.Body. |
| // This error type should not escape the net/http package to users. |
| type http_requestBodyReadError struct{ error } |
| |
| func http_idnaASCII(v string) (string, error) { |
| // TODO: Consider removing this check after verifying performance is okay. |
| // Right now punycode verification, length checks, context checks, and the |
| // permissible character tests are all omitted. It also prevents the ToASCII |
| // call from salvaging an invalid IDN, when possible. As a result it may be |
| // possible to have two IDNs that appear identical to the user where the |
| // ASCII-only version causes an error downstream whereas the non-ASCII |
| // version does not. |
| // Note that for correct ASCII IDNs ToASCII will only do considerably more |
| // work, but it will not cause an allocation. |
| if ascii.Is(v) { |
| return v, nil |
| } |
| return idna.Lookup.ToASCII(v) |
| } |
| |
| // removeZone removes IPv6 zone identifier from host. |
| // E.g., "[fe80::1%en0]:8080" to "[fe80::1]:8080" |
| func http_removeZone(host string) string { |
| if !strings.HasPrefix(host, "[") { |
| return host |
| } |
| i := strings.LastIndex(host, "]") |
| if i < 0 { |
| return host |
| } |
| j := strings.LastIndex(host[:i], "%") |
| if j < 0 { |
| return host |
| } |
| return host[:j] + host[i:] |
| } |
| |
| // ParseHTTPVersion parses an HTTP version string according to RFC 7230, section 2.6. |
| // "HTTP/1.0" returns (1, 0, true). Note that strings without |
| // a minor version, such as "HTTP/2", are not valid. |
| func http_ParseHTTPVersion(vers string) (major, minor int, ok bool) { |
| switch vers { |
| case "HTTP/1.1": |
| return 1, 1, true |
| case "HTTP/1.0": |
| return 1, 0, true |
| } |
| if !strings.HasPrefix(vers, "HTTP/") { |
| return 0, 0, false |
| } |
| if len(vers) != len("HTTP/X.Y") { |
| return 0, 0, false |
| } |
| if vers[6] != '.' { |
| return 0, 0, false |
| } |
| maj, err := strconv.ParseUint(vers[5:6], 10, 0) |
| if err != nil { |
| return 0, 0, false |
| } |
| min, err := strconv.ParseUint(vers[7:8], 10, 0) |
| if err != nil { |
| return 0, 0, false |
| } |
| return int(maj), int(min), true |
| } |
| |
| func http_validMethod(method string) bool { |
| /* |
| Method = "OPTIONS" ; Section 9.2 |
| | "GET" ; Section 9.3 |
| | "HEAD" ; Section 9.4 |
| | "POST" ; Section 9.5 |
| | "PUT" ; Section 9.6 |
| | "DELETE" ; Section 9.7 |
| | "TRACE" ; Section 9.8 |
| | "CONNECT" ; Section 9.9 |
| | extension-method |
| extension-method = token |
| token = 1*<any CHAR except CTLs or separators> |
| */ |
| return http_isToken(method) |
| } |
| |
| // NewRequest wraps [NewRequestWithContext] using [context.Background]. |
| func http_NewRequest(method, url string, body io.Reader) (*http_Request, error) { |
| return http_NewRequestWithContext(context.Background(), method, url, body) |
| } |
| |
| // NewRequestWithContext returns a new [Request] given a method, URL, and |
| // optional body. |
| // |
| // If the provided body is also an [io.Closer], the returned |
| // [Request.Body] is set to body and will be closed (possibly |
| // asynchronously) by the Client methods Do, Post, and PostForm, |
| // and [Transport.RoundTrip]. |
| // |
| // NewRequestWithContext returns a Request suitable for use with |
| // [Client.Do] or [Transport.RoundTrip]. To create a request for use with |
| // testing a Server Handler, either use the [net/http/httptest.NewRequest] function, |
| // use [ReadRequest], or manually update the Request fields. |
| // For an outgoing client request, the context |
| // controls the entire lifetime of a request and its response: |
| // obtaining a connection, sending the request, and reading the |
| // response headers and body. See the [Request] type's documentation for |
| // the difference between inbound and outbound request fields. |
| // |
| // If body is of type [*bytes.Buffer], [*bytes.Reader], or |
| // [*strings.Reader], the returned request's ContentLength is set to its |
| // exact value (instead of -1), GetBody is populated (so 307 and 308 |
| // redirects can replay the body), and Body is set to [NoBody] if the |
| // ContentLength is 0. |
| func http_NewRequestWithContext(ctx context.Context, method, url string, body io.Reader) (*http_Request, error) { |
| if method == "" { |
| // We document that "" means "GET" for Request.Method, and people have |
| // relied on that from NewRequest, so keep that working. |
| // We still enforce validMethod for non-empty methods. |
| method = "GET" |
| } |
| if !http_validMethod(method) { |
| return nil, fmt.Errorf("net/http: invalid method %q", method) |
| } |
| if ctx == nil { |
| return nil, errors.New("net/http: nil Context") |
| } |
| u, err := urlpkg.Parse(url) |
| if err != nil { |
| return nil, err |
| } |
| rc, ok := body.(io.ReadCloser) |
| if !ok && body != nil { |
| rc = io.NopCloser(body) |
| } |
| // The host's colon:port should be normalized. See Issue 14836. |
| u.Host = strings.TrimSuffix(u.Host, ":") |
| req := &http_Request{ |
| ctx: ctx, |
| Method: method, |
| URL: u, |
| Proto: "HTTP/1.1", |
| ProtoMajor: 1, |
| ProtoMinor: 1, |
| Header: make(http_Header), |
| Body: rc, |
| Host: u.Host, |
| } |
| if body != nil { |
| switch v := body.(type) { |
| case *bytes.Buffer: |
| req.ContentLength = int64(v.Len()) |
| buf := v.Bytes() |
| req.GetBody = func() (io.ReadCloser, error) { |
| r := bytes.NewReader(buf) |
| return io.NopCloser(r), nil |
| } |
| case *bytes.Reader: |
| req.ContentLength = int64(v.Len()) |
| snapshot := *v |
| req.GetBody = func() (io.ReadCloser, error) { |
| r := snapshot |
| return io.NopCloser(&r), nil |
| } |
| case *strings.Reader: |
| req.ContentLength = int64(v.Len()) |
| snapshot := *v |
| req.GetBody = func() (io.ReadCloser, error) { |
| r := snapshot |
| return io.NopCloser(&r), nil |
| } |
| default: |
| // This is where we'd set it to -1 (at least |
| // if body != NoBody) to mean unknown, but |
| // that broke people during the Go 1.8 testing |
| // period. People depend on it being 0 I |
| // guess. Maybe retry later. See Issue 18117. |
| } |
| // For client requests, Request.ContentLength of 0 |
| // means either actually 0, or unknown. The only way |
| // to explicitly say that the ContentLength is zero is |
| // to set the Body to nil. But turns out too much code |
| // depends on NewRequest returning a non-nil Body, |
| // so we use a well-known ReadCloser variable instead |
| // and have the http package also treat that sentinel |
| // variable to mean explicitly zero. |
| if req.GetBody != nil && req.ContentLength == 0 { |
| req.Body = http_NoBody |
| req.GetBody = func() (io.ReadCloser, error) { return http_NoBody, nil } |
| } |
| } |
| |
| return req, nil |
| } |
| |
| // BasicAuth returns the username and password provided in the request's |
| // Authorization header, if the request uses HTTP Basic Authentication. |
| // See RFC 2617, Section 2. |
| func (r *http_Request) BasicAuth() (username, password string, ok bool) { |
| auth := r.Header.Get("Authorization") |
| if auth == "" { |
| return "", "", false |
| } |
| return http_parseBasicAuth(auth) |
| } |
| |
| // parseBasicAuth parses an HTTP Basic Authentication string. |
| // "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==" returns ("Aladdin", "open sesame", true). |
| // |
| // parseBasicAuth should be an internal detail, |
| // but widely used packages access it using linkname. |
| // Notable members of the hall of shame include: |
| // - github.com/sagernet/sing |
| // |
| // Do not remove or change the type signature. |
| // See go.dev/issue/67401. |
| // |
| //go:linkname parseBasicAuth |
| func http_parseBasicAuth(auth string) (username, password string, ok bool) { |
| const prefix = "Basic " |
| // Case insensitive prefix match. See Issue 22736. |
| if len(auth) < len(prefix) || !ascii.EqualFold(auth[:len(prefix)], prefix) { |
| return "", "", false |
| } |
| c, err := base64.StdEncoding.DecodeString(auth[len(prefix):]) |
| if err != nil { |
| return "", "", false |
| } |
| cs := string(c) |
| username, password, ok = strings.Cut(cs, ":") |
| if !ok { |
| return "", "", false |
| } |
| return username, password, true |
| } |
| |
| // SetBasicAuth sets the request's Authorization header to use HTTP |
| // Basic Authentication with the provided username and password. |
| // |
| // With HTTP Basic Authentication the provided username and password |
| // are not encrypted. It should generally only be used in an HTTPS |
| // request. |
| // |
| // The username may not contain a colon. Some protocols may impose |
| // additional requirements on pre-escaping the username and |
| // password. For instance, when used with OAuth2, both arguments must |
| // be URL encoded first with [url.QueryEscape]. |
| func (r *http_Request) SetBasicAuth(username, password string) { |
| r.Header.Set("Authorization", "Basic "+http_basicAuth(username, password)) |
| } |
| |
| // parseRequestLine parses "GET /foo HTTP/1.1" into its three parts. |
| func http_parseRequestLine(line string) (method, requestURI, proto string, ok bool) { |
| method, rest, ok1 := strings.Cut(line, " ") |
| requestURI, proto, ok2 := strings.Cut(rest, " ") |
| if !ok1 || !ok2 { |
| return "", "", "", false |
| } |
| return method, requestURI, proto, true |
| } |
| |
| var http_textprotoReaderPool sync.Pool |
| |
| func http_newTextprotoReader(br *bufio.Reader) *textproto.Reader { |
| if v := http_textprotoReaderPool.Get(); v != nil { |
| tr := v.(*textproto.Reader) |
| tr.R = br |
| return tr |
| } |
| return textproto.NewReader(br) |
| } |
| |
| func http_putTextprotoReader(r *textproto.Reader) { |
| r.R = nil |
| http_textprotoReaderPool.Put(r) |
| } |
| |
| // ReadRequest reads and parses an incoming request from b. |
| // |
| // ReadRequest is a low-level function and should only be used for |
| // specialized applications; most code should use the [Server] to read |
| // requests and handle them via the [Handler] interface. ReadRequest |
| // only supports HTTP/1.x requests. For HTTP/2, use golang.org/x/net/http2. |
| func http_ReadRequest(b *bufio.Reader) (*http_Request, error) { |
| req, err := http_readRequest(b) |
| if err != nil { |
| return nil, err |
| } |
| |
| delete(req.Header, "Host") |
| return req, nil |
| } |
| |
| // readMIMEHeader is defined in package [net/textproto]. |
| // |
| //go:linkname readMIMEHeader net/textproto.readMIMEHeader |
| func http_readMIMEHeader(r *textproto.Reader, maxMemory, maxHeaders int64) (textproto.MIMEHeader, error) |
| |
| // readRequest should be an internal detail, |
| // but widely used packages access it using linkname. |
| // Notable members of the hall of shame include: |
| // - github.com/sagernet/sing |
| // - github.com/v2fly/v2ray-core/v4 |
| // - github.com/v2fly/v2ray-core/v5 |
| // |
| // Do not remove or change the type signature. |
| // See go.dev/issue/67401. |
| // |
| //go:linkname readRequest |
| func http_readRequest(b *bufio.Reader) (req *http_Request, err error) { |
| return http_readRequestLimit(b, math.MaxInt64) |
| } |
| |
| func http_readRequestLimit(b *bufio.Reader, maxHeaders int64) (req *http_Request, err error) { |
| tp := http_newTextprotoReader(b) |
| defer http_putTextprotoReader(tp) |
| |
| req = new(http_Request) |
| |
| // First line: GET /index.html HTTP/1.0 |
| var s string |
| if s, err = tp.ReadLine(); err != nil { |
| return nil, err |
| } |
| defer func() { |
| if err == io.EOF { |
| err = io.ErrUnexpectedEOF |
| } |
| }() |
| |
| var ok bool |
| req.Method, req.RequestURI, req.Proto, ok = http_parseRequestLine(s) |
| if !ok { |
| return nil, http_badStringError("malformed HTTP request", s) |
| } |
| if !http_validMethod(req.Method) { |
| return nil, http_badStringError("invalid method", req.Method) |
| } |
| rawurl := req.RequestURI |
| if req.ProtoMajor, req.ProtoMinor, ok = http_ParseHTTPVersion(req.Proto); !ok { |
| return nil, http_badStringError("malformed HTTP version", req.Proto) |
| } |
| |
| // CONNECT requests are used two different ways, and neither uses a full URL: |
| // The standard use is to tunnel HTTPS through an HTTP proxy. |
| // It looks like "CONNECT www.google.com:443 HTTP/1.1", and the parameter is |
| // just the authority section of a URL. This information should go in req.URL.Host. |
| // |
| // The net/rpc package also uses CONNECT, but there the parameter is a path |
| // that starts with a slash. It can be parsed with the regular URL parser, |
| // and the path will end up in req.URL.Path, where it needs to be in order for |
| // RPC to work. |
| justAuthority := req.Method == "CONNECT" && !strings.HasPrefix(rawurl, "/") |
| if justAuthority { |
| rawurl = "http://" + rawurl |
| } |
| |
| if req.URL, err = url.ParseRequestURI(rawurl); err != nil { |
| return nil, err |
| } |
| |
| if justAuthority { |
| // Strip the bogus "http://" back off. |
| req.URL.Scheme = "" |
| } |
| |
| // Subsequent lines: Key: value. |
| mimeHeader, err := http_readMIMEHeader(tp, math.MaxInt64, maxHeaders) |
| if err != nil { |
| // TODO: Add a distinguishable error to net/textproto. |
| if err.Error() == "message too large" { |
| return nil, http_errTooLarge |
| } |
| return nil, err |
| } |
| req.Header = http_Header(mimeHeader) |
| if len(req.Header["Host"]) > 1 { |
| return nil, fmt.Errorf("too many Host headers") |
| } |
| |
| // RFC 7230, section 5.3: Must treat |
| // GET /index.html HTTP/1.1 |
| // Host: www.google.com |
| // and |
| // GET http://www.google.com/index.html HTTP/1.1 |
| // Host: doesntmatter |
| // the same. In the second case, any Host line is ignored. |
| req.Host = req.URL.Host |
| if req.Host == "" { |
| req.Host = req.Header.get("Host") |
| } |
| |
| http_fixPragmaCacheControl(req.Header) |
| |
| req.Close = http_shouldClose(req.ProtoMajor, req.ProtoMinor, req.Header, false) |
| |
| err = http_readTransfer(req, b, maxHeaders) |
| if err != nil { |
| return nil, err |
| } |
| |
| if req.isH2Upgrade() { |
| // Because it's neither chunked, nor declared: |
| req.ContentLength = -1 |
| |
| // We want to give handlers a chance to hijack the |
| // connection, but we need to prevent the Server from |
| // dealing with the connection further if it's not |
| // hijacked. Set Close to ensure that: |
| req.Close = true |
| } |
| return req, nil |
| } |
| |
| // MaxBytesReader is similar to [io.LimitReader] but is intended for |
| // limiting the size of incoming request bodies. In contrast to |
| // io.LimitReader, MaxBytesReader's result is a ReadCloser, returns a |
| // non-nil error of type [*MaxBytesError] for a Read beyond the limit, |
| // and closes the underlying reader when its Close method is called. |
| // |
| // MaxBytesReader prevents clients from accidentally or maliciously |
| // sending a large request and wasting server resources. If possible, |
| // it tells the [ResponseWriter] to close the connection after the limit |
| // has been reached. |
| func http_MaxBytesReader(w http_ResponseWriter, r io.ReadCloser, n int64) io.ReadCloser { |
| if n < 0 { // Treat negative limits as equivalent to 0. |
| n = 0 |
| } |
| return &http_maxBytesReader{w: w, r: r, i: n, n: n} |
| } |
| |
| // MaxBytesError is returned by [MaxBytesReader] when its read limit is exceeded. |
| type http_MaxBytesError struct { |
| Limit int64 |
| } |
| |
| func (e *http_MaxBytesError) Error() string { |
| // Due to Hyrum's law, this text cannot be changed. |
| return "http: request body too large" |
| } |
| |
| type http_maxBytesReader struct { |
| w http_ResponseWriter |
| r io.ReadCloser // underlying reader |
| i int64 // max bytes initially, for MaxBytesError |
| n int64 // max bytes remaining |
| err error // sticky error |
| } |
| |
| func (l *http_maxBytesReader) Read(p []byte) (n int, err error) { |
| if l.err != nil { |
| return 0, l.err |
| } |
| if len(p) == 0 { |
| return 0, nil |
| } |
| // If they asked for a 32KB byte read but only 5 bytes are |
| // remaining, no need to read 32KB. 6 bytes will answer the |
| // question of the whether we hit the limit or go past it. |
| // 0 < len(p) < 2^63 |
| if int64(len(p))-1 > l.n { |
| p = p[:l.n+1] |
| } |
| n, err = l.r.Read(p) |
| |
| if int64(n) <= l.n { |
| l.n -= int64(n) |
| l.err = err |
| return n, err |
| } |
| |
| n = int(l.n) |
| l.n = 0 |
| |
| // The server code and client code both use |
| // maxBytesReader. This "requestTooLarge" check is |
| // only used by the server code. To prevent binaries |
| // which only using the HTTP Client code (such as |
| // cmd/go) from also linking in the HTTP server, don't |
| // use a static type assertion to the server |
| // "*response" type. Check this interface instead: |
| type requestTooLarger interface { |
| requestTooLarge() |
| } |
| if res, ok := l.w.(requestTooLarger); ok { |
| res.requestTooLarge() |
| } |
| l.err = &http_MaxBytesError{l.i} |
| return n, l.err |
| } |
| |
| func (l *http_maxBytesReader) Close() error { |
| return l.r.Close() |
| } |
| |
| func http_copyValues(dst, src url.Values) { |
| for k, vs := range src { |
| dst[k] = append(dst[k], vs...) |
| } |
| } |
| |
| func http_parsePostForm(r *http_Request) (vs url.Values, err error) { |
| if r.Body == nil { |
| err = errors.New("missing form body") |
| return |
| } |
| ct := r.Header.Get("Content-Type") |
| // RFC 7231, section 3.1.1.5 - empty type |
| // MAY be treated as application/octet-stream |
| if ct == "" { |
| ct = "application/octet-stream" |
| } |
| ct, _, err = mime.ParseMediaType(ct) |
| switch { |
| case ct == "application/x-www-form-urlencoded": |
| var reader io.Reader = r.Body |
| maxFormSize := int64(1<<63 - 1) |
| if _, ok := r.Body.(*http_maxBytesReader); !ok { |
| maxFormSize = int64(10 << 20) // 10 MB is a lot of text. |
| reader = io.LimitReader(r.Body, maxFormSize+1) |
| } |
| b, e := io.ReadAll(reader) |
| if e != nil { |
| if err == nil { |
| err = e |
| } |
| break |
| } |
| if int64(len(b)) > maxFormSize { |
| err = errors.New("http: POST too large") |
| return |
| } |
| vs, e = url.ParseQuery(string(b)) |
| if err == nil { |
| err = e |
| } |
| case ct == "multipart/form-data": |
| // handled by ParseMultipartForm (which is calling us, or should be) |
| // TODO(bradfitz): there are too many possible |
| // orders to call too many functions here. |
| // Clean this up and write more tests. |
| // request_test.go contains the start of this, |
| // in TestParseMultipartFormOrder and others. |
| } |
| return |
| } |
| |
| // ParseForm populates r.Form and r.PostForm. |
| // |
| // For all requests, ParseForm parses the raw query from the URL and updates |
| // r.Form. |
| // |
| // For POST, PUT, and PATCH requests, it also reads the request body, parses it |
| // as a form and puts the results into both r.PostForm and r.Form. Request body |
| // parameters take precedence over URL query string values in r.Form. |
| // |
| // If the request Body's size has not already been limited by [MaxBytesReader], |
| // the size is capped at 10MB. |
| // |
| // For other HTTP methods, or when the Content-Type is not |
| // application/x-www-form-urlencoded, the request Body is not read, and |
| // r.PostForm is initialized to a non-nil, empty value. |
| // |
| // [Request.ParseMultipartForm] calls ParseForm automatically. |
| // ParseForm is idempotent. |
| func (r *http_Request) ParseForm() error { |
| var err error |
| if r.PostForm == nil { |
| if r.Method == "POST" || r.Method == "PUT" || r.Method == "PATCH" { |
| r.PostForm, err = http_parsePostForm(r) |
| } |
| if r.PostForm == nil { |
| r.PostForm = make(url.Values) |
| } |
| } |
| if r.Form == nil { |
| if len(r.PostForm) > 0 { |
| r.Form = make(url.Values) |
| http_copyValues(r.Form, r.PostForm) |
| } |
| var newValues url.Values |
| if r.URL != nil { |
| var e error |
| newValues, e = url.ParseQuery(r.URL.RawQuery) |
| if err == nil { |
| err = e |
| } |
| } |
| if newValues == nil { |
| newValues = make(url.Values) |
| } |
| if r.Form == nil { |
| r.Form = newValues |
| } else { |
| http_copyValues(r.Form, newValues) |
| } |
| } |
| return err |
| } |
| |
| // ParseMultipartForm parses a request body as multipart/form-data. |
| // The whole request body is parsed and up to a total of maxMemory bytes of |
| // its file parts are stored in memory, with the remainder stored on |
| // disk in temporary files. |
| // ParseMultipartForm calls [Request.ParseForm] if necessary. |
| // If ParseForm returns an error, ParseMultipartForm returns it but also |
| // continues parsing the request body. |
| // After one call to ParseMultipartForm, subsequent calls have no effect. |
| func (r *http_Request) ParseMultipartForm(maxMemory int64) error { |
| if r.MultipartForm == http_multipartByReader { |
| return errors.New("http: multipart handled by MultipartReader") |
| } |
| var parseFormErr error |
| if r.Form == nil { |
| // Let errors in ParseForm fall through, and just |
| // return it at the end. |
| parseFormErr = r.ParseForm() |
| } |
| if r.MultipartForm != nil { |
| return nil |
| } |
| |
| mr, err := r.multipartReader(false) |
| if err != nil { |
| return err |
| } |
| |
| f, err := mr.ReadForm(maxMemory) |
| if err != nil { |
| return err |
| } |
| |
| if r.PostForm == nil { |
| r.PostForm = make(url.Values) |
| } |
| for k, v := range f.Value { |
| r.Form[k] = append(r.Form[k], v...) |
| // r.PostForm should also be populated. See Issue 9305. |
| r.PostForm[k] = append(r.PostForm[k], v...) |
| } |
| |
| r.MultipartForm = f |
| |
| return parseFormErr |
| } |
| |
| // FormValue returns the first value for the named component of the query. |
| // The precedence order: |
| // 1. application/x-www-form-urlencoded form body (POST, PUT, PATCH only) |
| // 2. query parameters (always) |
| // 3. multipart/form-data form body (always) |
| // |
| // FormValue calls [Request.ParseMultipartForm] and [Request.ParseForm] |
| // if necessary and ignores any errors returned by these functions. |
| // If key is not present, FormValue returns the empty string. |
| // To access multiple values of the same key, call ParseForm and |
| // then inspect [Request.Form] directly. |
| func (r *http_Request) FormValue(key string) string { |
| if r.Form == nil { |
| r.ParseMultipartForm(http_defaultMaxMemory) |
| } |
| if vs := r.Form[key]; len(vs) > 0 { |
| return vs[0] |
| } |
| return "" |
| } |
| |
| // PostFormValue returns the first value for the named component of the POST, |
| // PUT, or PATCH request body. URL query parameters are ignored. |
| // PostFormValue calls [Request.ParseMultipartForm] and [Request.ParseForm] if necessary and ignores |
| // any errors returned by these functions. |
| // If key is not present, PostFormValue returns the empty string. |
| func (r *http_Request) PostFormValue(key string) string { |
| if r.PostForm == nil { |
| r.ParseMultipartForm(http_defaultMaxMemory) |
| } |
| if vs := r.PostForm[key]; len(vs) > 0 { |
| return vs[0] |
| } |
| return "" |
| } |
| |
| // FormFile returns the first file for the provided form key. |
| // FormFile calls [Request.ParseMultipartForm] and [Request.ParseForm] if necessary. |
| func (r *http_Request) FormFile(key string) (multipart.File, *multipart.FileHeader, error) { |
| if r.MultipartForm == http_multipartByReader { |
| return nil, nil, errors.New("http: multipart handled by MultipartReader") |
| } |
| if r.MultipartForm == nil { |
| err := r.ParseMultipartForm(http_defaultMaxMemory) |
| if err != nil { |
| return nil, nil, err |
| } |
| } |
| if r.MultipartForm != nil && r.MultipartForm.File != nil { |
| if fhs := r.MultipartForm.File[key]; len(fhs) > 0 { |
| f, err := fhs[0].Open() |
| return f, fhs[0], err |
| } |
| } |
| return nil, nil, http_ErrMissingFile |
| } |
| |
| // PathValue returns the value for the named path wildcard in the [ServeMux] pattern |
| // that matched the request. |
| // It returns the empty string if the request was not matched against a pattern |
| // or there is no such wildcard in the pattern. |
| // |
| // The value is unescaped. For example, if the pattern "/b/{bucket}" matches |
| // the path "/b/a%2fb", PathValue("bucket") returns "a/b". |
| func (r *http_Request) PathValue(name string) string { |
| if i := r.patIndex(name); i >= 0 { |
| return r.matches[i] |
| } |
| return r.otherValues[name] |
| } |
| |
| // SetPathValue sets name to value, so that subsequent calls to r.PathValue(name) |
| // return value. |
| // It does not unescape value. |
| func (r *http_Request) SetPathValue(name, value string) { |
| if i := r.patIndex(name); i >= 0 { |
| r.matches[i] = value |
| } else { |
| if r.otherValues == nil { |
| r.otherValues = map[string]string{} |
| } |
| r.otherValues[name] = value |
| } |
| } |
| |
| // patIndex returns the index of name in the list of named wildcards of the |
| // request's pattern, or -1 if there is no such name. |
| func (r *http_Request) patIndex(name string) int { |
| // The linear search seems expensive compared to a map, but just creating the map |
| // takes a lot of time, and most patterns will just have a couple of wildcards. |
| if r.pat == nil { |
| return -1 |
| } |
| i := 0 |
| for _, seg := range r.pat.segments { |
| if seg.wild && seg.s != "" { |
| if name == seg.s { |
| return i |
| } |
| i++ |
| } |
| } |
| return -1 |
| } |
| |
| func (r *http_Request) expectsContinue() bool { |
| return http_hasToken(r.Header.get("Expect"), "100-continue") |
| } |
| |
| func (r *http_Request) wantsHttp10KeepAlive() bool { |
| if r.ProtoMajor != 1 || r.ProtoMinor != 0 { |
| return false |
| } |
| return http_hasToken(r.Header.get("Connection"), "keep-alive") |
| } |
| |
| func (r *http_Request) wantsClose() bool { |
| if r.Close { |
| return true |
| } |
| return http_hasToken(r.Header.get("Connection"), "close") |
| } |
| |
| func (r *http_Request) closeBody() error { |
| if r.Body == nil { |
| return nil |
| } |
| return r.Body.Close() |
| } |
| |
| func (r *http_Request) isReplayable() bool { |
| if r.Body == nil || r.Body == http_NoBody || r.GetBody != nil { |
| switch http_valueOrDefault(r.Method, "GET") { |
| case "GET", "HEAD", "OPTIONS", "TRACE": |
| return true |
| } |
| // The Idempotency-Key, while non-standard, is widely used to |
| // mean a POST or other request is idempotent. See |
| // https://golang.org/issue/19943#issuecomment-421092421 |
| if r.Header.has("Idempotency-Key") || r.Header.has("X-Idempotency-Key") { |
| return true |
| } |
| } |
| return false |
| } |
| |
| // outgoingLength reports the Content-Length of this outgoing (Client) request. |
| // It maps 0 into -1 (unknown) when the Body is non-nil. |
| func (r *http_Request) outgoingLength() int64 { |
| if r.Body == nil || r.Body == http_NoBody { |
| return 0 |
| } |
| if r.ContentLength != 0 { |
| return r.ContentLength |
| } |
| return -1 |
| } |
| |
| // requestMethodUsuallyLacksBody reports whether the given request |
| // method is one that typically does not involve a request body. |
| // This is used by the Transport (via |
| // transferWriter.shouldSendChunkedRequestBody) to determine whether |
| // we try to test-read a byte from a non-nil Request.Body when |
| // Request.outgoingLength() returns -1. See the comments in |
| // shouldSendChunkedRequestBody. |
| func http_requestMethodUsuallyLacksBody(method string) bool { |
| switch method { |
| case "GET", "HEAD", "DELETE", "OPTIONS", "PROPFIND", "SEARCH": |
| return true |
| } |
| return false |
| } |
| |
| // requiresHTTP1 reports whether this request requires being sent on |
| // an HTTP/1 connection. |
| func (r *http_Request) requiresHTTP1() bool { |
| return http_hasToken(r.Header.Get("Connection"), "upgrade") && |
| ascii.EqualFold(r.Header.Get("Upgrade"), "websocket") |
| } |
| |
| var http_respExcludeHeader = map[string]bool{ |
| "Content-Length": true, |
| "Transfer-Encoding": true, |
| "Trailer": true, |
| } |
| |
| // Response represents the response from an HTTP request. |
| // |
| // The [Client] and [Transport] return Responses from servers once |
| // the response headers have been received. The response body |
| // is streamed on demand as the Body field is read. |
| type http_Response struct { |
| Status string // e.g. "200 OK" |
| StatusCode int // e.g. 200 |
| Proto string // e.g. "HTTP/1.0" |
| ProtoMajor int // e.g. 1 |
| ProtoMinor int // e.g. 0 |
| |
| // Header maps header keys to values. If the response had multiple |
| // headers with the same key, they may be concatenated, with comma |
| // delimiters. (RFC 7230, section 3.2.2 requires that multiple headers |
| // be semantically equivalent to a comma-delimited sequence.) When |
| // Header values are duplicated by other fields in this struct (e.g., |
| // ContentLength, TransferEncoding, Trailer), the field values are |
| // authoritative. |
| // |
| // Keys in the map are canonicalized (see CanonicalHeaderKey). |
| Header http_Header |
| |
| // Body represents the response body. |
| // |
| // The response body is streamed on demand as the Body field |
| // is read. If the network connection fails or the server |
| // terminates the response, Body.Read calls return an error. |
| // |
| // The http Client and Transport guarantee that Body is always |
| // non-nil, even on responses without a body or responses with |
| // a zero-length body. It is the caller's responsibility to |
| // close Body. The default HTTP client's Transport may not |
| // reuse HTTP/1.x "keep-alive" TCP connections if the Body is |
| // not read to completion and closed; however, manually reading |
| // the body to completion should not be needed in most cases, |
| // as closing the body will also cause the body to be read to |
| // completion asynchronously, up to a conservative limit. |
| // |
| // The Body is automatically dechunked if the server replied |
| // with a "chunked" Transfer-Encoding. |
| // |
| // As of Go 1.12, the Body will also implement io.Writer |
| // on a successful "101 Switching Protocols" response, |
| // as used by WebSockets and HTTP/2's "h2c" mode. |
| Body io.ReadCloser |
| |
| // ContentLength records the length of the associated content. The |
| // value -1 indicates that the length is unknown. Unless Request.Method |
| // is "HEAD", values >= 0 indicate that the given number of bytes may |
| // be read from Body. |
| ContentLength int64 |
| |
| // Contains transfer encodings from outer-most to inner-most. Value is |
| // nil, means that "identity" encoding is used. |
| TransferEncoding []string |
| |
| // Close records whether the header directed that the connection be |
| // closed after reading Body. The value is advice for clients: neither |
| // ReadResponse nor Response.Write ever closes a connection. |
| Close bool |
| |
| // Uncompressed reports whether the response was sent compressed but |
| // was decompressed by the http package. When true, reading from |
| // Body yields the uncompressed content instead of the compressed |
| // content actually set from the server, ContentLength is set to -1, |
| // and the "Content-Length" and "Content-Encoding" fields are deleted |
| // from the responseHeader. To get the original response from |
| // the server, set Transport.DisableCompression to true. |
| Uncompressed bool |
| |
| // Trailer maps trailer keys to values in the same |
| // format as Header. |
| // |
| // The Trailer initially contains only nil values, one for |
| // each key specified in the server's "Trailer" header |
| // value. Those values are not added to Header. |
| // |
| // Trailer must not be accessed concurrently with Read calls |
| // on the Body. |
| // |
| // After Body.Read has returned io.EOF, Trailer will contain |
| // any trailer values sent by the server. |
| Trailer http_Header |
| |
| // Request is the request that was sent to obtain this Response. |
| // Request's Body is nil (having already been consumed). |
| // This is only populated for Client requests. |
| Request *http_Request |
| |
| // TLS contains information about the TLS connection on which the |
| // response was received. It is nil for unencrypted responses. |
| // The pointer is shared between responses and should not be |
| // modified. |
| TLS *tls.ConnectionState |
| } |
| |
| // Cookies parses and returns the cookies set in the Set-Cookie headers. |
| func (r *http_Response) Cookies() []*http_Cookie { |
| return http_readSetCookies(r.Header) |
| } |
| |
| // ErrNoLocation is returned by the [Response.Location] method |
| // when no Location header is present. |
| var http_ErrNoLocation = errors.New("http: no Location header in response") |
| |
| // Location returns the URL of the response's "Location" header, |
| // if present. Relative redirects are resolved relative to |
| // [Response.Request]. [ErrNoLocation] is returned if no |
| // Location header is present. |
| func (r *http_Response) Location() (*url.URL, error) { |
| lv := r.Header.Get("Location") |
| if lv == "" { |
| return nil, http_ErrNoLocation |
| } |
| if r.Request != nil && r.Request.URL != nil { |
| return r.Request.URL.Parse(lv) |
| } |
| return url.Parse(lv) |
| } |
| |
| // ReadResponse reads and returns an HTTP response from r. |
| // The req parameter optionally specifies the [Request] that corresponds |
| // to this [Response]. If nil, a GET request is assumed. |
| // Clients must call resp.Body.Close when finished reading resp.Body. |
| // After that call, clients can inspect resp.Trailer to find key/value |
| // pairs included in the response trailer. |
| func http_ReadResponse(r *bufio.Reader, req *http_Request) (*http_Response, error) { |
| tp := textproto.NewReader(r) |
| resp := &http_Response{ |
| Request: req, |
| } |
| |
| // Parse the first line of the response. |
| line, err := tp.ReadLine() |
| if err != nil { |
| if err == io.EOF { |
| err = io.ErrUnexpectedEOF |
| } |
| return nil, err |
| } |
| proto, status, ok := strings.Cut(line, " ") |
| if !ok { |
| return nil, http_badStringError("malformed HTTP response", line) |
| } |
| resp.Proto = proto |
| resp.Status = strings.TrimLeft(status, " ") |
| |
| statusCode, _, _ := strings.Cut(resp.Status, " ") |
| if len(statusCode) != 3 { |
| return nil, http_badStringError("malformed HTTP status code", statusCode) |
| } |
| resp.StatusCode, err = strconv.Atoi(statusCode) |
| if err != nil || resp.StatusCode < 0 { |
| return nil, http_badStringError("malformed HTTP status code", statusCode) |
| } |
| if resp.ProtoMajor, resp.ProtoMinor, ok = http_ParseHTTPVersion(resp.Proto); !ok { |
| return nil, http_badStringError("malformed HTTP version", resp.Proto) |
| } |
| |
| // Parse the response headers. |
| mimeHeader, err := tp.ReadMIMEHeader() |
| if err != nil { |
| if err == io.EOF { |
| err = io.ErrUnexpectedEOF |
| } |
| return nil, err |
| } |
| resp.Header = http_Header(mimeHeader) |
| |
| http_fixPragmaCacheControl(resp.Header) |
| |
| err = http_readTransfer(resp, r, math.MaxInt64) |
| if err != nil { |
| return nil, err |
| } |
| |
| return resp, nil |
| } |
| |
| // RFC 7234, section 5.4: Should treat |
| // |
| // Pragma: no-cache |
| // |
| // like |
| // |
| // Cache-Control: no-cache |
| func http_fixPragmaCacheControl(header http_Header) { |
| if hp, ok := header["Pragma"]; ok && len(hp) > 0 && hp[0] == "no-cache" { |
| if _, presentcc := header["Cache-Control"]; !presentcc { |
| header["Cache-Control"] = []string{"no-cache"} |
| } |
| } |
| } |
| |
| // ProtoAtLeast reports whether the HTTP protocol used |
| // in the response is at least major.minor. |
| func (r *http_Response) ProtoAtLeast(major, minor int) bool { |
| return r.ProtoMajor > major || |
| r.ProtoMajor == major && r.ProtoMinor >= minor |
| } |
| |
| // Write writes r to w in the HTTP/1.x server response format, |
| // including the status line, headers, body, and optional trailer. |
| // |
| // This method consults the following fields of the response r: |
| // |
| // StatusCode |
| // ProtoMajor |
| // ProtoMinor |
| // Request.Method |
| // TransferEncoding |
| // Trailer |
| // Body |
| // ContentLength |
| // Header, values for non-canonical keys will have unpredictable behavior |
| // |
| // The Response Body is closed after it is sent. |
| func (r *http_Response) Write(w io.Writer) error { |
| // Status line |
| text := r.Status |
| if text == "" { |
| text = http_StatusText(r.StatusCode) |
| if text == "" { |
| text = "status code " + strconv.Itoa(r.StatusCode) |
| } |
| } else { |
| // Just to reduce stutter, if user set r.Status to "200 OK" and StatusCode to 200. |
| // Not important. |
| text = strings.TrimPrefix(text, strconv.Itoa(r.StatusCode)+" ") |
| } |
| |
| if _, err := fmt.Fprintf(w, "HTTP/%d.%d %03d %s\r\n", r.ProtoMajor, r.ProtoMinor, r.StatusCode, text); err != nil { |
| return err |
| } |
| |
| // Clone it, so we can modify r1 as needed. |
| r1 := new(http_Response) |
| *r1 = *r |
| if r1.ContentLength == 0 && r1.Body != nil { |
| // Is it actually 0 length? Or just unknown? |
| var buf [1]byte |
| n, err := r1.Body.Read(buf[:]) |
| if err != nil && err != io.EOF { |
| return err |
| } |
| if n == 0 { |
| // Reset it to a known zero reader, in case underlying one |
| // is unhappy being read repeatedly. |
| r1.Body = http_NoBody |
| } else { |
| r1.ContentLength = -1 |
| r1.Body = struct { |
| io.Reader |
| io.Closer |
| }{ |
| io.MultiReader(bytes.NewReader(buf[:1]), r.Body), |
| r.Body, |
| } |
| } |
| } |
| // If we're sending a non-chunked HTTP/1.1 response without a |
| // content-length, the only way to do that is the old HTTP/1.0 |
| // way, by noting the EOF with a connection close, so we need |
| // to set Close. |
| if r1.ContentLength == -1 && !r1.Close && r1.ProtoAtLeast(1, 1) && !http_chunked(r1.TransferEncoding) && !r1.Uncompressed { |
| r1.Close = true |
| } |
| |
| // Process Body,ContentLength,Close,Trailer |
| tw, err := http_newTransferWriter(r1) |
| if err != nil { |
| return err |
| } |
| err = tw.writeHeader(w, nil) |
| if err != nil { |
| return err |
| } |
| |
| // Rest of header |
| err = r.Header.WriteSubset(w, http_respExcludeHeader) |
| if err != nil { |
| return err |
| } |
| |
| // contentLengthAlreadySent may have been already sent for |
| // POST/PUT requests, even if zero length. See Issue 8180. |
| contentLengthAlreadySent := tw.shouldSendContentLength() |
| if r1.ContentLength == 0 && !http_chunked(r1.TransferEncoding) && !contentLengthAlreadySent && http_bodyAllowedForStatus(r.StatusCode) { |
| if _, err := io.WriteString(w, "Content-Length: 0\r\n"); err != nil { |
| return err |
| } |
| } |
| |
| // End-of-header |
| if _, err := io.WriteString(w, "\r\n"); err != nil { |
| return err |
| } |
| |
| // Write body and trailer |
| err = tw.writeBody(w) |
| if err != nil { |
| return err |
| } |
| |
| // Success |
| return nil |
| } |
| |
| func (r *http_Response) closeBody() { |
| if r.Body != nil { |
| r.Body.Close() |
| } |
| } |
| |
| // bodyIsWritable reports whether the Body supports writing. The |
| // Transport returns Writable bodies for 101 Switching Protocols |
| // responses. |
| // The Transport uses this method to determine whether a persistent |
| // connection is done being managed from its perspective. Once we |
| // return a writable response body to a user, the net/http package is |
| // done managing that connection. |
| func (r *http_Response) bodyIsWritable() bool { |
| _, ok := r.Body.(io.Writer) |
| return ok |
| } |
| |
| // isProtocolSwitch reports whether the response code and header |
| // indicate a successful protocol upgrade response. |
| func (r *http_Response) isProtocolSwitch() bool { |
| return http_isProtocolSwitchResponse(r.StatusCode, r.Header) |
| } |
| |
| // isProtocolSwitchResponse reports whether the response code and |
| // response header indicate a successful protocol upgrade response. |
| func http_isProtocolSwitchResponse(code int, h http_Header) bool { |
| return code == http_StatusSwitchingProtocols && http_isProtocolSwitchHeader(h) |
| } |
| |
| // isProtocolSwitchHeader reports whether the request or response header |
| // is for a protocol switch. |
| func http_isProtocolSwitchHeader(h http_Header) bool { |
| return h.Get("Upgrade") != "" && |
| httpguts.HeaderValuesContainsToken(h["Connection"], "Upgrade") |
| } |
| |
| // A ResponseController is used by an HTTP handler to control the response. |
| // |
| // A ResponseController may not be used after the [Handler.ServeHTTP] method has returned. |
| type http_ResponseController struct { |
| rw http_ResponseWriter |
| } |
| |
| // NewResponseController creates a [ResponseController] for a request. |
| // |
| // The ResponseWriter should be the original value passed to the [Handler.ServeHTTP] method, |
| // or have an Unwrap method returning the original ResponseWriter. |
| // |
| // If the ResponseWriter implements any of the following methods, the ResponseController |
| // will call them as appropriate: |
| // |
| // Flush() |
| // FlushError() error // alternative Flush returning an error |
| // Hijack() (net.Conn, *bufio.ReadWriter, error) |
| // SetReadDeadline(deadline time.Time) error |
| // SetWriteDeadline(deadline time.Time) error |
| // EnableFullDuplex() error |
| // |
| // If the ResponseWriter does not support a method, ResponseController returns |
| // an error matching [ErrNotSupported]. |
| func http_NewResponseController(rw http_ResponseWriter) *http_ResponseController { |
| return &http_ResponseController{rw} |
| } |
| |
| type http_rwUnwrapper interface { |
| Unwrap() http_ResponseWriter |
| } |
| |
| // Flush flushes buffered data to the client. |
| func (c *http_ResponseController) Flush() error { |
| rw := c.rw |
| for { |
| switch t := rw.(type) { |
| case interface{ FlushError() error }: |
| return t.FlushError() |
| case http_Flusher: |
| t.Flush() |
| return nil |
| case http_rwUnwrapper: |
| rw = t.Unwrap() |
| default: |
| return http_errNotSupported() |
| } |
| } |
| } |
| |
| // Hijack lets the caller take over the connection. |
| // See the [Hijacker] interface for details. |
| func (c *http_ResponseController) Hijack() (net.Conn, *bufio.ReadWriter, error) { |
| rw := c.rw |
| for { |
| switch t := rw.(type) { |
| case http_Hijacker: |
| return t.Hijack() |
| case http_rwUnwrapper: |
| rw = t.Unwrap() |
| default: |
| return nil, nil, http_errNotSupported() |
| } |
| } |
| } |
| |
| // SetReadDeadline sets the deadline for reading the entire request, including the body. |
| // Reads from the request body after the deadline has been exceeded will return an error. |
| // A zero value means no deadline. |
| // |
| // Setting the read deadline after it has been exceeded will not extend it. |
| func (c *http_ResponseController) SetReadDeadline(deadline time.Time) error { |
| rw := c.rw |
| for { |
| switch t := rw.(type) { |
| case interface{ SetReadDeadline(time.Time) error }: |
| return t.SetReadDeadline(deadline) |
| case http_rwUnwrapper: |
| rw = t.Unwrap() |
| default: |
| return http_errNotSupported() |
| } |
| } |
| } |
| |
| // SetWriteDeadline sets the deadline for writing the response. |
| // Writes to the response body after the deadline has been exceeded will not block, |
| // but may succeed if the data has been buffered. |
| // A zero value means no deadline. |
| // |
| // Setting the write deadline after it has been exceeded will not extend it. |
| func (c *http_ResponseController) SetWriteDeadline(deadline time.Time) error { |
| rw := c.rw |
| for { |
| switch t := rw.(type) { |
| case interface{ SetWriteDeadline(time.Time) error }: |
| return t.SetWriteDeadline(deadline) |
| case http_rwUnwrapper: |
| rw = t.Unwrap() |
| default: |
| return http_errNotSupported() |
| } |
| } |
| } |
| |
| // EnableFullDuplex indicates that the request handler will interleave reads from [Request.Body] |
| // with writes to the [ResponseWriter]. |
| // |
| // For HTTP/1 requests, the Go HTTP server by default consumes any unread portion of |
| // the request body before beginning to write the response, preventing handlers from |
| // concurrently reading from the request and writing the response. |
| // Calling EnableFullDuplex disables this behavior and permits handlers to continue to read |
| // from the request while concurrently writing the response. |
| // |
| // For HTTP/2 requests, the Go HTTP server always permits concurrent reads and responses. |
| func (c *http_ResponseController) EnableFullDuplex() error { |
| rw := c.rw |
| for { |
| switch t := rw.(type) { |
| case interface{ EnableFullDuplex() error }: |
| return t.EnableFullDuplex() |
| case http_rwUnwrapper: |
| rw = t.Unwrap() |
| default: |
| return http_errNotSupported() |
| } |
| } |
| } |
| |
| // errNotSupported returns an error that Is ErrNotSupported, |
| // but is not == to it. |
| func http_errNotSupported() error { |
| return fmt.Errorf("%w", http_ErrNotSupported) |
| } |
| |
| // RoundTrip should be an internal detail, |
| // but widely used packages access it using linkname. |
| // Notable members of the hall of shame include: |
| // - github.com/erda-project/erda-infra |
| // |
| // Do not remove or change the type signature. |
| // See go.dev/issue/67401. |
| // |
| //go:linkname badRoundTrip net/http.(*Transport).RoundTrip |
| func http_badRoundTrip(*http_Transport, *http_Request) (*http_Response, error) |
| |
| // RoundTrip implements the [RoundTripper] interface. |
| // |
| // For higher-level HTTP client support (such as handling of cookies |
| // and redirects), see [Get], [Post], and the [Client] type. |
| // |
| // Like the RoundTripper interface, the error types returned |
| // by RoundTrip are unspecified. |
| func (t *http_Transport) RoundTrip(req *http_Request) (*http_Response, error) { |
| if t == nil { |
| panic("transport is nil") |
| } |
| return t.roundTrip(req) |
| } |
| |
| // A routingIndex optimizes conflict detection by indexing patterns. |
| // |
| // The basic idea is to rule out patterns that cannot conflict with a given |
| // pattern because they have a different literal in a corresponding segment. |
| // See the comments in [routingIndex.possiblyConflictingPatterns] for more details. |
| type http_routingIndex struct { |
| // map from a particular segment position and value to all registered patterns |
| // with that value in that position. |
| // For example, the key {1, "b"} would hold the patterns "/a/b" and "/a/b/c" |
| // but not "/a", "b/a", "/a/c" or "/a/{x}". |
| segments map[http_routingIndexKey][]*http_pattern |
| // All patterns that end in a multi wildcard (including trailing slash). |
| // We do not try to be clever about indexing multi patterns, because there |
| // are unlikely to be many of them. |
| multis []*http_pattern |
| } |
| |
| type http_routingIndexKey struct { |
| pos int // 0-based segment position |
| s string // literal, or empty for wildcard |
| } |
| |
| func (idx *http_routingIndex) addPattern(pat *http_pattern) { |
| if pat.lastSegment().multi { |
| idx.multis = append(idx.multis, pat) |
| } else { |
| if idx.segments == nil { |
| idx.segments = map[http_routingIndexKey][]*http_pattern{} |
| } |
| for pos, seg := range pat.segments { |
| key := http_routingIndexKey{pos: pos, s: ""} |
| if !seg.wild { |
| key.s = seg.s |
| } |
| idx.segments[key] = append(idx.segments[key], pat) |
| } |
| } |
| } |
| |
| // possiblyConflictingPatterns calls f on all patterns that might conflict with |
| // pat. If f returns a non-nil error, possiblyConflictingPatterns returns immediately |
| // with that error. |
| // |
| // To be correct, possiblyConflictingPatterns must include all patterns that |
| // might conflict. But it may also include patterns that cannot conflict. |
| // For instance, an implementation that returns all registered patterns is correct. |
| // We use this fact throughout, simplifying the implementation by returning more |
| // patterns that we might need to. |
| func (idx *http_routingIndex) possiblyConflictingPatterns(pat *http_pattern, f func(*http_pattern) error) (err error) { |
| // Terminology: |
| // dollar pattern: one ending in "{$}" |
| // multi pattern: one ending in a trailing slash or "{x...}" wildcard |
| // ordinary pattern: neither of the above |
| |
| // apply f to all the pats, stopping on error. |
| apply := func(pats []*http_pattern) error { |
| if err != nil { |
| return err |
| } |
| for _, p := range pats { |
| err = f(p) |
| if err != nil { |
| return err |
| } |
| } |
| return nil |
| } |
| |
| // Our simple indexing scheme doesn't try to prune multi patterns; assume |
| // any of them can match the argument. |
| if err := apply(idx.multis); err != nil { |
| return err |
| } |
| if pat.lastSegment().s == "/" { |
| // All paths that a dollar pattern matches end in a slash; no paths that |
| // an ordinary pattern matches do. So only other dollar or multi |
| // patterns can conflict with a dollar pattern. Furthermore, conflicting |
| // dollar patterns must have the {$} in the same position. |
| return apply(idx.segments[http_routingIndexKey{s: "/", pos: len(pat.segments) - 1}]) |
| } |
| // For ordinary and multi patterns, the only conflicts can be with a multi, |
| // or a pattern that has the same literal or a wildcard at some literal |
| // position. |
| // We could intersect all the possible matches at each position, but we |
| // do something simpler: we find the position with the fewest patterns. |
| var lmin, wmin []*http_pattern |
| min := math.MaxInt |
| hasLit := false |
| for i, seg := range pat.segments { |
| if seg.multi { |
| break |
| } |
| if !seg.wild { |
| hasLit = true |
| lpats := idx.segments[http_routingIndexKey{s: seg.s, pos: i}] |
| wpats := idx.segments[http_routingIndexKey{s: "", pos: i}] |
| if sum := len(lpats) + len(wpats); sum < min { |
| lmin = lpats |
| wmin = wpats |
| min = sum |
| } |
| } |
| } |
| if hasLit { |
| apply(lmin) |
| apply(wmin) |
| return err |
| } |
| |
| // This pattern is all wildcards. |
| // Check it against everything. |
| for _, pats := range idx.segments { |
| apply(pats) |
| } |
| return err |
| } |
| |
| // A routingNode is a node in the decision tree. |
| // The same struct is used for leaf and interior nodes. |
| type http_routingNode struct { |
| // A leaf node holds a single pattern and the Handler it was registered |
| // with. |
| pattern *http_pattern |
| handler http_Handler |
| |
| // An interior node maps parts of the incoming request to child nodes. |
| // special children keys: |
| // "/" trailing slash (resulting from {$}) |
| // "" single wildcard |
| children http_mapping[string, *http_routingNode] |
| multiChild *http_routingNode // child with multi wildcard |
| emptyChild *http_routingNode // optimization: child with key "" |
| } |
| |
| // addPattern adds a pattern and its associated Handler to the tree |
| // at root. |
| func (root *http_routingNode) addPattern(p *http_pattern, h http_Handler) { |
| // First level of tree is host. |
| n := root.addChild(p.host) |
| // Second level of tree is method. |
| n = n.addChild(p.method) |
| // Remaining levels are path. |
| n.addSegments(p.segments, p, h) |
| } |
| |
| // addSegments adds the given segments to the tree rooted at n. |
| // If there are no segments, then n is a leaf node that holds |
| // the given pattern and handler. |
| func (n *http_routingNode) addSegments(segs []http_segment, p *http_pattern, h http_Handler) { |
| if len(segs) == 0 { |
| n.set(p, h) |
| return |
| } |
| seg := segs[0] |
| if seg.multi { |
| if len(segs) != 1 { |
| panic("multi wildcard not last") |
| } |
| c := &http_routingNode{} |
| n.multiChild = c |
| c.set(p, h) |
| } else if seg.wild { |
| n.addChild("").addSegments(segs[1:], p, h) |
| } else { |
| n.addChild(seg.s).addSegments(segs[1:], p, h) |
| } |
| } |
| |
| // set sets the pattern and handler for n, which |
| // must be a leaf node. |
| func (n *http_routingNode) set(p *http_pattern, h http_Handler) { |
| if n.pattern != nil || n.handler != nil { |
| panic("non-nil leaf fields") |
| } |
| n.pattern = p |
| n.handler = h |
| } |
| |
| // addChild adds a child node with the given key to n |
| // if one does not exist, and returns the child. |
| func (n *http_routingNode) addChild(key string) *http_routingNode { |
| if key == "" { |
| if n.emptyChild == nil { |
| n.emptyChild = &http_routingNode{} |
| } |
| return n.emptyChild |
| } |
| if c := n.findChild(key); c != nil { |
| return c |
| } |
| c := &http_routingNode{} |
| n.children.add(key, c) |
| return c |
| } |
| |
| // findChild returns the child of n with the given key, or nil |
| // if there is no child with that key. |
| func (n *http_routingNode) findChild(key string) *http_routingNode { |
| if key == "" { |
| return n.emptyChild |
| } |
| r, _ := n.children.find(key) |
| return r |
| } |
| |
| // match returns the leaf node under root that matches the arguments, and a list |
| // of values for pattern wildcards in the order that the wildcards appear. |
| // For example, if the request path is "/a/b/c" and the pattern is "/{x}/b/{y}", |
| // then the second return value will be []string{"a", "c"}. |
| func (root *http_routingNode) match(host, method, path string) (*http_routingNode, []string) { |
| if host != "" { |
| // There is a host. If there is a pattern that specifies that host and it |
| // matches, we are done. If the pattern doesn't match, fall through to |
| // try patterns with no host. |
| if l, m := root.findChild(host).matchMethodAndPath(method, path); l != nil { |
| return l, m |
| } |
| } |
| return root.emptyChild.matchMethodAndPath(method, path) |
| } |
| |
| // matchMethodAndPath matches the method and path. |
| // Its return values are the same as [routingNode.match]. |
| // The receiver should be a child of the root. |
| func (n *http_routingNode) matchMethodAndPath(method, path string) (*http_routingNode, []string) { |
| if n == nil { |
| return nil, nil |
| } |
| if l, m := n.findChild(method).matchPath(path, nil); l != nil { |
| // Exact match of method name. |
| return l, m |
| } |
| if method == "HEAD" { |
| // GET matches HEAD too. |
| if l, m := n.findChild("GET").matchPath(path, nil); l != nil { |
| return l, m |
| } |
| } |
| // No exact match; try patterns with no method. |
| return n.emptyChild.matchPath(path, nil) |
| } |
| |
| // matchPath matches a path. |
| // Its return values are the same as [routingNode.match]. |
| // matchPath calls itself recursively. The matches argument holds the wildcard matches |
| // found so far. |
| func (n *http_routingNode) matchPath(path string, matches []string) (*http_routingNode, []string) { |
| if n == nil { |
| return nil, nil |
| } |
| // If path is empty, then we are done. |
| // If n is a leaf node, we found a match; return it. |
| // If n is an interior node (which means it has a nil pattern), |
| // then we failed to match. |
| if path == "" { |
| if n.pattern == nil { |
| return nil, nil |
| } |
| return n, matches |
| } |
| // Get the first segment of path. |
| seg, rest := http_firstSegment(path) |
| // First try matching against patterns that have a literal for this position. |
| // We know by construction that such patterns are more specific than those |
| // with a wildcard at this position (they are either more specific, equivalent, |
| // or overlap, and we ruled out the first two when the patterns were registered). |
| if n, m := n.findChild(seg).matchPath(rest, matches); n != nil { |
| return n, m |
| } |
| // If matching a literal fails, try again with patterns that have a single |
| // wildcard (represented by an empty string in the child mapping). |
| // Again, by construction, patterns with a single wildcard must be more specific than |
| // those with a multi wildcard. |
| // We skip this step if the segment is a trailing slash, because single wildcards |
| // don't match trailing slashes. |
| if seg != "/" { |
| if n, m := n.emptyChild.matchPath(rest, append(matches, seg)); n != nil { |
| return n, m |
| } |
| } |
| // Lastly, match the pattern (there can be at most one) that has a multi |
| // wildcard in this position to the rest of the path. |
| if c := n.multiChild; c != nil { |
| // Don't record a match for a nameless wildcard (which arises from a |
| // trailing slash in the pattern). |
| if c.pattern.lastSegment().s != "" { |
| matches = append(matches, http_pathUnescape(path[1:])) // remove initial slash |
| } |
| return c, matches |
| } |
| return nil, nil |
| } |
| |
| // firstSegment splits path into its first segment, and the rest. |
| // The path must begin with "/". |
| // If path consists of only a slash, firstSegment returns ("/", ""). |
| // The segment is returned unescaped, if possible. |
| func http_firstSegment(path string) (seg, rest string) { |
| if path == "/" { |
| return "/", "" |
| } |
| path = path[1:] // drop initial slash |
| i := strings.IndexByte(path, '/') |
| if i < 0 { |
| i = len(path) |
| } |
| return http_pathUnescape(path[:i]), path[i:] |
| } |
| |
| // matchingMethods adds to methodSet all the methods that would result in a |
| // match if passed to routingNode.match with the given host and path. |
| func (root *http_routingNode) matchingMethods(host, path string, methodSet map[string]bool) { |
| if host != "" { |
| root.findChild(host).matchingMethodsPath(path, methodSet) |
| } |
| root.emptyChild.matchingMethodsPath(path, methodSet) |
| if methodSet["GET"] { |
| methodSet["HEAD"] = true |
| } |
| } |
| |
| func (n *http_routingNode) matchingMethodsPath(path string, set map[string]bool) { |
| if n == nil { |
| return |
| } |
| n.children.eachPair(func(method string, c *http_routingNode) bool { |
| if p, _ := c.matchPath(path, nil); p != nil { |
| set[method] = true |
| } |
| return true |
| }) |
| // Don't look at the empty child. If there were an empty |
| // child, it would match on any method, but we only |
| // call this when we fail to match on a method. |
| } |
| |
| var http_httpmuxgo121 = godebug.New("httpmuxgo121") |
| |
| var http_use121 bool |
| |
| // Read httpmuxgo121 once at startup, since dealing with changes to it during |
| // program execution is too complex and error-prone. |
| func init() { |
| if http_httpmuxgo121.Value() == "1" { |
| http_use121 = true |
| http_httpmuxgo121.IncNonDefault() |
| } |
| } |
| |
| // serveMux121 holds the state of a ServeMux needed for Go 1.21 behavior. |
| type http_serveMux121 struct { |
| mu sync.RWMutex |
| m map[string]http_muxEntry |
| es []http_muxEntry // slice of entries sorted from longest to shortest. |
| hosts bool // whether any patterns contain hostnames |
| } |
| |
| type http_muxEntry struct { |
| h http_Handler |
| pattern string |
| } |
| |
| // Formerly ServeMux.Handle. |
| func (mux *http_serveMux121) handle(pattern string, handler http_Handler) { |
| mux.mu.Lock() |
| defer mux.mu.Unlock() |
| |
| if pattern == "" { |
| panic("http: invalid pattern") |
| } |
| if handler == nil { |
| panic("http: nil handler") |
| } |
| if _, exist := mux.m[pattern]; exist { |
| panic("http: multiple registrations for " + pattern) |
| } |
| |
| if mux.m == nil { |
| mux.m = make(map[string]http_muxEntry) |
| } |
| e := http_muxEntry{h: handler, pattern: pattern} |
| mux.m[pattern] = e |
| if pattern[len(pattern)-1] == '/' { |
| mux.es = http_appendSorted(mux.es, e) |
| } |
| |
| if pattern[0] != '/' { |
| mux.hosts = true |
| } |
| } |
| |
| func http_appendSorted(es []http_muxEntry, e http_muxEntry) []http_muxEntry { |
| n := len(es) |
| i := sort.Search(n, func(i int) bool { |
| return len(es[i].pattern) < len(e.pattern) |
| }) |
| if i == n { |
| return append(es, e) |
| } |
| // we now know that i points at where we want to insert |
| es = append(es, http_muxEntry{}) // try to grow the slice in place, any entry works. |
| copy(es[i+1:], es[i:]) // Move shorter entries down |
| es[i] = e |
| return es |
| } |
| |
| // Formerly ServeMux.HandleFunc. |
| func (mux *http_serveMux121) handleFunc(pattern string, handler func(http_ResponseWriter, *http_Request)) { |
| if handler == nil { |
| panic("http: nil handler") |
| } |
| mux.handle(pattern, http_HandlerFunc(handler)) |
| } |
| |
| // Formerly ServeMux.Handler. |
| func (mux *http_serveMux121) findHandler(r *http_Request) (h http_Handler, pattern string) { |
| |
| // CONNECT requests are not canonicalized. |
| if r.Method == "CONNECT" { |
| // If r.URL.Path is /tree and its handler is not registered, |
| // the /tree -> /tree/ redirect applies to CONNECT requests |
| // but the path canonicalization does not. |
| if u, ok := mux.redirectToPathSlash(r.URL.Host, r.URL.Path, r.URL); ok { |
| return http_RedirectHandler(u.String(), http_StatusMovedPermanently), u.Path |
| } |
| |
| return mux.handler(r.Host, r.URL.Path) |
| } |
| |
| // All other requests have any port stripped and path cleaned |
| // before passing to mux.handler. |
| host := http_stripHostPort(r.Host) |
| path := http_cleanPath(r.URL.Path) |
| |
| // If the given path is /tree and its handler is not registered, |
| // redirect for /tree/. |
| if u, ok := mux.redirectToPathSlash(host, path, r.URL); ok { |
| return http_RedirectHandler(u.String(), http_StatusMovedPermanently), u.Path |
| } |
| |
| if path != r.URL.Path { |
| _, pattern = mux.handler(host, path) |
| u := &url.URL{Path: path, RawQuery: r.URL.RawQuery} |
| return http_RedirectHandler(u.String(), http_StatusMovedPermanently), pattern |
| } |
| |
| return mux.handler(host, r.URL.Path) |
| } |
| |
| // handler is the main implementation of findHandler. |
| // The path is known to be in canonical form, except for CONNECT methods. |
| func (mux *http_serveMux121) handler(host, path string) (h http_Handler, pattern string) { |
| mux.mu.RLock() |
| defer mux.mu.RUnlock() |
| |
| // Host-specific pattern takes precedence over generic ones |
| if mux.hosts { |
| h, pattern = mux.match(host + path) |
| } |
| if h == nil { |
| h, pattern = mux.match(path) |
| } |
| if h == nil { |
| h, pattern = http_NotFoundHandler(), "" |
| } |
| return |
| } |
| |
| // Find a handler on a handler map given a path string. |
| // Most-specific (longest) pattern wins. |
| func (mux *http_serveMux121) match(path string) (h http_Handler, pattern string) { |
| // Check for exact match first. |
| v, ok := mux.m[path] |
| if ok { |
| return v.h, v.pattern |
| } |
| |
| // Check for longest valid match. mux.es contains all patterns |
| // that end in / sorted from longest to shortest. |
| for _, e := range mux.es { |
| if strings.HasPrefix(path, e.pattern) { |
| return e.h, e.pattern |
| } |
| } |
| return nil, "" |
| } |
| |
| // redirectToPathSlash determines if the given path needs appending "/" to it. |
| // This occurs when a handler for path + "/" was already registered, but |
| // not for path itself. If the path needs appending to, it creates a new |
| // URL, setting the path to u.Path + "/" and returning true to indicate so. |
| func (mux *http_serveMux121) redirectToPathSlash(host, path string, u *url.URL) (*url.URL, bool) { |
| mux.mu.RLock() |
| shouldRedirect := mux.shouldRedirectRLocked(host, path) |
| mux.mu.RUnlock() |
| if !shouldRedirect { |
| return u, false |
| } |
| path = path + "/" |
| u = &url.URL{Path: path, RawQuery: u.RawQuery} |
| return u, true |
| } |
| |
| // shouldRedirectRLocked reports whether the given path and host should be redirected to |
| // path+"/". This should happen if a handler is registered for path+"/" but |
| // not path -- see comments at ServeMux. |
| func (mux *http_serveMux121) shouldRedirectRLocked(host, path string) bool { |
| p := []string{path, host + path} |
| |
| for _, c := range p { |
| if _, exist := mux.m[c]; exist { |
| return false |
| } |
| } |
| |
| n := len(path) |
| if n == 0 { |
| return false |
| } |
| for _, c := range p { |
| if _, exist := mux.m[c+"/"]; exist { |
| return path[n-1] != '/' |
| } |
| } |
| |
| return false |
| } |
| |
| // Errors used by the HTTP server. |
| var ( |
| // ErrBodyNotAllowed is returned by ResponseWriter.Write calls |
| // when the HTTP method or response code does not permit a |
| // body. |
| http_ErrBodyNotAllowed = internal.ErrBodyNotAllowed |
| |
| // ErrHijacked is returned by ResponseWriter.Write calls when |
| // the underlying connection has been hijacked using the |
| // Hijacker interface. A zero-byte write on a hijacked |
| // connection will return ErrHijacked without any other side |
| // effects. |
| http_ErrHijacked = errors.New("http: connection has been hijacked") |
| |
| // ErrContentLength is returned by ResponseWriter.Write calls |
| // when a Handler set a Content-Length response header with a |
| // declared size and then attempted to write more bytes than |
| // declared. |
| http_ErrContentLength = errors.New("http: wrote more than the declared Content-Length") |
| |
| // Deprecated: ErrWriteAfterFlush is no longer returned by |
| // anything in the net/http package. Callers should not |
| // compare errors against this variable. |
| http_ErrWriteAfterFlush = errors.New("unused") |
| ) |
| |
| // A Handler responds to an HTTP request. |
| // |
| // [Handler.ServeHTTP] should write reply headers and data to the [ResponseWriter] |
| // and then return. Returning signals that the request is finished; it |
| // is not valid to use the [ResponseWriter] or read from the |
| // [Request.Body] after or concurrently with the completion of the |
| // ServeHTTP call. |
| // |
| // Depending on the HTTP client software, HTTP protocol version, and |
| // any intermediaries between the client and the Go server, it may not |
| // be possible to read from the [Request.Body] after writing to the |
| // [ResponseWriter]. Cautious handlers should read the [Request.Body] |
| // first, and then reply. |
| // |
| // Except for reading the body, handlers should not modify the |
| // provided Request. |
| // |
| // If ServeHTTP panics, the server (the caller of ServeHTTP) assumes |
| // that the effect of the panic was isolated to the active request. |
| // It recovers the panic, logs a stack trace to the server error log, |
| // and either closes the network connection or sends an HTTP/2 |
| // RST_STREAM, depending on the HTTP protocol. To abort a handler so |
| // the client sees an interrupted response but the server doesn't log |
| // an error, panic with the value [ErrAbortHandler]. |
| type http_Handler interface { |
| ServeHTTP(http_ResponseWriter, *http_Request) |
| } |
| |
| // A ResponseWriter interface is used by an HTTP handler to |
| // construct an HTTP response. |
| // |
| // A ResponseWriter may not be used after [Handler.ServeHTTP] has returned. |
| type http_ResponseWriter interface { |
| // Header returns the header map that will be sent by |
| // [ResponseWriter.WriteHeader]. The [Header] map also is the mechanism with which |
| // [Handler] implementations can set HTTP trailers. |
| // |
| // Changing the header map after a call to [ResponseWriter.WriteHeader] (or |
| // [ResponseWriter.Write]) has no effect unless the HTTP status code was of the |
| // 1xx class or the modified headers are trailers. |
| // |
| // There are two ways to set Trailers. The preferred way is to |
| // predeclare in the headers which trailers you will later |
| // send by setting the "Trailer" header to the names of the |
| // trailer keys which will come later. In this case, those |
| // keys of the Header map are treated as if they were |
| // trailers. See the example. The second way, for trailer |
| // keys not known to the [Handler] until after the first [ResponseWriter.Write], |
| // is to prefix the [Header] map keys with the [TrailerPrefix] |
| // constant value. |
| // |
| // To suppress automatic response headers (such as "Date"), set |
| // their value to nil. |
| Header() http_Header |
| |
| // Write writes the data to the connection as part of an HTTP reply. |
| // |
| // If [ResponseWriter.WriteHeader] has not yet been called, Write calls |
| // WriteHeader(http.StatusOK) before writing the data. If the Header |
| // does not contain a Content-Type line, Write adds a Content-Type set |
| // to the result of passing the initial 512 bytes of written data to |
| // [DetectContentType]. Additionally, if the total size of all written |
| // data is under a few KB and there are no Flush calls, the |
| // Content-Length header is added automatically. |
| // |
| // Depending on the HTTP protocol version and the client, calling |
| // Write or WriteHeader may prevent future reads on the |
| // Request.Body. For HTTP/1.x requests, handlers should read any |
| // needed request body data before writing the response. Once the |
| // headers have been flushed (due to either an explicit Flusher.Flush |
| // call or writing enough data to trigger a flush), the request body |
| // may be unavailable. For HTTP/2 requests, the Go HTTP server permits |
| // handlers to continue to read the request body while concurrently |
| // writing the response. However, such behavior may not be supported |
| // by all HTTP/2 clients. Handlers should read before writing if |
| // possible to maximize compatibility. |
| Write([]byte) (int, error) |
| |
| // WriteHeader sends an HTTP response header with the provided |
| // status code. |
| // |
| // If WriteHeader is not called explicitly, the first call to Write |
| // will trigger an implicit WriteHeader(http.StatusOK). |
| // Thus explicit calls to WriteHeader are mainly used to |
| // send error codes or 1xx informational responses. |
| // |
| // The provided code must be a valid HTTP 1xx-5xx status code. |
| // Any number of 1xx headers may be written, followed by at most |
| // one 2xx-5xx header. 1xx headers are sent immediately, but 2xx-5xx |
| // headers may be buffered. Use the Flusher interface to send |
| // buffered data. The header map is cleared when 2xx-5xx headers are |
| // sent, but not with 1xx headers. |
| // |
| // The server will automatically send a 100 (Continue) header |
| // on the first read from the request body if the request has |
| // an "Expect: 100-continue" header. |
| WriteHeader(statusCode int) |
| } |
| |
| // The Flusher interface is implemented by ResponseWriters that allow |
| // an HTTP handler to flush buffered data to the client. |
| // |
| // The default HTTP/1.x and HTTP/2 [ResponseWriter] implementations |
| // support [Flusher], but ResponseWriter wrappers may not. Handlers |
| // should always test for this ability at runtime. |
| // |
| // Note that even for ResponseWriters that support Flush, |
| // if the client is connected through an HTTP proxy, |
| // the buffered data may not reach the client until the response |
| // completes. |
| type http_Flusher interface { |
| // Flush sends any buffered data to the client. |
| Flush() |
| } |
| |
| // The Hijacker interface is implemented by ResponseWriters that allow |
| // an HTTP handler to take over the connection. |
| // |
| // The default [ResponseWriter] for HTTP/1.x connections supports |
| // Hijacker, but HTTP/2 connections intentionally do not. |
| // ResponseWriter wrappers may also not support Hijacker. Handlers |
| // should always test for this ability at runtime. |
| type http_Hijacker interface { |
| // Hijack lets the caller take over the connection. |
| // After a call to Hijack the HTTP server library |
| // will not do anything else with the connection. |
| // |
| // It becomes the caller's responsibility to manage |
| // and close the connection. |
| // |
| // The returned net.Conn may have read or write deadlines |
| // already set, depending on the configuration of the |
| // Server. It is the caller's responsibility to set |
| // or clear those deadlines as needed. |
| // |
| // The returned bufio.Reader may contain unprocessed buffered |
| // data from the client. |
| // |
| // After a call to Hijack, the original Request.Body must not |
| // be used. The original Request's Context remains valid and |
| // is not canceled until the Request's ServeHTTP method |
| // returns. |
| Hijack() (net.Conn, *bufio.ReadWriter, error) |
| } |
| |
| // The CloseNotifier interface is implemented by ResponseWriters which |
| // allow detecting when the underlying connection has gone away. |
| // |
| // This mechanism can be used to cancel long operations on the server |
| // if the client has disconnected before the response is ready. |
| // |
| // Deprecated: the CloseNotifier interface predates Go's context package. |
| // New code should use [Request.Context] instead. |
| type http_CloseNotifier interface { |
| // CloseNotify returns a channel that receives at most a |
| // single value (true) when the client connection has gone |
| // away. |
| // |
| // CloseNotify may wait to notify until Request.Body has been |
| // fully read. |
| // |
| // After the Handler has returned, there is no guarantee |
| // that the channel receives a value. |
| // |
| // If the protocol is HTTP/1.1 and CloseNotify is called while |
| // processing an idempotent request (such as GET) while |
| // HTTP/1.1 pipelining is in use, the arrival of a subsequent |
| // pipelined request may cause a value to be sent on the |
| // returned channel. In practice HTTP/1.1 pipelining is not |
| // enabled in browsers and not seen often in the wild. If this |
| // is a problem, use HTTP/2 or only use CloseNotify on methods |
| // such as POST. |
| CloseNotify() <-chan bool |
| } |
| |
| var ( |
| // ServerContextKey is a context key. It can be used in HTTP |
| // handlers with Context.Value to access the server that |
| // started the handler. The associated value will be of |
| // type *Server. |
| http_ServerContextKey = &http_contextKey{"http-server"} |
| |
| // LocalAddrContextKey is a context key. It can be used in |
| // HTTP handlers with Context.Value to access the local |
| // address the connection arrived on. |
| // The associated value will be of type net.Addr. |
| http_LocalAddrContextKey = &http_contextKey{"local-addr"} |
| ) |
| |
| // A conn represents the server side of an HTTP connection. |
| type http_conn struct { |
| // server is the server on which the connection arrived. |
| // Immutable; never nil. |
| server *http_Server |
| |
| // cancelCtx cancels the connection-level context. |
| cancelCtx context.CancelFunc |
| |
| // rwc is the underlying network connection. |
| // This is never wrapped by other types and is the value given out |
| // to [Hijacker] callers. It is usually of type *net.TCPConn or |
| // *tls.Conn. |
| rwc net.Conn |
| |
| // remoteAddr is rwc.RemoteAddr().String(). It is not populated synchronously |
| // inside the Listener's Accept goroutine, as some implementations block. |
| // It is populated immediately inside the (*conn).serve goroutine. |
| // This is the value of a Handler's (*Request).RemoteAddr. |
| remoteAddr string |
| |
| // tlsState is the TLS connection state when using TLS. |
| // nil means not TLS. |
| tlsState *tls.ConnectionState |
| |
| // werr is set to the first write error to rwc. |
| // It is set via checkConnErrorWriter{w}, where bufw writes. |
| werr error |
| |
| // r is bufr's read source. It's a wrapper around rwc that provides |
| // io.LimitedReader-style limiting (while reading request headers) |
| // and functionality to support CloseNotifier. See *connReader docs. |
| r *http_connReader |
| |
| // bufr reads from r. |
| bufr *bufio.Reader |
| |
| // bufw writes to checkConnErrorWriter{c}, which populates werr on error. |
| bufw *bufio.Writer |
| |
| // lastMethod is the method of the most recent request |
| // on this connection, if any. |
| lastMethod string |
| |
| curReq atomic.Pointer[http_response] // (which has a Request in it) |
| |
| curState atomic.Uint64 // packed (unixtime<<8|uint8(ConnState)) |
| |
| // mu guards hijackedv |
| mu sync.Mutex |
| |
| // hijackedv is whether this connection has been hijacked |
| // by a Handler with the Hijacker interface. |
| // It is guarded by mu. |
| hijackedv bool |
| } |
| |
| func (c *http_conn) hijacked() bool { |
| c.mu.Lock() |
| defer c.mu.Unlock() |
| return c.hijackedv |
| } |
| |
| // c.mu must be held. |
| func (c *http_conn) hijackLocked() (rwc net.Conn, buf *bufio.ReadWriter, err error) { |
| if c.hijackedv { |
| return nil, nil, http_ErrHijacked |
| } |
| c.r.abortPendingRead() |
| |
| c.hijackedv = true |
| rwc = c.rwc |
| rwc.SetDeadline(time.Time{}) |
| |
| if c.r.hasByte { |
| if _, err := c.bufr.Peek(c.bufr.Buffered() + 1); err != nil { |
| return nil, nil, fmt.Errorf("unexpected Peek failure reading buffered byte: %v", err) |
| } |
| } |
| c.bufw.Reset(rwc) |
| buf = bufio.NewReadWriter(c.bufr, c.bufw) |
| |
| c.setState(rwc, http_StateHijacked, http_runHooks) |
| return |
| } |
| |
| // This should be >= 512 bytes for DetectContentType, |
| // but otherwise it's somewhat arbitrary. |
| const http_bufferBeforeChunkingSize = 2048 |
| |
| // chunkWriter writes to a response's conn buffer, and is the writer |
| // wrapped by the response.w buffered writer. |
| // |
| // chunkWriter also is responsible for finalizing the Header, including |
| // conditionally setting the Content-Type and setting a Content-Length |
| // in cases where the handler's final output is smaller than the buffer |
| // size. It also conditionally adds chunk headers, when in chunking mode. |
| // |
| // See the comment above (*response).Write for the entire write flow. |
| type http_chunkWriter struct { |
| res *http_response |
| |
| // header is either nil or a deep clone of res.handlerHeader |
| // at the time of res.writeHeader, if res.writeHeader is |
| // called and extra buffering is being done to calculate |
| // Content-Type and/or Content-Length. |
| header http_Header |
| |
| // wroteHeader tells whether the header's been written to "the |
| // wire" (or rather: w.conn.buf). this is unlike |
| // (*response).wroteHeader, which tells only whether it was |
| // logically written. |
| wroteHeader bool |
| |
| // set by the writeHeader method: |
| chunking bool // using chunked transfer encoding for reply body |
| } |
| |
| var ( |
| http_crlf = []byte("\r\n") |
| http_colonSpace = []byte(": ") |
| ) |
| |
| func (cw *http_chunkWriter) Write(p []byte) (n int, err error) { |
| if !cw.wroteHeader { |
| cw.writeHeader(p) |
| } |
| if cw.res.req.Method == "HEAD" { |
| // Eat writes. |
| return len(p), nil |
| } |
| if cw.chunking { |
| _, err = fmt.Fprintf(cw.res.conn.bufw, "%x\r\n", len(p)) |
| if err != nil { |
| cw.res.conn.rwc.Close() |
| return |
| } |
| } |
| n, err = cw.res.conn.bufw.Write(p) |
| if cw.chunking && err == nil { |
| _, err = cw.res.conn.bufw.Write(http_crlf) |
| } |
| if err != nil { |
| cw.res.conn.rwc.Close() |
| } |
| return |
| } |
| |
| func (cw *http_chunkWriter) flush() error { |
| if !cw.wroteHeader { |
| cw.writeHeader(nil) |
| } |
| return cw.res.conn.bufw.Flush() |
| } |
| |
| func (cw *http_chunkWriter) close() { |
| if !cw.wroteHeader { |
| cw.writeHeader(nil) |
| } |
| if cw.chunking { |
| bw := cw.res.conn.bufw // conn's bufio writer |
| // zero chunk to mark EOF |
| bw.WriteString("0\r\n") |
| if trailers := cw.res.finalTrailers(); trailers != nil { |
| trailers.Write(bw) // the writer handles noting errors |
| } |
| // final blank line after the trailers (whether |
| // present or not) |
| bw.WriteString("\r\n") |
| } |
| } |
| |
| // A response represents the server side of an HTTP response. |
| type http_response struct { |
| conn *http_conn |
| req *http_Request // request for this response |
| reqBody *http_body // nil when NoBody |
| cancelCtx context.CancelFunc // when ServeHTTP exits |
| wroteHeader bool // a non-1xx header has been (logically) written |
| wants10KeepAlive bool // HTTP/1.0 w/ Connection "keep-alive" |
| wantsClose bool // HTTP request has Connection "close" |
| ecReader *http_expectContinueReader |
| |
| // canWriteContinue is an atomic boolean that says whether or |
| // not a 100 Continue header can be written to the |
| // connection. |
| // writeContinueMu must be held while writing the header. |
| // These two fields together synchronize the body reader (the |
| // expectContinueReader, which wants to write 100 Continue) |
| // against the main writer. |
| writeContinueMu sync.Mutex |
| canWriteContinue atomic.Bool |
| |
| w *bufio.Writer // buffers output in chunks to chunkWriter |
| cw http_chunkWriter |
| |
| // handlerHeader is the Header that Handlers get access to, |
| // which may be retained and mutated even after WriteHeader. |
| // handlerHeader is copied into cw.header at WriteHeader |
| // time, and privately mutated thereafter. |
| handlerHeader http_Header |
| calledHeader bool // handler accessed handlerHeader via Header |
| |
| written int64 // number of bytes written in body |
| contentLength int64 // explicitly-declared Content-Length; or -1 |
| status int // status code passed to WriteHeader |
| |
| // close connection after this reply. set on request and |
| // updated after response from handler if there's a |
| // "Connection: keep-alive" response header and a |
| // Content-Length. |
| closeAfterReply bool |
| |
| // When fullDuplex is false (the default), we consume any remaining |
| // request body before starting to write a response. |
| fullDuplex bool |
| |
| // requestBodyLimitHit is set by requestTooLarge when |
| // maxBytesReader hits its max size. It is checked in |
| // WriteHeader, to make sure we don't consume the |
| // remaining request body to try to advance to the next HTTP |
| // request. Instead, when this is set, we stop reading |
| // subsequent requests on this connection and stop reading |
| // input from it. |
| requestBodyLimitHit bool |
| |
| // trailers are the headers to be sent after the handler |
| // finishes writing the body. This field is initialized from |
| // the Trailer response header when the response header is |
| // written. |
| trailers []string |
| |
| handlerDone atomic.Bool // set true when the handler exits |
| |
| // Buffers for Date, Content-Length, and status code |
| dateBuf [len(http_TimeFormat)]byte |
| clenBuf [10]byte |
| statusBuf [3]byte |
| |
| // lazyCloseNotifyMu protects closeNotifyCh and closeNotifyTriggered. |
| lazyCloseNotifyMu sync.Mutex |
| // closeNotifyCh is the channel returned by CloseNotify. |
| closeNotifyCh chan bool |
| // closeNotifyTriggered tracks prior closeNotify calls. |
| closeNotifyTriggered bool |
| } |
| |
| func (c *http_response) SetReadDeadline(deadline time.Time) error { |
| return c.conn.rwc.SetReadDeadline(deadline) |
| } |
| |
| func (c *http_response) SetWriteDeadline(deadline time.Time) error { |
| return c.conn.rwc.SetWriteDeadline(deadline) |
| } |
| |
| func (c *http_response) EnableFullDuplex() error { |
| c.fullDuplex = true |
| return nil |
| } |
| |
| // TrailerPrefix is a magic prefix for [ResponseWriter.Header] map keys |
| // that, if present, signals that the map entry is actually for |
| // the response trailers, and not the response headers. The prefix |
| // is stripped after the ServeHTTP call finishes and the values are |
| // sent in the trailers. |
| // |
| // This mechanism is intended only for trailers that are not known |
| // prior to the headers being written. If the set of trailers is fixed |
| // or known before the header is written, the normal Go trailers mechanism |
| // is preferred: |
| // |
| // https://pkg.go.dev/net/http#ResponseWriter |
| // https://pkg.go.dev/net/http#example-ResponseWriter-Trailers |
| const http_TrailerPrefix = "Trailer:" |
| |
| // finalTrailers is called after the Handler exits and returns a non-nil |
| // value if the Handler set any trailers. |
| func (w *http_response) finalTrailers() http_Header { |
| var t http_Header |
| for k, vv := range w.handlerHeader { |
| if kk, found := strings.CutPrefix(k, http_TrailerPrefix); found { |
| if t == nil { |
| t = make(http_Header) |
| } |
| t[kk] = vv |
| } |
| } |
| for _, k := range w.trailers { |
| if t == nil { |
| t = make(http_Header) |
| } |
| for _, v := range w.handlerHeader[k] { |
| t.Add(k, v) |
| } |
| } |
| return t |
| } |
| |
| // declareTrailer is called for each Trailer header when the |
| // response header is written. It notes that a header will need to be |
| // written in the trailers at the end of the response. |
| func (w *http_response) declareTrailer(k string) { |
| k = http_CanonicalHeaderKey(k) |
| if !httpguts.ValidTrailerHeader(k) { |
| // Forbidden by RFC 7230, section 4.1.2 |
| return |
| } |
| w.trailers = append(w.trailers, k) |
| } |
| |
| // requestTooLarge is called by maxBytesReader when too much input has |
| // been read from the client. |
| func (w *http_response) requestTooLarge() { |
| w.closeAfterReply = true |
| w.requestBodyLimitHit = true |
| if !w.wroteHeader { |
| w.Header().Set("Connection", "close") |
| } |
| } |
| |
| // disableWriteContinue stops Request.Body.Read from sending an automatic |
| // 100 Continue. As the name implies, it is only useful when the request |
| // expects a 100 Continue and the body is wrapped in an expectContinueReader; |
| // otherwise, it is a no-op. |
| // If a 100-Continue is being written, it waits for it to complete before |
| // continuing. If skipDrain is true, it also prevents the server from draining |
| // the request body and flags the connection to be closed after the reply, as |
| // the client will never send the body. |
| func (w *http_response) disableWriteContinue(skipDrain bool) { |
| if w.ecReader == nil { |
| return |
| } |
| w.writeContinueMu.Lock() |
| if w.canWriteContinue.Load() { |
| w.canWriteContinue.Store(false) |
| if skipDrain { |
| // Make sure that the connection will not be reused by sending |
| // "Connection: close" header in the response. |
| w.closeAfterReply = true |
| // Ensure that the body will not be drained in Close. |
| w.ecReader.closed.Store(true) |
| } |
| } |
| w.writeContinueMu.Unlock() |
| } |
| |
| // writerOnly hides an io.Writer value's optional ReadFrom method |
| // from io.Copy. |
| type http_writerOnly struct { |
| io.Writer |
| } |
| |
| // ReadFrom is here to optimize copying from an [*os.File] regular file |
| // to a [*net.TCPConn] with sendfile, or from a supported src type such |
| // as a *net.TCPConn on Linux with splice. |
| func (w *http_response) ReadFrom(src io.Reader) (n int64, err error) { |
| buf := http_getCopyBuf() |
| defer http_putCopyBuf(buf) |
| |
| // Our underlying w.conn.rwc is usually a *TCPConn (with its |
| // own ReadFrom method). If not, just fall back to the normal |
| // copy method. |
| rf, ok := w.conn.rwc.(io.ReaderFrom) |
| if !ok { |
| return io.CopyBuffer(http_writerOnly{w}, src, buf) |
| } |
| |
| // Copy the first sniffLen bytes before switching to ReadFrom. |
| // This ensures we don't start writing the response before the |
| // source is available (see golang.org/issue/5660) and provides |
| // enough bytes to perform Content-Type sniffing when required. |
| if !w.cw.wroteHeader { |
| n0, err := io.CopyBuffer(http_writerOnly{w}, io.LimitReader(src, internal.SniffLen), buf) |
| n += n0 |
| if err != nil || n0 < internal.SniffLen { |
| return n, err |
| } |
| } |
| |
| w.w.Flush() // get rid of any previous writes |
| w.cw.flush() // make sure Header is written; flush data to rwc |
| |
| // Now that cw has been flushed, its chunking field is guaranteed initialized. |
| if !w.cw.chunking && w.bodyAllowed() && w.req.Method != "HEAD" { |
| // When a content length is declared, but exceeded; any excess bytes |
| // from src should be ignored, and ErrContentLength should be returned. |
| // This mirrors the behavior of response.Write. |
| if w.contentLength != -1 { |
| defer func(originalReader io.Reader) { |
| if w.written != w.contentLength { |
| return |
| } |
| if n, _ := originalReader.Read([]byte{0}); err == nil && n != 0 { |
| err = http_ErrContentLength |
| } |
| }(src) |
| // src can be an io.LimitedReader already. To avoid unnecessary |
| // alloc and having to unnest readers repeatedly in net.sendFile, |
| // just adjust the existing LimitedReader N when this is the case. |
| if lr, ok := src.(*io.LimitedReader); ok { |
| if lenDiff := lr.N - (w.contentLength - w.written); lenDiff > 0 { |
| defer func() { lr.N += lenDiff }() |
| lr.N -= lenDiff |
| } |
| } else { |
| src = io.LimitReader(src, w.contentLength-w.written) |
| } |
| } |
| n0, err := rf.ReadFrom(src) |
| n += n0 |
| w.written += n0 |
| return n, err |
| } |
| |
| n0, err := io.CopyBuffer(http_writerOnly{w}, src, buf) |
| n += n0 |
| return n, err |
| } |
| |
| // debugServerConnections controls whether all server connections are wrapped |
| // with a verbose logging wrapper. |
| const http_debugServerConnections = false |
| |
| // Create new connection from rwc. |
| func (s *http_Server) newConn(rwc net.Conn) *http_conn { |
| c := &http_conn{ |
| server: s, |
| rwc: rwc, |
| } |
| if http_debugServerConnections { |
| c.rwc = http_newLoggingConn("server", c.rwc) |
| } |
| return c |
| } |
| |
| type http_readResult struct { |
| _ http_incomparable |
| n int |
| err error |
| b byte // byte read, if n == 1 |
| } |
| |
| // connReader is the io.Reader wrapper used by *conn. It combines a |
| // selectively-activated io.LimitedReader (to bound request header |
| // read sizes) with support for selectively keeping an io.Reader.Read |
| // call blocked in a background goroutine to wait for activity and |
| // trigger a CloseNotifier channel. |
| // After a Handler has hijacked the conn and exited, connReader behaves like a |
| // proxy for the net.Conn and the aforementioned behavior is bypassed. |
| type http_connReader struct { |
| rwc net.Conn // rwc is the underlying network connection. |
| |
| mu sync.Mutex // guards following |
| conn *http_conn // conn is nil after handler exit. |
| hasByte bool |
| byteBuf [1]byte |
| cond *sync.Cond |
| inRead bool |
| aborted bool // set true before conn.rwc deadline is set to past |
| remain int64 // bytes remaining |
| } |
| |
| func (cr *http_connReader) lock() { |
| cr.mu.Lock() |
| if cr.cond == nil { |
| cr.cond = sync.NewCond(&cr.mu) |
| } |
| } |
| |
| func (cr *http_connReader) unlock() { cr.mu.Unlock() } |
| |
| func (cr *http_connReader) releaseConn() { |
| cr.lock() |
| defer cr.unlock() |
| cr.conn = nil |
| } |
| |
| func (cr *http_connReader) startBackgroundRead() { |
| cr.lock() |
| defer cr.unlock() |
| if cr.inRead { |
| panic("invalid concurrent Body.Read call") |
| } |
| if cr.hasByte { |
| return |
| } |
| cr.inRead = true |
| cr.rwc.SetReadDeadline(time.Time{}) |
| go cr.backgroundRead() |
| } |
| |
| func (cr *http_connReader) backgroundRead() { |
| n, err := cr.rwc.Read(cr.byteBuf[:]) |
| cr.lock() |
| if n == 1 { |
| cr.hasByte = true |
| // We were past the end of the previous request's body already |
| // (since we wouldn't be in a background read otherwise), so |
| // this is a pipelined HTTP request. Prior to Go 1.11 we used to |
| // send on the CloseNotify channel and cancel the context here, |
| // but the behavior was documented as only "may", and we only |
| // did that because that's how CloseNotify accidentally behaved |
| // in very early Go releases prior to context support. Once we |
| // added context support, people used a Handler's |
| // Request.Context() and passed it along. Having that context |
| // cancel on pipelined HTTP requests caused problems. |
| // Fortunately, almost nothing uses HTTP/1.x pipelining. |
| // Unfortunately, apt-get does, or sometimes does. |
| // New Go 1.11 behavior: don't fire CloseNotify or cancel |
| // contexts on pipelined requests. Shouldn't affect people, but |
| // fixes cases like Issue 23921. This does mean that a client |
| // closing their TCP connection after sending a pipelined |
| // request won't cancel the context, but we'll catch that on any |
| // write failure (in checkConnErrorWriter.Write). |
| // If the server never writes, yes, there are still contrived |
| // server & client behaviors where this fails to ever cancel the |
| // context, but that's kinda why HTTP/1.x pipelining died |
| // anyway. |
| } |
| if ne, ok := err.(net.Error); ok && cr.aborted && ne.Timeout() { |
| // Ignore this error. It's the expected error from |
| // another goroutine calling abortPendingRead. |
| } else if err != nil { |
| cr.handleReadErrorLocked(err) |
| } |
| cr.aborted = false |
| cr.inRead = false |
| cr.unlock() |
| cr.cond.Broadcast() |
| } |
| |
| func (cr *http_connReader) abortPendingRead() { |
| cr.lock() |
| defer cr.unlock() |
| if !cr.inRead { |
| return |
| } |
| cr.aborted = true |
| cr.rwc.SetReadDeadline(http_aLongTimeAgo) |
| for cr.inRead { |
| cr.cond.Wait() |
| } |
| cr.rwc.SetReadDeadline(time.Time{}) |
| } |
| |
| func (cr *http_connReader) setReadLimit(remain int64) { cr.remain = remain } |
| |
| func (cr *http_connReader) setInfiniteReadLimit() { cr.remain = http_maxInt64 } |
| |
| func (cr *http_connReader) hitReadLimit() bool { return cr.remain <= 0 } |
| |
| // handleReadErrorLocked is called whenever a Read from the client returns a |
| // non-nil error. |
| // |
| // The provided non-nil err is almost always io.EOF or a "use of |
| // closed network connection". In any case, the error is not |
| // particularly interesting, except perhaps for debugging during |
| // development. Any error means the connection is dead and we should |
| // down its context. |
| // |
| // The caller must hold connReader.mu. |
| func (cr *http_connReader) handleReadErrorLocked(_ error) { |
| if cr.conn == nil { |
| return |
| } |
| cr.conn.cancelCtx() |
| if res := cr.conn.curReq.Load(); res != nil { |
| res.closeNotify() |
| } |
| } |
| |
| func (cr *http_connReader) Read(p []byte) (n int, err error) { |
| cr.lock() |
| if cr.conn == nil { |
| cr.unlock() |
| return cr.rwc.Read(p) |
| } |
| if cr.inRead { |
| hijacked := cr.conn.hijacked() |
| cr.unlock() |
| if hijacked { |
| panic("invalid Body.Read call. After hijacked, the original Request must not be used") |
| } |
| panic("invalid concurrent Body.Read call") |
| } |
| if cr.hitReadLimit() { |
| cr.unlock() |
| return 0, io.EOF |
| } |
| if len(p) == 0 { |
| cr.unlock() |
| return 0, nil |
| } |
| if int64(len(p)) > cr.remain { |
| p = p[:cr.remain] |
| } |
| if cr.hasByte { |
| p[0] = cr.byteBuf[0] |
| cr.hasByte = false |
| cr.unlock() |
| return 1, nil |
| } |
| cr.inRead = true |
| cr.unlock() |
| n, err = cr.rwc.Read(p) |
| |
| cr.lock() |
| cr.inRead = false |
| if err != nil { |
| cr.handleReadErrorLocked(err) |
| } |
| cr.remain -= int64(n) |
| cr.unlock() |
| |
| cr.cond.Broadcast() |
| return n, err |
| } |
| |
| var ( |
| http_bufioReaderPool sync.Pool |
| http_bufioWriter2kPool sync.Pool |
| http_bufioWriter4kPool sync.Pool |
| ) |
| |
| const http_copyBufPoolSize = 32 * 1024 |
| |
| var http_copyBufPool = sync.Pool{New: func() any { return new([http_copyBufPoolSize]byte) }} |
| |
| func http_getCopyBuf() []byte { |
| return http_copyBufPool.Get().(*[http_copyBufPoolSize]byte)[:] |
| } |
| |
| func http_putCopyBuf(b []byte) { |
| if len(b) != http_copyBufPoolSize { |
| panic("trying to put back buffer of the wrong size in the copyBufPool") |
| } |
| http_copyBufPool.Put((*[http_copyBufPoolSize]byte)(b)) |
| } |
| |
| func http_bufioWriterPool(size int) *sync.Pool { |
| switch size { |
| case 2 << 10: |
| return &http_bufioWriter2kPool |
| case 4 << 10: |
| return &http_bufioWriter4kPool |
| } |
| return nil |
| } |
| |
| func http_newBufioReader(r io.Reader) *bufio.Reader { |
| if v := http_bufioReaderPool.Get(); v != nil { |
| br := v.(*bufio.Reader) |
| br.Reset(r) |
| return br |
| } |
| // Note: if this reader size is ever changed, update |
| // TestHandlerBodyClose's assumptions. |
| return bufio.NewReader(r) |
| } |
| |
| func http_putBufioReader(br *bufio.Reader) { |
| br.Reset(nil) |
| http_bufioReaderPool.Put(br) |
| } |
| |
| func http_newBufioWriterSize(w io.Writer, size int) *bufio.Writer { |
| pool := http_bufioWriterPool(size) |
| if pool != nil { |
| if v := pool.Get(); v != nil { |
| bw := v.(*bufio.Writer) |
| bw.Reset(w) |
| return bw |
| } |
| } |
| return bufio.NewWriterSize(w, size) |
| } |
| |
| func http_putBufioWriter(bw *bufio.Writer) { |
| bw.Reset(nil) |
| if pool := http_bufioWriterPool(bw.Available()); pool != nil { |
| pool.Put(bw) |
| } |
| } |
| |
| // DefaultMaxHeaderBytes is the maximum permitted size of the headers |
| // in an HTTP request. |
| // This can be overridden by setting [Server.MaxHeaderBytes]. |
| const http_DefaultMaxHeaderBytes = 1 << 20 // 1 MB |
| |
| // DefaultMaxHeaderValueCount is the maximum permitted number of |
| // header values in an HTTP request. |
| // This can be overridden by setting [Server.MaxHeaderValueCount]. |
| const http_DefaultMaxHeaderValueCount = 500 |
| |
| func (s *http_Server) maxHeaderBytes() int { |
| if s.MaxHeaderBytes > 0 { |
| return s.MaxHeaderBytes |
| } |
| return http_DefaultMaxHeaderBytes |
| } |
| |
| func (s *http_Server) maxHeaderValueCount() int { |
| if s.MaxHeaderValueCount > 0 { |
| return s.MaxHeaderValueCount |
| } |
| return http_DefaultMaxHeaderValueCount |
| } |
| |
| func (s *http_Server) initialReadLimitSize() int64 { |
| return int64(s.maxHeaderBytes()) + 4096 // bufio slop |
| } |
| |
| // tlsHandshakeTimeout returns the time limit permitted for the TLS |
| // handshake, or zero for unlimited. |
| // |
| // It returns the minimum of any positive ReadHeaderTimeout, |
| // ReadTimeout, or WriteTimeout. |
| func (s *http_Server) tlsHandshakeTimeout() time.Duration { |
| var ret time.Duration |
| for _, v := range [...]time.Duration{ |
| s.ReadHeaderTimeout, |
| s.ReadTimeout, |
| s.WriteTimeout, |
| } { |
| if v <= 0 { |
| continue |
| } |
| if ret == 0 || v < ret { |
| ret = v |
| } |
| } |
| return ret |
| } |
| |
| // wrapper around io.ReadCloser which on first read, sends an |
| // HTTP/1.1 100 Continue header |
| type http_expectContinueReader struct { |
| resp *http_response |
| readCloser io.ReadCloser |
| closed atomic.Bool |
| } |
| |
| func (ecr *http_expectContinueReader) Read(p []byte) (n int, err error) { |
| if ecr.closed.Load() { |
| return 0, http_ErrBodyReadAfterClose |
| } |
| w := ecr.resp |
| if w.canWriteContinue.Load() { |
| w.writeContinueMu.Lock() |
| if w.canWriteContinue.Load() { |
| w.conn.bufw.WriteString("HTTP/1.1 100 Continue\r\n\r\n") |
| w.conn.bufw.Flush() |
| w.canWriteContinue.Store(false) |
| } |
| w.writeContinueMu.Unlock() |
| } |
| return ecr.readCloser.Read(p) |
| } |
| |
| func (ecr *http_expectContinueReader) Close() error { |
| if ecr.resp.canWriteContinue.Load() { |
| ecr.resp.disableWriteContinue(true) |
| } |
| if ecr.closed.Swap(true) { |
| return nil |
| } |
| return ecr.readCloser.Close() |
| } |
| |
| // TimeFormat is the time format to use when generating times in HTTP |
| // headers. It is like [time.RFC1123] but hard-codes GMT as the time |
| // zone. The time being formatted must be in UTC for Format to |
| // generate the correct format. |
| // |
| // For parsing this time format, see [ParseTime]. |
| const http_TimeFormat = "Mon, 02 Jan 2006 15:04:05 GMT" |
| |
| var http_errTooLarge = errors.New("http: request too large") |
| |
| // Read next request from connection. |
| func (c *http_conn) readRequest(ctx context.Context) (w *http_response, err error) { |
| if c.hijacked() { |
| return nil, http_ErrHijacked |
| } |
| |
| t0 := time.Now() |
| var wholeReqDeadline time.Time // or zero if none |
| if d := c.server.ReadTimeout; d > 0 { |
| wholeReqDeadline = t0.Add(d) |
| } |
| if d := c.server.WriteTimeout; d > 0 { |
| defer func() { |
| c.rwc.SetWriteDeadline(time.Now().Add(d)) |
| }() |
| } |
| |
| c.r.setReadLimit(c.server.initialReadLimitSize()) |
| if c.lastMethod == "POST" { |
| // RFC 7230 section 3 tolerance for old buggy clients. |
| peek, _ := c.bufr.Peek(4) // ReadRequest will get err below |
| c.bufr.Discard(http_numLeadingCRorLF(peek)) |
| } |
| req, err := http_readRequestLimit(c.bufr, int64(c.server.maxHeaderValueCount())) |
| if err != nil { |
| if c.r.hitReadLimit() { |
| return nil, http_errTooLarge |
| } |
| return nil, err |
| } |
| |
| if !http_http1ServerSupportsRequest(req) { |
| return nil, http_statusError{http_StatusHTTPVersionNotSupported, "unsupported protocol version"} |
| } |
| |
| c.lastMethod = req.Method |
| c.r.setInfiniteReadLimit() |
| |
| hosts, haveHost := req.Header["Host"] |
| isH2Upgrade := req.isH2Upgrade() |
| if req.ProtoAtLeast(1, 1) && (!haveHost || len(hosts) == 0) && !isH2Upgrade && req.Method != "CONNECT" { |
| return nil, http_badRequestError("missing required Host header") |
| } |
| if len(hosts) == 1 && !httpguts.ValidHostHeader(hosts[0]) { |
| return nil, http_badRequestError("malformed Host header") |
| } |
| for k, vv := range req.Header { |
| if !httpguts.ValidHeaderFieldName(k) { |
| return nil, http_badRequestError("invalid header name") |
| } |
| for _, v := range vv { |
| if !httpguts.ValidHeaderFieldValue(v) { |
| return nil, http_badRequestError("invalid header value") |
| } |
| } |
| } |
| delete(req.Header, "Host") |
| |
| ctx, cancelCtx := context.WithCancel(ctx) |
| req.ctx = ctx |
| req.RemoteAddr = c.remoteAddr |
| req.TLS = c.tlsState |
| var reqBody *http_body |
| switch b := req.Body.(type) { |
| case http_noBody: |
| case *http_body: |
| reqBody = b |
| reqBody.doEarlyClose = true |
| default: |
| panic(fmt.Errorf("http: unexpected request body type %T", req.Body)) |
| } |
| |
| c.rwc.SetReadDeadline(wholeReqDeadline) |
| |
| w = &http_response{ |
| conn: c, |
| cancelCtx: cancelCtx, |
| req: req, |
| reqBody: reqBody, |
| handlerHeader: make(http_Header), |
| contentLength: -1, |
| |
| // We populate these ahead of time so we're not |
| // reading from req.Header after their Handler starts |
| // and maybe mutates it (Issue 14940) |
| wants10KeepAlive: req.wantsHttp10KeepAlive(), |
| wantsClose: req.wantsClose(), |
| } |
| if isH2Upgrade { |
| w.closeAfterReply = true |
| } |
| w.cw.res = w |
| w.w = http_newBufioWriterSize(&w.cw, http_bufferBeforeChunkingSize) |
| return w, nil |
| } |
| |
| // http1ServerSupportsRequest reports whether Go's HTTP/1.x server |
| // supports the given request. |
| func http_http1ServerSupportsRequest(req *http_Request) bool { |
| if req.ProtoMajor == 1 { |
| return true |
| } |
| // Accept "PRI * HTTP/2.0" upgrade requests, so Handlers can |
| // wire up their own HTTP/2 upgrades. |
| if req.ProtoMajor == 2 && req.ProtoMinor == 0 && |
| req.Method == "PRI" && req.RequestURI == "*" { |
| return true |
| } |
| // Reject HTTP/0.x, and all other HTTP/2+ requests (which |
| // aren't encoded in ASCII anyway). |
| return false |
| } |
| |
| func (w *http_response) Header() http_Header { |
| if w.cw.header == nil && w.wroteHeader && !w.cw.wroteHeader { |
| // Accessing the header between logically writing it |
| // and physically writing it means we need to allocate |
| // a clone to snapshot the logically written state. |
| w.cw.header = w.handlerHeader.Clone() |
| } |
| w.calledHeader = true |
| return w.handlerHeader |
| } |
| |
| // maxPostHandlerReadBytes is the max number of Request.Body bytes not |
| // consumed by a handler that the server will read from the client |
| // in order to keep a connection alive. If there are more bytes |
| // than this, the server, to be paranoid, instead sends a |
| // "Connection close" response. |
| // |
| // This number is approximately what a typical machine's TCP buffer |
| // size is anyway. (if we have the bytes on the machine, we might as |
| // well read them) |
| const http_maxPostHandlerReadBytes = 256 << 10 |
| |
| func http_checkWriteHeaderCode(code int) { |
| // Issue 22880: require valid WriteHeader status codes. |
| // For now we only enforce that it's three digits. |
| // In the future we might block things over 599 (600 and above aren't defined |
| // at https://httpwg.org/specs/rfc7231.html#status.codes). |
| // But for now any three digits. |
| // |
| // We used to send "HTTP/1.1 000 0" on the wire in responses but there's |
| // no equivalent bogus thing we can realistically send in HTTP/2, |
| // so we'll consistently panic instead and help people find their bugs |
| // early. (We can't return an error from WriteHeader even if we wanted to.) |
| if code < 100 || code > 999 { |
| panic(fmt.Sprintf("invalid WriteHeader code %v", code)) |
| } |
| } |
| |
| // relevantCaller searches the call stack for the first function outside of net/http. |
| // The purpose of this function is to provide more helpful error messages. |
| func http_relevantCaller() runtime.Frame { |
| pc := make([]uintptr, 16) |
| n := runtime.Callers(1, pc) |
| frames := runtime.CallersFrames(pc[:n]) |
| var frame runtime.Frame |
| for { |
| var more bool |
| frame, more = frames.Next() |
| if !strings.HasPrefix(frame.Function, "net/http.") { |
| return frame |
| } |
| if !more { |
| break |
| } |
| } |
| return frame |
| } |
| |
| func (w *http_response) WriteHeader(code int) { |
| if w.conn.hijacked() { |
| caller := http_relevantCaller() |
| w.conn.server.logf("http: response.WriteHeader on hijacked connection from %s (%s:%d)", caller.Function, path.Base(caller.File), caller.Line) |
| return |
| } |
| if w.wroteHeader { |
| caller := http_relevantCaller() |
| w.conn.server.logf("http: superfluous response.WriteHeader call from %s (%s:%d)", caller.Function, path.Base(caller.File), caller.Line) |
| return |
| } |
| http_checkWriteHeaderCode(code) |
| |
| // Sending a 100 Continue or any non-1XX header disables the |
| // automatically-sent 100 Continue from Request.Body.Read. If it is a final |
| // response (200 or higher), we skip draining the request body, which the |
| // client will never send. |
| if code == 100 || code >= 200 { |
| w.disableWriteContinue(code >= 200) |
| } |
| |
| // Handle informational headers. |
| // |
| // We shouldn't send any further headers after 101 Switching Protocols, |
| // so it takes the non-informational path. |
| if code >= 100 && code <= 199 && code != http_StatusSwitchingProtocols { |
| http_writeStatusLine(w.conn.bufw, w.req.ProtoAtLeast(1, 1), code, w.statusBuf[:]) |
| |
| // Per RFC 8297 we must not clear the current header map |
| w.handlerHeader.WriteSubset(w.conn.bufw, http_excludedHeadersNoBody) |
| w.conn.bufw.Write(http_crlf) |
| w.conn.bufw.Flush() |
| |
| return |
| } |
| |
| w.wroteHeader = true |
| w.status = code |
| |
| if w.calledHeader && w.cw.header == nil { |
| w.cw.header = w.handlerHeader.Clone() |
| } |
| |
| if cl := w.handlerHeader.get("Content-Length"); cl != "" { |
| v, err := strconv.ParseInt(cl, 10, 64) |
| if err == nil && v >= 0 { |
| w.contentLength = v |
| } else { |
| w.conn.server.logf("http: invalid Content-Length of %q", cl) |
| w.handlerHeader.Del("Content-Length") |
| } |
| } |
| } |
| |
| // extraHeader is the set of headers sometimes added by chunkWriter.writeHeader. |
| // This type is used to avoid extra allocations from cloning and/or populating |
| // the response Header map and all its 1-element slices. |
| type http_extraHeader struct { |
| contentType string |
| connection string |
| transferEncoding string |
| date []byte // written if not nil |
| contentLength []byte // written if not nil |
| } |
| |
| // Sorted the same as extraHeader.Write's loop. |
| var http_extraHeaderKeys = [][]byte{ |
| []byte("Content-Type"), |
| []byte("Connection"), |
| []byte("Transfer-Encoding"), |
| } |
| |
| var ( |
| http_headerContentLength = []byte("Content-Length: ") |
| http_headerDate = []byte("Date: ") |
| ) |
| |
| // Write writes the headers described in h to w. |
| // |
| // This method has a value receiver, despite the somewhat large size |
| // of h, because it prevents an allocation. The escape analysis isn't |
| // smart enough to realize this function doesn't mutate h. |
| func (h http_extraHeader) Write(w *bufio.Writer) { |
| if h.date != nil { |
| w.Write(http_headerDate) |
| w.Write(h.date) |
| w.Write(http_crlf) |
| } |
| if h.contentLength != nil { |
| w.Write(http_headerContentLength) |
| w.Write(h.contentLength) |
| w.Write(http_crlf) |
| } |
| for i, v := range []string{h.contentType, h.connection, h.transferEncoding} { |
| if v != "" { |
| w.Write(http_extraHeaderKeys[i]) |
| w.Write(http_colonSpace) |
| w.WriteString(v) |
| w.Write(http_crlf) |
| } |
| } |
| } |
| |
| // writeHeader finalizes the header sent to the client and writes it |
| // to cw.res.conn.bufw. |
| // |
| // p is not written by writeHeader, but is the first chunk of the body |
| // that will be written. It is sniffed for a Content-Type if none is |
| // set explicitly. It's also used to set the Content-Length, if the |
| // total body size was small and the handler has already finished |
| // running. |
| func (cw *http_chunkWriter) writeHeader(p []byte) { |
| if cw.wroteHeader { |
| return |
| } |
| cw.wroteHeader = true |
| |
| w := cw.res |
| keepAlivesEnabled := w.conn.server.doKeepAlives() |
| isHEAD := w.req.Method == "HEAD" |
| |
| // header is written out to w.conn.buf below. Depending on the |
| // state of the handler, we either own the map or not. If we |
| // don't own it, the exclude map is created lazily for |
| // WriteSubset to remove headers. The setHeader struct holds |
| // headers we need to add. |
| header := cw.header |
| owned := header != nil |
| if !owned { |
| header = w.handlerHeader |
| } |
| var excludeHeader map[string]bool |
| delHeader := func(key string) { |
| if owned { |
| header.Del(key) |
| return |
| } |
| if _, ok := header[key]; !ok { |
| return |
| } |
| if excludeHeader == nil { |
| excludeHeader = make(map[string]bool) |
| } |
| excludeHeader[key] = true |
| } |
| var setHeader http_extraHeader |
| |
| // Don't write out the fake "Trailer:foo" keys. See TrailerPrefix. |
| trailers := false |
| for k := range cw.header { |
| if strings.HasPrefix(k, http_TrailerPrefix) { |
| if excludeHeader == nil { |
| excludeHeader = make(map[string]bool) |
| } |
| excludeHeader[k] = true |
| trailers = true |
| } |
| } |
| for _, v := range cw.header["Trailer"] { |
| trailers = true |
| http_foreachHeaderElement(v, cw.res.declareTrailer) |
| } |
| |
| te := header.get("Transfer-Encoding") |
| hasTE := te != "" |
| |
| // If the handler is done but never sent a Content-Length |
| // response header and this is our first (and last) write, set |
| // it, even to zero. This helps HTTP/1.0 clients keep their |
| // "keep-alive" connections alive. |
| // Exceptions: 304/204/1xx responses never get Content-Length, and if |
| // it was a HEAD request, we don't know the difference between |
| // 0 actual bytes and 0 bytes because the handler noticed it |
| // was a HEAD request and chose not to write anything. So for |
| // HEAD, the handler should either write the Content-Length or |
| // write non-zero bytes. If it's actually 0 bytes and the |
| // handler never looked at the Request.Method, we just don't |
| // send a Content-Length header. |
| // Further, we don't send an automatic Content-Length if they |
| // set a Transfer-Encoding, because they're generally incompatible. |
| if w.handlerDone.Load() && !trailers && !hasTE && http_bodyAllowedForStatus(w.status) && !header.has("Content-Length") && (!isHEAD || len(p) > 0) { |
| w.contentLength = int64(len(p)) |
| setHeader.contentLength = strconv.AppendInt(cw.res.clenBuf[:0], int64(len(p)), 10) |
| } |
| |
| // If this was an HTTP/1.0 request with keep-alive and we sent a |
| // Content-Length back, we can make this a keep-alive response ... |
| if w.wants10KeepAlive && keepAlivesEnabled { |
| sentLength := header.get("Content-Length") != "" |
| if sentLength && header.get("Connection") == "keep-alive" { |
| w.closeAfterReply = false |
| } |
| } |
| |
| // Check for an explicit (and valid) Content-Length header. |
| hasCL := w.contentLength != -1 |
| |
| if w.wants10KeepAlive && (isHEAD || hasCL || !http_bodyAllowedForStatus(w.status)) { |
| _, connectionHeaderSet := header["Connection"] |
| if !connectionHeaderSet { |
| setHeader.connection = "keep-alive" |
| } |
| } else if !w.req.ProtoAtLeast(1, 1) || w.wantsClose { |
| w.closeAfterReply = true |
| } |
| |
| if header.get("Connection") == "close" || !keepAlivesEnabled { |
| w.closeAfterReply = true |
| } |
| |
| // If the client wanted a 100-continue but we never sent it to |
| // them (or, more strictly: we never finished reading their |
| // request body), don't reuse this connection. |
| // |
| // This behavior was first added on the theory that we don't know |
| // if the next bytes on the wire are going to be the remainder of |
| // the request body or the subsequent request (see issue 11549), |
| // but that's not correct: If we keep using the connection, |
| // the client is required to send the request body whether we |
| // asked for it or not. |
| // |
| // We probably do want to skip reusing the connection in most cases, |
| // however. If the client is offering a large request body that we |
| // don't intend to use, then it's better to close the connection |
| // than to read the body. For now, assume that if we're sending |
| // headers, the handler is done reading the body and we should |
| // drop the connection if we haven't seen EOF. |
| if w.ecReader != nil && w.reqBody.bodyRemains() { |
| w.closeAfterReply = true |
| } |
| |
| // We do this by default because there are a number of clients that |
| // send a full request before starting to read the response, and they |
| // can deadlock if we start writing the response with unconsumed body |
| // remaining. See Issue 15527 for some history. |
| // |
| // If full duplex mode has been enabled with ResponseController.EnableFullDuplex, |
| // then leave the request body alone. |
| // |
| // We don't take this path when w.closeAfterReply is set. |
| // We may not need to consume the request to get ready for the next one |
| // (since we're closing the conn), but a client which sends a full request |
| // before reading a response may deadlock in this case. |
| // This behavior has been present since CL 5268043 (2011), however, |
| // so it doesn't seem to be causing problems. |
| if w.req.ContentLength != 0 && w.reqBody != nil && !w.closeAfterReply && !w.fullDuplex { |
| var discard, tooBig bool |
| w.reqBody.mu.Lock() |
| switch { |
| case w.reqBody.closed: |
| if !w.reqBody.sawEOF { |
| // Body was closed in handler with non-EOF error. |
| w.closeAfterReply = true |
| } |
| case w.reqBody.unreadDataSizeLocked() >= http_maxPostHandlerReadBytes: |
| tooBig = true |
| default: |
| discard = true |
| } |
| w.reqBody.mu.Unlock() |
| |
| if discard { |
| w.reqBody.Close() |
| if w.reqBody.didEarlyClose() { |
| w.closeAfterReply = true |
| } |
| } |
| if tooBig { |
| w.requestTooLarge() |
| delHeader("Connection") |
| setHeader.connection = "close" |
| } |
| } |
| |
| code := w.status |
| if http_bodyAllowedForStatus(code) { |
| // If no content type, apply sniffing algorithm to body. |
| _, haveType := header["Content-Type"] |
| |
| // If the Content-Encoding was set and is non-blank, |
| // we shouldn't sniff the body. See Issue 31753. |
| ce := header.Get("Content-Encoding") |
| hasCE := len(ce) > 0 |
| if !hasCE && !haveType && !hasTE && len(p) > 0 { |
| setHeader.contentType = http_DetectContentType(p) |
| } |
| } else { |
| for _, k := range http_suppressedHeaders(code) { |
| delHeader(k) |
| } |
| } |
| |
| if !header.has("Date") { |
| setHeader.date = time.Now().UTC().AppendFormat(cw.res.dateBuf[:0], http_TimeFormat) |
| } |
| |
| if hasCL && hasTE && te != "identity" { |
| // TODO: return an error if WriteHeader gets a return parameter |
| // For now just ignore the Content-Length. |
| w.conn.server.logf("http: WriteHeader called with both Transfer-Encoding of %q and a Content-Length of %d", |
| te, w.contentLength) |
| delHeader("Content-Length") |
| hasCL = false |
| } |
| |
| if w.req.Method == "HEAD" || !http_bodyAllowedForStatus(code) || code == http_StatusNoContent { |
| // Response has no body. |
| delHeader("Transfer-Encoding") |
| } else if hasCL { |
| // Content-Length has been provided, so no chunking is to be done. |
| delHeader("Transfer-Encoding") |
| } else if w.req.ProtoAtLeast(1, 1) { |
| // HTTP/1.1 or greater: Transfer-Encoding has been set to identity, and no |
| // content-length has been provided. The connection must be closed after the |
| // reply is written, and no chunking is to be done. This is the setup |
| // recommended in the Server-Sent Events candidate recommendation 11, |
| // section 8. |
| if hasTE && te == "identity" { |
| cw.chunking = false |
| w.closeAfterReply = true |
| delHeader("Transfer-Encoding") |
| } else { |
| // HTTP/1.1 or greater: use chunked transfer encoding |
| // to avoid closing the connection at EOF. |
| cw.chunking = true |
| setHeader.transferEncoding = "chunked" |
| if hasTE && te == "chunked" { |
| // We will send the chunked Transfer-Encoding header later. |
| delHeader("Transfer-Encoding") |
| } |
| } |
| } else { |
| // HTTP version < 1.1: cannot do chunked transfer |
| // encoding and we don't know the Content-Length so |
| // signal EOF by closing connection. |
| w.closeAfterReply = true |
| delHeader("Transfer-Encoding") // in case already set |
| } |
| |
| // Cannot use Content-Length with non-identity Transfer-Encoding. |
| if cw.chunking { |
| delHeader("Content-Length") |
| } |
| if !w.req.ProtoAtLeast(1, 0) { |
| return |
| } |
| |
| // Only override the Connection header if it is not a successful |
| // protocol switch response and if KeepAlives are not enabled. |
| // See https://golang.org/issue/36381. |
| delConnectionHeader := w.closeAfterReply && |
| (!keepAlivesEnabled || !http_hasToken(cw.header.get("Connection"), "close")) && |
| !http_isProtocolSwitchResponse(w.status, header) |
| if delConnectionHeader { |
| delHeader("Connection") |
| if w.req.ProtoAtLeast(1, 1) { |
| setHeader.connection = "close" |
| } |
| } |
| |
| http_writeStatusLine(w.conn.bufw, w.req.ProtoAtLeast(1, 1), code, w.statusBuf[:]) |
| cw.header.WriteSubset(w.conn.bufw, excludeHeader) |
| setHeader.Write(w.conn.bufw) |
| w.conn.bufw.Write(http_crlf) |
| } |
| |
| // foreachHeaderElement splits v according to the "#rule" construction |
| // in RFC 7230 section 7 and calls fn for each non-empty element. |
| func http_foreachHeaderElement(v string, fn func(string)) { |
| v = textproto.TrimString(v) |
| if v == "" { |
| return |
| } |
| if !strings.Contains(v, ",") { |
| fn(v) |
| return |
| } |
| for f := range strings.SplitSeq(v, ",") { |
| if f = textproto.TrimString(f); f != "" { |
| fn(f) |
| } |
| } |
| } |
| |
| // writeStatusLine writes an HTTP/1.x Status-Line (RFC 7230 Section 3.1.2) |
| // to bw. is11 is whether the HTTP request is HTTP/1.1. false means HTTP/1.0. |
| // code is the response status code. |
| // scratch is an optional scratch buffer. If it has at least capacity 3, it's used. |
| func http_writeStatusLine(bw *bufio.Writer, is11 bool, code int, scratch []byte) { |
| if is11 { |
| bw.WriteString("HTTP/1.1 ") |
| } else { |
| bw.WriteString("HTTP/1.0 ") |
| } |
| if text := http_StatusText(code); text != "" { |
| bw.Write(strconv.AppendInt(scratch[:0], int64(code), 10)) |
| bw.WriteByte(' ') |
| bw.WriteString(text) |
| bw.WriteString("\r\n") |
| } else { |
| // don't worry about performance |
| fmt.Fprintf(bw, "%03d status code %d\r\n", code, code) |
| } |
| } |
| |
| // bodyAllowed reports whether a Write is allowed for this response type. |
| // It's illegal to call this before the header has been flushed. |
| func (w *http_response) bodyAllowed() bool { |
| if !w.wroteHeader { |
| panic("net/http: bodyAllowed called before the header was written") |
| } |
| return http_bodyAllowedForStatus(w.status) |
| } |
| |
| // The Life Of A Write is like this: |
| // |
| // Handler starts. No header has been sent. The handler can either |
| // write a header, or just start writing. Writing before sending a header |
| // sends an implicitly empty 200 OK header. |
| // |
| // If the handler didn't declare a Content-Length up front, we either |
| // go into chunking mode or, if the handler finishes running before |
| // the chunking buffer size, we compute a Content-Length and send that |
| // in the header instead. |
| // |
| // Likewise, if the handler didn't set a Content-Type, we sniff that |
| // from the initial chunk of output. |
| // |
| // The Writers are wired together like: |
| // |
| // 1. *response (the ResponseWriter) -> |
| // 2. (*response).w, a [*bufio.Writer] of bufferBeforeChunkingSize bytes -> |
| // 3. chunkWriter.Writer (whose writeHeader finalizes Content-Length/Type) |
| // and which writes the chunk headers, if needed -> |
| // 4. conn.bufw, a *bufio.Writer of default (4kB) bytes, writing to -> |
| // 5. checkConnErrorWriter{c}, which notes any non-nil error on Write |
| // and populates c.werr with it if so, but otherwise writes to -> |
| // 6. the rwc, the [net.Conn]. |
| // |
| // TODO(bradfitz): short-circuit some of the buffering when the |
| // initial header contains both a Content-Type and Content-Length. |
| // Also short-circuit in (1) when the header's been sent and not in |
| // chunking mode, writing directly to (4) instead, if (2) has no |
| // buffered data. More generally, we could short-circuit from (1) to |
| // (3) even in chunking mode if the write size from (1) is over some |
| // threshold and nothing is in (2). The answer might be mostly making |
| // bufferBeforeChunkingSize smaller and having bufio's fast-paths deal |
| // with this instead. |
| func (w *http_response) Write(data []byte) (n int, err error) { |
| return w.write(len(data), data, "") |
| } |
| |
| func (w *http_response) WriteString(data string) (n int, err error) { |
| return w.write(len(data), nil, data) |
| } |
| |
| // either dataB or dataS is non-zero. |
| func (w *http_response) write(lenData int, dataB []byte, dataS string) (n int, err error) { |
| if w.conn.hijacked() { |
| if lenData > 0 { |
| caller := http_relevantCaller() |
| w.conn.server.logf("http: response.Write on hijacked connection from %s (%s:%d)", caller.Function, path.Base(caller.File), caller.Line) |
| } |
| return 0, http_ErrHijacked |
| } |
| |
| if w.canWriteContinue.Load() { |
| // Body reader wants to write 100 Continue but hasn't yet. Tell it not to. |
| w.disableWriteContinue(true) |
| } |
| |
| if !w.wroteHeader { |
| w.WriteHeader(http_StatusOK) |
| } |
| if lenData == 0 { |
| return 0, nil |
| } |
| if !w.bodyAllowed() { |
| return 0, http_ErrBodyNotAllowed |
| } |
| |
| w.written += int64(lenData) // ignoring errors, for errorKludge |
| if w.contentLength != -1 && w.written > w.contentLength { |
| return 0, http_ErrContentLength |
| } |
| if dataB != nil { |
| return w.w.Write(dataB) |
| } else { |
| return w.w.WriteString(dataS) |
| } |
| } |
| |
| func (w *http_response) finishRequest() { |
| w.handlerDone.Store(true) |
| |
| if !w.wroteHeader { |
| w.WriteHeader(http_StatusOK) |
| } |
| |
| w.w.Flush() |
| http_putBufioWriter(w.w) |
| w.cw.close() |
| w.conn.bufw.Flush() |
| |
| w.conn.r.abortPendingRead() |
| w.reqBody.registerOnHitEOF(nil) // prevent new background read from starting |
| |
| if w.canWriteContinue.Load() { |
| w.disableWriteContinue(true) |
| } |
| |
| // Close the body (regardless of w.closeAfterReply) so we can |
| // re-use its bufio.Reader later safely. |
| // |
| // In full-duplex mode, this may also drain the remaining request body. |
| w.reqBody.Close() |
| |
| if w.req.MultipartForm != nil { |
| w.req.MultipartForm.RemoveAll() |
| } |
| } |
| |
| // shouldReuseConnection reports whether the underlying TCP connection can be reused. |
| // It must only be called after the handler is done executing. |
| func (w *http_response) shouldReuseConnection() bool { |
| if w.closeAfterReply { |
| // The request or something set while executing the |
| // handler indicated we shouldn't reuse this |
| // connection. |
| return false |
| } |
| |
| if w.req.Method != "HEAD" && w.contentLength != -1 && w.bodyAllowed() && w.contentLength != w.written { |
| // Did not write enough. Avoid getting out of sync. |
| return false |
| } |
| |
| // There was some error writing to the underlying connection |
| // during the request, so don't re-use this conn. |
| if w.conn.werr != nil { |
| return false |
| } |
| |
| if w.closedRequestBodyEarly() { |
| return false |
| } |
| |
| return true |
| } |
| |
| func (w *http_response) closedRequestBodyEarly() bool { |
| return w.reqBody != nil && w.reqBody.didEarlyClose() |
| } |
| |
| func (w *http_response) Flush() { |
| w.FlushError() |
| } |
| |
| func (w *http_response) FlushError() error { |
| if !w.wroteHeader { |
| w.WriteHeader(http_StatusOK) |
| } |
| err := w.w.Flush() |
| e2 := w.cw.flush() |
| if err == nil { |
| err = e2 |
| } |
| return err |
| } |
| |
| func (c *http_conn) finalFlush() { |
| if c.bufr != nil { |
| // Steal the bufio.Reader (~4KB worth of memory) and its associated |
| // reader for a future connection. |
| http_putBufioReader(c.bufr) |
| c.bufr = nil |
| } |
| |
| if c.bufw != nil { |
| c.bufw.Flush() |
| // Steal the bufio.Writer (~4KB worth of memory) and its associated |
| // writer for a future connection. |
| http_putBufioWriter(c.bufw) |
| c.bufw = nil |
| } |
| } |
| |
| // Close the connection. |
| func (c *http_conn) close() { |
| c.finalFlush() |
| c.rwc.Close() |
| } |
| |
| // rstAvoidanceDelay is the amount of time we sleep after closing the |
| // write side of a TCP connection before closing the entire socket. |
| // By sleeping, we increase the chances that the client sees our FIN |
| // and processes its final data before they process the subsequent RST |
| // from closing a connection with known unread data. |
| // This RST seems to occur mostly on BSD systems. (And Windows?) |
| // This timeout is somewhat arbitrary (~latency around the planet), |
| // and may be modified by tests. |
| // |
| // TODO(bcmills): This should arguably be a server configuration parameter, |
| // not a hard-coded value. |
| var http_rstAvoidanceDelay = 500 * time.Millisecond |
| |
| type http_closeWriter interface { |
| CloseWrite() error |
| } |
| |
| var _ http_closeWriter = (*net.TCPConn)(nil) |
| |
| // closeWriteAndWait flushes any outstanding data and sends a FIN packet (if |
| // client is connected via TCP), signaling that we're done. We then |
| // pause for a bit, hoping the client processes it before any |
| // subsequent RST. |
| // |
| // See https://golang.org/issue/3595 |
| func (c *http_conn) closeWriteAndWait() { |
| c.finalFlush() |
| if tcp, ok := c.rwc.(http_closeWriter); ok { |
| tcp.CloseWrite() |
| } |
| |
| // When we return from closeWriteAndWait, the caller will fully close the |
| // connection. If client is still writing to the connection, this will cause |
| // the write to fail with ECONNRESET or similar. Unfortunately, many TCP |
| // implementations will also drop unread packets from the client's read buffer |
| // when a write fails, causing our final response to be truncated away too. |
| // |
| // As a result, https://www.rfc-editor.org/rfc/rfc7230#section-6.6 recommends |
| // that “[t]he server … continues to read from the connection until it |
| // receives a corresponding close by the client, or until the server is |
| // reasonably certain that its own TCP stack has received the client's |
| // acknowledgement of the packet(s) containing the server's last response.” |
| // |
| // Unfortunately, we have no straightforward way to be “reasonably certain” |
| // that we have received the client's ACK, and at any rate we don't want to |
| // allow a misbehaving client to soak up server connections indefinitely by |
| // withholding an ACK, nor do we want to go through the complexity or overhead |
| // of using low-level APIs to figure out when a TCP round-trip has completed. |
| // |
| // Instead, we declare that we are “reasonably certain” that we received the |
| // ACK if maxRSTAvoidanceDelay has elapsed. |
| time.Sleep(http_rstAvoidanceDelay) |
| } |
| |
| // validNextProto reports whether the proto is a valid ALPN protocol name. |
| // Everything is valid except the empty string and built-in protocol types, |
| // so that those can't be overridden with alternate implementations. |
| func http_validNextProto(proto string) bool { |
| switch proto { |
| case "", "http/1.1", "http/1.0": |
| return false |
| } |
| return true |
| } |
| |
| const ( |
| http_runHooks = true |
| http_skipHooks = false |
| ) |
| |
| func (c *http_conn) setState(nc net.Conn, state http_ConnState, runHook bool) { |
| srv := c.server |
| switch state { |
| case http_StateNew: |
| srv.trackConn(c, true) |
| case http_StateHijacked, http_StateClosed: |
| srv.trackConn(c, false) |
| } |
| if state > 0xff || state < 0 { |
| panic("internal error") |
| } |
| packedState := uint64(time.Now().Unix()<<8) | uint64(state) |
| c.curState.Store(packedState) |
| if !runHook { |
| return |
| } |
| if hook := srv.ConnState; hook != nil { |
| hook(nc, state) |
| } |
| } |
| |
| func (c *http_conn) getState() (state http_ConnState, unixSec int64) { |
| packedState := c.curState.Load() |
| return http_ConnState(packedState & 0xff), int64(packedState >> 8) |
| } |
| |
| // badRequestError is a literal string (used by in the server in HTML, |
| // unescaped) to tell the user why their request was bad. It should |
| // be plain text without user info or other embedded errors. |
| func http_badRequestError(e string) error { return http_statusError{http_StatusBadRequest, e} } |
| |
| // statusError is an error used to respond to a request with an HTTP status. |
| // The text should be plain text without user info or other embedded errors. |
| type http_statusError struct { |
| code int |
| text string |
| } |
| |
| func (e http_statusError) Error() string { return http_StatusText(e.code) + ": " + e.text } |
| |
| // ErrAbortHandler is a sentinel panic value to abort a handler. |
| // While any panic from ServeHTTP aborts the response to the client, |
| // panicking with ErrAbortHandler also suppresses logging of a stack |
| // trace to the server's error log. |
| var http_ErrAbortHandler = internal.ErrAbortHandler |
| |
| // isCommonNetReadError reports whether err is a common error |
| // encountered during reading a request off the network when the |
| // client has gone away or had its read fail somehow. This is used to |
| // determine which logs are interesting enough to log about. |
| func http_isCommonNetReadError(err error) bool { |
| if err == io.EOF { |
| return true |
| } |
| if neterr, ok := err.(net.Error); ok && neterr.Timeout() { |
| return true |
| } |
| if oe, ok := err.(*net.OpError); ok && oe.Op == "read" { |
| return true |
| } |
| return false |
| } |
| |
| // Serve a new connection. |
| func (c *http_conn) serve(ctx context.Context) { |
| if ra := c.rwc.RemoteAddr(); ra != nil { |
| c.remoteAddr = ra.String() |
| } |
| ctx = context.WithValue(ctx, http_LocalAddrContextKey, c.rwc.LocalAddr()) |
| var inFlightResponse *http_response |
| defer func() { |
| if err := recover(); err != nil && err != http_ErrAbortHandler { |
| const size = 64 << 10 |
| buf := make([]byte, size) |
| buf = buf[:runtime.Stack(buf, false)] |
| c.server.logf("http: panic serving %v: %v\n%s", c.remoteAddr, err, buf) |
| } |
| if inFlightResponse != nil { |
| inFlightResponse.cancelCtx() |
| inFlightResponse.disableWriteContinue(true) |
| } |
| if !c.hijacked() { |
| if inFlightResponse != nil { |
| inFlightResponse.conn.r.abortPendingRead() |
| inFlightResponse.reqBody.Close() |
| } |
| c.close() |
| c.setState(c.rwc, http_StateClosed, http_runHooks) |
| } |
| }() |
| |
| type connectionStater interface { |
| ConnectionState() tls.ConnectionState |
| } |
| type handshakeContexter interface { |
| HandshakeContext(ctx context.Context) error |
| } |
| if connStater, ok := c.rwc.(connectionStater); ok { |
| tlsTO := c.server.tlsHandshakeTimeout() |
| if tlsTO > 0 { |
| dl := time.Now().Add(tlsTO) |
| c.rwc.SetReadDeadline(dl) |
| c.rwc.SetWriteDeadline(dl) |
| } |
| var err error |
| if handshaker, ok := c.rwc.(handshakeContexter); ok { |
| err = handshaker.HandshakeContext(ctx) |
| } |
| if err != nil { |
| // If the handshake failed due to the client not speaking |
| // TLS, assume they're speaking plaintext HTTP and write a |
| // 400 response on the TLS conn's underlying net.Conn. |
| var reason string |
| if re, ok := err.(tls.RecordHeaderError); ok && re.Conn != nil && http_tlsRecordHeaderLooksLikeHTTP(re.RecordHeader) { |
| io.WriteString(re.Conn, "HTTP/1.0 400 Bad Request\r\n\r\nClient sent an HTTP request to an HTTPS server.\n") |
| re.Conn.Close() |
| reason = "client sent an HTTP request to an HTTPS server" |
| } else { |
| reason = err.Error() |
| } |
| c.server.logf("http: TLS handshake error from %s: %v", c.rwc.RemoteAddr(), reason) |
| return |
| } |
| // Restore Conn-level deadlines. |
| if tlsTO > 0 { |
| c.rwc.SetReadDeadline(time.Time{}) |
| c.rwc.SetWriteDeadline(time.Time{}) |
| } |
| c.tlsState = new(tls.ConnectionState) |
| *c.tlsState = connStater.ConnectionState() |
| proto := c.tlsState.NegotiatedProtocol |
| if proto == "h2" && c.server.h2 != nil { |
| // net/http/internal/http2 path. |
| // |
| // Mark freshly created HTTP/2 as active and prevent any server state hooks |
| // from being run on these connections. This prevents closeIdleConns from |
| // closing such connections. See issue https://golang.org/issue/39776. |
| c.setState(c.rwc, http_StateActive, http_skipHooks) |
| const sawClientPreface = false |
| c.server.serveHTTP2Conn(ctx, c.rwc, http_serverHandler{c.server}, sawClientPreface, nil, nil) |
| return |
| } |
| tlsConn, tlsConnOK := c.rwc.(*tls.Conn) |
| if http_validNextProto(proto) && tlsConnOK { |
| // Legacy TLSNextProto path. |
| if fn := c.server.TLSNextProto[proto]; fn != nil { |
| h := http_initALPNRequest{ctx, tlsConn, http_serverHandler{c.server}} |
| // Mark freshly created HTTP/2 as active (see above). |
| c.setState(c.rwc, http_StateActive, http_skipHooks) |
| fn(c.server, tlsConn, h) |
| } |
| return |
| } |
| } |
| |
| // HTTP/1.x or unencrypted HTTP/2. |
| |
| // Set Request.TLS if the conn is not a *tls.Conn, but implements ConnectionState. |
| if c.tlsState == nil { |
| if tc, ok := c.rwc.(connectionStater); ok { |
| c.tlsState = new(tls.ConnectionState) |
| *c.tlsState = tc.ConnectionState() |
| } |
| } |
| |
| ctx, cancelCtx := context.WithCancel(ctx) |
| c.cancelCtx = cancelCtx |
| defer cancelCtx() |
| |
| c.r = &http_connReader{conn: c, rwc: c.rwc} |
| c.bufr = http_newBufioReader(c.r) |
| c.bufw = http_newBufioWriterSize(http_checkConnErrorWriter{c}, 4<<10) |
| |
| if d := c.server.readHeaderTimeout(); d > 0 { |
| c.rwc.SetReadDeadline(time.Now().Add(d)) |
| } |
| |
| protos := c.server.protocols() |
| if c.tlsState == nil && protos.UnencryptedHTTP2() { |
| if c.maybeServeUnencryptedHTTP2(ctx) { |
| return |
| } |
| } |
| if !protos.HTTP1() { |
| return |
| } |
| |
| // HTTP/1.x from here on. |
| |
| for { |
| w, err := c.readRequest(ctx) |
| if c.r.remain != c.server.initialReadLimitSize() { |
| // If we read any bytes off the wire, we're active. |
| c.setState(c.rwc, http_StateActive, http_runHooks) |
| } |
| if c.server.shuttingDown() { |
| return |
| } |
| if err != nil { |
| const errorHeaders = "\r\nContent-Type: text/plain; charset=utf-8\r\nConnection: close\r\n\r\n" |
| |
| switch { |
| case err == http_errTooLarge: |
| // Their HTTP client may or may not be |
| // able to read this if we're |
| // responding to them and hanging up |
| // while they're still writing their |
| // request. Undefined behavior. |
| const publicErr = "431 Request Header Fields Too Large" |
| fmt.Fprintf(c.rwc, "HTTP/1.1 "+publicErr+errorHeaders+publicErr) |
| c.closeWriteAndWait() |
| return |
| |
| case http_isUnsupportedTEError(err): |
| // Respond as per RFC 7230 Section 3.3.1 which says, |
| // A server that receives a request message with a |
| // transfer coding it does not understand SHOULD |
| // respond with 501 (Unimplemented). |
| code := http_StatusNotImplemented |
| |
| // We purposefully aren't echoing back the transfer-encoding's value, |
| // so as to mitigate the risk of cross side scripting by an attacker. |
| fmt.Fprintf(c.rwc, "HTTP/1.1 %d %s%sUnsupported transfer encoding", code, http_StatusText(code), errorHeaders) |
| return |
| |
| case http_isCommonNetReadError(err): |
| return // don't reply |
| |
| default: |
| if v, ok := err.(http_statusError); ok { |
| fmt.Fprintf(c.rwc, "HTTP/1.1 %d %s: %s%s%d %s: %s", v.code, http_StatusText(v.code), v.text, errorHeaders, v.code, http_StatusText(v.code), v.text) |
| return |
| } |
| const publicErr = "400 Bad Request" |
| fmt.Fprintf(c.rwc, "HTTP/1.1 "+publicErr+errorHeaders+publicErr) |
| return |
| } |
| } |
| |
| // Expect 100 Continue support |
| req := w.req |
| if req.expectsContinue() { |
| if req.ProtoAtLeast(1, 1) && req.ContentLength != 0 { |
| // Wrap the Body reader with one that replies on the connection |
| w.ecReader = &http_expectContinueReader{readCloser: req.Body, resp: w} |
| w.canWriteContinue.Store(true) |
| req.Body = w.ecReader |
| } |
| } else if req.Header.get("Expect") != "" { |
| w.sendExpectationFailed() |
| return |
| } |
| |
| c.curReq.Store(w) |
| |
| // Start background read, which detects when a client has closed its connection |
| // while a request handler is still running. When the request has a body, we |
| // start the background read only after the entire body has been consumed. |
| if w.reqBody.bodyRemains() { |
| w.reqBody.registerOnHitEOF(w.conn.r.startBackgroundRead) |
| } else { |
| w.conn.r.startBackgroundRead() |
| } |
| |
| // HTTP cannot have multiple simultaneous active requests.[*] |
| // Until the server replies to this request, it can't read another, |
| // so we might as well run the handler in this goroutine. |
| // [*] Not strictly true: HTTP pipelining. We could let them all process |
| // in parallel even if their responses need to be serialized. |
| // But we're not going to implement HTTP pipelining because it |
| // was never deployed in the wild and the answer is HTTP/2. |
| inFlightResponse = w |
| http_serverHandler{c.server}.ServeHTTP(w, w.req) |
| inFlightResponse = nil |
| w.cancelCtx() |
| if c.hijacked() { |
| c.r.releaseConn() |
| return |
| } |
| w.finishRequest() |
| c.rwc.SetWriteDeadline(time.Time{}) |
| if !w.shouldReuseConnection() { |
| if w.requestBodyLimitHit || w.closedRequestBodyEarly() { |
| c.closeWriteAndWait() |
| } |
| return |
| } |
| c.setState(c.rwc, http_StateIdle, http_runHooks) |
| c.curReq.Store(nil) |
| |
| if !w.conn.server.doKeepAlives() { |
| // We're in shutdown mode. We might've replied |
| // to the user without "Connection: close" and |
| // they might think they can send another |
| // request, but such is life with HTTP/1.1. |
| return |
| } |
| |
| if d := c.server.idleTimeout(); d > 0 { |
| c.rwc.SetReadDeadline(time.Now().Add(d)) |
| } else { |
| c.rwc.SetReadDeadline(time.Time{}) |
| } |
| |
| // Wait for the connection to become readable again before trying to |
| // read the next request. This prevents a ReadHeaderTimeout or |
| // ReadTimeout from starting until the first bytes of the next request |
| // have been received. |
| if _, err := c.bufr.Peek(4); err != nil { |
| return |
| } |
| |
| if d := c.server.readHeaderTimeout(); d > 0 { |
| c.rwc.SetReadDeadline(time.Now().Add(d)) |
| } else { |
| c.rwc.SetReadDeadline(time.Time{}) |
| } |
| } |
| } |
| |
| // unencryptedHTTP2Request is an HTTP handler that initializes |
| // certain uninitialized fields in its *Request. |
| // |
| // It's the unencrypted version of initALPNRequest. |
| type http_unencryptedHTTP2Request struct { |
| ctx context.Context |
| c net.Conn |
| h http_serverHandler |
| } |
| |
| func (h http_unencryptedHTTP2Request) BaseContext() context.Context { return h.ctx } |
| |
| func (h http_unencryptedHTTP2Request) ServeHTTP(rw http_ResponseWriter, req *http_Request) { |
| if req.Body == nil { |
| req.Body = http_NoBody |
| } |
| if req.RemoteAddr == "" { |
| req.RemoteAddr = h.c.RemoteAddr().String() |
| } |
| h.h.ServeHTTP(rw, req) |
| } |
| |
| // unencryptedNetConnInTLSConn is used to pass an unencrypted net.Conn to |
| // functions that only accept a *tls.Conn. |
| type http_unencryptedNetConnInTLSConn struct { |
| net.Conn // panic on all net.Conn methods |
| conn net.Conn |
| } |
| |
| func (c http_unencryptedNetConnInTLSConn) UnencryptedNetConn() net.Conn { |
| return c.conn |
| } |
| |
| func http_unencryptedTLSConn(c net.Conn) *tls.Conn { |
| return tls.Client(http_unencryptedNetConnInTLSConn{conn: c}, nil) |
| } |
| |
| // TLSNextProto key to use for unencrypted HTTP/2 connections. |
| // Not actually a TLS-negotiated protocol. |
| const http_nextProtoUnencryptedHTTP2 = "unencrypted_http2" |
| |
| func (c *http_conn) maybeServeUnencryptedHTTP2(ctx context.Context) bool { |
| var nextFunc func(*http_Server, *tls.Conn, http_Handler) |
| if c.server.h2 == nil { |
| var ok bool |
| nextFunc, ok = c.server.TLSNextProto[http_nextProtoUnencryptedHTTP2] |
| if !ok { |
| return false |
| } |
| } |
| hasPreface := func(c *http_conn, preface []byte) bool { |
| c.r.setReadLimit(int64(len(preface)) - int64(c.bufr.Buffered())) |
| got, err := c.bufr.Peek(len(preface)) |
| c.r.setInfiniteReadLimit() |
| return err == nil && bytes.Equal(got, preface) |
| } |
| if !hasPreface(c, []byte("PRI * HTTP/2.0")) { |
| return false |
| } |
| if !hasPreface(c, []byte("PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n")) { |
| return false |
| } |
| c.setState(c.rwc, http_StateActive, http_skipHooks) |
| if c.server.h2 != nil { |
| const sawClientPreface = true |
| c.server.serveHTTP2Conn(ctx, c.rwc, http_serverHandler{c.server}, sawClientPreface, nil, nil) |
| } else { |
| h := http_unencryptedHTTP2Request{ctx, c.rwc, http_serverHandler{c.server}} |
| nextFunc(c.server, http_unencryptedTLSConn(c.rwc), h) |
| } |
| return true |
| } |
| |
| func (w *http_response) sendExpectationFailed() { |
| // TODO(bradfitz): let ServeHTTP handlers handle |
| // requests with non-standard expectation[s]? Seems |
| // theoretical at best, and doesn't fit into the |
| // current ServeHTTP model anyway. We'd need to |
| // make the ResponseWriter an optional |
| // "ExpectReplier" interface or something. |
| // |
| // For now we'll just obey RFC 7231 5.1.1 which says |
| // "A server that receives an Expect field-value other |
| // than 100-continue MAY respond with a 417 (Expectation |
| // Failed) status code to indicate that the unexpected |
| // expectation cannot be met." |
| w.Header().Set("Connection", "close") |
| w.WriteHeader(http_StatusExpectationFailed) |
| w.finishRequest() |
| } |
| |
| // Hijack implements the [Hijacker.Hijack] method. Our response is both a [ResponseWriter] |
| // and a [Hijacker]. |
| func (w *http_response) Hijack() (rwc net.Conn, buf *bufio.ReadWriter, err error) { |
| if w.handlerDone.Load() { |
| panic("net/http: Hijack called after ServeHTTP finished") |
| } |
| w.disableWriteContinue(false) |
| if w.wroteHeader { |
| w.cw.flush() |
| } |
| |
| c := w.conn |
| c.mu.Lock() |
| defer c.mu.Unlock() |
| |
| // Release the bufioWriter that writes to the chunk writer, it is not |
| // used after a connection has been hijacked. |
| rwc, buf, err = c.hijackLocked() |
| if err == nil { |
| http_putBufioWriter(w.w) |
| w.w = nil |
| } |
| return rwc, buf, err |
| } |
| |
| func (w *http_response) CloseNotify() <-chan bool { |
| w.lazyCloseNotifyMu.Lock() |
| defer w.lazyCloseNotifyMu.Unlock() |
| if w.handlerDone.Load() { |
| panic("net/http: CloseNotify called after ServeHTTP finished") |
| } |
| if w.closeNotifyCh == nil { |
| w.closeNotifyCh = make(chan bool, 1) |
| if w.closeNotifyTriggered { |
| w.closeNotifyCh <- true // action prior closeNotify call |
| } |
| } |
| return w.closeNotifyCh |
| } |
| |
| func (w *http_response) closeNotify() { |
| w.lazyCloseNotifyMu.Lock() |
| defer w.lazyCloseNotifyMu.Unlock() |
| if w.closeNotifyTriggered { |
| return // already triggered |
| } |
| w.closeNotifyTriggered = true |
| if w.closeNotifyCh != nil { |
| w.closeNotifyCh <- true |
| } |
| } |
| |
| // The HandlerFunc type is an adapter to allow the use of |
| // ordinary functions as HTTP handlers. If f is a function |
| // with the appropriate signature, HandlerFunc(f) is a |
| // [Handler] that calls f. |
| type http_HandlerFunc func(http_ResponseWriter, *http_Request) |
| |
| // ServeHTTP calls f(w, r). |
| func (f http_HandlerFunc) ServeHTTP(w http_ResponseWriter, r *http_Request) { |
| f(w, r) |
| } |
| |
| // Helper handlers |
| |
| // Error replies to the request with the specified error message and HTTP code. |
| // It does not otherwise end the request; the caller should ensure no further |
| // writes are done to w. |
| // The error message should be plain text. |
| // |
| // Error deletes the Content-Length header, |
| // sets Content-Type to “text/plain; charset=utf-8”, |
| // and sets X-Content-Type-Options to “nosniff”. |
| // This configures the header properly for the error message, |
| // in case the caller had set it up expecting a successful output. |
| func http_Error(w http_ResponseWriter, error string, code int) { |
| h := w.Header() |
| |
| // Delete the Content-Length header, which might be for some other content. |
| // Assuming the error string fits in the writer's buffer, we'll figure |
| // out the correct Content-Length for it later. |
| // |
| // We don't delete Content-Encoding, because some middleware sets |
| // Content-Encoding: gzip and wraps the ResponseWriter to compress on-the-fly. |
| // See https://go.dev/issue/66343. |
| h.Del("Content-Length") |
| |
| // There might be content type already set, but we reset it to |
| // text/plain for the error message. |
| h.Set("Content-Type", "text/plain; charset=utf-8") |
| h.Set("X-Content-Type-Options", "nosniff") |
| w.WriteHeader(code) |
| fmt.Fprintln(w, error) |
| } |
| |
| // NotFound replies to the request with an HTTP 404 not found error. |
| func http_NotFound(w http_ResponseWriter, r *http_Request) { |
| http_Error(w, "404 page not found", http_StatusNotFound) |
| } |
| |
| // NotFoundHandler returns a simple request handler |
| // that replies to each request with a “404 page not found” reply. |
| func http_NotFoundHandler() http_Handler { return http_HandlerFunc(http_NotFound) } |
| |
| // StripPrefix returns a handler that serves HTTP requests by removing the |
| // given prefix from the request URL's Path (and RawPath if set) and invoking |
| // the handler h. StripPrefix handles a request for a path that doesn't begin |
| // with prefix by replying with an HTTP 404 not found error. The prefix must |
| // match exactly: if the prefix in the request contains escaped characters |
| // the reply is also an HTTP 404 not found error. |
| func http_StripPrefix(prefix string, h http_Handler) http_Handler { |
| if prefix == "" { |
| return h |
| } |
| return http_HandlerFunc(func(w http_ResponseWriter, r *http_Request) { |
| p := strings.TrimPrefix(r.URL.Path, prefix) |
| rp := strings.TrimPrefix(r.URL.RawPath, prefix) |
| if len(p) < len(r.URL.Path) && (r.URL.RawPath == "" || len(rp) < len(r.URL.RawPath)) { |
| r2 := new(http_Request) |
| *r2 = *r |
| r2.URL = new(url.URL) |
| *r2.URL = *r.URL |
| r2.URL.Path = p |
| r2.URL.RawPath = rp |
| h.ServeHTTP(w, r2) |
| } else { |
| http_NotFound(w, r) |
| } |
| }) |
| } |
| |
| // Redirect replies to the request with a redirect to url, |
| // which may be a path relative to the request path. |
| // Any non-ASCII characters in url will be percent-encoded, |
| // but existing percent encodings will not be changed. |
| // |
| // The provided code should be in the 3xx range and is usually |
| // [StatusMovedPermanently], [StatusFound] or [StatusSeeOther]. |
| // |
| // If the Content-Type header has not been set, [Redirect] sets it |
| // to "text/html; charset=utf-8" and writes a small HTML body. |
| // Setting the Content-Type header to any value, including nil, |
| // disables that behavior. |
| func http_Redirect(w http_ResponseWriter, r *http_Request, url string, code int) { |
| if u, err := urlpkg.Parse(url); err == nil { |
| // If url was relative, make its path absolute by |
| // combining with request path. |
| // The client would probably do this for us, |
| // but doing it ourselves is more reliable. |
| // See RFC 7231, section 7.1.2 |
| if u.Scheme == "" && u.Host == "" { |
| oldpath := r.URL.EscapedPath() |
| if oldpath == "" { // should not happen, but avoid a crash if it does |
| oldpath = "/" |
| } |
| |
| // no leading http://server |
| if url == "" || url[0] != '/' { |
| // make relative path absolute |
| olddir, _ := path.Split(oldpath) |
| url = olddir + url |
| } |
| |
| var query string |
| if i := strings.Index(url, "?"); i != -1 { |
| url, query = url[:i], url[i:] |
| } |
| |
| // clean up but preserve trailing slash |
| trailing := strings.HasSuffix(url, "/") |
| url = path.Clean(url) |
| if trailing && !strings.HasSuffix(url, "/") { |
| url += "/" |
| } |
| url += query |
| } |
| } |
| |
| h := w.Header() |
| |
| // RFC 7231 notes that a short HTML body is usually included in |
| // the response because older user agents may not understand 301/307. |
| // Do it only if the request didn't already have a Content-Type header. |
| _, hadCT := h["Content-Type"] |
| |
| h.Set("Location", http_hexEscapeNonASCII(url)) |
| if !hadCT && (r.Method == "GET" || r.Method == "HEAD") { |
| h.Set("Content-Type", "text/html; charset=utf-8") |
| } |
| w.WriteHeader(code) |
| |
| // Shouldn't send the body for POST or HEAD; that leaves GET. |
| if !hadCT && r.Method == "GET" { |
| body := "<a href=\"" + http_htmlEscape(url) + "\">" + http_StatusText(code) + "</a>.\n" |
| fmt.Fprintln(w, body) |
| } |
| } |
| |
| var http_htmlReplacer = strings.NewReplacer( |
| "&", "&", |
| "<", "<", |
| ">", ">", |
| // """ is shorter than """. |
| ` + "`" + `"` + "`" + `, """, |
| // "'" is shorter than "'" and apos was not in HTML until HTML5. |
| "'", "'", |
| ) |
| |
| func http_htmlEscape(s string) string { |
| return http_htmlReplacer.Replace(s) |
| } |
| |
| // Redirect to a fixed URL |
| type http_redirectHandler struct { |
| url string |
| code int |
| } |
| |
| func (rh *http_redirectHandler) ServeHTTP(w http_ResponseWriter, r *http_Request) { |
| http_Redirect(w, r, rh.url, rh.code) |
| } |
| |
| // RedirectHandler returns a request handler that redirects |
| // each request it receives to the given url using the given |
| // status code. |
| // |
| // The provided code should be in the 3xx range and is usually |
| // [StatusMovedPermanently], [StatusFound] or [StatusSeeOther]. |
| func http_RedirectHandler(url string, code int) http_Handler { |
| return &http_redirectHandler{url, code} |
| } |
| |
| // ServeMux is an HTTP request multiplexer. |
| // It matches the URL of each incoming request against a list of registered |
| // patterns and calls the handler for the pattern that |
| // most closely matches the URL. |
| // |
| // # Patterns |
| // |
| // Patterns can match the method, host and path of a request. |
| // Some examples: |
| // |
| // - "/index.html" matches the path "/index.html" for any host and method. |
| // - "GET /static/" matches a GET request whose path begins with "/static/". |
| // - "example.com/" matches any request to the host "example.com". |
| // - "example.com/{$}" matches requests with host "example.com" and path "/". |
| // - "/b/{bucket}/o/{objectname...}" matches paths whose first segment is "b" |
| // and whose third segment is "o". The name "bucket" denotes the second |
| // segment and "objectname" denotes the remainder of the path. |
| // |
| // In general, a pattern looks like |
| // |
| // [METHOD ][HOST]/[PATH] |
| // |
| // All three parts are optional; "/" is a valid pattern. |
| // If METHOD is present, it must be followed by at least one space or tab. |
| // |
| // Literal (that is, non-wildcard) parts of a pattern match |
| // the corresponding parts of a request case-sensitively. |
| // |
| // A pattern with no method matches every method. A pattern |
| // with the method GET matches both GET and HEAD requests. |
| // Otherwise, the method must match exactly. |
| // |
| // A pattern with no host matches every host. |
| // A pattern with a host matches URLs on that host only. |
| // |
| // A path can include wildcard segments of the form {NAME} or {NAME...}. |
| // For example, "/b/{bucket}/o/{objectname...}". |
| // The wildcard name must be a valid Go identifier. |
| // Wildcards must be full path segments: they must be preceded by a slash and followed by |
| // either a slash or the end of the string. |
| // For example, "/b_{bucket}" is not a valid pattern. |
| // |
| // Normally a wildcard matches only a single path segment, |
| // ending at the next literal slash (not %2F) in the request URL. |
| // But if the "..." is present, then the wildcard matches the remainder of the URL path, including slashes. |
| // (Therefore it is invalid for a "..." wildcard to appear anywhere but at the end of a pattern.) |
| // The match for a wildcard can be obtained by calling [Request.PathValue] with the wildcard's name. |
| // A trailing slash in a path acts as an anonymous "..." wildcard. |
| // |
| // The special wildcard {$} matches only the end of the URL. |
| // For example, the pattern "/{$}" matches only the path "/", |
| // whereas the pattern "/" matches every path. |
| // |
| // For matching, both pattern paths and incoming request paths are unescaped segment by segment. |
| // So, for example, the path "/a%2Fb/100%25" is treated as having two segments, "a/b" and "100%". |
| // The pattern "/a%2fb/" matches it, but the pattern "/a/b/" does not. |
| // |
| // # Precedence |
| // |
| // If two or more patterns match a request, then the most specific pattern takes precedence. |
| // A pattern P1 is more specific than P2 if P1 matches a strict subset of P2’s requests; |
| // that is, if P2 matches all the requests of P1 and more. |
| // If neither is more specific, then the patterns conflict. |
| // There is one exception to this rule, for backwards compatibility: |
| // if two patterns would otherwise conflict and one has a host while the other does not, |
| // then the pattern with the host takes precedence. |
| // If a pattern passed to [ServeMux.Handle] or [ServeMux.HandleFunc] conflicts with |
| // another pattern that is already registered, those functions panic. |
| // |
| // As an example of the general rule, "/images/thumbnails/" is more specific than "/images/", |
| // so both can be registered. |
| // The former matches paths beginning with "/images/thumbnails/" |
| // and the latter will match any other path in the "/images/" subtree. |
| // |
| // As another example, consider the patterns "GET /" and "/index.html": |
| // both match a GET request for "/index.html", but the former pattern |
| // matches all other GET and HEAD requests, while the latter matches any |
| // request for "/index.html" that uses a different method. |
| // The patterns conflict. |
| // |
| // # Trailing-slash redirection |
| // |
| // Consider a [ServeMux] with a handler for a subtree, registered using a trailing slash or "..." wildcard. |
| // If the ServeMux receives a request for the subtree root without a trailing slash, |
| // it redirects the request by adding the trailing slash. |
| // This behavior can be overridden with a separate registration for the path without |
| // the trailing slash or "..." wildcard. For example, registering "/images/" causes ServeMux |
| // to redirect a request for "/images" to "/images/", unless "/images" has |
| // been registered separately. |
| // |
| // # Request sanitizing |
| // |
| // ServeMux also takes care of sanitizing the URL request path and the Host |
| // header, stripping the port number and redirecting any request containing . or |
| // .. segments or repeated slashes to an equivalent, cleaner URL. |
| // Escaped path elements such as "%2e" for "." and "%2f" for "/" are preserved |
| // and aren't considered separators for request routing. |
| // |
| // # Compatibility |
| // |
| // The pattern syntax and matching behavior of ServeMux changed significantly |
| // in Go 1.22. To restore the old behavior, set the GODEBUG environment variable |
| // to "httpmuxgo121=1". This setting is read once, at program startup; changes |
| // during execution will be ignored. |
| // |
| // The backwards-incompatible changes include: |
| // - Wildcards are just ordinary literal path segments in 1.21. |
| // For example, the pattern "/{x}" will match only that path in 1.21, |
| // but will match any one-segment path in 1.22. |
| // - In 1.21, no pattern was rejected, unless it was empty or conflicted with an existing pattern. |
| // In 1.22, syntactically invalid patterns will cause [ServeMux.Handle] and [ServeMux.HandleFunc] to panic. |
| // For example, in 1.21, the patterns "/{" and "/a{x}" match themselves, |
| // but in 1.22 they are invalid and will cause a panic when registered. |
| // - In 1.22, each segment of a pattern is unescaped; this was not done in 1.21. |
| // For example, in 1.22 the pattern "/%61" matches the path "/a" ("%61" being the URL escape sequence for "a"), |
| // but in 1.21 it would match only the path "/%2561" (where "%25" is the escape for the percent sign). |
| // - When matching patterns to paths, in 1.22 each segment of the path is unescaped; in 1.21, the entire path is unescaped. |
| // This change mostly affects how paths with %2F escapes adjacent to slashes are treated. |
| // See https://go.dev/issue/21955 for details. |
| type http_ServeMux struct { |
| mu sync.RWMutex |
| tree http_routingNode |
| index http_routingIndex |
| mux121 http_serveMux121 // used only when GODEBUG=httpmuxgo121=1 |
| } |
| |
| // NewServeMux allocates and returns a new [ServeMux]. |
| func http_NewServeMux() *http_ServeMux { |
| return &http_ServeMux{} |
| } |
| |
| // DefaultServeMux is the default [ServeMux] used by [Serve]. |
| var http_DefaultServeMux = &http_defaultServeMux |
| |
| var http_defaultServeMux http_ServeMux |
| |
| // cleanPath returns the canonical path for p, eliminating . and .. elements. |
| |
| // cleanPath returns the canonical path for p, eliminating . and .. elements. |
| func http_cleanPath(p string) string { |
| if p == "" { |
| return "/" |
| } |
| if p[0] != '/' { |
| p = "/" + p |
| } |
| np := path.Clean(p) |
| // path.Clean removes trailing slash except for root; |
| // put the trailing slash back if necessary. |
| if p[len(p)-1] == '/' && np != "/" { |
| // Fast path for common case of p being the string we want: |
| if len(p) == len(np)+1 && strings.HasPrefix(p, np) { |
| np = p |
| } else { |
| np += "/" |
| } |
| } |
| return np |
| } |
| |
| // stripHostPort returns h without any trailing ":<port>". |
| func http_stripHostPort(h string) string { |
| // If no port on host, return unchanged |
| if !strings.Contains(h, ":") { |
| return h |
| } |
| host, _, err := net.SplitHostPort(h) |
| if err != nil { |
| return h // on error, return unchanged |
| } |
| return host |
| } |
| |
| // Handler returns the handler to use for the given request, |
| // consulting r.Method, r.Host, and r.URL.Path. It always returns |
| // a non-nil handler. If the path is not in its canonical form, the |
| // handler will be an internally-generated handler that redirects |
| // to the canonical path. If the host contains a port, it is ignored |
| // when matching handlers. |
| // |
| // The path and host are used unchanged for CONNECT requests. |
| // |
| // Handler also returns the registered pattern that matches the |
| // request or, in the case of internally-generated redirects, |
| // the path that will match after following the redirect. |
| // |
| // If there is no registered handler that applies to the request, |
| // Handler returns a “page not found” or “method not supported” |
| // handler and an empty pattern. |
| // |
| // Handler does not modify its argument. In particular, it does not |
| // populate named path wildcards, so r.PathValue will always return |
| // the empty string. |
| func (mux *http_ServeMux) Handler(r *http_Request) (h http_Handler, pattern string) { |
| if http_use121 { |
| return mux.mux121.findHandler(r) |
| } |
| h, p, _, _ := mux.findHandler(r) |
| return h, p |
| } |
| |
| // findHandler finds a handler for a request. |
| // If there is a matching handler, it returns it and the pattern that matched. |
| // Otherwise it returns a Redirect or NotFound handler with the path that would match |
| // after the redirect. |
| func (mux *http_ServeMux) findHandler(r *http_Request) (h http_Handler, patStr string, _ *http_pattern, matches []string) { |
| var n *http_routingNode |
| host := r.URL.Host |
| escapedPath := r.URL.EscapedPath() |
| path := escapedPath |
| // CONNECT requests are not canonicalized. |
| if r.Method == "CONNECT" { |
| // If r.URL.Path is /tree and its handler is not registered, |
| // the /tree -> /tree/ redirect applies to CONNECT requests |
| // but the path canonicalization does not. |
| _, _, u := mux.matchOrRedirect(host, r.Method, path, r.URL) |
| if u != nil { |
| return http_RedirectHandler(u.String(), http_StatusTemporaryRedirect), u.Path, nil, nil |
| } |
| // Redo the match, this time with r.Host instead of r.URL.Host. |
| // Pass a nil URL to skip the trailing-slash redirect logic. |
| n, matches, _ = mux.matchOrRedirect(r.Host, r.Method, path, nil) |
| } else { |
| // All other requests have any port stripped and path cleaned |
| // before passing to mux.handler. |
| host = http_stripHostPort(r.Host) |
| path = http_cleanPath(path) |
| |
| // If the given path is /tree and its handler is not registered, |
| // redirect for /tree/. |
| var u *url.URL |
| n, matches, u = mux.matchOrRedirect(host, r.Method, path, r.URL) |
| if u != nil { |
| return http_RedirectHandler(u.String(), http_StatusTemporaryRedirect), n.pattern.String(), nil, nil |
| } |
| if path != escapedPath { |
| // Redirect to cleaned path. |
| patStr := "" |
| if n != nil { |
| patStr = n.pattern.String() |
| } |
| u := http_urlFromEscaped(path, r.URL.RawQuery) |
| return http_RedirectHandler(u.String(), http_StatusTemporaryRedirect), patStr, nil, nil |
| } |
| } |
| if n == nil { |
| // We didn't find a match with the request method. To distinguish between |
| // Not Found and Method Not Allowed, see if there is another pattern that |
| // matches except for the method. |
| allowedMethods := mux.matchingMethods(host, path) |
| if len(allowedMethods) > 0 { |
| return http_HandlerFunc(func(w http_ResponseWriter, r *http_Request) { |
| w.Header().Set("Allow", strings.Join(allowedMethods, ", ")) |
| http_Error(w, http_StatusText(http_StatusMethodNotAllowed), http_StatusMethodNotAllowed) |
| }), "", nil, nil |
| } |
| return http_NotFoundHandler(), "", nil, nil |
| } |
| return n.handler, n.pattern.String(), n.pattern, matches |
| } |
| |
| // matchOrRedirect looks up a node in the tree that matches the host, method and path. |
| // |
| // If the url argument is non-nil, handler also deals with trailing-slash |
| // redirection: when a path doesn't match exactly, the match is tried again |
| // after appending "/" to the path. If that second match succeeds, the last |
| // return value is the URL to redirect to. |
| func (mux *http_ServeMux) matchOrRedirect(host, method, path string, u *url.URL) (_ *http_routingNode, matches []string, redirectTo *url.URL) { |
| mux.mu.RLock() |
| defer mux.mu.RUnlock() |
| |
| n, matches := mux.tree.match(host, method, path) |
| // We can terminate here if any of the following is true: |
| // - We have an exact match already. |
| // - We were asked not to try trailing slash redirection. |
| // - The URL already has a trailing slash. |
| // - The URL is an empty string. |
| if !http_exactMatch(n, path) && u != nil && !strings.HasSuffix(path, "/") && path != "" { |
| // If there is an exact match with a trailing slash, then redirect. |
| path += "/" |
| n2, _ := mux.tree.match(host, method, path) |
| if http_exactMatch(n2, path) { |
| // It is safe to return n2 here: it is used only in the second RedirectHandler case |
| // of findHandler, and that method returns before it does the "n == nil" check where |
| // the first return value matters. We return it here only to make the pattern available |
| // to findHandler. |
| return n2, nil, http_urlFromEscaped(path, u.RawQuery) |
| } |
| } |
| return n, matches, nil |
| } |
| |
| // urlFromEscaped returns a url.URL constructed from an escaped path and a raw |
| // query. |
| // |
| // It ensures that the Path and RawPath fields are in sync by unescaping the |
| // escaped path. Populating only the Path field and leaving RawPath empty (or |
| // failing to keep them in sync) can cause url.URL.String to produce a URL with |
| // either unexpected escaping (e.g., double-escaping "%" into "%25" in an |
| // already escaped path) or a lack thereof (e.g., losing the escaping of "%2f" |
| // and turning it into a literal path separator "/"). |
| func http_urlFromEscaped(escaped, rawQuery string) *url.URL { |
| unescaped, err := url.PathUnescape(escaped) |
| // Should be impossible, since ServeMux will reject unparsable URLs way |
| // earlier. |
| if err != nil { |
| unescaped = escaped |
| } |
| return &url.URL{ |
| Path: unescaped, |
| RawPath: escaped, |
| RawQuery: rawQuery, |
| } |
| } |
| |
| // exactMatch reports whether the node's pattern exactly matches the path. |
| // As a special case, if the node is nil, exactMatch return false. |
| // |
| // Before wildcards were introduced, it was clear that an exact match meant |
| // that the pattern and path were the same string. The only other possibility |
| // was that a trailing-slash pattern, like "/", matched a path longer than |
| // it, like "/a". |
| // |
| // With wildcards, we define an inexact match as any one where a multi wildcard |
| // matches a non-empty string. All other matches are exact. |
| // For example, these are all exact matches: |
| // |
| // pattern path |
| // /a /a |
| // /{x} /a |
| // /a/{$} /a/ |
| // /a/ /a/ |
| // |
| // The last case has a multi wildcard (implicitly), but the match is exact because |
| // the wildcard matches the empty string. |
| // |
| // Examples of matches that are not exact: |
| // |
| // pattern path |
| // / /a |
| // /a/{x...} /a/b |
| func http_exactMatch(n *http_routingNode, path string) bool { |
| if n == nil { |
| return false |
| } |
| // We can't directly implement the definition (empty match for multi |
| // wildcard) because we don't record a match for anonymous multis. |
| |
| // If there is no multi, the match is exact. |
| if !n.pattern.lastSegment().multi { |
| return true |
| } |
| |
| // If the path doesn't end in a trailing slash, then the multi match |
| // is non-empty. |
| if len(path) > 0 && path[len(path)-1] != '/' { |
| return false |
| } |
| // Only patterns ending in {$} or a multi wildcard can |
| // match a path with a trailing slash. |
| // For the match to be exact, the number of pattern |
| // segments should be the same as the number of slashes in the path. |
| // E.g. "/a/b/{$}" and "/a/b/{...}" exactly match "/a/b/", but "/a/" does not. |
| return len(n.pattern.segments) == strings.Count(path, "/") |
| } |
| |
| // matchingMethods return a sorted list of all methods that would match with the given host and path. |
| func (mux *http_ServeMux) matchingMethods(host, path string) []string { |
| // Hold the read lock for the entire method so that the two matches are done |
| // on the same set of registered patterns. |
| mux.mu.RLock() |
| defer mux.mu.RUnlock() |
| ms := map[string]bool{} |
| mux.tree.matchingMethods(host, path, ms) |
| // matchOrRedirect will try appending a trailing slash if there is no match. |
| if !strings.HasSuffix(path, "/") { |
| mux.tree.matchingMethods(host, path+"/", ms) |
| } |
| return slices.Sorted(maps.Keys(ms)) |
| } |
| |
| // ServeHTTP dispatches the request to the handler whose |
| // pattern most closely matches the request URL. |
| func (mux *http_ServeMux) ServeHTTP(w http_ResponseWriter, r *http_Request) { |
| if r.RequestURI == "*" { |
| if r.ProtoAtLeast(1, 1) { |
| w.Header().Set("Connection", "close") |
| } |
| w.WriteHeader(http_StatusBadRequest) |
| return |
| } |
| var h http_Handler |
| if http_use121 { |
| h, _ = mux.mux121.findHandler(r) |
| } else { |
| h, r.Pattern, r.pat, r.matches = mux.findHandler(r) |
| } |
| h.ServeHTTP(w, r) |
| } |
| |
| // The four functions below all call ServeMux.register so that callerLocation |
| // always refers to user code. |
| |
| // Handle registers the handler for the given pattern. |
| // If the given pattern conflicts with one that is already registered |
| // or if the pattern is invalid, Handle panics. |
| // |
| // See [ServeMux] for details on valid patterns and conflict rules. |
| func (mux *http_ServeMux) Handle(pattern string, handler http_Handler) { |
| if http_use121 { |
| mux.mux121.handle(pattern, handler) |
| } else { |
| mux.register(pattern, handler) |
| } |
| } |
| |
| // HandleFunc registers the handler function for the given pattern. |
| // If the given pattern conflicts with one that is already registered |
| // or if the pattern is invalid, HandleFunc panics. |
| // |
| // See [ServeMux] for details on valid patterns and conflict rules. |
| func (mux *http_ServeMux) HandleFunc(pattern string, handler func(http_ResponseWriter, *http_Request)) { |
| if http_use121 { |
| mux.mux121.handleFunc(pattern, handler) |
| } else { |
| mux.register(pattern, http_HandlerFunc(handler)) |
| } |
| } |
| |
| // Handle registers the handler for the given pattern in [DefaultServeMux]. |
| // The documentation for [ServeMux] explains how patterns are matched. |
| func http_Handle(pattern string, handler http_Handler) { |
| if http_use121 { |
| http_DefaultServeMux.mux121.handle(pattern, handler) |
| } else { |
| http_DefaultServeMux.register(pattern, handler) |
| } |
| } |
| |
| // HandleFunc registers the handler function for the given pattern in [DefaultServeMux]. |
| // The documentation for [ServeMux] explains how patterns are matched. |
| func http_HandleFunc(pattern string, handler func(http_ResponseWriter, *http_Request)) { |
| if http_use121 { |
| http_DefaultServeMux.mux121.handleFunc(pattern, handler) |
| } else { |
| http_DefaultServeMux.register(pattern, http_HandlerFunc(handler)) |
| } |
| } |
| |
| func (mux *http_ServeMux) register(pattern string, handler http_Handler) { |
| if err := mux.registerErr(pattern, handler); err != nil { |
| panic(err) |
| } |
| } |
| |
| func (mux *http_ServeMux) registerErr(patstr string, handler http_Handler) error { |
| if patstr == "" { |
| return errors.New("http: invalid pattern") |
| } |
| if handler == nil { |
| return errors.New("http: nil handler") |
| } |
| if f, ok := handler.(http_HandlerFunc); ok && f == nil { |
| return errors.New("http: nil handler") |
| } |
| |
| pat, err := http_parsePattern(patstr) |
| if err != nil { |
| return fmt.Errorf("parsing %q: %w", patstr, err) |
| } |
| |
| // Get the caller's location, for better conflict error messages. |
| // Skip register and whatever calls it. |
| _, file, line, ok := runtime.Caller(3) |
| if !ok { |
| pat.loc = "unknown location" |
| } else { |
| pat.loc = fmt.Sprintf("%s:%d", file, line) |
| } |
| |
| mux.mu.Lock() |
| defer mux.mu.Unlock() |
| // Check for conflict. |
| if err := mux.index.possiblyConflictingPatterns(pat, func(pat2 *http_pattern) error { |
| if pat.conflictsWith(pat2) { |
| d := http_describeConflict(pat, pat2) |
| return fmt.Errorf("pattern %q (registered at %s) conflicts with pattern %q (registered at %s):\n%s", |
| pat, pat.loc, pat2, pat2.loc, d) |
| } |
| return nil |
| }); err != nil { |
| return err |
| } |
| mux.tree.addPattern(pat, handler) |
| mux.index.addPattern(pat) |
| return nil |
| } |
| |
| // Serve accepts incoming HTTP connections on the listener l, |
| // creating a new service goroutine for each. The service goroutines |
| // read requests and then call handler to reply to them. |
| // |
| // The handler is typically nil, in which case [DefaultServeMux] is used. |
| // |
| // HTTP/2 support is only enabled if the Listener returns [*tls.Conn] |
| // connections or connections which implement the same ConnectionState |
| // method as *tls.Conn, and the connection state indicates that the "h2" |
| // protocol was negotiated by ALPN. |
| // |
| // Serve always returns a non-nil error. |
| func http_Serve(l net.Listener, handler http_Handler) error { |
| srv := &http_Server{Handler: handler} |
| return srv.Serve(l) |
| } |
| |
| // ServeTLS accepts incoming HTTPS connections on the listener l, |
| // creating a new service goroutine for each. The service goroutines |
| // read requests and then call handler to reply to them. |
| // |
| // The handler is typically nil, in which case [DefaultServeMux] is used. |
| // |
| // Additionally, files containing a certificate and matching private key |
| // for the server must be provided. If the certificate is signed by a |
| // certificate authority, the certFile should be the concatenation |
| // of the server's certificate, any intermediates, and the CA's certificate. |
| // |
| // ServeTLS always returns a non-nil error. |
| func http_ServeTLS(l net.Listener, handler http_Handler, certFile, keyFile string) error { |
| srv := &http_Server{Handler: handler} |
| return srv.ServeTLS(l, certFile, keyFile) |
| } |
| |
| // A Server defines parameters for running an HTTP server. |
| // The zero value for Server is a valid configuration. |
| type http_Server struct { |
| // Addr optionally specifies the TCP address for the server to listen on, |
| // in the form "host:port". If empty, ":http" (port 80) is used. |
| // The service names are defined in RFC 6335 and assigned by IANA. |
| // See net.Dial for details of the address format. |
| Addr string |
| |
| Handler http_Handler // handler to invoke, http.DefaultServeMux if nil |
| |
| // DisableGeneralOptionsHandler, if true, passes "OPTIONS *" requests to the Handler, |
| // otherwise responds with 200 OK and Content-Length: 0. |
| DisableGeneralOptionsHandler bool |
| |
| // TLSConfig optionally provides a TLS configuration for use |
| // by ServeTLS and ListenAndServeTLS. Note that this value is |
| // cloned by ServeTLS and ListenAndServeTLS, so it's not |
| // possible to modify the configuration with methods like |
| // tls.Config.SetSessionTicketKeys. To use |
| // SetSessionTicketKeys, use Server.Serve with a TLS Listener |
| // instead. |
| TLSConfig *tls.Config |
| |
| // ReadTimeout is the maximum duration for reading the entire |
| // request, including the body. A zero or negative value means |
| // there will be no timeout. |
| // |
| // Because ReadTimeout does not let Handlers make per-request |
| // decisions on each request body's acceptable deadline or |
| // upload rate, most users will prefer to use |
| // ReadHeaderTimeout. It is valid to use them both. |
| ReadTimeout time.Duration |
| |
| // ReadHeaderTimeout is the amount of time allowed to read |
| // request headers. The connection's read deadline is reset |
| // after reading the headers and the Handler can decide what |
| // is considered too slow for the body. If zero, the value of |
| // ReadTimeout is used. If negative, or if zero and ReadTimeout |
| // is zero or negative, there is no timeout. |
| ReadHeaderTimeout time.Duration |
| |
| // WriteTimeout is the maximum duration before timing out |
| // writes of the response. It is reset whenever a new |
| // request's header is read. Like ReadTimeout, it does not |
| // let Handlers make decisions on a per-request basis. |
| // A zero or negative value means there will be no timeout. |
| WriteTimeout time.Duration |
| |
| // IdleTimeout is the maximum amount of time to wait for the |
| // next request when keep-alives are enabled. If zero, the value |
| // of ReadTimeout is used. If negative, or if zero and ReadTimeout |
| // is zero or negative, there is no timeout. |
| IdleTimeout time.Duration |
| |
| // MaxHeaderBytes controls the maximum number of bytes the |
| // server will read parsing the request header's keys and |
| // values, including the request line. It does not limit the |
| // size of the request body. |
| // If zero, DefaultMaxHeaderBytes is used. |
| MaxHeaderBytes int |
| |
| // MaxHeaderValueCount controls the maximum number of header |
| // values that the server is willing to parse from a request. |
| // If zero, DefaultMaxHeaderValueCount is used. |
| // Note that comma-separated values in a single header line are |
| // counted once, while values sent as multiple header lines are |
| // counted multiple times. |
| MaxHeaderValueCount int |
| |
| // TLSNextProto optionally specifies a function to take over |
| // ownership of the provided TLS connection when an ALPN |
| // protocol upgrade has occurred. The map key is the protocol |
| // name negotiated. The Handler argument should be used to |
| // handle HTTP requests and will initialize the Request's TLS |
| // and RemoteAddr if not already set. The connection is |
| // automatically closed when the function returns. |
| // If TLSNextProto is not nil, HTTP/2 support is not enabled |
| // automatically. |
| // |
| // Historically, TLSNextProto was used to disable HTTP/2 support. |
| // The Server.Protocols field now provides a simpler way to do this. |
| TLSNextProto map[string]func(*http_Server, *tls.Conn, http_Handler) |
| |
| // ConnState specifies an optional callback function that is |
| // called when a client connection changes state. See the |
| // ConnState type and associated constants for details. |
| ConnState func(net.Conn, http_ConnState) |
| |
| // ErrorLog specifies an optional logger for errors accepting |
| // connections, unexpected behavior from handlers, and |
| // underlying FileSystem errors. |
| // If nil, logging is done via the log package's standard logger. |
| ErrorLog *log.Logger |
| |
| // BaseContext optionally specifies a function that returns |
| // the base context for incoming requests on this server. |
| // The provided Listener is the specific Listener that's |
| // about to start accepting requests. |
| // If BaseContext is nil, the default is context.Background(). |
| // If non-nil, it must return a non-nil context. |
| BaseContext func(net.Listener) context.Context |
| |
| // ConnContext optionally specifies a function that modifies |
| // the context used for a new connection c. The provided ctx |
| // is derived from the base context and has a ServerContextKey |
| // value. |
| ConnContext func(ctx context.Context, c net.Conn) context.Context |
| |
| // HTTP2 configures HTTP/2 connections. |
| HTTP2 *http_HTTP2Config |
| |
| // Protocols is the set of protocols accepted by the server. |
| // |
| // If Protocols includes UnencryptedHTTP2, the server will accept |
| // unencrypted HTTP/2 connections. The server can serve both |
| // HTTP/1 and unencrypted HTTP/2 on the same address and port. |
| // |
| // If Protocols is nil, the default is usually HTTP/1 and HTTP/2. |
| // If TLSNextProto is non-nil and does not contain an "h2" entry, |
| // the default is HTTP/1 only. |
| Protocols *http_Protocols |
| |
| // DisableClientPriority specifies whether client-specified priority, as |
| // specified in RFC 9218, should be respected or not. |
| // |
| // This field only takes effect if using HTTP/2, and if no custom write |
| // scheduler is defined for the HTTP/2 server. Otherwise, this field is a |
| // no-op. |
| // |
| // If set to true, requests will be served in a round-robin manner, without |
| // prioritization. |
| DisableClientPriority bool |
| |
| inShutdown atomic.Bool // true when server is in shutdown |
| |
| disableKeepAlives atomic.Bool |
| nextProtoOnce sync.Once // guards setupHTTP2_* init |
| nextProtoErr error // result of http2.ConfigureServer if used |
| |
| mu sync.Mutex |
| listeners map[*net.Listener]struct{} |
| activeConn map[*http_conn]struct{} |
| onShutdown []func() |
| h2 *http_http2Server |
| h2Config http_http2ExternalServerConfig |
| h2IdleTimeout time.Duration |
| h3 *http_http3ServerHandler |
| |
| listenerGroup sync.WaitGroup |
| } |
| |
| // Close immediately closes all active net.Listeners and any |
| // connections in state [StateNew], [StateActive], or [StateIdle]. For a |
| // graceful shutdown, use [Server.Shutdown]. |
| // |
| // Close does not attempt to close (and does not even know about) |
| // any hijacked connections, such as WebSockets. |
| // |
| // Close returns any error returned from closing the [Server]'s |
| // underlying Listener(s). |
| func (s *http_Server) Close() error { |
| s.inShutdown.Store(true) |
| s.mu.Lock() |
| defer s.mu.Unlock() |
| err := s.closeListenersLocked() |
| |
| // Unlock s.mu while waiting for listenerGroup. |
| // The group Add and Done calls are made with s.mu held, |
| // to avoid adding a new listener in the window between |
| // us setting inShutdown above and waiting here. |
| s.mu.Unlock() |
| s.listenerGroup.Wait() |
| s.mu.Lock() |
| |
| for c := range s.activeConn { |
| c.rwc.Close() |
| delete(s.activeConn, c) |
| } |
| return err |
| } |
| |
| // shutdownPollIntervalMax is the max polling interval when checking |
| // quiescence during Server.Shutdown. Polling starts with a small |
| // interval and backs off to the max. |
| // Ideally we could find a solution that doesn't involve polling, |
| // but which also doesn't have a high runtime cost (and doesn't |
| // involve any contentious mutexes), but that is left as an |
| // exercise for the reader. |
| const http_shutdownPollIntervalMax = 500 * time.Millisecond |
| |
| // Shutdown gracefully shuts down the server without interrupting any |
| // active connections. Shutdown works by first closing all open |
| // listeners, then closing all idle connections, and then waiting |
| // indefinitely for connections to return to idle and then shut down. |
| // If the provided context expires before the shutdown is complete, |
| // Shutdown returns the context's error, otherwise it returns any |
| // error returned from closing the [Server]'s underlying Listener(s). |
| // |
| // When Shutdown is called, [Serve], [ServeTLS], [ListenAndServe], and |
| // [ListenAndServeTLS] immediately return [ErrServerClosed]. Make sure the |
| // program doesn't exit and waits instead for Shutdown to return. |
| // |
| // Shutdown does not attempt to close nor wait for hijacked |
| // connections such as WebSockets. The caller of Shutdown should |
| // separately notify such long-lived connections of shutdown and wait |
| // for them to close, if desired. See [Server.RegisterOnShutdown] for a way to |
| // register shutdown notification functions. |
| // |
| // Once Shutdown has been called on a server, it may not be reused; |
| // future calls to methods such as Serve will return ErrServerClosed. |
| func (s *http_Server) Shutdown(ctx context.Context) error { |
| s.inShutdown.Store(true) |
| |
| s.mu.Lock() |
| if s.h3 != nil { |
| s.h3.shutdownCtx = ctx |
| } |
| lnerr := s.closeListenersLocked() |
| for _, f := range s.onShutdown { |
| go f() |
| } |
| s.mu.Unlock() |
| s.listenerGroup.Wait() |
| |
| pollIntervalBase := time.Millisecond |
| nextPollInterval := func() time.Duration { |
| // Add 10% jitter. |
| interval := pollIntervalBase + time.Duration(rand.IntN(int(pollIntervalBase/10))) |
| // Double and clamp for next time. |
| pollIntervalBase *= 2 |
| if pollIntervalBase > http_shutdownPollIntervalMax { |
| pollIntervalBase = http_shutdownPollIntervalMax |
| } |
| return interval |
| } |
| |
| timer := time.NewTimer(nextPollInterval()) |
| defer timer.Stop() |
| for { |
| if s.closeIdleConns() { |
| return lnerr |
| } |
| select { |
| case <-ctx.Done(): |
| return ctx.Err() |
| case <-timer.C: |
| timer.Reset(nextPollInterval()) |
| } |
| } |
| } |
| |
| // RegisterOnShutdown registers a function to call on [Server.Shutdown]. |
| // This can be used to gracefully shutdown connections that have |
| // undergone ALPN protocol upgrade or that have been hijacked. |
| // This function should start protocol-specific graceful shutdown, |
| // but should not wait for shutdown to complete. |
| func (s *http_Server) RegisterOnShutdown(f func()) { |
| s.mu.Lock() |
| s.onShutdown = append(s.onShutdown, f) |
| s.mu.Unlock() |
| } |
| |
| // closeIdleConns closes all idle connections and reports whether the |
| // server is quiescent. |
| func (s *http_Server) closeIdleConns() bool { |
| s.mu.Lock() |
| defer s.mu.Unlock() |
| quiescent := true |
| for c := range s.activeConn { |
| st, unixSec := c.getState() |
| // Issue 22682: treat StateNew connections as if |
| // they're idle if we haven't read the first request's |
| // header in over 5 seconds. |
| if st == http_StateNew && unixSec < time.Now().Unix()-5 { |
| st = http_StateIdle |
| } |
| if st != http_StateIdle || unixSec == 0 { |
| // Assume unixSec == 0 means it's a very new |
| // connection, without state set yet. |
| quiescent = false |
| continue |
| } |
| c.rwc.Close() |
| delete(s.activeConn, c) |
| } |
| return quiescent |
| } |
| |
| func (s *http_Server) closeListenersLocked() error { |
| var err error |
| for ln := range s.listeners { |
| if cerr := (*ln).Close(); cerr != nil && err == nil { |
| err = cerr |
| } |
| } |
| return err |
| } |
| |
| // A ConnState represents the state of a client connection to a server. |
| // It's used by the optional [Server.ConnState] hook. |
| type http_ConnState int |
| |
| const ( |
| // StateNew represents a new connection that is expected to |
| // send a request immediately. Connections begin at this |
| // state and then transition to either StateActive or |
| // StateClosed. |
| http_StateNew http_ConnState = iota |
| |
| // StateActive represents a connection that has read 1 or more |
| // bytes of a request. The Server.ConnState hook for |
| // StateActive fires before the request has entered a handler |
| // and doesn't fire again until the request has been |
| // handled. After the request is handled, the state |
| // transitions to StateClosed, StateHijacked, or StateIdle. |
| // For HTTP/2, StateActive fires on the transition from zero |
| // to one active request, and only transitions away once all |
| // active requests are complete. That means that ConnState |
| // cannot be used to do per-request work; ConnState only notes |
| // the overall state of the connection. |
| http_StateActive |
| |
| // StateIdle represents a connection that has finished |
| // handling a request and is in the keep-alive state, waiting |
| // for a new request. Connections transition from StateIdle |
| // to either StateActive or StateClosed. |
| http_StateIdle |
| |
| // StateHijacked represents a hijacked connection. |
| // This is a terminal state. It does not transition to StateClosed. |
| http_StateHijacked |
| |
| // StateClosed represents a closed connection. |
| // This is a terminal state. Hijacked connections do not |
| // transition to StateClosed. |
| http_StateClosed |
| ) |
| |
| var http_stateName = map[http_ConnState]string{ |
| http_StateNew: "new", |
| http_StateActive: "active", |
| http_StateIdle: "idle", |
| http_StateHijacked: "hijacked", |
| http_StateClosed: "closed", |
| } |
| |
| func (c http_ConnState) String() string { |
| return http_stateName[c] |
| } |
| |
| // serverHandler delegates to either the server's Handler or |
| // DefaultServeMux and also handles "OPTIONS *" requests. |
| type http_serverHandler struct { |
| srv *http_Server |
| } |
| |
| // ServeHTTP should be an internal detail, |
| // but widely used packages access it using linkname. |
| // Notable members of the hall of shame include: |
| // - github.com/erda-project/erda-infra |
| // |
| // Do not remove or change the type signature. |
| // See go.dev/issue/67401. |
| // |
| //go:linkname badServeHTTP net/http.serverHandler.ServeHTTP |
| func (sh http_serverHandler) ServeHTTP(rw http_ResponseWriter, req *http_Request) { |
| handler := sh.srv.Handler |
| if handler == nil { |
| handler = http_DefaultServeMux |
| } |
| if !sh.srv.DisableGeneralOptionsHandler && req.RequestURI == "*" && req.Method == "OPTIONS" { |
| handler = http_globalOptionsHandler{} |
| } |
| |
| handler.ServeHTTP(rw, req) |
| } |
| |
| func http_badServeHTTP(http_serverHandler, http_ResponseWriter, *http_Request) |
| |
| // AllowQuerySemicolons returns a handler that serves requests by converting any |
| // unescaped semicolons in the URL query to ampersands, and invoking the handler h. |
| // |
| // This restores the pre-Go 1.17 behavior of splitting query parameters on both |
| // semicolons and ampersands. (See golang.org/issue/25192). Note that this |
| // behavior doesn't match that of many proxies, and the mismatch can lead to |
| // security issues. |
| // |
| // AllowQuerySemicolons should be invoked before [Request.ParseForm] is called. |
| func http_AllowQuerySemicolons(h http_Handler) http_Handler { |
| return http_HandlerFunc(func(w http_ResponseWriter, r *http_Request) { |
| if strings.Contains(r.URL.RawQuery, ";") { |
| r2 := new(http_Request) |
| *r2 = *r |
| r2.URL = new(url.URL) |
| *r2.URL = *r.URL |
| r2.URL.RawQuery = strings.ReplaceAll(r.URL.RawQuery, ";", "&") |
| h.ServeHTTP(w, r2) |
| } else { |
| h.ServeHTTP(w, r) |
| } |
| }) |
| } |
| |
| // ListenAndServe listens on the TCP network address s.Addr and then |
| // calls [Serve] to handle requests on incoming connections. |
| // Accepted connections are configured to enable TCP keep-alives. |
| // |
| // If s.Addr is blank, ":http" is used. |
| // |
| // ListenAndServe always returns a non-nil error. After [Server.Shutdown] or [Server.Close], |
| // the returned error is [ErrServerClosed]. |
| func (s *http_Server) ListenAndServe() error { |
| if s.shuttingDown() { |
| return http_ErrServerClosed |
| } |
| addr := s.Addr |
| if addr == "" { |
| addr = ":http" |
| } |
| ln, err := net.Listen("tcp", addr) |
| if err != nil { |
| return err |
| } |
| return s.Serve(ln) |
| } |
| |
| var http_testHookServerServe func(*http_Server, net.Listener) // used if non-nil |
| |
| // shouldConfigureHTTP2ForServe reports whether Server.Serve should configure |
| // automatic HTTP/2. (which sets up the s.TLSNextProto map) |
| func (s *http_Server) shouldConfigureHTTP2ForServe() bool { |
| if s.TLSConfig == nil { |
| // Compatibility with Go 1.6: |
| // If there's no TLSConfig, it's possible that the user just |
| // didn't set it on the http.Server, but did pass it to |
| // tls.NewListener and passed that listener to Serve. |
| // So we should configure HTTP/2 (to set up s.TLSNextProto) |
| // in case the listener returns an "h2" *tls.Conn. |
| return true |
| } |
| if s.protocols().UnencryptedHTTP2() { |
| return true |
| } |
| // The user specified a TLSConfig on their http.Server. |
| // In this, case, only configure HTTP/2 if their tls.Config |
| // explicitly mentions "h2". Otherwise http2.ConfigureServer |
| // would modify the tls.Config to add it, but they probably already |
| // passed this tls.Config to tls.NewListener. And if they did, |
| // it's too late anyway to fix it. It would only be potentially racy. |
| // See Issue 15908. |
| return slices.Contains(s.TLSConfig.NextProtos, "h2") |
| } |
| |
| // ErrServerClosed is returned by the [Server.Serve], [ServeTLS], [ListenAndServe], |
| // and [ListenAndServeTLS] methods after a call to [Server.Shutdown] or [Server.Close]. |
| var http_ErrServerClosed = errors.New("http: Server closed") |
| |
| // Serve accepts incoming connections on the Listener l, creating a |
| // new service goroutine for each. The service goroutines read requests and |
| // then call s.Handler to reply to them. |
| // |
| // HTTP/2 support is only enabled if the Listener returns [*tls.Conn] |
| // connections and they were configured with "h2" in the TLS |
| // Config.NextProtos. |
| // |
| // Serve always returns a non-nil error and closes l. |
| // After [Server.Shutdown] or [Server.Close], the returned error is [ErrServerClosed]. |
| func (s *http_Server) Serve(l net.Listener) error { |
| if conf, ok := l.(http_http2ExternalServerConfig); ok { |
| // This is the sneaky path we use to let x/net/http2 wrap an http.Server: |
| // http2.ConfigureServer calls http.Server.Serve with a net.Listener that |
| // implements a certain interface, which we recognize here as an attempt |
| // to associate an http2.Server with us. |
| // |
| // (This is about as principled as the way we (ab)use Transport.RegisterProtocol, |
| // which is to say not at all. It's worth it.) |
| s.setHTTP2Config(conf) |
| // Server.Serve never returns a nil error under normal circumstances. |
| // Returning nil here informs our caller that we support this sneaky |
| // registration mechanism. |
| return nil |
| } |
| |
| if fn := http_testHookServerServe; fn != nil { |
| fn(s, l) // call hook with unwrapped listener |
| } |
| |
| origListener := l |
| l = &http_onceCloseListener{Listener: l} |
| defer l.Close() |
| |
| if err := s.setupHTTP2_Serve(); err != nil { |
| return err |
| } |
| |
| if !s.trackListener(&l, true) { |
| return http_ErrServerClosed |
| } |
| defer s.trackListener(&l, false) |
| |
| baseCtx := context.Background() |
| if s.BaseContext != nil { |
| baseCtx = s.BaseContext(origListener) |
| if baseCtx == nil { |
| panic("BaseContext returned a nil context") |
| } |
| } |
| |
| var tempDelay time.Duration // how long to sleep on accept failure |
| |
| ctx := context.WithValue(baseCtx, http_ServerContextKey, s) |
| for { |
| rw, err := l.Accept() |
| if err != nil { |
| if s.shuttingDown() { |
| return http_ErrServerClosed |
| } |
| if ne, ok := err.(net.Error); ok && ne.Temporary() { |
| if tempDelay == 0 { |
| tempDelay = 5 * time.Millisecond |
| } else { |
| tempDelay *= 2 |
| } |
| if max := 1 * time.Second; tempDelay > max { |
| tempDelay = max |
| } |
| s.logf("http: Accept error: %v; retrying in %v", err, tempDelay) |
| time.Sleep(tempDelay) |
| continue |
| } |
| return err |
| } |
| connCtx := ctx |
| if cc := s.ConnContext; cc != nil { |
| connCtx = cc(connCtx, rw) |
| if connCtx == nil { |
| panic("ConnContext returned nil") |
| } |
| } |
| tempDelay = 0 |
| c := s.newConn(rw) |
| c.setState(c.rwc, http_StateNew, http_runHooks) // before Serve can return |
| go c.serve(connCtx) |
| } |
| } |
| |
| func (s *http_Server) setupTLSConfig(certFile, keyFile string, nextProtos []string) (*tls.Config, error) { |
| config := http_cloneTLSConfig(s.TLSConfig) |
| config.NextProtos = nextProtos |
| |
| configHasCert := len(config.Certificates) > 0 || config.GetCertificate != nil || config.GetConfigForClient != nil |
| if !configHasCert || certFile != "" || keyFile != "" { |
| var err error |
| config.Certificates = make([]tls.Certificate, 1) |
| config.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile) |
| if err != nil { |
| return nil, err |
| } |
| } |
| return config, nil |
| } |
| |
| // ServeTLS accepts incoming connections on the Listener l, creating a |
| // new service goroutine for each. The service goroutines perform TLS |
| // setup and then read requests, calling s.Handler to reply to them. |
| // |
| // Files containing a certificate and matching private key for the |
| // server must be provided if neither the [Server]'s |
| // TLSConfig.Certificates, TLSConfig.GetCertificate nor |
| // config.GetConfigForClient are populated. |
| // If the certificate is signed by a certificate authority, the |
| // certFile should be the concatenation of the server's certificate, |
| // any intermediates, and the CA's certificate. |
| // |
| // ServeTLS always returns a non-nil error. After [Server.Shutdown] or [Server.Close], the |
| // returned error is [ErrServerClosed]. |
| func (s *http_Server) ServeTLS(l net.Listener, certFile, keyFile string) error { |
| // Setup HTTP/2 before s.Serve, to initialize s.TLSConfig |
| // before we clone it and create the TLS Listener. |
| if err := s.setupHTTP2_ServeTLS(); err != nil { |
| return err |
| } |
| |
| var nextProtos []string |
| if s.TLSConfig != nil { |
| nextProtos = s.TLSConfig.NextProtos |
| } |
| config, err := s.setupTLSConfig(certFile, keyFile, http_adjustNextProtos(nextProtos, s.protocols())) |
| if err != nil { |
| return err |
| } |
| |
| tlsListener := tls.NewListener(l, config) |
| return s.Serve(tlsListener) |
| } |
| |
| func (s *http_Server) protocols() http_Protocols { |
| if s.Protocols != nil { |
| // Historically, even when Protocols for a Server was set to be empty, |
| // the Server can still run normally with just HTTP/1. |
| // To keep backward-compatibility, the zero value of Protocols is |
| // defined as having only HTTP/1 enabled. |
| if s.Protocols.empty() { |
| var p http_Protocols |
| p.SetHTTP1(true) |
| return p |
| } |
| return *s.Protocols // user-configured set |
| } |
| |
| // The historic way of disabling HTTP/2 is to set TLSNextProto to |
| // a non-nil map with no "h2" entry. |
| _, hasH2 := s.TLSNextProto["h2"] |
| http2Disabled := s.TLSNextProto != nil && !hasH2 |
| |
| // If GODEBUG=http2server=0, then HTTP/2 is disabled unless |
| // the user has manually added an "h2" entry to TLSNextProto |
| // (probably by using x/net/http2 directly). |
| if http_http2server.Value() == "0" && !hasH2 { |
| http2Disabled = true |
| } |
| |
| var p http_Protocols |
| p.SetHTTP1(true) // default always includes HTTP/1 |
| if !http2Disabled { |
| p.SetHTTP2(true) |
| } |
| return p |
| } |
| |
| // adjustNextProtos adds or removes "http/1.1" and "h2" entries from |
| // a tls.Config.NextProtos list, according to the set of protocols in protos. |
| func http_adjustNextProtos(nextProtos []string, protos http_Protocols) []string { |
| // Make a copy of NextProtos since it might be shared with some other tls.Config. |
| // (tls.Config.Clone doesn't do a deep copy.) |
| // |
| // We could avoid an allocation in the common case by checking to see if the slice |
| // is already in order, but this is just one small allocation per connection. |
| nextProtos = slices.Clone(nextProtos) |
| var have http_Protocols |
| nextProtos = slices.DeleteFunc(nextProtos, func(s string) bool { |
| switch s { |
| case "http/1.1": |
| if !protos.HTTP1() { |
| return true |
| } |
| have.SetHTTP1(true) |
| case "h2": |
| if !protos.HTTP2() { |
| return true |
| } |
| have.SetHTTP2(true) |
| } |
| return false |
| }) |
| if protos.HTTP2() && !have.HTTP2() { |
| nextProtos = append(nextProtos, "h2") |
| } |
| if protos.HTTP1() && !have.HTTP1() { |
| nextProtos = append(nextProtos, "http/1.1") |
| } |
| return nextProtos |
| } |
| |
| // trackListener adds or removes a net.Listener to the set of tracked |
| // listeners. |
| // |
| // We store a pointer to interface in the map set, in case the |
| // net.Listener is not comparable. This is safe because we only call |
| // trackListener via Serve and can track+defer untrack the same |
| // pointer to local variable there. We never need to compare a |
| // Listener from another caller. |
| // |
| // It reports whether the server is still up (not Shutdown or Closed). |
| func (s *http_Server) trackListener(ln *net.Listener, add bool) bool { |
| s.mu.Lock() |
| defer s.mu.Unlock() |
| if s.listeners == nil { |
| s.listeners = make(map[*net.Listener]struct{}) |
| } |
| if add { |
| if s.shuttingDown() { |
| return false |
| } |
| s.listeners[ln] = struct{}{} |
| s.listenerGroup.Add(1) |
| } else { |
| delete(s.listeners, ln) |
| s.listenerGroup.Done() |
| } |
| return true |
| } |
| |
| func (s *http_Server) trackConn(c *http_conn, add bool) { |
| s.mu.Lock() |
| defer s.mu.Unlock() |
| if s.activeConn == nil { |
| s.activeConn = make(map[*http_conn]struct{}) |
| } |
| if add { |
| s.activeConn[c] = struct{}{} |
| } else { |
| delete(s.activeConn, c) |
| } |
| } |
| |
| func (s *http_Server) idleTimeout() time.Duration { |
| if s.IdleTimeout != 0 { |
| return s.IdleTimeout |
| } |
| return s.ReadTimeout |
| } |
| |
| func (s *http_Server) readHeaderTimeout() time.Duration { |
| if s.ReadHeaderTimeout != 0 { |
| return s.ReadHeaderTimeout |
| } |
| return s.ReadTimeout |
| } |
| |
| func (s *http_Server) doKeepAlives() bool { |
| return !s.disableKeepAlives.Load() && !s.shuttingDown() |
| } |
| |
| func (s *http_Server) shuttingDown() bool { |
| return s.inShutdown.Load() |
| } |
| |
| // SetKeepAlivesEnabled controls whether HTTP keep-alives are enabled. |
| // By default, keep-alives are always enabled. Only very |
| // resource-constrained environments or servers in the process of |
| // shutting down should disable them. |
| func (s *http_Server) SetKeepAlivesEnabled(v bool) { |
| if v { |
| s.disableKeepAlives.Store(false) |
| return |
| } |
| s.disableKeepAlives.Store(true) |
| |
| // Close idle HTTP/1 conns: |
| s.closeIdleConns() |
| |
| // TODO: Issue 26303: close HTTP/2 conns as soon as they become idle. |
| } |
| |
| func (s *http_Server) logf(format string, args ...any) { |
| if s.ErrorLog != nil { |
| s.ErrorLog.Printf(format, args...) |
| } else { |
| log.Printf(format, args...) |
| } |
| } |
| |
| // logf prints to the ErrorLog of the *Server associated with request r |
| // via ServerContextKey. If there's no associated server, or if ErrorLog |
| // is nil, logging is done via the log package's standard logger. |
| func http_logf(r *http_Request, format string, args ...any) { |
| s, _ := r.Context().Value(http_ServerContextKey).(*http_Server) |
| if s != nil && s.ErrorLog != nil { |
| s.ErrorLog.Printf(format, args...) |
| } else { |
| log.Printf(format, args...) |
| } |
| } |
| |
| // ListenAndServe listens on the TCP network address addr and then calls |
| // [Serve] with handler to handle requests on incoming connections. |
| // Accepted connections are configured to enable TCP keep-alives. |
| // |
| // The handler is typically nil, in which case [DefaultServeMux] is used. |
| // |
| // ListenAndServe always returns a non-nil error. |
| func http_ListenAndServe(addr string, handler http_Handler) error { |
| server := &http_Server{Addr: addr, Handler: handler} |
| return server.ListenAndServe() |
| } |
| |
| // ListenAndServeTLS acts identically to [ListenAndServe], except that it |
| // expects HTTPS connections. Additionally, files containing a certificate and |
| // matching private key for the server must be provided. If the certificate |
| // is signed by a certificate authority, the certFile should be the concatenation |
| // of the server's certificate, any intermediates, and the CA's certificate. |
| func http_ListenAndServeTLS(addr, certFile, keyFile string, handler http_Handler) error { |
| server := &http_Server{Addr: addr, Handler: handler} |
| return server.ListenAndServeTLS(certFile, keyFile) |
| } |
| |
| // http3ServerHandler implements an interface in an external library that |
| // supports HTTP/3, allowing an external implementation of HTTP/3 to be used |
| // via net/http. See https://go.dev/issue/77440 for details. |
| // |
| // This is currently only used with golang.org/x/net/internal/http3, to allow |
| // us to test our HTTP/3 implementation against tests in net/http. HTTP/3 is |
| // not yet accessible to end-users. |
| type http_http3ServerHandler struct { |
| handler http_serverHandler |
| tlsConfig *tls.Config |
| baseCtx context.Context |
| errc chan error |
| shutdownCtx context.Context |
| } |
| |
| // ServeHTTP ensures that http3ServerHandler implements the Handler interface, |
| // and gives an HTTP/3 server implementation access to the net/http handler. |
| func (h *http_http3ServerHandler) ServeHTTP(w http_ResponseWriter, r *http_Request) { |
| h.handler.ServeHTTP(w, r) |
| } |
| |
| // Addr gives an HTTP/3 server implementation the address that it should listen |
| // on. |
| func (h *http_http3ServerHandler) Addr() string { |
| return h.handler.srv.Addr |
| } |
| |
| // TLSConfig gives an HTTP/3 server implementation the *tls.Config that it |
| // should use. |
| func (h *http_http3ServerHandler) TLSConfig() *tls.Config { |
| return h.tlsConfig |
| } |
| |
| // BaseContext gives an HTTP/3 server implementation the base context to use |
| // for server requests. |
| func (h *http_http3ServerHandler) BaseContext() context.Context { |
| return h.baseCtx |
| } |
| |
| // ListenErrHook should be called by an HTTP/3 server implementation to |
| // propagate any error it encounters when trying to listen, if any, to |
| // net/http. |
| func (h *http_http3ServerHandler) ListenErrHook(err error) { |
| h.errc <- err |
| } |
| |
| // ShutdownContext gives an HTTP/3 server implementation the context that is |
| // used when [Server.Shutdown] is called. This allows an HTTP/3 server |
| // implementation to know how long it can take to gracefully shutdown in the |
| // function it registers with [Server.RegisterOnShutdown]. Callers must not use |
| // this method for any other purpose. |
| func (h *http_http3ServerHandler) ShutdownContext() context.Context { |
| return h.shutdownCtx |
| } |
| |
| // ListenAndServeTLS listens on the TCP network address s.Addr and |
| // then calls [ServeTLS] to handle requests on incoming TLS connections. |
| // Accepted connections are configured to enable TCP keep-alives. |
| // |
| // Filenames containing a certificate and matching private key for the |
| // server must be provided if neither the [Server]'s TLSConfig.Certificates |
| // nor TLSConfig.GetCertificate are populated. If the certificate is |
| // signed by a certificate authority, the certFile should be the |
| // concatenation of the server's certificate, any intermediates, and |
| // the CA's certificate. |
| // |
| // If s.Addr is blank, ":https" is used. |
| // |
| // ListenAndServeTLS always returns a non-nil error. After [Server.Shutdown] or |
| // [Server.Close], the returned error is [ErrServerClosed]. |
| func (s *http_Server) ListenAndServeTLS(certFile, keyFile string) error { |
| if s.shuttingDown() { |
| return http_ErrServerClosed |
| } |
| addr := s.Addr |
| if addr == "" { |
| addr = ":https" |
| } |
| |
| p := s.protocols() |
| if p.http3() { |
| fn, ok := s.TLSNextProto["http/3"] |
| if !ok { |
| return errors.New("http: Server.Protocols contains HTTP3, but Server does not support HTTP/3") |
| } |
| config, err := s.setupTLSConfig(certFile, keyFile, []string{"h3"}) |
| if err != nil { |
| return err |
| } |
| errc := make(chan error, 1) |
| s.mu.Lock() |
| s.h3 = &http_http3ServerHandler{ |
| handler: http_serverHandler{s}, |
| tlsConfig: config, |
| baseCtx: context.WithValue(context.Background(), http_ServerContextKey, s), |
| errc: errc, |
| } |
| s.mu.Unlock() |
| go fn(s, nil, s.h3) |
| if err := <-errc; err != nil { |
| return err |
| } |
| } |
| |
| // Only start a TCP listener if HTTP/1 or HTTP/2 is used. |
| if !p.HTTP1() && !p.HTTP2() && !p.UnencryptedHTTP2() { |
| return nil |
| } |
| ln, err := net.Listen("tcp", addr) |
| if err != nil { |
| return err |
| } |
| defer ln.Close() |
| return s.ServeTLS(ln, certFile, keyFile) |
| } |
| |
| // setupHTTP2_ServeTLS conditionally configures HTTP/2 on |
| // s and reports whether there was an error setting it up. If it is |
| // not configured for policy reasons, nil is returned. |
| func (s *http_Server) setupHTTP2_ServeTLS() error { |
| s.nextProtoOnce.Do(s.onceSetNextProtoDefaults) |
| return s.nextProtoErr |
| } |
| |
| // setupHTTP2_Serve is called from (*Server).Serve and conditionally |
| // configures HTTP/2 on s using a more conservative policy than |
| // setupHTTP2_ServeTLS because Serve is called after tls.Listen, |
| // and may be called concurrently. See shouldConfigureHTTP2ForServe. |
| // |
| // The tests named TestTransportAutomaticHTTP2* and |
| // TestConcurrentServerServe in server_test.go demonstrate some |
| // of the supported use cases and motivations. |
| func (s *http_Server) setupHTTP2_Serve() error { |
| s.nextProtoOnce.Do(s.onceSetNextProtoDefaults_Serve) |
| return s.nextProtoErr |
| } |
| |
| func (s *http_Server) onceSetNextProtoDefaults_Serve() { |
| if s.shouldConfigureHTTP2ForServe() { |
| s.onceSetNextProtoDefaults() |
| } |
| } |
| |
| var http_http2server = godebug.New("http2server") |
| |
| // onceSetNextProtoDefaults configures HTTP/2, if the user hasn't |
| // configured otherwise. (by setting s.TLSNextProto non-nil) |
| // It must only be called via s.nextProtoOnce (use s.setupHTTP2_*). |
| func (s *http_Server) onceSetNextProtoDefaults() { |
| if http_omitBundledHTTP2 { |
| return |
| } |
| p := s.protocols() |
| if !p.HTTP2() && !p.UnencryptedHTTP2() { |
| return |
| } |
| if http_http2server.Value() == "0" { |
| http_http2server.IncNonDefault() |
| return |
| } |
| if _, ok := s.TLSNextProto["h2"]; ok { |
| // TLSNextProto already contains an HTTP/2 implementation. |
| // The user probably called golang.org/x/net/http2.ConfigureServer |
| // to add it. |
| return |
| } |
| s.configureHTTP2() |
| } |
| |
| // TimeoutHandler returns a [Handler] that runs h with the given time limit. |
| // |
| // The new Handler calls h.ServeHTTP to handle each request, but if a |
| // call runs for longer than its time limit, the handler responds with |
| // a 503 Service Unavailable error and the given message in its body. |
| // (If msg is empty, a suitable default message will be sent.) |
| // After such a timeout, writes by h to its [ResponseWriter] will return |
| // [ErrHandlerTimeout]. |
| // |
| // TimeoutHandler supports the [Pusher] interface but does not support |
| // the [Hijacker] or [Flusher] interfaces. |
| func http_TimeoutHandler(h http_Handler, dt time.Duration, msg string) http_Handler { |
| return &http_timeoutHandler{ |
| handler: h, |
| body: msg, |
| dt: dt, |
| } |
| } |
| |
| // ErrHandlerTimeout is returned on [ResponseWriter] Write calls |
| // in handlers which have timed out. |
| var http_ErrHandlerTimeout = errors.New("http: Handler timeout") |
| |
| type http_timeoutHandler struct { |
| handler http_Handler |
| body string |
| dt time.Duration |
| |
| // When set, no context will be created and this context will |
| // be used instead. |
| testContext context.Context |
| } |
| |
| func (h *http_timeoutHandler) errorBody() string { |
| if h.body != "" { |
| return h.body |
| } |
| return "<html><head><title>Timeout</title></head><body><h1>Timeout</h1></body></html>" |
| } |
| |
| func (h *http_timeoutHandler) ServeHTTP(w http_ResponseWriter, r *http_Request) { |
| ctx := h.testContext |
| if ctx == nil { |
| var cancelCtx context.CancelFunc |
| ctx, cancelCtx = context.WithTimeout(r.Context(), h.dt) |
| defer cancelCtx() |
| } |
| r = r.WithContext(ctx) |
| done := make(chan struct{}) |
| tw := &http_timeoutWriter{ |
| w: w, |
| h: make(http_Header), |
| req: r, |
| } |
| panicChan := make(chan any, 1) |
| go func() { |
| defer func() { |
| if p := recover(); p != nil { |
| panicChan <- p |
| } |
| }() |
| h.handler.ServeHTTP(tw, r) |
| close(done) |
| }() |
| select { |
| case p := <-panicChan: |
| panic(p) |
| case <-done: |
| tw.mu.Lock() |
| defer tw.mu.Unlock() |
| dst := w.Header() |
| maps.Copy(dst, tw.h) |
| if !tw.wroteHeader { |
| tw.code = http_StatusOK |
| } |
| w.WriteHeader(tw.code) |
| w.Write(tw.wbuf.Bytes()) |
| case <-ctx.Done(): |
| tw.mu.Lock() |
| defer tw.mu.Unlock() |
| switch err := ctx.Err(); err { |
| case context.DeadlineExceeded: |
| w.WriteHeader(http_StatusServiceUnavailable) |
| io.WriteString(w, h.errorBody()) |
| tw.err = http_ErrHandlerTimeout |
| default: |
| w.WriteHeader(http_StatusServiceUnavailable) |
| tw.err = err |
| } |
| } |
| } |
| |
| type http_timeoutWriter struct { |
| w http_ResponseWriter |
| h http_Header |
| wbuf bytes.Buffer |
| req *http_Request |
| |
| mu sync.Mutex |
| err error |
| wroteHeader bool |
| code int |
| } |
| |
| var _ http_Pusher = (*http_timeoutWriter)(nil) |
| |
| // Push implements the [Pusher] interface. |
| func (tw *http_timeoutWriter) Push(target string, opts *http_PushOptions) error { |
| if pusher, ok := tw.w.(http_Pusher); ok { |
| return pusher.Push(target, opts) |
| } |
| return http_ErrNotSupported |
| } |
| |
| func (tw *http_timeoutWriter) Header() http_Header { return tw.h } |
| |
| func (tw *http_timeoutWriter) Write(p []byte) (int, error) { |
| tw.mu.Lock() |
| defer tw.mu.Unlock() |
| if tw.err != nil { |
| return 0, tw.err |
| } |
| if !tw.wroteHeader { |
| tw.writeHeaderLocked(http_StatusOK) |
| } |
| return tw.wbuf.Write(p) |
| } |
| |
| func (tw *http_timeoutWriter) writeHeaderLocked(code int) { |
| http_checkWriteHeaderCode(code) |
| |
| switch { |
| case tw.err != nil: |
| return |
| case tw.wroteHeader: |
| if tw.req != nil { |
| caller := http_relevantCaller() |
| http_logf(tw.req, "http: superfluous response.WriteHeader call from %s (%s:%d)", caller.Function, path.Base(caller.File), caller.Line) |
| } |
| default: |
| tw.wroteHeader = true |
| tw.code = code |
| } |
| } |
| |
| func (tw *http_timeoutWriter) WriteHeader(code int) { |
| tw.mu.Lock() |
| defer tw.mu.Unlock() |
| tw.writeHeaderLocked(code) |
| } |
| |
| // onceCloseListener wraps a net.Listener, protecting it from |
| // multiple Close calls. |
| type http_onceCloseListener struct { |
| net.Listener |
| once sync.Once |
| closeErr error |
| } |
| |
| func (oc *http_onceCloseListener) Close() error { |
| oc.once.Do(oc.close) |
| return oc.closeErr |
| } |
| |
| func (oc *http_onceCloseListener) close() { oc.closeErr = oc.Listener.Close() } |
| |
| // globalOptionsHandler responds to "OPTIONS *" requests. |
| type http_globalOptionsHandler struct{} |
| |
| func (http_globalOptionsHandler) ServeHTTP(w http_ResponseWriter, r *http_Request) { |
| w.Header().Set("Content-Length", "0") |
| if r.ContentLength != 0 { |
| // Read up to 4KB of OPTIONS body (as mentioned in the |
| // spec as being reserved for future use), but anything |
| // over that is considered a waste of server resources |
| // (or an attack) and we abort and close the connection, |
| // courtesy of MaxBytesReader's EOF behavior. |
| mb := http_MaxBytesReader(w, r.Body, 4<<10) |
| io.Copy(io.Discard, mb) |
| } |
| } |
| |
| // initALPNRequest is an HTTP handler that initializes certain |
| // uninitialized fields in its *Request. Such partially-initialized |
| // Requests come from ALPN protocol handlers. |
| type http_initALPNRequest struct { |
| ctx context.Context |
| c *tls.Conn |
| h http_serverHandler |
| } |
| |
| // BaseContext is an exported but unadvertised [http.Handler] method |
| // recognized by x/net/http2 to pass down a context; the TLSNextProto |
| // API predates context support so we shoehorn through the only |
| // interface we have available. |
| func (h http_initALPNRequest) BaseContext() context.Context { return h.ctx } |
| |
| func (h http_initALPNRequest) ServeHTTP(rw http_ResponseWriter, req *http_Request) { |
| if req.TLS == nil { |
| req.TLS = &tls.ConnectionState{} |
| *req.TLS = h.c.ConnectionState() |
| } |
| if req.Body == nil { |
| req.Body = http_NoBody |
| } |
| if req.RemoteAddr == "" { |
| req.RemoteAddr = h.c.RemoteAddr().String() |
| } |
| h.h.ServeHTTP(rw, req) |
| } |
| |
| // loggingConn is used for debugging. |
| type http_loggingConn struct { |
| name string |
| net.Conn |
| } |
| |
| var ( |
| http_uniqNameMu sync.Mutex |
| http_uniqNameNext = make(map[string]int) |
| ) |
| |
| func http_newLoggingConn(baseName string, c net.Conn) net.Conn { |
| http_uniqNameMu.Lock() |
| defer http_uniqNameMu.Unlock() |
| http_uniqNameNext[baseName]++ |
| return &http_loggingConn{ |
| name: fmt.Sprintf("%s-%d", baseName, http_uniqNameNext[baseName]), |
| Conn: c, |
| } |
| } |
| |
| func (c *http_loggingConn) Write(p []byte) (n int, err error) { |
| log.Printf("%s.Write(%d) = ....", c.name, len(p)) |
| n, err = c.Conn.Write(p) |
| log.Printf("%s.Write(%d) = %d, %v", c.name, len(p), n, err) |
| return |
| } |
| |
| func (c *http_loggingConn) Read(p []byte) (n int, err error) { |
| log.Printf("%s.Read(%d) = ....", c.name, len(p)) |
| n, err = c.Conn.Read(p) |
| log.Printf("%s.Read(%d) = %d, %v", c.name, len(p), n, err) |
| return |
| } |
| |
| func (c *http_loggingConn) Close() (err error) { |
| log.Printf("%s.Close() = ...", c.name) |
| err = c.Conn.Close() |
| log.Printf("%s.Close() = %v", c.name, err) |
| return |
| } |
| |
| // checkConnErrorWriter writes to c.rwc and records any write errors to c.werr. |
| // It only contains one field (and a pointer field at that), so it |
| // fits in an interface value without an extra allocation. |
| type http_checkConnErrorWriter struct { |
| c *http_conn |
| } |
| |
| func (w http_checkConnErrorWriter) Write(p []byte) (n int, err error) { |
| n, err = w.c.rwc.Write(p) |
| if err != nil && w.c.werr == nil { |
| w.c.werr = err |
| w.c.cancelCtx() |
| } |
| return |
| } |
| |
| func http_numLeadingCRorLF(v []byte) (n int) { |
| for _, b := range v { |
| if b == '\r' || b == '\n' { |
| n++ |
| continue |
| } |
| break |
| } |
| return |
| } |
| |
| // tlsRecordHeaderLooksLikeHTTP reports whether a TLS record header |
| // looks like it might've been a misdirected plaintext HTTP request. |
| func http_tlsRecordHeaderLooksLikeHTTP(hdr [5]byte) bool { |
| switch string(hdr[:]) { |
| case "GET /", "HEAD ", "POST ", "PUT /", "OPTIO": |
| return true |
| } |
| return false |
| } |
| |
| // MaxBytesHandler returns a [Handler] that runs h with its [ResponseWriter] and [Request.Body] wrapped by a MaxBytesReader. |
| func http_MaxBytesHandler(h http_Handler, n int64) http_Handler { |
| return http_HandlerFunc(func(w http_ResponseWriter, r *http_Request) { |
| r2 := *r |
| r2.Body = http_MaxBytesReader(w, r.Body, n) |
| h.ServeHTTP(w, &r2) |
| }) |
| } |
| |
| // DetectContentType implements the algorithm described |
| // at https://mimesniff.spec.whatwg.org/ to determine the |
| // Content-Type of the given data. It considers at most the |
| // first 512 bytes of data. DetectContentType always returns |
| // a valid MIME type: if it cannot determine a more specific one, it |
| // returns "application/octet-stream". |
| func http_DetectContentType(data []byte) string { |
| return internal.DetectContentType(data) |
| } |
| |
| var ( |
| http_socksnoDeadline = time.Time{} |
| http_socksaLongTimeAgo = time.Unix(1, 0) |
| ) |
| |
| func (d *http_socksDialer) connect(ctx context.Context, c net.Conn, address string) (_ net.Addr, ctxErr error) { |
| host, port, err := http_sockssplitHostPort(address) |
| if err != nil { |
| return nil, err |
| } |
| if deadline, ok := ctx.Deadline(); ok && !deadline.IsZero() { |
| c.SetDeadline(deadline) |
| defer c.SetDeadline(http_socksnoDeadline) |
| } |
| if ctx != context.Background() { |
| errCh := make(chan error, 1) |
| done := make(chan struct{}) |
| defer func() { |
| close(done) |
| if ctxErr == nil { |
| ctxErr = <-errCh |
| } |
| }() |
| go func() { |
| select { |
| case <-ctx.Done(): |
| c.SetDeadline(http_socksaLongTimeAgo) |
| errCh <- ctx.Err() |
| case <-done: |
| errCh <- nil |
| } |
| }() |
| } |
| |
| b := make([]byte, 0, 6+len(host)) // the size here is just an estimate |
| b = append(b, http_socksVersion5) |
| if len(d.AuthMethods) == 0 || d.Authenticate == nil { |
| b = append(b, 1, byte(http_socksAuthMethodNotRequired)) |
| } else { |
| ams := d.AuthMethods |
| if len(ams) > 255 { |
| return nil, errors.New("too many authentication methods") |
| } |
| b = append(b, byte(len(ams))) |
| for _, am := range ams { |
| b = append(b, byte(am)) |
| } |
| } |
| if _, ctxErr = c.Write(b); ctxErr != nil { |
| return |
| } |
| |
| if _, ctxErr = io.ReadFull(c, b[:2]); ctxErr != nil { |
| return |
| } |
| if b[0] != http_socksVersion5 { |
| return nil, errors.New("unexpected protocol version " + strconv.Itoa(int(b[0]))) |
| } |
| am := http_socksAuthMethod(b[1]) |
| if am == http_socksAuthMethodNoAcceptableMethods { |
| return nil, errors.New("no acceptable authentication methods") |
| } |
| if d.Authenticate != nil { |
| if ctxErr = d.Authenticate(ctx, c, am); ctxErr != nil { |
| return |
| } |
| } |
| |
| b = b[:0] |
| b = append(b, http_socksVersion5, byte(d.cmd), 0) |
| if ip := net.ParseIP(host); ip != nil { |
| if ip4 := ip.To4(); ip4 != nil { |
| b = append(b, http_socksAddrTypeIPv4) |
| b = append(b, ip4...) |
| } else if ip6 := ip.To16(); ip6 != nil { |
| b = append(b, http_socksAddrTypeIPv6) |
| b = append(b, ip6...) |
| } else { |
| return nil, errors.New("unknown address type") |
| } |
| } else { |
| if len(host) > 255 { |
| return nil, errors.New("FQDN too long") |
| } |
| b = append(b, http_socksAddrTypeFQDN) |
| b = append(b, byte(len(host))) |
| b = append(b, host...) |
| } |
| b = append(b, byte(port>>8), byte(port)) |
| if _, ctxErr = c.Write(b); ctxErr != nil { |
| return |
| } |
| |
| if _, ctxErr = io.ReadFull(c, b[:4]); ctxErr != nil { |
| return |
| } |
| if b[0] != http_socksVersion5 { |
| return nil, errors.New("unexpected protocol version " + strconv.Itoa(int(b[0]))) |
| } |
| if cmdErr := http_socksReply(b[1]); cmdErr != http_socksStatusSucceeded { |
| return nil, errors.New("unknown error " + cmdErr.String()) |
| } |
| if b[2] != 0 { |
| return nil, errors.New("non-zero reserved field") |
| } |
| l := 2 |
| var a http_socksAddr |
| switch b[3] { |
| case http_socksAddrTypeIPv4: |
| l += net.IPv4len |
| a.IP = make(net.IP, net.IPv4len) |
| case http_socksAddrTypeIPv6: |
| l += net.IPv6len |
| a.IP = make(net.IP, net.IPv6len) |
| case http_socksAddrTypeFQDN: |
| if _, err := io.ReadFull(c, b[:1]); err != nil { |
| return nil, err |
| } |
| l += int(b[0]) |
| default: |
| return nil, errors.New("unknown address type " + strconv.Itoa(int(b[3]))) |
| } |
| if cap(b) < l { |
| b = make([]byte, l) |
| } else { |
| b = b[:l] |
| } |
| if _, ctxErr = io.ReadFull(c, b); ctxErr != nil { |
| return |
| } |
| if a.IP != nil { |
| copy(a.IP, b) |
| } else { |
| a.Name = string(b[:len(b)-2]) |
| } |
| a.Port = int(b[len(b)-2])<<8 | int(b[len(b)-1]) |
| return &a, nil |
| } |
| |
| func http_sockssplitHostPort(address string) (string, int, error) { |
| host, port, err := net.SplitHostPort(address) |
| if err != nil { |
| return "", 0, err |
| } |
| portnum, err := strconv.Atoi(port) |
| if err != nil { |
| return "", 0, err |
| } |
| if 1 > portnum || portnum > 0xffff { |
| return "", 0, errors.New("port number out of range " + port) |
| } |
| return host, portnum, nil |
| } |
| |
| // A Command represents a SOCKS command. |
| type http_socksCommand int |
| |
| func (cmd http_socksCommand) String() string { |
| switch cmd { |
| case http_socksCmdConnect: |
| return "socks connect" |
| case http_sockscmdBind: |
| return "socks bind" |
| default: |
| return "socks " + strconv.Itoa(int(cmd)) |
| } |
| } |
| |
| // An AuthMethod represents a SOCKS authentication method. |
| type http_socksAuthMethod int |
| |
| // A Reply represents a SOCKS command reply code. |
| type http_socksReply int |
| |
| func (code http_socksReply) String() string { |
| switch code { |
| case http_socksStatusSucceeded: |
| return "succeeded" |
| case 0x01: |
| return "general SOCKS server failure" |
| case 0x02: |
| return "connection not allowed by ruleset" |
| case 0x03: |
| return "network unreachable" |
| case 0x04: |
| return "host unreachable" |
| case 0x05: |
| return "connection refused" |
| case 0x06: |
| return "TTL expired" |
| case 0x07: |
| return "command not supported" |
| case 0x08: |
| return "address type not supported" |
| default: |
| return "unknown code: " + strconv.Itoa(int(code)) |
| } |
| } |
| |
| // Wire protocol constants. |
| const ( |
| http_socksVersion5 = 0x05 |
| |
| http_socksAddrTypeIPv4 = 0x01 |
| http_socksAddrTypeFQDN = 0x03 |
| http_socksAddrTypeIPv6 = 0x04 |
| |
| http_socksCmdConnect http_socksCommand = 0x01 // establishes an active-open forward proxy connection |
| http_sockscmdBind http_socksCommand = 0x02 // establishes a passive-open forward proxy connection |
| |
| http_socksAuthMethodNotRequired http_socksAuthMethod = 0x00 // no authentication required |
| http_socksAuthMethodUsernamePassword http_socksAuthMethod = 0x02 // use username/password |
| http_socksAuthMethodNoAcceptableMethods http_socksAuthMethod = 0xff // no acceptable authentication methods |
| |
| http_socksStatusSucceeded http_socksReply = 0x00 |
| ) |
| |
| // An Addr represents a SOCKS-specific address. |
| // Either Name or IP is used exclusively. |
| type http_socksAddr struct { |
| Name string // fully-qualified domain name |
| IP net.IP |
| Port int |
| } |
| |
| func (a *http_socksAddr) Network() string { return "socks" } |
| |
| func (a *http_socksAddr) String() string { |
| if a == nil { |
| return "<nil>" |
| } |
| port := strconv.Itoa(a.Port) |
| if a.IP == nil { |
| return net.JoinHostPort(a.Name, port) |
| } |
| return net.JoinHostPort(a.IP.String(), port) |
| } |
| |
| // A Conn represents a forward proxy connection. |
| type http_socksConn struct { |
| net.Conn |
| |
| boundAddr net.Addr |
| } |
| |
| // BoundAddr returns the address assigned by the proxy server for |
| // connecting to the command target address from the proxy server. |
| func (c *http_socksConn) BoundAddr() net.Addr { |
| if c == nil { |
| return nil |
| } |
| return c.boundAddr |
| } |
| |
| // A Dialer holds SOCKS-specific options. |
| type http_socksDialer struct { |
| cmd http_socksCommand // either CmdConnect or cmdBind |
| proxyNetwork string // network between a proxy server and a client |
| proxyAddress string // proxy server address |
| |
| // ProxyDial specifies the optional dial function for |
| // establishing the transport connection. |
| ProxyDial func(context.Context, string, string) (net.Conn, error) |
| |
| // AuthMethods specifies the list of request authentication |
| // methods. |
| // If empty, SOCKS client requests only AuthMethodNotRequired. |
| AuthMethods []http_socksAuthMethod |
| |
| // Authenticate specifies the optional authentication |
| // function. It must be non-nil when AuthMethods is not empty. |
| // It must return an error when the authentication is failed. |
| Authenticate func(context.Context, io.ReadWriter, http_socksAuthMethod) error |
| } |
| |
| // DialContext connects to the provided address on the provided |
| // network. |
| // |
| // The returned error value may be a net.OpError. When the Op field of |
| // net.OpError contains "socks", the Source field contains a proxy |
| // server address and the Addr field contains a command target |
| // address. |
| // |
| // See func Dial of the net package of standard library for a |
| // description of the network and address parameters. |
| func (d *http_socksDialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) { |
| if err := d.validateTarget(network, address); err != nil { |
| proxy, dst, _ := d.pathAddrs(address) |
| return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err} |
| } |
| if ctx == nil { |
| proxy, dst, _ := d.pathAddrs(address) |
| return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: errors.New("nil context")} |
| } |
| var err error |
| var c net.Conn |
| if d.ProxyDial != nil { |
| c, err = d.ProxyDial(ctx, d.proxyNetwork, d.proxyAddress) |
| } else { |
| var dd net.Dialer |
| c, err = dd.DialContext(ctx, d.proxyNetwork, d.proxyAddress) |
| } |
| if err != nil { |
| proxy, dst, _ := d.pathAddrs(address) |
| return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err} |
| } |
| a, err := d.connect(ctx, c, address) |
| if err != nil { |
| c.Close() |
| proxy, dst, _ := d.pathAddrs(address) |
| return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err} |
| } |
| return &http_socksConn{Conn: c, boundAddr: a}, nil |
| } |
| |
| // DialWithConn initiates a connection from SOCKS server to the target |
| // network and address using the connection c that is already |
| // connected to the SOCKS server. |
| // |
| // It returns the connection's local address assigned by the SOCKS |
| // server. |
| func (d *http_socksDialer) DialWithConn(ctx context.Context, c net.Conn, network, address string) (net.Addr, error) { |
| if err := d.validateTarget(network, address); err != nil { |
| proxy, dst, _ := d.pathAddrs(address) |
| return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err} |
| } |
| if ctx == nil { |
| proxy, dst, _ := d.pathAddrs(address) |
| return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: errors.New("nil context")} |
| } |
| a, err := d.connect(ctx, c, address) |
| if err != nil { |
| proxy, dst, _ := d.pathAddrs(address) |
| return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err} |
| } |
| return a, nil |
| } |
| |
| // Dial connects to the provided address on the provided network. |
| // |
| // Unlike DialContext, it returns a raw transport connection instead |
| // of a forward proxy connection. |
| // |
| // Deprecated: Use DialContext or DialWithConn instead. |
| func (d *http_socksDialer) Dial(network, address string) (net.Conn, error) { |
| if err := d.validateTarget(network, address); err != nil { |
| proxy, dst, _ := d.pathAddrs(address) |
| return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err} |
| } |
| var err error |
| var c net.Conn |
| if d.ProxyDial != nil { |
| c, err = d.ProxyDial(context.Background(), d.proxyNetwork, d.proxyAddress) |
| } else { |
| c, err = net.Dial(d.proxyNetwork, d.proxyAddress) |
| } |
| if err != nil { |
| proxy, dst, _ := d.pathAddrs(address) |
| return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err} |
| } |
| if _, err := d.DialWithConn(context.Background(), c, network, address); err != nil { |
| c.Close() |
| return nil, err |
| } |
| return c, nil |
| } |
| |
| func (d *http_socksDialer) validateTarget(network, address string) error { |
| switch network { |
| case "tcp", "tcp6", "tcp4": |
| default: |
| return errors.New("network not implemented") |
| } |
| switch d.cmd { |
| case http_socksCmdConnect, http_sockscmdBind: |
| default: |
| return errors.New("command not implemented") |
| } |
| return nil |
| } |
| |
| func (d *http_socksDialer) pathAddrs(address string) (proxy, dst net.Addr, err error) { |
| for i, s := range []string{d.proxyAddress, address} { |
| host, port, err := http_sockssplitHostPort(s) |
| if err != nil { |
| return nil, nil, err |
| } |
| a := &http_socksAddr{Port: port} |
| a.IP = net.ParseIP(host) |
| if a.IP == nil { |
| a.Name = host |
| } |
| if i == 0 { |
| proxy = a |
| } else { |
| dst = a |
| } |
| } |
| return |
| } |
| |
| // NewDialer returns a new Dialer that dials through the provided |
| // proxy server's network and address. |
| func http_socksNewDialer(network, address string) *http_socksDialer { |
| return &http_socksDialer{proxyNetwork: network, proxyAddress: address, cmd: http_socksCmdConnect} |
| } |
| |
| const ( |
| http_socksauthUsernamePasswordVersion = 0x01 |
| http_socksauthStatusSucceeded = 0x00 |
| ) |
| |
| // UsernamePassword are the credentials for the username/password |
| // authentication method. |
| type http_socksUsernamePassword struct { |
| Username string |
| Password string |
| } |
| |
| // Authenticate authenticates a pair of username and password with the |
| // proxy server. |
| func (up *http_socksUsernamePassword) Authenticate(ctx context.Context, rw io.ReadWriter, auth http_socksAuthMethod) error { |
| switch auth { |
| case http_socksAuthMethodNotRequired: |
| return nil |
| case http_socksAuthMethodUsernamePassword: |
| if len(up.Username) == 0 || len(up.Username) > 255 || len(up.Password) > 255 { |
| return errors.New("invalid username/password") |
| } |
| b := []byte{http_socksauthUsernamePasswordVersion} |
| b = append(b, byte(len(up.Username))) |
| b = append(b, up.Username...) |
| b = append(b, byte(len(up.Password))) |
| b = append(b, up.Password...) |
| // TODO(mikio): handle IO deadlines and cancellation if |
| // necessary |
| if _, err := rw.Write(b); err != nil { |
| return err |
| } |
| if _, err := io.ReadFull(rw, b[:2]); err != nil { |
| return err |
| } |
| if b[0] != http_socksauthUsernamePasswordVersion { |
| return errors.New("invalid username/password version") |
| } |
| if b[1] != http_socksauthStatusSucceeded { |
| return errors.New("username/password authentication failed") |
| } |
| return nil |
| } |
| return errors.New("unsupported authentication method " + strconv.Itoa(int(auth))) |
| } |
| |
| // HTTP status codes as registered with IANA. |
| // See: https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml |
| const ( |
| http_StatusContinue = 100 // RFC 9110, 15.2.1 |
| http_StatusSwitchingProtocols = 101 // RFC 9110, 15.2.2 |
| http_StatusProcessing = 102 // RFC 2518, 10.1 |
| http_StatusEarlyHints = 103 // RFC 8297 |
| |
| http_StatusOK = 200 // RFC 9110, 15.3.1 |
| http_StatusCreated = 201 // RFC 9110, 15.3.2 |
| http_StatusAccepted = 202 // RFC 9110, 15.3.3 |
| http_StatusNonAuthoritativeInfo = 203 // RFC 9110, 15.3.4 |
| http_StatusNoContent = 204 // RFC 9110, 15.3.5 |
| http_StatusResetContent = 205 // RFC 9110, 15.3.6 |
| http_StatusPartialContent = 206 // RFC 9110, 15.3.7 |
| http_StatusMultiStatus = 207 // RFC 4918, 11.1 |
| http_StatusAlreadyReported = 208 // RFC 5842, 7.1 |
| http_StatusIMUsed = 226 // RFC 3229, 10.4.1 |
| |
| http_StatusMultipleChoices = 300 // RFC 9110, 15.4.1 |
| http_StatusMovedPermanently = 301 // RFC 9110, 15.4.2 |
| http_StatusFound = 302 // RFC 9110, 15.4.3 |
| http_StatusSeeOther = 303 // RFC 9110, 15.4.4 |
| http_StatusNotModified = 304 // RFC 9110, 15.4.5 |
| http_StatusUseProxy = 305 // RFC 9110, 15.4.6 |
| _ = 306 // RFC 9110, 15.4.7 (Unused) |
| http_StatusTemporaryRedirect = 307 // RFC 9110, 15.4.8 |
| http_StatusPermanentRedirect = 308 // RFC 9110, 15.4.9 |
| |
| http_StatusBadRequest = 400 // RFC 9110, 15.5.1 |
| http_StatusUnauthorized = 401 // RFC 9110, 15.5.2 |
| http_StatusPaymentRequired = 402 // RFC 9110, 15.5.3 |
| http_StatusForbidden = 403 // RFC 9110, 15.5.4 |
| http_StatusNotFound = 404 // RFC 9110, 15.5.5 |
| http_StatusMethodNotAllowed = 405 // RFC 9110, 15.5.6 |
| http_StatusNotAcceptable = 406 // RFC 9110, 15.5.7 |
| http_StatusProxyAuthRequired = 407 // RFC 9110, 15.5.8 |
| http_StatusRequestTimeout = 408 // RFC 9110, 15.5.9 |
| http_StatusConflict = 409 // RFC 9110, 15.5.10 |
| http_StatusGone = 410 // RFC 9110, 15.5.11 |
| http_StatusLengthRequired = 411 // RFC 9110, 15.5.12 |
| http_StatusPreconditionFailed = 412 // RFC 9110, 15.5.13 |
| http_StatusRequestEntityTooLarge = 413 // RFC 9110, 15.5.14 |
| http_StatusRequestURITooLong = 414 // RFC 9110, 15.5.15 |
| http_StatusUnsupportedMediaType = 415 // RFC 9110, 15.5.16 |
| http_StatusRequestedRangeNotSatisfiable = 416 // RFC 9110, 15.5.17 |
| http_StatusExpectationFailed = 417 // RFC 9110, 15.5.18 |
| http_StatusTeapot = 418 // RFC 9110, 15.5.19 (Unused) |
| http_StatusMisdirectedRequest = 421 // RFC 9110, 15.5.20 |
| http_StatusUnprocessableEntity = 422 // RFC 9110, 15.5.21 |
| http_StatusLocked = 423 // RFC 4918, 11.3 |
| http_StatusFailedDependency = 424 // RFC 4918, 11.4 |
| http_StatusTooEarly = 425 // RFC 8470, 5.2. |
| http_StatusUpgradeRequired = 426 // RFC 9110, 15.5.22 |
| http_StatusPreconditionRequired = 428 // RFC 6585, 3 |
| http_StatusTooManyRequests = 429 // RFC 6585, 4 |
| http_StatusRequestHeaderFieldsTooLarge = 431 // RFC 6585, 5 |
| http_StatusUnavailableForLegalReasons = 451 // RFC 7725, 3 |
| |
| http_StatusInternalServerError = 500 // RFC 9110, 15.6.1 |
| http_StatusNotImplemented = 501 // RFC 9110, 15.6.2 |
| http_StatusBadGateway = 502 // RFC 9110, 15.6.3 |
| http_StatusServiceUnavailable = 503 // RFC 9110, 15.6.4 |
| http_StatusGatewayTimeout = 504 // RFC 9110, 15.6.5 |
| http_StatusHTTPVersionNotSupported = 505 // RFC 9110, 15.6.6 |
| http_StatusVariantAlsoNegotiates = 506 // RFC 2295, 8.1 |
| http_StatusInsufficientStorage = 507 // RFC 4918, 11.5 |
| http_StatusLoopDetected = 508 // RFC 5842, 7.2 |
| http_StatusNotExtended = 510 // RFC 2774, 7 |
| http_StatusNetworkAuthenticationRequired = 511 // RFC 6585, 6 |
| ) |
| |
| // StatusText returns a text for the HTTP status code. It returns the empty |
| // string if the code is unknown. |
| func http_StatusText(code int) string { |
| switch code { |
| case http_StatusContinue: |
| return "Continue" |
| case http_StatusSwitchingProtocols: |
| return "Switching Protocols" |
| case http_StatusProcessing: |
| return "Processing" |
| case http_StatusEarlyHints: |
| return "Early Hints" |
| case http_StatusOK: |
| return "OK" |
| case http_StatusCreated: |
| return "Created" |
| case http_StatusAccepted: |
| return "Accepted" |
| case http_StatusNonAuthoritativeInfo: |
| return "Non-Authoritative Information" |
| case http_StatusNoContent: |
| return "No Content" |
| case http_StatusResetContent: |
| return "Reset Content" |
| case http_StatusPartialContent: |
| return "Partial Content" |
| case http_StatusMultiStatus: |
| return "Multi-Status" |
| case http_StatusAlreadyReported: |
| return "Already Reported" |
| case http_StatusIMUsed: |
| return "IM Used" |
| case http_StatusMultipleChoices: |
| return "Multiple Choices" |
| case http_StatusMovedPermanently: |
| return "Moved Permanently" |
| case http_StatusFound: |
| return "Found" |
| case http_StatusSeeOther: |
| return "See Other" |
| case http_StatusNotModified: |
| return "Not Modified" |
| case http_StatusUseProxy: |
| return "Use Proxy" |
| case http_StatusTemporaryRedirect: |
| return "Temporary Redirect" |
| case http_StatusPermanentRedirect: |
| return "Permanent Redirect" |
| case http_StatusBadRequest: |
| return "Bad Request" |
| case http_StatusUnauthorized: |
| return "Unauthorized" |
| case http_StatusPaymentRequired: |
| return "Payment Required" |
| case http_StatusForbidden: |
| return "Forbidden" |
| case http_StatusNotFound: |
| return "Not Found" |
| case http_StatusMethodNotAllowed: |
| return "Method Not Allowed" |
| case http_StatusNotAcceptable: |
| return "Not Acceptable" |
| case http_StatusProxyAuthRequired: |
| return "Proxy Authentication Required" |
| case http_StatusRequestTimeout: |
| return "Request Timeout" |
| case http_StatusConflict: |
| return "Conflict" |
| case http_StatusGone: |
| return "Gone" |
| case http_StatusLengthRequired: |
| return "Length Required" |
| case http_StatusPreconditionFailed: |
| return "Precondition Failed" |
| case http_StatusRequestEntityTooLarge: |
| return "Request Entity Too Large" |
| case http_StatusRequestURITooLong: |
| return "Request URI Too Long" |
| case http_StatusUnsupportedMediaType: |
| return "Unsupported Media Type" |
| case http_StatusRequestedRangeNotSatisfiable: |
| return "Requested Range Not Satisfiable" |
| case http_StatusExpectationFailed: |
| return "Expectation Failed" |
| case http_StatusTeapot: |
| return "I'm a teapot" |
| case http_StatusMisdirectedRequest: |
| return "Misdirected Request" |
| case http_StatusUnprocessableEntity: |
| return "Unprocessable Entity" |
| case http_StatusLocked: |
| return "Locked" |
| case http_StatusFailedDependency: |
| return "Failed Dependency" |
| case http_StatusTooEarly: |
| return "Too Early" |
| case http_StatusUpgradeRequired: |
| return "Upgrade Required" |
| case http_StatusPreconditionRequired: |
| return "Precondition Required" |
| case http_StatusTooManyRequests: |
| return "Too Many Requests" |
| case http_StatusRequestHeaderFieldsTooLarge: |
| return "Request Header Fields Too Large" |
| case http_StatusUnavailableForLegalReasons: |
| return "Unavailable For Legal Reasons" |
| case http_StatusInternalServerError: |
| return "Internal Server Error" |
| case http_StatusNotImplemented: |
| return "Not Implemented" |
| case http_StatusBadGateway: |
| return "Bad Gateway" |
| case http_StatusServiceUnavailable: |
| return "Service Unavailable" |
| case http_StatusGatewayTimeout: |
| return "Gateway Timeout" |
| case http_StatusHTTPVersionNotSupported: |
| return "HTTP Version Not Supported" |
| case http_StatusVariantAlsoNegotiates: |
| return "Variant Also Negotiates" |
| case http_StatusInsufficientStorage: |
| return "Insufficient Storage" |
| case http_StatusLoopDetected: |
| return "Loop Detected" |
| case http_StatusNotExtended: |
| return "Not Extended" |
| case http_StatusNetworkAuthenticationRequired: |
| return "Network Authentication Required" |
| default: |
| return "" |
| } |
| } |
| |
| // ErrLineTooLong is returned when reading request or response bodies |
| // with malformed chunked encoding. |
| var http_ErrLineTooLong = internal.ErrLineTooLong |
| |
| type http_errorReader struct { |
| err error |
| } |
| |
| func (r http_errorReader) Read(p []byte) (n int, err error) { |
| return 0, r.err |
| } |
| |
| type http_byteReader struct { |
| b byte |
| done bool |
| } |
| |
| func (br *http_byteReader) Read(p []byte) (n int, err error) { |
| if br.done { |
| return 0, io.EOF |
| } |
| if len(p) == 0 { |
| return 0, nil |
| } |
| br.done = true |
| p[0] = br.b |
| return 1, io.EOF |
| } |
| |
| // transferWriter inspects the fields of a user-supplied Request or Response, |
| // sanitizes them without changing the user object and provides methods for |
| // writing the respective header, body and trailer in wire format. |
| type http_transferWriter struct { |
| Method string |
| Body io.Reader |
| BodyCloser io.Closer |
| ResponseToHEAD bool |
| ContentLength int64 // -1 means unknown, 0 means exactly none |
| Close bool |
| TransferEncoding []string |
| Header http_Header |
| Trailer http_Header |
| IsResponse bool |
| bodyReadError error // any non-EOF error from reading Body |
| |
| FlushHeaders bool // flush headers to network before body |
| ByteReadCh chan http_readResult // non-nil if probeRequestBody called |
| } |
| |
| func http_newTransferWriter(r any) (t *http_transferWriter, err error) { |
| t = &http_transferWriter{} |
| |
| // Extract relevant fields |
| atLeastHTTP11 := false |
| switch rr := r.(type) { |
| case *http_Request: |
| if rr.ContentLength != 0 && rr.Body == nil { |
| return nil, fmt.Errorf("http: Request.ContentLength=%d with nil Body", rr.ContentLength) |
| } |
| t.Method = http_valueOrDefault(rr.Method, "GET") |
| t.Close = rr.Close |
| t.TransferEncoding = rr.TransferEncoding |
| t.Header = rr.Header |
| t.Trailer = rr.Trailer |
| t.Body = rr.Body |
| t.BodyCloser = rr.Body |
| t.ContentLength = rr.outgoingLength() |
| if t.ContentLength < 0 && len(t.TransferEncoding) == 0 && t.shouldSendChunkedRequestBody() { |
| t.TransferEncoding = []string{"chunked"} |
| } |
| // If there's a body, conservatively flush the headers |
| // to any bufio.Writer we're writing to, just in case |
| // the server needs the headers early, before we copy |
| // the body and possibly block. We make an exception |
| // for the common standard library in-memory types, |
| // though, to avoid unnecessary TCP packets on the |
| // wire. (Issue 22088.) |
| if t.ContentLength != 0 && !http_isKnownInMemoryReader(t.Body) { |
| t.FlushHeaders = true |
| } |
| |
| atLeastHTTP11 = true // Transport requests are always 1.1 or 2.0 |
| case *http_Response: |
| t.IsResponse = true |
| if rr.Request != nil { |
| t.Method = rr.Request.Method |
| } |
| t.Body = rr.Body |
| t.BodyCloser = rr.Body |
| t.ContentLength = rr.ContentLength |
| t.Close = rr.Close |
| t.TransferEncoding = rr.TransferEncoding |
| t.Header = rr.Header |
| t.Trailer = rr.Trailer |
| atLeastHTTP11 = rr.ProtoAtLeast(1, 1) |
| t.ResponseToHEAD = http_noResponseBodyExpected(t.Method) |
| } |
| |
| // Sanitize Body,ContentLength,TransferEncoding |
| if t.ResponseToHEAD { |
| t.Body = nil |
| if http_chunked(t.TransferEncoding) { |
| t.ContentLength = -1 |
| } |
| } else { |
| if !atLeastHTTP11 || t.Body == nil { |
| t.TransferEncoding = nil |
| } |
| if http_chunked(t.TransferEncoding) { |
| t.ContentLength = -1 |
| } else if t.Body == nil { // no chunking, no body |
| t.ContentLength = 0 |
| } |
| } |
| |
| // Sanitize Trailer |
| if !http_chunked(t.TransferEncoding) { |
| t.Trailer = nil |
| } |
| |
| // Validate Trailer names and values. The names are later written |
| // unmodified on the "Trailer:" line of the header, so invalid bytes |
| // (in particular CR and LF) would permit header injection. (Issue 78775.) |
| if err := http_validateHeaders(t.Trailer); err != "" { |
| return nil, fmt.Errorf("net/http: invalid trailer %s", err) |
| } |
| |
| return t, nil |
| } |
| |
| // shouldSendChunkedRequestBody reports whether we should try to send a |
| // chunked request body to the server. In particular, the case we really |
| // want to prevent is sending a GET or other typically-bodyless request to a |
| // server with a chunked body when the body has zero bytes, since GETs with |
| // bodies (while acceptable according to specs), even zero-byte chunked |
| // bodies, are approximately never seen in the wild and confuse most |
| // servers. See Issue 18257, as one example. |
| // |
| // The only reason we'd send such a request is if the user set the Body to a |
| // non-nil value (say, io.NopCloser(bytes.NewReader(nil))) and didn't |
| // set ContentLength, or NewRequest set it to -1 (unknown), so then we assume |
| // there's bytes to send. |
| // |
| // This code tries to read a byte from the Request.Body in such cases to see |
| // whether the body actually has content (super rare) or is actually just |
| // a non-nil content-less ReadCloser (the more common case). In that more |
| // common case, we act as if their Body were nil instead, and don't send |
| // a body. |
| func (t *http_transferWriter) shouldSendChunkedRequestBody() bool { |
| // Note that t.ContentLength is the corrected content length |
| // from rr.outgoingLength, so 0 actually means zero, not unknown. |
| if t.ContentLength >= 0 || t.Body == nil { // redundant checks; caller did them |
| return false |
| } |
| if t.Method == "CONNECT" { |
| return false |
| } |
| if http_requestMethodUsuallyLacksBody(t.Method) { |
| // Only probe the Request.Body for GET/HEAD/DELETE/etc |
| // requests, because it's only those types of requests |
| // that confuse servers. |
| t.probeRequestBody() // adjusts t.Body, t.ContentLength |
| return t.Body != nil |
| } |
| // For all other request types (PUT, POST, PATCH, or anything |
| // made-up we've never heard of), assume it's normal and the server |
| // can deal with a chunked request body. Maybe we'll adjust this |
| // later. |
| return true |
| } |
| |
| // probeRequestBody reads a byte from t.Body to see whether it's empty |
| // (returns io.EOF right away). |
| // |
| // But because we've had problems with this blocking users in the past |
| // (issue 17480) when the body is a pipe (perhaps waiting on the response |
| // headers before the pipe is fed data), we need to be careful and bound how |
| // long we wait for it. This delay will only affect users if all the following |
| // are true: |
| // - the request body blocks |
| // - the content length is not set (or set to -1) |
| // - the method doesn't usually have a body (GET, HEAD, DELETE, ...) |
| // - there is no transfer-encoding=chunked already set. |
| // |
| // In other words, this delay will not normally affect anybody, and there |
| // are workarounds if it does. |
| func (t *http_transferWriter) probeRequestBody() { |
| t.ByteReadCh = make(chan http_readResult, 1) |
| go func(body io.Reader) { |
| var buf [1]byte |
| var rres http_readResult |
| rres.n, rres.err = body.Read(buf[:]) |
| if rres.n == 1 { |
| rres.b = buf[0] |
| } |
| t.ByteReadCh <- rres |
| close(t.ByteReadCh) |
| }(t.Body) |
| timer := time.NewTimer(200 * time.Millisecond) |
| select { |
| case rres := <-t.ByteReadCh: |
| timer.Stop() |
| if rres.n == 0 && rres.err == io.EOF { |
| // It was empty. |
| t.Body = nil |
| t.ContentLength = 0 |
| } else if rres.n == 1 { |
| if rres.err != nil { |
| t.Body = io.MultiReader(&http_byteReader{b: rres.b}, http_errorReader{rres.err}) |
| } else { |
| t.Body = io.MultiReader(&http_byteReader{b: rres.b}, t.Body) |
| } |
| } else if rres.err != nil { |
| t.Body = http_errorReader{rres.err} |
| } |
| case <-timer.C: |
| // Too slow. Don't wait. Read it later, and keep |
| // assuming that this is ContentLength == -1 |
| // (unknown), which means we'll send a |
| // "Transfer-Encoding: chunked" header. |
| t.Body = io.MultiReader(http_finishAsyncByteRead{t}, t.Body) |
| // Request that Request.Write flush the headers to the |
| // network before writing the body, since our body may not |
| // become readable until it's seen the response headers. |
| t.FlushHeaders = true |
| } |
| } |
| |
| func http_noResponseBodyExpected(requestMethod string) bool { |
| return requestMethod == "HEAD" |
| } |
| |
| func (t *http_transferWriter) shouldSendContentLength() bool { |
| if http_chunked(t.TransferEncoding) { |
| return false |
| } |
| if t.ContentLength > 0 { |
| return true |
| } |
| if t.ContentLength < 0 { |
| return false |
| } |
| // Many servers expect a Content-Length for these methods |
| if t.Method == "POST" || t.Method == "PUT" || t.Method == "PATCH" { |
| return true |
| } |
| if t.ContentLength == 0 && http_isIdentity(t.TransferEncoding) { |
| if t.Method == "GET" || t.Method == "HEAD" { |
| return false |
| } |
| return true |
| } |
| |
| return false |
| } |
| |
| func (t *http_transferWriter) writeHeader(w io.Writer, trace *httptrace.ClientTrace) error { |
| if t.Close && !http_hasToken(t.Header.get("Connection"), "close") { |
| if _, err := io.WriteString(w, "Connection: close\r\n"); err != nil { |
| return err |
| } |
| if trace != nil && trace.WroteHeaderField != nil { |
| trace.WroteHeaderField("Connection", []string{"close"}) |
| } |
| } |
| |
| // Write Content-Length and/or Transfer-Encoding whose values are a |
| // function of the sanitized field triple (Body, ContentLength, |
| // TransferEncoding) |
| if t.shouldSendContentLength() { |
| if _, err := io.WriteString(w, "Content-Length: "); err != nil { |
| return err |
| } |
| if _, err := io.WriteString(w, strconv.FormatInt(t.ContentLength, 10)+"\r\n"); err != nil { |
| return err |
| } |
| if trace != nil && trace.WroteHeaderField != nil { |
| trace.WroteHeaderField("Content-Length", []string{strconv.FormatInt(t.ContentLength, 10)}) |
| } |
| } else if http_chunked(t.TransferEncoding) { |
| if _, err := io.WriteString(w, "Transfer-Encoding: chunked\r\n"); err != nil { |
| return err |
| } |
| if trace != nil && trace.WroteHeaderField != nil { |
| trace.WroteHeaderField("Transfer-Encoding", []string{"chunked"}) |
| } |
| } |
| |
| // Write Trailer header |
| if t.Trailer != nil { |
| keys := make([]string, 0, len(t.Trailer)) |
| for k := range t.Trailer { |
| k = http_CanonicalHeaderKey(k) |
| switch k { |
| case "Transfer-Encoding", "Trailer", "Content-Length": |
| return http_badStringError("invalid Trailer key", k) |
| } |
| keys = append(keys, k) |
| } |
| if len(keys) > 0 { |
| slices.Sort(keys) |
| // TODO: could do better allocation-wise here, but trailers are rare, |
| // so being lazy for now. |
| if _, err := io.WriteString(w, "Trailer: "+strings.Join(keys, ",")+"\r\n"); err != nil { |
| return err |
| } |
| if trace != nil && trace.WroteHeaderField != nil { |
| trace.WroteHeaderField("Trailer", keys) |
| } |
| } |
| } |
| |
| return nil |
| } |
| |
| // always closes t.BodyCloser |
| func (t *http_transferWriter) writeBody(w io.Writer) (err error) { |
| var ncopy int64 |
| closed := false |
| defer func() { |
| if closed || t.BodyCloser == nil { |
| return |
| } |
| if closeErr := t.BodyCloser.Close(); closeErr != nil && err == nil { |
| err = closeErr |
| } |
| }() |
| |
| // Write body. We "unwrap" the body first if it was wrapped in a |
| // nopCloser or readTrackingBody. This is to ensure that we can take advantage of |
| // OS-level optimizations in the event that the body is an |
| // *os.File. |
| if !t.ResponseToHEAD && t.Body != nil { |
| var body = t.unwrapBody() |
| if http_chunked(t.TransferEncoding) { |
| if bw, ok := w.(*bufio.Writer); ok && !t.IsResponse { |
| w = &internal.FlushAfterChunkWriter{Writer: bw} |
| } |
| cw := internal.NewChunkedWriter(w) |
| _, err = t.doBodyCopy(cw, body) |
| if err == nil { |
| err = cw.Close() |
| } |
| } else if t.ContentLength == -1 { |
| dst := w |
| if t.Method == "CONNECT" { |
| dst = http_bufioFlushWriter{dst} |
| } |
| ncopy, err = t.doBodyCopy(dst, body) |
| } else { |
| ncopy, err = t.doBodyCopy(w, io.LimitReader(body, t.ContentLength)) |
| if err != nil { |
| return err |
| } |
| var nextra int64 |
| nextra, err = t.doBodyCopy(io.Discard, body) |
| ncopy += nextra |
| } |
| if err != nil { |
| return err |
| } |
| } |
| if t.BodyCloser != nil { |
| closed = true |
| if err := t.BodyCloser.Close(); err != nil { |
| return err |
| } |
| } |
| |
| if !t.ResponseToHEAD && t.ContentLength != -1 && t.ContentLength != ncopy { |
| return fmt.Errorf("http: ContentLength=%d with Body length %d", |
| t.ContentLength, ncopy) |
| } |
| |
| if !t.ResponseToHEAD && http_chunked(t.TransferEncoding) { |
| // Write Trailer header |
| if t.Trailer != nil { |
| if err := t.Trailer.Write(w); err != nil { |
| return err |
| } |
| } |
| // Last chunk, empty trailer |
| _, err = io.WriteString(w, "\r\n") |
| } |
| return err |
| } |
| |
| // doBodyCopy wraps a copy operation, with any resulting error also |
| // being saved in bodyReadError. |
| // |
| // This function is only intended for use in writeBody. |
| func (t *http_transferWriter) doBodyCopy(dst io.Writer, src io.Reader) (n int64, err error) { |
| buf := http_getCopyBuf() |
| defer http_putCopyBuf(buf) |
| |
| n, err = io.CopyBuffer(dst, src, buf) |
| if err != nil && err != io.EOF { |
| t.bodyReadError = err |
| } |
| return |
| } |
| |
| // unwrapBody unwraps the body's inner reader if it's a |
| // nopCloser. This is to ensure that body writes sourced from local |
| // files (*os.File types) are properly optimized. |
| // |
| // This function is only intended for use in writeBody. |
| func (t *http_transferWriter) unwrapBody() io.Reader { |
| if r, ok := http_unwrapNopCloser(t.Body); ok { |
| return r |
| } |
| if r, ok := t.Body.(*http_readTrackingBody); ok { |
| r.didRead = true |
| return r.ReadCloser |
| } |
| return t.Body |
| } |
| |
| type http_transferReader struct { |
| // Input |
| Header http_Header |
| StatusCode int |
| RequestMethod string |
| ProtoMajor int |
| ProtoMinor int |
| // Output |
| Body io.ReadCloser |
| ContentLength int64 |
| Chunked bool |
| Close bool |
| Trailer http_Header |
| } |
| |
| func (t *http_transferReader) protoAtLeast(m, n int) bool { |
| return t.ProtoMajor > m || (t.ProtoMajor == m && t.ProtoMinor >= n) |
| } |
| |
| // bodyAllowedForStatus reports whether a given response status code |
| // permits a body. See RFC 7230, section 3.3. |
| func http_bodyAllowedForStatus(status int) bool { |
| switch { |
| case status >= 100 && status <= 199: |
| return false |
| case status == 204: |
| return false |
| case status == 304: |
| return false |
| } |
| return true |
| } |
| |
| var ( |
| http_suppressedHeaders304 = []string{"Content-Type", "Content-Length", "Transfer-Encoding"} |
| http_suppressedHeadersNoBody = []string{"Content-Length", "Transfer-Encoding"} |
| http_excludedHeadersNoBody = map[string]bool{"Content-Length": true, "Transfer-Encoding": true} |
| ) |
| |
| func http_suppressedHeaders(status int) []string { |
| switch { |
| case status == 304: |
| // RFC 7232 section 4.1 |
| return http_suppressedHeaders304 |
| case !http_bodyAllowedForStatus(status): |
| return http_suppressedHeadersNoBody |
| } |
| return nil |
| } |
| |
| // msg is *Request or *Response. |
| func http_readTransfer(msg any, r *bufio.Reader, maxTrailerHeaders int64) (err error) { |
| t := &http_transferReader{RequestMethod: "GET"} |
| |
| // Unify input |
| isResponse := false |
| switch rr := msg.(type) { |
| case *http_Response: |
| t.Header = rr.Header |
| t.StatusCode = rr.StatusCode |
| t.ProtoMajor = rr.ProtoMajor |
| t.ProtoMinor = rr.ProtoMinor |
| t.Close = http_shouldClose(t.ProtoMajor, t.ProtoMinor, t.Header, true) |
| isResponse = true |
| if rr.Request != nil { |
| t.RequestMethod = rr.Request.Method |
| } |
| case *http_Request: |
| t.Header = rr.Header |
| t.RequestMethod = rr.Method |
| t.ProtoMajor = rr.ProtoMajor |
| t.ProtoMinor = rr.ProtoMinor |
| // Transfer semantics for Requests are exactly like those for |
| // Responses with status code 200, responding to a GET method |
| t.StatusCode = 200 |
| t.Close = rr.Close |
| default: |
| panic("unexpected type") |
| } |
| |
| // Default to HTTP/1.1 |
| if t.ProtoMajor == 0 && t.ProtoMinor == 0 { |
| t.ProtoMajor, t.ProtoMinor = 1, 1 |
| } |
| |
| // Transfer-Encoding: chunked, and overriding Content-Length. |
| if err := t.parseTransferEncoding(); err != nil { |
| return err |
| } |
| |
| realLength, err := http_fixLength(isResponse, t.StatusCode, t.RequestMethod, t.Header, t.Chunked) |
| if err != nil { |
| return err |
| } |
| if isResponse && t.RequestMethod == "HEAD" { |
| if n, err := http_parseContentLength(t.Header["Content-Length"]); err != nil { |
| return err |
| } else { |
| t.ContentLength = n |
| } |
| } else { |
| t.ContentLength = realLength |
| } |
| |
| // Trailer |
| t.Trailer, err = http_fixTrailer(t.Header, t.Chunked) |
| if err != nil { |
| return err |
| } |
| |
| // If there is no Content-Length or chunked Transfer-Encoding on a *Response |
| // and the status is not 1xx, 204 or 304, then the body is unbounded. |
| // See RFC 7230, section 3.3. |
| switch msg.(type) { |
| case *http_Response: |
| if realLength == -1 && !t.Chunked && http_bodyAllowedForStatus(t.StatusCode) { |
| // Unbounded body. |
| t.Close = true |
| } |
| } |
| |
| // Prepare body reader. ContentLength < 0 means chunked encoding |
| // or close connection when finished, since multipart is not supported yet |
| switch { |
| case t.Chunked: |
| if isResponse && (http_noResponseBodyExpected(t.RequestMethod) || !http_bodyAllowedForStatus(t.StatusCode)) { |
| t.Body = http_NoBody |
| } else { |
| t.Body = &http_body{src: internal.NewChunkedReader(r), hdr: msg, r: r, closing: t.Close, maxTrailerHeaders: maxTrailerHeaders} |
| } |
| case realLength == 0: |
| t.Body = http_NoBody |
| case realLength > 0: |
| t.Body = &http_body{src: io.LimitReader(r, realLength), closing: t.Close} |
| default: |
| // realLength < 0, i.e. "Content-Length" not mentioned in header |
| if t.Close { |
| // Close semantics (i.e. HTTP/1.0) |
| t.Body = &http_body{src: r, closing: t.Close} |
| } else { |
| // Persistent connection (i.e. HTTP/1.1) |
| t.Body = http_NoBody |
| } |
| } |
| |
| // Unify output |
| switch rr := msg.(type) { |
| case *http_Request: |
| rr.Body = t.Body |
| rr.ContentLength = t.ContentLength |
| if t.Chunked { |
| rr.TransferEncoding = []string{"chunked"} |
| } |
| rr.Close = t.Close |
| rr.Trailer = t.Trailer |
| case *http_Response: |
| rr.Body = t.Body |
| rr.ContentLength = t.ContentLength |
| if t.Chunked { |
| rr.TransferEncoding = []string{"chunked"} |
| } |
| rr.Close = t.Close |
| rr.Trailer = t.Trailer |
| } |
| |
| return nil |
| } |
| |
| // Checks whether chunked is part of the encodings stack. |
| func http_chunked(te []string) bool { return len(te) > 0 && te[0] == "chunked" } |
| |
| // Checks whether the encoding is explicitly "identity". |
| func http_isIdentity(te []string) bool { return len(te) == 1 && te[0] == "identity" } |
| |
| // unsupportedTEError reports unsupported transfer-encodings. |
| type http_unsupportedTEError struct { |
| err string |
| } |
| |
| func (uste *http_unsupportedTEError) Error() string { |
| return uste.err |
| } |
| |
| // isUnsupportedTEError checks if the error is of type |
| // unsupportedTEError. It is usually invoked with a non-nil err. |
| func http_isUnsupportedTEError(err error) bool { |
| _, ok := err.(*http_unsupportedTEError) |
| return ok |
| } |
| |
| // parseTransferEncoding sets t.Chunked based on the Transfer-Encoding header. |
| func (t *http_transferReader) parseTransferEncoding() error { |
| raw, present := t.Header["Transfer-Encoding"] |
| if !present { |
| return nil |
| } |
| delete(t.Header, "Transfer-Encoding") |
| |
| // Issue 12785; ignore Transfer-Encoding on HTTP/1.0 requests. |
| if !t.protoAtLeast(1, 1) { |
| return nil |
| } |
| |
| // Like nginx, we only support a single Transfer-Encoding header field, and |
| // only if set to "chunked". This is one of the most security sensitive |
| // surfaces in HTTP/1.1 due to the risk of request smuggling, so we keep it |
| // strict and simple. |
| if len(raw) != 1 { |
| return &http_unsupportedTEError{fmt.Sprintf("too many transfer encodings: %q", raw)} |
| } |
| if !ascii.EqualFold(raw[0], "chunked") { |
| return &http_unsupportedTEError{fmt.Sprintf("unsupported transfer encoding: %q", raw[0])} |
| } |
| |
| t.Chunked = true |
| return nil |
| } |
| |
| // Determine the expected body length, using RFC 7230 Section 3.3. This |
| // function is not a method, because ultimately it should be shared by |
| // ReadResponse and ReadRequest. |
| func http_fixLength(isResponse bool, status int, requestMethod string, header http_Header, chunked bool) (n int64, err error) { |
| isRequest := !isResponse |
| contentLens := header["Content-Length"] |
| |
| // Hardening against HTTP request smuggling |
| if len(contentLens) > 1 { |
| // Per RFC 7230 Section 3.3.2, prevent multiple |
| // Content-Length headers if they differ in value. |
| // If there are dups of the value, remove the dups. |
| // See Issue 16490. |
| first := textproto.TrimString(contentLens[0]) |
| for _, ct := range contentLens[1:] { |
| if first != textproto.TrimString(ct) { |
| return 0, fmt.Errorf("http: message cannot contain multiple Content-Length headers; got %q", contentLens) |
| } |
| } |
| |
| // deduplicate Content-Length |
| header.Del("Content-Length") |
| header.Add("Content-Length", first) |
| |
| contentLens = header["Content-Length"] |
| } |
| |
| // Reject requests with invalid Content-Length headers. |
| if len(contentLens) > 0 { |
| n, err = http_parseContentLength(contentLens) |
| if err != nil { |
| return -1, err |
| } |
| } |
| |
| // Logic based on response type or status |
| if isResponse && http_noResponseBodyExpected(requestMethod) { |
| return 0, nil |
| } |
| if status/100 == 1 { |
| return 0, nil |
| } |
| switch status { |
| case 204, 304: |
| return 0, nil |
| } |
| |
| // According to RFC 9112, "If a message is received with both a |
| // Transfer-Encoding and a Content-Length header field, the Transfer-Encoding |
| // overrides the Content-Length. Such a message might indicate an attempt to |
| // perform request smuggling (Section 11.2) or response splitting (Section 11.1) |
| // and ought to be handled as an error. An intermediary that chooses to forward |
| // the message MUST first remove the received Content-Length field and process |
| // the Transfer-Encoding (as described below) prior to forwarding the message downstream." |
| // |
| // Chunked-encoding requests with either valid Content-Length |
| // headers or no Content-Length headers are accepted after removing |
| // the Content-Length field from header. |
| // |
| // Logic based on Transfer-Encoding |
| if chunked { |
| header.Del("Content-Length") |
| return -1, nil |
| } |
| |
| // Logic based on Content-Length |
| if len(contentLens) > 0 { |
| return n, nil |
| } |
| |
| header.Del("Content-Length") |
| |
| if isRequest { |
| // RFC 7230 neither explicitly permits nor forbids an |
| // entity-body on a GET request so we permit one if |
| // declared, but we default to 0 here (not -1 below) |
| // if there's no mention of a body. |
| // Likewise, all other request methods are assumed to have |
| // no body if neither Transfer-Encoding chunked nor a |
| // Content-Length are set. |
| return 0, nil |
| } |
| |
| // Body-EOF logic based on other methods (like closing, or chunked coding) |
| return -1, nil |
| } |
| |
| // Determine whether to hang up after sending a request and body, or |
| // receiving a response and body |
| // 'header' is the request headers. |
| func http_shouldClose(major, minor int, header http_Header, removeCloseHeader bool) bool { |
| if major < 1 { |
| return true |
| } |
| |
| conv := header["Connection"] |
| hasClose := httpguts.HeaderValuesContainsToken(conv, "close") |
| if major == 1 && minor == 0 { |
| return hasClose || !httpguts.HeaderValuesContainsToken(conv, "keep-alive") |
| } |
| |
| if hasClose && removeCloseHeader { |
| header.Del("Connection") |
| } |
| |
| return hasClose |
| } |
| |
| // Parse the trailer header. |
| func http_fixTrailer(header http_Header, chunked bool) (http_Header, error) { |
| vv, ok := header["Trailer"] |
| if !ok { |
| return nil, nil |
| } |
| if !chunked { |
| // Trailer and no chunking: |
| // this is an invalid use case for trailer header. |
| // Nevertheless, no error will be returned and we |
| // let users decide if this is a valid HTTP message. |
| // The Trailer header will be kept in Response.Header |
| // but not populate Response.Trailer. |
| // See issue #27197. |
| return nil, nil |
| } |
| header.Del("Trailer") |
| |
| trailer := make(http_Header) |
| var err error |
| for _, v := range vv { |
| http_foreachHeaderElement(v, func(key string) { |
| key = http_CanonicalHeaderKey(key) |
| switch key { |
| case "Transfer-Encoding", "Trailer", "Content-Length": |
| if err == nil { |
| err = http_badStringError("bad trailer key", key) |
| return |
| } |
| } |
| trailer[key] = nil |
| }) |
| } |
| if err != nil { |
| return nil, err |
| } |
| if len(trailer) == 0 { |
| return nil, nil |
| } |
| return trailer, nil |
| } |
| |
| // body turns a Reader into a ReadCloser. |
| // Close ensures that the body has been fully read |
| // and then reads the trailer if necessary. |
| type http_body struct { |
| src io.Reader |
| hdr any // non-nil (Response or Request) value means read trailer |
| r *bufio.Reader // underlying wire-format reader for the trailer |
| closing bool // is the connection to be closed after reading body? |
| doEarlyClose bool // whether Close should stop early |
| maxTrailerHeaders int64 // how many trailer header values are allowed |
| |
| mu sync.Mutex // guards following, and calls to Read and Close |
| sawEOF bool |
| closed bool |
| earlyClose bool // Close called and we didn't read to the end of src |
| onHitEOF func() // if non-nil, func to call when EOF is Read |
| } |
| |
| // ErrBodyReadAfterClose is returned when reading a [Request] or [Response] |
| // Body after the body has been closed. This typically happens when the body is |
| // read after an HTTP [Handler] calls WriteHeader or Write on its |
| // [ResponseWriter]. |
| var http_ErrBodyReadAfterClose = errors.New("http: invalid Read on closed Body") |
| |
| func (b *http_body) Read(p []byte) (n int, err error) { |
| if b == nil { |
| return 0, io.EOF |
| } |
| b.mu.Lock() |
| defer b.mu.Unlock() |
| if b.closed { |
| return 0, http_ErrBodyReadAfterClose |
| } |
| return b.readLocked(p) |
| } |
| |
| // Must hold b.mu. |
| func (b *http_body) readLocked(p []byte) (n int, err error) { |
| if b.sawEOF { |
| return 0, io.EOF |
| } |
| n, err = b.src.Read(p) |
| |
| if err == io.EOF { |
| b.sawEOF = true |
| // Chunked case. Read the trailer. |
| if b.hdr != nil { |
| if e := b.readTrailer(); e != nil { |
| err = e |
| // Something went wrong in the trailer, we must not allow any |
| // further reads of any kind to succeed from body, nor any |
| // subsequent requests on the server connection. See |
| // golang.org/issue/12027 |
| b.sawEOF = false |
| b.closed = true |
| } |
| b.hdr = nil |
| } else { |
| // If the server declared the Content-Length, our body is a LimitedReader |
| // and we need to check whether this EOF arrived early. |
| if lr, ok := b.src.(*io.LimitedReader); ok && lr.N > 0 { |
| err = io.ErrUnexpectedEOF |
| } |
| } |
| } |
| |
| // If we can return an EOF here along with the read data, do |
| // so. This is optional per the io.Reader contract, but doing |
| // so helps the HTTP transport code recycle its connection |
| // earlier (since it will see this EOF itself), even if the |
| // client doesn't do future reads or Close. |
| if err == nil && n > 0 { |
| if lr, ok := b.src.(*io.LimitedReader); ok && lr.N == 0 { |
| err = io.EOF |
| b.sawEOF = true |
| } |
| } |
| |
| if b.sawEOF && b.onHitEOF != nil { |
| b.onHitEOF() |
| } |
| |
| return n, err |
| } |
| |
| var ( |
| http_singleCRLF = []byte("\r\n") |
| http_doubleCRLF = []byte("\r\n\r\n") |
| ) |
| |
| func http_seeUpcomingDoubleCRLF(r *bufio.Reader) bool { |
| for peekSize := 4; ; peekSize++ { |
| // This loop stops when Peek returns an error, |
| // which it does when r's buffer has been filled. |
| buf, err := r.Peek(peekSize) |
| if bytes.HasSuffix(buf, http_doubleCRLF) { |
| return true |
| } |
| if err != nil { |
| break |
| } |
| } |
| return false |
| } |
| |
| var http_errTrailerEOF = errors.New("http: unexpected EOF reading trailer") |
| |
| func (b *http_body) readTrailer() error { |
| // The common case, since nobody uses trailers. |
| buf, err := b.r.Peek(2) |
| if bytes.Equal(buf, http_singleCRLF) { |
| b.r.Discard(2) |
| return nil |
| } |
| if len(buf) < 2 { |
| return http_errTrailerEOF |
| } |
| if err != nil { |
| return err |
| } |
| |
| // Make sure there's a header terminator coming up, to prevent |
| // a DoS with an unbounded size Trailer. It's not easy to |
| // slip in a LimitReader here, as textproto.NewReader requires |
| // a concrete *bufio.Reader. Also, we can't get all the way |
| // back up to our conn's LimitedReader that *might* be backing |
| // this bufio.Reader. Instead, a hack: we iteratively Peek up |
| // to the bufio.Reader's max size, looking for a double CRLF. |
| // This limits the trailer to the underlying buffer size, typically 4kB. |
| if !http_seeUpcomingDoubleCRLF(b.r) { |
| return errors.New("http: suspiciously long trailer after chunked body") |
| } |
| |
| hdr, err := http_readMIMEHeader(textproto.NewReader(b.r), math.MaxInt64, b.maxTrailerHeaders) |
| if err != nil { |
| if err == io.EOF { |
| return http_errTrailerEOF |
| } |
| return err |
| } |
| switch rr := b.hdr.(type) { |
| case *http_Request: |
| http_mergeSetHeader(&rr.Trailer, http_Header(hdr)) |
| case *http_Response: |
| http_mergeSetHeader(&rr.Trailer, http_Header(hdr)) |
| } |
| return nil |
| } |
| |
| func http_mergeSetHeader(dst *http_Header, src http_Header) { |
| if *dst == nil { |
| *dst = src |
| return |
| } |
| maps.Copy(*dst, src) |
| } |
| |
| // unreadDataSizeLocked returns the number of bytes of unread input. |
| // It returns -1 if unknown. |
| // b.mu must be held. |
| func (b *http_body) unreadDataSizeLocked() int64 { |
| if lr, ok := b.src.(*io.LimitedReader); ok { |
| return lr.N |
| } |
| return -1 |
| } |
| |
| func (b *http_body) Close() error { |
| if b == nil { |
| return nil |
| } |
| b.mu.Lock() |
| defer b.mu.Unlock() |
| if b.closed { |
| return nil |
| } |
| var err error |
| switch { |
| case b.sawEOF: |
| // Already saw EOF, so no need going to look for it. |
| case b.hdr == nil && b.closing: |
| // no trailer and closing the connection next. |
| // no point in reading to EOF. |
| case b.doEarlyClose: |
| // Read up to maxPostHandlerReadBytes bytes of the body, looking |
| // for EOF (and trailers), so we can re-use this connection. |
| if lr, ok := b.src.(*io.LimitedReader); ok && lr.N > http_maxPostHandlerReadBytes { |
| // There was a declared Content-Length, and we have more bytes remaining |
| // than our maxPostHandlerReadBytes tolerance. So, give up. |
| b.earlyClose = true |
| } else { |
| var n int64 |
| // Consume the body, or, which will also lead to us reading |
| // the trailer headers after the body, if present. |
| n, err = io.CopyN(io.Discard, http_bodyLocked{b}, http_maxPostHandlerReadBytes+1) |
| b.earlyClose = true |
| if err == io.EOF && n <= http_maxPostHandlerReadBytes { |
| b.earlyClose = false |
| b.sawEOF = true |
| } |
| } |
| default: |
| // Fully consume the body, which will also lead to us reading |
| // the trailer headers after the body, if present. |
| _, err = io.Copy(io.Discard, http_bodyLocked{b}) |
| } |
| b.closed = true |
| return err |
| } |
| |
| func (b *http_body) didEarlyClose() bool { |
| b.mu.Lock() |
| defer b.mu.Unlock() |
| return b.earlyClose |
| } |
| |
| // bodyRemains reports whether future Read calls might |
| // yield data. |
| func (b *http_body) bodyRemains() bool { |
| if b == nil { |
| return false |
| } |
| b.mu.Lock() |
| defer b.mu.Unlock() |
| return !b.sawEOF |
| } |
| |
| func (b *http_body) registerOnHitEOF(fn func()) { |
| if b == nil { |
| return |
| } |
| b.mu.Lock() |
| defer b.mu.Unlock() |
| b.onHitEOF = fn |
| } |
| |
| // bodyLocked is an io.Reader reading from a *body when its mutex is |
| // already held. |
| type http_bodyLocked struct { |
| b *http_body |
| } |
| |
| func (bl http_bodyLocked) Read(p []byte) (n int, err error) { |
| if bl.b.closed { |
| return 0, http_ErrBodyReadAfterClose |
| } |
| return bl.b.readLocked(p) |
| } |
| |
| var http_httplaxcontentlength = godebug.New("httplaxcontentlength") |
| |
| // parseContentLength checks that the header is valid and then trims |
| // whitespace. It returns -1 if no value is set otherwise the value |
| // if it's >= 0. |
| func http_parseContentLength(clHeaders []string) (int64, error) { |
| if len(clHeaders) == 0 { |
| return -1, nil |
| } |
| cl := textproto.TrimString(clHeaders[0]) |
| |
| // The Content-Length must be a valid numeric value. |
| // See: https://datatracker.ietf.org/doc/html/rfc2616/#section-14.13 |
| if cl == "" { |
| if http_httplaxcontentlength.Value() == "1" { |
| http_httplaxcontentlength.IncNonDefault() |
| return -1, nil |
| } |
| return 0, http_badStringError("invalid empty Content-Length", cl) |
| } |
| n, err := strconv.ParseUint(cl, 10, 63) |
| if err != nil { |
| return 0, http_badStringError("bad Content-Length", cl) |
| } |
| return int64(n), nil |
| } |
| |
| // finishAsyncByteRead finishes reading the 1-byte sniff |
| // from the ContentLength==0, Body!=nil case. |
| type http_finishAsyncByteRead struct { |
| tw *http_transferWriter |
| } |
| |
| func (fr http_finishAsyncByteRead) Read(p []byte) (n int, err error) { |
| if len(p) == 0 { |
| return |
| } |
| rres := <-fr.tw.ByteReadCh |
| n, err = rres.n, rres.err |
| if n == 1 { |
| p[0] = rres.b |
| } |
| if err == nil { |
| err = io.EOF |
| } |
| return |
| } |
| |
| var http_nopCloserType = reflect.TypeOf(io.NopCloser(nil)) |
| |
| var http_nopCloserWriterToType = reflect.TypeOf(io.NopCloser(struct { |
| io.Reader |
| io.WriterTo |
| }{})) |
| |
| // unwrapNopCloser return the underlying reader and true if r is a NopCloser |
| // else it return false. |
| func http_unwrapNopCloser(r io.Reader) (underlyingReader io.Reader, isNopCloser bool) { |
| switch reflect.TypeOf(r) { |
| case http_nopCloserType, http_nopCloserWriterToType: |
| return reflect.ValueOf(r).Field(0).Interface().(io.Reader), true |
| default: |
| return nil, false |
| } |
| } |
| |
| // isKnownInMemoryReader reports whether r is a type known to not |
| // block on Read. Its caller uses this as an optional optimization to |
| // send fewer TCP packets. |
| func http_isKnownInMemoryReader(r io.Reader) bool { |
| switch r.(type) { |
| case *bytes.Reader, *bytes.Buffer, *strings.Reader: |
| return true |
| } |
| if r, ok := http_unwrapNopCloser(r); ok { |
| return http_isKnownInMemoryReader(r) |
| } |
| if r, ok := r.(*http_readTrackingBody); ok { |
| return http_isKnownInMemoryReader(r.ReadCloser) |
| } |
| return false |
| } |
| |
| // bufioFlushWriter is an io.Writer wrapper that flushes all writes |
| // on its wrapped writer if it's a *bufio.Writer. |
| type http_bufioFlushWriter struct{ w io.Writer } |
| |
| func (fw http_bufioFlushWriter) Write(p []byte) (n int, err error) { |
| n, err = fw.w.Write(p) |
| if bw, ok := fw.w.(*bufio.Writer); n > 0 && ok { |
| ferr := bw.Flush() |
| if ferr != nil && err == nil { |
| err = ferr |
| } |
| } |
| return |
| } |
| |
| // DefaultTransport is the default implementation of [Transport] and is |
| // used by [DefaultClient]. It establishes network connections as needed |
| // and caches them for reuse by subsequent calls. It uses HTTP proxies |
| // as directed by the environment variables HTTP_PROXY, HTTPS_PROXY |
| // and NO_PROXY (or the lowercase versions thereof, which take |
| // precedence over the uppercase versions). |
| var http_DefaultTransport http_RoundTripper = &http_Transport{ |
| Proxy: http_ProxyFromEnvironment, |
| DialContext: http_defaultTransportDialContext(&net.Dialer{ |
| Timeout: 30 * time.Second, |
| KeepAlive: 30 * time.Second, |
| }), |
| ForceAttemptHTTP2: true, |
| MaxIdleConns: 100, |
| IdleConnTimeout: 90 * time.Second, |
| TLSHandshakeTimeout: 10 * time.Second, |
| ExpectContinueTimeout: 1 * time.Second, |
| } |
| |
| // DefaultMaxIdleConnsPerHost is the default value of [Transport]'s |
| // MaxIdleConnsPerHost. |
| const http_DefaultMaxIdleConnsPerHost = 2 |
| |
| // Transport is an implementation of [RoundTripper] that supports HTTP, |
| // HTTPS, and HTTP proxies (for either HTTP or HTTPS with CONNECT). |
| // |
| // By default, Transport caches connections for future re-use. |
| // This may leave many open connections when accessing many hosts. |
| // This behavior can be managed using [Transport.CloseIdleConnections] method |
| // and the [Transport.MaxIdleConnsPerHost] and [Transport.DisableKeepAlives] fields. |
| // |
| // Transports should be reused instead of created as needed. |
| // Transports are safe for concurrent use by multiple goroutines. |
| // |
| // A Transport is a low-level primitive for making HTTP and HTTPS requests. |
| // For high-level functionality, such as cookies and redirects, see [Client]. |
| // |
| // Transport uses HTTP/1.1 for HTTP URLs and either HTTP/1.1 or HTTP/2 |
| // for HTTPS URLs, depending on whether the server supports HTTP/2, |
| // and how the Transport is configured. The [DefaultTransport] supports HTTP/2. |
| // To explicitly enable HTTP/2 on a transport, set [Transport.Protocols]. |
| // |
| // Responses with status codes in the 1xx range are either handled |
| // automatically (100 expect-continue) or ignored. The one |
| // exception is HTTP status code 101 (Switching Protocols), which is |
| // considered a terminal status and returned by [Transport.RoundTrip]. To see the |
| // ignored 1xx responses, use the httptrace trace package's |
| // ClientTrace.Got1xxResponse. |
| // |
| // Transport only retries a request upon encountering a network error |
| // if the connection has already been used successfully and if the |
| // request is idempotent and either has no body or has its [Request.GetBody] |
| // defined. HTTP requests are considered idempotent if they have HTTP methods |
| // GET, HEAD, OPTIONS, or TRACE; or if their [Header] map contains an |
| // "Idempotency-Key" or "X-Idempotency-Key" entry. If the idempotency key |
| // value is a zero-length slice, the request is treated as idempotent but the |
| // header is not sent on the wire. |
| type http_Transport struct { |
| idleMu sync.Mutex |
| closeIdle bool // user has requested to close all idle conns |
| idleConn map[http_connectMethodKey][]*http_persistConn // most recently used at end |
| idleConnWait map[http_connectMethodKey]http_wantConnQueue // waiting getConns |
| idleLRU http_connLRU |
| |
| reqMu sync.Mutex |
| reqCanceler map[*http_Request]context.CancelCauseFunc |
| |
| altMu sync.Mutex // guards changing altProto only |
| altProto atomic.Value // of nil or map[string]RoundTripper, key is URI scheme |
| |
| connsPerHostMu sync.Mutex |
| connsPerHost map[http_connectMethodKey]int |
| connsPerHostWait map[http_connectMethodKey]http_wantConnQueue // waiting getConns |
| dialsInProgress http_wantConnQueue |
| |
| // Proxy specifies a function to return a proxy for a given |
| // Request. If the function returns a non-nil error, the |
| // request is aborted with the provided error. |
| // |
| // The proxy type is determined by the URL scheme. "http", |
| // "https", "socks5", and "socks5h" are supported. If the scheme is empty, |
| // "http" is assumed. |
| // "socks5" is treated the same as "socks5h". |
| // |
| // If the proxy URL contains a userinfo subcomponent, |
| // the proxy request will pass the username and password |
| // in a Proxy-Authorization header. |
| // |
| // If Proxy is nil or returns a nil *URL, no proxy is used. |
| Proxy func(*http_Request) (*url.URL, error) |
| |
| // OnProxyConnectResponse is called when the Transport gets an HTTP response from |
| // a proxy for a CONNECT request. It's called before the check for a 200 OK response. |
| // If it returns an error, the request fails with that error. |
| OnProxyConnectResponse func(ctx context.Context, proxyURL *url.URL, connectReq *http_Request, connectRes *http_Response) error |
| |
| // DialContext specifies the dial function for creating unencrypted TCP connections. |
| // If DialContext is nil (and the deprecated Dial below is also nil), |
| // then the transport dials using package net. |
| // |
| // DialContext runs concurrently with calls to RoundTrip. |
| // A RoundTrip call that initiates a dial may end up using |
| // a connection dialed previously when the earlier connection |
| // becomes idle before the later DialContext completes. |
| DialContext func(ctx context.Context, network, addr string) (net.Conn, error) |
| |
| // Dial specifies the dial function for creating unencrypted TCP connections. |
| // |
| // Dial runs concurrently with calls to RoundTrip. |
| // A RoundTrip call that initiates a dial may end up using |
| // a connection dialed previously when the earlier connection |
| // becomes idle before the later Dial completes. |
| // |
| // Deprecated: Use DialContext instead, which allows the transport |
| // to cancel dials as soon as they are no longer needed. |
| // If both are set, DialContext takes priority. |
| Dial func(network, addr string) (net.Conn, error) |
| |
| // DialTLSContext specifies an optional dial function for creating |
| // TLS connections for non-proxied HTTPS requests. |
| // |
| // If DialTLSContext is nil (and the deprecated DialTLS below is also nil), |
| // DialContext and TLSClientConfig are used. |
| // |
| // If DialTLSContext is set, the Dial and DialContext hooks are not used for HTTPS |
| // requests and the TLSClientConfig and TLSHandshakeTimeout |
| // are ignored. The returned net.Conn is assumed to already be |
| // past the TLS handshake. |
| // |
| // To support ALPN protocol negotiation, the returned net.Conn should be |
| // a *tls.Conn or implement the same ConnectionState method as *tls.Conn. |
| DialTLSContext func(ctx context.Context, network, addr string) (net.Conn, error) |
| |
| // DialTLS specifies an optional dial function for creating |
| // TLS connections for non-proxied HTTPS requests. |
| // |
| // Deprecated: Use DialTLSContext instead, which allows the transport |
| // to cancel dials as soon as they are no longer needed. |
| // If both are set, DialTLSContext takes priority. |
| DialTLS func(network, addr string) (net.Conn, error) |
| |
| // TLSClientConfig specifies the TLS configuration to use with |
| // tls.Client. |
| // If nil, the default configuration is used. |
| // If non-nil, HTTP/2 support may not be enabled by default. |
| TLSClientConfig *tls.Config |
| |
| // TLSHandshakeTimeout specifies the maximum amount of time to |
| // wait for a TLS handshake. Zero means no timeout. |
| TLSHandshakeTimeout time.Duration |
| |
| // DisableKeepAlives, if true, disables HTTP keep-alives and |
| // will only use the connection to the server for a single |
| // HTTP request. |
| // |
| // This is unrelated to the similarly named TCP keep-alives. |
| DisableKeepAlives bool |
| |
| // DisableCompression, if true, prevents the Transport from |
| // requesting compression with an "Accept-Encoding: gzip" |
| // request header when the Request contains no existing |
| // Accept-Encoding value. If the Transport requests gzip on |
| // its own and gets a gzipped response, it's transparently |
| // decoded in the Response.Body. However, if the user |
| // explicitly requested gzip it is not automatically |
| // uncompressed. |
| DisableCompression bool |
| |
| // MaxIdleConns controls the maximum number of idle (keep-alive) |
| // connections across all hosts. Zero means no limit. |
| MaxIdleConns int |
| |
| // MaxIdleConnsPerHost, if non-zero, controls the maximum idle |
| // (keep-alive) connections to keep per-host. If zero, |
| // DefaultMaxIdleConnsPerHost is used. |
| MaxIdleConnsPerHost int |
| |
| // MaxConnsPerHost optionally limits the total number of |
| // connections per host, including connections in the dialing, |
| // active, and idle states. On limit violation, dials will block. |
| // |
| // Zero means no limit. |
| MaxConnsPerHost int |
| |
| // IdleConnTimeout is the maximum amount of time an idle |
| // (keep-alive) connection will remain idle before closing |
| // itself. |
| // Zero means no limit. |
| IdleConnTimeout time.Duration |
| |
| // ResponseHeaderTimeout, if non-zero, specifies the amount of |
| // time to wait for a server's response headers after fully |
| // writing the request (including its body, if any). This |
| // time does not include the time to read the response body. |
| ResponseHeaderTimeout time.Duration |
| |
| // ExpectContinueTimeout, if non-zero, specifies the amount of |
| // time to wait for a server's first response headers after fully |
| // writing the request headers if the request has an |
| // "Expect: 100-continue" header. Zero means no timeout and |
| // causes the body to be sent immediately, without |
| // waiting for the server to approve. |
| // This time does not include the time to send the request header. |
| ExpectContinueTimeout time.Duration |
| |
| // TLSNextProto specifies how the Transport switches to an |
| // alternate protocol (such as HTTP/2) after a TLS ALPN |
| // protocol negotiation. If Transport dials a TLS connection |
| // with a non-empty protocol name and TLSNextProto contains a |
| // map entry for that key (such as "h2"), then the func is |
| // called with the request's authority (such as "example.com" |
| // or "example.com:1234") and the TLS connection. The function |
| // must return a RoundTripper that then handles the request. |
| // If TLSNextProto is not nil, HTTP/2 support is not enabled |
| // automatically. |
| // |
| // Historically, TLSNextProto was used to disable HTTP/2 support. |
| // The Transport.Protocols field now provides a simpler way to do this. |
| TLSNextProto map[string]func(authority string, c *tls.Conn) http_RoundTripper |
| |
| // ProxyConnectHeader optionally specifies headers to send to |
| // proxies during CONNECT requests. |
| // To set the header dynamically, see GetProxyConnectHeader. |
| ProxyConnectHeader http_Header |
| |
| // GetProxyConnectHeader optionally specifies a func to return |
| // headers to send to proxyURL during a CONNECT request to the |
| // ip:port target. |
| // If it returns an error, the Transport's RoundTrip fails with |
| // that error. It can return (nil, nil) to not add headers. |
| // If GetProxyConnectHeader is non-nil, ProxyConnectHeader is |
| // ignored. |
| GetProxyConnectHeader func(ctx context.Context, proxyURL *url.URL, target string) (http_Header, error) |
| |
| // MaxResponseHeaderBytes specifies a limit on how many |
| // response bytes are allowed in the server's response |
| // header. |
| // |
| // Zero means to use a default limit. |
| MaxResponseHeaderBytes int64 |
| |
| // WriteBufferSize specifies the size of the write buffer used |
| // when writing to the transport. |
| // If zero, a default (currently 4KB) is used. |
| WriteBufferSize int |
| |
| // ReadBufferSize specifies the size of the read buffer used |
| // when reading from the transport. |
| // If zero, a default (currently 4KB) is used. |
| ReadBufferSize int |
| |
| // nextProtoOnce guards initialization of TLSNextProto and |
| // h2Transport (via onceSetNextProtoDefaults) |
| nextProtoOnce sync.Once |
| closeIdleFunc http_closeIdleConnectionser // non-nil if http2 wired up |
| h2Transport *http_http2Transport |
| h2Config http_http2ExternalTransportConfig |
| h3Transport http_dialClientConner // non-nil if http3 wired up |
| tlsNextProtoWasNil bool // whether TLSNextProto was nil when the Once fired |
| |
| // ForceAttemptHTTP2 controls whether HTTP/2 is enabled when a non-zero |
| // Dial, DialTLS, or DialContext func or TLSClientConfig is provided. |
| // By default, use of any those fields conservatively disables HTTP/2. |
| // To use a custom dialer or TLS config and still attempt HTTP/2 |
| // upgrades, set this to true. |
| ForceAttemptHTTP2 bool |
| |
| // HTTP2 configures HTTP/2 connections. |
| HTTP2 *http_HTTP2Config |
| |
| // Protocols is the set of protocols supported by the transport. |
| // |
| // If Protocols includes UnencryptedHTTP2 and does not include HTTP1, |
| // the transport will use unencrypted HTTP/2 for requests for http:// URLs. |
| // |
| // If Protocols is nil, the default is usually HTTP/1 only. |
| // If ForceAttemptHTTP2 is true, or if TLSNextProto contains an "h2" entry, |
| // the default is HTTP/1 and HTTP/2. |
| Protocols *http_Protocols |
| } |
| |
| func (t *http_Transport) writeBufferSize() int { |
| if t.WriteBufferSize > 0 { |
| return t.WriteBufferSize |
| } |
| return 4 << 10 |
| } |
| |
| func (t *http_Transport) readBufferSize() int { |
| if t.ReadBufferSize > 0 { |
| return t.ReadBufferSize |
| } |
| return 4 << 10 |
| } |
| |
| func (t *http_Transport) maxHeaderResponseSize() int64 { |
| if t.MaxResponseHeaderBytes > 0 { |
| return t.MaxResponseHeaderBytes |
| } |
| return 10 << 20 // conservative default; same as http2 |
| } |
| |
| // Clone returns a deep copy of t's exported fields. |
| func (t *http_Transport) Clone() *http_Transport { |
| t.nextProtoOnce.Do(t.onceSetNextProtoDefaults) |
| t2 := &http_Transport{ |
| Proxy: t.Proxy, |
| OnProxyConnectResponse: t.OnProxyConnectResponse, |
| DialContext: t.DialContext, |
| Dial: t.Dial, |
| DialTLS: t.DialTLS, |
| DialTLSContext: t.DialTLSContext, |
| TLSHandshakeTimeout: t.TLSHandshakeTimeout, |
| DisableKeepAlives: t.DisableKeepAlives, |
| DisableCompression: t.DisableCompression, |
| MaxIdleConns: t.MaxIdleConns, |
| MaxIdleConnsPerHost: t.MaxIdleConnsPerHost, |
| MaxConnsPerHost: t.MaxConnsPerHost, |
| IdleConnTimeout: t.IdleConnTimeout, |
| ResponseHeaderTimeout: t.ResponseHeaderTimeout, |
| ExpectContinueTimeout: t.ExpectContinueTimeout, |
| ProxyConnectHeader: t.ProxyConnectHeader.Clone(), |
| GetProxyConnectHeader: t.GetProxyConnectHeader, |
| MaxResponseHeaderBytes: t.MaxResponseHeaderBytes, |
| ForceAttemptHTTP2: t.ForceAttemptHTTP2, |
| WriteBufferSize: t.WriteBufferSize, |
| ReadBufferSize: t.ReadBufferSize, |
| } |
| if t.TLSClientConfig != nil { |
| t2.TLSClientConfig = t.TLSClientConfig.Clone() |
| } |
| if t.HTTP2 != nil { |
| t2.HTTP2 = &http_HTTP2Config{} |
| *t2.HTTP2 = *t.HTTP2 |
| } |
| if t.Protocols != nil { |
| t2.Protocols = &http_Protocols{} |
| *t2.Protocols = *t.Protocols |
| } |
| if !t.tlsNextProtoWasNil { |
| npm := maps.Clone(t.TLSNextProto) |
| if npm == nil { |
| npm = make(map[string]func(authority string, c *tls.Conn) http_RoundTripper) |
| } |
| t2.TLSNextProto = npm |
| } |
| return t2 |
| } |
| |
| type http_dialClientConner interface { |
| // DialClientConn creates a new client connection to address. |
| // |
| // If proxy is non-nil, the connection should use the provided proxy. |
| // If HTTP/3 proxies are not supported, DialClientConn should return |
| // an error wrapping [errors.ErrUnsupported]. |
| // |
| // The RoundTripper returned by DialClientConn must also implement the |
| // following methods to support [ClientConn] methods of the same name: |
| // Close() error |
| // Err() error |
| // Reserve() error |
| // Release() error |
| // Available() int |
| // InFlight() int |
| // |
| // The client connection should arrange to call internalStateHook |
| // when the connection closes, when requests complete, and when the |
| // connection concurrency limit changes. |
| // |
| // The client connection must call the internal state hook when |
| // the connection state changes asynchronously, such as when a request completes. |
| // |
| // The internal state hook need not be called after synchronous changes |
| // to the state: Close, Reserve, Release, and RoundTrip calls |
| // which don't start a request do not need to call the hook. |
| DialClientConn(ctx context.Context, address string, proxy *url.URL, internalStateHook func()) (http_RoundTripper, error) |
| } |
| |
| type http_closeIdleConnectionser interface { |
| // CloseIdleConnections is called by Transport.CloseIdleConnections. |
| // |
| // We expect to use this on transports supplied by x/net/http2 or x/net/http3. |
| // |
| // The transport will close idle connections created with DialClientConn |
| // before calling this method. The HTTP/3 transport should not attempt to |
| // close idle connections, but may clean up shared resources such as UDP |
| // sockets if no connections remain. |
| CloseIdleConnections() |
| } |
| |
| func (t *http_Transport) hasCustomTLSDialer() bool { |
| return t.DialTLS != nil || t.DialTLSContext != nil |
| } |
| |
| var http_http2client = godebug.New("http2client") |
| |
| // onceSetNextProtoDefaults initializes TLSNextProto. |
| // It must be called via t.nextProtoOnce.Do. |
| func (t *http_Transport) onceSetNextProtoDefaults() { |
| t.tlsNextProtoWasNil = (t.TLSNextProto == nil) |
| if http_http2client.Value() == "0" { |
| http_http2client.IncNonDefault() |
| return |
| } |
| |
| // If they've already configured http2 with |
| // golang.org/x/net/http2 instead of the bundled copy, try to |
| // get at its http2.Transport value (via the "https" |
| // altproto map) so we can call CloseIdleConnections on it if |
| // requested. (Issue 22891) |
| altProto, _ := t.altProto.Load().(map[string]http_RoundTripper) |
| if rv := reflect.ValueOf(altProto["https"]); rv.IsValid() && rv.Type().Kind() == reflect.Struct && rv.Type().NumField() == 1 { |
| if v := rv.Field(0); v.CanInterface() { |
| if h2i, ok := v.Interface().(http_closeIdleConnectionser); ok { |
| t.closeIdleFunc = h2i |
| return |
| } |
| } |
| } |
| |
| if _, ok := t.TLSNextProto["h2"]; ok { |
| // There's an existing HTTP/2 implementation installed. |
| return |
| } |
| protocols := t.protocols() |
| if !protocols.HTTP2() && !protocols.UnencryptedHTTP2() { |
| return |
| } |
| if http_omitBundledHTTP2 { |
| return |
| } |
| |
| t.configureHTTP2(protocols) |
| } |
| |
| func (t *http_Transport) protocols() http_Protocols { |
| if t.Protocols != nil { |
| return *t.Protocols // user-configured set |
| } |
| var p http_Protocols |
| p.SetHTTP1(true) // default always includes HTTP/1 |
| switch { |
| case t.TLSNextProto != nil: |
| // Setting TLSNextProto to an empty map is a documented way |
| // to disable HTTP/2 on a Transport. |
| if t.TLSNextProto["h2"] != nil { |
| p.SetHTTP2(true) |
| } |
| case !t.ForceAttemptHTTP2 && (t.TLSClientConfig != nil || t.Dial != nil || t.DialContext != nil || t.hasCustomTLSDialer()): |
| // Be conservative and don't automatically enable |
| // http2 if they've specified a custom TLS config or |
| // custom dialers. Let them opt-in themselves via |
| // Transport.Protocols.SetHTTP2(true) so we don't surprise them |
| // by modifying their tls.Config. Issue 14275. |
| // However, if ForceAttemptHTTP2 is true, it overrides the above checks. |
| case http_http2client.Value() == "0": |
| default: |
| p.SetHTTP2(true) |
| } |
| return p |
| } |
| |
| // ProxyFromEnvironment returns the URL of the proxy to use for a |
| // given request, as indicated by the environment variables |
| // HTTP_PROXY, HTTPS_PROXY and NO_PROXY (or the lowercase versions |
| // thereof, which take precedence over the uppercase versions). |
| // Requests use the proxy from the environment variable |
| // matching their scheme, unless excluded by NO_PROXY. |
| // |
| // The environment values may be either a complete URL or a |
| // "host[:port]", in which case the "http" scheme is assumed. |
| // An error is returned if the value is a different form. |
| // |
| // A nil URL and nil error are returned if no proxy is defined in the |
| // environment, or a proxy should not be used for the given request, |
| // as defined by NO_PROXY. |
| // |
| // As a special case, if req.URL.Host is "localhost" (with or without |
| // a port number), then a nil URL and nil error will be returned. |
| func http_ProxyFromEnvironment(req *http_Request) (*url.URL, error) { |
| return http_envProxyFunc()(req.URL) |
| } |
| |
| // ProxyURL returns a proxy function (for use in a [Transport]) |
| // that always returns the same URL. |
| func http_ProxyURL(fixedURL *url.URL) func(*http_Request) (*url.URL, error) { |
| return func(*http_Request) (*url.URL, error) { |
| return fixedURL, nil |
| } |
| } |
| |
| // transportRequest is a wrapper around a *Request that adds |
| // optional extra headers to write and stores any error to return |
| // from roundTrip. |
| type http_transportRequest struct { |
| *http_Request // original request, not to be mutated |
| extra http_Header // extra headers to write, or nil |
| trace *httptrace.ClientTrace // optional |
| |
| ctx context.Context // canceled when we are done with the request |
| cancel context.CancelCauseFunc |
| |
| mu sync.Mutex // guards err |
| err error // first setError value for mapRoundTripError to consider |
| } |
| |
| func (tr *http_transportRequest) extraHeaders() http_Header { |
| if tr.extra == nil { |
| tr.extra = make(http_Header) |
| } |
| return tr.extra |
| } |
| |
| func (tr *http_transportRequest) setError(err error) { |
| tr.mu.Lock() |
| if tr.err == nil { |
| tr.err = err |
| } |
| tr.mu.Unlock() |
| } |
| |
| // useRegisteredProtocol reports whether an alternate protocol (as registered |
| // with Transport.RegisterProtocol) should be respected for this request. |
| func (t *http_Transport) useRegisteredProtocol(req *http_Request) bool { |
| if req.URL.Scheme == "https" && req.requiresHTTP1() { |
| // If this request requires HTTP/1, don't use the |
| // "https" alternate protocol, which is used by the |
| // HTTP/2 code to take over requests if there's an |
| // existing cached HTTP/2 connection. |
| return false |
| } |
| return true |
| } |
| |
| // alternateRoundTripper returns the alternate RoundTripper to use |
| // for this request if the Request's URL scheme requires one, |
| // or nil for the normal case of using the Transport. |
| func (t *http_Transport) alternateRoundTripper(req *http_Request) http_RoundTripper { |
| if !t.useRegisteredProtocol(req) { |
| return nil |
| } |
| if req.URL.Scheme == "https" && t.h2Config != nil && t.h2Config.ExternalRoundTrip() { |
| // This Transport has been configured to use an x/net/http2 Transport |
| // with a user-provided ClientConnPool. We're going to pass off the |
| // RoundTrip to x/net/http2 so it can use that pool. |
| // |
| // The ClientConnPool API is deprecated, but we're doing our best here |
| // to continue supporting any users who are using it. |
| return t.h2Config |
| } |
| altProto, _ := t.altProto.Load().(map[string]http_RoundTripper) |
| return altProto[req.URL.Scheme] |
| } |
| |
| func http_validateHeaders(hdrs http_Header) string { |
| for k, vv := range hdrs { |
| if !httpguts.ValidHeaderFieldName(k) { |
| return fmt.Sprintf("field name %q", k) |
| } |
| for _, v := range vv { |
| if !httpguts.ValidHeaderFieldValue(v) { |
| // Don't include the value in the error, |
| // because it may be sensitive. |
| return fmt.Sprintf("field value for %q", k) |
| } |
| } |
| } |
| return "" |
| } |
| |
| // roundTrip implements a RoundTripper over HTTP. |
| func (t *http_Transport) roundTrip(req *http_Request) (_ *http_Response, err error) { |
| t.nextProtoOnce.Do(t.onceSetNextProtoDefaults) |
| ctx := req.Context() |
| trace := httptrace.ContextClientTrace(ctx) |
| |
| if req.URL == nil { |
| req.closeBody() |
| return nil, errors.New("http: nil Request.URL") |
| } |
| if req.Header == nil { |
| req.closeBody() |
| return nil, errors.New("http: nil Request.Header") |
| } |
| scheme := req.URL.Scheme |
| isHTTP := scheme == "http" || scheme == "https" |
| if isHTTP { |
| // Validate the outgoing headers. |
| if err := http_validateHeaders(req.Header); err != "" { |
| req.closeBody() |
| return nil, fmt.Errorf("net/http: invalid header %s", err) |
| } |
| |
| // Validate the outgoing trailers too. |
| if err := http_validateHeaders(req.Trailer); err != "" { |
| req.closeBody() |
| return nil, fmt.Errorf("net/http: invalid trailer %s", err) |
| } |
| } |
| |
| origReq := req |
| req = http_setupRewindBody(req) |
| |
| if altRT := t.alternateRoundTripper(req); altRT != nil { |
| if resp, err := altRT.RoundTrip(req); err != http_ErrSkipAltProtocol { |
| return resp, err |
| } |
| var err error |
| req, err = http_rewindBody(req) |
| if err != nil { |
| return nil, err |
| } |
| } |
| if !isHTTP { |
| req.closeBody() |
| return nil, http_badStringError("unsupported protocol scheme", scheme) |
| } |
| if req.Method != "" && !http_validMethod(req.Method) { |
| req.closeBody() |
| return nil, fmt.Errorf("net/http: invalid method %q", req.Method) |
| } |
| if req.URL.Host == "" { |
| req.closeBody() |
| return nil, errors.New("http: no Host in request URL") |
| } |
| |
| // Transport request context. |
| // |
| // If RoundTrip returns an error, it cancels this context before returning. |
| // |
| // If RoundTrip returns no error: |
| // - For an HTTP/1 request, persistConn.readLoop cancels this context |
| // after reading the request body. |
| // - For an HTTP/2 request, RoundTrip cancels this context after the HTTP/2 |
| // RoundTripper returns. |
| ctx, cancel := context.WithCancelCause(req.Context()) |
| |
| // Convert Request.Cancel into context cancellation. |
| if origReq.Cancel != nil { |
| go http_awaitLegacyCancel(ctx, cancel, origReq) |
| } |
| |
| // Convert Transport.CancelRequest into context cancellation. |
| // |
| // This is lamentably expensive. CancelRequest has been deprecated for a long time |
| // and doesn't work on HTTP/2 requests. Perhaps we should drop support for it entirely. |
| cancel = t.prepareTransportCancel(origReq, cancel) |
| |
| defer func() { |
| if err != nil { |
| cancel(err) |
| } |
| }() |
| |
| for { |
| select { |
| case <-ctx.Done(): |
| req.closeBody() |
| return nil, context.Cause(ctx) |
| default: |
| } |
| |
| // treq gets modified by roundTrip, so we need to recreate for each retry. |
| treq := &http_transportRequest{http_Request: req, trace: trace, ctx: ctx, cancel: cancel} |
| cm, err := t.connectMethodForRequest(treq) |
| if err != nil { |
| req.closeBody() |
| return nil, err |
| } |
| |
| // Get the cached or newly-created connection to either the |
| // host (for http or https), the http proxy, or the http proxy |
| // pre-CONNECTed to https server. In any case, we'll be ready |
| // to send it requests. |
| pconn, err := t.getConn(treq, cm) |
| if err != nil { |
| req.closeBody() |
| return nil, err |
| } |
| |
| var resp *http_Response |
| if pconn.alt != nil { |
| // HTTP/2 path. |
| resp, err = pconn.alt.RoundTrip(req) |
| } else { |
| resp, err = pconn.roundTrip(treq) |
| } |
| if err == nil { |
| if pconn.alt != nil { |
| // HTTP/2 requests are not cancelable with CancelRequest, |
| // so we have no further need for the request context. |
| // |
| // On the HTTP/1 path, roundTrip takes responsibility for |
| // canceling the context after the response body is read. |
| cancel(http_errRequestDone) |
| } |
| resp.Request = origReq |
| return resp, nil |
| } |
| |
| // Failed. Clean up and determine whether to retry. |
| if http_http2isNoCachedConnError(err) { |
| if t.removeIdleConn(pconn) { |
| t.decConnsPerHost(pconn.cacheKey) |
| } |
| } else if !pconn.shouldRetryRequest(req, err) { |
| // Issue 16465: return underlying net.Conn.Read error from peek, |
| // as we've historically done. |
| if e, ok := err.(http_nothingWrittenError); ok { |
| err = e.error |
| } |
| if e, ok := err.(http_transportReadFromServerError); ok { |
| err = e.err |
| } |
| if b, ok := req.Body.(*http_readTrackingBody); ok && !b.didClose.Load() { |
| // Issue 49621: Close the request body if pconn.roundTrip |
| // didn't do so already. This can happen if the pconn |
| // write loop exits without reading the write request. |
| req.closeBody() |
| } |
| return nil, err |
| } |
| http_testHookRoundTripRetried() |
| |
| // Rewind the body if we're able to. |
| req, err = http_rewindBody(req) |
| if err != nil { |
| return nil, err |
| } |
| } |
| } |
| |
| func http_http2isNoCachedConnError(err error) bool { |
| _, ok := err.(interface{ IsHTTP2NoCachedConnError() }) |
| return ok |
| } |
| |
| func http_awaitLegacyCancel(ctx context.Context, cancel context.CancelCauseFunc, req *http_Request) { |
| select { |
| case <-req.Cancel: |
| cancel(http_errRequestCanceled) |
| case <-ctx.Done(): |
| } |
| } |
| |
| var http_errCannotRewind = errors.New("net/http: cannot rewind body after connection loss") |
| |
| type http_readTrackingBody struct { |
| io.ReadCloser |
| didRead bool // not atomic.Bool because only one goroutine (the user's) should be accessing |
| didClose atomic.Bool |
| } |
| |
| func (r *http_readTrackingBody) Read(data []byte) (int, error) { |
| r.didRead = true |
| return r.ReadCloser.Read(data) |
| } |
| |
| func (r *http_readTrackingBody) Close() error { |
| if !r.didClose.CompareAndSwap(false, true) { |
| return nil |
| } |
| return r.ReadCloser.Close() |
| } |
| |
| // setupRewindBody returns a new request with a custom body wrapper |
| // that can report whether the body needs rewinding. |
| // This lets rewindBody avoid an error result when the request |
| // does not have GetBody but the body hasn't been read at all yet. |
| func http_setupRewindBody(req *http_Request) *http_Request { |
| if req.Body == nil || req.Body == http_NoBody { |
| return req |
| } |
| newReq := *req |
| newReq.Body = &http_readTrackingBody{ReadCloser: req.Body} |
| return &newReq |
| } |
| |
| // rewindBody returns a new request with the body rewound. |
| // It returns req unmodified if the body does not need rewinding. |
| // rewindBody takes care of closing req.Body when appropriate |
| // (in all cases except when rewindBody returns req unmodified). |
| func http_rewindBody(req *http_Request) (rewound *http_Request, err error) { |
| if req.Body == nil || req.Body == http_NoBody || (!req.Body.(*http_readTrackingBody).didRead && !req.Body.(*http_readTrackingBody).didClose.Load()) { |
| return req, nil // nothing to rewind |
| } |
| if !req.Body.(*http_readTrackingBody).didClose.Load() { |
| req.closeBody() |
| } |
| if req.GetBody == nil { |
| return nil, http_errCannotRewind |
| } |
| body, err := req.GetBody() |
| if err != nil { |
| return nil, err |
| } |
| newReq := *req |
| newReq.Body = &http_readTrackingBody{ReadCloser: body} |
| return &newReq, nil |
| } |
| |
| // shouldRetryRequest reports whether we should retry sending a failed |
| // HTTP request on a new connection. The non-nil input error is the |
| // error from roundTrip. |
| func (pc *http_persistConn) shouldRetryRequest(req *http_Request, err error) bool { |
| if http_http2isNoCachedConnError(err) { |
| // Issue 16582: if the user started a bunch of |
| // requests at once, they can all pick the same conn |
| // and violate the server's max concurrent streams. |
| // Instead, match the HTTP/1 behavior for now and dial |
| // again to get a new TCP connection, rather than failing |
| // this request. |
| return true |
| } |
| if err == http_errMissingHost { |
| // User error. |
| return false |
| } |
| if !pc.isReused() { |
| // This was a fresh connection. There's no reason the server |
| // should've hung up on us. |
| // |
| // Also, if we retried now, we could loop forever |
| // creating new connections and retrying if the server |
| // is just hanging up on us because it doesn't like |
| // our request (as opposed to sending an error). |
| return false |
| } |
| if _, ok := err.(http_nothingWrittenError); ok { |
| // We never wrote anything, so it's safe to retry, if there's no body or we |
| // can "rewind" the body with GetBody. |
| return req.outgoingLength() == 0 || req.GetBody != nil |
| } |
| if !req.isReplayable() { |
| // Don't retry non-idempotent requests. |
| return false |
| } |
| if _, ok := err.(http_transportReadFromServerError); ok { |
| // We got some non-EOF net.Conn.Read failure reading |
| // the 1st response byte from the server. |
| return true |
| } |
| if err == http_errServerClosedIdle { |
| // The server replied with io.EOF while we were trying to |
| // read the response. Probably an unfortunately keep-alive |
| // timeout, just as the client was writing a request. |
| return true |
| } |
| return false // conservatively |
| } |
| |
| // ErrSkipAltProtocol is a sentinel error value defined by Transport.RegisterProtocol. |
| var http_ErrSkipAltProtocol = internal.ErrSkipAltProtocol |
| |
| // RegisterProtocol registers a new protocol with scheme. |
| // The [Transport] will pass requests using the given scheme to rt. |
| // It is rt's responsibility to simulate HTTP request semantics. |
| // |
| // RegisterProtocol can be used by other packages to provide |
| // implementations of protocol schemes like "ftp" or "file". |
| // |
| // If rt.RoundTrip returns [ErrSkipAltProtocol], the Transport will |
| // handle the [Transport.RoundTrip] itself for that one request, as if the |
| // protocol were not registered. |
| func (t *http_Transport) RegisterProtocol(scheme string, rt http_RoundTripper) { |
| if err := t.registerProtocol(scheme, rt); err != nil { |
| panic(err) |
| } |
| } |
| |
| func (t *http_Transport) registerProtocol(scheme string, rt http_RoundTripper) error { |
| t.altMu.Lock() |
| defer t.altMu.Unlock() |
| |
| if scheme == "http/2" { |
| if t.h2Config != nil { |
| panic("http: HTTP/2 Transport already registered") |
| } |
| var ok bool |
| if t.h2Config, ok = rt.(http_http2ExternalTransportConfig); !ok { |
| panic("http: HTTP/2 configuration does not implement ExternalTransportConfig") |
| } |
| t.h2Config.Registered(t) |
| } |
| |
| if scheme == "http/3" { |
| var ok bool |
| if t.h3Transport, ok = rt.(http_dialClientConner); !ok { |
| panic("http: HTTP/3 RoundTripper does not implement DialClientConn") |
| } |
| } |
| |
| oldMap, _ := t.altProto.Load().(map[string]http_RoundTripper) |
| if _, exists := oldMap[scheme]; exists { |
| return errors.New("protocol " + scheme + " already registered") |
| } |
| newMap := maps.Clone(oldMap) |
| if newMap == nil { |
| newMap = make(map[string]http_RoundTripper) |
| } |
| newMap[scheme] = rt |
| t.altProto.Store(newMap) |
| return nil |
| } |
| |
| // CloseIdleConnections closes any connections which were previously |
| // connected from previous requests but are now sitting idle in |
| // a "keep-alive" state. It does not interrupt any connections currently |
| // in use. |
| func (t *http_Transport) CloseIdleConnections() { |
| t.nextProtoOnce.Do(t.onceSetNextProtoDefaults) |
| t.idleMu.Lock() |
| m := t.idleConn |
| t.idleConn = nil |
| t.closeIdle = true // close newly idle connections |
| t.idleLRU = http_connLRU{} |
| t.idleMu.Unlock() |
| for _, conns := range m { |
| for _, pconn := range conns { |
| pconn.close(http_errCloseIdleConns) |
| } |
| } |
| t.connsPerHostMu.Lock() |
| t.dialsInProgress.all(func(w *http_wantConn) { |
| if w.cancelCtx != nil && !w.waiting() { |
| w.cancelCtx() |
| } |
| }) |
| t.connsPerHostMu.Unlock() |
| |
| // Tell various associated transports to close their connections. |
| |
| // net/http/internal/http2 transport. This is the common case for HTTP/2 users. |
| if tr2 := t.h2Transport; tr2 != nil { |
| tr2.CloseIdleConnections() |
| } |
| // Probably an older x/net/http2 transport registered via Transport.RegisterProtocol. |
| // This is a legacy path; modern users just use internal/http2. |
| // (Note that we don't use this path when x/net/http2 wraps the net/http transport; |
| // this is supporting pre-wrapping x/net/http2.) |
| if t2 := t.closeIdleFunc; t2 != nil { |
| t2.CloseIdleConnections() |
| } |
| // HTTP/3 transport, probably from x/net/http3. |
| if cc, ok := t.h3Transport.(http_closeIdleConnectionser); ok { |
| cc.CloseIdleConnections() |
| } |
| } |
| |
| // prepareTransportCancel sets up state to convert Transport.CancelRequest into context cancellation. |
| func (t *http_Transport) prepareTransportCancel(req *http_Request, origCancel context.CancelCauseFunc) context.CancelCauseFunc { |
| // Historically, RoundTrip has not modified the Request in any way. |
| // We could avoid the need to keep a map of all in-flight requests by adding |
| // a field to the Request containing its cancel func, and setting that field |
| // while the request is in-flight. Callers aren't supposed to reuse a Request |
| // until after the response body is closed, so this wouldn't violate any |
| // concurrency guarantees. |
| cancel := func(err error) { |
| origCancel(err) |
| t.reqMu.Lock() |
| delete(t.reqCanceler, req) |
| t.reqMu.Unlock() |
| } |
| t.reqMu.Lock() |
| if t.reqCanceler == nil { |
| t.reqCanceler = make(map[*http_Request]context.CancelCauseFunc) |
| } |
| t.reqCanceler[req] = cancel |
| t.reqMu.Unlock() |
| return cancel |
| } |
| |
| // CancelRequest cancels an in-flight request by closing its connection. |
| // CancelRequest should only be called after [Transport.RoundTrip] has returned. |
| // |
| // Deprecated: Use [Request.WithContext] to create a request with a |
| // cancelable context instead. CancelRequest cannot cancel HTTP/2 |
| // requests. This may become a no-op in a future release of Go. |
| func (t *http_Transport) CancelRequest(req *http_Request) { |
| t.reqMu.Lock() |
| cancel := t.reqCanceler[req] |
| t.reqMu.Unlock() |
| if cancel != nil { |
| cancel(http_errRequestCanceled) |
| } |
| } |
| |
| // |
| // Private implementation past this point. |
| // |
| |
| var ( |
| http_envProxyOnce sync.Once |
| http_envProxyFuncValue func(*url.URL) (*url.URL, error) |
| ) |
| |
| // envProxyFunc returns a function that reads the |
| // environment variable to determine the proxy address. |
| func http_envProxyFunc() func(*url.URL) (*url.URL, error) { |
| http_envProxyOnce.Do(func() { |
| http_envProxyFuncValue = httpproxy.FromEnvironment().ProxyFunc() |
| }) |
| return http_envProxyFuncValue |
| } |
| |
| // resetProxyConfig is used by tests. |
| func http_resetProxyConfig() { |
| http_envProxyOnce = sync.Once{} |
| http_envProxyFuncValue = nil |
| } |
| |
| func (t *http_Transport) connectMethodForRequest(treq *http_transportRequest) (cm http_connectMethod, err error) { |
| cm.targetScheme = treq.URL.Scheme |
| cm.targetAddr = http_canonicalAddr(treq.URL) |
| if t.Proxy != nil { |
| cm.proxyURL, err = t.Proxy(treq.http_Request) |
| } |
| cm.onlyH1 = treq.requiresHTTP1() |
| return cm, err |
| } |
| |
| // proxyAuth returns the Proxy-Authorization header to set |
| // on requests, if applicable. |
| func (cm *http_connectMethod) proxyAuth() string { |
| if cm.proxyURL == nil { |
| return "" |
| } |
| if u := cm.proxyURL.User; u != nil { |
| username := u.Username() |
| password, _ := u.Password() |
| return "Basic " + http_basicAuth(username, password) |
| } |
| return "" |
| } |
| |
| // error values for debugging and testing, not seen by users. |
| var ( |
| http_errKeepAlivesDisabled = errors.New("http: putIdleConn: keep alives disabled") |
| http_errConnBroken = errors.New("http: putIdleConn: connection is in bad state") |
| http_errCloseIdle = errors.New("http: putIdleConn: CloseIdleConnections was called") |
| http_errTooManyIdle = errors.New("http: putIdleConn: too many idle connections") |
| http_errTooManyIdleHost = errors.New("http: putIdleConn: too many idle connections for host") |
| http_errCloseIdleConns = errors.New("http: CloseIdleConnections called") |
| http_errReadLoopExiting = errors.New("http: persistConn.readLoop exiting") |
| http_errIdleConnTimeout = errors.New("http: idle connection timeout") |
| |
| // errServerClosedIdle is not seen by users for idempotent requests, but may be |
| // seen by a user if the server shuts down an idle connection and sends its FIN |
| // in flight with already-written POST body bytes from the client. |
| // See https://github.com/golang/go/issues/19943#issuecomment-355607646 |
| http_errServerClosedIdle = errors.New("http: server closed idle connection") |
| ) |
| |
| // transportReadFromServerError is used by Transport.readLoop when the |
| // 1 byte peek read fails and we're actually anticipating a response. |
| // Usually this is just due to the inherent keep-alive shut down race, |
| // where the server closed the connection at the same time the client |
| // wrote. The underlying err field is usually io.EOF or some |
| // ECONNRESET sort of thing which varies by platform. But it might be |
| // the user's custom net.Conn.Read error too, so we carry it along for |
| // them to return from Transport.RoundTrip. |
| type http_transportReadFromServerError struct { |
| err error |
| } |
| |
| func (e http_transportReadFromServerError) Unwrap() error { return e.err } |
| |
| func (e http_transportReadFromServerError) Error() string { |
| return fmt.Sprintf("net/http: Transport failed to read from server: %v", e.err) |
| } |
| |
| func (t *http_Transport) putOrCloseIdleConn(pconn *http_persistConn) { |
| if err := t.tryPutIdleConn(pconn); err != nil { |
| pconn.close(err) |
| } |
| } |
| |
| func (t *http_Transport) maxIdleConnsPerHost() int { |
| if v := t.MaxIdleConnsPerHost; v != 0 { |
| return v |
| } |
| return http_DefaultMaxIdleConnsPerHost |
| } |
| |
| // tryPutIdleConn adds pconn to the list of idle persistent connections awaiting |
| // a new request. |
| // If pconn is no longer needed or not in a good state, tryPutIdleConn returns |
| // an error explaining why it wasn't registered. |
| // tryPutIdleConn does not close pconn. Use putOrCloseIdleConn instead for that. |
| func (t *http_Transport) tryPutIdleConn(pconn *http_persistConn) error { |
| if t.DisableKeepAlives || t.MaxIdleConnsPerHost < 0 { |
| return http_errKeepAlivesDisabled |
| } |
| if pconn.isBroken() { |
| return http_errConnBroken |
| } |
| pconn.markReused() |
| if pconn.isClientConn { |
| // internalStateHook is always set for conns created by NewClientConn. |
| defer pconn.internalStateHook() |
| pconn.mu.Lock() |
| defer pconn.mu.Unlock() |
| if !pconn.inFlight { |
| panic("pconn is not in flight") |
| } |
| pconn.inFlight = false |
| select { |
| case pconn.availch <- struct{}{}: |
| default: |
| panic("unable to make pconn available") |
| } |
| return nil |
| } |
| |
| t.idleMu.Lock() |
| defer t.idleMu.Unlock() |
| |
| // HTTP/2 (pconn.alt != nil) connections do not come out of the idle list, |
| // because multiple goroutines can use them simultaneously. |
| // If this is an HTTP/2 connection being “returned,” we're done. |
| if pconn.alt != nil && t.idleLRU.m[pconn] != nil { |
| return nil |
| } |
| |
| // Deliver pconn to goroutine waiting for idle connection, if any. |
| // (They may be actively dialing, but this conn is ready first. |
| // Chrome calls this socket late binding. |
| // See https://www.chromium.org/developers/design-documents/network-stack#TOC-Connection-Management.) |
| key := pconn.cacheKey |
| if q, ok := t.idleConnWait[key]; ok { |
| done := false |
| if pconn.alt == nil { |
| // HTTP/1. |
| // Loop over the waiting list until we find a w that isn't done already, and hand it pconn. |
| for q.len() > 0 { |
| w := q.popFront() |
| if w.tryDeliver(pconn, nil, time.Time{}) { |
| done = true |
| break |
| } |
| } |
| } else { |
| // HTTP/2. |
| // Can hand the same pconn to everyone in the waiting list, |
| // and we still won't be done: we want to put it in the idle |
| // list unconditionally, for any future clients too. |
| for q.len() > 0 { |
| w := q.popFront() |
| w.tryDeliver(pconn, nil, time.Time{}) |
| } |
| } |
| if q.len() == 0 { |
| delete(t.idleConnWait, key) |
| } else { |
| t.idleConnWait[key] = q |
| } |
| if done { |
| return nil |
| } |
| } |
| |
| if t.closeIdle { |
| return http_errCloseIdle |
| } |
| if t.idleConn == nil { |
| t.idleConn = make(map[http_connectMethodKey][]*http_persistConn) |
| } |
| idles := t.idleConn[key] |
| if len(idles) >= t.maxIdleConnsPerHost() { |
| return http_errTooManyIdleHost |
| } |
| for _, exist := range idles { |
| if exist == pconn { |
| log.Fatalf("dup idle pconn %p in freelist", pconn) |
| } |
| } |
| t.idleConn[key] = append(idles, pconn) |
| t.idleLRU.add(pconn) |
| if t.MaxIdleConns != 0 && t.idleLRU.len() > t.MaxIdleConns { |
| oldest := t.idleLRU.removeOldest() |
| oldest.close(http_errTooManyIdle) |
| t.removeIdleConnLocked(oldest) |
| } |
| |
| // Set idle timer, but only for HTTP/1 (pconn.alt == nil). |
| // The HTTP/2 implementation manages the idle timer itself |
| // (see idleConnTimeout in h2_bundle.go). |
| if t.IdleConnTimeout > 0 && pconn.alt == nil { |
| if pconn.idleTimer != nil { |
| pconn.idleTimer.Reset(t.IdleConnTimeout) |
| } else { |
| pconn.idleTimer = time.AfterFunc(t.IdleConnTimeout, pconn.closeConnIfStillIdle) |
| } |
| } |
| pconn.idleAt = time.Now() |
| return nil |
| } |
| |
| // queueForIdleConn queues w to receive the next idle connection for w.cm. |
| // As an optimization hint to the caller, queueForIdleConn reports whether |
| // it successfully delivered an already-idle connection. |
| func (t *http_Transport) queueForIdleConn(w *http_wantConn) (delivered bool) { |
| if t.DisableKeepAlives { |
| return false |
| } |
| |
| t.idleMu.Lock() |
| defer t.idleMu.Unlock() |
| |
| // Stop closing connections that become idle - we might want one. |
| // (That is, undo the effect of t.CloseIdleConnections.) |
| t.closeIdle = false |
| |
| if w == nil { |
| // Happens in test hook. |
| return false |
| } |
| |
| // If IdleConnTimeout is set, calculate the oldest |
| // persistConn.idleAt time we're willing to use a cached idle |
| // conn. |
| var oldTime time.Time |
| if t.IdleConnTimeout > 0 { |
| oldTime = time.Now().Add(-t.IdleConnTimeout) |
| } |
| |
| // Look for most recently-used idle connection. |
| if list, ok := t.idleConn[w.key]; ok { |
| stop := false |
| delivered := false |
| for len(list) > 0 && !stop { |
| pconn := list[len(list)-1] |
| |
| // See whether this connection has been idle too long, considering |
| // only the wall time (the Round(0)), in case this is a laptop or VM |
| // coming out of suspend with previously cached idle connections. |
| tooOld := !oldTime.IsZero() && pconn.idleAt.Round(0).Before(oldTime) |
| if tooOld { |
| // Async cleanup. Launch in its own goroutine (as if a |
| // time.AfterFunc called it); it acquires idleMu, which we're |
| // holding, and does a synchronous net.Conn.Close. |
| go pconn.closeConnIfStillIdle() |
| } |
| if pconn.isBroken() || tooOld { |
| // If either persistConn.readLoop has marked the connection |
| // broken, but Transport.removeIdleConn has not yet removed it |
| // from the idle list, or if this persistConn is too old (it was |
| // idle too long), then ignore it and look for another. In both |
| // cases it's already in the process of being closed. |
| list = list[:len(list)-1] |
| continue |
| } |
| delivered = w.tryDeliver(pconn, nil, pconn.idleAt) |
| if delivered { |
| if pconn.alt != nil { |
| // HTTP/2: multiple clients can share pconn. |
| // Leave it in the list. |
| } else { |
| // HTTP/1: only one client can use pconn. |
| // Remove it from the list. |
| t.idleLRU.remove(pconn) |
| list = list[:len(list)-1] |
| } |
| } |
| stop = true |
| } |
| if len(list) > 0 { |
| t.idleConn[w.key] = list |
| } else { |
| delete(t.idleConn, w.key) |
| } |
| if stop { |
| return delivered |
| } |
| } |
| |
| // Register to receive next connection that becomes idle. |
| if t.idleConnWait == nil { |
| t.idleConnWait = make(map[http_connectMethodKey]http_wantConnQueue) |
| } |
| q := t.idleConnWait[w.key] |
| q.cleanFrontNotWaiting() |
| q.pushBack(w) |
| t.idleConnWait[w.key] = q |
| return false |
| } |
| |
| // removeIdleConn marks pconn as dead. |
| func (t *http_Transport) removeIdleConn(pconn *http_persistConn) bool { |
| if pconn.isClientConn { |
| return true |
| } |
| t.idleMu.Lock() |
| defer t.idleMu.Unlock() |
| return t.removeIdleConnLocked(pconn) |
| } |
| |
| // t.idleMu must be held. |
| func (t *http_Transport) removeIdleConnLocked(pconn *http_persistConn) bool { |
| if pconn.idleTimer != nil { |
| pconn.idleTimer.Stop() |
| } |
| t.idleLRU.remove(pconn) |
| key := pconn.cacheKey |
| pconns := t.idleConn[key] |
| var removed bool |
| switch len(pconns) { |
| case 0: |
| // Nothing |
| case 1: |
| if pconns[0] == pconn { |
| delete(t.idleConn, key) |
| removed = true |
| } |
| default: |
| for i, v := range pconns { |
| if v != pconn { |
| continue |
| } |
| // Slide down, keeping most recently-used |
| // conns at the end. |
| copy(pconns[i:], pconns[i+1:]) |
| t.idleConn[key] = pconns[:len(pconns)-1] |
| removed = true |
| break |
| } |
| } |
| return removed |
| } |
| |
| var http_zeroDialer net.Dialer |
| |
| func (t *http_Transport) dial(ctx context.Context, network, addr string) (net.Conn, error) { |
| if t.DialContext != nil { |
| c, err := t.DialContext(ctx, network, addr) |
| if c == nil && err == nil { |
| err = errors.New("net/http: Transport.DialContext hook returned (nil, nil)") |
| } |
| return c, err |
| } |
| if t.Dial != nil { |
| c, err := t.Dial(network, addr) |
| if c == nil && err == nil { |
| err = errors.New("net/http: Transport.Dial hook returned (nil, nil)") |
| } |
| return c, err |
| } |
| return http_zeroDialer.DialContext(ctx, network, addr) |
| } |
| |
| // A wantConn records state about a wanted connection |
| // (that is, an active call to getConn). |
| // The conn may be gotten by dialing or by finding an idle connection, |
| // or a cancellation may make the conn no longer wanted. |
| // These three options are racing against each other and use |
| // wantConn to coordinate and agree about the winning outcome. |
| type http_wantConn struct { |
| cm http_connectMethod |
| key http_connectMethodKey // cm.key() |
| |
| // hooks for testing to know when dials are done |
| // beforeDial is called in the getConn goroutine when the dial is queued. |
| // afterDial is called when the dial is completed or canceled. |
| beforeDial func() |
| afterDial func() |
| |
| mu sync.Mutex // protects ctx, done and sending of the result |
| ctx context.Context // context for dial, cleared after delivered or canceled |
| cancelCtx context.CancelFunc |
| done bool // true after delivered or canceled |
| result chan http_connOrError // channel to deliver connection or error |
| } |
| |
| type http_connOrError struct { |
| pc *http_persistConn |
| err error |
| idleAt time.Time |
| } |
| |
| // waiting reports whether w is still waiting for an answer (connection or error). |
| func (w *http_wantConn) waiting() bool { |
| w.mu.Lock() |
| defer w.mu.Unlock() |
| |
| return !w.done |
| } |
| |
| // getCtxForDial returns context for dial or nil if connection was delivered or canceled. |
| func (w *http_wantConn) getCtxForDial() context.Context { |
| w.mu.Lock() |
| defer w.mu.Unlock() |
| |
| return w.ctx |
| } |
| |
| // tryDeliver attempts to deliver pc, err to w and reports whether it succeeded. |
| func (w *http_wantConn) tryDeliver(pc *http_persistConn, err error, idleAt time.Time) bool { |
| w.mu.Lock() |
| defer w.mu.Unlock() |
| |
| if w.done { |
| return false |
| } |
| if (pc == nil) == (err == nil) { |
| panic("net/http: internal error: misuse of tryDeliver") |
| } |
| w.ctx = nil |
| w.done = true |
| |
| w.result <- http_connOrError{pc: pc, err: err, idleAt: idleAt} |
| close(w.result) |
| |
| return true |
| } |
| |
| // cancel marks w as no longer wanting a result (for example, due to cancellation). |
| // If a connection has been delivered already, cancel returns it with t.putOrCloseIdleConn. |
| func (w *http_wantConn) cancel(t *http_Transport) { |
| w.mu.Lock() |
| var pc *http_persistConn |
| if w.done { |
| if r, ok := <-w.result; ok { |
| pc = r.pc |
| } |
| } else { |
| close(w.result) |
| } |
| w.ctx = nil |
| w.done = true |
| w.mu.Unlock() |
| |
| // HTTP/2 connections (pc.alt != nil) aren't removed from the idle pool on use, |
| // and should not be added back here. If the pconn isn't in the idle pool, |
| // it's because we removed it due to an error. |
| if pc != nil && pc.alt == nil { |
| t.putOrCloseIdleConn(pc) |
| } |
| } |
| |
| // A wantConnQueue is a queue of wantConns. |
| type http_wantConnQueue struct { |
| // This is a queue, not a deque. |
| // It is split into two stages - head[headPos:] and tail. |
| // popFront is trivial (headPos++) on the first stage, and |
| // pushBack is trivial (append) on the second stage. |
| // If the first stage is empty, popFront can swap the |
| // first and second stages to remedy the situation. |
| // |
| // This two-stage split is analogous to the use of two lists |
| // in Okasaki's purely functional queue but without the |
| // overhead of reversing the list when swapping stages. |
| head []*http_wantConn |
| headPos int |
| tail []*http_wantConn |
| } |
| |
| // len returns the number of items in the queue. |
| func (q *http_wantConnQueue) len() int { |
| return len(q.head) - q.headPos + len(q.tail) |
| } |
| |
| // pushBack adds w to the back of the queue. |
| func (q *http_wantConnQueue) pushBack(w *http_wantConn) { |
| q.tail = append(q.tail, w) |
| } |
| |
| // popFront removes and returns the wantConn at the front of the queue. |
| func (q *http_wantConnQueue) popFront() *http_wantConn { |
| if q.headPos >= len(q.head) { |
| if len(q.tail) == 0 { |
| return nil |
| } |
| // Pick up tail as new head, clear tail. |
| q.head, q.headPos, q.tail = q.tail, 0, q.head[:0] |
| } |
| w := q.head[q.headPos] |
| q.head[q.headPos] = nil |
| q.headPos++ |
| return w |
| } |
| |
| // peekFront returns the wantConn at the front of the queue without removing it. |
| func (q *http_wantConnQueue) peekFront() *http_wantConn { |
| if q.headPos < len(q.head) { |
| return q.head[q.headPos] |
| } |
| if len(q.tail) > 0 { |
| return q.tail[0] |
| } |
| return nil |
| } |
| |
| // cleanFrontNotWaiting pops any wantConns that are no longer waiting from the head of the |
| // queue, reporting whether any were popped. |
| func (q *http_wantConnQueue) cleanFrontNotWaiting() (cleaned bool) { |
| for { |
| w := q.peekFront() |
| if w == nil || w.waiting() { |
| return cleaned |
| } |
| q.popFront() |
| cleaned = true |
| } |
| } |
| |
| // cleanFrontCanceled pops any wantConns with canceled dials from the head of the queue. |
| func (q *http_wantConnQueue) cleanFrontCanceled() { |
| for { |
| w := q.peekFront() |
| if w == nil || w.cancelCtx != nil { |
| return |
| } |
| q.popFront() |
| } |
| } |
| |
| // all iterates over all wantConns in the queue. |
| // The caller must not modify the queue while iterating. |
| func (q *http_wantConnQueue) all(f func(*http_wantConn)) { |
| for _, w := range q.head[q.headPos:] { |
| f(w) |
| } |
| for _, w := range q.tail { |
| f(w) |
| } |
| } |
| |
| func (t *http_Transport) customDialTLS(ctx context.Context, network, addr string) (conn net.Conn, err error) { |
| if t.DialTLSContext != nil { |
| conn, err = t.DialTLSContext(ctx, network, addr) |
| } else { |
| conn, err = t.DialTLS(network, addr) |
| } |
| if conn == nil && err == nil { |
| err = errors.New("net/http: Transport.DialTLS or DialTLSContext returned (nil, nil)") |
| } |
| return |
| } |
| |
| // getConn dials and creates a new persistConn to the target as |
| // specified in the connectMethod. This includes doing a proxy CONNECT |
| // and/or setting up TLS. If this doesn't return an error, the persistConn |
| // is ready to write requests to. |
| func (t *http_Transport) getConn(treq *http_transportRequest, cm http_connectMethod) (_ *http_persistConn, err error) { |
| req := treq.http_Request |
| trace := treq.trace |
| ctx := req.Context() |
| if trace != nil && trace.GetConn != nil { |
| trace.GetConn(cm.addr()) |
| } |
| |
| // Detach from the request context's cancellation signal. |
| // The dial should proceed even if the request is canceled, |
| // because a future request may be able to make use of the connection. |
| // |
| // We retain the request context's values. |
| dialCtx, dialCancel := context.WithCancel(context.WithoutCancel(ctx)) |
| |
| w := &http_wantConn{ |
| cm: cm, |
| key: cm.key(), |
| ctx: dialCtx, |
| cancelCtx: dialCancel, |
| result: make(chan http_connOrError, 1), |
| beforeDial: http_testHookPrePendingDial, |
| afterDial: http_testHookPostPendingDial, |
| } |
| defer func() { |
| if err != nil { |
| w.cancel(t) |
| } |
| }() |
| |
| // Queue for idle connection. |
| if delivered := t.queueForIdleConn(w); !delivered { |
| t.queueForDial(w) |
| } |
| |
| // Wait for completion or cancellation. |
| select { |
| case r := <-w.result: |
| // Trace success but only for HTTP/1. |
| // HTTP/2 calls trace.GotConn itself. |
| if r.pc != nil && r.pc.alt == nil && trace != nil && trace.GotConn != nil { |
| info := httptrace.GotConnInfo{ |
| Conn: r.pc.conn, |
| Reused: r.pc.isReused(), |
| } |
| if !r.idleAt.IsZero() { |
| info.WasIdle = true |
| info.IdleTime = time.Since(r.idleAt) |
| } |
| trace.GotConn(info) |
| } |
| if r.err != nil { |
| // If the request has been canceled, that's probably |
| // what caused r.err; if so, prefer to return the |
| // cancellation error (see golang.org/issue/16049). |
| select { |
| case <-treq.ctx.Done(): |
| err := context.Cause(treq.ctx) |
| if err == http_errRequestCanceled { |
| err = http_errRequestCanceledConn |
| } |
| return nil, err |
| default: |
| // return below |
| } |
| } |
| return r.pc, r.err |
| case <-treq.ctx.Done(): |
| err := context.Cause(treq.ctx) |
| if err == http_errRequestCanceled { |
| err = http_errRequestCanceledConn |
| } |
| return nil, err |
| } |
| } |
| |
| // queueForDial queues w to wait for permission to begin dialing. |
| // Once w receives permission to dial, it will do so in a separate goroutine. |
| func (t *http_Transport) queueForDial(w *http_wantConn) { |
| w.beforeDial() |
| |
| t.connsPerHostMu.Lock() |
| defer t.connsPerHostMu.Unlock() |
| |
| if t.MaxConnsPerHost <= 0 { |
| t.startDialConnForLocked(w) |
| return |
| } |
| |
| if n := t.connsPerHost[w.key]; n < t.MaxConnsPerHost { |
| if t.connsPerHost == nil { |
| t.connsPerHost = make(map[http_connectMethodKey]int) |
| } |
| t.connsPerHost[w.key] = n + 1 |
| t.startDialConnForLocked(w) |
| return |
| } |
| |
| if t.connsPerHostWait == nil { |
| t.connsPerHostWait = make(map[http_connectMethodKey]http_wantConnQueue) |
| } |
| q := t.connsPerHostWait[w.key] |
| q.cleanFrontNotWaiting() |
| q.pushBack(w) |
| t.connsPerHostWait[w.key] = q |
| } |
| |
| // startDialConnFor calls dialConn in a new goroutine. |
| // t.connsPerHostMu must be held. |
| func (t *http_Transport) startDialConnForLocked(w *http_wantConn) { |
| t.dialsInProgress.cleanFrontCanceled() |
| t.dialsInProgress.pushBack(w) |
| go func() { |
| t.dialConnFor(w) |
| t.connsPerHostMu.Lock() |
| defer t.connsPerHostMu.Unlock() |
| w.cancelCtx = nil |
| }() |
| } |
| |
| // dialConnFor dials on behalf of w and delivers the result to w. |
| // dialConnFor has received permission to dial w.cm and is counted in t.connCount[w.cm.key()]. |
| // If the dial is canceled or unsuccessful, dialConnFor decrements t.connCount[w.cm.key()]. |
| func (t *http_Transport) dialConnFor(w *http_wantConn) { |
| defer w.afterDial() |
| ctx := w.getCtxForDial() |
| if ctx == nil { |
| t.decConnsPerHost(w.key) |
| return |
| } |
| |
| const isClientConn = false |
| pc, err := t.dialConn(ctx, w.cm, isClientConn, nil) |
| delivered := w.tryDeliver(pc, err, time.Time{}) |
| if err == nil && (!delivered || pc.alt != nil) { |
| // pconn was not passed to w, |
| // or it is HTTP/2 and can be shared. |
| // Add to the idle connection pool. |
| t.putOrCloseIdleConn(pc) |
| } |
| if err != nil { |
| t.decConnsPerHost(w.key) |
| } |
| } |
| |
| // decConnsPerHost decrements the per-host connection count for key, |
| // which may in turn give a different waiting goroutine permission to dial. |
| func (t *http_Transport) decConnsPerHost(key http_connectMethodKey) { |
| if t.MaxConnsPerHost <= 0 { |
| return |
| } |
| |
| t.connsPerHostMu.Lock() |
| defer t.connsPerHostMu.Unlock() |
| n := t.connsPerHost[key] |
| if n == 0 { |
| // Shouldn't happen, but if it does, the counting is buggy and could |
| // easily lead to a silent deadlock, so report the problem loudly. |
| panic("net/http: internal error: connCount underflow") |
| } |
| |
| // Can we hand this count to a goroutine still waiting to dial? |
| // (Some goroutines on the wait list may have timed out or |
| // gotten a connection another way. If they're all gone, |
| // we don't want to kick off any spurious dial operations.) |
| if q := t.connsPerHostWait[key]; q.len() > 0 { |
| done := false |
| for q.len() > 0 { |
| w := q.popFront() |
| if w.waiting() { |
| t.startDialConnForLocked(w) |
| done = true |
| break |
| } |
| } |
| if q.len() == 0 { |
| delete(t.connsPerHostWait, key) |
| } else { |
| // q is a value (like a slice), so we have to store |
| // the updated q back into the map. |
| t.connsPerHostWait[key] = q |
| } |
| if done { |
| return |
| } |
| } |
| |
| // Otherwise, decrement the recorded count. |
| if n--; n == 0 { |
| delete(t.connsPerHost, key) |
| } else { |
| t.connsPerHost[key] = n |
| } |
| } |
| |
| // Add TLS to a persistent connection, i.e. negotiate a TLS session. If pconn is already a TLS |
| // tunnel, this function establishes a nested TLS session inside the encrypted channel. |
| // The remote endpoint's name may be overridden by TLSClientConfig.ServerName. |
| func (pconn *http_persistConn) addTLS(ctx context.Context, name string, trace *httptrace.ClientTrace) error { |
| // Initiate TLS and check remote host name against certificate. |
| cfg := http_cloneTLSConfig(pconn.t.TLSClientConfig) |
| if cfg.ServerName == "" { |
| cfg.ServerName = name |
| } |
| if pconn.cacheKey.onlyH1 { |
| cfg.NextProtos = nil |
| } |
| plainConn := pconn.conn |
| tlsConn := tls.Client(plainConn, cfg) |
| errc := make(chan error, 2) |
| var timer *time.Timer // for canceling TLS handshake |
| if d := pconn.t.TLSHandshakeTimeout; d != 0 { |
| timer = time.AfterFunc(d, func() { |
| errc <- http_tlsHandshakeTimeoutError{} |
| }) |
| } |
| go func() { |
| if trace != nil && trace.TLSHandshakeStart != nil { |
| trace.TLSHandshakeStart() |
| } |
| err := tlsConn.HandshakeContext(ctx) |
| if timer != nil { |
| timer.Stop() |
| } |
| errc <- err |
| }() |
| if err := <-errc; err != nil { |
| plainConn.Close() |
| if err == (http_tlsHandshakeTimeoutError{}) { |
| // Now that we have closed the connection, |
| // wait for the call to HandshakeContext to return. |
| <-errc |
| } |
| if trace != nil && trace.TLSHandshakeDone != nil { |
| trace.TLSHandshakeDone(tls.ConnectionState{}, err) |
| } |
| return err |
| } |
| cs := tlsConn.ConnectionState() |
| if trace != nil && trace.TLSHandshakeDone != nil { |
| trace.TLSHandshakeDone(cs, nil) |
| } |
| pconn.tlsState = &cs |
| pconn.conn = tlsConn |
| return nil |
| } |
| |
| type http_erringRoundTripper interface { |
| RoundTripErr() error |
| } |
| |
| var http_testHookProxyConnectTimeout = context.WithTimeout |
| |
| func (t *http_Transport) dialConn(ctx context.Context, cm http_connectMethod, isClientConn bool, internalStateHook func()) (pconn *http_persistConn, err error) { |
| // TODO: actually support HTTP/3. Among other things: |
| // - make HTTP/3 play well with proxy. |
| // - implement happy eyeball between HTTP/3 and HTTP/1 & HTTP/2. |
| // - clean up the connection pooling logic. |
| if p := t.protocols(); p.http3() { |
| if p.HTTP1() || p.HTTP2() || p.UnencryptedHTTP2() { |
| return nil, errors.New("http: when using HTTP3, Transport.Protocols must contain only HTTP3") |
| } |
| if t.h3Transport == nil { |
| return nil, errors.New("http: Transport.Protocols contains HTTP3, but Transport does not support HTTP/3") |
| } |
| rt, err := t.h3Transport.DialClientConn(ctx, cm.addr(), cm.proxyURL, internalStateHook) |
| if err != nil { |
| return nil, err |
| } |
| return &http_persistConn{ |
| t: t, |
| cacheKey: cm.key(), |
| alt: rt, |
| }, nil |
| } |
| |
| pconn = &http_persistConn{ |
| t: t, |
| cacheKey: cm.key(), |
| reqch: make(chan http_requestAndChan, 1), |
| writech: make(chan http_writeRequest, 1), |
| closech: make(chan struct{}), |
| writeErrCh: make(chan error, 1), |
| writeLoopDone: make(chan struct{}), |
| isClientConn: isClientConn, |
| internalStateHook: internalStateHook, |
| } |
| trace := httptrace.ContextClientTrace(ctx) |
| wrapErr := func(err error) error { |
| if cm.proxyURL != nil { |
| // Return a typed error, per Issue 16997 |
| return &net.OpError{Op: "proxyconnect", Net: "tcp", Err: err} |
| } |
| return err |
| } |
| |
| if rt, err := t.http2ExternalDial(ctx, cm); err != errors.ErrUnsupported { |
| if err != nil { |
| return nil, err |
| } |
| return &http_persistConn{t: t, cacheKey: pconn.cacheKey, alt: rt}, nil |
| } |
| |
| if cm.scheme() == "https" && t.hasCustomTLSDialer() { |
| var err error |
| pconn.conn, err = t.customDialTLS(ctx, "tcp", cm.addr()) |
| if err != nil { |
| return nil, wrapErr(err) |
| } |
| type connectionStater interface { |
| ConnectionState() tls.ConnectionState |
| } |
| type handshaker interface { |
| HandshakeContext(context.Context) error |
| } |
| if cstater, ok := pconn.conn.(connectionStater); ok { |
| if trace != nil && trace.TLSHandshakeStart != nil { |
| trace.TLSHandshakeStart() |
| } |
| if handshaker, ok := cstater.(handshaker); ok { |
| // Handshake here, in case DialTLS didn't. TLSNextProto below |
| // depends on it for knowing the connection state. |
| if err := handshaker.HandshakeContext(ctx); err != nil { |
| go pconn.conn.Close() |
| if trace != nil && trace.TLSHandshakeDone != nil { |
| trace.TLSHandshakeDone(tls.ConnectionState{}, err) |
| } |
| return nil, err |
| } |
| } |
| cs := cstater.ConnectionState() |
| if trace != nil && trace.TLSHandshakeDone != nil { |
| trace.TLSHandshakeDone(cs, nil) |
| } |
| pconn.tlsState = &cs |
| } |
| } else { |
| conn, err := t.dial(ctx, "tcp", cm.addr()) |
| if err != nil { |
| return nil, wrapErr(err) |
| } |
| pconn.conn = conn |
| if cm.scheme() == "https" { |
| var firstTLSHost string |
| if firstTLSHost, _, err = net.SplitHostPort(cm.addr()); err != nil { |
| return nil, wrapErr(err) |
| } |
| if err = pconn.addTLS(ctx, firstTLSHost, trace); err != nil { |
| return nil, wrapErr(err) |
| } |
| } |
| } |
| |
| // Proxy setup. |
| switch { |
| case cm.proxyURL == nil: |
| // Do nothing. Not using a proxy. |
| case cm.proxyURL.Scheme == "socks5" || cm.proxyURL.Scheme == "socks5h": |
| conn := pconn.conn |
| d := http_socksNewDialer("tcp", conn.RemoteAddr().String()) |
| if u := cm.proxyURL.User; u != nil { |
| auth := &http_socksUsernamePassword{ |
| Username: u.Username(), |
| } |
| auth.Password, _ = u.Password() |
| d.AuthMethods = []http_socksAuthMethod{ |
| http_socksAuthMethodNotRequired, |
| http_socksAuthMethodUsernamePassword, |
| } |
| d.Authenticate = auth.Authenticate |
| } |
| if _, err := d.DialWithConn(ctx, conn, "tcp", cm.targetAddr); err != nil { |
| conn.Close() |
| return nil, err |
| } |
| case cm.targetScheme == "http": |
| pconn.isProxy = true |
| if pa := cm.proxyAuth(); pa != "" { |
| pconn.mutateHeaderFunc = func(h http_Header) { |
| h.Set("Proxy-Authorization", pa) |
| } |
| } |
| case cm.targetScheme == "https": |
| conn := pconn.conn |
| var hdr http_Header |
| if t.GetProxyConnectHeader != nil { |
| var err error |
| hdr, err = t.GetProxyConnectHeader(ctx, cm.proxyURL, cm.targetAddr) |
| if err != nil { |
| conn.Close() |
| return nil, err |
| } |
| } else { |
| hdr = t.ProxyConnectHeader |
| } |
| if hdr == nil { |
| hdr = make(http_Header) |
| } |
| if pa := cm.proxyAuth(); pa != "" { |
| hdr = hdr.Clone() |
| hdr.Set("Proxy-Authorization", pa) |
| } |
| connectReq := &http_Request{ |
| Method: "CONNECT", |
| URL: &url.URL{Opaque: cm.targetAddr}, |
| Host: cm.targetAddr, |
| Header: hdr, |
| } |
| |
| // Set a (long) timeout here to make sure we don't block forever |
| // and leak a goroutine if the connection stops replying after |
| // the TCP connect. |
| connectCtx, cancel := http_testHookProxyConnectTimeout(ctx, 1*time.Minute) |
| defer cancel() |
| |
| didReadResponse := make(chan struct{}) // closed after CONNECT write+read is done or fails |
| var ( |
| resp *http_Response |
| err error // write or read error |
| ) |
| // Write the CONNECT request & read the response. |
| go func() { |
| defer close(didReadResponse) |
| err = connectReq.Write(conn) |
| if err != nil { |
| return |
| } |
| // Okay to use and discard buffered reader here, because |
| // TLS server will not speak until spoken to. |
| br := bufio.NewReader(&io.LimitedReader{R: conn, N: t.maxHeaderResponseSize()}) |
| resp, err = http_ReadResponse(br, connectReq) |
| }() |
| select { |
| case <-connectCtx.Done(): |
| conn.Close() |
| <-didReadResponse |
| return nil, connectCtx.Err() |
| case <-didReadResponse: |
| // resp or err now set |
| } |
| if err != nil { |
| conn.Close() |
| return nil, err |
| } |
| |
| if t.OnProxyConnectResponse != nil { |
| err = t.OnProxyConnectResponse(ctx, cm.proxyURL, connectReq, resp) |
| if err != nil { |
| conn.Close() |
| return nil, err |
| } |
| } |
| |
| if resp.StatusCode != 200 { |
| _, text, ok := strings.Cut(resp.Status, " ") |
| conn.Close() |
| if !ok { |
| return nil, errors.New("unknown status code") |
| } |
| return nil, errors.New(text) |
| } |
| } |
| |
| if cm.proxyURL != nil && cm.targetScheme == "https" { |
| if err := pconn.addTLS(ctx, cm.tlsHost(), trace); err != nil { |
| return nil, err |
| } |
| } |
| |
| // Possible unencrypted HTTP/2 with prior knowledge. |
| unencryptedHTTP2 := pconn.tlsState == nil && |
| t.Protocols != nil && |
| t.Protocols.UnencryptedHTTP2() && |
| !t.Protocols.HTTP1() |
| |
| http2 := unencryptedHTTP2 || |
| (pconn.tlsState != nil && pconn.tlsState.NegotiatedProtocol == "h2") |
| |
| if http2 && t.h2Transport != nil { |
| if isClientConn { |
| cc, err := t.http2NewClientConn(pconn.conn, internalStateHook) |
| if err == nil { |
| return &http_persistConn{t: t, cacheKey: pconn.cacheKey, alt: cc, isClientConn: true}, nil |
| } |
| if err != errors.ErrUnsupported { |
| return nil, err |
| } |
| } else { |
| rt, err := t.http2AddConn(cm.targetScheme, cm.targetAddr, pconn.conn) |
| if err == nil { |
| return &http_persistConn{t: t, cacheKey: pconn.cacheKey, alt: rt}, nil |
| } |
| if err != errors.ErrUnsupported { |
| return nil, err |
| } |
| } |
| } |
| |
| if isClientConn && (unencryptedHTTP2 || (pconn.tlsState != nil && pconn.tlsState.NegotiatedProtocol == "h2")) { |
| altProto, _ := t.altProto.Load().(map[string]http_RoundTripper) |
| h2, ok := altProto["https"].(http_newClientConner) |
| if !ok { |
| return nil, errors.New("http: HTTP/2 implementation does not support NewClientConn (update golang.org/x/net?)") |
| } |
| alt, err := h2.NewClientConn(pconn.conn, internalStateHook) |
| if err != nil { |
| pconn.conn.Close() |
| return nil, err |
| } |
| return &http_persistConn{t: t, cacheKey: pconn.cacheKey, alt: alt, isClientConn: true}, nil |
| } |
| |
| if unencryptedHTTP2 { |
| next, ok := t.TLSNextProto[http_nextProtoUnencryptedHTTP2] |
| if !ok { |
| return nil, errors.New("http: Transport does not support unencrypted HTTP/2") |
| } |
| alt := next(cm.targetAddr, http_unencryptedTLSConn(pconn.conn)) |
| if e, ok := alt.(http_erringRoundTripper); ok { |
| // pconn.conn was closed by next (http2configureTransports.upgradeFn). |
| return nil, e.RoundTripErr() |
| } |
| return &http_persistConn{t: t, cacheKey: pconn.cacheKey, alt: alt}, nil |
| } |
| |
| if s := pconn.tlsState; s != nil && s.NegotiatedProtocolIsMutual && s.NegotiatedProtocol != "" { |
| tlsConn, tlsConnOK := pconn.conn.(*tls.Conn) |
| if next, ok := t.TLSNextProto[s.NegotiatedProtocol]; tlsConnOK && ok { |
| alt := next(cm.targetAddr, tlsConn) |
| if e, ok := alt.(http_erringRoundTripper); ok { |
| // pconn.conn was closed by next (http2configureTransports.upgradeFn). |
| return nil, e.RoundTripErr() |
| } |
| return &http_persistConn{t: t, cacheKey: pconn.cacheKey, alt: alt}, nil |
| } |
| } |
| |
| pconn.br = bufio.NewReaderSize(pconn, t.readBufferSize()) |
| pconn.bw = bufio.NewWriterSize(http_persistConnWriter{pconn}, t.writeBufferSize()) |
| |
| go pconn.readLoop() |
| go pconn.writeLoop() |
| return pconn, nil |
| } |
| |
| // persistConnWriter is the io.Writer written to by pc.bw. |
| // It accumulates the number of bytes written to the underlying conn, |
| // so the retry logic can determine whether any bytes made it across |
| // the wire. |
| // This is exactly 1 pointer field wide so it can go into an interface |
| // without allocation. |
| type http_persistConnWriter struct { |
| pc *http_persistConn |
| } |
| |
| func (w http_persistConnWriter) Write(p []byte) (n int, err error) { |
| n, err = w.pc.conn.Write(p) |
| w.pc.nwrite += int64(n) |
| return |
| } |
| |
| // ReadFrom exposes persistConnWriter's underlying Conn to io.Copy and if |
| // the Conn implements io.ReaderFrom, it can take advantage of optimizations |
| // such as sendfile. |
| func (w http_persistConnWriter) ReadFrom(r io.Reader) (n int64, err error) { |
| n, err = io.Copy(w.pc.conn, r) |
| w.pc.nwrite += n |
| return |
| } |
| |
| var _ io.ReaderFrom = (*http_persistConnWriter)(nil) |
| |
| // connectMethod is the map key (in its String form) for keeping persistent |
| // TCP connections alive for subsequent HTTP requests. |
| // |
| // A connect method may be of the following types: |
| // |
| // connectMethod.key().String() Description |
| // ------------------------------ ------------------------- |
| // |http|foo.com http directly to server, no proxy |
| // |https|foo.com https directly to server, no proxy |
| // |https,h1|foo.com https directly to server w/o HTTP/2, no proxy |
| // http://proxy.com|https|foo.com http to proxy, then CONNECT to foo.com |
| // http://proxy.com|http http to proxy, http to anywhere after that |
| // socks5://proxy.com|http|foo.com socks5 to proxy, then http to foo.com |
| // socks5://proxy.com|https|foo.com socks5 to proxy, then https to foo.com |
| // https://proxy.com|https|foo.com https to proxy, then CONNECT to foo.com |
| // https://proxy.com|http https to proxy, http to anywhere after that |
| type http_connectMethod struct { |
| _ http_incomparable |
| proxyURL *url.URL // nil for no proxy, else full proxy URL |
| targetScheme string // "http" or "https" |
| // If proxyURL specifies an http or https proxy, and targetScheme is http (not https), |
| // then targetAddr is not included in the connect method key, because the socket can |
| // be reused for different targetAddr values. |
| targetAddr string |
| onlyH1 bool // whether to disable HTTP/2 and force HTTP/1 |
| } |
| |
| func (cm *http_connectMethod) key() http_connectMethodKey { |
| proxyStr := "" |
| targetAddr := cm.targetAddr |
| if cm.proxyURL != nil { |
| proxyStr = cm.proxyURL.String() |
| if (cm.proxyURL.Scheme == "http" || cm.proxyURL.Scheme == "https") && cm.targetScheme == "http" { |
| targetAddr = "" |
| } |
| } |
| return http_connectMethodKey{ |
| proxy: proxyStr, |
| scheme: cm.targetScheme, |
| addr: targetAddr, |
| onlyH1: cm.onlyH1, |
| } |
| } |
| |
| // scheme returns the first hop scheme: http, https, or socks5 |
| func (cm *http_connectMethod) scheme() string { |
| if cm.proxyURL != nil { |
| return cm.proxyURL.Scheme |
| } |
| return cm.targetScheme |
| } |
| |
| // addr returns the first hop "host:port" to which we need to TCP connect. |
| func (cm *http_connectMethod) addr() string { |
| if cm.proxyURL != nil { |
| return http_canonicalAddr(cm.proxyURL) |
| } |
| return cm.targetAddr |
| } |
| |
| // tlsHost returns the host name to match against the peer's |
| // TLS certificate. |
| func (cm *http_connectMethod) tlsHost() string { |
| h := cm.targetAddr |
| return http_removePort(h) |
| } |
| |
| // connectMethodKey is the map key version of connectMethod, with a |
| // stringified proxy URL (or the empty string) instead of a pointer to |
| // a URL. |
| type http_connectMethodKey struct { |
| proxy, scheme, addr string |
| onlyH1 bool |
| } |
| |
| func (k http_connectMethodKey) String() string { |
| // Only used by tests. |
| var h1 string |
| if k.onlyH1 { |
| h1 = ",h1" |
| } |
| return fmt.Sprintf("%s|%s%s|%s", k.proxy, k.scheme, h1, k.addr) |
| } |
| |
| // persistConn wraps a connection, usually a persistent one |
| // (but may be used for non-keep-alive requests as well) |
| type http_persistConn struct { |
| // alt optionally specifies the TLS NextProto RoundTripper. |
| // This is used for HTTP/2 today and future protocols later. |
| // If it's non-nil, the rest of the fields are unused. |
| alt http_RoundTripper |
| |
| t *http_Transport |
| cacheKey http_connectMethodKey |
| conn net.Conn |
| tlsState *tls.ConnectionState |
| br *bufio.Reader // from conn |
| bw *bufio.Writer // to conn |
| nwrite int64 // bytes written |
| reqch chan http_requestAndChan // written by roundTrip; read by readLoop |
| writech chan http_writeRequest // written by roundTrip; read by writeLoop |
| closech chan struct{} // closed when conn closed |
| availch chan struct{} // ClientConn only: contains a value when conn is usable |
| isProxy bool |
| sawEOF bool // whether we've seen EOF from conn; owned by readLoop |
| isClientConn bool // whether this is a ClientConn (outside any pool) |
| readLimit int64 // bytes allowed to be read; owned by readLoop |
| // writeErrCh passes the request write error (usually nil) |
| // from the writeLoop goroutine to the readLoop which passes |
| // it off to the res.Body reader, which then uses it to decide |
| // whether or not a connection can be reused. Issue 7569. |
| writeErrCh chan error |
| |
| writeLoopDone chan struct{} // closed when write loop ends |
| |
| // Both guarded by Transport.idleMu: |
| idleAt time.Time // time it last become idle |
| idleTimer *time.Timer // holding an AfterFunc to close it |
| |
| mu sync.Mutex // guards following fields |
| numExpectedResponses int |
| closed error // set non-nil when conn is closed, before closech is closed |
| canceledErr error // set non-nil if conn is canceled |
| reused bool // whether conn has had successful request/response and is being reused. |
| reserved bool // ClientConn only: concurrency slot reserved |
| inFlight bool // ClientConn only: request is in flight |
| internalStateHook func() // ClientConn state hook |
| |
| // mutateHeaderFunc is an optional func to modify extra |
| // headers on each outbound request before it's written. (the |
| // original Request given to RoundTrip is not modified) |
| mutateHeaderFunc func(http_Header) |
| } |
| |
| func (pc *http_persistConn) maxHeaderResponseSize() int64 { |
| return pc.t.maxHeaderResponseSize() |
| } |
| |
| func (pc *http_persistConn) Read(p []byte) (n int, err error) { |
| if pc.readLimit <= 0 { |
| return 0, fmt.Errorf("read limit of %d bytes exhausted", pc.maxHeaderResponseSize()) |
| } |
| if int64(len(p)) > pc.readLimit { |
| p = p[:pc.readLimit] |
| } |
| n, err = pc.conn.Read(p) |
| if err == io.EOF { |
| pc.sawEOF = true |
| } |
| pc.readLimit -= int64(n) |
| return |
| } |
| |
| // isBroken reports whether this connection is in a known broken state. |
| func (pc *http_persistConn) isBroken() bool { |
| pc.mu.Lock() |
| b := pc.closed != nil |
| pc.mu.Unlock() |
| return b |
| } |
| |
| // canceled returns non-nil if the connection was closed due to |
| // CancelRequest or due to context cancellation. |
| func (pc *http_persistConn) canceled() error { |
| pc.mu.Lock() |
| defer pc.mu.Unlock() |
| return pc.canceledErr |
| } |
| |
| // isReused reports whether this connection has been used before. |
| func (pc *http_persistConn) isReused() bool { |
| pc.mu.Lock() |
| r := pc.reused |
| pc.mu.Unlock() |
| return r |
| } |
| |
| func (pc *http_persistConn) cancelRequest(err error) { |
| pc.mu.Lock() |
| defer pc.mu.Unlock() |
| pc.canceledErr = err |
| pc.closeLocked(http_errRequestCanceled) |
| } |
| |
| // closeConnIfStillIdle closes the connection if it's still sitting idle. |
| // This is what's called by the persistConn's idleTimer, and is run in its |
| // own goroutine. |
| func (pc *http_persistConn) closeConnIfStillIdle() { |
| t := pc.t |
| t.idleMu.Lock() |
| defer t.idleMu.Unlock() |
| if _, ok := t.idleLRU.m[pc]; !ok { |
| // Not idle. |
| return |
| } |
| t.removeIdleConnLocked(pc) |
| pc.close(http_errIdleConnTimeout) |
| } |
| |
| // mapRoundTripError returns the appropriate error value for |
| // persistConn.roundTrip. |
| // |
| // The provided err is the first error that (*persistConn).roundTrip |
| // happened to receive from its select statement. |
| // |
| // The startBytesWritten value should be the value of pc.nwrite before the roundTrip |
| // started writing the request. |
| func (pc *http_persistConn) mapRoundTripError(req *http_transportRequest, startBytesWritten int64, err error) error { |
| if err == nil { |
| return nil |
| } |
| |
| // Wait for the writeLoop goroutine to terminate to avoid data |
| // races on callers who mutate the request on failure. |
| // |
| // When resc in pc.roundTrip and hence rc.ch receives a responseAndError |
| // with a non-nil error it implies that the persistConn is either closed |
| // or closing. Waiting on pc.writeLoopDone is hence safe as all callers |
| // close closech which in turn ensures writeLoop returns. |
| <-pc.writeLoopDone |
| |
| // If the request was canceled, that's better than network |
| // failures that were likely the result of tearing down the |
| // connection. |
| if cerr := pc.canceled(); cerr != nil { |
| return cerr |
| } |
| |
| // See if an error was set explicitly. |
| req.mu.Lock() |
| reqErr := req.err |
| req.mu.Unlock() |
| if reqErr != nil { |
| return reqErr |
| } |
| |
| if err == http_errServerClosedIdle { |
| // Don't decorate |
| return err |
| } |
| |
| if _, ok := err.(http_transportReadFromServerError); ok { |
| if pc.nwrite == startBytesWritten { |
| return http_nothingWrittenError{err} |
| } |
| // Don't decorate |
| return err |
| } |
| if pc.isBroken() { |
| if pc.nwrite == startBytesWritten { |
| return http_nothingWrittenError{err} |
| } |
| return fmt.Errorf("net/http: HTTP/1.x transport connection broken: %w", err) |
| } |
| return err |
| } |
| |
| // errCallerOwnsConn is an internal sentinel error used when we hand |
| // off a writable response.Body to the caller. We use this to prevent |
| // closing a net.Conn that is now owned by the caller. |
| var http_errCallerOwnsConn = errors.New("read loop ending; caller owns writable underlying conn") |
| |
| // maxPostCloseReadBytes is the max number of bytes that a client is willing to |
| // read when draining the response body of any unread bytes after it has been |
| // closed. This number is chosen for consistency with maxPostHandlerReadBytes. |
| const http_maxPostCloseReadBytes = 256 << 10 |
| |
| // maxPostCloseReadTime defines the maximum amount of time that a client is |
| // willing to spend on draining a response body of any unread bytes after it |
| // has been closed. |
| const http_maxPostCloseReadTime = 50 * time.Millisecond |
| |
| func http_maybeDrainBody(body io.Reader) bool { |
| drainedCh := make(chan bool, 1) |
| go func() { |
| if _, err := io.CopyN(io.Discard, body, http_maxPostCloseReadBytes+1); err == io.EOF { |
| drainedCh <- true |
| } else { |
| drainedCh <- false |
| } |
| }() |
| select { |
| case drained := <-drainedCh: |
| return drained |
| case <-time.After(http_maxPostCloseReadTime): |
| return false |
| } |
| } |
| |
| func (pc *http_persistConn) readLoop() { |
| closeErr := http_errReadLoopExiting // default value, if not changed below |
| defer func() { |
| pc.close(closeErr) |
| pc.t.removeIdleConn(pc) |
| if pc.internalStateHook != nil { |
| pc.internalStateHook() |
| } |
| }() |
| |
| tryPutIdleConn := func(treq *http_transportRequest) bool { |
| trace := treq.trace |
| if err := pc.t.tryPutIdleConn(pc); err != nil { |
| closeErr = err |
| if trace != nil && trace.PutIdleConn != nil && err != http_errKeepAlivesDisabled { |
| trace.PutIdleConn(err) |
| } |
| return false |
| } |
| if trace != nil && trace.PutIdleConn != nil { |
| trace.PutIdleConn(nil) |
| } |
| return true |
| } |
| |
| // eofc is used to block caller goroutines reading from Response.Body |
| // at EOF until this goroutines has (potentially) added the connection |
| // back to the idle pool. |
| eofc := make(chan struct{}) |
| defer close(eofc) // unblock reader on errors |
| |
| // Read this once, before loop starts. (to avoid races in tests) |
| http_testHookMu.Lock() |
| testHookReadLoopBeforeNextRead := http_testHookReadLoopBeforeNextRead |
| http_testHookMu.Unlock() |
| |
| alive := true |
| for alive { |
| pc.readLimit = pc.maxHeaderResponseSize() |
| _, err := pc.br.Peek(1) |
| |
| pc.mu.Lock() |
| if pc.numExpectedResponses == 0 { |
| pc.readLoopPeekFailLocked(err) |
| pc.mu.Unlock() |
| return |
| } |
| pc.mu.Unlock() |
| |
| rc := <-pc.reqch |
| trace := rc.treq.trace |
| |
| var resp *http_Response |
| if err == nil { |
| resp, err = pc.readResponse(rc, trace) |
| } else { |
| err = http_transportReadFromServerError{err} |
| closeErr = err |
| } |
| |
| if err != nil { |
| if pc.readLimit <= 0 { |
| err = fmt.Errorf("net/http: server response headers exceeded %d bytes; aborted", pc.maxHeaderResponseSize()) |
| } |
| |
| select { |
| case rc.ch <- http_responseAndError{err: err}: |
| case <-rc.callerGone: |
| return |
| } |
| return |
| } |
| pc.readLimit = http_maxInt64 // effectively no limit for response bodies |
| |
| pc.mu.Lock() |
| pc.numExpectedResponses-- |
| pc.mu.Unlock() |
| |
| bodyWritable := resp.bodyIsWritable() |
| hasBody := rc.treq.http_Request.Method != "HEAD" && resp.ContentLength != 0 |
| |
| if resp.Close || rc.treq.http_Request.Close || resp.StatusCode <= 199 || bodyWritable { |
| // Don't do keep-alive on error if either party requested a close |
| // or we get an unexpected informational (1xx) response. |
| // StatusCode 100 is already handled above. |
| alive = false |
| } |
| |
| if !hasBody || bodyWritable { |
| // Put the idle conn back into the pool before we send the response |
| // so if they process it quickly and make another request, they'll |
| // get this same conn. But we use the unbuffered channel 'rc' |
| // to guarantee that persistConn.roundTrip got out of its select |
| // potentially waiting for this persistConn to close. |
| alive = alive && |
| !pc.sawEOF && |
| pc.wroteRequest() && |
| tryPutIdleConn(rc.treq) |
| |
| if bodyWritable { |
| closeErr = http_errCallerOwnsConn |
| } |
| |
| select { |
| case rc.ch <- http_responseAndError{res: resp}: |
| case <-rc.callerGone: |
| return |
| } |
| |
| rc.treq.cancel(http_errRequestDone) |
| |
| // Now that they've read from the unbuffered channel, they're safely |
| // out of the select that also waits on this goroutine to die, so |
| // we're allowed to exit now if needed (if alive is false) |
| testHookReadLoopBeforeNextRead() |
| continue |
| } |
| |
| waitForBodyRead := make(chan bool, 2) |
| body := &http_bodyEOFSignal{ |
| body: resp.Body, |
| earlyCloseFn: func() error { |
| waitForBodyRead <- false |
| <-eofc // will be closed by deferred call at the end of the function |
| return nil |
| |
| }, |
| fn: func(err error) error { |
| isEOF := err == io.EOF |
| waitForBodyRead <- isEOF |
| if isEOF { |
| <-eofc // see comment above eofc declaration |
| } else if err != nil { |
| if cerr := pc.canceled(); cerr != nil { |
| return cerr |
| } |
| } |
| return err |
| }, |
| } |
| |
| resp.Body = body |
| if rc.addedGzip && ascii.EqualFold(resp.Header.Get("Content-Encoding"), "gzip") { |
| resp.Body = &http_gzipReader{body: body} |
| resp.Header.Del("Content-Encoding") |
| resp.Header.Del("Content-Length") |
| resp.ContentLength = -1 |
| resp.Uncompressed = true |
| } |
| |
| select { |
| case rc.ch <- http_responseAndError{res: resp}: |
| case <-rc.callerGone: |
| return |
| } |
| |
| // Before looping back to the top of this function and peeking on |
| // the bufio.Reader, wait for the caller goroutine to finish |
| // reading the response body. (or for cancellation or death) |
| select { |
| case bodyEOF := <-waitForBodyRead: |
| tryDrain := !bodyEOF && resp.ContentLength <= http_maxPostCloseReadBytes |
| if tryDrain { |
| eofc <- struct{}{} |
| bodyEOF = http_maybeDrainBody(body.body) |
| } |
| alive = alive && |
| bodyEOF && |
| !pc.sawEOF && |
| pc.wroteRequest() && |
| tryPutIdleConn(rc.treq) |
| if !tryDrain && bodyEOF { |
| eofc <- struct{}{} |
| } |
| case <-rc.treq.ctx.Done(): |
| alive = false |
| pc.cancelRequest(context.Cause(rc.treq.ctx)) |
| case <-pc.closech: |
| alive = false |
| } |
| |
| rc.treq.cancel(http_errRequestDone) |
| testHookReadLoopBeforeNextRead() |
| } |
| } |
| |
| func (pc *http_persistConn) readLoopPeekFailLocked(peekErr error) { |
| if pc.closed != nil { |
| return |
| } |
| if n := pc.br.Buffered(); n > 0 { |
| buf, _ := pc.br.Peek(n) |
| if http_is408Message(buf) { |
| pc.closeLocked(http_errServerClosedIdle) |
| return |
| } else { |
| log.Printf("Unsolicited response received on idle HTTP channel starting with %q; err=%v", buf, peekErr) |
| } |
| } |
| if peekErr == io.EOF { |
| // common case. |
| pc.closeLocked(http_errServerClosedIdle) |
| } else { |
| pc.closeLocked(fmt.Errorf("readLoopPeekFailLocked: %w", peekErr)) |
| } |
| } |
| |
| // is408Message reports whether buf has the prefix of an |
| // HTTP 408 Request Timeout response. |
| // See golang.org/issue/32310. |
| func http_is408Message(buf []byte) bool { |
| if len(buf) < len("HTTP/1.x 408") { |
| return false |
| } |
| if string(buf[:7]) != "HTTP/1." { |
| return false |
| } |
| return string(buf[8:12]) == " 408" |
| } |
| |
| // readResponse reads an HTTP response (or two, in the case of "Expect: |
| // 100-continue") from the server. It returns the final non-100 one. |
| // trace is optional. |
| func (pc *http_persistConn) readResponse(rc http_requestAndChan, trace *httptrace.ClientTrace) (resp *http_Response, err error) { |
| if trace != nil && trace.GotFirstResponseByte != nil { |
| if peek, err := pc.br.Peek(1); err == nil && len(peek) == 1 { |
| trace.GotFirstResponseByte() |
| } |
| } |
| |
| continueCh := rc.continueCh |
| for { |
| resp, err = http_ReadResponse(pc.br, rc.treq.http_Request) |
| if err != nil { |
| return |
| } |
| resCode := resp.StatusCode |
| if continueCh != nil && resCode == http_StatusContinue { |
| if trace != nil && trace.Got100Continue != nil { |
| trace.Got100Continue() |
| } |
| continueCh <- struct{}{} |
| continueCh = nil |
| } |
| is1xx := 100 <= resCode && resCode <= 199 |
| // treat 101 as a terminal status, see issue 26161 |
| is1xxNonTerminal := is1xx && resCode != http_StatusSwitchingProtocols |
| if is1xxNonTerminal { |
| if trace != nil && trace.Got1xxResponse != nil { |
| if err := trace.Got1xxResponse(resCode, textproto.MIMEHeader(resp.Header)); err != nil { |
| return nil, err |
| } |
| // If the 1xx response was delivered to the user, |
| // then they're responsible for limiting the number of |
| // responses. Reset the header limit. |
| // |
| // If the user didn't examine the 1xx response, then we |
| // limit the size of all headers (including both 1xx |
| // and the final response) to maxHeaderResponseSize. |
| pc.readLimit = pc.maxHeaderResponseSize() // reset the limit |
| } |
| continue |
| } |
| break |
| } |
| if resp.isProtocolSwitch() { |
| resp.Body = http_newReadWriteCloserBody(pc.br, pc.conn) |
| } |
| if continueCh != nil { |
| // We send an "Expect: 100-continue" header, but the server |
| // responded with a terminal status and no 100 Continue. |
| // |
| // If we're going to keep using the connection, we need to send the request body. |
| // Tell writeLoop to skip sending the body if we're going to close the connection, |
| // or to send it otherwise. |
| // |
| // The case where we receive a 101 Switching Protocols response is a bit |
| // ambiguous, since we don't know what protocol we're switching to. |
| // Conceivably, it's one that doesn't need us to send the body. |
| // Given that we'll send the body if ExpectContinueTimeout expires, |
| // be consistent and always send it if we aren't closing the connection. |
| if resp.Close || rc.treq.http_Request.Close { |
| close(continueCh) // don't send the body; the connection will close |
| } else { |
| continueCh <- struct{}{} // send the body |
| } |
| } |
| |
| resp.TLS = pc.tlsState |
| return |
| } |
| |
| // waitForContinue returns the function to block until |
| // any response, timeout or connection close. After any of them, |
| // the function returns a bool which indicates if the body should be sent. |
| func (pc *http_persistConn) waitForContinue(continueCh <-chan struct{}) func() bool { |
| if continueCh == nil { |
| return nil |
| } |
| return func() bool { |
| timer := time.NewTimer(pc.t.ExpectContinueTimeout) |
| defer timer.Stop() |
| |
| select { |
| case _, ok := <-continueCh: |
| return ok |
| case <-timer.C: |
| return true |
| case <-pc.closech: |
| return false |
| } |
| } |
| } |
| |
| func http_newReadWriteCloserBody(br *bufio.Reader, rwc io.ReadWriteCloser) io.ReadWriteCloser { |
| body := &http_readWriteCloserBody{ReadWriteCloser: rwc} |
| if br.Buffered() != 0 { |
| body.br = br |
| } |
| return body |
| } |
| |
| // readWriteCloserBody is the Response.Body type used when we want to |
| // give users write access to the Body through the underlying |
| // connection (TCP, unless using custom dialers). This is then |
| // the concrete type for a Response.Body on the 101 Switching |
| // Protocols response, as used by WebSockets, h2c, etc. |
| type http_readWriteCloserBody struct { |
| _ http_incomparable |
| br *bufio.Reader // used until empty |
| io.ReadWriteCloser |
| } |
| |
| func (b *http_readWriteCloserBody) Read(p []byte) (n int, err error) { |
| if b.br != nil { |
| if n := b.br.Buffered(); len(p) > n { |
| p = p[:n] |
| } |
| n, err = b.br.Read(p) |
| if b.br.Buffered() == 0 { |
| b.br = nil |
| } |
| return n, err |
| } |
| return b.ReadWriteCloser.Read(p) |
| } |
| |
| func (b *http_readWriteCloserBody) CloseWrite() error { |
| if cw, ok := b.ReadWriteCloser.(interface{ CloseWrite() error }); ok { |
| return cw.CloseWrite() |
| } |
| return fmt.Errorf("CloseWrite: %w", http_ErrNotSupported) |
| } |
| |
| // nothingWrittenError wraps a write errors which ended up writing zero bytes. |
| type http_nothingWrittenError struct { |
| error |
| } |
| |
| func (nwe http_nothingWrittenError) Unwrap() error { |
| return nwe.error |
| } |
| |
| func (pc *http_persistConn) writeLoop() { |
| defer close(pc.writeLoopDone) |
| for { |
| select { |
| case wr := <-pc.writech: |
| startBytesWritten := pc.nwrite |
| err := wr.req.http_Request.write(pc.bw, pc.isProxy, wr.req.extra, pc.waitForContinue(wr.continueCh)) |
| if bre, ok := err.(http_requestBodyReadError); ok { |
| err = bre.error |
| // Errors reading from the user's |
| // Request.Body are high priority. |
| // Set it here before sending on the |
| // channels below or calling |
| // pc.close() which tears down |
| // connections and causes other |
| // errors. |
| wr.req.setError(err) |
| } |
| if err == nil { |
| err = pc.bw.Flush() |
| } |
| if err != nil { |
| if pc.nwrite == startBytesWritten { |
| err = http_nothingWrittenError{err} |
| } |
| } |
| pc.writeErrCh <- err // to the body reader, which might recycle us |
| wr.ch <- err // to the roundTrip function |
| if err != nil { |
| pc.close(err) |
| return |
| } |
| case <-pc.closech: |
| return |
| } |
| } |
| } |
| |
| // maxWriteWaitBeforeConnReuse is how long the a Transport RoundTrip |
| // will wait to see the Request's Body.Write result after getting a |
| // response from the server. See comments in (*persistConn).wroteRequest. |
| // |
| // In tests, we set this to a large value to avoid flakiness from inconsistent |
| // recycling of connections. |
| var http_maxWriteWaitBeforeConnReuse = 50 * time.Millisecond |
| |
| // wroteRequest is a check before recycling a connection that the previous write |
| // (from writeLoop above) happened and was successful. |
| func (pc *http_persistConn) wroteRequest() bool { |
| select { |
| case err := <-pc.writeErrCh: |
| // Common case: the write happened well before the response, so |
| // avoid creating a timer. |
| return err == nil |
| default: |
| // Rare case: the request was written in writeLoop above but |
| // before it could send to pc.writeErrCh, the reader read it |
| // all, processed it, and called us here. In this case, give the |
| // write goroutine a bit of time to finish its send. |
| // |
| // Less rare case: We also get here in the legitimate case of |
| // Issue 7569, where the writer is still writing (or stalled), |
| // but the server has already replied. In this case, we don't |
| // want to wait too long, and we want to return false so this |
| // connection isn't re-used. |
| t := time.NewTimer(http_maxWriteWaitBeforeConnReuse) |
| defer t.Stop() |
| select { |
| case err := <-pc.writeErrCh: |
| return err == nil |
| case <-t.C: |
| return false |
| } |
| } |
| } |
| |
| // responseAndError is how the goroutine reading from an HTTP/1 server |
| // communicates with the goroutine doing the RoundTrip. |
| type http_responseAndError struct { |
| _ http_incomparable |
| res *http_Response // else use this response (see res method) |
| err error |
| } |
| |
| type http_requestAndChan struct { |
| _ http_incomparable |
| treq *http_transportRequest |
| ch chan http_responseAndError // unbuffered; always send in select on callerGone |
| |
| // whether the Transport (as opposed to the user client code) |
| // added the Accept-Encoding gzip header. If the Transport |
| // set it, only then do we transparently decode the gzip. |
| addedGzip bool |
| |
| // Optional blocking chan for Expect: 100-continue (for send). |
| // If the request has an "Expect: 100-continue" header and |
| // the server responds 100 Continue, readLoop send a value |
| // to writeLoop via this chan. |
| continueCh chan<- struct{} |
| |
| callerGone <-chan struct{} // closed when roundTrip caller has returned |
| } |
| |
| // A writeRequest is sent by the caller's goroutine to the |
| // writeLoop's goroutine to write a request while the read loop |
| // concurrently waits on both the write response and the server's |
| // reply. |
| type http_writeRequest struct { |
| req *http_transportRequest |
| ch chan<- error |
| |
| // Optional blocking chan for Expect: 100-continue (for receive). |
| // If not nil, writeLoop blocks sending request body until |
| // it receives from this chan. |
| continueCh <-chan struct{} |
| } |
| |
| // httpTimeoutError represents a timeout. |
| // It implements net.Error and wraps context.DeadlineExceeded. |
| type http_timeoutError struct { |
| err string |
| } |
| |
| func (e *http_timeoutError) Error() string { return e.err } |
| |
| func (e *http_timeoutError) Timeout() bool { return true } |
| |
| func (e *http_timeoutError) Temporary() bool { return true } |
| |
| func (e *http_timeoutError) Is(err error) bool { return err == context.DeadlineExceeded } |
| |
| var http_errTimeout error = &http_timeoutError{"net/http: timeout awaiting response headers"} |
| |
| // errRequestCanceled is set to be identical to the one from h2 to facilitate |
| // testing. |
| var http_errRequestCanceled = internal.ErrRequestCanceled |
| |
| var http_errRequestCanceledConn = errors.New("net/http: request canceled while waiting for connection") // TODO: unify? |
| |
| // errRequestDone is used to cancel the round trip Context after a request is successfully done. |
| // It should not be seen by the user. |
| var http_errRequestDone = errors.New("net/http: request completed") |
| |
| func http_nop() {} |
| |
| // testHooks. Always non-nil. |
| var ( |
| http_testHookEnterRoundTrip = http_nop |
| http_testHookWaitResLoop = http_nop |
| http_testHookRoundTripRetried = http_nop |
| http_testHookPrePendingDial = http_nop |
| http_testHookPostPendingDial = http_nop |
| |
| http_testHookMu sync.Locker = http_fakeLocker{} // guards following |
| http_testHookReadLoopBeforeNextRead = http_nop |
| ) |
| |
| func (pc *http_persistConn) waitForAvailability(ctx context.Context) error { |
| select { |
| case <-pc.availch: |
| return nil |
| case <-pc.closech: |
| return pc.closed |
| case <-ctx.Done(): |
| return ctx.Err() |
| } |
| } |
| |
| func (pc *http_persistConn) roundTrip(req *http_transportRequest) (resp *http_Response, err error) { |
| http_testHookEnterRoundTrip() |
| |
| pc.mu.Lock() |
| if pc.isClientConn { |
| if !pc.reserved { |
| pc.mu.Unlock() |
| if err := pc.waitForAvailability(req.ctx); err != nil { |
| return nil, err |
| } |
| pc.mu.Lock() |
| } |
| pc.reserved = false |
| pc.inFlight = true |
| } |
| pc.numExpectedResponses++ |
| headerFn := pc.mutateHeaderFunc |
| pc.mu.Unlock() |
| |
| if headerFn != nil { |
| headerFn(req.extraHeaders()) |
| } |
| |
| // Ask for a compressed version if the caller didn't set their |
| // own value for Accept-Encoding. We only attempt to |
| // uncompress the gzip stream if we were the layer that |
| // requested it. |
| requestedGzip := false |
| if !pc.t.DisableCompression && |
| req.Header.Get("Accept-Encoding") == "" && |
| req.Header.Get("Range") == "" && |
| req.Method != "HEAD" { |
| // Request gzip only, not deflate. Deflate is ambiguous and |
| // not as universally supported anyway. |
| // See: https://zlib.net/zlib_faq.html#faq39 |
| // |
| // Note that we don't request this for HEAD requests, |
| // due to a bug in nginx: |
| // https://trac.nginx.org/nginx/ticket/358 |
| // https://golang.org/issue/5522 |
| // |
| // We don't request gzip if the request is for a range, since |
| // auto-decoding a portion of a gzipped document will just fail |
| // anyway. See https://golang.org/issue/8923 |
| requestedGzip = true |
| req.extraHeaders().Set("Accept-Encoding", "gzip") |
| } |
| |
| var continueCh chan struct{} |
| if req.ProtoAtLeast(1, 1) && req.Body != nil && req.expectsContinue() { |
| continueCh = make(chan struct{}, 1) |
| } |
| |
| if pc.t.DisableKeepAlives && |
| !req.wantsClose() && |
| !http_isProtocolSwitchHeader(req.Header) { |
| req.extraHeaders().Set("Connection", "close") |
| } |
| |
| gone := make(chan struct{}) |
| defer close(gone) |
| |
| const debugRoundTrip = false |
| |
| // Write the request concurrently with waiting for a response, |
| // in case the server decides to reply before reading our full |
| // request body. |
| startBytesWritten := pc.nwrite |
| writeErrCh := make(chan error, 1) |
| pc.writech <- http_writeRequest{req, writeErrCh, continueCh} |
| |
| resc := make(chan http_responseAndError) |
| pc.reqch <- http_requestAndChan{ |
| treq: req, |
| ch: resc, |
| addedGzip: requestedGzip, |
| continueCh: continueCh, |
| callerGone: gone, |
| } |
| |
| handleResponse := func(re http_responseAndError) (*http_Response, error) { |
| if (re.res == nil) == (re.err == nil) { |
| panic(fmt.Sprintf("internal error: exactly one of res or err should be set; nil=%v", re.res == nil)) |
| } |
| if debugRoundTrip { |
| req.logf("resc recv: %p, %T/%#v", re.res, re.err, re.err) |
| } |
| if re.err != nil { |
| return nil, pc.mapRoundTripError(req, startBytesWritten, re.err) |
| } |
| return re.res, nil |
| } |
| |
| var respHeaderTimer <-chan time.Time |
| ctxDoneChan := req.ctx.Done() |
| pcClosed := pc.closech |
| for { |
| http_testHookWaitResLoop() |
| select { |
| case err := <-writeErrCh: |
| if debugRoundTrip { |
| req.logf("writeErrCh recv: %T/%#v", err, err) |
| } |
| if err != nil { |
| pc.close(fmt.Errorf("write error: %w", err)) |
| return nil, pc.mapRoundTripError(req, startBytesWritten, err) |
| } |
| if d := pc.t.ResponseHeaderTimeout; d > 0 { |
| if debugRoundTrip { |
| req.logf("starting timer for %v", d) |
| } |
| timer := time.NewTimer(d) |
| defer timer.Stop() // prevent leaks |
| respHeaderTimer = timer.C |
| } |
| case <-pcClosed: |
| select { |
| case re := <-resc: |
| // The pconn closing raced with the response to the request, |
| // probably after the server wrote a response and immediately |
| // closed the connection. Use the response. |
| return handleResponse(re) |
| default: |
| } |
| if debugRoundTrip { |
| req.logf("closech recv: %T %#v", pc.closed, pc.closed) |
| } |
| return nil, pc.mapRoundTripError(req, startBytesWritten, pc.closed) |
| case <-respHeaderTimer: |
| if debugRoundTrip { |
| req.logf("timeout waiting for response headers.") |
| } |
| pc.close(http_errTimeout) |
| return nil, http_errTimeout |
| case re := <-resc: |
| return handleResponse(re) |
| case <-ctxDoneChan: |
| select { |
| case re := <-resc: |
| // readLoop is responsible for canceling req.ctx after |
| // it reads the response body. Check for a response racing |
| // the context close, and use the response if available. |
| return handleResponse(re) |
| default: |
| } |
| pc.cancelRequest(context.Cause(req.ctx)) |
| } |
| } |
| } |
| |
| // tLogKey is a context WithValue key for test debugging contexts containing |
| // a t.Logf func. See export_test.go's Request.WithT method. |
| type http_tLogKey struct{} |
| |
| func (tr *http_transportRequest) logf(format string, args ...any) { |
| if logf, ok := tr.http_Request.Context().Value(http_tLogKey{}).(func(string, ...any)); ok { |
| logf(time.Now().Format(time.RFC3339Nano)+": "+format, args...) |
| } |
| } |
| |
| // markReused marks this connection as having been successfully used for a |
| // request and response. |
| func (pc *http_persistConn) markReused() { |
| pc.mu.Lock() |
| pc.reused = true |
| pc.mu.Unlock() |
| } |
| |
| // close closes the underlying TCP connection and closes |
| // the pc.closech channel. |
| // |
| // The provided err is only for testing and debugging; in normal |
| // circumstances it should never be seen by users. |
| func (pc *http_persistConn) close(err error) { |
| pc.mu.Lock() |
| defer pc.mu.Unlock() |
| pc.closeLocked(err) |
| } |
| |
| func (pc *http_persistConn) closeLocked(err error) { |
| if err == nil { |
| panic("nil error") |
| } |
| if pc.closed == nil { |
| pc.closed = err |
| pc.t.decConnsPerHost(pc.cacheKey) |
| // Close HTTP/1 (pc.alt == nil) connection. |
| // HTTP/2 closes its connection itself. |
| // Close HTTP/3 connection if it implements io.Closer. |
| if pc.alt == nil { |
| if err != http_errCallerOwnsConn { |
| pc.conn.Close() |
| } |
| close(pc.closech) |
| } else { |
| if cc, ok := pc.alt.(io.Closer); ok { |
| cc.Close() |
| } |
| } |
| } |
| pc.mutateHeaderFunc = nil |
| } |
| |
| func http_schemePort(scheme string) string { |
| switch scheme { |
| case "http": |
| return "80" |
| case "https": |
| return "443" |
| case "socks5", "socks5h": |
| return "1080" |
| default: |
| return "" |
| } |
| } |
| |
| func http_idnaASCIIFromURL(url *url.URL) string { |
| addr := url.Hostname() |
| if v, err := http_idnaASCII(addr); err == nil { |
| addr = v |
| } |
| return addr |
| } |
| |
| // canonicalAddr returns url.Host but always with a ":port" suffix. |
| func http_canonicalAddr(url *url.URL) string { |
| port := url.Port() |
| if port == "" { |
| port = http_schemePort(url.Scheme) |
| } |
| return net.JoinHostPort(http_idnaASCIIFromURL(url), port) |
| } |
| |
| // bodyEOFSignal is used by the HTTP/1 transport when reading response |
| // bodies to make sure we see the end of a response body before |
| // proceeding and reading on the connection again. |
| // |
| // It wraps a ReadCloser but runs fn (if non-nil) at most |
| // once, right before its final (error-producing) Read or Close call |
| // returns. fn should return the new error to return from Read or Close. |
| // |
| // If earlyCloseFn is non-nil and Close is called before io.EOF is |
| // seen, earlyCloseFn is called instead of fn, and its return value is |
| // the return value from Close. |
| type http_bodyEOFSignal struct { |
| body io.ReadCloser |
| mu sync.Mutex // guards following 4 fields |
| closed bool // whether Close has been called |
| rerr error // sticky Read error |
| fn func(error) error // err will be nil on Read io.EOF |
| earlyCloseFn func() error // optional alt Close func used if io.EOF not seen |
| } |
| |
| var http_errReadOnClosedResBody = errors.New("http: read on closed response body") |
| |
| var http_errConcurrentReadOnResBody = errors.New("http: concurrent read on response body") |
| |
| func (es *http_bodyEOFSignal) Read(p []byte) (n int, err error) { |
| es.mu.Lock() |
| closed, rerr := es.closed, es.rerr |
| es.mu.Unlock() |
| if closed { |
| return 0, http_errReadOnClosedResBody |
| } |
| if rerr != nil { |
| return 0, rerr |
| } |
| |
| n, err = es.body.Read(p) |
| if err != nil { |
| es.mu.Lock() |
| defer es.mu.Unlock() |
| if es.rerr == nil { |
| es.rerr = err |
| } |
| err = es.condfn(err) |
| } |
| return |
| } |
| |
| func (es *http_bodyEOFSignal) Close() error { |
| es.mu.Lock() |
| defer es.mu.Unlock() |
| if es.closed { |
| return nil |
| } |
| es.closed = true |
| if es.earlyCloseFn != nil && es.rerr != io.EOF { |
| return es.earlyCloseFn() |
| } |
| err := es.body.Close() |
| return es.condfn(err) |
| } |
| |
| // caller must hold es.mu. |
| func (es *http_bodyEOFSignal) condfn(err error) error { |
| if es.fn == nil { |
| return err |
| } |
| err = es.fn(err) |
| es.fn = nil |
| return err |
| } |
| |
| // gzipReader wraps a response body so it can lazily |
| // get gzip.Reader from the pool on the first call to Read. |
| // After Close is called it puts gzip.Reader to the pool immediately |
| // if there is no Read in progress or later when Read completes. |
| type http_gzipReader struct { |
| _ http_incomparable |
| body *http_bodyEOFSignal // underlying HTTP/1 response body framing |
| mu sync.Mutex // guards zr and zerr |
| zr *gzip.Reader // stores gzip reader from the pool between reads |
| zerr error // sticky gzip reader init error or sentinel value to detect concurrent read and read after close |
| } |
| |
| type http_eofReader struct{} |
| |
| func (http_eofReader) Read([]byte) (int, error) { return 0, io.EOF } |
| |
| func (http_eofReader) ReadByte() (byte, error) { return 0, io.EOF } |
| |
| var http_gzipPool = sync.Pool{New: func() any { return new(gzip.Reader) }} |
| |
| // gzipPoolGet gets a gzip.Reader from the pool and resets it to read from r. |
| func http_gzipPoolGet(r io.Reader) (*gzip.Reader, error) { |
| zr := http_gzipPool.Get().(*gzip.Reader) |
| if err := zr.Reset(r); err != nil { |
| http_gzipPoolPut(zr) |
| return nil, err |
| } |
| return zr, nil |
| } |
| |
| // gzipPoolPut puts a gzip.Reader back into the pool. |
| func http_gzipPoolPut(zr *gzip.Reader) { |
| // Reset will allocate bufio.Reader if we pass it anything |
| // other than a flate.Reader, so ensure that it's getting one. |
| var r flate.Reader = http_eofReader{} |
| zr.Reset(r) |
| http_gzipPool.Put(zr) |
| } |
| |
| // acquire returns a gzip.Reader for reading response body. |
| // The reader must be released after use. |
| func (gz *http_gzipReader) acquire() (*gzip.Reader, error) { |
| gz.mu.Lock() |
| defer gz.mu.Unlock() |
| if gz.zerr != nil { |
| return nil, gz.zerr |
| } |
| if gz.zr == nil { |
| // gzipPoolGet might block indefinitely since it reads the gzip header. |
| // Therefore, drop mu temporarily when using gzipPoolGet. |
| // We set zerr to errConcurrentReadOnResBody to prevent concurrent read |
| // even when mu is temporarily dropped. |
| gz.zerr = http_errConcurrentReadOnResBody |
| gz.mu.Unlock() |
| zr, err := http_gzipPoolGet(gz.body) |
| gz.mu.Lock() |
| // Guard against Close being called while gzipPoolGet is running. |
| if gz.zerr != http_errConcurrentReadOnResBody { |
| if zr != nil { |
| http_gzipPoolPut(zr) |
| } |
| return nil, gz.zerr |
| } |
| gz.zr, gz.zerr = zr, err |
| if gz.zerr != nil { |
| return nil, gz.zerr |
| } |
| } |
| ret := gz.zr |
| gz.zr, gz.zerr = nil, http_errConcurrentReadOnResBody |
| return ret, nil |
| } |
| |
| // release returns the gzip.Reader to the pool if Close was called during Read. |
| func (gz *http_gzipReader) release(zr *gzip.Reader) { |
| gz.mu.Lock() |
| defer gz.mu.Unlock() |
| if gz.zerr == http_errConcurrentReadOnResBody { |
| gz.zr, gz.zerr = zr, nil |
| } else { // errReadOnClosedResBody |
| http_gzipPoolPut(zr) |
| } |
| } |
| |
| // close returns the gzip.Reader to the pool immediately or |
| // signals release to do so after Read completes. |
| func (gz *http_gzipReader) close() { |
| gz.mu.Lock() |
| defer gz.mu.Unlock() |
| if gz.zerr == nil && gz.zr != nil { |
| http_gzipPoolPut(gz.zr) |
| gz.zr = nil |
| } |
| gz.zerr = http_errReadOnClosedResBody |
| } |
| |
| func (gz *http_gzipReader) Read(p []byte) (n int, err error) { |
| zr, err := gz.acquire() |
| if err != nil { |
| return 0, err |
| } |
| defer gz.release(zr) |
| |
| return zr.Read(p) |
| } |
| |
| func (gz *http_gzipReader) Close() error { |
| gz.close() |
| |
| return gz.body.Close() |
| } |
| |
| type http_tlsHandshakeTimeoutError struct{} |
| |
| func (http_tlsHandshakeTimeoutError) Timeout() bool { return true } |
| |
| func (http_tlsHandshakeTimeoutError) Temporary() bool { return true } |
| |
| func (http_tlsHandshakeTimeoutError) Error() string { return "net/http: TLS handshake timeout" } |
| |
| // fakeLocker is a sync.Locker which does nothing. It's used to guard |
| // test-only fields when not under test, to avoid runtime atomic |
| // overhead. |
| type http_fakeLocker struct{} |
| |
| func (http_fakeLocker) Lock() {} |
| |
| func (http_fakeLocker) Unlock() {} |
| |
| // cloneTLSConfig returns a shallow clone of cfg, or a new zero tls.Config if |
| // cfg is nil. This is safe to call even if cfg is in active use by a TLS |
| // client or server. |
| // |
| // cloneTLSConfig should be an internal detail, |
| // but widely used packages access it using linkname. |
| // Notable members of the hall of shame include: |
| // - github.com/searKing/golang |
| // |
| // Do not remove or change the type signature. |
| // See go.dev/issue/67401. |
| // |
| //go:linkname cloneTLSConfig |
| func http_cloneTLSConfig(cfg *tls.Config) *tls.Config { |
| if cfg == nil { |
| return &tls.Config{} |
| } |
| return cfg.Clone() |
| } |
| |
| type http_connLRU struct { |
| ll *list.List // list.Element.Value type is of *persistConn |
| m map[*http_persistConn]*list.Element |
| } |
| |
| // add adds pc to the head of the linked list. |
| func (cl *http_connLRU) add(pc *http_persistConn) { |
| if cl.ll == nil { |
| cl.ll = list.New() |
| cl.m = make(map[*http_persistConn]*list.Element) |
| } |
| ele := cl.ll.PushFront(pc) |
| if _, ok := cl.m[pc]; ok { |
| panic("persistConn was already in LRU") |
| } |
| cl.m[pc] = ele |
| } |
| |
| func (cl *http_connLRU) removeOldest() *http_persistConn { |
| ele := cl.ll.Back() |
| pc := ele.Value.(*http_persistConn) |
| cl.ll.Remove(ele) |
| delete(cl.m, pc) |
| return pc |
| } |
| |
| // remove removes pc from cl. |
| func (cl *http_connLRU) remove(pc *http_persistConn) { |
| if ele, ok := cl.m[pc]; ok { |
| cl.ll.Remove(ele) |
| delete(cl.m, pc) |
| } |
| } |
| |
| // len returns the number of items in the cache. |
| func (cl *http_connLRU) len() int { |
| return len(cl.m) |
| } |
| |
| func http_defaultTransportDialContext(dialer *net.Dialer) func(context.Context, string, string) (net.Conn, error) { |
| return dialer.DialContext |
| } |
| ` |