fix(security): enhance timeout configurations and body size limits fo… (#3984)

* fix(security): enhance timeout configurations and body size limits for HTTP requests

Signed-off-by: Ramkumar Chinchani <rchincha.dev@gmail.com>

* fix(tests): refactor backend result handling in proxyHTTPRequest test

Signed-off-by: Ramkumar Chinchani <rchincha.dev@gmail.com>

* fix(security): preserve ContentLength in proxied requests to prevent server hang

Signed-off-by: Ramkumar Chinchani <rchincha.dev@gmail.com>

* fix(security): preserve explicit zero-length request bodies in proxyHTTPRequest
fix(tests): add test for normalizedTimeout function to ensure default fallback

Signed-off-by: Ramkumar Chinchani <rchincha.dev@gmail.com>

* fix(security): prevent default HTTP timeout values from being set unless explicitly configured

Signed-off-by: Ramkumar Chinchani <rchincha.dev@gmail.com>

* fix(security): refactor timeout handling to use explicit checks for nil and non-positive values

Signed-off-by: Ramkumar Chinchani <rchincha.dev@gmail.com>

* fix(tests): add wait_for_event_count function to ensure expected event generation

Signed-off-by: Ramkumar Chinchani <rchincha.dev@gmail.com>

* fix(security): improve timeout handling and update error responses for large requests

Signed-off-by: Ramkumar Chinchani <rchincha.dev@gmail.com>

* fix(security): enhance HTTP timeout handling with explicit accessors and default values

Signed-off-by: Ramkumar Chinchani <rchincha.dev@gmail.com>

* fix(security): increase default API key body size and timeout values for improved performance

Signed-off-by: Ramkumar Chinchani <rchincha.dev@gmail.com>

* fix(security): unify timeout handling by replacing specific read/write timeouts with a single default timeout

Signed-off-by: Ramkumar Chinchani <rchincha.dev@gmail.com>

* fix(security): consolidate HTTP timeout accessors and enhance timeout handling

Signed-off-by: Ramkumar Chinchani <rchincha.dev@gmail.com>

* fix(security): simplify HTTP timeout accessors and set default values for read/write timeouts

Co-authored-by: Copilot <copilot@github.com>
Signed-off-by: Ramkumar Chinchani <rchincha.dev@gmail.com>

---------

Signed-off-by: Ramkumar Chinchani <rchincha.dev@gmail.com>
Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
Ramkumar Chinchani
2026-04-26 12:23:48 -07:00
committed by GitHub
parent 8282aef12b
commit 934b22d124
17 changed files with 440 additions and 56 deletions
+15 -36
View File
@@ -1,7 +1,6 @@
package api
import (
"bytes"
"context"
"fmt"
"io"
@@ -131,15 +130,28 @@ func proxyHTTPRequest(ctx context.Context, req *http.Request,
cloneURL.Scheme = proxyQueryScheme
cloneURL.Host = targetMember
clonedBody := cloneRequestBody(req)
requestBody := io.Reader(http.NoBody)
if req.Body != nil {
requestBody = req.Body
}
fwdRequest, err := http.NewRequestWithContext(ctx, req.Method, cloneURL.String(), clonedBody)
fwdRequest, err := http.NewRequestWithContext(ctx, req.Method, cloneURL.String(), requestBody)
if err != nil {
return nil, err
}
copyHeader(fwdRequest.Header, req.Header)
// Preserve ContentLength from original request, including explicit zero-length
// bodies, so empty requests are not forwarded as unknown-length chunked bodies.
if req.ContentLength >= 0 {
fwdRequest.ContentLength = req.ContentLength
if req.ContentLength == 0 {
fwdRequest.Body = http.NoBody
}
}
// always set hop count to 1 for now.
// the handler wrapper above will terminate the process if it sees a request that
// already has a hop count but is due for proxying.
@@ -171,42 +183,9 @@ func proxyHTTPRequest(ctx context.Context, req *http.Request,
return nil, err
}
var clonedRespBody bytes.Buffer
// copy out the contents into a new buffer as the response body
// stream should be closed to get all the data out.
_, _ = io.Copy(&clonedRespBody, resp.Body)
resp.Body.Close()
// after closing the original body, substitute it with a new reader
// using the buffer that was just created.
// this buffer should be closed later by the consumer of the response.
resp.Body = io.NopCloser(bytes.NewReader(clonedRespBody.Bytes()))
return resp, nil
}
func cloneRequestBody(src *http.Request) io.Reader {
var bCloneForOriginal, bCloneForCopy bytes.Buffer
multiWriter := io.MultiWriter(&bCloneForOriginal, &bCloneForCopy)
numBytesCopied, _ := io.Copy(multiWriter, src.Body)
// if the body is a type of io.NopCloser and length is 0,
// the Content-Length header is not sent in the proxied request.
// explicitly returning http.NoBody allows the implementation
// to set the header.
// ref: https://github.com/golang/go/issues/34295
if numBytesCopied == 0 {
src.Body = http.NoBody
return http.NoBody
}
src.Body = io.NopCloser(&bCloneForOriginal)
return bytes.NewReader(bCloneForCopy.Bytes())
}
func copyHeader(dst, src http.Header) {
for k, vv := range src {
for _, v := range vv {