...
Run Format

Source file src/pkg/net/http/client.go

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

View as plain text