Using the new RequestArguments API throughout the project.

This commit is contained in:
s3lph 2018-06-29 22:13:39 +02:00
parent 2f927cec41
commit 0fb60d1828
11 changed files with 73 additions and 75 deletions

View file

@ -13,7 +13,7 @@ from uuid import uuid4
from datetime import datetime, timedelta from datetime import datetime, timedelta
from matemat import __version__ as matemat_version from matemat import __version__ as matemat_version
from matemat.webserver import RequestArgument from matemat.webserver import RequestArguments
from matemat.webserver.util import parse_args from matemat.webserver.util import parse_args
@ -31,7 +31,7 @@ BaseHTTPRequestHandler.log_error = lambda self, fstring='', *args: None
# Dictionary to hold registered pagelet paths and their handler functions # Dictionary to hold registered pagelet paths and their handler functions
_PAGELET_PATHS: Dict[str, Callable[[str, # HTTP method (GET, POST, ...) _PAGELET_PATHS: Dict[str, Callable[[str, # HTTP method (GET, POST, ...)
str, # Request path str, # Request path
Dict[str, RequestArgument], # args: (name, argument) RequestArguments, # HTTP Request arguments
Dict[str, Any], # Session vars Dict[str, Any], # Session vars
Dict[str, str]], # Response headers Dict[str, str]], # Response headers
Tuple[int, Union[bytes, str]]]] = dict() # Returns: (status code, response body) Tuple[int, Union[bytes, str]]]] = dict() # Returns: (status code, response body)
@ -51,7 +51,7 @@ def pagelet(path: str):
(method: str, (method: str,
path: str, path: str,
args: Dict[str, RequestArgument], args: RequestArguments,
session_vars: Dict[str, Any], session_vars: Dict[str, Any],
headers: Dict[str, str]) headers: Dict[str, str])
-> (int, Optional[Union[str, bytes]]) -> (int, Optional[Union[str, bytes]])
@ -69,7 +69,7 @@ def pagelet(path: str):
def http_handler(fun: Callable[[str, def http_handler(fun: Callable[[str,
str, str,
Dict[str, RequestArgument], RequestArguments,
Dict[str, Any], Dict[str, Any],
Dict[str, str]], Dict[str, str]],
Tuple[int, Union[bytes, str]]]): Tuple[int, Union[bytes, str]]]):
@ -181,7 +181,7 @@ class HttpHandler(BaseHTTPRequestHandler):
if session_id in self.server.session_vars: if session_id in self.server.session_vars:
del self.server.session_vars[session_id] del self.server.session_vars[session_id]
def _handle(self, method: str, path: str, args: Dict[str, RequestArgument]) -> None: def _handle(self, method: str, path: str, args: RequestArguments) -> None:
""" """
Handle a HTTP request by either dispatching it to the appropriate pagelet or by serving a static resource. Handle a HTTP request by either dispatching it to the appropriate pagelet or by serving a static resource.

View file

