X-Git-Url: https://code.kerkeslager.com/?a=blobdiff_plain;f=src%2Ffwx%2F__init__.py;h=2cbe707eaedd15da22fa13d44167363c1842cde4;hb=2e9a0dbf5bd8fb60f19bf2665fb4020a79320090;hp=e0abb7523994c58847b1ee6501fcbb6299aacae4;hpb=add99327a18a300ff26d64759921fc0f6847a07b;p=fwx diff --git a/src/fwx/__init__.py b/src/fwx/__init__.py index e0abb75..2cbe707 100644 --- a/src/fwx/__init__.py +++ b/src/fwx/__init__.py @@ -132,8 +132,8 @@ class Response(_Response): content_type = kwargs.pop('content_type') assert isinstance(content_type, str) - extra_headers = kwargs.pop('extra_headers', ()) - assert isinstance(extra_headers, tuple) + extra_headers = kwargs.pop('extra_headers', {}) + assert isinstance(extra_headers, dict) assert len(kwargs) == 0 @@ -147,9 +147,25 @@ class Response(_Response): @property def headers(self): - return ( - ('Content-Type', self.content_type), - ) + # Start with the defaults + result = { + 'X-Content-Type-Options': 'nosniff', + } + + result = {**result, **(self.extra_headers)} + + builtin_headers = { + 'Content-Type': self.content_type, + } + + for key, value in builtin_headers.items(): + if key in result: + raise Exception('Header "{}" defined twice'.format(key)) + else: + result[key] = value + + return tuple(sorted(result.items())) + class HTMLResponse(Response): def __new__(cls, content, **kwargs): @@ -158,7 +174,7 @@ class HTMLResponse(Response): return super().__new__( cls, content, - content_type='text/html', + content_type='text/html; charset=utf-8', **kwargs, ) @@ -170,7 +186,7 @@ class JSONResponse(Response): self = super().__new__( cls, content=json.dumps(content_json), - content_type='application/json', + content_type='application/json; charset=utf-8', **kwargs, ) self.content_json = content_json @@ -183,7 +199,7 @@ class TextResponse(Response): return super().__new__( cls, content, - content_type='text/plain', + content_type='text/plain; charset=utf-8', **kwargs, ) @@ -266,7 +282,7 @@ REQUEST_METHODS = ( ) def default_method_not_allowed_handler(request): - return Response('') + return TextResponse('', status=405) def default_options_handler(handlers): def handler(request): @@ -299,9 +315,42 @@ def route_on_method(**kwargs): def _get_status(response): return { + 100: '100 Continue', + 101: '101 Switching Protocols', 200: '200 OK', + 201: '201 Created', + 202: '202 Accepted', + 203: '203 Non-Authoritative Information', + 204: '204 No Content', + 205: '205 Reset Content', + 300: '300 Multiple Choices', + 301: '301 Moved Permanently', + 304: '304 Not Modified', 307: '307 Temporary Redirect', 308: '308 Permanent Redirect', + 400: '400 Bad Request', + 401: '401 Unauthorized', + 402: '402 Payment Required', + 403: '403 Forbidden', + 404: '404 Not Found', + 405: '405 Method Not Allowed', + 406: '406 Not Acceptable', + 409: '409 Conflict', + 410: '410 Gone', + 411: '411 Length Required', + 412: '412 Precondition Failed', + 413: '413 Payload Too Large', + 414: '414 URI Too Long', + 415: '415 Unsupported Media Type', + 416: '416 Range Not Satisfiable', + 417: '417 Expectation Failed', + 418: "418 I'm a teapot", + 429: '429 Too Many Requests', + 431: '431 Request Header Fields Too Large', + 451: '451 Unavailable For Legal Reasons', + 500: '500 Internal Server Error', + 501: '501 Not Implemented', + 503: '503 Service Unavailable', }[response.status] def _get_headers(response):