Bug
The backend service proxy drops the incoming HTTP request context before making the upstream service request.
backend/pkg/serviceproxy/handler.go receives the client request and has access to r.Context(), but handleServiceProxy() calls the service connection without passing that context:
handleServiceProxy(conn, requestURI, w)
The context is then replaced with context.Background() in backend/pkg/serviceproxy/connection.go:
body, err := HTTPGet(context.Background(), fullURL.String())
HTTPGet() already accepts a context.Context, so the upstream request can be cancelled, but the service proxy path never passes the original request context into it.
Why this matters
If the browser tab is closed, the network drops, or a reverse proxy cancels the Headlamp request, the backend should stop work for that proxied request.
Instead, the upstream service request keeps running until the service responds or the fixed HTTP client timeout expires. Under repeated abandoned requests to slow in-cluster services, Headlamp can keep unnecessary goroutines and upstream connections alive after clients have gone away.
Steps to reproduce
- Run Headlamp with access to a cluster.
- Expose an in-cluster Service endpoint that delays its response.
- Start a request through the service proxy:
GET /clusters/<clusterName>/serviceproxy/<namespace>/<serviceName>?request=/slow
- Cancel the client request before the service responds, for example by closing the browser tab or aborting the HTTP client.
- Observe that the backend service proxy request continues until the upstream service response or timeout instead of cancelling immediately with the client request.
Expected behavior
The service proxy should propagate r.Context() to the upstream request so cancellation of the client request cancels the in-flight service request.
Actual behavior
The service proxy uses context.Background() for the upstream request, so client cancellation is not propagated.
Code references
backend/pkg/serviceproxy/handler.go
RequestHandler() has r.Context() but does not pass it to handleServiceProxy()
handleServiceProxy() accepts only conn, requestURI, and w
backend/pkg/serviceproxy/connection.go
Connection.Get() calls HTTPGet(context.Background(), fullURL.String())
backend/pkg/serviceproxy/http.go
HTTPGet(ctx, uri) already supports receiving a cancellable context
Possible fix
Thread the request context through the service proxy call chain:
handleServiceProxy(r.Context(), conn, requestURI, w)
and update the connection interface to accept a context:
type ServiceConnection interface {
Get(context.Context, string) ([]byte, error)
}
func (c *Connection) Get(ctx context.Context, requestURI string) ([]byte, error) {
...
return HTTPGet(ctx, fullURL.String())
}
Duplicate check
I checked related backend proxy issues:
This issue is specifically about serviceproxy losing request cancellation by replacing r.Context() with context.Background().
Bug
The backend service proxy drops the incoming HTTP request context before making the upstream service request.
backend/pkg/serviceproxy/handler.goreceives the client request and has access tor.Context(), buthandleServiceProxy()calls the service connection without passing that context:The context is then replaced with
context.Background()inbackend/pkg/serviceproxy/connection.go:HTTPGet()already accepts acontext.Context, so the upstream request can be cancelled, but the service proxy path never passes the original request context into it.Why this matters
If the browser tab is closed, the network drops, or a reverse proxy cancels the Headlamp request, the backend should stop work for that proxied request.
Instead, the upstream service request keeps running until the service responds or the fixed HTTP client timeout expires. Under repeated abandoned requests to slow in-cluster services, Headlamp can keep unnecessary goroutines and upstream connections alive after clients have gone away.
Steps to reproduce
Expected behavior
The service proxy should propagate
r.Context()to the upstream request so cancellation of the client request cancels the in-flight service request.Actual behavior
The service proxy uses
context.Background()for the upstream request, so client cancellation is not propagated.Code references
backend/pkg/serviceproxy/handler.goRequestHandler()hasr.Context()but does not pass it tohandleServiceProxy()handleServiceProxy()accepts onlyconn,requestURI, andwbackend/pkg/serviceproxy/connection.goConnection.Get()callsHTTPGet(context.Background(), fullURL.String())backend/pkg/serviceproxy/http.goHTTPGet(ctx, uri)already supports receiving a cancellable contextPossible fix
Thread the request context through the service proxy call chain:
and update the connection interface to accept a context:
Duplicate check
I checked related backend proxy issues:
/externalproxyandserviceproxybuffering entire upstream responses in memory.500./externalproxycreating a client per request and lacking upstream timeout/pool tuning.This issue is specifically about
serviceproxylosing request cancellation by replacingr.Context()withcontext.Background().