@@ -24,19 +24,35 @@ import (
24
24
// ErrInvalidContentType is returned when the content-type is not a JSON body.
25
25
var ErrInvalidContentType = errors .New ("form parameter encoding not supported, please change the hook to send JSON payloads" )
26
26
27
- type PagerDutyInterceptor struct {}
27
+ type ErrorCodeWithReason struct {
28
+ ErrorCode int
29
+ Reason string
30
+ }
28
31
29
- func (pdi PagerDutyInterceptor ) ServeHTTP (w http.ResponseWriter , r * http.Request ) {
30
- b , err := pdi .executeInterceptor (r )
31
- if err != nil {
32
- var e Error
33
- if errors .As (err , & e ) {
34
- logging .Infof ("HTTP %d - %s" , e .Status (), e )
35
- http .Error (w , e .Error (), e .Status ())
36
- } else {
37
- logging .Errorf ("Non Status Error: %s" , err )
38
- http .Error (w , http .StatusText (http .StatusInternalServerError ), http .StatusInternalServerError )
39
- }
32
+ type InterceptorStats struct {
33
+ RequestsCount uint64
34
+ CodeWithReasonToErrorsCount map [ErrorCodeWithReason ]int
35
+ }
36
+
37
+ func CreateInterceptorStats () * InterceptorStats {
38
+ return & InterceptorStats {CodeWithReasonToErrorsCount : make (map [ErrorCodeWithReason ]int )}
39
+ }
40
+
41
+ type interceptorHandler struct {
42
+ stats * InterceptorStats
43
+ }
44
+
45
+ func CreateInterceptorHandler (stats * InterceptorStats ) http.Handler {
46
+ return & interceptorHandler {stats }
47
+ }
48
+
49
+ func (pdi interceptorHandler ) ServeHTTP (w http.ResponseWriter , r * http.Request ) {
50
+ pdi .stats .RequestsCount ++
51
+
52
+ b , httpErr := pdi .executeInterceptor (r )
53
+ if httpErr != nil {
54
+ logging .Infof ("HTTP %d - %s" , httpErr .code , httpErr .err )
55
+ http .Error (w , httpErr .err .Error (), httpErr .code )
40
56
}
41
57
42
58
w .Header ().Add ("Content-Type" , "application/json" )
@@ -45,46 +61,35 @@ func (pdi PagerDutyInterceptor) ServeHTTP(w http.ResponseWriter, r *http.Request
45
61
}
46
62
}
47
63
48
- // Error represents a handler error. It provides methods for a HTTP status
49
- // code and embeds the built-in error interface.
50
- type Error interface {
51
- error
52
- Status () int
64
+ // httpError represents an error with an associated HTTP status code.
65
+ type httpError struct {
66
+ code int
67
+ err error
53
68
}
54
69
55
- // HTTPError represents an error with an associated HTTP status code.
56
- type HTTPError struct {
57
- Code int
58
- Err error
59
- }
60
-
61
- // Allows HTTPError to satisfy the error interface.
62
- func (se HTTPError ) Error () string {
63
- return se .Err .Error ()
64
- }
70
+ func (pdi * interceptorHandler ) httpError (errorCode int , reason string , err error ) * httpError {
71
+ pdi .stats .CodeWithReasonToErrorsCount [ErrorCodeWithReason {errorCode , reason }]++
65
72
66
- // Returns our HTTP status code.
67
- func (se HTTPError ) Status () int {
68
- return se .Code
73
+ return & httpError {code : errorCode , err : fmt .Errorf ("%s: %w" , reason , err )}
69
74
}
70
75
71
- func badRequest (err error ) HTTPError {
72
- return HTTPError { Code : http .StatusBadRequest , Err : err }
76
+ func ( pdi * interceptorHandler ) badRequest (reason string , err error ) * httpError {
77
+ return pdi . httpError ( http .StatusBadRequest , reason , err )
73
78
}
74
79
75
- func internal (err error ) HTTPError {
76
- return HTTPError { Code : http .StatusInternalServerError , Err : err }
80
+ func ( pdi * interceptorHandler ) internal (reason string , err error ) * httpError {
81
+ return pdi . httpError ( http .StatusInternalServerError , reason , err )
77
82
}
78
83
79
- func (pdi * PagerDutyInterceptor ) executeInterceptor (r * http.Request ) ([]byte , error ) {
84
+ func (pdi * interceptorHandler ) executeInterceptor (r * http.Request ) ([]byte , * httpError ) {
80
85
// Create a context
81
86
ctx , cancel := context .WithTimeout (r .Context (), 3 * time .Second )
82
87
defer cancel ()
83
88
84
89
var body bytes.Buffer
85
90
defer r .Body .Close () //nolint:errcheck
86
91
if _ , err := io .Copy (& body , r .Body ); err != nil {
87
- return nil , internal (fmt . Errorf ( "failed to read body: %w " , err ) )
92
+ return nil , pdi . internal ("failed to read body" , err )
88
93
}
89
94
r .Body = io .NopCloser (bytes .NewReader (body .Bytes ()))
90
95
@@ -95,12 +100,12 @@ func (pdi *PagerDutyInterceptor) executeInterceptor(r *http.Request) ([]byte, er
95
100
Header map [string ][]string `json:"header"`
96
101
}
97
102
if err := json .Unmarshal (body .Bytes (), & originalReq ); err != nil {
98
- return nil , badRequest (fmt . Errorf ( "failed to parse request body: %w " , err ) )
103
+ return nil , pdi . badRequest ("failed to parse body" , err )
99
104
}
100
105
101
106
extractedRequest , err := http .NewRequestWithContext (ctx , r .Method , r .URL .String (), bytes .NewReader ([]byte (originalReq .Body )))
102
107
if err != nil {
103
- return nil , internal (fmt . Errorf ( "malformed body/header in unwrapped request: %w " , err ) )
108
+ return nil , pdi . internal ("malformed body/header in unwrapped request" , err )
104
109
}
105
110
106
111
for k , v := range originalReq .Header {
@@ -117,26 +122,26 @@ func (pdi *PagerDutyInterceptor) executeInterceptor(r *http.Request) ([]byte, er
117
122
118
123
err = webhookv3 .VerifySignature (extractedRequest , token )
119
124
if err != nil {
120
- return nil , badRequest (fmt . Errorf ( "failed to verify signature: %w " , err ) )
125
+ return nil , pdi . badRequest ("failed to verify signature" , err )
121
126
}
122
127
123
128
logging .Info ("Signature verified successfully" )
124
129
125
130
if err := json .Unmarshal (body .Bytes (), & ireq ); err != nil {
126
- return nil , badRequest (fmt . Errorf ( "failed to parse body as InterceptorRequest: %w " , err ) )
131
+ return nil , pdi . badRequest ("failed to parse body as InterceptorRequest" , err )
127
132
}
128
133
logging .Debugf ("Interceptor request body is: %s" , ireq .Body )
129
134
130
- iresp := pdi .Process (ctx , & ireq )
135
+ iresp := pdi .process (ctx , & ireq )
131
136
logging .Debugf ("Interceptor response is: %+v" , iresp )
132
137
respBytes , err := json .Marshal (iresp )
133
138
if err != nil {
134
- return nil , internal (err )
139
+ return nil , pdi . internal ("failed to encode response" , err )
135
140
}
136
141
return respBytes , nil
137
142
}
138
143
139
- func (pdi * PagerDutyInterceptor ) Process (ctx context.Context , r * triggersv1.InterceptorRequest ) * triggersv1.InterceptorResponse {
144
+ func (pdi * interceptorHandler ) process (ctx context.Context , r * triggersv1.InterceptorRequest ) * triggersv1.InterceptorResponse {
140
145
pdClient , err := pagerduty .GetPDClient ([]byte (r .Body ))
141
146
if err != nil {
142
147
return interceptors .Failf (codes .InvalidArgument , "could not initialize pagerduty client: %v" , err )
0 commit comments