src/pkg/net/http/client.go - The Go Programming Language

Golang

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		"net/url"
    18		"strings"
    19	)
    20	
    21	// A Client is an HTTP client. Its zero value (DefaultClient) is a usable client
    22	// that uses DefaultTransport.
    23	//
    24	// The Client's Transport typically has internal state (cached
    25	// TCP connections), so Clients should be reused instead of created as
    26	// needed. Clients are safe for concurrent use by multiple goroutines.
    27	type Client struct {
    28		// Transport specifies the mechanism by which individual
    29		// HTTP requests are made.
    30		// If nil, DefaultTransport is used.
    31		Transport RoundTripper
    32	
    33		// CheckRedirect specifies the policy for handling redirects.
    34		// If CheckRedirect is not nil, the client calls it before
    35		// following an HTTP redirect. The arguments req and via
    36		// are the upcoming request and the requests made already,
    37		// oldest first. If CheckRedirect returns an error, the client
    38		// returns that error instead of issue the Request req.
    39		//
    40		// If CheckRedirect is nil, the Client uses its default policy,
    41		// which is to stop after 10 consecutive requests.
    42		CheckRedirect func(req *Request, via []*Request) error
    43	
    44		// Jar specifies the cookie jar. 
    45		// If Jar is nil, cookies are not sent in requests and ignored 
    46		// in responses.
    47		Jar CookieJar
    48	}
    49	
    50	// DefaultClient is the default Client and is used by Get, Head, and Post.
    51	var DefaultClient = &Client{}
    52	
    53	// RoundTripper is an interface representing the ability to execute a
    54	// single HTTP transaction, obtaining the Response for a given Request.
    55	//
    56	// A RoundTripper must be safe for concurrent use by multiple
    57	// goroutines.
    58	type RoundTripper interface {
    59		// RoundTrip executes a single HTTP transaction, returning
    60		// the Response for the request req.  RoundTrip should not
    61		// attempt to interpret the response.  In particular,
    62		// RoundTrip must return err == nil if it obtained a response,
    63		// regardless of the response's HTTP status code.  A non-nil
    64		// err should be reserved for failure to obtain a response.
    65		// Similarly, RoundTrip should not attempt to handle
    66		// higher-level protocol details such as redirects,
    67		// authentication, or cookies.
    68		//
    69		// RoundTrip should not modify the request, except for
    70		// consuming the Body.  The request's URL and Header fields
    71		// are guaranteed to be initialized.
    72		RoundTrip(*Request) (*Response, error)
    73	}
    74	
    75	// Given a string of the form "host", "host:port", or "[ipv6::address]:port",
    76	// return true if the string includes a port.
    77	func hasPort(s string) bool { return strings.LastIndex(s, ":") > strings.LastIndex(s, "]") }
    78	
    79	// Used in Send to implement io.ReadCloser by bundling together the
    80	// bufio.Reader through which we read the response, and the underlying
    81	// network connection.
    82	type readClose struct {
    83		io.Reader
    84		io.Closer
    85	}
    86	
    87	// Do sends an HTTP request and returns an HTTP response, following
    88	// policy (e.g. redirects, cookies, auth) as configured on the client.
    89	//
    90	// A non-nil response always contains a non-nil resp.Body.
    91	//
    92	// Callers should close resp.Body when done reading from it. If
    93	// resp.Body is not closed, the Client's underlying RoundTripper
    94	// (typically Transport) may not be able to re-use a persistent TCP
    95	// connection to the server for a subsequent "keep-alive" request.
    96	//
    97	// Generally Get, Post, or PostForm will be used instead of Do.
    98	func (c *Client) Do(req *Request) (resp *Response, err error) {
    99		if req.Method == "GET" || req.Method == "HEAD" {
   100			return c.doFollowingRedirects(req)
   101		}
   102		return send(req, c.Transport)
   103	}
   104	
   105	// send issues an HTTP request.  Caller should close resp.Body when done reading from it.
   106	func send(req *Request, t RoundTripper) (resp *Response, err error) {
   107		if t == nil {
   108			t = DefaultTransport
   109			if t == nil {
   110				err = errors.New("http: no Client.Transport or DefaultTransport")
   111				return
   112			}
   113		}
   114	
   115		if req.URL == nil {
   116			return nil, errors.New("http: nil Request.URL")
   117		}
   118	
   119		if req.RequestURI != "" {
   120			return nil, errors.New("http: Request.RequestURI can't be set in client requests.")
   121		}
   122	
   123		// Most the callers of send (Get, Post, et al) don't need
   124		// Headers, leaving it uninitialized.  We guarantee to the
   125		// Transport that this has been initialized, though.
   126		if req.Header == nil {
   127			req.Header = make(Header)
   128		}
   129	
   130		if u := req.URL.User; u != nil {
   131			req.Header.Set("Authorization", "Basic "+base64.URLEncoding.EncodeToString([]byte(u.String())))
   132		}
   133		return t.RoundTrip(req)
   134	}
   135	
   136	// True if the specified HTTP status code is one for which the Get utility should
   137	// automatically redirect.
   138	func shouldRedirect(statusCode int) bool {
   139		switch statusCode {
   140		case StatusMovedPermanently, StatusFound, StatusSeeOther, StatusTemporaryRedirect:
   141			return true
   142		}
   143		return false
   144	}
   145	
   146	// Get issues a GET to the specified URL.  If the response is one of the following
   147	// redirect codes, Get follows the redirect, up to a maximum of 10 redirects:
   148	//
   149	//    301 (Moved Permanently)
   150	//    302 (Found)
   151	//    303 (See Other)
   152	//    307 (Temporary Redirect)
   153	//
   154	// Caller should close r.Body when done reading from it.
   155	//
   156	// Get is a wrapper around DefaultClient.Get.
   157	func Get(url string) (r *Response, err error) {
   158		return DefaultClient.Get(url)
   159	}
   160	
   161	// Get issues a GET to the specified URL.  If the response is one of the
   162	// following redirect codes, Get follows the redirect after calling the
   163	// Client's CheckRedirect function.
   164	//
   165	//    301 (Moved Permanently)
   166	//    302 (Found)
   167	//    303 (See Other)
   168	//    307 (Temporary Redirect)
   169	//
   170	// Caller should close r.Body when done reading from it.
   171	func (c *Client) Get(url string) (r *Response, err error) {
   172		req, err := NewRequest("GET", url, nil)
   173		if err != nil {
   174			return nil, err
   175		}
   176		return c.doFollowingRedirects(req)
   177	}
   178	
   179	func (c *Client) doFollowingRedirects(ireq *Request) (r *Response, err error) {
   180		// TODO: if/when we add cookie support, the redirected request shouldn't
   181		// necessarily supply the same cookies as the original.
   182		var base *url.URL
   183		redirectChecker := c.CheckRedirect
   184		if redirectChecker == nil {
   185			redirectChecker = defaultCheckRedirect
   186		}
   187		var via []*Request
   188	
   189		if ireq.URL == nil {
   190			return nil, errors.New("http: nil Request.URL")
   191		}
   192	
   193		jar := c.Jar
   194		if jar == nil {
   195			jar = blackHoleJar{}
   196		}
   197	
   198		req := ireq
   199		urlStr := "" // next relative or absolute URL to fetch (after first request)
   200		for redirect := 0; ; redirect++ {
   201			if redirect != 0 {
   202				req = new(Request)
   203				req.Method = ireq.Method
   204				req.Header = make(Header)
   205				req.URL, err = base.Parse(urlStr)
   206				if err != nil {
   207					break
   208				}
   209				if len(via) > 0 {
   210					// Add the Referer header.
   211					lastReq := via[len(via)-1]
   212					if lastReq.URL.Scheme != "https" {
   213						req.Header.Set("Referer", lastReq.URL.String())
   214					}
   215	
   216					err = redirectChecker(req, via)
   217					if err != nil {
   218						break
   219					}
   220				}
   221			}
   222	
   223			for _, cookie := range jar.Cookies(req.URL) {
   224				req.AddCookie(cookie)
   225			}
   226			urlStr = req.URL.String()
   227			if r, err = send(req, c.Transport); err != nil {
   228				break
   229			}
   230			if c := r.Cookies(); len(c) > 0 {
   231				jar.SetCookies(req.URL, c)
   232			}
   233	
   234			if shouldRedirect(r.StatusCode) {
   235				r.Body.Close()
   236				if urlStr = r.Header.Get("Location"); urlStr == "" {
   237					err = errors.New(fmt.Sprintf("%d response missing Location header", r.StatusCode))
   238					break
   239				}
   240				base = req.URL
   241				via = append(via, req)
   242				continue
   243			}
   244			return
   245		}
   246	
   247		method := ireq.Method
   248		err = &url.Error{
   249			Op:  method[0:1] + strings.ToLower(method[1:]),
   250			URL: urlStr,
   251			Err: err,
   252		}
   253		return
   254	}
   255	
   256	func defaultCheckRedirect(req *Request, via []*Request) error {
   257		if len(via) >= 10 {
   258			return errors.New("stopped after 10 redirects")
   259		}
   260		return nil
   261	}
   262	
   263	// Post issues a POST to the specified URL.
   264	//
   265	// Caller should close r.Body when done reading from it.
   266	//
   267	// Post is a wrapper around DefaultClient.Post
   268	func Post(url string, bodyType string, body io.Reader) (r *Response, err error) {
   269		return DefaultClient.Post(url, bodyType, body)
   270	}
   271	
   272	// Post issues a POST to the specified URL.
   273	//
   274	// Caller should close r.Body when done reading from it.
   275	func (c *Client) Post(url string, bodyType string, body io.Reader) (r *Response, err error) {
   276		req, err := NewRequest("POST", url, body)
   277		if err != nil {
   278			return nil, err
   279		}
   280		req.Header.Set("Content-Type", bodyType)
   281		r, err = send(req, c.Transport)
   282		if err == nil && c.Jar != nil {
   283			c.Jar.SetCookies(req.URL, r.Cookies())
   284		}
   285		return r, err
   286	}
   287	
   288	// PostForm issues a POST to the specified URL, 
   289	// with data's keys and values urlencoded as the request body.
   290	//
   291	// Caller should close r.Body when done reading from it.
   292	//
   293	// PostForm is a wrapper around DefaultClient.PostForm
   294	func PostForm(url string, data url.Values) (r *Response, err error) {
   295		return DefaultClient.PostForm(url, data)
   296	}
   297	
   298	// PostForm issues a POST to the specified URL, 
   299	// with data's keys and values urlencoded as the request body.
   300	//
   301	// Caller should close r.Body when done reading from it.
   302	func (c *Client) PostForm(url string, data url.Values) (r *Response, err error) {
   303		return c.Post(url, "application/x-www-form-urlencoded", strings.NewReader(data.Encode()))
   304	}
   305	
   306	// Head issues a HEAD to the specified URL.  If the response is one of the
   307	// following redirect codes, Head follows the redirect after calling the
   308	// Client's CheckRedirect function.
   309	//
   310	//    301 (Moved Permanently)
   311	//    302 (Found)
   312	//    303 (See Other)
   313	//    307 (Temporary Redirect)
   314	//
   315	// Head is a wrapper around DefaultClient.Head
   316	func Head(url string) (r *Response, err error) {
   317		return DefaultClient.Head(url)
   318	}
   319	
   320	// Head issues a HEAD to the specified URL.  If the response is one of the
   321	// following redirect codes, Head follows the redirect after calling the
   322	// Client's CheckRedirect function.
   323	//
   324	//    301 (Moved Permanently)
   325	//    302 (Found)
   326	//    303 (See Other)
   327	//    307 (Temporary Redirect)
   328	func (c *Client) Head(url string) (r *Response, err error) {
   329		req, err := NewRequest("HEAD", url, nil)
   330		if err != nil {
   331			return nil, err
   332		}
   333		return c.doFollowingRedirects(req)
   334	}