net/http Transport

$ go version go version go1.20.5 darwin/arm64 DefaultTransport 看一下 DefaultTransport 的 RoundTrip 方法。 1// src/net/http/roundtrip.go#L8 2// RoundTrip implements the RoundTripper interface. 3// 4// For higher-level HTTP client support (such as handling of cookies 5// and redirects), see Get, Post, and the Client type. 6// 7// Like the RoundTripper interface, the error types returned 8// by RoundTrip are unspecified. 9func (t *Transport) RoundTrip(req *Request) (*Response, error) { 10 return t.roundTrip(req) 11} 12 13// src/net/http/transport.go#L510 14// roundTrip implements a RoundTripper over HTTP. 15func (t *Transport) roundTrip(req *Request) (*Response, error) { 16 t.nextProtoOnce.Do(t.onceSetNextProtoDefaults) 17 ctx := req.Context() 18 trace := httptrace.ContextClientTrace(ctx) 19 20 if req.URL == nil { 21 req.closeBody() 22 return nil, errors.New("http: nil Request.URL") 23 } 24 if req.Header == nil { 25 req.closeBody() 26 return nil, errors.New("http: nil Request.Header") 27 } 28 scheme := req.URL.Scheme 29 isHTTP := scheme == "http" || scheme == "https" 30 if isHTTP { 31 for k, vv := range req.Header { 32 if !httpguts.ValidHeaderFieldName(k) { 33 req.closeBody() 34 return nil, fmt.Errorf("net/http: invalid header field name %q", k) 35 } 36 for _, v := range vv { 37 if !httpguts.ValidHeaderFieldValue(v) { 38 req.closeBody() 39 // Don't include the value in the error, because it may be sensitive. 40 return nil, fmt.Errorf("net/http: invalid header field value for %q", k) 41 } 42 } 43 } 44 } 45 46 origReq := req 47 cancelKey := cancelKey{origReq} 48 req = setupRewindBody(req) 49 50 if altRT := t.alternateRoundTripper(req); altRT != nil { 51 if resp, err := altRT.RoundTrip(req); err != ErrSkipAltProtocol { 52 return resp, err 53 } 54 var err error 55 req, err = rewindBody(req) 56 if err != nil { 57 return nil, err 58 } 59 } 60 if !isHTTP { 61 req.closeBody() 62 return nil, badStringError("unsupported protocol scheme", scheme) 63 } 64 if req.Method != "" && !validMethod(req.Method) { 65 req.closeBody() 66 return nil, fmt.Errorf("net/http: invalid method %q", req.Method) 67 } 68 if req.URL.Host == "" { 69 req.closeBody() 70 return nil, errors.New("http: no Host in request URL") 71 } 72 73 for { 74 select { 75 case <-ctx.Done(): 76 req.closeBody() 77 return nil, ctx.Err() 78 default: 79 } 80 81 // treq gets modified by roundTrip, so we need to recreate for each retry. 82 treq := &transportRequest{Request: req, trace: trace, cancelKey: cancelKey} 83 cm, err := t.connectMethodForRequest(treq) 84 if err != nil { 85 req.closeBody() 86 return nil, err 87 } 88 89 // Get the cached or newly-created connection to either the 90 // host (for http or https), the http proxy, or the http proxy 91 // pre-CONNECTed to https server. In any case, we'll be ready 92 // to send it requests. 93 pconn, err := t.getConn(treq, cm) 94 if err != nil { 95 t.setReqCanceler(cancelKey, nil) 96 req.closeBody() 97 return nil, err 98 } 99 100 var resp *Response 101 if pconn.alt != nil { 102 // HTTP/2 path. 103 t.setReqCanceler(cancelKey, nil) // not cancelable with CancelRequest 104 resp, err = pconn.alt.RoundTrip(req) 105 } else { 106 resp, err = pconn.roundTrip(treq) 107 } 108 if err == nil { 109 resp.Request = origReq 110 return resp, nil 111 } 112 113 // Failed. Clean up and determine whether to retry. 114 if http2isNoCachedConnError(err) { 115 if t.removeIdleConn(pconn) { 116 t.decConnsPerHost(pconn.cacheKey) 117 } 118 } else if !pconn.shouldRetryRequest(req, err) { 119 // Issue 16465: return underlying net.Conn.Read error from peek, 120 // as we've historically done. 121 if e, ok := err.(nothingWrittenError); ok { 122 err = e.error 123 } 124 if e, ok := err.(transportReadFromServerError); ok { 125 err = e.err 126 } 127 return nil, err 128 } 129 testHookRoundTripRetried() 130 131 // Rewind the body if we're able to. 132 req, err = rewindBody(req) 133 if err != nil { 134 return nil, err 135 } 136 } 137} 因为 RoundTripper 说明了请求的 URL 和 Header 必须初始化,所以一开始检查报错。再往后,如果 schema 是 http 的话,检查了一下 header 的键值对的非法字符。再往后,注意到有一个 setupRewindBody,是用于恢复请求 body 的 ...

August 29, 2023 · 🦉

net/http Client

