Subversion Repositories php-qbpwcf

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
3 liveuser 1
<?php
2
 
3
namespace React\Socket;
4
 
5
use Evenement\EventEmitter;
6
use Exception;
7
use 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
 */
36
class 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
}