Skip to content
This repository was archived by the owner on Apr 30, 2025. It is now read-only.

Commit a3bd9ab

Browse files
maxmoehldomdom82
andcommitted
fix: do not retry if context has been cancelled
The additional retry logic introduced by #337 caused any error to become retry-able if the request did not make it to the back end. This included errors that came from context cancellation. This commit makes requests non-retry-able as soon as the context has been cancelled. Co-Authored-By: Dominik Froehlich <[email protected]>
1 parent a39d4f5 commit a3bd9ab

File tree

2 files changed

+44
-0
lines changed

2 files changed

+44
-0
lines changed

integration/retry_test.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package integration
33
import (
44
"bytes"
55
"fmt"
6+
"net"
67
"net/http"
78

89
"code.cloudfoundry.org/gorouter/route"
@@ -29,6 +30,44 @@ var _ = Describe("Retries", func() {
2930
}
3031
})
3132

33+
Context("when gorouter is called by a bad client", func() {
34+
var appURL string
35+
var app *common.TestApp
36+
37+
BeforeEach(func() {
38+
appURL = "bad-app." + test_util.LocalhostDNS
39+
40+
app = common.NewTestApp([]route.Uri{route.Uri(appURL)}, testState.cfg.Port, testState.mbusClient, nil, "")
41+
app.TlsRegister(testState.trustedBackendServerCertSAN)
42+
43+
app.AddHandler("/", func(w http.ResponseWriter, r *http.Request) {
44+
w.WriteHeader(http.StatusTeapot)
45+
})
46+
47+
err := app.TlsListen(testState.trustedBackendTLSConfig)
48+
Expect(err).ToNot(HaveOccurred())
49+
})
50+
51+
AfterEach(func() {
52+
app.Stop()
53+
})
54+
55+
It("does not prune the endpoint on context cancelled", func() {
56+
conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", appURL, testState.cfg.Port))
57+
Expect(err).ToNot(HaveOccurred())
58+
59+
_, err = conn.Write([]byte(fmt.Sprintf("GET / HTTP/1.1\r\nHost: %s\r\n\r\n", appURL)))
60+
Expect(err).ToNot(HaveOccurred())
61+
62+
_ = conn.Close()
63+
64+
Consistently(func() bool {
65+
res, err := testState.client.Do(testState.newRequest("http://" + appURL))
66+
return err == nil && res.StatusCode == http.StatusTeapot
67+
}).Should(Equal(true))
68+
})
69+
})
70+
3271
Context("when gorouter talks to a broken app behind envoy", func() {
3372
var appURL string
3473
var badApp *common.TcpApp

proxy/round_tripper/proxy_round_tripper.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,11 @@ func isIdempotent(request *http.Request) bool {
441441
}
442442

443443
func (rt *roundTripper) isRetriable(request *http.Request, err error, trace *requestTracer) (bool, error) {
444+
// if the context has been cancelled we do not perform further retries
445+
if request.Context().Err() != nil {
446+
return false, fmt.Errorf("%w (%w)", request.Context().Err(), err)
447+
}
448+
444449
// io.EOF errors are considered safe to retry for certain requests
445450
// Replace the error here to track this state when classifying later.
446451
if err == io.EOF && isIdempotent(request) {

0 commit comments

Comments
 (0)