summaryrefslogtreecommitdiff
path: root/libgo/go/net/http/client.go
diff options
context:
space:
mode:
Diffstat (limited to 'libgo/go/net/http/client.go')
-rw-r--r--libgo/go/net/http/client.go82
1 files changed, 64 insertions, 18 deletions
diff --git a/libgo/go/net/http/client.go b/libgo/go/net/http/client.go
index ce884d1f07b..7f2fbb4678e 100644
--- a/libgo/go/net/http/client.go
+++ b/libgo/go/net/http/client.go
@@ -19,6 +19,7 @@ import (
"net/url"
"strings"
"sync"
+ "sync/atomic"
"time"
)
@@ -211,7 +212,7 @@ func send(req *Request, t RoundTripper) (resp *Response, err error) {
req.Header = make(Header)
}
- if u := req.URL.User; u != nil {
+ if u := req.URL.User; u != nil && req.Header.Get("Authorization") == "" {
username := u.Username()
password, _ := u.Password()
req.Header.Set("Authorization", "Basic "+basicAuth(username, password))
@@ -256,8 +257,9 @@ func shouldRedirectPost(statusCode int) bool {
return false
}
-// Get issues a GET to the specified URL. If the response is one of the following
-// redirect codes, Get follows the redirect, up to a maximum of 10 redirects:
+// Get issues a GET to the specified URL. If the response is one of
+// the following redirect codes, Get follows the redirect, up to a
+// maximum of 10 redirects:
//
// 301 (Moved Permanently)
// 302 (Found)
@@ -272,13 +274,16 @@ func shouldRedirectPost(statusCode int) bool {
// Caller should close resp.Body when done reading from it.
//
// Get is a wrapper around DefaultClient.Get.
+//
+// To make a request with custom headers, use NewRequest and
+// DefaultClient.Do.
func Get(url string) (resp *Response, err error) {
return DefaultClient.Get(url)
}
-// Get issues a GET to the specified URL. If the response is one of the
+// Get issues a GET to the specified URL. If the response is one of the
// following redirect codes, Get follows the redirect after calling the
-// Client's CheckRedirect function.
+// Client's CheckRedirect function:
//
// 301 (Moved Permanently)
// 302 (Found)
@@ -291,6 +296,8 @@ func Get(url string) (resp *Response, err error) {
//
// When err is nil, resp always contains a non-nil resp.Body.
// Caller should close resp.Body when done reading from it.
+//
+// To make a request with custom headers, use NewRequest and Client.Do.
func (c *Client) Get(url string) (resp *Response, err error) {
req, err := NewRequest("GET", url, nil)
if err != nil {
@@ -299,6 +306,8 @@ func (c *Client) Get(url string) (resp *Response, err error) {
return c.doFollowingRedirects(req, shouldRedirectGet)
}
+func alwaysFalse() bool { return false }
+
func (c *Client) doFollowingRedirects(ireq *Request, shouldRedirect func(int) bool) (resp *Response, err error) {
var base *url.URL
redirectChecker := c.CheckRedirect
@@ -316,7 +325,10 @@ func (c *Client) doFollowingRedirects(ireq *Request, shouldRedirect func(int) bo
req := ireq
var timer *time.Timer
+ var atomicWasCanceled int32 // atomic bool (1 or 0)
+ var wasCanceled = alwaysFalse
if c.Timeout > 0 {
+ wasCanceled = func() bool { return atomic.LoadInt32(&atomicWasCanceled) != 0 }
type canceler interface {
CancelRequest(*Request)
}
@@ -325,6 +337,7 @@ func (c *Client) doFollowingRedirects(ireq *Request, shouldRedirect func(int) bo
return nil, fmt.Errorf("net/http: Client Transport of type %T doesn't support CancelRequest; Timeout not supported", c.transport())
}
timer = time.AfterFunc(c.Timeout, func() {
+ atomic.StoreInt32(&atomicWasCanceled, 1)
reqmu.Lock()
defer reqmu.Unlock()
tr.CancelRequest(req)
@@ -365,6 +378,12 @@ func (c *Client) doFollowingRedirects(ireq *Request, shouldRedirect func(int) bo
urlStr = req.URL.String()
if resp, err = c.send(req); err != nil {
+ if wasCanceled() {
+ err = &httpError{
+ err: err.Error() + " (Client.Timeout exceeded while awaiting headers)",
+ timeout: true,
+ }
+ }
break
}
@@ -377,7 +396,7 @@ func (c *Client) doFollowingRedirects(ireq *Request, shouldRedirect func(int) bo
}
resp.Body.Close()
if urlStr = resp.Header.Get("Location"); urlStr == "" {
- err = errors.New(fmt.Sprintf("%d response missing Location header", resp.StatusCode))
+ err = fmt.Errorf("%d response missing Location header", resp.StatusCode)
break
}
base = req.URL
@@ -385,7 +404,11 @@ func (c *Client) doFollowingRedirects(ireq *Request, shouldRedirect func(int) bo
continue
}
if timer != nil {
- resp.Body = &cancelTimerBody{timer, resp.Body}
+ resp.Body = &cancelTimerBody{
+ t: timer,
+ rc: resp.Body,
+ reqWasCanceled: wasCanceled,
+ }
}
return resp, nil
}
@@ -400,7 +423,7 @@ func (c *Client) doFollowingRedirects(ireq *Request, shouldRedirect func(int) bo
if redirectFailed {
// Special case for Go 1 compatibility: return both the response
// and an error if the CheckRedirect function failed.
- // See http://golang.org/issue/3795
+ // See https://golang.org/issue/3795
return resp, urlErr
}
@@ -421,7 +444,12 @@ func defaultCheckRedirect(req *Request, via []*Request) error {
//
// Caller should close resp.Body when done reading from it.
//
-// Post is a wrapper around DefaultClient.Post
+// If the provided body is an io.Closer, it is closed after the
+// request.
+//
+// Post is a wrapper around DefaultClient.Post.
+//
+// To set custom headers, use NewRequest and DefaultClient.Do.
func Post(url string, bodyType string, body io.Reader) (resp *Response, err error) {
return DefaultClient.Post(url, bodyType, body)
}
@@ -430,8 +458,10 @@ func Post(url string, bodyType string, body io.Reader) (resp *Response, err erro
//
// Caller should close resp.Body when done reading from it.
//
-// If the provided body is also an io.Closer, it is closed after the
+// If the provided body is an io.Closer, it is closed after the
// request.
+//
+// To set custom headers, use NewRequest and Client.Do.
func (c *Client) Post(url string, bodyType string, body io.Reader) (resp *Response, err error) {
req, err := NewRequest("POST", url, body)
if err != nil {
@@ -444,16 +474,22 @@ func (c *Client) Post(url string, bodyType string, body io.Reader) (resp *Respon
// PostForm issues a POST to the specified URL, with data's keys and
// values URL-encoded as the request body.
//
+// The Content-Type header is set to application/x-www-form-urlencoded.
+// To set other headers, use NewRequest and DefaultClient.Do.
+//
// When err is nil, resp always contains a non-nil resp.Body.
// Caller should close resp.Body when done reading from it.
//
-// PostForm is a wrapper around DefaultClient.PostForm
+// PostForm is a wrapper around DefaultClient.PostForm.
func PostForm(url string, data url.Values) (resp *Response, err error) {
return DefaultClient.PostForm(url, data)
}
// PostForm issues a POST to the specified URL,
-// with data's keys and values urlencoded as the request body.
+// with data's keys and values URL-encoded as the request body.
+//
+// The Content-Type header is set to application/x-www-form-urlencoded.
+// To set other headers, use NewRequest and DefaultClient.Do.
//
// When err is nil, resp always contains a non-nil resp.Body.
// Caller should close resp.Body when done reading from it.
@@ -461,9 +497,9 @@ func (c *Client) PostForm(url string, data url.Values) (resp *Response, err erro
return c.Post(url, "application/x-www-form-urlencoded", strings.NewReader(data.Encode()))
}
-// Head issues a HEAD to the specified URL. If the response is one of the
-// following redirect codes, Head follows the redirect after calling the
-// Client's CheckRedirect function.
+// Head issues a HEAD to the specified URL. If the response is one of
+// the following redirect codes, Head follows the redirect, up to a
+// maximum of 10 redirects:
//
// 301 (Moved Permanently)
// 302 (Found)
@@ -477,7 +513,7 @@ func Head(url string) (resp *Response, err error) {
// Head issues a HEAD to the specified URL. If the response is one of the
// following redirect codes, Head follows the redirect after calling the
-// Client's CheckRedirect function.
+// Client's CheckRedirect function:
//
// 301 (Moved Permanently)
// 302 (Found)
@@ -491,15 +527,25 @@ func (c *Client) Head(url string) (resp *Response, err error) {
return c.doFollowingRedirects(req, shouldRedirectGet)
}
+// cancelTimerBody is an io.ReadCloser that wraps rc with two features:
+// 1) on Read EOF or Close, the timer t is Stopped,
+// 2) On Read failure, if reqWasCanceled is true, the error is wrapped and
+// marked as net.Error that hit its timeout.
type cancelTimerBody struct {
- t *time.Timer
- rc io.ReadCloser
+ t *time.Timer
+ rc io.ReadCloser
+ reqWasCanceled func() bool
}
func (b *cancelTimerBody) Read(p []byte) (n int, err error) {
n, err = b.rc.Read(p)
if err == io.EOF {
b.t.Stop()
+ } else if err != nil && b.reqWasCanceled() {
+ return n, &httpError{
+ err: err.Error() + " (Client.Timeout exceeded while reading body)",
+ timeout: true,
+ }
}
return
}