1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package http
17
18 import (
19 "bytes"
20 "fmt"
21 "io"
22 "net/http"
23 "strconv"
24 "strings"
25 "time"
26
27 "github.com/go-logr/logr"
28 "github.com/pkg/errors"
29
30 "github.com/chaos-mesh/chaos-mesh/api/v1alpha1"
31 )
32
33 type httpExecutor struct {
34 logger logr.Logger
35
36 timeoutSeconds int
37 httpStatusCheck v1alpha1.HTTPStatusCheck
38 }
39
40 func NewExecutor(logger logr.Logger, timeoutSeconds int, httpStatusCheck v1alpha1.HTTPStatusCheck) *httpExecutor {
41 return &httpExecutor{logger: logger, timeoutSeconds: timeoutSeconds, httpStatusCheck: httpStatusCheck}
42 }
43
44 type response struct {
45 statusCode int
46 body string
47 }
48
49 func (e *httpExecutor) Type() string {
50 return "HTTP"
51 }
52
53 func (e *httpExecutor) Do() (bool, string, error) {
54 client := &http.Client{
55 Timeout: time.Duration(e.timeoutSeconds) * time.Second,
56 }
57
58 httpStatusCheck := e.httpStatusCheck
59 return e.DoHTTPRequest(client,
60 httpStatusCheck.RequestUrl,
61 string(httpStatusCheck.RequestMethod),
62 httpStatusCheck.RequestHeaders,
63 []byte(httpStatusCheck.RequestBody),
64 httpStatusCheck.Criteria)
65 }
66
67 func (e *httpExecutor) DoHTTPRequest(client *http.Client, url, method string,
68 headers http.Header, body []byte, criteria v1alpha1.HTTPCriteria) (bool, string, error) {
69 req, err := http.NewRequest(method, url, bytes.NewReader(body))
70 if err != nil {
71 return false, errors.Wrap(err, "new http request").Error(), nil
72 }
73 req.Header = headers
74 resp, err := client.Do(req)
75 if err != nil {
76 return false, errors.Wrap(err, "do http request").Error(), nil
77 }
78 defer resp.Body.Close()
79
80 responseBody, err := io.ReadAll(resp.Body)
81 if err != nil {
82 return false, "", errors.Wrap(err, "read response body")
83 }
84
85 return validate(e.logger.WithValues("url", url),
86 criteria, response{statusCode: resp.StatusCode, body: string(responseBody)})
87 }
88
89 func validate(logger logr.Logger, criteria v1alpha1.HTTPCriteria, resp response) (bool, string, error) {
90 ok := validateStatusCode(criteria.StatusCode, resp)
91 if !ok {
92 logger.Info("validate status code failed",
93 "criteria", criteria.StatusCode,
94 "statusCode", resp.statusCode)
95 return false, fmt.Sprintf("unexpected status code: %d", resp.statusCode), nil
96 }
97 return ok, "", nil
98 }
99
100
101
102
103
104 func validateStatusCode(criteria string, resp response) bool {
105 if code, err := strconv.Atoi(criteria); err == nil {
106 return code == resp.statusCode
107 }
108 index := strings.Index(criteria, "-")
109 if index == -1 {
110 return false
111 }
112 start := criteria[:index]
113 end := criteria[index+1:]
114 startStatusCode, err := strconv.Atoi(start)
115 if err != nil {
116 return false
117 }
118 endStatusCode, err := strconv.Atoi(end)
119 if err != nil {
120 return false
121 }
122 return resp.statusCode >= startStatusCode &&
123 resp.statusCode <= endStatusCode
124 }
125