@ -2,7 +2,7 @@
from typing import Any, Dict, Optional, Tuple, Union from typing import Any, Dict, Optional, Tuple, Union
from matemat.exceptions import AuthenticationError from matemat.exceptions import AuthenticationError
from matemat.webserver import pagelet, RequestArgument from matemat.webserver import pagelet, RequestArguments
from matemat.primitives import User from matemat.primitives import User
from matemat.db import MatematDatabase from matemat.db import MatematDatabase
@ -10,7 +10,7 @@ from matemat.db import MatematDatabase
@pagelet('/login') @pagelet('/login')
def login_page(method: str, def login_page(method: str,
path: str, path: str,
args: Dict[str, RequestArgument], args: RequestArguments,
session_vars: Dict[str, Any], session_vars: Dict[str, Any],
headers: Dict[str, str])\ headers: Dict[str, str])\
-> Tuple[int, Optional[Union[str, bytes]]]: -> Tuple[int, Optional[Union[str, bytes]]]:
@ -41,13 +41,11 @@ def login_page(method: str,
</body> </body>
</html> </html>
''' '''
return 200, data.format(msg=args['msg'] if 'msg' in args else '') return 200, data.format(msg=str(args.msg) if 'msg' in args else '')
elif method == 'POST': elif method == 'POST':
username: RequestArgument = args['username']
password: RequestArgument = args['password']
with MatematDatabase('test.db') as db: with MatematDatabase('test.db') as db:
try: try:
user: User = db.login(username.get_str(), password.get_str()) user: User = db.login(str(args.username), str(args.password))
except AuthenticationError: except AuthenticationError:
headers['Location'] = '/login?msg=Username%20or%20password%20wrong.%20Please%20try%20again.' headers['Location'] = '/login?msg=Username%20or%20password%20wrong.%20Please%20try%20again.'
return 301, bytes() return 301, bytes()

View file

@ -1,13 +1,13 @@
from typing import Any, Dict, List, Optional, Tuple, Union from typing import Any, Dict, List, Optional, Tuple, Union
from matemat.webserver import pagelet, RequestArgument from matemat.webserver import pagelet, RequestArguments
@pagelet('/logout') @pagelet('/logout')
def logout(method: str, def logout(method: str,
path: str, path: str,
args: Dict[str, RequestArgument], args: RequestArguments,
session_vars: Dict[str, Any], session_vars: Dict[str, Any],
headers: Dict[str, str])\ headers: Dict[str, str])\
-> Tuple[int, Optional[Union[str, bytes]]]: -> Tuple[int, Optional[Union[str, bytes]]]:

View file

@ -1,7 +1,7 @@
from typing import Any, Dict, List, Optional, Tuple, Union from typing import Any, Dict, Optional, Tuple, Union
from matemat.webserver import MatematWebserver, pagelet, RequestArgument from matemat.webserver import pagelet, RequestArguments
from matemat.primitives import User from matemat.primitives import User
from matemat.db import MatematDatabase from matemat.db import MatematDatabase
@ -9,7 +9,7 @@ from matemat.db import MatematDatabase
@pagelet('/') @pagelet('/')
def main_page(method: str, def main_page(method: str,
path: str, path: str,
args: Dict[str, RequestArgument], args: RequestArguments,
session_vars: Dict[str, Any], session_vars: Dict[str, Any],
headers: Dict[str, str])\ headers: Dict[str, str])\
-> Tuple[int, Optional[Union[str, bytes]]]: -> Tuple[int, Optional[Union[str, bytes]]]:

View file

@ -1,8 +1,8 @@
from typing import Any, Dict, List, Optional, Tuple, Union from typing import Any, Dict, Optional, Tuple, Union
from matemat.exceptions import AuthenticationError from matemat.exceptions import AuthenticationError
from matemat.webserver import pagelet, RequestArgument from matemat.webserver import pagelet, RequestArguments
from matemat.primitives import User from matemat.primitives import User
from matemat.db import MatematDatabase from matemat.db import MatematDatabase
@ -10,7 +10,7 @@ from matemat.db import MatematDatabase
@pagelet('/touchkey') @pagelet('/touchkey')
def touchkey_page(method: str, def touchkey_page(method: str,
path: str, path: str,
args: Dict[str, RequestArgument], args: RequestArguments,
session_vars: Dict[str, Any], session_vars: Dict[str, Any],
headers: Dict[str, str])\ headers: Dict[str, str])\
-> Tuple[int, Optional[Union[str, bytes]]]: -> Tuple[int, Optional[Union[str, bytes]]]:
@ -40,13 +40,11 @@ def touchkey_page(method: str,
</body> </body>
</html> </html>
''' '''
return 200, data.format(username=args['username'] if 'username' in args else '') return 200, data.format(username=str(args.username) if 'username' in args else '')
elif method == 'POST': elif method == 'POST':
username: RequestArgument = args['username']
touchkey: RequestArgument = args['touchkey']
with MatematDatabase('test.db') as db: with MatematDatabase('test.db') as db:
try: try:
user: User = db.login(username.get_str(), touchkey=touchkey.get_str()) user: User = db.login(str(args.username), touchkey=str(args.touchkey))
except AuthenticationError: except AuthenticationError:
headers['Location'] = f'/touchkey?username={args["username"]}&msg=Please%20try%20again.' headers['Location'] = f'/touchkey?username={args["username"]}&msg=Please%20try%20again.'
return 301, bytes() return 301, bytes()

View file

@ -1,5 +1,5 @@
from typing import Any, Callable, Dict, List, Tuple, Union from typing import Any, Callable, Dict, Tuple, Union
import unittest.mock import unittest.mock
from io import BytesIO from io import BytesIO
@ -9,7 +9,7 @@ from abc import ABC
from datetime import datetime from datetime import datetime
from http.server import HTTPServer from http.server import HTTPServer
from matemat.webserver import pagelet, RequestArgument from matemat.webserver import pagelet, RequestArguments
class HttpResponse: class HttpResponse:
@ -158,14 +158,14 @@ def test_pagelet(path: str):
def with_testing_headers(fun: Callable[[str, def with_testing_headers(fun: Callable[[str,
str, str,
Dict[str, RequestArgument], RequestArguments,
Dict[str, Any], Dict[str, Any],
Dict[str, str]], Dict[str, str]],
Tuple[int, Union[bytes, str]]]): Tuple[int, Union[bytes, str]]]):
@pagelet(path) @pagelet(path)
def testing_wrapper(method: str, def testing_wrapper(method: str,
path: str, path: str,
args: Dict[str, RequestArgument], args: RequestArguments,
session_vars: Dict[str, Any], session_vars: Dict[str, Any],
headers: Dict[str, str]): headers: Dict[str, str]):
status, body = fun(method, path, args, session_vars, headers) status, body = fun(method, path, args, session_vars, headers)

View file

@ -20,9 +20,9 @@ class TestParseRequest(unittest.TestCase):
path, args = parse_args('/?foo=42&bar=1337&baz=Hello,%20World!') path, args = parse_args('/?foo=42&bar=1337&baz=Hello,%20World!')
self.assertEqual('/', path) self.assertEqual('/', path)
self.assertEqual(3, len(args)) self.assertEqual(3, len(args))
self.assertIn('foo', args.keys()) self.assertIn('foo', args)
self.assertIn('bar', args.keys()) self.assertIn('bar', args)
self.assertIn('baz', args.keys()) self.assertIn('baz', args)
self.assertTrue(args['foo'].is_scalar) self.assertTrue(args['foo'].is_scalar)
self.assertTrue(args['bar'].is_scalar) self.assertTrue(args['bar'].is_scalar)
self.assertTrue(args['baz'].is_scalar) self.assertTrue(args['baz'].is_scalar)
@ -37,9 +37,9 @@ class TestParseRequest(unittest.TestCase):
path, args = parse_args('/abc/def?foo=42&bar=1337&baz=Hello,%20World!') path, args = parse_args('/abc/def?foo=42&bar=1337&baz=Hello,%20World!')
self.assertEqual('/abc/def', path) self.assertEqual('/abc/def', path)
self.assertEqual(3, len(args)) self.assertEqual(3, len(args))
self.assertIn('foo', args.keys()) self.assertIn('foo', args)
self.assertIn('bar', args.keys()) self.assertIn('bar', args)
self.assertIn('baz', args.keys()) self.assertIn('baz', args)
self.assertTrue(args['foo'].is_scalar) self.assertTrue(args['foo'].is_scalar)
self.assertTrue(args['bar'].is_scalar) self.assertTrue(args['bar'].is_scalar)
self.assertTrue(args['baz'].is_scalar) self.assertTrue(args['baz'].is_scalar)
@ -54,8 +54,8 @@ class TestParseRequest(unittest.TestCase):
path, args = parse_args('/abc/def?foo=42&foo=1337&baz=Hello,%20World!') path, args = parse_args('/abc/def?foo=42&foo=1337&baz=Hello,%20World!')
self.assertEqual('/abc/def', path) self.assertEqual('/abc/def', path)
self.assertEqual(2, len(args)) self.assertEqual(2, len(args))
self.assertIn('foo', args.keys()) self.assertIn('foo', args)
self.assertIn('baz', args.keys()) self.assertIn('baz', args)
self.assertTrue(args['foo'].is_array) self.assertTrue(args['foo'].is_array)
self.assertTrue(args['baz'].is_scalar) self.assertTrue(args['baz'].is_scalar)
self.assertEqual(2, len(args['foo'])) self.assertEqual(2, len(args['foo']))
@ -65,8 +65,8 @@ class TestParseRequest(unittest.TestCase):
def test_parse_get_zero_arg(self): def test_parse_get_zero_arg(self):
path, args = parse_args('/abc/def?foo=&bar=42') path, args = parse_args('/abc/def?foo=&bar=42')
self.assertEqual(2, len(args)) self.assertEqual(2, len(args))
self.assertIn('foo', args.keys()) self.assertIn('foo', args)
self.assertIn('bar', args.keys()) self.assertIn('bar', args)
self.assertTrue(args['foo'].is_scalar) self.assertTrue(args['foo'].is_scalar)
self.assertTrue(args['bar'].is_scalar) self.assertTrue(args['bar'].is_scalar)
self.assertEqual(1, len(args['foo'])) self.assertEqual(1, len(args['foo']))
@ -83,9 +83,9 @@ class TestParseRequest(unittest.TestCase):
enctype='application/x-www-form-urlencoded') enctype='application/x-www-form-urlencoded')
self.assertEqual('/', path) self.assertEqual('/', path)
self.assertEqual(3, len(args)) self.assertEqual(3, len(args))
self.assertIn('foo', args.keys()) self.assertIn('foo', args)
self.assertIn('bar', args.keys()) self.assertIn('bar', args)
self.assertIn('baz', args.keys()) self.assertIn('baz', args)
self.assertTrue(args['foo'].is_scalar) self.assertTrue(args['foo'].is_scalar)
self.assertTrue(args['bar'].is_scalar) self.assertTrue(args['bar'].is_scalar)
self.assertTrue(args['baz'].is_scalar) self.assertTrue(args['baz'].is_scalar)
@ -102,8 +102,8 @@ class TestParseRequest(unittest.TestCase):
enctype='application/x-www-form-urlencoded') enctype='application/x-www-form-urlencoded')
self.assertEqual('/', path) self.assertEqual('/', path)
self.assertEqual(2, len(args)) self.assertEqual(2, len(args))
self.assertIn('foo', args.keys()) self.assertIn('foo', args)
self.assertIn('baz', args.keys()) self.assertIn('baz', args)
self.assertTrue(args['foo'].is_array) self.assertTrue(args['foo'].is_array)
self.assertTrue(args['baz'].is_scalar) self.assertTrue(args['baz'].is_scalar)
self.assertEqual(2, len(args['foo'])) self.assertEqual(2, len(args['foo']))
@ -113,8 +113,8 @@ class TestParseRequest(unittest.TestCase):
def test_parse_post_urlencoded_zero_arg(self): def test_parse_post_urlencoded_zero_arg(self):
path, args = parse_args('/abc/def', postbody=b'foo=&bar=42', enctype='application/x-www-form-urlencoded') path, args = parse_args('/abc/def', postbody=b'foo=&bar=42', enctype='application/x-www-form-urlencoded')
self.assertEqual(2, len(args)) self.assertEqual(2, len(args))
self.assertIn('foo', args.keys()) self.assertIn('foo', args)
self.assertIn('bar', args.keys()) self.assertIn('bar', args)
self.assertTrue(args['foo'].is_scalar) self.assertTrue(args['foo'].is_scalar)
self.assertTrue(args['bar'].is_scalar) self.assertTrue(args['bar'].is_scalar)
self.assertEqual(1, len(args['foo'])) self.assertEqual(1, len(args['foo']))
@ -152,9 +152,9 @@ class TestParseRequest(unittest.TestCase):
enctype='multipart/form-data; boundary=testBoundary1337') enctype='multipart/form-data; boundary=testBoundary1337')
self.assertEqual('/', path) self.assertEqual('/', path)
self.assertEqual(3, len(args)) self.assertEqual(3, len(args))
self.assertIn('foo', args.keys()) self.assertIn('foo', args)
self.assertIn('bar', args.keys()) self.assertIn('bar', args)
self.assertIn('baz', args.keys()) self.assertIn('baz', args)
self.assertTrue(args['foo'].is_scalar) self.assertTrue(args['foo'].is_scalar)
self.assertTrue(args['bar'].is_scalar) self.assertTrue(args['bar'].is_scalar)
self.assertTrue(args['baz'].is_scalar) self.assertTrue(args['baz'].is_scalar)
@ -177,8 +177,8 @@ class TestParseRequest(unittest.TestCase):
b'--testBoundary1337--\r\n', b'--testBoundary1337--\r\n',
enctype='multipart/form-data; boundary=testBoundary1337') enctype='multipart/form-data; boundary=testBoundary1337')
self.assertEqual(2, len(args)) self.assertEqual(2, len(args))
self.assertIn('foo', args.keys()) self.assertIn('foo', args)
self.assertIn('bar', args.keys()) self.assertIn('bar', args)
self.assertTrue(args['foo'].is_scalar) self.assertTrue(args['foo'].is_scalar)
self.assertTrue(args['bar'].is_scalar) self.assertTrue(args['bar'].is_scalar)
self.assertEqual(1, len(args['foo'])) self.assertEqual(1, len(args['foo']))

