Source File
client.go
Belonging Package
net/http
// Copyright 2009 The Go Authors. All rights reserved.// Use of this source code is governed by a BSD-style// license that can be found in the LICENSE file.// HTTP client. See RFC 7230 through 7235.//// This is the high-level Client interface.// The low-level implementation is in transport.go.package httpimport ()// 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 Client struct {// Transport specifies the mechanism by which individual// HTTP requests are made.// If nil, DefaultTransport is used.Transport 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 *Request, via []*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 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 DefaultClient = &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 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(*Request) (*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 refererForURL(, *url.URL, 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 .Scheme == "https" && .Scheme == "http" {return ""}if != "" {return}:= .String()if .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:= .User.String() + "@"= strings.Replace(, , "", 1)}return}// didTimeout is non-nil only if err != nil.func ( *Client) ( *Request, time.Time) ( *Response, func() bool, error) {if .Jar != nil {for , := range .Jar.Cookies(.URL) {.AddCookie()}}, , = send(, .transport(), )if != nil {return nil, ,}if .Jar != nil {if := .Cookies(); len() > 0 {.Jar.SetCookies(.URL, )}}return , nil, nil}func ( *Client) () time.Time {if .Timeout > 0 {return time.Now().Add(.Timeout)}return time.Time{}}func ( *Client) () RoundTripper {if .Transport != nil {return .Transport}return DefaultTransport}// ErrSchemeMismatch is returned when a server returns an HTTP response to an HTTPS client.var 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 send( *Request, RoundTripper, time.Time) ( *Response, func() bool, error) {:= // req is either the original request, or a modified forkif == nil {.closeBody()return nil, alwaysFalse, errors.New("http: no Client.Transport or DefaultTransport")}if .URL == nil {.closeBody()return nil, alwaysFalse, errors.New("http: nil Request.URL")}if .RequestURI != "" {.closeBody()return nil, 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.:= func() {if == {= new(Request)* = * // 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 .Header == nil {().Header = make(Header)}if := .URL.User; != nil && .Header.Get("Authorization") == "" {:= .Username(), := .Password()().Header = cloneOrMakeHeader(.Header).Header.Set("Authorization", "Basic "+basicAuth(, ))}if !.IsZero() {()}, := setRequestCancel(, , ), = .RoundTrip()if != nil {()if != nil {log.Printf("RoundTripper returned a response & error; ignoring response")}if , := .(tls.RecordHeaderError); {// 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(.RecordHeader[:]) == "HTTP/" {= ErrSchemeMismatch}}return nil, ,}if == nil {return nil, , fmt.Errorf("http: RoundTripper implementation (%T) returned a nil *Response with a nil error", )}if .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 .ContentLength > 0 && .Method != "HEAD" {return nil, , fmt.Errorf("http: RoundTripper implementation (%T) returned a *Response with content length %d but a nil Body", , .ContentLength)}.Body = io.NopCloser(strings.NewReader(""))}if !.IsZero() {.Body = &cancelTimerBody{stop: ,rc: .Body,reqDidTimeout: ,}}return , 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 timeBeforeContextDeadline( time.Time, context.Context) bool {, := .Deadline()if ! {return true}return .Before()}// 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 knownRoundTripperImpl( RoundTripper, *Request) bool {switch t := .(type) {case *Transport:if := .alternateRoundTripper(); != nil {return (, )}return truecase *http2Transport, http2noDialH2RoundTripper: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().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 setRequestCancel( *Request, RoundTripper, time.Time) ( func(), func() bool) {if .IsZero() {return nop, alwaysFalse}:= knownRoundTripperImpl(, ):= .Context()if .Cancel == nil && {// If they already had a Request.Context that's// expiring sooner, do nothing:if !timeBeforeContextDeadline(, ) {return nop, alwaysFalse}var func().ctx, = context.WithDeadline(, )return , func() bool { return time.Now().After() }}:= .Cancel // the user's original Request.Cancel, if anyvar func()if timeBeforeContextDeadline(, ) {.ctx, = context.WithDeadline(, )}:= make(chan struct{}).Cancel =:= func() {// The second way in the func comment above:close()// The first way, used only for RoundTripper// implementations written before Go 1.5 or Go 1.6.type interface{ (*Request) }if , := .(); {.()}}:= make(chan struct{})= sync.OnceFunc(func() {close()if != nil {()}}):= time.NewTimer(time.Until())var atomic.Boolgo func() {select {case <-:().Stop()case <-.C:.Store(true)()case <-:.Stop()}}()return , .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 basicAuth(, string) string {:= + ":" +return base64.StdEncoding.EncodeToString([]byte())}// 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 ( string) ( *Response, error) {return DefaultClient.Get()}// 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 ( *Client) ( string) ( *Response, error) {, := NewRequest("GET", , nil)if != nil {return nil,}return .Do()}func 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 ErrUseLastResponse = errors.New("net/http: use last response")// checkRedirect calls either the user's configured CheckRedirect// function, or the default.func ( *Client) ( *Request, []*Request) error {:= .CheckRedirectif == nil {= defaultCheckRedirect}return (, )}// redirectBehavior describes what should happen when the// client encounters a 3xx status code from the server.func redirectBehavior( string, *Response, *Request) ( string, , bool) {switch .StatusCode {case 301, 302, 303:== true= 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 != "GET" && != "HEAD" {= "GET"}case 307, 308:== true= trueif .GetBody == nil && .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.= false}}return , ,}// urlErrorOp returns the (*url.Error).Op value to use for the// provided (*Request).Method value.func urlErrorOp( string) string {if == "" {return "Get"}if , := ascii.ToLower(); {return [:1] + [1:]}return}// 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.//// 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.//// 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 ( *Client) ( *Request) (*Response, error) {return .do()}var testHookClientDoResult func(retres *Response, reterr error)func ( *Client) ( *Request) ( *Response, error) {if testHookClientDoResult != nil {defer func() { testHookClientDoResult(, ) }()}if .URL == nil {.closeBody()return nil, &url.Error{Op: urlErrorOp(.Method),Err: errors.New("http: nil Request.URL"),}}_ = * // panic early if c is nil; see go.dev/issue/53521var (= .deadline()[]*Request*Response= .makeHeadersCopier()= false // have we closed the current req.Body?// Redirect behavior:string= true= false):= func( error) error {// the body may have been closed already by c.send()if ! {.closeBody()}var stringif != nil && .Request != nil {= stripPassword(.Request.URL)} else {= stripPassword(.URL)}return &url.Error{Op: urlErrorOp([0].Method),URL: ,Err: ,}}for {// For all but the first request, create the next// request hop and replace req.if len() > 0 {:= .Header.Get("Location")if == "" {// 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 , nil}, := .URL.Parse()if != nil {.closeBody()return nil, (fmt.Errorf("failed to parse Location header %q: %v", , ))}:= ""if .Host != "" && .Host != .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 , := url.Parse(); != nil && !.IsAbs() {= .Host}}:= [0]= &Request{Method: ,Response: ,URL: ,Header: make(Header),Host: ,Cancel: .Cancel,ctx: .ctx,}if && .GetBody != nil {.Body, = .GetBody()if != nil {.closeBody()return nil, ()}.GetBody = .GetBody.ContentLength = .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 ! && [0].URL.Host != .URL.Host {if !shouldCopyHeaderOnRedirect([0].URL, .URL) {= true}}(, )// Add the Referer header from the most recent// request URL to the new one, if it's not https->http:if := refererForURL([len()-1].URL, .URL, .Header.Get("Referer")); != "" {.Header.Set("Referer", )}= .checkRedirect(, )// Sentinel error to let users select the// previous response, without closing its// body. See Issue 10069.if == ErrUseLastResponse {return , 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 = 2 << 10if .ContentLength == -1 || .ContentLength <= {io.CopyN(io.Discard, .Body, )}.Body.Close()if != 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.:= ().(*url.Error).URL =return ,}}= append(, )var errorvar func() boolif , , = .send(, ); != nil {// c.send() always closes req.Body= trueif !.IsZero() && () {= &timeoutError{.Error() + " (Client.Timeout exceeded while awaiting headers)"}}return nil, ()}var , bool, , = redirectBehavior(.Method, , [0])if ! {return , nil}if ! {// Once a hop drops the body, we never send it again// (because we're now handling a redirect for a request with no body).= false}.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 ( *Client) ( *Request) func( *Request, 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 (= cloneOrMakeHeader(.Header)map[string][]*Cookie)if .Jar != nil && .Header.Get("Cookie") != "" {= make(map[string][]*Cookie)for , := range .Cookies() {[.Name] = append([.Name], )}}return func( *Request, 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/17494if .Jar != nil && != nil {var bool:= .Response // The response that caused the upcoming redirectfor , := range .Cookies() {if , := [.Name]; {delete(, .Name)= true}}if {.Del("Cookie")var []stringfor , := range {for , := range {= append(, .Name+"="+.Value)}}slices.Sort() // Ensure deterministic headers.Set("Cookie", strings.Join(, "; "))}}// Copy the initial request's Header values// (at least the safe ones).for , := range {:= falseswitch CanonicalHeaderKey() {case "Authorization", "Www-Authenticate", "Cookie", "Cookie2","Proxy-Authorization", "Proxy-Authenticate":= true}if !( && ) {.Header[] =}}}}func defaultCheckRedirect( *Request, []*Request) error {if len() >= 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 (, string, io.Reader) ( *Response, error) {return DefaultClient.Post(, , )}// 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 ( *Client) (, string, io.Reader) ( *Response, error) {, := NewRequest("POST", , )if != nil {return nil,}.Header.Set("Content-Type", )return .Do()}// 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 ( string, url.Values) ( *Response, error) {return DefaultClient.PostForm(, )}// 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 ( *Client) ( string, url.Values) ( *Response, error) {return .Post(, "application/x-www-form-urlencoded", strings.NewReader(.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 ( string) ( *Response, error) {return DefaultClient.Head()}// 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 ( *Client) ( string) ( *Response, error) {, := NewRequest("HEAD", , nil)if != nil {return nil,}return .Do()}// 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 ( *Client) () {type interface {()}if , := .transport().(); {.()}}// 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 cancelTimerBody struct {stop func() // stops the time.Timer waiting to cancel the requestrc io.ReadCloserreqDidTimeout func() bool}func ( *cancelTimerBody) ( []byte) ( int, error) {, = .rc.Read()if == nil {return , nil}if == io.EOF {return ,}if .reqDidTimeout() {= &timeoutError{.Error() + " (Client.Timeout or context cancellation while reading body)"}}return ,}func ( *cancelTimerBody) () error {:= .rc.Close().stop()return}func shouldCopyHeaderOnRedirect(, *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.:= idnaASCIIFromURL():= idnaASCIIFromURL()return isDomainOrSubdomain(, )}// isDomainOrSubdomain reports whether sub is a subdomain (or exact// match) of the parent domain.//// Both domains must already be in canonical form.func isDomainOrSubdomain(, string) bool {if == {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(, ":%") {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(, ) {return false}return [len()-len()-1] == '.'}func stripPassword( *url.URL) string {, := .User.Password()if {return strings.Replace(.String(), .User.String()+"@", .User.Username()+":***@", 1)}return .String()}
![]() |
The pages are generated with Golds v0.7.9-preview. (GOOS=linux GOARCH=amd64) Golds is a Go 101 project developed by Tapir Liu. PR and bug reports are welcome and can be submitted to the issue list. Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds. |