SelectiveTransportExecutor.php 3.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. <?php
  2. namespace React\Dns\Query;
  3. use React\Promise\Promise;
  4. /**
  5. * Send DNS queries over a UDP or TCP/IP stream transport.
  6. *
  7. * This class will automatically choose the correct transport protocol to send
  8. * a DNS query to your DNS server. It will always try to send it over the more
  9. * efficient UDP transport first. If this query yields a size related issue
  10. * (truncated messages), it will retry over a streaming TCP/IP transport.
  11. *
  12. * For more advanced usages one can utilize this class directly.
  13. * The following example looks up the `IPv6` address for `reactphp.org`.
  14. *
  15. * ```php
  16. * $executor = new SelectiveTransportExecutor($udpExecutor, $tcpExecutor);
  17. *
  18. * $executor->query(
  19. * new Query($name, Message::TYPE_AAAA, Message::CLASS_IN)
  20. * )->then(function (Message $message) {
  21. * foreach ($message->answers as $answer) {
  22. * echo 'IPv6: ' . $answer->data . PHP_EOL;
  23. * }
  24. * }, 'printf');
  25. * ```
  26. *
  27. * Note that this executor only implements the logic to select the correct
  28. * transport for the given DNS query. Implementing the correct transport logic,
  29. * implementing timeouts and any retry logic is left up to the given executors,
  30. * see also [`UdpTransportExecutor`](#udptransportexecutor) and
  31. * [`TcpTransportExecutor`](#tcptransportexecutor) for more details.
  32. *
  33. * Note that this executor is entirely async and as such allows you to execute
  34. * any number of queries concurrently. You should probably limit the number of
  35. * concurrent queries in your application or you're very likely going to face
  36. * rate limitations and bans on the resolver end. For many common applications,
  37. * you may want to avoid sending the same query multiple times when the first
  38. * one is still pending, so you will likely want to use this in combination with
  39. * a `CoopExecutor` like this:
  40. *
  41. * ```php
  42. * $executor = new CoopExecutor(
  43. * new SelectiveTransportExecutor(
  44. * $datagramExecutor,
  45. * $streamExecutor
  46. * )
  47. * );
  48. * ```
  49. */
  50. class SelectiveTransportExecutor implements ExecutorInterface
  51. {
  52. private $datagramExecutor;
  53. private $streamExecutor;
  54. public function __construct(ExecutorInterface $datagramExecutor, ExecutorInterface $streamExecutor)
  55. {
  56. $this->datagramExecutor = $datagramExecutor;
  57. $this->streamExecutor = $streamExecutor;
  58. }
  59. public function query(Query $query)
  60. {
  61. $stream = $this->streamExecutor;
  62. $pending = $this->datagramExecutor->query($query);
  63. return new Promise(function ($resolve, $reject) use (&$pending, $stream, $query) {
  64. $pending->then(
  65. $resolve,
  66. function ($e) use (&$pending, $stream, $query, $resolve, $reject) {
  67. if ($e->getCode() === (\defined('SOCKET_EMSGSIZE') ? \SOCKET_EMSGSIZE : 90)) {
  68. $pending = $stream->query($query)->then($resolve, $reject);
  69. } else {
  70. $reject($e);
  71. }
  72. }
  73. );
  74. }, function () use (&$pending) {
  75. $pending->cancel();
  76. $pending = null;
  77. });
  78. }
  79. }