View file

@ -1,7 +1,7 @@
from typing import Any, Dict, List, Tuple, Union from typing import Any, Dict, List
from matemat.webserver import HttpHandler, RequestArgument from matemat.webserver import HttpHandler, RequestArguments
from matemat.webserver.test.abstract_httpd_test import AbstractHttpdTest, test_pagelet from matemat.webserver.test.abstract_httpd_test import AbstractHttpdTest, test_pagelet
import codecs import codecs
@ -10,7 +10,7 @@ import codecs
@test_pagelet('/just/testing/post') @test_pagelet('/just/testing/post')
def post_test_pagelet(method: str, def post_test_pagelet(method: str,
path: str, path: str,
args: Dict[str, RequestArgument], args: RequestArguments,
session_vars: Dict[str, Any], session_vars: Dict[str, Any],
headers: Dict[str, str]): headers: Dict[str, str]):
""" """
@ -18,12 +18,12 @@ def post_test_pagelet(method: str,
""" """
headers['Content-Type'] = 'text/plain' headers['Content-Type'] = 'text/plain'
dump: str = '' dump: str = ''
for k, ra in args.items(): for ra in args:
for a in ra: for a in ra:
if a.get_content_type().startswith('text/'): if a.get_content_type().startswith('text/'):
dump += f'{k}: {a.get_str()}\n' dump += f'{a.name}: {a.get_str()}\n'
else: else:
dump += f'{k}: {codecs.encode(a.get_bytes(), "hex").decode("utf-8")}\n' dump += f'{a.name}: {codecs.encode(a.get_bytes(), "hex").decode("utf-8")}\n'
return 200, dump return 200, dump

View file

@ -1,16 +1,16 @@
from typing import Any, Dict, Union from typing import Any, Dict
import os import os
import os.path import os.path
from matemat.webserver import HttpHandler, RequestArgument from matemat.webserver import HttpHandler, RequestArguments
from matemat.webserver.test.abstract_httpd_test import AbstractHttpdTest, test_pagelet from matemat.webserver.test.abstract_httpd_test import AbstractHttpdTest, test_pagelet
@test_pagelet('/just/testing/serve_pagelet_ok') @test_pagelet('/just/testing/serve_pagelet_ok')
def serve_test_pagelet_ok(method: str, def serve_test_pagelet_ok(method: str,
path: str, path: str,
args: Dict[str, RequestArgument], args: RequestArguments,
session_vars: Dict[str, Any], session_vars: Dict[str, Any],
headers: Dict[str, str]): headers: Dict[str, str]):
headers['Content-Type'] = 'text/plain' headers['Content-Type'] = 'text/plain'
@ -20,7 +20,7 @@ def serve_test_pagelet_ok(method: str,
@test_pagelet('/just/testing/serve_pagelet_fail') @test_pagelet('/just/testing/serve_pagelet_fail')
def serve_test_pagelet_fail(method: str, def serve_test_pagelet_fail(method: str,
path: str, path: str,
args: Dict[str, RequestArgument], args: RequestArguments,
session_vars: Dict[str, Any], session_vars: Dict[str, Any],
headers: Dict[str, str]): headers: Dict[str, str]):
session_vars['test'] = 'hello, world!' session_vars['test'] = 'hello, world!'

View file

@ -1,17 +1,17 @@
from typing import Any, Dict, Union from typing import Any, Dict
from datetime import datetime, timedelta from datetime import datetime, timedelta
from time import sleep from time import sleep
from matemat.webserver import HttpHandler, RequestArgument from matemat.webserver import HttpHandler, RequestArguments
from matemat.webserver.test.abstract_httpd_test import AbstractHttpdTest, test_pagelet from matemat.webserver.test.abstract_httpd_test import AbstractHttpdTest, test_pagelet
@test_pagelet('/just/testing/sessions') @test_pagelet('/just/testing/sessions')
def session_test_pagelet(method: str, def session_test_pagelet(method: str,
path: str, path: str,
args: Dict[str, RequestArgument], args: RequestArguments,
session_vars: Dict[str, Any], session_vars: Dict[str, Any],
headers: Dict[str, str]): headers: Dict[str, str]):
session_vars['test'] = 'hello, world!' session_vars['test'] = 'hello, world!'

View file

@ -1,9 +1,9 @@
from typing import Dict, List, Tuple, Optional, Union from typing import Dict, List, Tuple, Optional
import urllib.parse import urllib.parse
from matemat.webserver import RequestArgument from matemat.webserver import RequestArguments, RequestArgument
def _parse_multipart(body: bytes, boundary: str) -> List[RequestArgument]: def _parse_multipart(body: bytes, boundary: str) -> List[RequestArgument]:
@ -76,7 +76,7 @@ def _parse_multipart(body: bytes, boundary: str) -> List[RequestArgument]:
def parse_args(request: str, postbody: Optional[bytes] = None, enctype: str = 'text/plain') \ def parse_args(request: str, postbody: Optional[bytes] = None, enctype: str = 'text/plain') \
-> Tuple[str, Dict[str, RequestArgument]]: -> Tuple[str, RequestArguments]:
""" """
Given a HTTP request path, and optionally a HTTP POST body in application/x-www-form-urlencoded or Given a HTTP request path, and optionally a HTTP POST body in application/x-www-form-urlencoded or
multipart/form-data form, parse the arguments and return them as a dictionary. multipart/form-data form, parse the arguments and return them as a dictionary.
@ -98,11 +98,11 @@ def parse_args(request: str, postbody: Optional[bytes] = None, enctype: str = 't
else: else:
getargs = urllib.parse.parse_qs(tokens.query, strict_parsing=True, keep_blank_values=True, errors='strict') getargs = urllib.parse.parse_qs(tokens.query, strict_parsing=True, keep_blank_values=True, errors='strict')
args: Dict[str, RequestArgument] = dict() args = RequestArguments()
for k, v in getargs.items(): for k, vs in getargs.items():
args[k] = RequestArgument(k) args[k].clear()
for _v in v: for v in vs:
args[k].append('text/plain', _v) args[k].append('text/plain', v)
if postbody is not None: if postbody is not None:
if enctype == 'application/x-www-form-urlencoded': if enctype == 'application/x-www-form-urlencoded':
@ -113,10 +113,10 @@ def parse_args(request: str, postbody: Optional[bytes] = None, enctype: str = 't
else: else:
postargs = urllib.parse.parse_qs(pb, strict_parsing=True, keep_blank_values=True, errors='strict') postargs = urllib.parse.parse_qs(pb, strict_parsing=True, keep_blank_values=True, errors='strict')
# Write all POST values into the dict, overriding potential duplicates from GET # Write all POST values into the dict, overriding potential duplicates from GET
for k, v in postargs.items(): for k, vs in postargs.items():
args[k] = RequestArgument(k) args[k].clear()
for _v in v: for v in vs:
args[k].append('text/plain', _v) args[k].append('text/plain', v)
elif enctype.startswith('multipart/form-data'): elif enctype.startswith('multipart/form-data'):
# Parse the multipart boundary from the Content-Type header # Parse the multipart boundary from the Content-Type header
try: try:
@ -126,7 +126,9 @@ def parse_args(request: str, postbody: Optional[bytes] = None, enctype: str = 't
# Parse the multipart body # Parse the multipart body
mpargs = _parse_multipart(postbody, boundary) mpargs = _parse_multipart(postbody, boundary)
for ra in mpargs: for ra in mpargs:
args[ra.name] = ra args[ra.name].clear()
for a in ra:
args[ra.name].append(a.get_content_type(), bytes(a))
else: else:
raise ValueError(f'Unsupported Content-Type: {enctype}') raise ValueError(f'Unsupported Content-Type: {enctype}')
# Return the path and the parsed arguments # Return the path and the parsed arguments