Skip to content

ensure all handlers respond with a status code, rather than rely on t…#183

Closed
iwarapter wants to merge 1 commit intoelimity-com:masterfrom
iwarapter:feat/ensure-http-status-for-handlers
Closed

ensure all handlers respond with a status code, rather than rely on t…#183
iwarapter wants to merge 1 commit intoelimity-com:masterfrom
iwarapter:feat/ensure-http-status-for-handlers

Conversation

@iwarapter
Copy link
Contributor

When wrapping the server handler with a logging middleware, e.g:

type detailedResponseWriter struct {
	http.ResponseWriter
	statusCode int
}

func (dw *detailedResponseWriter) WriteHeader(code int) {
	dw.statusCode = code
	dw.ResponseWriter.WriteHeader(code)
}

func (dw *detailedResponseWriter) StatusCode() int {
	return dw.statusCode
}

func Logger(log *slog.Logger) func(next http.Handler) http.Handler {
	return func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			start := time.Now()
			dw := &detailedResponseWriter{ResponseWriter: w}
			next.ServeHTTP(dw, r)
			log.InfoContext(r.Context(), "request log",
				slog.String("method", r.Method),
				slog.String("request_uri", r.URL.Path),
				slog.Any("duration", time.Since(start)),
				slog.Int("status_code", dw.StatusCode()))
		})
	}
}

because most of the underlying handlers rely on the http.Server defaulting the status at the very end - it is not possible to correctly get the status for endpoints which return a status 200.

This change just ensures a consistently setting the http status from all handlers.

@q-uint
Copy link
Collaborator

q-uint commented Mar 24, 2026

Thanks for raising this! You're right that handlers should explicitly set status codes for observability middleware to work.

I would like to go with a slightly different approach: a ResponseWriter wrapper in ServeHTTP that ensures WriteHeader is always called explicitly, even if individual handlers forget. This way it's handled centrally rather than requiring each handler to remember.

I'll reference this PR in our commit. Appreciate the contribution!

@q-uint q-uint closed this Mar 24, 2026
q-uint added a commit that referenced this pull request Mar 24, 2026
Wrap ResponseWriter in ServeHTTP with a statusResponseWriter that
guarantees WriteHeader is always called explicitly. This allows
observability middleware to reliably capture status codes, which
was not possible when handlers relied on net/http's implicit 200
on first Write call.

The wrapper also guards against duplicate WriteHeader calls by
ignoring subsequent invocations after the first.

Closes #183

Co-Authored-By: iwarapter <iwarapter@users.noreply.github.com>
q-uint added a commit that referenced this pull request Mar 24, 2026
* feat: ensure all handlers explicitly set HTTP status codes

Wrap ResponseWriter in ServeHTTP with a statusResponseWriter that
guarantees WriteHeader is always called explicitly. This allows
observability middleware to reliably capture status codes, which
was not possible when handlers relied on net/http's implicit 200
on first Write call.

The wrapper also guards against duplicate WriteHeader calls by
ignoring subsequent invocations after the first.

Closes #183

Co-Authored-By: iwarapter <iwarapter@users.noreply.github.com>

* chore: build goarrange via nix and fix formatting

---------

Co-authored-by: iwarapter <iwarapter@users.noreply.github.com>
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.

2 participants