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