Skip to content

Commit

Permalink
Client should return ErrTimeout on timeout (#736)
Browse files Browse the repository at this point in the history
Not ErrConnectionClosed which is incorrect.

Fixes: #355
  • Loading branch information
erikdubbelboer committed Jan 31, 2020
1 parent 38e068a commit 69d5c37
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 6 deletions.
23 changes: 20 additions & 3 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -1284,9 +1284,6 @@ var (
// see this error.
ErrNoFreeConns = errors.New("no free connections available to host")

// ErrTimeout is returned from timed out calls.
ErrTimeout = errors.New("timeout")

// ErrConnectionClosed may be returned from client methods if the server
// closes connection before returning the first response byte.
//
Expand All @@ -1298,6 +1295,26 @@ var (
"Make sure the server returns 'Connection: close' response header before closing the connection")
)

type timeoutError struct {
}

func (e *timeoutError) Error() string {
return "timeout"
}

// Only implement the Timeout() function of the net.Error interface.
// This allows for checks like:
//
// if x, ok := err.(interface{ Timeout() bool }); ok && x.Timeout() {
func (e *timeoutError) Timeout() bool {
return true
}

var (
// ErrTimeout is returned from timed out calls.
ErrTimeout = &timeoutError{}
)

// SetMaxConns sets up the maximum number of connections which may be established to all hosts listed in Addr.
func (c *HostClient) SetMaxConns(newMaxConns int) {
c.connsLock.Lock()
Expand Down
4 changes: 3 additions & 1 deletion client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -436,7 +436,9 @@ func TestClientReadTimeout(t *testing.T) {
req.SetRequestURI("http://localhost")
req.SetConnectionClose()

c.Do(req, res) //nolint:errcheck
if err := c.Do(req, res); err != ErrTimeout {
t.Errorf("expected ErrTimeout got %#v", err)
}

ReleaseRequest(req)
ReleaseResponse(res)
Expand Down
19 changes: 18 additions & 1 deletion fasthttputil/pipeconns.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,9 +197,26 @@ func (c *pipeConn) readNextByteBuffer(mayBlock bool) error {
var (
errWouldBlock = errors.New("would block")
errConnectionClosed = errors.New("connection closed")
)

type timeoutError struct {
}

func (e *timeoutError) Error() string {
return "timeout"
}

// Only implement the Timeout() function of the net.Error interface.
// This allows for checks like:
//
// if x, ok := err.(interface{ Timeout() bool }); ok && x.Timeout() {
func (e *timeoutError) Timeout() bool {
return true
}

var (
// ErrTimeout is returned from Read() or Write() on timeout.
ErrTimeout = errors.New("timeout")
ErrTimeout = &timeoutError{}
)

func (c *pipeConn) Close() error {
Expand Down
6 changes: 5 additions & 1 deletion header.go
Original file line number Diff line number Diff line change
Expand Up @@ -1307,7 +1307,11 @@ func (h *ResponseHeader) tryRead(r *bufio.Reader, n int) error {
h.resetSkipNormalize()
b, err := r.Peek(n)
if len(b) == 0 {
// treat all errors on the first byte read as EOF
// Return ErrTimeout on any timeout.
if x, ok := err.(interface{ Timeout() bool }); ok && x.Timeout() {
return ErrTimeout
}
// treat all other errors on the first byte read as EOF
if n == 1 || err == io.EOF {
return io.EOF
}
Expand Down

0 comments on commit 69d5c37

Please sign in to comment.