Search code examples
pythonsslpython-asyncioalpn

How to retrieve the selected ALPN protocol from an asyncio stream?


I am writing a HTTP client in Python. In order to select the correct HTTP version, I use ALPN.
While there is an easy and documented way to get the selected protocol from a ssl-wrapped socket (SSLSocket.selected_alpn_protocol()), I have found no official way to do this for asyncio streams.

I am quite new to asyncio, so maybe I have completely misunderstood the concept, but this is still puzzling. Although I have found a way to retrieve the selected protocol, it's too complex to be the official way. Additionally, I have found little to no information on this subject online.

I've found the following:

writer.transport._ssl_protocol._sslobj.selected_alpn_protocol()
reader._transport._ssl_protocol._sslpipe.ssl_object.selected_alpn_protocol()

These sometimes don't work though, so I have no idea whether this is version specific or I'm just doing something completely wrong.

Code to reproduce

import asyncio
import ssl

async def test_alpn():
    ctx = ssl.create_default_context()
    ctx.set_alpn_protocols(["http/1.1", "h2"])
    reader, writer = await asyncio.open_connection('example.org', 443, ssl = ctx, server_hostname = 'example.org')
    return writer.transport._ssl_protocol._sslobj.selected_alpn_protocol()

print(asyncio.run(test_alpn()))

Solution

  • I found this out way too late, but use the get_extra_info() function for this. Extract the SSL object using

    writer.transport.get_extra_info("ssl_object")
    

    and then call the selected_alpn_protocol() function on it.

    This is certainly better than messing around with asyncio's internals, as I did originally.