* Add CodSpeed benchmark suite for HTTP protocol hot paths * Suppress mypy operator error on ASGI message body concatenation * Use OIDC token and pin CodSpeed action to latest commit
99 lines
3.6 KiB
Python
99 lines
3.6 KiB
Python
from __future__ import annotations
|
|
|
|
from typing import TYPE_CHECKING
|
|
|
|
import pytest
|
|
|
|
from tests.benchmarks.http import (
|
|
CONNECTION_CLOSE_REQUEST,
|
|
FINISH_POST_REQUEST,
|
|
HTTP10_GET_REQUEST,
|
|
LARGE_POST_REQUEST,
|
|
SIMPLE_GET_REQUEST,
|
|
SIMPLE_POST_REQUEST,
|
|
START_POST_REQUEST,
|
|
get_connected_protocol,
|
|
)
|
|
from tests.response import Response
|
|
from uvicorn._types import ASGIReceiveCallable, ASGISendCallable, Scope
|
|
|
|
if TYPE_CHECKING:
|
|
from tests.benchmarks.http import HTTPProtocol
|
|
|
|
pytestmark = [pytest.mark.anyio, pytest.mark.benchmark]
|
|
|
|
_plain_text_app = Response("Hello, world", media_type="text/plain")
|
|
_no_content_app = Response(b"", status_code=204)
|
|
_chunked_app = Response(b"Hello, world!", status_code=200, headers={"transfer-encoding": "chunked"})
|
|
|
|
|
|
async def _body_echo_app(scope: Scope, receive: ASGIReceiveCallable, send: ASGISendCallable) -> None:
|
|
body = b""
|
|
while True:
|
|
message = await receive()
|
|
body += message.get("body", b"") # type: ignore[operator]
|
|
if not message.get("more_body", False):
|
|
break
|
|
headers = [(b"content-length", str(len(body)).encode())]
|
|
await send({"type": "http.response.start", "status": 200, "headers": headers})
|
|
await send({"type": "http.response.body", "body": body})
|
|
|
|
|
|
async def test_bench_simple_get(http_protocol_cls: type[HTTPProtocol]) -> None:
|
|
protocol = get_connected_protocol(_plain_text_app, http_protocol_cls)
|
|
protocol.data_received(SIMPLE_GET_REQUEST)
|
|
await protocol.loop.run_one()
|
|
|
|
|
|
async def test_bench_simple_post(http_protocol_cls: type[HTTPProtocol]) -> None:
|
|
protocol = get_connected_protocol(_plain_text_app, http_protocol_cls)
|
|
protocol.data_received(SIMPLE_POST_REQUEST)
|
|
await protocol.loop.run_one()
|
|
|
|
|
|
async def test_bench_large_post(http_protocol_cls: type[HTTPProtocol]) -> None:
|
|
protocol = get_connected_protocol(_plain_text_app, http_protocol_cls)
|
|
protocol.data_received(LARGE_POST_REQUEST)
|
|
await protocol.loop.run_one()
|
|
|
|
|
|
async def test_bench_pipelined_requests(http_protocol_cls: type[HTTPProtocol]) -> None:
|
|
protocol = get_connected_protocol(_plain_text_app, http_protocol_cls)
|
|
protocol.data_received(SIMPLE_GET_REQUEST * 3)
|
|
await protocol.loop.run_one()
|
|
await protocol.loop.run_one()
|
|
await protocol.loop.run_one()
|
|
|
|
|
|
async def test_bench_keepalive_reuse(http_protocol_cls: type[HTTPProtocol]) -> None:
|
|
protocol = get_connected_protocol(_plain_text_app, http_protocol_cls)
|
|
protocol.data_received(SIMPLE_GET_REQUEST)
|
|
await protocol.loop.run_one()
|
|
protocol.data_received(SIMPLE_GET_REQUEST)
|
|
await protocol.loop.run_one()
|
|
|
|
|
|
async def test_bench_chunked_response(http_protocol_cls: type[HTTPProtocol]) -> None:
|
|
protocol = get_connected_protocol(_chunked_app, http_protocol_cls)
|
|
protocol.data_received(SIMPLE_GET_REQUEST)
|
|
await protocol.loop.run_one()
|
|
|
|
|
|
async def test_bench_http10(http_protocol_cls: type[HTTPProtocol]) -> None:
|
|
protocol = get_connected_protocol(_plain_text_app, http_protocol_cls)
|
|
protocol.data_received(HTTP10_GET_REQUEST)
|
|
await protocol.loop.run_one()
|
|
|
|
|
|
async def test_bench_connection_close(http_protocol_cls: type[HTTPProtocol]) -> None:
|
|
protocol = get_connected_protocol(_plain_text_app, http_protocol_cls)
|
|
protocol.data_received(CONNECTION_CLOSE_REQUEST)
|
|
await protocol.loop.run_one()
|
|
|
|
|
|
async def test_bench_post_body_receive(http_protocol_cls: type[HTTPProtocol]) -> None:
|
|
protocol = get_connected_protocol(_body_echo_app, http_protocol_cls)
|
|
protocol.data_received(START_POST_REQUEST)
|
|
protocol.data_received(FINISH_POST_REQUEST)
|
|
await protocol.loop.run_one()
|