Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS
Mirrored from https://github.com/Slazee/caddy.git
Synced Nov 11, 03:30 UTC
fix: preserve FastCGI dial errors for retry (#8172)
Wrap backend dial failures as reverseproxy.DialError so non-GET requests can be retried when no connection was made. AI-assisted implementation and test; reviewed by zemamba. Signed-off-by: zemamba <zemamba@gmail.com>
zemamba committed
Oct 8, 2026 at 07:02 UTC
221ebc4503398fa8c87d95a52d43d0d93a6aeef4
3 files changed
+23
-1
modules/caddyhttp/reverseproxy/fastcgi/fastcgi.go
+1
-1
@@ -213,7 +213,7 @@ func (t Transport) RoundTrip(r *http.Request) (*http.Response, error) {
213
dialer := net.Dialer{Timeout: time.Duration(t.DialTimeout)}
214
conn, err := dialer.DialContext(ctx, network, address)
215
if err != nil {
216
- return nil, fmt.Errorf("dialing backend: %v", err)
216
+ return nil, reverseproxy.NewDialError(fmt.Errorf("dialing backend: %w", err))
217
}
218
defer func() {
219
// conn will be closed with the response body unless there's an error
modules/caddyhttp/reverseproxy/fastcgi/incremental_test.go
+17
@@ -13,6 +13,7 @@ import (
13
14
"github.com/caddyserver/caddy/v2"
15
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
16
+ "github.com/caddyserver/caddy/v2/modules/caddyhttp/reverseproxy"
17
)
18
19
// fcgiBackend serves h over FastCGI and returns its address.
@@ -121,6 +122,22 @@ func TestRequiresContentLength(t *testing.T) {
122
}
123
}
124
125
+func TestRoundTripDialError(t *testing.T) {
126
+ req := httptest.NewRequest(http.MethodPost, "http://example.com/index.php", strings.NewReader("body"))
127
+ _, err := fcgiRoundTrip(t, "invalid address", req)
128
+ if err == nil {
129
+ t.Fatal("RoundTrip() error = nil, want a dial error")
130
+ }
131
+
132
+ var dialErr reverseproxy.DialError
133
+ if !errors.As(err, &dialErr) {
134
+ t.Fatalf("RoundTrip() error type = %T, want reverseproxy.DialError", err)
135
+ }
136
+ if !strings.HasPrefix(err.Error(), "dialing backend:") {
137
+ t.Errorf("RoundTrip() error = %q, want dialing context", err)
138
+ }
139
+}
140
+
141
// The request buffer fastcgi asks for by default is what supplies the
142
// CONTENT_LENGTH above, so these values matter beyond this package.
143
func TestDefaultBufferSizes(t *testing.T) {
modules/caddyhttp/reverseproxy/reverseproxy.go
+5
@@ -1925,6 +1925,11 @@ var hopHeaders = []string{
1925
// in a call to Dial or DialContext.
1926
type DialError struct{ error }
1927
1928
+// NewDialError wraps err as a DialError for transports outside this package.
1929
+func NewDialError(err error) DialError {
1930
+ return DialError{err}
1931
+}
1932
+
1933
// TLSTransport is implemented by transports
1934
// that are capable of using TLS.
1935
type TLSTransport interface {