github.com/coreos/goproxy@v0.0.0-20190513173959-f8dc2d7ba04e/transport/transport.go (about)

     1  // Copyright 2011 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // HTTP client implementation. See RFC 2616.
     6  // 
     7  // This is the low-level Transport implementation of RoundTripper.
     8  // The high-level interface is in client.go.
     9  
    10  // This file is DEPRECATED and keep solely for backward compatibility.
    11  
    12  package transport
    13  
    14  import (
    15  	"net/http"
    16  	"bufio"
    17  	"compress/gzip"
    18  	"crypto/tls"
    19  	"encoding/base64"
    20  	"errors"
    21  	"fmt"
    22  	"io"
    23  	"io/ioutil"
    24  	"log"
    25  	"net"
    26  	"net/url"
    27  	"os"
    28  	"strings"
    29  	"sync"
    30  )
    31  
    32  // DefaultTransport is the default implementation of Transport and is
    33  // used by DefaultClient.  It establishes a new network connection for
    34  // each call to Do and uses HTTP proxies as directed by the
    35  // $HTTP_PROXY and $NO_PROXY (or $http_proxy and $no_proxy)
    36  // environment variables.
    37  var DefaultTransport RoundTripper = &Transport{Proxy: ProxyFromEnvironment}
    38  
    39  // DefaultMaxIdleConnsPerHost is the default value of Transport's
    40  // MaxIdleConnsPerHost.
    41  const DefaultMaxIdleConnsPerHost = 2
    42  
    43  // Transport is an implementation of RoundTripper that supports http,
    44  // https, and http proxies (for either http or https with CONNECT).
    45  // Transport can also cache connections for future re-use.
    46  type Transport struct {
    47  	lk       sync.Mutex
    48  	idleConn map[string][]*persistConn
    49  	altProto map[string]RoundTripper // nil or map of URI scheme => RoundTripper
    50  
    51  	// TODO: tunable on global max cached connections
    52  	// TODO: tunable on timeout on cached connections
    53  	// TODO: optional pipelining
    54  
    55  	// Proxy specifies a function to return a proxy for a given
    56  	// Request. If the function returns a non-nil error, the
    57  	// request is aborted with the provided error.
    58  	// If Proxy is nil or returns a nil *URL, no proxy is used.
    59  	Proxy func(*http.Request) (*url.URL, error)
    60  
    61  	// Dial specifies the dial function for creating TCP
    62  	// connections.
    63  	// If Dial is nil, net.Dial is used.
    64  	Dial func(net, addr string) (c net.Conn, err error)
    65  
    66  	// TLSClientConfig specifies the TLS configuration to use with
    67  	// tls.Client. If nil, the default configuration is used.
    68  	TLSClientConfig *tls.Config
    69  
    70  	DisableKeepAlives  bool
    71  	DisableCompression bool
    72  
    73  	// MaxIdleConnsPerHost, if non-zero, controls the maximum idle
    74  	// (keep-alive) to keep to keep per-host.  If zero,
    75  	// DefaultMaxIdleConnsPerHost is used.
    76  	MaxIdleConnsPerHost int
    77  }
    78  
    79  // ProxyFromEnvironment returns the URL of the proxy to use for a
    80  // given request, as indicated by the environment variables
    81  // $HTTP_PROXY and $NO_PROXY (or $http_proxy and $no_proxy).
    82  // An error is returned if the proxy environment is invalid.
    83  // A nil URL and nil error are returned if no proxy is defined in the
    84  // environment, or a proxy should not be used for the given request.
    85  func ProxyFromEnvironment(req *http.Request) (*url.URL, error) {
    86  	proxy := getenvEitherCase("HTTP_PROXY")
    87  	if proxy == "" {
    88  		return nil, nil
    89  	}
    90  	if !useProxy(canonicalAddr(req.URL)) {
    91  		return nil, nil
    92  	}
    93  	proxyURL, err := url.Parse(proxy)
    94  	if err != nil || proxyURL.Scheme == "" {
    95  		if u, err := url.Parse("http://" + proxy); err == nil {
    96  			proxyURL = u
    97  			err = nil
    98  		}
    99  	}
   100  	if err != nil {
   101  		return nil, fmt.Errorf("invalid proxy address %q: %v", proxy, err)
   102  	}
   103  	return proxyURL, nil
   104  }
   105  
   106  // ProxyURL returns a proxy function (for use in a Transport)
   107  // that always returns the same URL.
   108  func ProxyURL(fixedURL *url.URL) func(*http.Request) (*url.URL, error) {
   109  	return func(*http.Request) (*url.URL, error) {
   110  		return fixedURL, nil
   111  	}
   112  }
   113  
   114  // transportRequest is a wrapper around a *Request that adds
   115  // optional extra headers to write.
   116  type transportRequest struct {
   117  	*http.Request        // original request, not to be mutated
   118  	extra    http.Header // extra headers to write, or nil
   119  }
   120  
   121  func (tr *transportRequest) extraHeaders() http.Header {
   122  	if tr.extra == nil {
   123  		tr.extra = make(http.Header)
   124  	}
   125  	return tr.extra
   126  }
   127  
   128  type RoundTripDetails struct {
   129  	Host string
   130  	TCPAddr *net.TCPAddr
   131  	IsProxy bool
   132  	Error error
   133  }
   134  
   135  func (t *Transport) DetailedRoundTrip(req *http.Request) (details *RoundTripDetails, resp *http.Response, err error) {
   136  	if req.URL == nil {
   137  		return nil, nil, errors.New("http: nil Request.URL")
   138  	}
   139  	if req.Header == nil {
   140  		return nil, nil, errors.New("http: nil Request.Header")
   141  	}
   142  	if req.URL.Scheme != "http" && req.URL.Scheme != "https" {
   143  		t.lk.Lock()
   144  		var rt RoundTripper
   145  		if t.altProto != nil {
   146  			rt = t.altProto[req.URL.Scheme]
   147  		}
   148  		t.lk.Unlock()
   149  		if rt == nil {
   150  			return nil, nil, &badStringError{"unsupported protocol scheme", req.URL.Scheme}
   151  		}
   152  		return rt.DetailedRoundTrip(req)
   153  	}
   154  	treq := &transportRequest{Request: req}
   155  	cm, err := t.connectMethodForRequest(treq)
   156  	if err != nil {
   157  		return nil, nil, err
   158  	}
   159  
   160  	// Get the cached or newly-created connection to either the
   161  	// host (for http or https), the http proxy, or the http proxy
   162  	// pre-CONNECTed to https server.  In any case, we'll be ready
   163  	// to send it requests.
   164  	pconn, err := t.getConn(cm)
   165  	if err != nil {
   166  		return nil, nil, err
   167  	}
   168  
   169  	resp, err = pconn.roundTrip(treq)
   170  	return &RoundTripDetails{pconn.host, pconn.ip, pconn.isProxy, err}, resp, err
   171  }
   172  
   173  // RoundTrip implements the RoundTripper interface.
   174  func (t *Transport) RoundTrip(req *http.Request) (resp *http.Response, err error) {
   175  	_, resp, err = t.DetailedRoundTrip(req)
   176  	return
   177  }
   178  
   179  // RegisterProtocol registers a new protocol with scheme.
   180  // The Transport will pass requests using the given scheme to rt.
   181  // It is rt's responsibility to simulate HTTP request semantics.
   182  //
   183  // RegisterProtocol can be used by other packages to provide
   184  // implementations of protocol schemes like "ftp" or "file".
   185  func (t *Transport) RegisterProtocol(scheme string, rt RoundTripper) {
   186  	if scheme == "http" || scheme == "https" {
   187  		panic("protocol " + scheme + " already registered")
   188  	}
   189  	t.lk.Lock()
   190  	defer t.lk.Unlock()
   191  	if t.altProto == nil {
   192  		t.altProto = make(map[string]RoundTripper)
   193  	}
   194  	if _, exists := t.altProto[scheme]; exists {
   195  		panic("protocol " + scheme + " already registered")
   196  	}
   197  	t.altProto[scheme] = rt
   198  }
   199  
   200  // CloseIdleConnections closes any connections which were previously
   201  // connected from previous requests but are now sitting idle in
   202  // a "keep-alive" state. It does not interrupt any connections currently
   203  // in use.
   204  func (t *Transport) CloseIdleConnections() {
   205  	t.lk.Lock()
   206  	defer t.lk.Unlock()
   207  	if t.idleConn == nil {
   208  		return
   209  	}
   210  	for _, conns := range t.idleConn {
   211  		for _, pconn := range conns {
   212  			pconn.close()
   213  		}
   214  	}
   215  	t.idleConn = make(map[string][]*persistConn)
   216  }
   217  
   218  //
   219  // Private implementation past this point.
   220  //
   221  
   222  func getenvEitherCase(k string) string {
   223  	if v := os.Getenv(strings.ToUpper(k)); v != "" {
   224  		return v
   225  	}
   226  	return os.Getenv(strings.ToLower(k))
   227  }
   228  
   229  func (t *Transport) connectMethodForRequest(treq *transportRequest) (*connectMethod, error) {
   230  	cm := &connectMethod{
   231  		targetScheme: treq.URL.Scheme,
   232  		targetAddr:   canonicalAddr(treq.URL),
   233  	}
   234  	if t.Proxy != nil {
   235  		var err error
   236  		cm.proxyURL, err = t.Proxy(treq.Request)
   237  		if err != nil {
   238  			return nil, err
   239  		}
   240  	}
   241  	return cm, nil
   242  }
   243  
   244  // proxyAuth returns the Proxy-Authorization header to set
   245  // on requests, if applicable.
   246  func (cm *connectMethod) proxyAuth() string {
   247  	if cm.proxyURL == nil {
   248  		return ""
   249  	}
   250  	if u := cm.proxyURL.User; u != nil {
   251  		return "Basic " + base64.URLEncoding.EncodeToString([]byte(u.String()))
   252  	}
   253  	return ""
   254  }
   255  
   256  // putIdleConn adds pconn to the list of idle persistent connections awaiting
   257  // a new request.
   258  // If pconn is no longer needed or not in a good state, putIdleConn
   259  // returns false.
   260  func (t *Transport) putIdleConn(pconn *persistConn) bool {
   261  	t.lk.Lock()
   262  	defer t.lk.Unlock()
   263  	if t.DisableKeepAlives || t.MaxIdleConnsPerHost < 0 {
   264  		pconn.close()
   265  		return false
   266  	}
   267  	if pconn.isBroken() {
   268  		return false
   269  	}
   270  	key := pconn.cacheKey
   271  	max := t.MaxIdleConnsPerHost
   272  	if max == 0 {
   273  		max = DefaultMaxIdleConnsPerHost
   274  	}
   275  	if len(t.idleConn[key]) >= max {
   276  		pconn.close()
   277  		return false
   278  	}
   279  	t.idleConn[key] = append(t.idleConn[key], pconn)
   280  	return true
   281  }
   282  
   283  func (t *Transport) getIdleConn(cm *connectMethod) (pconn *persistConn) {
   284  	t.lk.Lock()
   285  	defer t.lk.Unlock()
   286  	if t.idleConn == nil {
   287  		t.idleConn = make(map[string][]*persistConn)
   288  	}
   289  	key := cm.String()
   290  	for {
   291  		pconns, ok := t.idleConn[key]
   292  		if !ok {
   293  			return nil
   294  		}
   295  		if len(pconns) == 1 {
   296  			pconn = pconns[0]
   297  			delete(t.idleConn, key)
   298  		} else {
   299  			// 2 or more cached connections; pop last
   300  			// TODO: queue?
   301  			pconn = pconns[len(pconns)-1]
   302  			t.idleConn[key] = pconns[0 : len(pconns)-1]
   303  		}
   304  		if !pconn.isBroken() {
   305  			return
   306  		}
   307  	}
   308  	return
   309  }
   310  
   311  func (t *Transport) dial(network, addr string) (c net.Conn, raddr string, ip *net.TCPAddr, err error) {
   312  	if t.Dial != nil {
   313  		ip, err = net.ResolveTCPAddr("tcp", addr)
   314  		if err!=nil {
   315  			return
   316  		}
   317  		c, err = t.Dial(network, addr)
   318  		raddr = addr
   319  		return
   320  	}
   321  	addri, err := net.ResolveTCPAddr("tcp", addr)
   322  	if err!=nil {
   323  		return
   324  	}
   325  	c, err = net.DialTCP("tcp", nil, addri)
   326  	raddr = addr
   327  	ip = addri
   328  	return
   329  }
   330  
   331  // getConn dials and creates a new persistConn to the target as
   332  // specified in the connectMethod.  This includes doing a proxy CONNECT
   333  // and/or setting up TLS.  If this doesn't return an error, the persistConn
   334  // is ready to write requests to.
   335  func (t *Transport) getConn(cm *connectMethod) (*persistConn, error) {
   336  	if pc := t.getIdleConn(cm); pc != nil {
   337  		return pc, nil
   338  	}
   339  
   340  	conn, raddr, ip, err := t.dial("tcp", cm.addr())
   341  	if err != nil {
   342  		if cm.proxyURL != nil {
   343  			err = fmt.Errorf("http: error connecting to proxy %s: %v", cm.proxyURL, err)
   344  		}
   345  		return nil, err
   346  	}
   347  
   348  	pa := cm.proxyAuth()
   349  
   350  	pconn := &persistConn{
   351  		t:        t,
   352  		cacheKey: cm.String(),
   353  		conn:     conn,
   354  		reqch:    make(chan requestAndChan, 50),
   355  		host: raddr,
   356  		ip: ip,
   357  	}
   358  
   359  	switch {
   360  	case cm.proxyURL == nil:
   361  		// Do nothing.
   362  	case cm.targetScheme == "http":
   363  		pconn.isProxy = true
   364  		if pa != "" {
   365  			pconn.mutateHeaderFunc = func(h http.Header) {
   366  				h.Set("Proxy-Authorization", pa)
   367  			}
   368  		}
   369  	case cm.targetScheme == "https":
   370  		connectReq := &http.Request{
   371  			Method: "CONNECT",
   372  			URL:    &url.URL{Opaque: cm.targetAddr},
   373  			Host:   cm.targetAddr,
   374  			Header: make(http.Header),
   375  		}
   376  		if pa != "" {
   377  			connectReq.Header.Set("Proxy-Authorization", pa)
   378  		}
   379  		connectReq.Write(conn)
   380  
   381  		// Read response.
   382  		// Okay to use and discard buffered reader here, because
   383  		// TLS server will not speak until spoken to.
   384  		br := bufio.NewReader(conn)
   385  		resp, err := http.ReadResponse(br, connectReq)
   386  		if err != nil {
   387  			conn.Close()
   388  			return nil, err
   389  		}
   390  		if resp.StatusCode != 200 {
   391  			f := strings.SplitN(resp.Status, " ", 2)
   392  			conn.Close()
   393  			return nil, errors.New(f[1])
   394  		}
   395  	}
   396  
   397  	if cm.targetScheme == "https" {
   398  		// Initiate TLS and check remote host name against certificate.
   399  		conn = tls.Client(conn, t.TLSClientConfig)
   400  		if err = conn.(*tls.Conn).Handshake(); err != nil {
   401  			return nil, err
   402  		}
   403  		if t.TLSClientConfig == nil || !t.TLSClientConfig.InsecureSkipVerify {
   404  			if err = conn.(*tls.Conn).VerifyHostname(cm.tlsHost()); err != nil {
   405  				return nil, err
   406  			}
   407  		}
   408  		pconn.conn = conn
   409  	}
   410  
   411  	pconn.br = bufio.NewReader(pconn.conn)
   412  	pconn.bw = bufio.NewWriter(pconn.conn)
   413  	go pconn.readLoop()
   414  	return pconn, nil
   415  }
   416  
   417  // useProxy returns true if requests to addr should use a proxy,
   418  // according to the NO_PROXY or no_proxy environment variable.
   419  // addr is always a canonicalAddr with a host and port.
   420  func useProxy(addr string) bool {
   421  	if len(addr) == 0 {
   422  		return true
   423  	}
   424  	host, _, err := net.SplitHostPort(addr)
   425  	if err != nil {
   426  		return false
   427  	}
   428  	if host == "localhost" {
   429  		return false
   430  	}
   431  	if ip := net.ParseIP(host); ip != nil {
   432  		if ip.IsLoopback() {
   433  			return false
   434  		}
   435  	}
   436  
   437  	no_proxy := getenvEitherCase("NO_PROXY")
   438  	if no_proxy == "*" {
   439  		return false
   440  	}
   441  
   442  	addr = strings.ToLower(strings.TrimSpace(addr))
   443  	if hasPort(addr) {
   444  		addr = addr[:strings.LastIndex(addr, ":")]
   445  	}
   446  
   447  	for _, p := range strings.Split(no_proxy, ",") {
   448  		p = strings.ToLower(strings.TrimSpace(p))
   449  		if len(p) == 0 {
   450  			continue
   451  		}
   452  		if hasPort(p) {
   453  			p = p[:strings.LastIndex(p, ":")]
   454  		}
   455  		if addr == p || (p[0] == '.' && (strings.HasSuffix(addr, p) || addr == p[1:])) {
   456  			return false
   457  		}
   458  	}
   459  	return true
   460  }
   461  
   462  // connectMethod is the map key (in its String form) for keeping persistent
   463  // TCP connections alive for subsequent HTTP requests.
   464  //
   465  // A connect method may be of the following types:
   466  //
   467  // Cache key form                Description
   468  // -----------------             -------------------------
   469  // ||http|foo.com                http directly to server, no proxy
   470  // ||https|foo.com               https directly to server, no proxy
   471  // http://proxy.com|https|foo.com  http to proxy, then CONNECT to foo.com
   472  // http://proxy.com|http           http to proxy, http to anywhere after that
   473  //
   474  // Note: no support to https to the proxy yet.
   475  //
   476  type connectMethod struct {
   477  	proxyURL     *url.URL // nil for no proxy, else full proxy URL
   478  	targetScheme string   // "http" or "https"
   479  	targetAddr   string   // Not used if proxy + http targetScheme (4th example in table)
   480  }
   481  
   482  func (ck *connectMethod) String() string {
   483  	proxyStr := ""
   484  	if ck.proxyURL != nil {
   485  		proxyStr = ck.proxyURL.String()
   486  	}
   487  	return strings.Join([]string{proxyStr, ck.targetScheme, ck.targetAddr}, "|")
   488  }
   489  
   490  // addr returns the first hop "host:port" to which we need to TCP connect.
   491  func (cm *connectMethod) addr() string {
   492  	if cm.proxyURL != nil {
   493  		return canonicalAddr(cm.proxyURL)
   494  	}
   495  	return cm.targetAddr
   496  }
   497  
   498  // tlsHost returns the host name to match against the peer's
   499  // TLS certificate.
   500  func (cm *connectMethod) tlsHost() string {
   501  	h := cm.targetAddr
   502  	if hasPort(h) {
   503  		h = h[:strings.LastIndex(h, ":")]
   504  	}
   505  	return h
   506  }
   507  
   508  // persistConn wraps a connection, usually a persistent one
   509  // (but may be used for non-keep-alive requests as well)
   510  type persistConn struct {
   511  	t        *Transport
   512  	cacheKey string // its connectMethod.String()
   513  	conn     net.Conn
   514  	br       *bufio.Reader       // from conn
   515  	bw       *bufio.Writer       // to conn
   516  	reqch    chan requestAndChan // written by roundTrip(); read by readLoop()
   517  	isProxy  bool
   518  
   519  	// mutateHeaderFunc is an optional func to modify extra
   520  	// headers on each outbound request before it's written. (the
   521  	// original Request given to RoundTrip is not modified)
   522  	mutateHeaderFunc func(http.Header)
   523  
   524  	lk                   sync.Mutex // guards numExpectedResponses and broken
   525  	numExpectedResponses int
   526  	broken               bool // an error has happened on this connection; marked broken so it's not reused.
   527  
   528  	host string
   529  	ip *net.TCPAddr
   530  }
   531  
   532  func (pc *persistConn) isBroken() bool {
   533  	pc.lk.Lock()
   534  	defer pc.lk.Unlock()
   535  	return pc.broken
   536  }
   537  
   538  var remoteSideClosedFunc func(error) bool // or nil to use default
   539  
   540  func remoteSideClosed(err error) bool {
   541  	if err == io.EOF {
   542  		return true
   543  	}
   544  	if remoteSideClosedFunc != nil {
   545  		return remoteSideClosedFunc(err)
   546  	}
   547  	return false
   548  }
   549  
   550  func (pc *persistConn) readLoop() {
   551  	alive := true
   552  	var lastbody io.ReadCloser // last response body, if any, read on this connection
   553  
   554  	for alive {
   555  		pb, err := pc.br.Peek(1)
   556  
   557  		pc.lk.Lock()
   558  		if pc.numExpectedResponses == 0 {
   559  			pc.closeLocked()
   560  			pc.lk.Unlock()
   561  			if len(pb) > 0 {
   562  				log.Printf("Unsolicited response received on idle HTTP channel starting with %q; err=%v",
   563  					string(pb), err)
   564  			}
   565  			return
   566  		}
   567  		pc.lk.Unlock()
   568  
   569  		rc := <-pc.reqch
   570  
   571  		// Advance past the previous response's body, if the
   572  		// caller hasn't done so.
   573  		if lastbody != nil {
   574  			lastbody.Close() // assumed idempotent
   575  			lastbody = nil
   576  		}
   577  		resp, err := http.ReadResponse(pc.br, rc.req)
   578  
   579  		if err != nil {
   580  			pc.close()
   581  		} else {
   582  			hasBody := rc.req.Method != "HEAD" && resp.ContentLength != 0
   583  			if rc.addedGzip && hasBody && resp.Header.Get("Content-Encoding") == "gzip" {
   584  				resp.Header.Del("Content-Encoding")
   585  				resp.Header.Del("Content-Length")
   586  				resp.ContentLength = -1
   587  				gzReader, zerr := gzip.NewReader(resp.Body)
   588  				if zerr != nil {
   589  					pc.close()
   590  					err = zerr
   591  				} else {
   592  					resp.Body = &readFirstCloseBoth{&discardOnCloseReadCloser{gzReader}, resp.Body}
   593  				}
   594  			}
   595  			resp.Body = &bodyEOFSignal{body: resp.Body}
   596  		}
   597  
   598  		if err != nil || resp.Close || rc.req.Close {
   599  			alive = false
   600  		}
   601  
   602  		hasBody := resp != nil && resp.ContentLength != 0
   603  		var waitForBodyRead chan bool
   604  		if alive {
   605  			if hasBody {
   606  				lastbody = resp.Body
   607  				waitForBodyRead = make(chan bool)
   608  				resp.Body.(*bodyEOFSignal).fn = func() {
   609  					if !pc.t.putIdleConn(pc) {
   610  						alive = false
   611  					}
   612  					waitForBodyRead <- true
   613  				}
   614  			} else {
   615  				// When there's no response body, we immediately
   616  				// reuse the TCP connection (putIdleConn), but
   617  				// we need to prevent ClientConn.Read from
   618  				// closing the Response.Body on the next
   619  				// loop, otherwise it might close the body
   620  				// before the client code has had a chance to
   621  				// read it (even though it'll just be 0, EOF).
   622  				lastbody = nil
   623  
   624  				if !pc.t.putIdleConn(pc) {
   625  					alive = false
   626  				}
   627  			}
   628  		}
   629  
   630  		rc.ch <- responseAndError{resp, err}
   631  
   632  		// Wait for the just-returned response body to be fully consumed
   633  		// before we race and peek on the underlying bufio reader.
   634  		if waitForBodyRead != nil {
   635  			<-waitForBodyRead
   636  		}
   637  	}
   638  }
   639  
   640  type responseAndError struct {
   641  	res *http.Response
   642  	err error
   643  }
   644  
   645  type requestAndChan struct {
   646  	req *http.Request
   647  	ch  chan responseAndError
   648  
   649  	// did the Transport (as opposed to the client code) add an
   650  	// Accept-Encoding gzip header? only if it we set it do
   651  	// we transparently decode the gzip.
   652  	addedGzip bool
   653  }
   654  
   655  func (pc *persistConn) roundTrip(req *transportRequest) (resp *http.Response, err error) {
   656  	if pc.mutateHeaderFunc != nil {
   657  		panic("mutateHeaderFunc not supported in modified Transport")
   658  		pc.mutateHeaderFunc(req.extraHeaders())
   659  	}
   660  
   661  	// Ask for a compressed version if the caller didn't set their
   662  	// own value for Accept-Encoding. We only attempted to
   663  	// uncompress the gzip stream if we were the layer that
   664  	// requested it.
   665  	requestedGzip := false
   666  	if !pc.t.DisableCompression && req.Header.Get("Accept-Encoding") == "" {
   667  		// Request gzip only, not deflate. Deflate is ambiguous and 
   668  		// not as universally supported anyway.
   669  		// See: http://www.gzip.org/zlib/zlib_faq.html#faq38
   670  		requestedGzip = true
   671  		req.extraHeaders().Set("Accept-Encoding", "gzip")
   672  	}
   673  
   674  	pc.lk.Lock()
   675  	pc.numExpectedResponses++
   676  	pc.lk.Unlock()
   677  
   678  	// orig: err = req.Request.write(pc.bw, pc.isProxy, req.extra)
   679  	if pc.isProxy {
   680  		err = req.Request.WriteProxy(pc.bw)
   681  	} else {
   682  		err = req.Request.Write(pc.bw)
   683  	}
   684  	if err != nil {
   685  		pc.close()
   686  		return
   687  	}
   688  	pc.bw.Flush()
   689  
   690  	ch := make(chan responseAndError, 1)
   691  	pc.reqch <- requestAndChan{req.Request, ch, requestedGzip}
   692  	re := <-ch
   693  	pc.lk.Lock()
   694  	pc.numExpectedResponses--
   695  	pc.lk.Unlock()
   696  
   697  	return re.res, re.err
   698  }
   699  
   700  func (pc *persistConn) close() {
   701  	pc.lk.Lock()
   702  	defer pc.lk.Unlock()
   703  	pc.closeLocked()
   704  }
   705  
   706  func (pc *persistConn) closeLocked() {
   707  	pc.broken = true
   708  	pc.conn.Close()
   709  	pc.mutateHeaderFunc = nil
   710  }
   711  
   712  var portMap = map[string]string{
   713  	"http":  "80",
   714  	"https": "443",
   715  }
   716  
   717  // canonicalAddr returns url.Host but always with a ":port" suffix
   718  func canonicalAddr(url *url.URL) string {
   719  	addr := url.Host
   720  	if !hasPort(addr) {
   721  		return addr + ":" + portMap[url.Scheme]
   722  	}
   723  	return addr
   724  }
   725  
   726  func responseIsKeepAlive(res *http.Response) bool {
   727  	// TODO: implement.  for now just always shutting down the connection.
   728  	return false
   729  }
   730  
   731  // bodyEOFSignal wraps a ReadCloser but runs fn (if non-nil) at most
   732  // once, right before the final Read() or Close() call returns, but after
   733  // EOF has been seen.
   734  type bodyEOFSignal struct {
   735  	body     io.ReadCloser
   736  	fn       func()
   737  	isClosed bool
   738  }
   739  
   740  func (es *bodyEOFSignal) Read(p []byte) (n int, err error) {
   741  	n, err = es.body.Read(p)
   742  	if es.isClosed && n > 0 {
   743  		panic("http: unexpected bodyEOFSignal Read after Close; see issue 1725")
   744  	}
   745  	if err == io.EOF && es.fn != nil {
   746  		es.fn()
   747  		es.fn = nil
   748  	}
   749  	return
   750  }
   751  
   752  func (es *bodyEOFSignal) Close() (err error) {
   753  	if es.isClosed {
   754  		return nil
   755  	}
   756  	es.isClosed = true
   757  	err = es.body.Close()
   758  	if err == nil && es.fn != nil {
   759  		es.fn()
   760  		es.fn = nil
   761  	}
   762  	return
   763  }
   764  
   765  type readFirstCloseBoth struct {
   766  	io.ReadCloser
   767  	io.Closer
   768  }
   769  
   770  func (r *readFirstCloseBoth) Close() error {
   771  	if err := r.ReadCloser.Close(); err != nil {
   772  		r.Closer.Close()
   773  		return err
   774  	}
   775  	if err := r.Closer.Close(); err != nil {
   776  		return err
   777  	}
   778  	return nil
   779  }
   780  
   781  // discardOnCloseReadCloser consumes all its input on Close.
   782  type discardOnCloseReadCloser struct {
   783  	io.ReadCloser
   784  }
   785  
   786  func (d *discardOnCloseReadCloser) Close() error {
   787  	io.Copy(ioutil.Discard, d.ReadCloser) // ignore errors; likely invalid or already closed
   788  	return d.ReadCloser.Close()
   789  }