Uvicorn: HTTP 2.0 Support

Created on 3 Jul 2017  Â·  19Comments  Â·  Source: encode/uvicorn

Using the h2 package.

enhancement help wanted

Most helpful comment

Yes, it actually works well!
It might be a bit rough yet, but the following code does the job.

@pytest.fixture()
def CA():
    yield trustme.CA()


@pytest.fixture()
def server_cert_file(CA):
    server_cert = CA.issue_cert("localtest.me").private_key_and_cert_chain_pem
    with server_cert.tempfile() as ca_temp_path:
        yield ca_temp_path


@pytest.fixture()
async def client_cert_file(CA):
    with CA.cert_pem.tempfile() as ca_temp_path:
        yield ca_temp_path


@pytest.mark.asyncio
@pytest.fixture()
async def async_test_client(client_cert_file):
    async with AsyncClient(
        http_versions=["HTTP/1.1"],
        base_url="https://localtest.me:8000",
        verify=client_cert_file,
    ) as async_test_client:
        yield async_test_client


@pytest.mark.asyncio
async def test_baseline_h11(async_test_client, CA, server_cert_file):
    config = Config(
        app=App, limit_max_requests=3, http="h11", ssl_certfile=server_cert_file
    )
    server = Server(config=config)
    config.load()
    CA.configure_trust(config.ssl)

    # Prepare the coroutine to serve the request
    run_request = server.serve()

    # Run coroutines
    results = await asyncio.gather(
        *[
            run_request,
            async_test_client.get("/"),
            async_test_client.get("/"),
            async_test_client.get("/"),
        ]
    )

Update: And httpx also speaks HTTP/2 as well now 🎉

All 19 comments

@tomchristie if you're going to use h2 for http2, it might be worth using https://github.com/njsmith/h11 for http 1.1.

As they both have the same interface

I'd certainly like to have h11 as an option for a pure python interface yes.

@tomchristie there's been lots of talk about getting httptools and h11 to share an interface.

That would be pretty ace. Anywhere worth following?

There was some stuff on IRC a while ago, and https://github.com/njsmith/h11/issues/9

mostly worried about:

httptools is definitely faster -- but AFAICT that's mostly because it does less. There are a lot of fiddly bits required to correctly implement HTTP, and those have a cost, which h11 pays.

Note that python-hyper also implements HTTP2 in a PyPy compatible way since it's mostly pure python and a bit of CFFI.
Not sure if you're talking about that.

+1 for being PyPy-friendly. I think a lot of people who are interested in uvicorn will want to use PyPy as well for the same reason (performance).

@thedrow fyi Hyper is implemented using h2

We're in a good place to tackle this now, against 0.2.
If anyone wants to take it on I'd suggest taking a look at both daphne and hypercorn, which both have implementations built on the h2 package.

@tomchristie How do you think this one is important? If there's no higher priority around I can give it a crack with h2.

It’d be a great addition yup!
Shouldn’t be too hard to tackle - there’s the h11 and httptools implementations to work from, plus hypercorn’s http2 support, so lots of existing work to help with getting an implementation done.

@gvbgduh - https://pypi.org/project/trustme/ may come in handy here.
Really we ought to be using that in our test cases already.
It's possible that there's some useful command line stuff we could do here, eg. something along the lines of a --mock-certificate flag that allows uvicorn to generate a mock certificate on startup. Not dug into it enough to understand exactly what'd need to happen there.

That's awesome, thanks a lot @tomchristie! I'll give it a try.

Yes, it actually works well!
It might be a bit rough yet, but the following code does the job.

@pytest.fixture()
def CA():
    yield trustme.CA()


@pytest.fixture()
def server_cert_file(CA):
    server_cert = CA.issue_cert("localtest.me").private_key_and_cert_chain_pem
    with server_cert.tempfile() as ca_temp_path:
        yield ca_temp_path


@pytest.fixture()
async def client_cert_file(CA):
    with CA.cert_pem.tempfile() as ca_temp_path:
        yield ca_temp_path


@pytest.mark.asyncio
@pytest.fixture()
async def async_test_client(client_cert_file):
    async with AsyncClient(
        http_versions=["HTTP/1.1"],
        base_url="https://localtest.me:8000",
        verify=client_cert_file,
    ) as async_test_client:
        yield async_test_client


@pytest.mark.asyncio
async def test_baseline_h11(async_test_client, CA, server_cert_file):
    config = Config(
        app=App, limit_max_requests=3, http="h11", ssl_certfile=server_cert_file
    )
    server = Server(config=config)
    config.load()
    CA.configure_trust(config.ssl)

    # Prepare the coroutine to serve the request
    run_request = server.serve()

    # Run coroutines
    results = await asyncio.gather(
        *[
            run_request,
            async_test_client.get("/"),
            async_test_client.get("/"),
            async_test_client.get("/"),
        ]
    )

Update: And httpx also speaks HTTP/2 as well now 🎉

@tomchristie I've been going round in circles with some issues/questions for a while. I'm currently implementing the simplest flow and to finish this I'd love to hear your advice/opinion about what's the best strategy to close the connection.

The matter is that the request-response is tied to the streams against the connection, and on the one hand, it's advised that

Servers are encouraged to maintain open connections for as long as
possible but are permitted to terminate idle connections if
necessary.

as of https://tools.ietf.org/html/rfc7540#section-9.1.

On the other hand, I see that in hypercorn (with _not very thorough_ debugging) a connection is closed just after all streams are done (in absence of the keep-alive policies). Shall we proceed the same way or keep them for a while being idle?

It's probably an additional question how to manage the keep-alive policy as well.

I also wonder if it's worth keeping in mind the connection reuse as of https://tools.ietf.org/html/rfc7540#section-9.1.1, but it seems to be rather a complex feature.

I don’t see any reason to differ from our HTTP/1.1 keep-alive policy.

The “keep them hanging around for as long as possible” seems like nonsense to me.

Yeah, fair enough indeed, not sure how I stuck in other thoughts, but it took to write it down to see it's quite obvious. Thanks!

Any Progress about this issue?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

HenrikOssipoff picture HenrikOssipoff  Â·  5Comments

gnat picture gnat  Â·  6Comments

unitto1 picture unitto1  Â·  7Comments

toonknapen picture toonknapen  Â·  5Comments

tomchristie picture tomchristie  Â·  6Comments