Skip to content

Commit 74a0507

Browse files
iv-mensheninIgor Menshenin
andauthored
Immediately return ErrTimeout if deadline is already reached. (#1497)
* fix: Immediately return ErrTimeout if deadline is already reached. * test: Added tests for negative timeout --------- Co-authored-by: Igor Menshenin <[email protected]>
1 parent 559d536 commit 74a0507

File tree

2 files changed

+68
-0
lines changed

2 files changed

+68
-0
lines changed

client.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,7 @@ func (c *Client) Post(dst []byte, url string, postArgs *Args) (statusCode int, b
374374
//
375375
// ErrTimeout is returned if the response wasn't returned during
376376
// the given timeout.
377+
// Immediately returns ErrTimeout if timeout value is negative.
377378
//
378379
// ErrNoFreeConns is returned if all Client.MaxConnsPerHost connections
379380
// to the requested host are busy.
@@ -387,6 +388,9 @@ func (c *Client) Post(dst []byte, url string, postArgs *Args) (statusCode int, b
387388
// try setting a ReadTimeout.
388389
func (c *Client) DoTimeout(req *Request, resp *Response, timeout time.Duration) error {
389390
req.timeout = timeout
391+
if req.timeout < 0 {
392+
return ErrTimeout
393+
}
390394
return c.Do(req, resp)
391395
}
392396

@@ -407,6 +411,7 @@ func (c *Client) DoTimeout(req *Request, resp *Response, timeout time.Duration)
407411
//
408412
// ErrTimeout is returned if the response wasn't returned until
409413
// the given deadline.
414+
// Immediately returns ErrTimeout if the deadline has already been reached.
410415
//
411416
// ErrNoFreeConns is returned if all Client.MaxConnsPerHost connections
412417
// to the requested host are busy.
@@ -415,6 +420,9 @@ func (c *Client) DoTimeout(req *Request, resp *Response, timeout time.Duration)
415420
// and AcquireResponse in performance-critical code.
416421
func (c *Client) DoDeadline(req *Request, resp *Response, deadline time.Time) error {
417422
req.timeout = time.Until(deadline)
423+
if req.timeout < 0 {
424+
return ErrTimeout
425+
}
418426
return c.Do(req, resp)
419427
}
420428

@@ -1139,6 +1147,7 @@ func ReleaseResponse(resp *Response) {
11391147
//
11401148
// ErrTimeout is returned if the response wasn't returned during
11411149
// the given timeout.
1150+
// Immediately returns ErrTimeout if timeout value is negative.
11421151
//
11431152
// ErrNoFreeConns is returned if all HostClient.MaxConns connections
11441153
// to the host are busy.
@@ -1152,6 +1161,9 @@ func ReleaseResponse(resp *Response) {
11521161
// try setting a ReadTimeout.
11531162
func (c *HostClient) DoTimeout(req *Request, resp *Response, timeout time.Duration) error {
11541163
req.timeout = timeout
1164+
if req.timeout < 0 {
1165+
return ErrTimeout
1166+
}
11551167
return c.Do(req, resp)
11561168
}
11571169

@@ -1167,6 +1179,7 @@ func (c *HostClient) DoTimeout(req *Request, resp *Response, timeout time.Durati
11671179
//
11681180
// ErrTimeout is returned if the response wasn't returned until
11691181
// the given deadline.
1182+
// Immediately returns ErrTimeout if the deadline has already been reached.
11701183
//
11711184
// ErrNoFreeConns is returned if all HostClient.MaxConns connections
11721185
// to the host are busy.
@@ -1175,6 +1188,9 @@ func (c *HostClient) DoTimeout(req *Request, resp *Response, timeout time.Durati
11751188
// and AcquireResponse in performance-critical code.
11761189
func (c *HostClient) DoDeadline(req *Request, resp *Response, deadline time.Time) error {
11771190
req.timeout = time.Until(deadline)
1191+
if req.timeout < 0 {
1192+
return ErrTimeout
1193+
}
11781194
return c.Do(req, resp)
11791195
}
11801196

client_test.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,32 @@ func testPipelineClientSetUserAgent(t *testing.T, timeout time.Duration) {
120120
}
121121
}
122122

123+
func TestHostClientNegativeTimeout(t *testing.T) {
124+
t.Parallel()
125+
126+
ln := fasthttputil.NewInmemoryListener()
127+
s := &Server{
128+
Handler: func(ctx *RequestCtx) {
129+
},
130+
}
131+
go s.Serve(ln) //nolint:errcheck
132+
c := &HostClient{
133+
Dial: func(addr string) (net.Conn, error) {
134+
return ln.Dial()
135+
},
136+
}
137+
req := AcquireRequest()
138+
req.Header.SetMethod(MethodGet)
139+
req.SetRequestURI("http://example.com")
140+
if err := c.DoTimeout(req, nil, -time.Second); err != ErrTimeout {
141+
t.Fatalf("expected ErrTimeout error got: %+v", err)
142+
}
143+
if err := c.DoDeadline(req, nil, time.Now().Add(-time.Second)); err != ErrTimeout {
144+
t.Fatalf("expected ErrTimeout error got: %+v", err)
145+
}
146+
ln.Close()
147+
}
148+
123149
func TestPipelineClientIssue832(t *testing.T) {
124150
t.Parallel()
125151

@@ -306,6 +332,32 @@ func TestClientNilResp(t *testing.T) {
306332
ln.Close()
307333
}
308334

335+
func TestClientNegativeTimeout(t *testing.T) {
336+
t.Parallel()
337+
338+
ln := fasthttputil.NewInmemoryListener()
339+
s := &Server{
340+
Handler: func(ctx *RequestCtx) {
341+
},
342+
}
343+
go s.Serve(ln) //nolint:errcheck
344+
c := &Client{
345+
Dial: func(addr string) (net.Conn, error) {
346+
return ln.Dial()
347+
},
348+
}
349+
req := AcquireRequest()
350+
req.Header.SetMethod(MethodGet)
351+
req.SetRequestURI("http://example.com")
352+
if err := c.DoTimeout(req, nil, -time.Second); err != ErrTimeout {
353+
t.Fatalf("expected ErrTimeout error got: %+v", err)
354+
}
355+
if err := c.DoDeadline(req, nil, time.Now().Add(-time.Second)); err != ErrTimeout {
356+
t.Fatalf("expected ErrTimeout error got: %+v", err)
357+
}
358+
ln.Close()
359+
}
360+
309361
func TestPipelineClientNilResp(t *testing.T) {
310362
t.Parallel()
311363

0 commit comments

Comments
 (0)