55from time import perf_counter as pc
66from urllib .parse import urlparse
77
8+ import re
9+
810
911class Config :
1012 """Lambda function runtime configuration"""
@@ -18,6 +20,7 @@ class Config:
1820 REPORT_AS_CW_METRICS = 'REPORT_AS_CW_METRICS'
1921 CW_METRICS_NAMESPACE = 'CW_METRICS_NAMESPACE'
2022 CW_METRICS_METRIC_NAME = 'CW_METRICS_METRIC_NAME'
23+ BODY_REGEX_MATCH = 'BODY_REGEX_MATCH'
2124
2225 def __init__ (self , event ):
2326 self .event = event
@@ -29,7 +32,8 @@ def __init__(self, event):
2932 self .REPORT_RESPONSE_BODY : '0' ,
3033 self .REPORT_AS_CW_METRICS : '1' ,
3134 self .CW_METRICS_NAMESPACE : 'HttpCheck' ,
32- self .HEADERS : ''
35+ self .HEADERS : '' ,
36+ self .BODY_REGEX_MATCH : None
3337 }
3438
3539 def __get_property (self , property_name ):
@@ -57,7 +61,7 @@ def payload(self):
5761 return payload
5862
5963 @property
60- def timeoutms (self ):
64+ def timeout (self ):
6165 return self .__get_property (self .TIMEOUT )
6266
6367 @property
@@ -75,42 +79,47 @@ def headers(self):
7579 except :
7680 print (f"Could not decode headers: { headers } " )
7781
82+ @property
83+ def bodyregexmatch (self ):
84+ return self .__get_property (self .BODY_REGEX_MATCH )
85+
7886 @property
7987 def cwoptions (self ):
8088 return {
8189 '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 ),
8391 }
8492
8593
8694class HttpCheck :
8795 """Execution of HTTP(s) request"""
8896
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
95104
96105 def execute (self ):
97106 url = urlparse (self .endpoint )
98107 location = url .netloc
99108 if url .scheme == 'http' :
100109 request = http .client .HTTPConnection (location , timeout = int (self .timeout ))
101-
110+
102111 if url .scheme == 'https' :
103112 request = http .client .HTTPSConnection (location , timeout = int (self .timeout ))
104-
113+
105114 if 'HTTP_DEBUG' in os .environ and os .environ ['HTTP_DEBUG' ] == '1' :
106115 request .set_debuglevel (1 )
107-
116+
108117 path = url .path
109118 if path == '' :
110119 path = '/'
111120 if url .query is not None :
112121 path = path + "?" + url .query
113-
122+
114123 try :
115124 t0 = pc ()
116125
@@ -122,14 +131,22 @@ def execute(self):
122131 # stop the stopwatch
123132 t1 = pc ()
124133
125- # return structure with data
126- return {
134+ response_body = str ( response_data . read (). decode ())
135+ result = {
127136 'Reason' : response_data .reason ,
128- 'ResponseBody' : str ( response_data . read (). decode ()) ,
137+ 'ResponseBody' : response_body ,
129138 'StatusCode' : response_data .status ,
130139 'TimeTaken' : int ((t1 - t0 ) * 1000 ),
131140 'Available' : '1'
132141 }
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
133150 except Exception as e :
134151 print (f"Failed to connect to { self .endpoint } \n { e } " )
135152 return {'Available' : 0 , 'Reason' : str (e )}
@@ -171,6 +188,16 @@ def report(self, result):
171188 'Unit' : 'None' ,
172189 'Value' : int (result ['StatusCode' ])
173190 })
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+
174201 result = cloudwatch .put_metric_data (
175202 MetricData = metric_data ,
176203 Namespace = self .options ['namespace' ]
@@ -184,23 +211,17 @@ def http_check(event, context):
184211 """Lambda function handler"""
185212
186213 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 )
194215
195216 result = http_check .execute ()
196217
218+ # report results
219+ ResultReporter (config , result ).report (result )
220+
197221 # Remove body if not required
198222 if (config .reportbody != '1' ) and ('ResponseBody' in result ):
199223 del result ['ResponseBody' ]
200224
201- # report results
202- ResultReporter (config , result ).report (result )
203-
204225 result_json = json .dumps (result , indent = 4 )
205226 # log results
206227 print (f"Result of checking { config .method } { config .endpoint } \n { result_json } " )
0 commit comments