Skip to content

feat(launcher): add HTTPMiddleware seam to launcher.Config#974

Open
nuthalapativarun wants to merge 1 commit into
google:mainfrom
nuthalapativarun:feat/965-http-middleware-seam
Open

feat(launcher): add HTTPMiddleware seam to launcher.Config#974
nuthalapativarun wants to merge 1 commit into
google:mainfrom
nuthalapativarun:feat/965-http-middleware-seam

Conversation

@nuthalapativarun
Copy link
Copy Markdown

Please ensure you have read the contribution guide before creating a pull request.

Link to Issue or Description of Change

Root Cause / Motivation

Embedders that wrap ADK Go inside a larger application have no way to insert HTTP middleware into the launcher's request pipeline. webLauncher.Run builds the http.Server directly from the router with no public hook for wrapping the handler. The concrete case described in the issue is distributed-tracing context extraction (otelhttp.NewHandler), but the pattern applies to any cross-cutting concern (auth, rate-limiting, request-ID injection, etc.).

Change

Add HTTPMiddleware []func(http.Handler) http.Handler to launcher.Config:

// HTTPMiddleware is applied in order to the HTTP handler before serving.
// Each entry wraps the handler returned by the previous one. The first
// middleware in the slice is the outermost (runs first on inbound requests).
// A nil or empty slice is a no-op; existing callers are unaffected.
HTTPMiddleware []func(http.Handler) http.Handler

Apply the chain in webLauncher.Run before constructing http.Server:

var handler http.Handler = router
for i := len(config.HTTPMiddleware) - 1; i >= 0; i-- {
    handler = config.HTTPMiddleware[i](handler)
}

The reverse-index loop produces outer-first ordering (slice index 0 runs first on inbound requests), matching chi.Use / mux.Use conventions. The zero-value (nil slice) is a no-op, so all existing callers are unaffected.

Testing Plan

Unit Tests:

  • Added TestHTTPMiddleware_AppliedOutermostFirst in cmd/launcher/web/web_test.go: starts the web server with two named middlewares, fires a /ping request, and asserts that the invocation order matches slice order (["first", "second", ...]).
  • All existing tests in ./cmd/launcher/... continue to pass.

Manual End-to-End (E2E) Tests:
Embedder usage after the change:

config := &launcher.Config{
    AgentLoader: agent.NewSingleLoader(myAgent),
    TelemetryOptions: []telemetry.Option{
        telemetry.WithTracerProvider(myTP),
    },
    HTTPMiddleware: []func(http.Handler) http.Handler{
        func(h http.Handler) http.Handler {
            return otelhttp.NewHandler(h, "agent-name",
                otelhttp.WithPropagators(otel.GetTextMapPropagator()))
        },
    },
}
return full.NewLauncher().Execute(ctx, config, args)

Checklist

  • Read CONTRIBUTING.md
  • Self-reviewed code
  • Commented field doc (ordering semantics are non-obvious)
  • Added tests proving the feature
  • New and existing tests pass
  • Purely additive — zero-value is a no-op, no existing callers affected

Add an HTTPMiddleware field to launcher.Config that lets embedders wrap
the HTTP handler before the server is created. Middleware is applied
outermost-first (slice index 0 runs first on inbound requests), matching
the chi.Use / mux.Use convention.

The change is purely additive: existing callers that do not set
HTTPMiddleware see no behavior difference (the handler is unchanged).

Closes google#965
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

cmd/launcher/web: expose an HTTP middleware seam on launcher.Config for embedders

1 participant