HappyEyeBallsConnector.php 2.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980
  1. <?php
  2. namespace React\Socket;
  3. use React\Dns\Resolver\ResolverInterface;
  4. use React\EventLoop\Loop;
  5. use React\EventLoop\LoopInterface;
  6. use React\Promise;
  7. final class HappyEyeBallsConnector implements ConnectorInterface
  8. {
  9. private $loop;
  10. private $connector;
  11. private $resolver;
  12. /**
  13. * @param ?LoopInterface $loop
  14. * @param ConnectorInterface $connector
  15. * @param ResolverInterface $resolver
  16. */
  17. public function __construct($loop = null, $connector = null, $resolver = null)
  18. {
  19. // $connector and $resolver arguments are actually required, marked
  20. // optional for technical reasons only. Nullable $loop without default
  21. // requires PHP 7.1, null default is also supported in legacy PHP
  22. // versions, but required parameters are not allowed after arguments
  23. // with null default. Mark all parameters optional and check accordingly.
  24. if ($loop !== null && !$loop instanceof LoopInterface) { // manual type check to support legacy PHP < 7.1
  25. throw new \InvalidArgumentException('Argument #1 ($loop) expected null|React\EventLoop\LoopInterface');
  26. }
  27. if (!$connector instanceof ConnectorInterface) { // manual type check to support legacy PHP < 7.1
  28. throw new \InvalidArgumentException('Argument #2 ($connector) expected React\Socket\ConnectorInterface');
  29. }
  30. if (!$resolver instanceof ResolverInterface) { // manual type check to support legacy PHP < 7.1
  31. throw new \InvalidArgumentException('Argument #3 ($resolver) expected React\Dns\Resolver\ResolverInterface');
  32. }
  33. $this->loop = $loop ?: Loop::get();
  34. $this->connector = $connector;
  35. $this->resolver = $resolver;
  36. }
  37. public function connect($uri)
  38. {
  39. $original = $uri;
  40. if (\strpos($uri, '://') === false) {
  41. $uri = 'tcp://' . $uri;
  42. $parts = \parse_url($uri);
  43. if (isset($parts['scheme'])) {
  44. unset($parts['scheme']);
  45. }
  46. } else {
  47. $parts = \parse_url($uri);
  48. }
  49. if (!$parts || !isset($parts['host'])) {
  50. return Promise\reject(new \InvalidArgumentException(
  51. 'Given URI "' . $original . '" is invalid (EINVAL)',
  52. \defined('SOCKET_EINVAL') ? \SOCKET_EINVAL : (\defined('PCNTL_EINVAL') ? \PCNTL_EINVAL : 22)
  53. ));
  54. }
  55. $host = \trim($parts['host'], '[]');
  56. // skip DNS lookup / URI manipulation if this URI already contains an IP
  57. if (@\inet_pton($host) !== false) {
  58. return $this->connector->connect($original);
  59. }
  60. $builder = new HappyEyeBallsConnectionBuilder(
  61. $this->loop,
  62. $this->connector,
  63. $this->resolver,
  64. $uri,
  65. $host,
  66. $parts
  67. );
  68. return $builder->connect();
  69. }
  70. }