2016-06-15 07:45:05 -05:00
package notifications
import (
2016-06-16 01:15:48 -05:00
"bytes"
2016-10-03 02:38:03 -05:00
"context"
2019-01-11 01:56:50 -06:00
"crypto/tls"
2016-07-27 05:09:55 -05:00
"fmt"
2022-08-10 08:37:51 -05:00
"io"
2017-02-24 10:22:12 -06:00
"net"
2016-06-15 07:45:05 -05:00
"net/http"
2017-02-24 10:22:12 -06:00
"time"
2016-06-15 07:45:05 -05:00
2016-06-16 01:15:48 -05:00
"github.com/grafana/grafana/pkg/util"
2016-06-15 07:45:05 -05:00
)
type Webhook struct {
2017-03-23 15:53:54 -05:00
Url string
User string
Password string
Body string
HttpMethod string
HttpHeader map [ string ] string
ContentType string
2022-07-14 12:15:18 -05:00
// Validation is a function that will validate the response body and statusCode of the webhook. Any returned error will cause the webhook request to be considered failed.
// This can be useful when a webhook service communicates failures in creative ways, such as using the response body instead of the status code.
Validation func ( body [ ] byte , statusCode int ) error
}
// WebhookClient exists to mock the client in tests.
type WebhookClient interface {
Do ( req * http . Request ) ( * http . Response , error )
2016-06-15 07:45:05 -05:00
}
2017-02-24 10:22:12 -06:00
var netTransport = & http . Transport {
2019-01-11 01:56:50 -06:00
TLSClientConfig : & tls . Config {
Renegotiation : tls . RenegotiateFreelyAsClient ,
} ,
2017-03-02 06:57:28 -06:00
Proxy : http . ProxyFromEnvironment ,
2017-02-24 10:22:12 -06:00
Dial : ( & net . Dialer {
2019-05-08 03:37:48 -05:00
Timeout : 30 * time . Second ,
2017-02-24 10:22:12 -06:00
} ) . Dial ,
TLSHandshakeTimeout : 5 * time . Second ,
}
2022-07-14 12:15:18 -05:00
var netClient WebhookClient = & http . Client {
2017-02-24 10:22:12 -06:00
Timeout : time . Second * 30 ,
Transport : netTransport ,
}
2018-04-27 06:01:32 -05:00
func ( ns * NotificationService ) sendWebRequestSync ( ctx context . Context , webhook * Webhook ) error {
2016-10-18 09:18:16 -05:00
if webhook . HttpMethod == "" {
webhook . HttpMethod = http . MethodPost
}
2022-03-31 13:57:48 -05:00
ns . log . Debug ( "Sending webhook" , "url" , webhook . Url , "http method" , webhook . HttpMethod )
2020-11-24 03:42:54 -06:00
if webhook . HttpMethod != http . MethodPost && webhook . HttpMethod != http . MethodPut {
return fmt . Errorf ( "webhook only supports HTTP methods PUT or POST" )
}
2022-04-11 07:46:21 -05:00
request , err := http . NewRequestWithContext ( ctx , webhook . HttpMethod , webhook . Url , bytes . NewReader ( [ ] byte ( webhook . Body ) ) )
2016-06-15 07:45:05 -05:00
if err != nil {
return err
}
2017-03-23 15:53:54 -05:00
if webhook . ContentType == "" {
webhook . ContentType = "application/json"
}
2020-12-14 08:13:01 -06:00
request . Header . Set ( "Content-Type" , webhook . ContentType )
request . Header . Set ( "User-Agent" , "Grafana" )
2018-05-07 08:40:43 -05:00
2016-12-05 03:44:31 -06:00
if webhook . User != "" && webhook . Password != "" {
2020-12-14 08:13:01 -06:00
request . Header . Set ( "Authorization" , util . GetBasicAuthHeader ( webhook . User , webhook . Password ) )
2016-12-05 03:44:31 -06:00
}
2017-01-19 02:29:40 -06:00
for k , v := range webhook . HttpHeader {
request . Header . Set ( k , v )
2017-01-19 01:25:21 -06:00
}
2022-04-11 07:46:21 -05:00
resp , err := netClient . Do ( request )
2016-06-15 07:45:05 -05:00
if err != nil {
return err
}
2020-12-15 02:32:06 -06:00
defer func ( ) {
if err := resp . Body . Close ( ) ; err != nil {
ns . log . Warn ( "Failed to close response body" , "err" , err )
}
} ( )
2018-12-27 06:16:58 -06:00
2022-08-10 08:37:51 -05:00
body , err := io . ReadAll ( resp . Body )
2016-10-03 02:38:03 -05:00
if err != nil {
return err
2016-07-27 05:09:55 -05:00
}
2016-10-03 02:38:03 -05:00
2022-07-14 12:15:18 -05:00
if webhook . Validation != nil {
err := webhook . Validation ( body , resp . StatusCode )
if err != nil {
ns . log . Debug ( "Webhook failed validation" , "url" , webhook . Url , "statuscode" , resp . Status , "body" , string ( body ) )
return fmt . Errorf ( "webhook failed validation: %w" , err )
}
}
if resp . StatusCode / 100 == 2 {
ns . log . Debug ( "Webhook succeeded" , "url" , webhook . Url , "statuscode" , resp . Status )
return nil
}
2020-06-26 13:47:06 -05:00
ns . log . Debug ( "Webhook failed" , "url" , webhook . Url , "statuscode" , resp . Status , "body" , string ( body ) )
2022-07-14 12:15:18 -05:00
return fmt . Errorf ( "webhook response status %v" , resp . Status )
2016-06-15 07:45:05 -05:00
}