Skip to content

backend: serviceproxy drops request cancellation context #5721

@harrshita123

Description

@harrshita123

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

  1. Run Headlamp with access to a cluster.
  2. Expose an in-cluster Service endpoint that delays its response.
  3. Start a request through the service proxy:
GET /clusters/<clusterName>/serviceproxy/<namespace>/<serviceName>?request=/slow
  1. Cancel the client request before the service responds, for example by closing the browser tab or aborting the HTTP client.
  2. 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().

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions