@marcel91200 / caddy Mirror · read-only / commits / dccce456
Fork 0

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, 01:00 UTC

reverseproxy: close upstream body when handle_response status is invalid (#8318)

When a handle_response status code did not evaluate to an integer, the handler returned a 500 without closing the upstream response body, leaking the connection.

D'Ali Ben Macha committed Nov 3, 2026 at 07:58 UTC dccce456608325272c653a30077b03922ed2c12d
2 files changed +57
modules/caddyhttp/reverseproxy/handleresponse_test.go new
+56
@@ -0,0 +1,56 @@
1 +// Copyright 2015 Matthew Holt and The Caddy Authors
2 +//
3 +// Licensed under the Apache License, Version 2.0 (the "License");
4 +// you may not use this file except in compliance with the License.
5 +// You may obtain a copy of the License at
6 +//
7 +// http://www.apache.org/licenses/LICENSE-2.0
8 +//
9 +// Unless required by applicable law or agreed to in writing, software
10 +// distributed under the License is distributed on an "AS IS" BASIS,
11 +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 +// See the License for the specific language governing permissions and
13 +// limitations under the License.
14 +
15 +package reverseproxy
16 +
17 +import (
18 + "bytes"
19 + "net/http"
20 + "net/http/httptest"
21 + "testing"
22 +
23 + "github.com/caddyserver/caddy/v2/modules/caddyhttp"
24 +)
25 +
26 +// TestHandleResponseInvalidStatusCodeClosesBody verifies that when a
27 +// handle_response status code does not evaluate to an integer, the
28 +// upstream response body is closed instead of being leaked.
29 +func TestHandleResponseInvalidStatusCodeClosesBody(t *testing.T) {
30 + body := &closeTrackingBody{Buffer: bytes.NewBufferString("from-upstream")}
31 +
32 + h := minimalHandler(0, &Upstream{Host: new(Host), Dial: "unused.invalid:80"})
33 + h.Transport = roundTripFunc(func(req *http.Request) (*http.Response, error) {
34 + return &http.Response{
35 + StatusCode: http.StatusOK,
36 + Status: "200 OK",
37 + Header: make(http.Header),
38 + Body: body,
39 + Request: req,
40 + }, nil
41 + })
42 + h.HandleResponse = []caddyhttp.ResponseHandler{{
43 + StatusCode: caddyhttp.WeakString("not-a-number"),
44 + }}
45 +
46 + req := prepareTestRequest(httptest.NewRequest(http.MethodGet, "http://example.com/", nil))
47 + err := h.ServeHTTP(httptest.NewRecorder(), req, caddyhttp.HandlerFunc(func(http.ResponseWriter, *http.Request) error {
48 + return nil
49 + }))
50 + if err == nil {
51 + t.Fatal("expected an error for a non-integer status code, got nil")
52 + }
53 + if !body.closed {
54 + t.Error("upstream response body was not closed")
55 + }
56 +}
modules/caddyhttp/reverseproxy/reverseproxy.go
+1
@@ -1339,6 +1339,7 @@ func (h *Handler) reverseProxy(rw http.ResponseWriter, req *http.Request, origRe
1339 if statusCodeStr := rh.StatusCode.String(); statusCodeStr != "" {
1340 statusCode, err := strconv.Atoi(repl.ReplaceAll(statusCodeStr, ""))
1341 if err != nil {
1342 + res.Body.Close()
1343 return caddyhttp.Error(http.StatusInternalServerError, err)
1344 }
1345 if statusCode != 0 {