uvloop icon indicating copy to clipboard operation
uvloop copied to clipboard

Missing support for `interleave` and `happy_eyeballs_delay` in `create_connection`

Open vuori777 opened this issue 4 years ago • 6 comments

  • uvloop version: 0.15.2
  • Python version: 3.8.5
  • Platform: Ubuntu 20.04 amd64
  • Can you reproduce the bug with PYTHONASYNCIODEBUG in env?: Not tested.
  • Does uvloop behave differently from vanilla asyncio? How?: Yes, fails if interleave and/or happy_eyeballs_delay is set with create_connection.

asyncio loop.create_connection supports the interleave and happy_eyeballs_delay keyword arguments to enable RFC 8305 "Happy Eyeballs" IPv6-to-IPv4 fallback since Python 3.8. The arguments are documented here: https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.create_connection

The corresponding function in uvloop does not accept these arguments. These would be nice to have because I'm working on a server that proxies requests to external third-party servers which occasionally have a broken IPv6 configuration and traditional connect timeout behavior causes quite a bit of delay.

vuori777 avatar Mar 22 '21 12:03 vuori777

This looks interesting to me. I've dug around for a few days now, but I've never worked with the cython.

I've read this RFC https://tools.ietf.org/html/rfc8305.html and basically try to backport this https://github.com/python/cpython/pull/7237/files

If someone has any tips or guidance I would really appreciate that.

paulefoe avatar Apr 10 '21 03:04 paulefoe

I haven't implemented this myself and am also unfamiliar with Cython so can't really help. We managed to work around the problem with selective application of native asyncio, since this problem only affected a relative low-traffic portion of the app.

vuori777 avatar Apr 13 '21 11:04 vuori777

Hey @1st1 @fantix I think I really want to dig into this and implement it, but I want to double-check if it's something you will be willing to accept?

paulefoe avatar Apr 27 '21 02:04 paulefoe

We're obviously willing to accept such a PR.

1st1 avatar Aug 12 '21 17:08 1st1

Seems like this is doable. From what I've seen, the scope of the feature would apply to changes to loop.pyx and loop.pyi. Hypothetically, it might need to satisfy:

import uvloop
import asyncio
from typing import *

async def leave_happy(
        host, 
        port,  
        happy_eyeballs_delay: Optional[float] = None, 
        interleave: Optional[int] = None):

    if interleave is not None:
        if not interleave & 1:
            raise ValueError("`interleave` argument must be non-negative")
    else:
        interleave = 0

    loop = asyncio.get_running_loop()
    
    (transp, pro) = await loop.create_connection(
            uvloop.EventLoopPolicy, 
            host=host, 
            port=port,
            happy_eyeballs_delay=happy_eyeballs_delay,
            interleave=interleave
    )
    
    return (transp, pro)

async def main():
    await leave_happy(host="127.0.0.1", port=2160)

asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
asyncio.run(main())

dmgolembiowski avatar Oct 20 '21 23:10 dmgolembiowski

We're obviously willing to accept such a PR.

I briefly compared Lib/asyncio/base_events.py against uvloop/handles/handle.pyx, and believe I found the appropriate entrypoint to inject this logic. Since loop.create_server shares a similar signature with loop.create_connection, does it seem valid to apply this here?

dmgolembiowski avatar Oct 21 '21 02:10 dmgolembiowski