@@ -2,10 +2,12 @@ package httpclient
22
33import (
44 "bytes"
5+ "context"
56 "io"
67 "net/http"
78 "net/http/httptest"
89 "strings"
10+ "sync/atomic"
911 "testing"
1012 "time"
1113
@@ -518,3 +520,107 @@ func respBody(t *testing.T, response *http.Response) string {
518520
519521 return string (respBody )
520522}
523+
524+ func TestHTTPClientDoContextCancelledDuringRetry (t * testing.T ) {
525+ noOfRetries := 3
526+ backoffInterval := 100 * time .Millisecond
527+ maximumJitterInterval := 10 * time .Millisecond
528+
529+ client := NewClient (
530+ WithHTTPTimeout (10 * time .Millisecond ),
531+ WithRetryCount (noOfRetries ),
532+ WithRetrier (heimdall .NewRetrier (heimdall .NewConstantBackoff (backoffInterval , maximumJitterInterval ))),
533+ )
534+
535+ count := atomic.Int32 {}
536+ count .Store (0 )
537+ dummyHandler := func (w http.ResponseWriter , r * http.Request ) {
538+ w .WriteHeader (http .StatusInternalServerError )
539+ count .Add (1 )
540+ }
541+
542+ server := httptest .NewServer (http .HandlerFunc (dummyHandler ))
543+ defer server .Close ()
544+
545+ ctx , cancel := context .WithCancel (context .Background ())
546+ req , err := http .NewRequest (http .MethodGet , server .URL , nil )
547+ require .NoError (t , err )
548+ req = req .WithContext (ctx )
549+
550+ // Cancel the context after a short delay to simulate context cancellation during sleep
551+ go func () {
552+ time .Sleep (10 * time .Millisecond )
553+ cancel ()
554+ }()
555+
556+ _ , err = client .Do (req )
557+ require .Error (t , err )
558+ assert .Contains (t , err .Error (), context .Canceled .Error ())
559+ assert .Less (t , count .Load (), int32 (noOfRetries + 1 ), "should not have completed all retries due to context cancellation" )
560+ }
561+
562+ func TestHTTPClientDoContextCancelledBeforeRetry (t * testing.T ) {
563+ client := NewClient (
564+ WithHTTPTimeout (10 * time .Millisecond ),
565+ WithRetryCount (3 ),
566+ WithRetrier (heimdall .NewRetrierFunc (func (retry int ) time.Duration {
567+ assert .Fail (t , "should not have retrier func due to context cancellation" )
568+ return 0
569+ })),
570+ )
571+ ctx , cancel := context .WithCancel (context .Background ())
572+
573+ count := atomic.Int32 {}
574+ count .Store (0 )
575+ dummyHandler := func (w http.ResponseWriter , r * http.Request ) {
576+ cancel () // Cancel immediately
577+ count .Add (1 )
578+ w .WriteHeader (http .StatusInternalServerError )
579+ }
580+
581+ server := httptest .NewServer (http .HandlerFunc (dummyHandler ))
582+ defer server .Close ()
583+
584+ req , err := http .NewRequest (http .MethodGet , server .URL , nil )
585+ require .NoError (t , err )
586+ req = req .WithContext (ctx )
587+
588+ _ , err = client .Do (req )
589+ require .Error (t , err )
590+ assert .Contains (t , err .Error (), context .Canceled .Error ())
591+ assert .Equal (t , int32 (1 ), count .Load ())
592+ }
593+
594+ func TestHTTPClientDoContextTimeoutDuringRetry (t * testing.T ) {
595+ noOfRetries := 3
596+ backoffInterval := 100 * time .Millisecond
597+ maximumJitterInterval := 10 * time .Millisecond
598+
599+ client := NewClient (
600+ WithHTTPTimeout (10 * time .Millisecond ),
601+ WithRetryCount (noOfRetries ),
602+ WithRetrier (heimdall .NewRetrier (heimdall .NewConstantBackoff (backoffInterval , maximumJitterInterval ))),
603+ )
604+
605+ count := atomic.Int32 {}
606+ count .Store (0 )
607+ dummyHandler := func (w http.ResponseWriter , r * http.Request ) {
608+ w .WriteHeader (http .StatusInternalServerError )
609+ count .Add (1 )
610+ }
611+
612+ server := httptest .NewServer (http .HandlerFunc (dummyHandler ))
613+ defer server .Close ()
614+
615+ ctx , cancel := context .WithTimeout (context .Background (), 15 * time .Millisecond )
616+ defer cancel ()
617+
618+ req , err := http .NewRequest (http .MethodGet , server .URL , nil )
619+ require .NoError (t , err )
620+ req = req .WithContext (ctx )
621+
622+ _ , err = client .Do (req )
623+ require .Error (t , err )
624+ assert .Contains (t , err .Error (), context .DeadlineExceeded .Error ())
625+ assert .Less (t , count .Load (), int32 (noOfRetries + 1 ), "should not have completed all retries due to context timeout" )
626+ }
0 commit comments