func traceGotConn(req *http.Request, cc *ClientConn) { trace := httptrace.ContextClientTrace(req.Context()) if trace == nil || trace.GotConn == nil { return } ci := httptrace.GotConnInfo{Conn: cc.tconn} cc.mu.Lock() ci.Reused = cc.nextStreamID > 1 ci.WasIdle = len(cc.streams) == 0 if ci.WasIdle { ci.IdleTime = time.Now().Sub(cc.lastActive) } cc.mu.Unlock() trace.GotConn(ci) }
// extraHeaders may be nil // waitForContinue may be nil func (req *Request) write(w io.Writer, usingProxy bool, extraHeaders Header, waitForContinue func() bool) (err error) { trace := httptrace.ContextClientTrace(req.Context()) if trace != nil && trace.WroteRequest != nil { defer func() { trace.WroteRequest(httptrace.WroteRequestInfo{ Err: err, }) }() } // 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 := cleanHost(req.Host) if host == "" { if req.URL == nil { return errMissingHost } host = cleanHost(req.URL.Host) } // According to RFC 6874, an HTTP client, proxy, or other // intermediary must remove any IPv6 zone identifier attached // to an outgoing URI. host = removeZone(host) ruri := req.URL.RequestURI() if usingProxy && req.URL.Scheme != "" && req.URL.Opaque == "" { ruri = req.URL.Scheme + "://" + host + ruri } else if req.Method == "CONNECT" && req.URL.Path == "" { // CONNECT requests normally give just the host and port, not a full URL. ruri = host } // TODO(bradfitz): escape at least newlines in ruri? // 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", valueOrDefault(req.Method, "GET"), ruri) if err != nil { return err } // Header lines _, err = fmt.Fprintf(w, "Host: %s\r\n", host) if err != nil { return err } // Use the defaultUserAgent unless the Header contains one, which // may be blank to not send the header. userAgent := defaultUserAgent if _, ok := req.Header["User-Agent"]; ok { userAgent = req.Header.Get("User-Agent") } if userAgent != "" { _, err = fmt.Fprintf(w, "User-Agent: %s\r\n", userAgent) if err != nil { return err } } // Process Body,ContentLength,Close,Trailer tw, err := newTransferWriter(req) if err != nil { return err } err = tw.WriteHeader(w) if err != nil { return err } err = req.Header.WriteSubset(w, reqWriteExcludeHeader) if err != nil { return err } if extraHeaders != nil { err = extraHeaders.Write(w) 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() { req.closeBody() return nil } } // Write body and trailer err = tw.WriteBody(w) if err != nil { return err } if bw != nil { return bw.Flush() } return nil }
func requestTrace(req *http.Request) *clientTrace { trace := httptrace.ContextClientTrace(req.Context()) return (*clientTrace)(trace) }