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}