Home | History | Annotate | Download | only in http
      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 }
    558