github.com/peggyl/go@v0.0.0-20151008231540-ae315999c2d5/src/net/http/client.go (about)

     1  // Copyright 2009 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. See RFC 2616.
     6  //
     7  // This is the high-level Client interface.
     8  // The low-level implementation is in transport.go.
     9  
    10  package http
    11  
    12  import (
    13  	"encoding/base64"
    14  	"errors"
    15  	"fmt"
    16  	"io"
    17  	"io/ioutil"
    18  	"log"
    19  	"net/url"
    20  	"strings"
    21  	"sync"
    22  	"sync/atomic"
    23  	"time"
    24  )
    25  
    26  // A Client is an HTTP client. Its zero value (DefaultClient) is a
    27  // usable client that uses DefaultTransport.
    28  //
    29  // The Client's Transport typically has internal state (cached TCP
    30  // connections), so Clients should be reused instead of created as
    31  // needed. Clients are safe for concurrent use by multiple goroutines.
    32  //
    33  // A Client is higher-level than a RoundTripper (such as Transport)
    34  // and additionally handles HTTP details such as cookies and
    35  // redirects.
    36  type Client struct {
    37  	// Transport specifies the mechanism by which individual
    38  	// HTTP requests are made.
    39  	// If nil, DefaultTransport is used.
    40  	Transport RoundTripper
    41  
    42  	// CheckRedirect specifies the policy for handling redirects.
    43  	// If CheckRedirect is not nil, the client calls it before
    44  	// following an HTTP redirect. The arguments req and via are
    45  	// the upcoming request and the requests made already, oldest
    46  	// first. If CheckRedirect returns an error, the Client's Get
    47  	// method returns both the previous Response and
    48  	// CheckRedirect's error (wrapped in a url.Error) instead of
    49  	// issuing the Request req.
    50  	//
    51  	// If CheckRedirect is nil, the Client uses its default policy,
    52  	// which is to stop after 10 consecutive requests.
    53  	CheckRedirect func(req *Request, via []*Request) error
    54  
    55  	// Jar specifies the cookie jar.
    56  	// If Jar is nil, cookies are not sent in requests and ignored
    57  	// in responses.
    58  	Jar CookieJar
    59  
    60  	// Timeout specifies a time limit for requests made by this
    61  	// Client. The timeout includes connection time, any
    62  	// redirects, and reading the response body. The timer remains
    63  	// running after Get, Head, Post, or Do return and will
    64  	// interrupt reading of the Response.Body.
    65  	//
    66  	// A Timeout of zero means no timeout.
    67  	//
    68  	// The Client's Transport must support the CancelRequest
    69  	// method or Client will return errors when attempting to make
    70  	// a request with Get, Head, Post, or Do. Client's default
    71  	// Transport (DefaultTransport) supports CancelRequest.
    72  	Timeout time.Duration
    73  }
    74  
    75  // DefaultClient is the default Client and is used by Get, Head, and Post.
    76  var DefaultClient = &Client{}
    77  
    78  // RoundTripper is an interface representing the ability to execute a
    79  // single HTTP transaction, obtaining the Response for a given Request.
    80  //
    81  // A RoundTripper must be safe for concurrent use by multiple
    82  // goroutines.
    83  type RoundTripper interface {
    84  	// RoundTrip executes a single HTTP transaction, returning
    85  	// the Response for the request req.  RoundTrip should not
    86  	// attempt to interpret the response.  In particular,
    87  	// RoundTrip must return err == nil if it obtained a response,
    88  	// regardless of the response's HTTP status code.  A non-nil
    89  	// err should be reserved for failure to obtain a response.
    90  	// Similarly, RoundTrip should not attempt to handle
    91  	// higher-level protocol details such as redirects,
    92  	// authentication, or cookies.
    93  	//
    94  	// RoundTrip should not modify the request, except for
    95  	// consuming and closing the Body, including on errors. The
    96  	// request's URL and Header fields are guaranteed to be
    97  	// initialized.
    98  	RoundTrip(*Request) (*Response, error)
    99  }
   100  
   101  // Given a string of the form "host", "host:port", or "[ipv6::address]:port",
   102  // return true if the string includes a port.
   103  func hasPort(s string) bool { return strings.LastIndex(s, ":") > strings.LastIndex(s, "]") }
   104  
   105  // refererForURL returns a referer without any authentication info or
   106  // an empty string if lastReq scheme is https and newReq scheme is http.
   107  func refererForURL(lastReq, newReq *url.URL) string {
   108  	// https://tools.ietf.org/html/rfc7231#section-5.5.2
   109  	//   "Clients SHOULD NOT include a Referer header field in a
   110  	//    (non-secure) HTTP request if the referring page was
   111  	//    transferred with a secure protocol."
   112  	if lastReq.Scheme == "https" && newReq.Scheme == "http" {
   113  		return ""
   114  	}
   115  	referer := lastReq.String()
   116  	if lastReq.User != nil {
   117  		// This is not very efficient, but is the best we can
   118  		// do without:
   119  		// - introducing a new method on URL
   120  		// - creating a race condition
   121  		// - copying the URL struct manually, which would cause
   122  		//   maintenance problems down the line
   123  		auth := lastReq.User.String() + "@"
   124  		referer = strings.Replace(referer, auth, "", 1)
   125  	}
   126  	return referer
   127  }
   128  
   129  // Used in Send to implement io.ReadCloser by bundling together the
   130  // bufio.Reader through which we read the response, and the underlying
   131  // network connection.
   132  type readClose struct {
   133  	io.Reader
   134  	io.Closer
   135  }
   136  
   137  func (c *Client) send(req *Request) (*Response, error) {
   138  	if c.Jar != nil {
   139  		for _, cookie := range c.Jar.Cookies(req.URL) {
   140  			req.AddCookie(cookie)
   141  		}
   142  	}
   143  	resp, err := send(req, c.transport())
   144  	if err != nil {
   145  		return nil, err
   146  	}
   147  	if c.Jar != nil {
   148  		if rc := resp.Cookies(); len(rc) > 0 {
   149  			c.Jar.SetCookies(req.URL, rc)
   150  		}
   151  	}
   152  	return resp, err
   153  }
   154  
   155  // Do sends an HTTP request and returns an HTTP response, following
   156  // policy (e.g. redirects, cookies, auth) as configured on the client.
   157  //
   158  // An error is returned if caused by client policy (such as
   159  // CheckRedirect), or if there was an HTTP protocol error.
   160  // A non-2xx response doesn't cause an error.
   161  //
   162  // When err is nil, resp always contains a non-nil resp.Body.
   163  //
   164  // Callers should close resp.Body when done reading from it. If
   165  // resp.Body is not closed, the Client's underlying RoundTripper
   166  // (typically Transport) may not be able to re-use a persistent TCP
   167  // connection to the server for a subsequent "keep-alive" request.
   168  //
   169  // The request Body, if non-nil, will be closed by the underlying
   170  // Transport, even on errors.
   171  //
   172  // Generally Get, Post, or PostForm will be used instead of Do.
   173  func (c *Client) Do(req *Request) (resp *Response, err error) {
   174  	if req.Method == "GET" || req.Method == "HEAD" {
   175  		return c.doFollowingRedirects(req, shouldRedirectGet)
   176  	}
   177  	if req.Method == "POST" || req.Method == "PUT" {
   178  		return c.doFollowingRedirects(req, shouldRedirectPost)
   179  	}
   180  	return c.send(req)
   181  }
   182  
   183  func (c *Client) transport() RoundTripper {
   184  	if c.Transport != nil {
   185  		return c.Transport
   186  	}
   187  	return DefaultTransport
   188  }
   189  
   190  // send issues an HTTP request.
   191  // Caller should close resp.Body when done reading from it.
   192  func send(req *Request, t RoundTripper) (resp *Response, err error) {
   193  	if t == nil {
   194  		req.closeBody()
   195  		return nil, errors.New("http: no Client.Transport or DefaultTransport")
   196  	}
   197  
   198  	if req.URL == nil {
   199  		req.closeBody()
   200  		return nil, errors.New("http: nil Request.URL")
   201  	}
   202  
   203  	if req.RequestURI != "" {
   204  		req.closeBody()
   205  		return nil, errors.New("http: Request.RequestURI can't be set in client requests.")
   206  	}
   207  
   208  	// Most the callers of send (Get, Post, et al) don't need
   209  	// Headers, leaving it uninitialized.  We guarantee to the
   210  	// Transport that this has been initialized, though.
   211  	if req.Header == nil {
   212  		req.Header = make(Header)
   213  	}
   214  
   215  	if u := req.URL.User; u != nil && req.Header.Get("Authorization") == "" {
   216  		username := u.Username()
   217  		password, _ := u.Password()
   218  		req.Header.Set("Authorization", "Basic "+basicAuth(username, password))
   219  	}
   220  	resp, err = t.RoundTrip(req)
   221  	if err != nil {
   222  		if resp != nil {
   223  			log.Printf("RoundTripper returned a response & error; ignoring response")
   224  		}
   225  		return nil, err
   226  	}
   227  	return resp, nil
   228  }
   229  
   230  // See 2 (end of page 4) http://www.ietf.org/rfc/rfc2617.txt
   231  // "To receive authorization, the client sends the userid and password,
   232  // separated by a single colon (":") character, within a base64
   233  // encoded string in the credentials."
   234  // It is not meant to be urlencoded.
   235  func basicAuth(username, password string) string {
   236  	auth := username + ":" + password
   237  	return base64.StdEncoding.EncodeToString([]byte(auth))
   238  }
   239  
   240  // True if the specified HTTP status code is one for which the Get utility should
   241  // automatically redirect.
   242  func shouldRedirectGet(statusCode int) bool {
   243  	switch statusCode {
   244  	case StatusMovedPermanently, StatusFound, StatusSeeOther, StatusTemporaryRedirect:
   245  		return true
   246  	}
   247  	return false
   248  }
   249  
   250  // True if the specified HTTP status code is one for which the Post utility should
   251  // automatically redirect.
   252  func shouldRedirectPost(statusCode int) bool {
   253  	switch statusCode {
   254  	case StatusFound, StatusSeeOther:
   255  		return true
   256  	}
   257  	return false
   258  }
   259  
   260  // Get issues a GET to the specified URL. If the response is one of
   261  // the following redirect codes, Get follows the redirect, up to a
   262  // maximum of 10 redirects:
   263  //
   264  //    301 (Moved Permanently)
   265  //    302 (Found)
   266  //    303 (See Other)
   267  //    307 (Temporary Redirect)
   268  //
   269  // An error is returned if there were too many redirects or if there
   270  // was an HTTP protocol error. A non-2xx response doesn't cause an
   271  // error.
   272  //
   273  // When err is nil, resp always contains a non-nil resp.Body.
   274  // Caller should close resp.Body when done reading from it.
   275  //
   276  // Get is a wrapper around DefaultClient.Get.
   277  //
   278  // To make a request with custom headers, use NewRequest and
   279  // DefaultClient.Do.
   280  func Get(url string) (resp *Response, err error) {
   281  	return DefaultClient.Get(url)
   282  }
   283  
   284  // Get issues a GET to the specified URL. If the response is one of the
   285  // following redirect codes, Get follows the redirect after calling the
   286  // Client's CheckRedirect function:
   287  //
   288  //    301 (Moved Permanently)
   289  //    302 (Found)
   290  //    303 (See Other)
   291  //    307 (Temporary Redirect)
   292  //
   293  // An error is returned if the Client's CheckRedirect function fails
   294  // or if there was an HTTP protocol error. A non-2xx response doesn't
   295  // cause an error.
   296  //
   297  // When err is nil, resp always contains a non-nil resp.Body.
   298  // Caller should close resp.Body when done reading from it.
   299  //
   300  // To make a request with custom headers, use NewRequest and Client.Do.
   301  func (c *Client) Get(url string) (resp *Response, err error) {
   302  	req, err := NewRequest("GET", url, nil)
   303  	if err != nil {
   304  		return nil, err
   305  	}
   306  	return c.doFollowingRedirects(req, shouldRedirectGet)
   307  }
   308  
   309  func alwaysFalse() bool { return false }
   310  
   311  func (c *Client) doFollowingRedirects(ireq *Request, shouldRedirect func(int) bool) (resp *Response, err error) {
   312  	var base *url.URL
   313  	redirectChecker := c.CheckRedirect
   314  	if redirectChecker == nil {
   315  		redirectChecker = defaultCheckRedirect
   316  	}
   317  	var via []*Request
   318  
   319  	if ireq.URL == nil {
   320  		ireq.closeBody()
   321  		return nil, errors.New("http: nil Request.URL")
   322  	}
   323  
   324  	var reqmu sync.Mutex // guards req
   325  	req := ireq
   326  
   327  	var timer *time.Timer
   328  	var atomicWasCanceled int32 // atomic bool (1 or 0)
   329  	var wasCanceled = alwaysFalse
   330  	if c.Timeout > 0 {
   331  		wasCanceled = func() bool { return atomic.LoadInt32(&atomicWasCanceled) != 0 }
   332  		type canceler interface {
   333  			CancelRequest(*Request)
   334  		}
   335  		tr, ok := c.transport().(canceler)
   336  		if !ok {
   337  			return nil, fmt.Errorf("net/http: Client Transport of type %T doesn't support CancelRequest; Timeout not supported", c.transport())
   338  		}
   339  		timer = time.AfterFunc(c.Timeout, func() {
   340  			atomic.StoreInt32(&atomicWasCanceled, 1)
   341  			reqmu.Lock()
   342  			defer reqmu.Unlock()
   343  			tr.CancelRequest(req)
   344  		})
   345  	}
   346  
   347  	urlStr := "" // next relative or absolute URL to fetch (after first request)
   348  	redirectFailed := false
   349  	for redirect := 0; ; redirect++ {
   350  		if redirect != 0 {
   351  			nreq := new(Request)
   352  			nreq.Method = ireq.Method
   353  			if ireq.Method == "POST" || ireq.Method == "PUT" {
   354  				nreq.Method = "GET"
   355  			}
   356  			nreq.Header = make(Header)
   357  			nreq.URL, err = base.Parse(urlStr)
   358  			if err != nil {
   359  				break
   360  			}
   361  			if len(via) > 0 {
   362  				// Add the Referer header.
   363  				lastReq := via[len(via)-1]
   364  				if ref := refererForURL(lastReq.URL, nreq.URL); ref != "" {
   365  					nreq.Header.Set("Referer", ref)
   366  				}
   367  
   368  				err = redirectChecker(nreq, via)
   369  				if err != nil {
   370  					redirectFailed = true
   371  					break
   372  				}
   373  			}
   374  			reqmu.Lock()
   375  			req = nreq
   376  			reqmu.Unlock()
   377  		}
   378  
   379  		urlStr = req.URL.String()
   380  		if resp, err = c.send(req); err != nil {
   381  			if wasCanceled() {
   382  				err = &httpError{
   383  					err:     err.Error() + " (Client.Timeout exceeded while awaiting headers)",
   384  					timeout: true,
   385  				}
   386  			}
   387  			break
   388  		}
   389  
   390  		if shouldRedirect(resp.StatusCode) {
   391  			// Read the body if small so underlying TCP connection will be re-used.
   392  			// No need to check for errors: if it fails, Transport won't reuse it anyway.
   393  			const maxBodySlurpSize = 2 << 10
   394  			if resp.ContentLength == -1 || resp.ContentLength <= maxBodySlurpSize {
   395  				io.CopyN(ioutil.Discard, resp.Body, maxBodySlurpSize)
   396  			}
   397  			resp.Body.Close()
   398  			if urlStr = resp.Header.Get("Location"); urlStr == "" {
   399  				err = fmt.Errorf("%d response missing Location header", resp.StatusCode)
   400  				break
   401  			}
   402  			base = req.URL
   403  			via = append(via, req)
   404  			continue
   405  		}
   406  		if timer != nil {
   407  			resp.Body = &cancelTimerBody{
   408  				t:              timer,
   409  				rc:             resp.Body,
   410  				reqWasCanceled: wasCanceled,
   411  			}
   412  		}
   413  		return resp, nil
   414  	}
   415  
   416  	method := ireq.Method
   417  	urlErr := &url.Error{
   418  		Op:  method[0:1] + strings.ToLower(method[1:]),
   419  		URL: urlStr,
   420  		Err: err,
   421  	}
   422  
   423  	if redirectFailed {
   424  		// Special case for Go 1 compatibility: return both the response
   425  		// and an error if the CheckRedirect function failed.
   426  		// See https://golang.org/issue/3795
   427  		return resp, urlErr
   428  	}
   429  
   430  	if resp != nil {
   431  		resp.Body.Close()
   432  	}
   433  	return nil, urlErr
   434  }
   435  
   436  func defaultCheckRedirect(req *Request, via []*Request) error {
   437  	if len(via) >= 10 {
   438  		return errors.New("stopped after 10 redirects")
   439  	}
   440  	return nil
   441  }
   442  
   443  // Post issues a POST to the specified URL.
   444  //
   445  // Caller should close resp.Body when done reading from it.
   446  //
   447  // If the provided body is an io.Closer, it is closed after the
   448  // request.
   449  //
   450  // Post is a wrapper around DefaultClient.Post.
   451  //
   452  // To set custom headers, use NewRequest and DefaultClient.Do.
   453  func Post(url string, bodyType string, body io.Reader) (resp *Response, err error) {
   454  	return DefaultClient.Post(url, bodyType, body)
   455  }
   456  
   457  // Post issues a POST to the specified URL.
   458  //
   459  // Caller should close resp.Body when done reading from it.
   460  //
   461  // If the provided body is an io.Closer, it is closed after the
   462  // request.
   463  //
   464  // To set custom headers, use NewRequest and Client.Do.
   465  func (c *Client) Post(url string, bodyType string, body io.Reader) (resp *Response, err error) {
   466  	req, err := NewRequest("POST", url, body)
   467  	if err != nil {
   468  		return nil, err
   469  	}
   470  	req.Header.Set("Content-Type", bodyType)
   471  	return c.doFollowingRedirects(req, shouldRedirectPost)
   472  }
   473  
   474  // PostForm issues a POST to the specified URL, with data's keys and
   475  // values URL-encoded as the request body.
   476  //
   477  // The Content-Type header is set to application/x-www-form-urlencoded.
   478  // To set other headers, use NewRequest and DefaultClient.Do.
   479  //
   480  // When err is nil, resp always contains a non-nil resp.Body.
   481  // Caller should close resp.Body when done reading from it.
   482  //
   483  // PostForm is a wrapper around DefaultClient.PostForm.
   484  func PostForm(url string, data url.Values) (resp *Response, err error) {
   485  	return DefaultClient.PostForm(url, data)
   486  }
   487  
   488  // PostForm issues a POST to the specified URL,
   489  // with data's keys and values URL-encoded as the request body.
   490  //
   491  // The Content-Type header is set to application/x-www-form-urlencoded.
   492  // To set other headers, use NewRequest and DefaultClient.Do.
   493  //
   494  // When err is nil, resp always contains a non-nil resp.Body.
   495  // Caller should close resp.Body when done reading from it.
   496  func (c *Client) PostForm(url string, data url.Values) (resp *Response, err error) {
   497  	return c.Post(url, "application/x-www-form-urlencoded", strings.NewReader(data.Encode()))
   498  }
   499  
   500  // Head issues a HEAD to the specified URL.  If the response is one of
   501  // the following redirect codes, Head follows the redirect, up to a
   502  // maximum of 10 redirects:
   503  //
   504  //    301 (Moved Permanently)
   505  //    302 (Found)
   506  //    303 (See Other)
   507  //    307 (Temporary Redirect)
   508  //
   509  // Head is a wrapper around DefaultClient.Head
   510  func Head(url string) (resp *Response, err error) {
   511  	return DefaultClient.Head(url)
   512  }
   513  
   514  // Head issues a HEAD to the specified URL.  If the response is one of the
   515  // following redirect codes, Head follows the redirect after calling the
   516  // Client's CheckRedirect function:
   517  //
   518  //    301 (Moved Permanently)
   519  //    302 (Found)
   520  //    303 (See Other)
   521  //    307 (Temporary Redirect)
   522  func (c *Client) Head(url string) (resp *Response, err error) {
   523  	req, err := NewRequest("HEAD", url, nil)
   524  	if err != nil {
   525  		return nil, err
   526  	}
   527  	return c.doFollowingRedirects(req, shouldRedirectGet)
   528  }
   529  
   530  // cancelTimerBody is an io.ReadCloser that wraps rc with two features:
   531  // 1) on Read EOF or Close, the timer t is Stopped,
   532  // 2) On Read failure, if reqWasCanceled is true, the error is wrapped and
   533  //    marked as net.Error that hit its timeout.
   534  type cancelTimerBody struct {
   535  	t              *time.Timer
   536  	rc             io.ReadCloser
   537  	reqWasCanceled func() bool
   538  }
   539  
   540  func (b *cancelTimerBody) Read(p []byte) (n int, err error) {
   541  	n, err = b.rc.Read(p)
   542  	if err == io.EOF {
   543  		b.t.Stop()
   544  	} else if err != nil && b.reqWasCanceled() {
   545  		return n, &httpError{
   546  			err:     err.Error() + " (Client.Timeout exceeded while reading body)",
   547  			timeout: true,
   548  		}
   549  	}
   550  	return
   551  }
   552  
   553  func (b *cancelTimerBody) Close() error {
   554  	err := b.rc.Close()
   555  	b.t.Stop()
   556  	return err
   557  }