def _file(value: FileInput, 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 content_type = _ensure_bytes(content_type) if isinstance(value, str): # value is treated as a path content = StreamedContent(content_type, _get_file_provider(value)) elif isinstance(value, BytesIO): async def data_provider(): while True: chunk = value.read(1024 * 64) if not chunk: break yield chunk yield b'' content = StreamedContent(content_type, data_provider) elif callable(value): # value is treated as an async generator async def data_provider(): async for chunk in value(): yield chunk yield b'' content = StreamedContent(content_type, data_provider) elif isinstance(value, bytes): content = Content(content_type, value) elif isinstance(value, bytearray): content = Content(content_type, bytes(value)) else: raise ValueError( 'Invalid value, expected one of: Callable, str, bytes, bytearray, io.BytesIO' ) return Response( 200, [(b'Content-Disposition', content_disposition_value.encode())], content)
async def multiple_db_queries_test(request): """Test type 3: Multiple Database Queries""" num_queries = get_num_queries(request) row_ids = [randint(1, 10000) for _ in range(num_queries)] worlds = [] connection = await db_pool.acquire() try: statement = await connection.prepare( 'SELECT "randomnumber" FROM "world" WHERE id = $1' ) for row_id in row_ids: number = await statement.fetchval(row_id) worlds.append({"id": row_id, "randomNumber": number}) finally: await db_pool.release(connection) return Response( 200, content=Content( b"application/json; charset=utf-8", json_dumps(worlds).encode("utf-8") ), )
async def test_if_read_json_fails_content_type_header_is_checked_json_gives_bad_request_format(): request = Request("POST", b"/", [(b"Content-Type", b"application/json")]) request.with_content(Content(b"application/json", b'{"hello":')) # broken json with pytest.raises(BadRequestFormat): await request.json()
async def home(url: str, request: Request): headers = request.headers proxy_query_header = make_request_headers(headers) try: async with httpx.AsyncClient() as client: proxy_response = await client.get(url, headers=proxy_query_header, timeout=TIMEOUT) except requests.exceptions.SSLError: # Invalid SSL Certificate status = 526 except requests.exceptions.ConnectionError: status = 523 except requests.exceptions.Timeout: status = 524 except requests.exceptions.TooManyRedirects: status = 520 else: body = proxy_response.content if proxy_response.status_code == 500: status = 520 else: status = 200 headers = [] if status == 200: # copy_proxy_headers(proxy_response, response) headers += [(b"Access-Control-Allow-Origin", get_access_url(headers))] response = blacksheep.Response(status, content=Content(b"text/html", body), headers=headers) return response
def html(value: str, status: int = 200) -> Response: """ Returns a response with text/html content, and given status (default HTTP 200 OK). """ return Response(status, None, Content(b"text/html; charset=utf-8", value.encode("utf8")))
async def test_can_read_json_data_even_without_content_type_header(): request = Request("POST", b"/", None) request.with_content(Content(b"application/json", b'{"hello":"world","foo":false}')) json = await request.json() assert json == {"hello": "world", "foo": False}
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)))
async def db_updates_test(request): """Test type 5: Database Updates""" num_queries = get_num_queries(request) updates = [(randint(1, 10000), randint(1, 10000)) for _ in range(num_queries)] worlds = [{ 'id': row_id, 'randomNumber': number } for row_id, number in updates] connection = await db_pool.acquire() try: statement = await connection.prepare( 'SELECT "randomnumber" FROM "world" WHERE id = $1') for row_id, _ in updates: await statement.fetchval(row_id) await connection.executemany( 'UPDATE "world" SET "randomnumber"=$1 WHERE id=$2', updates) finally: await db_pool.release(connection) return Response(200, content=Content(b'application/json', json_dumps(worlds).encode('utf-8')))
async def test_if_read_json_fails_content_type_header_is_checked_non_json_gives_invalid_operation(): request = Request('POST', b'/', []) request.with_content(Content(b'text/html', b'{"hello":')) # broken json; broken content-type with pytest.raises(InvalidOperation): await request.json()
async def test_if_read_json_fails_content_type_header_is_checked_non_json_gives_invalid_operation( ): request = Request("POST", b"/", []) request.with_content(Content( b"text/html", b'{"hello":')) # broken json; broken content-type with pytest.raises(BadRequestFormat): await request.json()
async def json_test(request): """Test type 1: JSON Serialization""" return Response(200, content=Content( b'application/json; charset=utf-8', json_dumps({ 'message': 'Hello, world!' }).encode('utf-8')))
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))
async def json_test(request): """Test type 1: JSON Serialization""" return Response( 200, content=Content( b"application/json; charset=utf-8", json_dumps({"message": "Hello, world!"}).encode("utf-8"), ), )
async def single_db_query_test(request): """Test type 2: Single Database Query""" row_id = randint(1, 10000) connection = await db_pool.acquire() try: number = await connection.fetchval('SELECT "randomnumber" FROM "world" WHERE id = $1', row_id) world = {'id': row_id, 'randomNumber': number} finally: await db_pool.release(connection) return Response(200, content=Content(b'application/json; charset=utf-8', json_dumps(world).encode('utf-8')))
def json(data: Any, status: int = 200, dumps=friendly_dumps) -> Response: """ Returns a response with application/json content, and given status (default HTTP 200 OK). """ return Response( status, None, Content( b"application/json", dumps(data, cls=FriendlyEncoder, separators=(",", ":")).encode("utf8"), ), )
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 pretty_json(data: Any, status: int = 200, dumps=friendly_dumps, indent: int = 4) -> Response: """ Returns a response with indented application/json content, and given status (default HTTP 200 OK). """ return Response( status, None, Content( b"application/json", dumps(data, cls=FriendlyEncoder, indent=indent).encode("utf8"), ), )
async def test_chunked_encoding_with_generated_content(): def data_generator(): yield b'{"hello":"world",' yield b'"lorem":' yield b'"ipsum","dolor":"sit"' yield b',"amet":"consectetur"}' content = Content(b'application/json', data_generator) gen = data_generator() async for chunk in write_chunks(content): try: generator_bytes = next(gen) except StopIteration: assert chunk == b'0\r\n\r\n' else: assert chunk == hex(len(generator_bytes))[2:].encode() \ + b'\r\n' + generator_bytes + b'\r\n'
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
foo = object() assert ( hasattr(response, "response") is False ), "This test makes sense if such attribute is not defined" response.foo = foo # type: ignore assert response.foo is foo # type: ignore @pytest.mark.asyncio @pytest.mark.parametrize( "response,cookies,expected_result", [ ( Response(400, [(b"Server", b"BlackSheep")]).with_content( Content(b"text/plain", b"Hello, World") ), [], b"HTTP/1.1 400 Bad Request\r\n" b"Server: BlackSheep\r\n" b"content-type: text/plain\r\n" b"content-length: 12\r\n\r\nHello, World", ), ( Response(400, [(b"Server", b"BlackSheep")]).with_content( Content(b"text/plain", b"Hello, World") ), [Cookie("session", "123")], b"HTTP/1.1 400 Bad Request\r\n" b"Server: BlackSheep\r\n" b"set-cookie: session=123\r\n"
async def echo_streamed_test(request): async def echo(): async for chunk in request.stream(): yield chunk return Response(200, content=Content(b'text/plain; charset=utf-8', echo))
def test_response_supports_dynamic_attributes(): response = Response(200) foo = object() assert hasattr(response, 'response') is False, 'This test makes sense if such attribute is not defined' response.foo = foo assert response.foo is foo @pytest.mark.asyncio @pytest.mark.parametrize('response,cookies,expected_result', [ ( Response(400, [ (b'Server', b'BlackSheep'), ]).with_content(Content(b'text/plain', b'Hello, World')), [], b'HTTP/1.1 400 Bad Request\r\n' b'Server: BlackSheep\r\n' b'content-type: text/plain\r\n' b'content-length: 12\r\n\r\nHello, World' ), ( Response(400, [ (b'Server', b'BlackSheep'), ]).with_content(Content(b'text/plain', b'Hello, World')), [Cookie(b'session', b'123')], b'HTTP/1.1 400 Bad Request\r\n' b'Server: BlackSheep\r\n' b'set-cookie: session=123\r\n' b'content-type: text/plain\r\n'
response = Response(200) foo = object() assert hasattr( response, 'response' ) is False, 'This test makes sense if such attribute is not defined' response.foo = foo assert response.foo is foo @pytest.mark.asyncio @pytest.mark.parametrize( 'response,cookies,expected_result', [(Response(400, Headers([ Header(b'Server', b'BlackSheep'), ]), Content(b'text/plain', b'Hello, World')), [], b'HTTP/1.1 400 Bad Request\r\n' b'Server: BlackSheep\r\n' b'Content-Type: text/plain\r\n' b'Content-Length: 12\r\n\r\nHello, World'), (Response(400, Headers([ Header(b'Server', b'BlackSheep'), ]), Content(b'text/plain', b'Hello, World')), [Cookie(b'session', b'123')], b'HTTP/1.1 400 Bad Request\r\n' b'Server: BlackSheep\r\n' b'Content-Type: text/plain\r\n' b'Content-Length: 12\r\n' b'Set-Cookie: session=123\r\n\r\nHello, World'), (Response(400, Headers([Header(b'Server', b'BlackSheep')]), Content(b'text/plain', b'Hello, World')), [ Cookie(b'session', b'123'), Cookie(b'aaa', b'bbb', domain=b'bezkitu.org')
def pretty_json(data: Any, status: int = 200, dumps=JSON.dumps, indent: int = 4): """Returns a response with indented application/json content, and given status (default HTTP 200 OK).""" return Response(status, None, Content(b'application/json', dumps(data, indent=indent).encode('utf8')))
def text(value: str, status: int = 200): """Returns a response with text/plain content, and given status (default HTTP 200 OK).""" return Response(status, None, Content(b'text/plain; charset=utf-8', value.encode('utf8')))
async def plaintext_test(request): """Test type 6: Plaintext""" return Response(200, content=Content(b'text/plain', b'Hello, World!'))
def json(data: Any, status: int = 200, dumps=JSON.dumps): """Returns a response with application/json content, and given status (default HTTP 200 OK).""" return Response(status, None, Content(b'application/json', dumps(data, separators=(',', ':')).encode('utf8')))
def get_response(html: str): return Response(200, [(b"Cache-Control", b"no-cache")]).with_content( Content(b"text/html; charset=utf-8", html.encode("utf8")) )
def get_response(html: str): return Response(200, [ (b'Cache-Control', b'no-cache') ]).with_content(Content(b'text/html; charset=utf-8', html.encode('utf8')))
async def plaintext_test(request): """Test type 6: Plaintext""" return Response(200, content=Content(b"text/plain", b"Hello, World!"))