mattermost-community-enterp.../vendor/github.com/getsentry/sentry-go/http/sentryhttp.go
Claude ec1f89217a Merge: Complete Mattermost Server with Community Enterprise
Full Mattermost server source with integrated Community Enterprise features.
Includes vendor directory for offline/air-gapped builds.

Structure:
- enterprise-impl/: Enterprise feature implementations
- enterprise-community/: Init files that register implementations
- enterprise/: Bridge imports (community_imports.go)
- vendor/: All dependencies for offline builds

Build (online):
  go build ./cmd/mattermost

Build (offline/air-gapped):
  go build -mod=vendor ./cmd/mattermost

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-17 23:59:07 +09:00

142 lines
4.2 KiB
Go

// Package sentryhttp provides Sentry integration for servers based on the
// net/http package.
package sentryhttp
import (
"context"
"net/http"
"time"
"github.com/getsentry/sentry-go"
"github.com/getsentry/sentry-go/internal/httputils"
"github.com/getsentry/sentry-go/internal/traceutils"
)
// The identifier of the HTTP SDK.
const sdkIdentifier = "sentry.go.http"
// A Handler is an HTTP middleware factory that provides integration with
// Sentry.
type Handler struct {
repanic bool
waitForDelivery bool
timeout time.Duration
}
// Options configure a Handler.
type Options struct {
// Repanic configures whether to panic again after recovering from a panic.
// Use this option if you have other panic handlers or want the default
// behavior from Go's http package, as documented in
// https://golang.org/pkg/net/http/#Handler.
Repanic bool
// WaitForDelivery indicates, in case of a panic, whether to block the
// current goroutine and wait until the panic event has been reported to
// Sentry before repanicking or resuming normal execution.
//
// This option is normally not needed. Unless you need different behaviors
// for different HTTP handlers, configure the SDK to use the
// HTTPSyncTransport instead.
//
// Waiting (or using HTTPSyncTransport) is useful when the web server runs
// in an environment that interrupts execution at the end of a request flow,
// like modern serverless platforms.
WaitForDelivery bool
// Timeout for the delivery of panic events. Defaults to 2s. Only relevant
// when WaitForDelivery is true.
//
// If the timeout is reached, the current goroutine is no longer blocked
// waiting, but the delivery is not canceled.
Timeout time.Duration
}
// New returns a new Handler. Use the Handle and HandleFunc methods to wrap
// existing HTTP handlers.
func New(options Options) *Handler {
if options.Timeout == 0 {
options.Timeout = 2 * time.Second
}
return &Handler{
repanic: options.Repanic,
timeout: options.Timeout,
waitForDelivery: options.WaitForDelivery,
}
}
// Handle works as a middleware that wraps an existing http.Handler. A wrapped
// handler will recover from and report panics to Sentry, and provide access to
// a request-specific hub to report messages and errors.
func (h *Handler) Handle(handler http.Handler) http.Handler {
return h.handle(handler)
}
// HandleFunc is like Handle, but with a handler function parameter for cases
// where that is convenient. In particular, use it to wrap a handler function
// literal.
//
// http.Handle(pattern, h.HandleFunc(func (w http.ResponseWriter, r *http.Request) {
// // handler code here
// }))
func (h *Handler) HandleFunc(handler http.HandlerFunc) http.HandlerFunc {
return h.handle(handler)
}
func (h *Handler) handle(handler http.Handler) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
hub := sentry.GetHubFromContext(r.Context())
if hub == nil {
hub = sentry.CurrentHub().Clone()
ctx = sentry.SetHubOnContext(ctx, hub)
}
if client := hub.Client(); client != nil {
client.SetSDKIdentifier(sdkIdentifier)
}
options := []sentry.SpanOption{
sentry.ContinueTrace(hub, r.Header.Get(sentry.SentryTraceHeader), r.Header.Get(sentry.SentryBaggageHeader)),
sentry.WithOpName("http.server"),
sentry.WithTransactionSource(sentry.SourceURL),
sentry.WithSpanOrigin(sentry.SpanOriginStdLib),
}
transaction := sentry.StartTransaction(ctx,
traceutils.GetHTTPSpanName(r),
options...,
)
transaction.SetData("http.request.method", r.Method)
rw := httputils.NewWrapResponseWriter(w, r.ProtoMajor)
defer func() {
status := rw.Status()
transaction.Status = sentry.HTTPtoSpanStatus(status)
transaction.SetData("http.response.status_code", status)
transaction.Finish()
}()
hub.Scope().SetRequest(r)
r = r.WithContext(transaction.Context())
defer h.recoverWithSentry(hub, r)
handler.ServeHTTP(rw, r)
}
}
func (h *Handler) recoverWithSentry(hub *sentry.Hub, r *http.Request) {
if err := recover(); err != nil {
eventID := hub.RecoverWithContext(
context.WithValue(r.Context(), sentry.RequestContextKey, r),
err,
)
if eventID != nil && h.waitForDelivery {
hub.Flush(h.timeout)
}
if h.repanic {
panic(err)
}
}
}