<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet"
        integrity="sha384-GLhlTQ8iRABdZLl6O3oVMWSktQOp6b7In1Zl3/Jr59b6EGGoI1aFkw7cmDA6j6gD" crossorigin="anonymous">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.3.0/css/all.min.css"
        integrity="sha512-SzlrxWUlpfuzQ+pcUCosxcglQRNAq/DZjVsC0lE40xsADsfeQoEypE+enwcOiGjk/bSuGGKHEyjSoQ1zVisanQ=="
        crossorigin="anonymous" referrerpolicy="no-referrer" />
</head>
</html>
<?php

namespace RectorPrefix202411\React\Dns\Query;

use RectorPrefix202411\React\Promise\Promise;
/**
 * Send DNS queries over a UDP or TCP/IP stream transport.
 *
 * This class will automatically choose the correct transport protocol to send
 * a DNS query to your DNS server. It will always try to send it over the more
 * efficient UDP transport first. If this query yields a size related issue
 * (truncated messages), it will retry over a streaming TCP/IP transport.
 *
 * For more advanced usages one can utilize this class directly.
 * The following example looks up the `IPv6` address for `reactphp.org`.
 *
 * ```php
 * $executor = new SelectiveTransportExecutor($udpExecutor, $tcpExecutor);
 *
 * $executor->query(
 *     new Query($name, Message::TYPE_AAAA, Message::CLASS_IN)
 * )->then(function (Message $message) {
 *     foreach ($message->answers as $answer) {
 *         echo 'IPv6: ' . $answer->data . PHP_EOL;
 *     }
 * }, 'printf');
 * ```
 *
 * Note that this executor only implements the logic to select the correct
 * transport for the given DNS query. Implementing the correct transport logic,
 * implementing timeouts and any retry logic is left up to the given executors,
 * see also [`UdpTransportExecutor`](#udptransportexecutor) and
 * [`TcpTransportExecutor`](#tcptransportexecutor) for more details.
 *
 * Note that this executor is entirely async and as such allows you to execute
 * any number of queries concurrently. You should probably limit the number of
 * concurrent queries in your application or you're very likely going to face
 * rate limitations and bans on the resolver end. For many common applications,
 * you may want to avoid sending the same query multiple times when the first
 * one is still pending, so you will likely want to use this in combination with
 * a `CoopExecutor` like this:
 *
 * ```php
 * $executor = new CoopExecutor(
 *     new SelectiveTransportExecutor(
 *         $datagramExecutor,
 *         $streamExecutor
 *     )
 * );
 * ```
 */
class SelectiveTransportExecutor implements ExecutorInterface
{
    private $datagramExecutor;
    private $streamExecutor;
    public function __construct(ExecutorInterface $datagramExecutor, ExecutorInterface $streamExecutor)
    {
        $this->datagramExecutor = $datagramExecutor;
        $this->streamExecutor = $streamExecutor;
    }
    public function query(Query $query)
    {
        $stream = $this->streamExecutor;
        $pending = $this->datagramExecutor->query($query);
        return new Promise(function ($resolve, $reject) use(&$pending, $stream, $query) {
            $pending->then($resolve, function ($e) use(&$pending, $stream, $query, $resolve, $reject) {
                if ($e->getCode() === (\defined('SOCKET_EMSGSIZE') ? \SOCKET_EMSGSIZE : 90)) {
                    $pending = $stream->query($query)->then($resolve, $reject);
                } else {
                    $reject($e);
                }
            });
        }, function () use(&$pending) {
            $pending->cancel();
            $pending = null;
        });
    }
}
