Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions lib/kube/proxy/forwarder.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
package proxy

import (
"bytes"
"context"
"crypto/tls"
"encoding/json"
Expand Down Expand Up @@ -2138,6 +2139,20 @@ func (f *Forwarder) catchAll(authCtx *authContext, w http.ResponseWriter, req *h
return nil, trace.Wrap(err)
}

// Buffering req.Body so that HTTP/2 transport can retry
if req.Body != nil && req.Body != http.NoBody {
bodyBytes, err := io.ReadAll(req.Body)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid buffering entire request body in catchAll

catchAll is the fallback path for non-special Kubernetes routes, and this new io.ReadAll(req.Body) forces full in-memory buffering before forwarding instead of streaming. For large or streaming request bodies (for example, proxied aggregated API/service endpoints), this can significantly increase latency and memory usage, and allows a single authenticated large upload to consume substantial heap on the proxy. This should be bounded or gated so retries don’t require unbounded buffering for every request body.

Useful? React with 👍 / 👎.

if err != nil {
return nil, trace.Wrap(err)
}
req.Body.Close()
req.Body = io.NopCloser(bytes.NewReader(bodyBytes))
req.ContentLength = int64(len(bodyBytes))
req.GetBody = func() (io.ReadCloser, error) {
return io.NopCloser(bytes.NewReader(bodyBytes)), nil
}
}

isLocalKubeCluster := sess.isLocalKubernetesCluster
isListRequest := authCtx.requestVerb == types.KubeVerbList
// Watch requests can be send to a single resource or to a collection of resources.
Expand Down
Loading