tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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)