http-cache.py (5141B)
1 import datetime 2 import json 3 import time 4 from base64 import b64decode 5 6 from wptserve.utils import isomorphic_decode, isomorphic_encode 7 8 NOTEHDRS = set([u'content-type', u'access-control-allow-origin', u'last-modified', u'etag']) 9 NOBODYSTATUS = set([204, 304]) 10 LOCATIONHDRS = set([u'location', u'content-location']) 11 DATEHDRS = set([u'date', u'expires', u'last-modified']) 12 13 def main(request, response): 14 dispatch = request.GET.first(b"dispatch", None) 15 uuid = request.GET.first(b"uuid", None) 16 response.headers.set(b"Access-Control-Allow-Credentials", b"true") 17 18 if request.method == u"OPTIONS": 19 return handle_preflight(uuid, request, response) 20 if not uuid: 21 response.status = (404, b"Not Found") 22 response.headers.set(b"Content-Type", b"text/plain") 23 return b"UUID not found" 24 if dispatch == b'test': 25 return handle_test(uuid, request, response) 26 elif dispatch == b'state': 27 return handle_state(uuid, request, response) 28 response.status = (404, b"Not Found") 29 response.headers.set(b"Content-Type", b"text/plain") 30 return b"Fallthrough" 31 32 def handle_preflight(uuid, request, response): 33 response.status = (200, b"OK") 34 response.headers.set(b"Access-Control-Allow-Origin", request.headers.get(b"origin") or '*') 35 response.headers.set(b"Access-Control-Allow-Methods", b"GET") 36 response.headers.set(b"Access-Control-Allow-Headers", request.headers.get(b"Access-Control-Request-Headers") or "*") 37 response.headers.set(b"Access-Control-Max-Age", b"86400") 38 return b"Preflight request" 39 40 def handle_state(uuid, request, response): 41 response.headers.set(b"Content-Type", b"text/plain") 42 return json.dumps(request.server.stash.take(uuid)) 43 44 def handle_test(uuid, request, response): 45 server_state = request.server.stash.take(uuid) or [] 46 try: 47 requests = json.loads(b64decode(request.headers.get(b'Test-Requests', b""))) 48 except: 49 response.status = (400, b"Bad Request") 50 response.headers.set(b"Content-Type", b"text/plain") 51 return b"No or bad Test-Requests request header" 52 config = requests[len(server_state)] 53 if not config: 54 response.status = (404, b"Not Found") 55 response.headers.set(b"Content-Type", b"text/plain") 56 return b"Config not found" 57 noted_headers = {} 58 now = time.time() 59 for header in config.get(u'response_headers', []): 60 if header[0].lower() in LOCATIONHDRS: # magic locations 61 if (len(header[1]) > 0): 62 header[1] = u"%s&target=%s" % (request.url, header[1]) 63 else: 64 header[1] = request.url 65 if header[0].lower() in DATEHDRS and isinstance(header[1], int): # magic dates 66 header[1] = http_date(now, header[1]) 67 response.headers.set(isomorphic_encode(header[0]), isomorphic_encode(header[1])) 68 if header[0].lower() in NOTEHDRS: 69 noted_headers[header[0].lower()] = header[1] 70 state = { 71 u'now': now, 72 u'request_method': request.method, 73 u'request_headers': dict([[isomorphic_decode(h.lower()), isomorphic_decode(request.headers[h])] for h in request.headers]), 74 u'response_headers': noted_headers 75 } 76 server_state.append(state) 77 request.server.stash.put(uuid, server_state) 78 79 if u"access-control-allow-origin" not in noted_headers: 80 response.headers.set(b"Access-Control-Allow-Origin", b"*") 81 if u"content-type" not in noted_headers: 82 response.headers.set(b"Content-Type", b"text/plain") 83 response.headers.set(b"Server-Request-Count", len(server_state)) 84 85 code, phrase = config.get(u"response_status", [200, b"OK"]) 86 if config.get(u"expected_type", u"").endswith(u'validated'): 87 ref_hdrs = server_state[0][u'response_headers'] 88 previous_lm = ref_hdrs.get(u'last-modified', False) 89 if previous_lm and request.headers.get(b"If-Modified-Since", False) == isomorphic_encode(previous_lm): 90 code, phrase = [304, b"Not Modified"] 91 previous_etag = ref_hdrs.get(u'etag', False) 92 if previous_etag and request.headers.get(b"If-None-Match", False) == isomorphic_encode(previous_etag): 93 code, phrase = [304, b"Not Modified"] 94 if code != 304: 95 code, phrase = [999, b'304 Not Generated'] 96 response.status = (code, phrase) 97 98 content = config.get(u"response_body", uuid) 99 if code in NOBODYSTATUS: 100 return b"" 101 return content 102 103 104 def get_header(headers, header_name): 105 result = None 106 for header in headers: 107 if header[0].lower() == header_name.lower(): 108 result = header[1] 109 return result 110 111 WEEKDAYS = [u'Mon', u'Tue', u'Wed', u'Thu', u'Fri', u'Sat', u'Sun'] 112 MONTHS = [None, u'Jan', u'Feb', u'Mar', u'Apr', u'May', u'Jun', u'Jul', 113 u'Aug', u'Sep', u'Oct', u'Nov', u'Dec'] 114 115 def http_date(now, delta_secs=0): 116 date = datetime.datetime.utcfromtimestamp(now + delta_secs) 117 return u"%s, %.2d %s %.4d %.2d:%.2d:%.2d GMT" % ( 118 WEEKDAYS[date.weekday()], 119 date.day, 120 MONTHS[date.month], 121 date.year, 122 date.hour, 123 date.minute, 124 date.second)