| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364 |
- package middleware
- import (
- "errors"
- "net"
- "net/http"
- "os"
- "strings"
- infraerrors "github.com/Wei-Shaw/sub2api/internal/pkg/errors"
- "github.com/Wei-Shaw/sub2api/internal/pkg/response"
- "github.com/gin-gonic/gin"
- )
- // Recovery converts panics into the project's standard JSON error envelope.
- //
- // It preserves Gin's broken-pipe handling by not attempting to write a response
- // when the client connection is already gone.
- func Recovery() gin.HandlerFunc {
- return gin.CustomRecoveryWithWriter(gin.DefaultErrorWriter, func(c *gin.Context, recovered any) {
- recoveredErr, _ := recovered.(error)
- if isBrokenPipe(recoveredErr) {
- if recoveredErr != nil {
- _ = c.Error(recoveredErr)
- }
- c.Abort()
- return
- }
- if c.Writer.Written() {
- c.Abort()
- return
- }
- response.ErrorWithDetails(
- c,
- http.StatusInternalServerError,
- infraerrors.UnknownMessage,
- infraerrors.UnknownReason,
- nil,
- )
- c.Abort()
- })
- }
- func isBrokenPipe(err error) bool {
- if err == nil {
- return false
- }
- var opErr *net.OpError
- if !errors.As(err, &opErr) {
- return false
- }
- var syscallErr *os.SyscallError
- if !errors.As(opErr.Err, &syscallErr) {
- return false
- }
- msg := strings.ToLower(syscallErr.Error())
- return strings.Contains(msg, "broken pipe") || strings.Contains(msg, "connection reset by peer")
- }
|