Commit 5e711b47 authored by Rick Arnold's avatar Rick Arnold Committed by Brad Fitzpatrick

net/http: make responseAndError satisfy the net.Error interface

Allow clients to check for timeouts without relying on error substring
matching.

Fixes #6185.

LGTM=bradfitz
R=golang-codereviews, bradfitz
CC=golang-codereviews
https://golang.org/cl/55470048
parent c5076011
...@@ -869,6 +869,18 @@ type writeRequest struct { ...@@ -869,6 +869,18 @@ type writeRequest struct {
ch chan<- error ch chan<- error
} }
type httpError struct {
err string
timeout bool
}
func (e *httpError) Error() string { return e.err }
func (e *httpError) Timeout() bool { return e.timeout }
func (e *httpError) Temporary() bool { return true }
var errTimeout error = &httpError{err: "net/http: timeout awaiting response headers", timeout: true}
var errClosed error = &httpError{err: "net/http: transport closed before response was received"}
func (pc *persistConn) roundTrip(req *transportRequest) (resp *Response, err error) { func (pc *persistConn) roundTrip(req *transportRequest) (resp *Response, err error) {
pc.t.setReqConn(req.Request, pc) pc.t.setReqConn(req.Request, pc)
pc.lk.Lock() pc.lk.Lock()
...@@ -939,11 +951,11 @@ WaitResponse: ...@@ -939,11 +951,11 @@ WaitResponse:
pconnDeadCh = nil // avoid spinning pconnDeadCh = nil // avoid spinning
failTicker = time.After(100 * time.Millisecond) // arbitrary time to wait for resc failTicker = time.After(100 * time.Millisecond) // arbitrary time to wait for resc
case <-failTicker: case <-failTicker:
re = responseAndError{err: errors.New("net/http: transport closed before response was received")} re = responseAndError{err: errClosed}
break WaitResponse break WaitResponse
case <-respHeaderTimer: case <-respHeaderTimer:
pc.close() pc.close()
re = responseAndError{err: errors.New("net/http: timeout awaiting response headers")} re = responseAndError{err: errTimeout}
break WaitResponse break WaitResponse
case re = <-resc: case re = <-resc:
break WaitResponse break WaitResponse
......
...@@ -1237,6 +1237,20 @@ func TestTransportResponseHeaderTimeout(t *testing.T) { ...@@ -1237,6 +1237,20 @@ func TestTransportResponseHeaderTimeout(t *testing.T) {
for i, tt := range tests { for i, tt := range tests {
res, err := c.Get(ts.URL + tt.path) res, err := c.Get(ts.URL + tt.path)
if err != nil { if err != nil {
uerr, ok := err.(*url.Error)
if !ok {
t.Errorf("error is not an url.Error; got: %#v", err)
continue
}
nerr, ok := uerr.Err.(net.Error)
if !ok {
t.Errorf("error does not satisfy net.Error interface; got: %#v", err)
continue
}
if !nerr.Timeout() {
t.Errorf("want timeout error; got: %q", nerr)
continue
}
if strings.Contains(err.Error(), tt.wantErr) { if strings.Contains(err.Error(), tt.wantErr) {
continue continue
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment