cookie_helper.py (3266B)
1 # -*- coding: utf-8 -*- 2 3 # Active wptserve handler for cookie operations. 4 # 5 # This must support the following requests: 6 # 7 # - GET with the following query parameters: 8 # - charset: (optional) character set for response (default: utf-8) 9 # A cookie: request header (if present) is echoed in the body with a 10 # cookie= prefix followed by the urlencoded bytes from the header. 11 # Used to inspect the cookie jar from an HTTP request header context. 12 # - POST with form-data in the body and the following query-or-form parameters: 13 # - set-cookie: (optional; repeated) echoed in the set-cookie: response 14 # header and also echoed in the body with a set-cookie= prefix 15 # followed by the urlencoded bytes from the parameter; multiple occurrences 16 # are CRLF-delimited. 17 # Used to set cookies from an HTTP response header context. 18 # 19 # The response has 200 status and content-type: text/plain; charset=<charset> 20 import encodings, re 21 22 from urllib.parse import parse_qs, quote 23 24 from wptserve.utils import isomorphic_decode, isomorphic_encode 25 26 # NOTE: These are intentionally very lax to permit testing 27 DISALLOWED_IN_COOKIE_NAME_RE = re.compile(br'[;\0-\x1f\x7f]') 28 DISALLOWED_IN_HEADER_RE = re.compile(br'[\0-\x1f\x7f]') 29 30 # Ensure common charset names do not end up with different 31 # capitalization or punctuation 32 CHARSET_OVERRIDES = { 33 encodings.codecs.lookup(charset).name: charset 34 for charset in (u'utf-8', u'iso-8859-1',) 35 } 36 37 def quote_str(cookie_str): 38 return isomorphic_encode(quote(isomorphic_decode(cookie_str), u'', encoding=u'iso-8859-1')) 39 40 def parse_qs_str(query_str): 41 args = parse_qs(isomorphic_decode(query_str), keep_blank_values=True, encoding=u'iso-8859-1') 42 binary_args = {} 43 for key, val in args.items(): 44 binary_args[isomorphic_encode(key)] = [isomorphic_encode(x) for x in val] 45 return binary_args 46 47 def main(request, response): 48 assert request.method in ( 49 u'GET', 50 u'POST', 51 ), u'request method was neither GET nor POST: %r' % request.method 52 qd = (isomorphic_encode(request.url).split(b'#')[0].split(b'?', 1) + [b''])[1] 53 if request.method == u'POST': 54 qd += b'&' + request.body 55 args = parse_qs_str(qd) 56 57 charset = encodings.codecs.lookup([isomorphic_decode(x) for x in args.get(b'charset', [u'utf-8'])][-1]).name 58 charset = CHARSET_OVERRIDES.get(charset, charset) 59 headers = [(b'content-type', b'text/plain; charset=' + isomorphic_encode(charset))] 60 body = [] 61 if request.method == u'POST': 62 for set_cookie in args.get(b'set-cookie', []): 63 if b'=' in set_cookie.split(b';', 1)[0]: 64 name, rest = set_cookie.split(b'=', 1) 65 assert re.search( 66 DISALLOWED_IN_COOKIE_NAME_RE, 67 name 68 ) is None, b'name had disallowed characters: %r' % name 69 else: 70 rest = set_cookie 71 assert re.search( 72 DISALLOWED_IN_HEADER_RE, 73 rest 74 ) is None, b'rest had disallowed characters: %r' % rest 75 headers.append((b'set-cookie', set_cookie)) 76 body.append(b'set-cookie=' + quote_str(set_cookie)) 77 78 else: 79 cookie = request.headers.get(b'cookie') 80 if cookie is not None: 81 body.append(b'cookie=' + quote_str(cookie)) 82 body = b'\r\n'.join(body) 83 headers.append((b'content-length', len(body))) 84 return 200, headers, body