Connect to the given host and port over TCP. If the given ``host`` has multiple IP addresses associated with it, then we have a problem: which one do we use? One approach would be to attempt to connect to the first one, and then if that fails, attempt to connect to the second one .
(
host: str | bytes,
port: int,
*,
happy_eyeballs_delay: float | None = DEFAULT_DELAY,
local_address: str | None = None,
)
| 183 | # AF_INET6: "..."} |
| 184 | # this might be simpler after |
| 185 | async def open_tcp_stream( |
| 186 | host: str | bytes, |
| 187 | port: int, |
| 188 | *, |
| 189 | happy_eyeballs_delay: float | None = DEFAULT_DELAY, |
| 190 | local_address: str | None = None, |
| 191 | ) -> trio.SocketStream: |
| 192 | """Connect to the given host and port over TCP. |
| 193 | |
| 194 | If the given ``host`` has multiple IP addresses associated with it, then |
| 195 | we have a problem: which one do we use? |
| 196 | |
| 197 | One approach would be to attempt to connect to the first one, and then if |
| 198 | that fails, attempt to connect to the second one ... until we've tried all |
| 199 | of them. But the problem with this is that if the first IP address is |
| 200 | unreachable (for example, because it's an IPv6 address and our network |
| 201 | discards IPv6 packets), then we might end up waiting tens of seconds for |
| 202 | the first connection attempt to timeout before we try the second address. |
| 203 | |
| 204 | Another approach would be to attempt to connect to all of the addresses at |
| 205 | the same time, in parallel, and then use whichever connection succeeds |
| 206 | first, abandoning the others. This would be fast, but create a lot of |
| 207 | unnecessary load on the network and the remote server. |
| 208 | |
| 209 | This function strikes a balance between these two extremes: it works its |
| 210 | way through the available addresses one at a time, like the first |
| 211 | approach; but, if ``happy_eyeballs_delay`` seconds have passed and it's |
| 212 | still waiting for an attempt to succeed or fail, then it gets impatient |
| 213 | and starts the next connection attempt in parallel. As soon as any one |
| 214 | connection attempt succeeds, all the other attempts are cancelled. This |
| 215 | avoids unnecessary load because most connections will succeed after just |
| 216 | one or two attempts, but if one of the addresses is unreachable then it |
| 217 | doesn't slow us down too much. |
| 218 | |
| 219 | This is known as a "happy eyeballs" algorithm, and our particular variant |
| 220 | is modelled after how Chrome connects to webservers; see `RFC 6555 |
| 221 | <https://tools.ietf.org/html/rfc6555>`__ for more details. |
| 222 | |
| 223 | Args: |
| 224 | host (str or bytes): The host to connect to. Can be an IPv4 address, |
| 225 | IPv6 address, or a hostname. |
| 226 | |
| 227 | port (int): The port to connect to. |
| 228 | |
| 229 | happy_eyeballs_delay (float or None): How many seconds to wait for each |
| 230 | connection attempt to succeed or fail before getting impatient and |
| 231 | starting another one in parallel. Set to `None` if you want |
| 232 | to limit to only one connection attempt at a time (like |
| 233 | :func:`socket.create_connection`). Default: 0.25 (250 ms). |
| 234 | |
| 235 | local_address (None or str): The local IP address or hostname to use as |
| 236 | the source for outgoing connections. If ``None``, we let the OS pick |
| 237 | the source IP. |
| 238 | |
| 239 | This is useful in some exotic networking configurations where your |
| 240 | host has multiple IP addresses, and you want to force the use of a |
| 241 | specific one. |
| 242 |
searching dependent graphs…