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/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 print("HOST value is: %s " % (options.proxy))
301 return "404 Not found, no remote HOST specified on the emulator !!!"
303 url = '%s%s' % (HOST, self.path)
304 response = requests.get(url, auth=AUTH, headers=HEADERS, stream=True)
306 if response.status_code == 200:
307 self._write_cache(cached_file_folder, cached_file_header, cached_file_content, response)
309 print('Error when requesting file :')
310 print('Requested url : %s' % (url,))
311 print('Status code : %s' % (response.status_code,))
312 print('Content : %s' % (response.content,))
313 self.send_response(response.status_code)
315 self.wfile.write('404 Not found, nothing found on the remote server !!!')
316 return response.content
318 print("Request for data currently present in cache: %s" % (cached_file_folder,))
319 print("HOST value is: %s " % (HOST))
321 self._send_content(cached_file_header, cached_file_content)
323 if self.path.startswith("/dcae-service-types?asdcResourceId="):
324 print "DCAE case deleting folder created " + cached_file_folder
325 shutil.rmtree(cached_file_folder, ignore_errors=False, onerror=None)
326 elif self.path.startswith("/dcae-service-types"):
327 print "DCAE case deleting folder created " + cached_file_folder
328 shutil.rmtree(cached_file_folder, ignore_errors=False, onerror=None)
330 print "NOT in DCAE case deleting folder created " + cached_file_folder
333 cached_file_folder = ""
334 cached_file_content =""
335 cached_file_header=""
336 print("\n\n\nGot a POST for %s" % self.path)
337 self.check_credentials()
338 self.data_string = self.rfile.read(int(self.headers['Content-Length']))
339 print("data-string:\n %s" % self.data_string)
340 print("self.headers:\n %s" % self.headers)
342 is_special = self._execute_content_generated_cases("POST")
344 cached_file_folder = self._get_cached_file_folder_name(TMP_ROOT)
345 cached_file_content = self._get_cached_content_file_name(cached_file_folder)
346 cached_file_header = self._get_cached_header_file_name(cached_file_folder)
348 cached_file_folder = self._get_cached_file_folder_name(CACHE_ROOT)
349 cached_file_content = self._get_cached_content_file_name(cached_file_folder)
350 cached_file_header = self._get_cached_header_file_name(cached_file_folder)
352 _file_available = os.path.exists(cached_file_content)
354 if not _file_available:
357 self.send_response(404)
359 self.wfile.write('404 Not found, no remote HOST specified on the emulator !!!')
360 return "404 Not found, no remote HOST specified on the emulator !!!"
362 print("Request for data currently not present in cache: %s" % (cached_file_folder,))
364 url = '%s%s' % (HOST, self.path)
365 print("url: %s" % (url,))
366 response = requests.post(url, data=self.data_string, headers=self.headers, stream=True)
368 if response.status_code == 200:
369 self._write_cache(cached_file_folder, cached_file_header, cached_file_content, response)
371 print('Error when requesting file :')
372 print('Requested url : %s' % (url,))
373 print('Status code : %s' % (response.status_code,))
374 print('Content : %s' % (response.content,))
375 self.send_response(response.status_code)
377 self.wfile.write('404 Not found, nothing found on the remote server !!!')
378 return response.content
380 print("Request for data present in cache: %s" % (cached_file_folder,))
382 self._send_content(cached_file_header, cached_file_content)
385 cached_file_folder = ""
386 cached_file_content =""
387 cached_file_header=""
388 print("\n\n\nGot a PUT for %s " % self.path)
389 self.check_credentials()
390 self.data_string = self.rfile.read(int(self.headers['Content-Length']))
391 print("data-string:\n %s" % self.data_string)
392 print("self.headers:\n %s" % self.headers)
394 is_special = self._execute_content_generated_cases("PUT")
396 cached_file_folder = self._get_cached_file_folder_name(TMP_ROOT)
397 cached_file_content = self._get_cached_content_file_name(cached_file_folder)
398 cached_file_header = self._get_cached_header_file_name(cached_file_folder)
400 cached_file_folder = self._get_cached_file_folder_name(CACHE_ROOT)
401 cached_file_content = self._get_cached_content_file_name(cached_file_folder)
402 cached_file_header = self._get_cached_header_file_name(cached_file_folder)
404 _file_available = os.path.exists(cached_file_content)
406 if not _file_available:
408 self.send_response(404)
410 self.wfile.write('404 Not found, no remote HOST specified on the emulator !!!')
411 return "404 Not found, no remote HOST specified on the emulator !!!"
413 print("Request for data currently not present in cache: %s" % (cached_file_folder,))
415 url = '%s%s' % (HOST, self.path)
416 print("url: %s" % (url,))
417 response = requests.put(url, data=self.data_string, headers=self.headers, stream=True)
419 if response.status_code == 200:
420 self._write_cache(cached_file_folder, cached_file_header, cached_file_content, response)
422 print('Error when requesting file :')
423 print('Requested url : %s' % (url,))
424 print('Status code : %s' % (response.status_code,))
425 print('Content : %s' % (response.content,))
426 self.send_response(response.status_code)
428 self.wfile.write('404 Not found, nothing found on the remote server !!!')
429 return response.content
431 print("Request for data present in cache: %s" % (cached_file_folder,))
433 self._send_content(cached_file_header, cached_file_content)
437 cached_file_folder = ""
438 cached_file_content =""
439 cached_file_header=""
440 print("\n\n\nGot a DELETE for %s " % self.path)
441 self.check_credentials()
442 if self.headers.get('Content-Length') is not None:
443 self.data_string = self.rfile.read(int(self.headers['Content-Length']))
445 self.data_string = "empty generated"
446 print("self.headers:\n %s" % self.headers)
448 is_special = self._execute_content_generated_cases("DELETE")
450 cached_file_folder = self._get_cached_file_folder_name(TMP_ROOT)
451 cached_file_content = self._get_cached_content_file_name(cached_file_folder)
452 cached_file_header = self._get_cached_header_file_name(cached_file_folder)
454 cached_file_folder = self._get_cached_file_folder_name(CACHE_ROOT)
455 cached_file_content = self._get_cached_content_file_name(cached_file_folder)
456 cached_file_header = self._get_cached_header_file_name(cached_file_folder)
458 _file_available = os.path.exists(cached_file_content)
460 if not _file_available:
462 self.send_response(404)
464 self.wfile.write('404 Not found, no remote HOST specified on the emulator !!!')
465 return "404 Not found, no remote HOST specified on the emulator !!!"
467 print("Request for data currently not present in cache: %s" % (cached_file_folder,))
469 url = '%s%s' % (HOST, self.path)
470 print("url: %s" % (url,))
471 response = requests.put(url, data=self.data_string, headers=self.headers, stream=True)
473 if response.status_code == 200:
474 self._write_cache(cached_file_folder, cached_file_header, cached_file_content, response)
476 print('Error when requesting file :')
477 print('Requested url : %s' % (url,))
478 print('Status code : %s' % (response.status_code,))
479 print('Content : %s' % (response.content,))
480 self.send_response(response.status_code)
482 self.wfile.write('404 Not found, nothing found on the remote server !!!')
483 return response.content
485 print("Request for data present in cache: %s" % (cached_file_folder,))
487 self._send_content(cached_file_header, cached_file_content)
491 # Main code that start the HTTP server
492 httpd = SocketServer.ForkingTCPServer(('', PORT), Proxy)
493 httpd.allow_reuse_address = True
494 print "Listening on port "+ str(PORT) + "(Press Ctrl+C/Ctrl+Z to stop HTTPD Caching script)"
495 print "Caching folder " + CACHE_ROOT + ", Tmp folder for generated files " + TMP_ROOT
496 signal.signal(signal.SIGINT, signal_handler)
497 httpd.serve_forever()