friendship ended with social-app. php is my new best friend
1<?php
2
3namespace React\Socket;
4
5use Evenement\EventEmitter;
6use Exception;
7use OverflowException;
8
9/**
10 * The `LimitingServer` decorator wraps a given `ServerInterface` and is responsible
11 * for limiting and keeping track of open connections to this server instance.
12 *
13 * Whenever the underlying server emits a `connection` event, it will check its
14 * limits and then either
15 * - keep track of this connection by adding it to the list of
16 * open connections and then forward the `connection` event
17 * - or reject (close) the connection when its limits are exceeded and will
18 * forward an `error` event instead.
19 *
20 * Whenever a connection closes, it will remove this connection from the list of
21 * open connections.
22 *
23 * ```php
24 * $server = new React\Socket\LimitingServer($server, 100);
25 * $server->on('connection', function (React\Socket\ConnectionInterface $connection) {
26 * $connection->write('hello there!' . PHP_EOL);
27 * …
28 * });
29 * ```
30 *
31 * See also the `ServerInterface` for more details.
32 *
33 * @see ServerInterface
34 * @see ConnectionInterface
35 */
36class LimitingServer extends EventEmitter implements ServerInterface
37{
38 private $connections = array();
39 private $server;
40 private $limit;
41
42 private $pauseOnLimit = false;
43 private $autoPaused = false;
44 private $manuPaused = false;
45
46 /**
47 * Instantiates a new LimitingServer.
48 *
49 * You have to pass a maximum number of open connections to ensure
50 * the server will automatically reject (close) connections once this limit
51 * is exceeded. In this case, it will emit an `error` event to inform about
52 * this and no `connection` event will be emitted.
53 *
54 * ```php
55 * $server = new React\Socket\LimitingServer($server, 100);
56 * $server->on('connection', function (React\Socket\ConnectionInterface $connection) {
57 * $connection->write('hello there!' . PHP_EOL);
58 * …
59 * });
60 * ```
61 *
62 * You MAY pass a `null` limit in order to put no limit on the number of
63 * open connections and keep accepting new connection until you run out of
64 * operating system resources (such as open file handles). This may be
65 * useful if you do not want to take care of applying a limit but still want
66 * to use the `getConnections()` method.
67 *
68 * You can optionally configure the server to pause accepting new
69 * connections once the connection limit is reached. In this case, it will
70 * pause the underlying server and no longer process any new connections at
71 * all, thus also no longer closing any excessive connections.
72 * The underlying operating system is responsible for keeping a backlog of
73 * pending connections until its limit is reached, at which point it will
74 * start rejecting further connections.
75 * Once the server is below the connection limit, it will continue consuming
76 * connections from the backlog and will process any outstanding data on
77 * each connection.
78 * This mode may be useful for some protocols that are designed to wait for
79 * a response message (such as HTTP), but may be less useful for other
80 * protocols that demand immediate responses (such as a "welcome" message in
81 * an interactive chat).
82 *
83 * ```php
84 * $server = new React\Socket\LimitingServer($server, 100, true);
85 * $server->on('connection', function (React\Socket\ConnectionInterface $connection) {
86 * $connection->write('hello there!' . PHP_EOL);
87 * …
88 * });
89 * ```
90 *
91 * @param ServerInterface $server
92 * @param int|null $connectionLimit
93 * @param bool $pauseOnLimit
94 */
95 public function __construct(ServerInterface $server, $connectionLimit, $pauseOnLimit = false)
96 {
97 $this->server = $server;
98 $this->limit = $connectionLimit;
99 if ($connectionLimit !== null) {
100 $this->pauseOnLimit = $pauseOnLimit;
101 }
102
103 $this->server->on('connection', array($this, 'handleConnection'));
104 $this->server->on('error', array($this, 'handleError'));
105 }
106
107 /**
108 * Returns an array with all currently active connections
109 *
110 * ```php
111 * foreach ($server->getConnection() as $connection) {
112 * $connection->write('Hi!');
113 * }
114 * ```
115 *
116 * @return ConnectionInterface[]
117 */
118 public function getConnections()
119 {
120 return $this->connections;
121 }
122
123 public function getAddress()
124 {
125 return $this->server->getAddress();
126 }
127
128 public function pause()
129 {
130 if (!$this->manuPaused) {
131 $this->manuPaused = true;
132
133 if (!$this->autoPaused) {
134 $this->server->pause();
135 }
136 }
137 }
138
139 public function resume()
140 {
141 if ($this->manuPaused) {
142 $this->manuPaused = false;
143
144 if (!$this->autoPaused) {
145 $this->server->resume();
146 }
147 }
148 }
149
150 public function close()
151 {
152 $this->server->close();
153 }
154
155 /** @internal */
156 public function handleConnection(ConnectionInterface $connection)
157 {
158 // close connection if limit exceeded
159 if ($this->limit !== null && \count($this->connections) >= $this->limit) {
160 $this->handleError(new \OverflowException('Connection closed because server reached connection limit'));
161 $connection->close();
162 return;
163 }
164
165 $this->connections[] = $connection;
166 $that = $this;
167 $connection->on('close', function () use ($that, $connection) {
168 $that->handleDisconnection($connection);
169 });
170
171 // pause accepting new connections if limit exceeded
172 if ($this->pauseOnLimit && !$this->autoPaused && \count($this->connections) >= $this->limit) {
173 $this->autoPaused = true;
174
175 if (!$this->manuPaused) {
176 $this->server->pause();
177 }
178 }
179
180 $this->emit('connection', array($connection));
181 }
182
183 /** @internal */
184 public function handleDisconnection(ConnectionInterface $connection)
185 {
186 unset($this->connections[\array_search($connection, $this->connections)]);
187
188 // continue accepting new connection if below limit
189 if ($this->autoPaused && \count($this->connections) < $this->limit) {
190 $this->autoPaused = false;
191
192 if (!$this->manuPaused) {
193 $this->server->resume();
194 }
195 }
196 }
197
198 /** @internal */
199 public function handleError(\Exception $error)
200 {
201 $this->emit('error', array($error));
202 }
203}