def get_frozen_file_getter(file_path, cache_max_age=12000): mime = get_mime_type(file_path) size = os.path.getsize(file_path) current_etag = str(os.path.getmtime(file_path)).encode() headers = [ Header(b'Last-Modified', unix_timestamp_to_datetime(os.path.getmtime(file_path))), Header(b'ETag', current_etag), Header(b'Cache-Control', b'max-age=' + str(cache_max_age).encode()) ] head_headers = headers + [ Header(b'Content-Type', mime), Header(b'Content-Length', str(size).encode()) ] data = get_file_data(file_path, size, size_limit=1.049e+7) async def frozen_file_getter(request): previous_etag = request.if_none_match if previous_etag and previous_etag == current_etag: return Response(304, headers, None) if request.method == b'HEAD': return Response(200, head_headers, None) return Response(200, Headers(headers), Content(mime, data)) return frozen_file_getter
def get_response_for_file(request, resource_path, cache_time): # TODO: support for accept-range and bytes ranges file_size = os.path.getsize(resource_path) modified_time = os.path.getmtime(resource_path) current_etag = str(modified_time).encode() previous_etag = request.if_none_match headers = [ Header(b'Last-Modified', unix_timestamp_to_datetime(modified_time)), Header(b'ETag', current_etag) ] if cache_time > 0: headers.append( Header(b'Cache-Control', b'max-age=' + str(cache_time).encode())) if previous_etag and current_etag == previous_etag: return Response(304, headers, None) if request.method == b'HEAD': headers.append(Header(b'Content-Type', get_mime_type(resource_path))) headers.append(Header(b'Content-Length', str(file_size).encode())) return Response(200, headers, None) return Response( 200, Headers(headers), Content(get_mime_type(resource_path), get_file_data(resource_path, file_size)))
def test_http_header_check_equality(name_a, value_a, name_b, value_b, expected_result): a = Header(name_a, value_a) b = Header(name_b, value_b) assert (a == b) == expected_result assert (a != b) != expected_result assert (b != a) != expected_result
def test_http_header_collection_get_single_raises_if_more_items_are_present(): headers = Headers() values = [ Header(b'Cookie', b'Hello=World;'), Header(b'Cookie', b'Foo=foo;'), Header(b'Cookie', b'Ufo=ufo;'), ] headers.add_many(values) # keeps only the last one, if a single header is expected headers.get_single(b'cookie').value == b'Ufo=ufo;'
def test_iadd_http_header_collection_concatenation_with_duplicate_header(): headers = Headers([ Header(b'Hello', b'World'), Header(b'Svil', b'Power'), ]) headers += Header(b'Svil', b'Kitty') example = headers[b'Svil'] assert len(example) == 2 assert any(x.value == b'Power' for x in example) assert any(x.value == b'Kitty' for x in example)
def test_iadd_http_header_collection_concatenation_with_header(): headers = Headers([ Header(b'Hello', b'World'), Header(b'Svil', b'Power'), ]) headers += Header(b'Foo', b'foo') example = headers[b'Foo'] assert len(example) == 1 header = example[0] assert header.name == b'Foo' assert header.value == b'foo'
def test_iadd_http_header_collection_concatenation_with_list_of_headers(): headers = Headers([ Header(b'Hello', b'World'), Header(b'Svil', b'Power'), ]) headers += [Header(b'foo', b'foo'), Header(b'ufo', b'ufo')] for name in {b'foo', b'ufo'}: example = headers[name] assert len(example) == 1 header = example[0] assert header.name.lower() == name assert header.value == name
def test_cookie_parsing_multiple_cookie_headers(): request = Request( b'POST', b'/', Headers([ Header(b'Cookie', b'ai=something; hello=world; foo=Hello%20World%3B;'), Header(b'Cookie', b'jib=jab; ai=else;'), ]), None) assert request.cookies == { b'ai': b'else', b'hello': b'world', b'foo': b'Hello World;', b'jib': b'jab' }
async def test_application_get_handler(): app = FakeApplication() @app.router.get(b'/') async def home(request): pass @app.router.get(b'/foo') async def foo(request): pass handler = get_new_connection_handler(app) message = b'\r\n'.join([ b'GET / HTTP/1.1', b'User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:63.0) Gecko/20100101 Firefox/63.0', b'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', b'Accept-Language: en-US,en;q=0.5', b'Connection: keep-alive', b'Upgrade-Insecure-Requests: 1', b'Host: foo\r\n\r\n' ]) handler.data_received(message) await app.response_done.wait() request = app.request # type: Request assert request is not None connection = request.headers[b'connection'] assert connection == [Header(b'Connection', b'keep-alive')] text = await request.text() assert text == ''
async def test_cookies_jar_single_cookie(): fake_pools = FakePools([ Response( 200, Headers([ Header(b'Set-Cookie', write_response_cookie(Cookie(b'X-Foo', b'Foo'))) ]), TextContent('Hello, World!')), Response(200, Headers(), TextContent('Hello!')) ]) check_cookie = False async def middleware_for_assertions(request, next_handler): if check_cookie: cookie = request.cookies.get(b'X-Foo') assert cookie is not None, 'X-Foo cookie must be configured for following requests' return await next_handler(request) async with ClientSession(url=b'https://bezkitu.org', pools=fake_pools, middlewares=[middleware_for_assertions ]) as client: await client.get( b'/' ) # the first request doesn't have any cookie because the response will set; check_cookie = True await client.get(b'/')
async def test_request_headers(): fake_pools = FakePools( [Response(200, Headers(), TextContent('Hello, World!'))]) async def middleware_for_assertions(request, next_handler): assert b'Hello' in request.headers assert request.headers.get_single(b'Hello').value == b'World' return await next_handler(request) async with ClientSession(url=b'http://localhost:8080', pools=fake_pools, middlewares=[middleware_for_assertions ]) as client: await client.get(b'/', headers=[Header(b'Hello', b'World')]) await client.post(b'/', headers=[Header(b'Hello', b'World')]) await client.put(b'/', headers=[Header(b'Hello', b'World')]) await client.delete(b'/', headers=[Header(b'Hello', b'World')])
def test_http_header_collection_add_multiple_times_items(): headers = Headers() values = [ Header(b'Cookie', b'Hello=World;'), Header(b'Cookie', b'Foo=foo;'), Header(b'Cookie', b'Ufo=ufo;'), ] headers.add_many(values) cookie_headers = headers[b'cookie'] assert cookie_headers assert len(cookie_headers) == 3 assert any(x.value == b'Hello=World;' for x in cookie_headers) assert any(x.value == b'Foo=foo;' for x in cookie_headers) assert any(x.value == b'Ufo=ufo;' for x in cookie_headers)
def test_http_header_collection_instantiating_with_dict_values(values): headers = Headers(list( Header(key, value) for key, value in values.items())) for key, value in values.items(): header = headers[key] assert len(header) == 1 assert header[0].name == key assert header[0].value == value
async def test_remove_cookie_with_expiration(): expire_cookie = Cookie(b'X-Foo', b'Foo') expire_cookie.expiration = datetime.utcnow() + timedelta(days=-2) fake_pools = FakePools([ Response( 200, Headers([ Header(b'Set-Cookie', write_response_cookie(Cookie(b'X-Foo', b'Foo'))) ]), TextContent('Hello, World!')), Response(200, Headers(), TextContent('Hello!')), Response( 200, Headers( [Header(b'Set-Cookie', write_response_cookie(expire_cookie))]), TextContent('Hello, World!')), Response(200, Headers(), TextContent('Hello!')) ]) expect_cookie = False async def middleware_for_assertions(request, next_handler): cookie = request.cookies.get(b'X-Foo') if expect_cookie: assert cookie is not None, 'X-Foo cookie must be configured' else: assert cookie is None return await next_handler(request) async with ClientSession(url=b'https://bezkitu.org', pools=fake_pools, middlewares=[middleware_for_assertions ]) as client: await client.get(b'/') # <-- cookie set here expect_cookie = True await client.get(b'/') # <-- expect cookie in request expect_cookie = True await client.get( b'/') # <-- expect cookie in request; it gets removed here expect_cookie = False await client.get( b'/' ) # <-- expect missing cookie; was deleted by previous response
def test_http_header_collection_concatenation_with_other_collection(): headers = Headers([ Header(b'Hello', b'World'), Header(b'Svil', b'Power'), ]) with_addition = headers + Headers([ Header(b'Foo', b'foo'), Header(b'Ufo', b'ufo') ]) for name in {b'foo', b'ufo'}: assert headers[name] == [] example = with_addition[name] assert len(example) == 1 header = example[0] assert header.name.lower() == name assert header.value == name
async def test_if_read_json_fails_content_type_header_is_checked_non_json_gives_invalid_operation( ): request = Request(b'POST', b'/', Headers([Header(b'Content-Type', b'text/html')]), None) request.extend_body(b'{"hello":') # broken json request.complete.set() with pytest.raises(InvalidOperation): await request.json()
async def test_if_read_json_fails_content_type_header_is_checked_json_gives_bad_request_format( ): request = Request(b'POST', b'/', Headers([Header(b'Content-Type', b'application/json')]), None) request.extend_body(b'{"hello":') # broken json request.complete.set() with pytest.raises(BadRequestFormat): await request.json()
async def test_from_header_binding(expected_type, header_value, expected_value): request = Request(b'GET', b'/', Headers([Header(b'X-Foo', header_value)]), None) parameter = FromHeader(expected_type, 'X-Foo') value = await parameter.get_value(request) assert isinstance(value, expected_type) assert value == expected_value
async def create_cat(request): nonlocal called_times called_times += 1 assert request is not None content = await request.read() assert b'{"name":"Celine","kind":"Persian"}' == content data = await request.json() assert {"name": "Celine", "kind": "Persian"} == data return Response(201, Headers([Header(b'Server', b'Python/3.7')]), JsonContent({'id': '123'}))
async def test_from_header_binding_iterables(declared_type, expected_type, header_values, expected_values): request = Request( b'GET', b'/', Headers([Header(b'X-Foo', value) for value in header_values]), None) parameter = FromHeader(declared_type, 'X-Foo') value = await parameter.get_value(request) assert isinstance(value, expected_type) assert value == expected_values
def test_cookie_parsing(): request = Request( b'POST', b'/', Headers([ Header(b'Cookie', b'ai=something; hello=world; foo=Hello%20World%3B;') ]), None) assert request.cookies == { b'ai': b'something', b'hello': b'world', b'foo': b'Hello World;' }
def test_cookie_parsing_duplicated_cookie_header_value(): request = Request( b'POST', b'/', Headers([ Header( b'Cookie', b'ai=something; hello=world; foo=Hello%20World%3B; hello=kitty;' ) ]), None) assert request.cookies == { b'ai': b'something', b'hello': b'kitty', b'foo': b'Hello World;' }
async def fortunes_test(request): """Test type 4: Fortunes""" connection = await db_pool.acquire() try: fortunes = await connection.fetch('SELECT * FROM Fortune') finally: await db_pool.release(connection) fortunes.append([0, 'Additional fortune added at request time.']) fortunes.sort(key=lambda x: x[1]) return Response(200, Headers([ Header(b'Cache-Control', b'no-cache') ]), content=Content(b'text/html; charset=utf-8', fortune_template.render(fortunes=fortunes).encode('utf8')))
def _file(value: Union[Callable, bytes], content_type: Union[str, bytes], content_disposition_type: ContentDispositionType, file_name: str = None): if file_name: exact_file_name = ntpath.basename(file_name) if not exact_file_name: raise ValueError( 'Invalid file name: it should be an exact file name without path, for example: "foo.txt"' ) content_disposition_value = f'{content_disposition_type.value}; filename="{exact_file_name}"' else: content_disposition_value = content_disposition_type.value response = Response(200, content=Content(_ensure_bytes(content_type), value)) response.headers.add( Header(b'Content-Disposition', content_disposition_value.encode())) return response
def get_response(html: str): return Response(200, Headers([Header(b'Cache-Control', b'no-cache')]), content=HtmlContent(html))
def redirect(location: Union[bytes, str]): """Returns an HTTP 302 Found response (commonly called redirect), to the given location""" return Response(302, Headers([Header(b'Location', _ensure_bytes(location))]))
]) as client: await client.get( b'/' ) # the first request doesn't have any cookie because the response will set; check_cookie = True await client.get(b'/') @pytest.mark.asyncio @pytest.mark.parametrize( 'first_request_url,second_request_url,set_cookies,expected_cookies', [[ b'https://foo.bezkitu.org', b'https://bezkitu.org', [ Header( b'Set-Cookie', write_response_cookie( Cookie(b'X-Foo', b'Foo', domain=b'bezkitu.org'))) ], [b'X-Foo'] ], [ b'https://foo.bezkitu.org', b'https://foo.bezkitu.org', [ Header( b'Set-Cookie', write_response_cookie( Cookie(b'X-Foo', b'Foo', domain=b'foo.bezkitu.org'))) ], [b'X-Foo'] ], [ b'https://foo.bezkitu.org', b'https://bezkitu.org', [
def see_other(location: Union[bytes, str]): """Returns an HTTP 303 See Other response, to the given location.""" return Response(303, Headers([Header(b'Location', _ensure_bytes(location))]))
def temporary_redirect(location: Union[bytes, str]): """Returns an HTTP 307 Temporary Redirect response, to the given location.""" return Response(307, Headers([Header(b'Location', _ensure_bytes(location))]))
import pytest from pytest import raises from typing import List, Sequence, Set, Tuple from blacksheep import Request, Headers, Header, JsonContent from blacksheep.server.bindings import (FromJson, FromHeader, FromQuery, FromRoute, FromServices, RequestBinder, InvalidRequestBody, MissingConverterError, BadRequest) JsonContentType = Header(b'Content-Type', b'application/json') class ExampleOne: def __init__(self, a, b): self.a = a self.b = b class ExampleTwo: def __init__(self, a, b, **kwargs): self.a = a self.b = b @pytest.mark.asyncio async def test_from_body_json_binding(): request = Request(b'POST', b'/', Headers([JsonContentType]), JsonContent({ 'a': 'world', 'b': 9000