# flare.dns.async_resolve

> A module of flare_mlake's API.

DNS resolution on a separate thread + happy-eyeballs ordering.

``getaddrinfo(3)`` is a blocking syscall with no async variant on the
platforms flare targets. :func:`resolve_async` runs it on a fresh pool
thread (the pthread mechanism :func:`block_in_pool` uses) **and waits
for that thread**: the caller is blocked for the whole lookup, exactly
as with :func:`flare.dns.resolve`. What it adds is a separate stack and
a :class:`Cancel` check before the lookup starts and after it returns.
It does not free a reactor thread to serve other connections while the
lookup runs, and a cancel that arrives mid-lookup takes effect only
when ``getaddrinfo`` returns. Errors come back with the resolver's
message (``DnsError(host): reason``) as a plain ``Error``, which is
what a raised ``DnsError`` is by the time any caller sees it.

:func:`order_happy_eyeballs` reorders a resolved address list into the
RFC 8305 connection-attempt order (interleave IPv6 / IPv4, starting with
the family of the first address) so a dialer can race families without one
stalling the other.

The worker thread is joined (the public API is synchronous --
the submitter waits for the result anyway), so a flipped ``cancel`` is
honored at the pre-flight and post-flight boundaries, the same contract
as ``block_in_pool``. A truly fire-and-forget resolve that returns the
reactor to its loop while the lookup runs would need reactor-side
completion wiring (eventfd/pipe wakeup) instead.

## Functions

### `resolve_async`

```mojo
def resolve_async(host: String, cancel: Cancel) -> List[IpAddr]
```

Resolve ``host`` on a pool thread and wait for it.

Same result as :func:`flare.dns.resolve`, and it blocks the caller
just as long: the ``getaddrinfo`` call runs on a fresh kernel thread
that this function joins. A flipped ``cancel`` is honoured before
the thread starts and after it finishes, not during the lookup.

Args:

- `host`: Hostname or numeric IP string.
- `cancel`: Per-request cancel token (use ``Cancel.never()`` for an
    uncancellable call).

Raises: AddressParseError: empty ``host``.
DnsError / Error: resolver failure (propagated from the worker),
or cancellation.

### `order_happy_eyeballs`

```mojo
def order_happy_eyeballs(addrs: List[IpAddr]) -> List[IpAddr]
```

Reorder ``addrs`` into RFC 8305 connection-attempt order.

Interleaves the two address families, **starting with the family of
``addrs[0]``** (the resolver's most-preferred address, RFC 8305 §4:
"whichever address family is first in the list should be followed by an
address of the other address family"), preserving each family's relative
order: ``[v6, v4, v6, v4, ...]`` for an IPv6-first input and
``[v4, v6, v4, v6, ...]`` for an IPv4-first one. A dialer can then race
the two families without one family's slow first address starving the
other, and the first attempt still goes to the address the OS ranked
highest. Returns a new list; the input is unchanged.
