friendship ended with social-app. php is my new best friend
1<?php 2 3namespace React\Http\Io; 4 5use Psr\Http\Message\UriInterface; 6use React\EventLoop\LoopInterface; 7use React\EventLoop\TimerInterface; 8use React\Promise\PromiseInterface; 9use React\Socket\ConnectionInterface; 10use React\Socket\ConnectorInterface; 11 12/** 13 * [Internal] Manages outgoing HTTP connections for the HTTP client 14 * 15 * @internal 16 * @final 17 */ 18class ClientConnectionManager 19{ 20 /** @var ConnectorInterface */ 21 private $connector; 22 23 /** @var LoopInterface */ 24 private $loop; 25 26 /** @var string[] */ 27 private $idleUris = array(); 28 29 /** @var ConnectionInterface[] */ 30 private $idleConnections = array(); 31 32 /** @var TimerInterface[] */ 33 private $idleTimers = array(); 34 35 /** @var \Closure[] */ 36 private $idleStreamHandlers = array(); 37 38 /** @var float */ 39 private $maximumTimeToKeepAliveIdleConnection = 0.001; 40 41 public function __construct(ConnectorInterface $connector, LoopInterface $loop) 42 { 43 $this->connector = $connector; 44 $this->loop = $loop; 45 } 46 47 /** 48 * @return PromiseInterface<ConnectionInterface> 49 */ 50 public function connect(UriInterface $uri) 51 { 52 $scheme = $uri->getScheme(); 53 if ($scheme !== 'https' && $scheme !== 'http') { 54 return \React\Promise\reject(new \InvalidArgumentException( 55 'Invalid request URL given' 56 )); 57 } 58 59 $port = $uri->getPort(); 60 if ($port === null) { 61 $port = $scheme === 'https' ? 443 : 80; 62 } 63 $uri = ($scheme === 'https' ? 'tls://' : '') . $uri->getHost() . ':' . $port; 64 65 // Reuse idle connection for same URI if available 66 foreach ($this->idleConnections as $id => $connection) { 67 if ($this->idleUris[$id] === $uri) { 68 assert($this->idleStreamHandlers[$id] instanceof \Closure); 69 $connection->removeListener('close', $this->idleStreamHandlers[$id]); 70 $connection->removeListener('data', $this->idleStreamHandlers[$id]); 71 $connection->removeListener('error', $this->idleStreamHandlers[$id]); 72 73 assert($this->idleTimers[$id] instanceof TimerInterface); 74 $this->loop->cancelTimer($this->idleTimers[$id]); 75 unset($this->idleUris[$id], $this->idleConnections[$id], $this->idleTimers[$id], $this->idleStreamHandlers[$id]); 76 77 return \React\Promise\resolve($connection); 78 } 79 } 80 81 // Create new connection if no idle connection to same URI is available 82 return $this->connector->connect($uri); 83 } 84 85 /** 86 * Hands back an idle connection to the connection manager for possible future reuse. 87 * 88 * @return void 89 */ 90 public function keepAlive(UriInterface $uri, ConnectionInterface $connection) 91 { 92 $scheme = $uri->getScheme(); 93 assert($scheme === 'https' || $scheme === 'http'); 94 95 $port = $uri->getPort(); 96 if ($port === null) { 97 $port = $scheme === 'https' ? 443 : 80; 98 } 99 100 $this->idleUris[] = ($scheme === 'https' ? 'tls://' : '') . $uri->getHost() . ':' . $port; 101 $this->idleConnections[] = $connection; 102 103 $that = $this; 104 $cleanUp = function () use ($connection, $that) { 105 // call public method to support legacy PHP 5.3 106 $that->cleanUpConnection($connection); 107 }; 108 109 // clean up and close connection when maximum time to keep-alive idle connection has passed 110 $this->idleTimers[] = $this->loop->addTimer($this->maximumTimeToKeepAliveIdleConnection, $cleanUp); 111 112 // clean up and close connection when unexpected close/data/error event happens during idle time 113 $this->idleStreamHandlers[] = $cleanUp; 114 $connection->on('close', $cleanUp); 115 $connection->on('data', $cleanUp); 116 $connection->on('error', $cleanUp); 117 } 118 119 /** 120 * @internal 121 * @return void 122 */ 123 public function cleanUpConnection(ConnectionInterface $connection) // private (PHP 5.4+) 124 { 125 $id = \array_search($connection, $this->idleConnections, true); 126 if ($id === false) { 127 return; 128 } 129 130 assert(\is_int($id)); 131 assert($this->idleTimers[$id] instanceof TimerInterface); 132 $this->loop->cancelTimer($this->idleTimers[$id]); 133 unset($this->idleUris[$id], $this->idleConnections[$id], $this->idleTimers[$id], $this->idleStreamHandlers[$id]); 134 135 $connection->close(); 136 } 137}