3 # ============LICENSE_START=======================================================
5 # ================================================================================
6 # Copyright (C) 2018 AT&T Intellectual Property. All rights
8 # ================================================================================
9 # Licensed under the Apache License, Version 2.0 (the "License");
10 # you may not use this file except in compliance with the License.
11 # You may obtain a copy of the License at
13 # http://www.apache.org/licenses/LICENSE-2.0
15 # Unless required by applicable law or agreed to in writing, software
16 # distributed under the License is distributed on an "AS IS" BASIS,
17 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 # See the License for the specific language governing permissions and
19 # limitations under the License.
20 # ============LICENSE_END============================================
21 # ===================================================================
30 import SimpleHTTPServer
38 parser = argparse.ArgumentParser(description="3rd party Cache & Replay")
39 parser.add_argument("--username", "-u", type=str, help="Set the username for contacting 3rd party - only used for GET")
40 parser.add_argument("--password", "-p", type=str, help="Set the password for contacting 3rd party - only used for GET")
41 parser.add_argument("--root", "-r", default=tempfile.mkdtemp(), type=str, help="Root folder for the proxy cache")
42 parser.add_argument("--temp", "-t", default=tempfile.mkdtemp(), type=str, help="Temp folder for the generated content")
43 parser.add_argument("--proxy" , type=str, help="Url of the Act as a proxy. If not set, this script only uses the cache and will return a 404 if files aren't found")
44 parser.add_argument("--port", "-P", type=int, default="8081", help="Port on which the proxy should listen to")
45 parser.add_argument("--verbose", "-v", type=bool, help="Print more information in case of error")
46 parser.add_argument("--proxyaddress","-a", type=str, help="Address of this proxy, generally either third-party-proxy:8085 or localhost:8085 depending if started with docker-compose or not")
47 options = parser.parse_args()
52 AUTH = (options.username, options.password)
53 HEADERS = {'X-ECOMP-InstanceID':'CLAMP'}
54 CACHE_ROOT = str(options.root)
55 TMP_ROOT = str(options.temp)
56 PROXY_ADDRESS=str(options.proxyaddress)
58 def signal_handler(signal_sent, frame):
60 if signal_sent == signal.SIGINT:
61 print('Got Ctrl-C (SIGINT)')
66 class Proxy(SimpleHTTPServer.SimpleHTTPRequestHandler):
67 def print_headers(self):
68 for header,value in self.headers.items():
69 print("header: %s : %s" % (header, value))
71 def check_credentials(self):
74 def _send_content(self, header_file, content_file):
75 self.send_response(200)
76 with open(header_file, 'rb') as f:
77 headers = json.load(f)
78 for key,value in headers.items():
79 if key in ('Transfer-Encoding',):
81 self.send_header(key, value)
83 with open(content_file,'rb') as f:
87 def _write_cache(self,cached_file_folder, header_file, content_file, response):
88 os.makedirs(cached_file_folder, 0777)
89 with open(content_file, 'w') as f:
90 f.write(response.raw.read())
91 with open(header_file, 'w') as f:
92 json.dump(dict(response.raw.headers), f)
93 # Entry point of the code
94 def _get_cached_file_folder_name(self,folder):
95 cached_file_folder = '%s/%s' % (folder, self.path,)
96 print("Cached file name before escaping : %s" % cached_file_folder)
97 cached_file_folder = cached_file_folder.replace('<','<').replace('>','>').replace('?','?').replace('*','*').replace('\\','*').replace(':',':').replace('|','|')
98 print("Cached file name after escaping (used for cache storage) : %s" % cached_file_folder)
99 return cached_file_folder
101 def _get_cached_content_file_name(self,cached_file_folder):
102 return "%s/.file" % (cached_file_folder,)
104 def _get_cached_header_file_name(self,cached_file_folder):
105 return "%s/.header" % (cached_file_folder,)
107 def _execute_content_generated_cases(self,http_type):
108 print("Testing special cases, cache files will be sent to :" +TMP_ROOT)
109 cached_file_folder = self._get_cached_file_folder_name(TMP_ROOT)
110 cached_file_content = self._get_cached_content_file_name(cached_file_folder)
111 cached_file_header = self._get_cached_header_file_name(cached_file_folder)
112 _file_available = os.path.exists(cached_file_content)
114 if self.path.startswith("/dcae-service-types?asdcResourceId=") and http_type == "GET":
115 if not _file_available:
116 print "self.path start with /dcae-service-types?asdcResourceId=, generating response json..."
117 uuidGenerated = str(uuid.uuid4())
118 typeId = "typeId-" + uuidGenerated
119 typeName = "typeName-" + uuidGenerated
120 print "typeId generated: " + typeName + " and typeName: "+ typeId
121 jsonGenerated = "{\"totalCount\":1, \"items\":[{\"typeId\":\"" + typeId + "\", \"typeName\":\"" + typeName +"\"}]}"
122 print "jsonGenerated: " + jsonGenerated
124 os.makedirs(cached_file_folder, 0777)
125 with open(cached_file_header, 'w') as f:
126 f.write("{\"Content-Length\": \"" + str(len(jsonGenerated)) + "\", \"Content-Type\": \"application/json\"}")
127 with open(cached_file_content, 'w') as f:
128 f.write(jsonGenerated)
130 elif self.path.startswith("/dcae-operationstatus/install") and http_type == "GET":
131 if not _file_available:
132 print "self.path start with /dcae-operationstatus/install, generating response json..."
133 jsonGenerated = "{\"operationType\": \"install\", \"status\": \"succeeded\"}"
134 print "jsonGenerated: " + jsonGenerated
137 os.makedirs(cached_file_folder, 0777)
139 if e.errno != errno.EEXIST:
141 print(cached_file_folder+" already exists")
143 with open(cached_file_header, 'w') as f:
144 f.write("{\"Content-Length\": \"" + str(len(jsonGenerated)) + "\", \"Content-Type\": \"application/json\"}")
145 with open(cached_file_content, 'w') as f:
146 f.write(jsonGenerated)
148 elif self.path.startswith("/dcae-operationstatus/uninstall") and http_type == "GET":
149 if not _file_available:
150 print "self.path start with /dcae-operationstatus/uninstall, generating response json..."
151 jsonGenerated = "{\"operationType\": \"uninstall\", \"status\": \"succeeded\"}"
152 print "jsonGenerated: " + jsonGenerated
155 os.makedirs(cached_file_folder, 0777)
157 if e.errno != errno.EEXIST:
159 print(cached_file_folder+" already exists")
161 with open(cached_file_header, 'w') as f:
162 f.write("{\"Content-Length\": \"" + str(len(jsonGenerated)) + "\", \"Content-Type\": \"application/json\"}")
163 with open(cached_file_content, 'w') as f:
164 f.write(jsonGenerated)
166 elif self.path.startswith("/sdc/v1/catalog/services/") and http_type == "POST":
167 if not _file_available:
168 print "self.path start with /sdc/v1/catalog/services/, generating response json..."
169 jsondata = json.loads(self.data_string)
170 jsonGenerated = "{\"artifactName\":\"" + jsondata['artifactName'] + "\",\"artifactType\":\"" + jsondata['artifactType'] + "\",\"artifactURL\":\"" + self.path + "\",\"artifactDescription\":\"" + jsondata['description'] + "\",\"artifactChecksum\":\"ZjJlMjVmMWE2M2M1OTM2MDZlODlmNTVmZmYzNjViYzM=\",\"artifactUUID\":\"" + str(uuid.uuid4()) + "\",\"artifactVersion\":\"1\"}"
171 print "jsonGenerated: " + jsonGenerated
173 os.makedirs(cached_file_folder, 0777)
174 with open(cached_file_header, 'w') as f:
175 f.write("{\"Content-Length\": \"" + str(len(jsonGenerated)) + "\", \"Content-Type\": \"application/json\"}")
176 with open(cached_file_content, 'w') as f:
177 f.write(jsonGenerated)
179 elif self.path.startswith("/dcae-deployments/") and http_type == "PUT":
180 print "self.path start with /dcae-deployments/ DEPLOY, generating response json..."
181 #jsondata = json.loads(self.data_string)
182 jsonGenerated = "{\"operationType\":\"install\",\"status\":\"processing\",\"links\":{\"status\":\"http:\/\/" + PROXY_ADDRESS + "\/dcae-operationstatus/install\"}}"
183 print "jsonGenerated: " + jsonGenerated
184 if not os.path.exists(cached_file_folder):
185 os.makedirs(cached_file_folder, 0777)
186 with open(cached_file_header, 'w+') as f:
187 f.write("{\"Content-Length\": \"" + str(len(jsonGenerated)) + "\", \"Content-Type\": \"application/json\"}")
188 with open(cached_file_content, 'w+') as f:
189 f.write(jsonGenerated)
191 elif self.path.startswith("/dcae-deployments/") and http_type == "DELETE":
192 print "self.path start with /dcae-deployments/ UNDEPLOY, generating response json..."
193 #jsondata = json.loads(self.data_string)
194 jsonGenerated = "{\"operationType\":\"uninstall\",\"status\":\"processing\",\"links\":{\"status\":\"http:\/\/" + PROXY_ADDRESS + "\/dcae-operationstatus/uninstall\"}}"
195 print "jsonGenerated: " + jsonGenerated
196 if not os.path.exists(cached_file_folder):
197 os.makedirs(cached_file_folder, 0777)
198 with open(cached_file_header, 'w+') as f:
199 f.write("{\"Content-Length\": \"" + str(len(jsonGenerated)) + "\", \"Content-Type\": \"application/json\"}")
200 with open(cached_file_content, 'w+') as f:
201 f.write(jsonGenerated)
203 elif (self.path.startswith("/pdp/api/") and (http_type == "PUT" or http_type == "DELETE")) or (self.path.startswith("/pdp/api/policyEngineImport") and http_type == "POST"):
204 print "self.path start with /pdp/api/, copying body to response ..."
205 if not os.path.exists(cached_file_folder):
206 os.makedirs(cached_file_folder, 0777)
207 with open(cached_file_header, 'w+') as f:
208 f.write("{\"Content-Length\": \"" + str(len(self.data_string)) + "\", \"Content-Type\": \""+str(self.headers['Content-Type'])+"\"}")
209 with open(cached_file_content, 'w+') as f:
210 f.write(self.data_string)
212 elif self.path.startswith("/policy/api/v1/policytypes/") and http_type == "POST":
213 print "self.path start with POST new policy API /pdp/api/, copying body to response ..."
214 if not os.path.exists(cached_file_folder):
215 os.makedirs(cached_file_folder, 0777)
216 with open(cached_file_header, 'w+') as f:
217 f.write("{\"Content-Length\": \"" + str(len(self.data_string)) + "\", \"Content-Type\": \""+str(self.headers['Content-Type'])+"\"}")
218 with open(cached_file_content, 'w+') as f:
219 f.write(self.data_string)
221 elif self.path.startswith("/policy/api/v1/policytypes/") and http_type == "DELETE":
222 print "self.path start with DELETE new policy API /policy/api/v1/policytypes/ ..."
223 if not os.path.exists(cached_file_folder):
224 os.makedirs(cached_file_folder, 0777)
226 with open(cached_file_header, 'w+') as f:
227 f.write("{\"Content-Length\": \"" + str(len("")) + "\", \"Content-Type\": \""+str("")+"\"}")
228 with open(cached_file_content, 'w+') as f:
229 f.write(self.data_string)
231 elif self.path.startswith("/policy/pap/v1/pdps/policies") and http_type == "POST":
232 print "self.path start with POST new policy API /policy/pap/v1/pdps/ ..."
233 if not os.path.exists(cached_file_folder):
234 os.makedirs(cached_file_folder, 0777)
235 with open(cached_file_header, 'w+') as f:
236 f.write("{\"Content-Length\": \"" + str(len("")) + "\", \"Content-Type\": \""+str("")+"\"}")
237 with open(cached_file_content, 'w+') as f:
238 f.write(self.data_string)
240 elif (self.path.startswith("/policy/api/v1/policytypes/") or self.path.startswith("/policy/pap/v1/policies/deployed/")) and http_type == "GET":
241 print "self.path start with /policy/api/v1/policytypes/, generating response json..."
242 jsonGenerated = "{\"policyTypeId\": \"onap.policies.controlloop.operational\",\"policyTypeVersion\": \"1.0.0\",\"policyId\": \"OPERATIONAL_z711F_v1_0_ResourceInstanceName1_tca\"}"
243 print "jsonGenerated: " + jsonGenerated
244 if not os.path.exists(cached_file_folder):
245 os.makedirs(cached_file_folder, 0777)
247 with open(cached_file_header, 'w') as f:
248 f.write("{\"Content-Length\": \"" + str(len(jsonGenerated)) + "\", \"Content-Type\": \"application/json\"}")
249 with open(cached_file_content, 'w') as f:
250 f.write(jsonGenerated)
252 elif self.path.startswith("/dcae-service-types") and http_type == "GET":
253 if not _file_available:
254 self.path = "/dcae-service-types"
255 cached_file_folder = '%s/%s' % (TMP_ROOT, self.path)
256 cached_file_content = self._get_cached_content_file_name(cached_file_folder)
257 cached_file_header = self._get_cached_header_file_name(cached_file_folder)
258 print "self.path start with /dcae-service-types, generating response json..."
259 response = "{\"links\": {\"previousLink\": {\"title\": \"string\",\"rel\": \"string\",\"uri\": \"string\",\"uriBuilder\": {},\"rels\": [\"string\"],\"params\": {\"additionalProp1\": \"string\",\"additionalProp2\": \"string\",\"additionalProp3\": \"string\"},\"type\": \"string\"},\"nextLink\": {\"title\": \"string\",\"rel\": \"string\",\"uri\": \"string\",\"uriBuilder\": {},\"rels\": [\"string\"],\"params\": {\"additionalProp1\": \"string\",\"additionalProp2\": \"string\",\"additionalProp3\": \"string\"},\"type\": \"string\"}},\"totalCount\": 1,\"items\": [{\"owner\": \"testOwner\",\"application\": \"testApplication\",\"component\": \"testComponent\",\"typeName\": \"testTypeName\",\"typeVersion\": 0,\"blueprintTemplate\": \"testBlueprintTemplate\",\"serviceIds\": [\"serviceId1\", \"serviceId2\"],\"vnfTypes\": [\"vnfType1\", \"vnfType2\"],\"serviceLocations\": [\"serviceLocation1\", \"serviceLocation2\"],\"asdcServiceId\": \"testAsdcServiceId\",\"asdcResourceId\": \"0\",\"asdcServiceURL\": \"testAsdcServiceURL\",\"typeId\": \"testtypeId\",\"selfLink\": {\"title\": \"selfLinkTitle\",\"rel\": \"selfLinkRel\",\"uri\": \"selfLinkUri\",\"uriBuilder\": {},\"rels\": [\"string\"],\"params\": {\"additionalProp1\": \"string\",\"additionalProp2\": \"string\",\"additionalProp3\": \"string\"},\"type\": \"string\"},\"created\": \"2020-01-22T09:38:15.436Z\",\"deactivated\": \"2020-01-22T09:38:15.437Z\"},{\"owner\": \"testOwner2\",\"application\": \"testApplication1\",\"component\": \"testComponent2\",\"typeName\": \"testTypeName2\",\"typeVersion\": 0,\"blueprintTemplate\": \"testBlueprintTemplate2\",\"serviceIds\": [\"serviceId3\", \"serviceId4\"],\"vnfTypes\": [\"vnfType13\", \"vnfType4\"],\"serviceLocations\": [\"serviceLocation3\", \"serviceLocation4\"],\"asdcServiceId\": \"testAsdcServiceId\",\"asdcResourceId\": \"1\",\"asdcServiceURL\": \"testAsdcServiceURL2\",\"typeId\": \"testtypeId2\",\"selfLink\": {\"title\": \"selfLinkTitle\",\"rel\": \"selfLinkRel\",\"uri\": \"selfLinkUri\",\"uriBuilder\": {},\"rels\": [\"string\"],\"params\": {\"additionalProp1\": \"string\",\"additionalProp2\": \"string\",\"additionalProp3\": \"string\"},\"type\": \"string\"},\"created\": \"2020-01-22T09:38:15.436Z\",\"deactivated\": \"2020-01-22T09:38:15.437Z\"}]}"
260 print "jsonGenerated: " + response
262 os.makedirs(cached_file_folder, 0777)
263 with open(cached_file_header, 'w') as f:
264 f.write("{\"Content-Length\": \"" + str(len(response)) + "\", \"Content-Type\": \"application/json\"}")
265 with open(cached_file_content, 'w') as f:
273 cached_file_folder = ""
274 cached_file_content =""
275 cached_file_header=""
276 print("\n\n\nGot a GET request for %s " % self.path)
279 self.check_credentials()
280 # Verify if it's a special case
281 is_special = self._execute_content_generated_cases("GET")
283 cached_file_folder = self._get_cached_file_folder_name(TMP_ROOT)
284 cached_file_content = self._get_cached_content_file_name(cached_file_folder)
285 cached_file_header = self._get_cached_header_file_name(cached_file_folder)
287 cached_file_folder = self._get_cached_file_folder_name(CACHE_ROOT)
288 cached_file_content = self._get_cached_content_file_name(cached_file_folder)
289 cached_file_header = self._get_cached_header_file_name(cached_file_folder)
291 _file_available = os.path.exists(cached_file_content)
293 if not _file_available:
294 print("Request for data currently not present in cache: %s" % (cached_file_folder,))
297 self.send_response(404)
299 self.wfile.write('404 Not found, no remote HOST specified on the emulator !!!')
300 return "404 Not found, no remote HOST specified on the emulator !!!"
302 url = '%s%s' % (HOST, self.path)
303 response = requests.get(url, auth=AUTH, headers=HEADERS, stream=True)
305 if response.status_code == 200:
306 self._write_cache(cached_file_folder, cached_file_header, cached_file_content, response)
308 print('Error when requesting file :')
309 print('Requested url : %s' % (url,))
310 print('Status code : %s' % (response.status_code,))
311 print('Content : %s' % (response.content,))
312 self.send_response(response.status_code)
314 self.wfile.write('404 Not found, nothing found on the remote server !!!')
315 return response.content
317 print("Request for data currently present in cache: %s" % (cached_file_folder,))
319 self._send_content(cached_file_header, cached_file_content)
321 if self.path.startswith("/dcae-service-types?asdcResourceId="):
322 print "DCAE case deleting folder created " + cached_file_folder
323 shutil.rmtree(cached_file_folder, ignore_errors=False, onerror=None)
324 elif self.path.startswith("/dcae-service-types"):
325 print "DCAE case deleting folder created " + cached_file_folder
326 shutil.rmtree(cached_file_folder, ignore_errors=False, onerror=None)
328 print "NOT in DCAE case deleting folder created " + cached_file_folder
331 cached_file_folder = ""
332 cached_file_content =""
333 cached_file_header=""
334 print("\n\n\nGot a POST for %s" % self.path)
335 self.check_credentials()
336 self.data_string = self.rfile.read(int(self.headers['Content-Length']))
337 print("data-string:\n %s" % self.data_string)
338 print("self.headers:\n %s" % self.headers)
340 is_special = self._execute_content_generated_cases("POST")
342 cached_file_folder = self._get_cached_file_folder_name(TMP_ROOT)
343 cached_file_content = self._get_cached_content_file_name(cached_file_folder)
344 cached_file_header = self._get_cached_header_file_name(cached_file_folder)
346 cached_file_folder = self._get_cached_file_folder_name(CACHE_ROOT)
347 cached_file_content = self._get_cached_content_file_name(cached_file_folder)
348 cached_file_header = self._get_cached_header_file_name(cached_file_folder)
350 _file_available = os.path.exists(cached_file_content)
352 if not _file_available:
355 self.send_response(404)
357 self.wfile.write('404 Not found, no remote HOST specified on the emulator !!!')
358 return "404 Not found, no remote HOST specified on the emulator !!!"
360 print("Request for data currently not present in cache: %s" % (cached_file_folder,))
362 url = '%s%s' % (HOST, self.path)
363 print("url: %s" % (url,))
364 response = requests.post(url, data=self.data_string, headers=self.headers, stream=True)
366 if response.status_code == 200:
367 self._write_cache(cached_file_folder, cached_file_header, cached_file_content, response)
369 print('Error when requesting file :')
370 print('Requested url : %s' % (url,))
371 print('Status code : %s' % (response.status_code,))
372 print('Content : %s' % (response.content,))
373 self.send_response(response.status_code)
375 self.wfile.write('404 Not found, nothing found on the remote server !!!')
376 return response.content
378 print("Request for data present in cache: %s" % (cached_file_folder,))
380 self._send_content(cached_file_header, cached_file_content)
383 cached_file_folder = ""
384 cached_file_content =""
385 cached_file_header=""
386 print("\n\n\nGot a PUT for %s " % self.path)
387 self.check_credentials()
388 self.data_string = self.rfile.read(int(self.headers['Content-Length']))
389 print("data-string:\n %s" % self.data_string)
390 print("self.headers:\n %s" % self.headers)
392 is_special = self._execute_content_generated_cases("PUT")
394 cached_file_folder = self._get_cached_file_folder_name(TMP_ROOT)
395 cached_file_content = self._get_cached_content_file_name(cached_file_folder)
396 cached_file_header = self._get_cached_header_file_name(cached_file_folder)
398 cached_file_folder = self._get_cached_file_folder_name(CACHE_ROOT)
399 cached_file_content = self._get_cached_content_file_name(cached_file_folder)
400 cached_file_header = self._get_cached_header_file_name(cached_file_folder)
402 _file_available = os.path.exists(cached_file_content)
404 if not _file_available:
406 self.send_response(404)
408 self.wfile.write('404 Not found, no remote HOST specified on the emulator !!!')
409 return "404 Not found, no remote HOST specified on the emulator !!!"
411 print("Request for data currently not present in cache: %s" % (cached_file_folder,))
413 url = '%s%s' % (HOST, self.path)
414 print("url: %s" % (url,))
415 response = requests.put(url, data=self.data_string, headers=self.headers, stream=True)
417 if response.status_code == 200:
418 self._write_cache(cached_file_folder, cached_file_header, cached_file_content, response)
420 print('Error when requesting file :')
421 print('Requested url : %s' % (url,))
422 print('Status code : %s' % (response.status_code,))
423 print('Content : %s' % (response.content,))
424 self.send_response(response.status_code)
426 self.wfile.write('404 Not found, nothing found on the remote server !!!')
427 return response.content
429 print("Request for data present in cache: %s" % (cached_file_folder,))
431 self._send_content(cached_file_header, cached_file_content)
435 cached_file_folder = ""
436 cached_file_content =""
437 cached_file_header=""
438 print("\n\n\nGot a DELETE for %s " % self.path)
439 self.check_credentials()
440 if self.headers.get('Content-Length') is not None:
441 self.data_string = self.rfile.read(int(self.headers['Content-Length']))
443 self.data_string = "empty generated"
444 print("self.headers:\n %s" % self.headers)
446 is_special = self._execute_content_generated_cases("DELETE")
448 cached_file_folder = self._get_cached_file_folder_name(TMP_ROOT)
449 cached_file_content = self._get_cached_content_file_name(cached_file_folder)
450 cached_file_header = self._get_cached_header_file_name(cached_file_folder)
452 cached_file_folder = self._get_cached_file_folder_name(CACHE_ROOT)
453 cached_file_content = self._get_cached_content_file_name(cached_file_folder)
454 cached_file_header = self._get_cached_header_file_name(cached_file_folder)
456 _file_available = os.path.exists(cached_file_content)
458 if not _file_available:
460 self.send_response(404)
462 self.wfile.write('404 Not found, no remote HOST specified on the emulator !!!')
463 return "404 Not found, no remote HOST specified on the emulator !!!"
465 print("Request for data currently not present in cache: %s" % (cached_file_folder,))
467 url = '%s%s' % (HOST, self.path)
468 print("url: %s" % (url,))
469 response = requests.put(url, data=self.data_string, headers=self.headers, stream=True)
471 if response.status_code == 200:
472 self._write_cache(cached_file_folder, cached_file_header, cached_file_content, response)
474 print('Error when requesting file :')
475 print('Requested url : %s' % (url,))
476 print('Status code : %s' % (response.status_code,))
477 print('Content : %s' % (response.content,))
478 self.send_response(response.status_code)
480 self.wfile.write('404 Not found, nothing found on the remote server !!!')
481 return response.content
483 print("Request for data present in cache: %s" % (cached_file_folder,))
485 self._send_content(cached_file_header, cached_file_content)
489 # Main code that start the HTTP server
490 httpd = SocketServer.ForkingTCPServer(('', PORT), Proxy)
491 httpd.allow_reuse_address = True
492 print "Listening on port "+ str(PORT) + "(Press Ctrl+C/Ctrl+Z to stop HTTPD Caching script)"
493 print "Caching folder " + CACHE_ROOT + ", Tmp folder for generated files " + TMP_ROOT
494 signal.signal(signal.SIGINT, signal_handler)
495 httpd.serve_forever()