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