A stateless implementation of the "Happy Eyeballs" algorithm. "Happy Eyeballs" is documented in RFC6555 as the recommended practice for when both IPv4 and IPv6 addresses are available. In this implementation, we partition the addresses by family, and make the first connection attem
| 35 | |
| 36 | |
| 37 | class _Connector(object): |
| 38 | """A stateless implementation of the "Happy Eyeballs" algorithm. |
| 39 | |
| 40 | "Happy Eyeballs" is documented in RFC6555 as the recommended practice |
| 41 | for when both IPv4 and IPv6 addresses are available. |
| 42 | |
| 43 | In this implementation, we partition the addresses by family, and |
| 44 | make the first connection attempt to whichever address was |
| 45 | returned first by ``getaddrinfo``. If that connection fails or |
| 46 | times out, we begin a connection in parallel to the first address |
| 47 | of the other family. If there are additional failures we retry |
| 48 | with other addresses, keeping one connection attempt per family |
| 49 | in flight at a time. |
| 50 | |
| 51 | http://tools.ietf.org/html/rfc6555 |
| 52 | |
| 53 | """ |
| 54 | |
| 55 | def __init__( |
| 56 | self, |
| 57 | addrinfo: List[Tuple], |
| 58 | connect: Callable[ |
| 59 | [socket.AddressFamily, Tuple], Tuple[IOStream, "Future[IOStream]"] |
| 60 | ], |
| 61 | ) -> None: |
| 62 | self.io_loop = IOLoop.current() |
| 63 | self.connect = connect |
| 64 | |
| 65 | self.future = ( |
| 66 | Future() |
| 67 | ) # type: Future[Tuple[socket.AddressFamily, Any, IOStream]] |
| 68 | self.timeout = None # type: Optional[object] |
| 69 | self.connect_timeout = None # type: Optional[object] |
| 70 | self.last_error = None # type: Optional[Exception] |
| 71 | self.remaining = len(addrinfo) |
| 72 | self.primary_addrs, self.secondary_addrs = self.split(addrinfo) |
| 73 | self.streams = set() # type: Set[IOStream] |
| 74 | |
| 75 | @staticmethod |
| 76 | def split( |
| 77 | addrinfo: List[Tuple], |
| 78 | ) -> Tuple[ |
| 79 | List[Tuple[socket.AddressFamily, Tuple]], |
| 80 | List[Tuple[socket.AddressFamily, Tuple]], |
| 81 | ]: |
| 82 | """Partition the ``addrinfo`` list by address family. |
| 83 | |
| 84 | Returns two lists. The first list contains the first entry from |
| 85 | ``addrinfo`` and all others with the same family, and the |
| 86 | second list contains all other addresses (normally one list will |
| 87 | be AF_INET and the other AF_INET6, although non-standard resolvers |
| 88 | may return additional families). |
| 89 | """ |
| 90 | primary = [] |
| 91 | secondary = [] |
| 92 | primary_af = addrinfo[0][0] |
| 93 | for af, addr in addrinfo: |
| 94 | if af == primary_af: |
no outgoing calls