The template's files at a77fd30, without its history or LICENSE, after script/rename simplexcalc. Model: opus-5-5
60 lines
1.9 KiB
Go
60 lines
1.9 KiB
Go
package middleware
|
|
|
|
import (
|
|
"net/http"
|
|
"runtime/debug"
|
|
)
|
|
|
|
// panicBody is the entire response a recovered panic produces. No
|
|
// template, no detail: the client learns that the request failed, and
|
|
// everything about why goes to the log and to Sentry, where it is not
|
|
// attacker-readable.
|
|
const panicBody = "internal server error"
|
|
|
|
// Recoverer turns a panicking handler into a 500 rather than a dropped
|
|
// connection.
|
|
//
|
|
// net/http already recovers panics, but what it does is close the
|
|
// connection without a response, so the client sees a transport error
|
|
// and no status. Answering 500 is the difference between "the service
|
|
// is broken" and "the network is broken" for everyone downstream.
|
|
//
|
|
// A panic after the response has started cannot be turned into a 500 —
|
|
// the status is already on the wire — so in that case the connection is
|
|
// deliberately dropped by re-panicking to net/http, which is the only
|
|
// honest signal left that the body is truncated. A truncated 200 that
|
|
// looks complete is worse than a broken connection.
|
|
func (m *Middleware) Recoverer() func(http.Handler) http.Handler {
|
|
return func(next http.Handler) http.Handler {
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
rec := newResponseRecorder(w)
|
|
|
|
defer func() {
|
|
v := recover()
|
|
if v == nil {
|
|
return
|
|
}
|
|
|
|
// http.ErrAbortHandler is net/http's documented way for
|
|
// a handler to abandon a response on purpose. It is not
|
|
// a bug, so it is not reported; it is re-raised for
|
|
// net/http to handle as it always does.
|
|
//nolint:errorlint,err113 // a sentinel value, compared as net/http documents.
|
|
if v == http.ErrAbortHandler {
|
|
panic(v)
|
|
}
|
|
|
|
m.params.Sentry.CapturePanic(v, debug.Stack())
|
|
|
|
if rec.Written() {
|
|
panic(v)
|
|
}
|
|
|
|
http.Error(rec, panicBody, http.StatusInternalServerError)
|
|
}()
|
|
|
|
next.ServeHTTP(rec, r)
|
|
})
|
|
}
|
|
}
|