$ go version go version go1.20.5 darwin/arm64 如果一个文件一个文件挨着看未免也太枯燥了,并且没有和实践结合,感觉看了也体会不深。这里从发起一个请求到最后收到应答的整个流程,跟随一个 http.Request 的生命周期去探索相关的源码。 http.Client 开始发请求之前先看一下 http.Client 结构体 1// src/net/http/client.go#L29 2// A Client is an HTTP client. Its zero value (DefaultClient) is a 3// usable client that uses DefaultTransport. 4// 5// The Client's Transport typically has internal state (cached TCP 6// connections), so Clients should be reused instead of created as 7// needed. Clients are safe for concurrent use by multiple goroutines. 8// 9// A Client is higher-level than a RoundTripper (such as Transport) 10// and additionally handles HTTP details such as cookies and 11// redirects. 12// 13// When following redirects, the Client will forward all headers set on the 14// initial Request except: 15// 16// • when forwarding sensitive headers like "Authorization", 17// "WWW-Authenticate", and "Cookie" to untrusted targets. 18// These headers will be ignored when following a redirect to a domain 19// that is not a subdomain match or exact match of the initial domain. 20// For example, a redirect from "foo.com" to either "foo.com" or "sub.foo.com" 21// will forward the sensitive headers, but a redirect to "bar.com" will not. 22// 23// • when forwarding the "Cookie" header with a non-nil cookie Jar. 24// Since each redirect may mutate the state of the cookie jar, 25// a redirect may possibly alter a cookie set in the initial request. 26// When forwarding the "Cookie" header, any mutated cookies will be omitted, 27// with the expectation that the Jar will insert those mutated cookies 28// with the updated values (assuming the origin matches). 29// If Jar is nil, the initial cookies are forwarded without change. 30type Client struct { 31 // Transport specifies the mechanism by which individual 32 // HTTP requests are made. 33 // If nil, DefaultTransport is used. 34 Transport RoundTripper 35 36 // CheckRedirect specifies the policy for handling redirects. 37 // If CheckRedirect is not nil, the client calls it before 38 // following an HTTP redirect. The arguments req and via are 39 // the upcoming request and the requests made already, oldest 40 // first. If CheckRedirect returns an error, the Client's Get 41 // method returns both the previous Response (with its Body 42 // closed) and CheckRedirect's error (wrapped in a url.Error) 43 // instead of issuing the Request req. 44 // As a special case, if CheckRedirect returns ErrUseLastResponse, 45 // then the most recent response is returned with its body 46 // unclosed, along with a nil error. 47 // 48 // If CheckRedirect is nil, the Client uses its default policy, 49 // which is to stop after 10 consecutive requests. 50 CheckRedirect func(req *Request, via []*Request) error 51 52 // Jar specifies the cookie jar. 53 // 54 // The Jar is used to insert relevant cookies into every 55 // outbound Request and is updated with the cookie values 56 // of every inbound Response. The Jar is consulted for every 57 // redirect that the Client follows. 58 // 59 // If Jar is nil, cookies are only sent if they are explicitly 60 // set on the Request. 61 Jar CookieJar 62 63 // Timeout specifies a time limit for requests made by this 64 // Client. The timeout includes connection time, any 65 // redirects, and reading the response body. The timer remains 66 // running after Get, Head, Post, or Do return and will 67 // interrupt reading of the Response.Body. 68 // 69 // A Timeout of zero means no timeout. 70 // 71 // The Client cancels requests to the underlying Transport 72 // as if the Request's Context ended. 73 // 74 // For compatibility, the Client will also use the deprecated 75 // CancelRequest method on Transport if found. New 76 // RoundTripper implementations should use the Request's Context 77 // for cancellation instead of implementing CancelRequest. 78 Timeout time.Duration 79} 80 81// DefaultClient is the default Client and is used by Get, Head, and Post. 82var DefaultClient = &Client{} 83 84// RoundTripper is an interface representing the ability to execute a 85// single HTTP transaction, obtaining the Response for a given Request. 86// 87// A RoundTripper must be safe for concurrent use by multiple 88// goroutines. 89type RoundTripper interface { 90 // RoundTrip executes a single HTTP transaction, returning 91 // a Response for the provided Request. 92 // 93 // RoundTrip should not attempt to interpret the response. In 94 // particular, RoundTrip must return err == nil if it obtained 95 // a response, regardless of the response's HTTP status code. 96 // A non-nil err should be reserved for failure to obtain a 97 // response. Similarly, RoundTrip should not attempt to 98 // handle higher-level protocol details such as redirects, 99 // authentication, or cookies. 100 // 101 // RoundTrip should not modify the request, except for 102 // consuming and closing the Request's Body. RoundTrip may 103 // read fields of the request in a separate goroutine. Callers 104 // should not mutate or reuse the request until the Response's 105 // Body has been closed. 106 // 107 // RoundTrip must always close the body, including on errors, 108 // but depending on the implementation may do so in a separate 109 // goroutine even after RoundTrip returns. This means that 110 // callers wanting to reuse the body for subsequent requests 111 // must arrange to wait for the Close call before doing so. 112 // 113 // The Request's URL and Header fields must be initialized. 114 RoundTrip(*Request) (*Response, error) 115} 简单翻译一下注释。 ...

August 29, 2023 · 🦉