5
5
from time import perf_counter as pc
6
6
from urllib .parse import urlparse
7
7
8
+ import re
9
+
8
10
9
11
class Config :
10
12
"""Lambda function runtime configuration"""
@@ -18,6 +20,7 @@ class Config:
18
20
REPORT_AS_CW_METRICS = 'REPORT_AS_CW_METRICS'
19
21
CW_METRICS_NAMESPACE = 'CW_METRICS_NAMESPACE'
20
22
CW_METRICS_METRIC_NAME = 'CW_METRICS_METRIC_NAME'
23
+ BODY_REGEX_MATCH = 'BODY_REGEX_MATCH'
21
24
22
25
def __init__ (self , event ):
23
26
self .event = event
@@ -29,7 +32,8 @@ def __init__(self, event):
29
32
self .REPORT_RESPONSE_BODY : '0' ,
30
33
self .REPORT_AS_CW_METRICS : '1' ,
31
34
self .CW_METRICS_NAMESPACE : 'HttpCheck' ,
32
- self .HEADERS : ''
35
+ self .HEADERS : '' ,
36
+ self .BODY_REGEX_MATCH : None
33
37
}
34
38
35
39
def __get_property (self , property_name ):
@@ -57,7 +61,7 @@ def payload(self):
57
61
return payload
58
62
59
63
@property
60
- def timeoutms (self ):
64
+ def timeout (self ):
61
65
return self .__get_property (self .TIMEOUT )
62
66
63
67
@property
@@ -75,42 +79,47 @@ def headers(self):
75
79
except :
76
80
print (f"Could not decode headers: { headers } " )
77
81
82
+ @property
83
+ def bodyregexmatch (self ):
84
+ return self .__get_property (self .BODY_REGEX_MATCH )
85
+
78
86
@property
79
87
def cwoptions (self ):
80
88
return {
81
89
'enabled' : self .__get_property (self .REPORT_AS_CW_METRICS ),
82
- 'namespace' : self .__get_property (self .CW_METRICS_NAMESPACE )
90
+ 'namespace' : self .__get_property (self .CW_METRICS_NAMESPACE ),
83
91
}
84
92
85
93
86
94
class HttpCheck :
87
95
"""Execution of HTTP(s) request"""
88
96
89
- def __init__ (self , endpoint , timeout = 120000 , method = 'GET' , payload = None , headers = {}):
90
- self .method = method
91
- self .endpoint = endpoint
92
- self .timeout = timeout
93
- self .payload = payload
94
- self .headers = headers
97
+ def __init__ (self , config ):
98
+ self .method = config .method
99
+ self .endpoint = config .endpoint
100
+ self .timeout = config .timeout
101
+ self .payload = config .payload
102
+ self .headers = config .headers
103
+ self .bodyregexmatch = config .bodyregexmatch
95
104
96
105
def execute (self ):
97
106
url = urlparse (self .endpoint )
98
107
location = url .netloc
99
108
if url .scheme == 'http' :
100
109
request = http .client .HTTPConnection (location , timeout = int (self .timeout ))
101
-
110
+
102
111
if url .scheme == 'https' :
103
112
request = http .client .HTTPSConnection (location , timeout = int (self .timeout ))
104
-
113
+
105
114
if 'HTTP_DEBUG' in os .environ and os .environ ['HTTP_DEBUG' ] == '1' :
106
115
request .set_debuglevel (1 )
107
-
116
+
108
117
path = url .path
109
118
if path == '' :
110
119
path = '/'
111
120
if url .query is not None :
112
121
path = path + "?" + url .query
113
-
122
+
114
123
try :
115
124
t0 = pc ()
116
125
@@ -122,14 +131,22 @@ def execute(self):
122
131
# stop the stopwatch
123
132
t1 = pc ()
124
133
125
- # return structure with data
126
- return {
134
+ response_body = str ( response_data . read (). decode ())
135
+ result = {
127
136
'Reason' : response_data .reason ,
128
- 'ResponseBody' : str ( response_data . read (). decode ()) ,
137
+ 'ResponseBody' : response_body ,
129
138
'StatusCode' : response_data .status ,
130
139
'TimeTaken' : int ((t1 - t0 ) * 1000 ),
131
140
'Available' : '1'
132
141
}
142
+
143
+ if self .bodyregexmatch is not None :
144
+ regex = re .compile (self .bodyregexmatch )
145
+ value = 1 if regex .match (response_body ) else 0
146
+ result ['ResponseBodyRegexMatch' ] = value
147
+
148
+ # return structure with data
149
+ return result
133
150
except Exception as e :
134
151
print (f"Failed to connect to { self .endpoint } \n { e } " )
135
152
return {'Available' : 0 , 'Reason' : str (e )}
@@ -171,6 +188,16 @@ def report(self, result):
171
188
'Unit' : 'None' ,
172
189
'Value' : int (result ['StatusCode' ])
173
190
})
191
+ if 'ResponseBodyRegexMatch' in result :
192
+ metric_data .append ({
193
+ 'MetricName' : 'ResponseBodyRegexMatch' ,
194
+ 'Dimensions' : [
195
+ {'Name' : 'Endpoint' , 'Value' : self .endpoint }
196
+ ],
197
+ 'Unit' : 'None' ,
198
+ 'Value' : int (result ['ResponseBodyRegexMatch' ])
199
+ })
200
+
174
201
result = cloudwatch .put_metric_data (
175
202
MetricData = metric_data ,
176
203
Namespace = self .options ['namespace' ]
@@ -184,23 +211,17 @@ def http_check(event, context):
184
211
"""Lambda function handler"""
185
212
186
213
config = Config (event )
187
- http_check = HttpCheck (
188
- config .endpoint ,
189
- config .timeoutms ,
190
- config .method ,
191
- config .payload ,
192
- config .headers
193
- )
214
+ http_check = HttpCheck (config )
194
215
195
216
result = http_check .execute ()
196
217
218
+ # report results
219
+ ResultReporter (config , result ).report (result )
220
+
197
221
# Remove body if not required
198
222
if (config .reportbody != '1' ) and ('ResponseBody' in result ):
199
223
del result ['ResponseBody' ]
200
224
201
- # report results
202
- ResultReporter (config , result ).report (result )
203
-
204
225
result_json = json .dumps (result , indent = 4 )
205
226
# log results
206
227
print (f"Result of checking { config .method } { config .endpoint } \n { result_json } " )
0 commit comments