Subversion Repositories qbpwcf-lib(archive)

Rev

Rev 915 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 liveuser 1
<?php
2
 
3
namespace React\Socket;
4
 
5
use Evenement\EventEmitter;
6
use React\EventLoop\LoopInterface;
7
use React\Stream\DuplexResourceStream;
8
use React\Stream\Util;
9
use React\Stream\WritableResourceStream;
10
use React\Stream\WritableStreamInterface;
11
 
12
/**
13
 * The actual connection implementation for ConnectionInterface
14
 *
15
 * This class should only be used internally, see ConnectionInterface instead.
16
 *
17
 * @see ConnectionInterface
18
 * @internal
19
 */
20
class Connection extends EventEmitter implements ConnectionInterface
21
{
22
    /**
23
     * Internal flag whether this is a Unix domain socket (UDS) connection
24
     *
25
     * @internal
26
     */
27
    public $unix = false;
28
 
29
    /**
30
     * Internal flag whether encryption has been enabled on this connection
31
     *
32
     * Mostly used by internal StreamEncryption so that connection returns
33
     * `tls://` scheme for encrypted connections instead of `tcp://`.
34
     *
35
     * @internal
36
     */
37
    public $encryptionEnabled = false;
38
 
39
    /** @internal */
40
    public $stream;
41
 
42
    private $input;
43
 
44
    public function __construct($resource, LoopInterface $loop)
45
    {
46
        // PHP < 7.3.3 (and PHP < 7.2.15) suffers from a bug where feof() might
47
        // block with 100% CPU usage on fragmented TLS records.
48
        // We try to work around this by always consuming the complete receive
49
        // buffer at once to avoid stale data in TLS buffers. This is known to
50
        // work around high CPU usage for well-behaving peers, but this may
51
        // cause very large data chunks for high throughput scenarios. The buggy
52
        // behavior can still be triggered due to network I/O buffers or
53
        // malicious peers on affected versions, upgrading is highly recommended.
54
        // @link https://bugs.php.net/bug.php?id=77390
55
        $clearCompleteBuffer = \PHP_VERSION_ID < 70215 || (\PHP_VERSION_ID >= 70300 && \PHP_VERSION_ID < 70303);
56
 
57
        // PHP < 7.1.4 (and PHP < 7.0.18) suffers from a bug when writing big
58
        // chunks of data over TLS streams at once.
59
        // We try to work around this by limiting the write chunk size to 8192
60
        // bytes for older PHP versions only.
61
        // This is only a work-around and has a noticable performance penalty on
62
        // affected versions. Please update your PHP version.
63
        // This applies to all streams because TLS may be enabled later on.
64
        // See https://github.com/reactphp/socket/issues/105
65
        $limitWriteChunks = (\PHP_VERSION_ID < 70018 || (\PHP_VERSION_ID >= 70100 && \PHP_VERSION_ID < 70104));
66
 
67
        $this->input = new DuplexResourceStream(
68
            $resource,
69
            $loop,
70
            $clearCompleteBuffer ? -1 : null,
71
            new WritableResourceStream($resource, $loop, null, $limitWriteChunks ? 8192 : null)
72
        );
73
 
74
        $this->stream = $resource;
75
 
76
        Util::forwardEvents($this->input, $this, array('data', 'end', 'error', 'close', 'pipe', 'drain'));
77
 
78
        $this->input->on('close', array($this, 'close'));
79
    }
80
 
81
    public function isReadable()
82
    {
83
        return $this->input->isReadable();
84
    }
85
 
86
    public function isWritable()
87
    {
88
        return $this->input->isWritable();
89
    }
90
 
91
    public function pause()
92
    {
93
        $this->input->pause();
94
    }
95
 
96
    public function resume()
97
    {
98
        $this->input->resume();
99
    }
100
 
101
    public function pipe(WritableStreamInterface $dest, array $options = array())
102
    {
103
        return $this->input->pipe($dest, $options);
104
    }
105
 
106
    public function write($data)
107
    {
108
        return $this->input->write($data);
109
    }
110
 
111
    public function end($data = null)
112
    {
113
        $this->input->end($data);
114
    }
115
 
116
    public function close()
117
    {
118
        $this->input->close();
119
        $this->handleClose();
120
        $this->removeAllListeners();
121
    }
122
 
123
    public function handleClose()
124
    {
125
        if (!\is_resource($this->stream)) {
126
            return;
127
        }
128
 
129
        // Try to cleanly shut down socket and ignore any errors in case other
130
        // side already closed. Shutting down may return to blocking mode on
131
        // some legacy versions, so reset to non-blocking just in case before
132
        // continuing to close the socket resource.
133
        // Underlying Stream implementation will take care of closing file
134
        // handle, so we otherwise keep this open here.
135
        @\stream_socket_shutdown($this->stream, \STREAM_SHUT_RDWR);
136
        \stream_set_blocking($this->stream, false);
137
    }
138
 
139
    public function getRemoteAddress()
140
    {
141
        if (!\is_resource($this->stream)) {
142
            return null;
143
        }
144
 
145
        return $this->parseAddress(\stream_socket_get_name($this->stream, true));
146
    }
147
 
148
    public function getLocalAddress()
149
    {
150
        if (!\is_resource($this->stream)) {
151
            return null;
152
        }
153
 
154
        return $this->parseAddress(\stream_socket_get_name($this->stream, false));
155
    }
156
 
157
    private function parseAddress($address)
158
    {
159
        if ($address === false) {
160
            return null;
161
        }
162
 
163
        if ($this->unix) {
164
            // remove trailing colon from address for HHVM < 3.19: https://3v4l.org/5C1lo
165
            // note that technically ":" is a valid address, so keep this in place otherwise
166
            if (\substr($address, -1) === ':' && \defined('HHVM_VERSION_ID') && \HHVM_VERSION_ID < 31900) {
167
                $address = (string)\substr($address, 0, -1); // @codeCoverageIgnore
168
            }
169
 
170
            // work around unknown addresses should return null value: https://3v4l.org/5C1lo and https://bugs.php.net/bug.php?id=74556
171
            // PHP uses "\0" string and HHVM uses empty string (colon removed above)
172
            if ($address === '' || $address[0] === "\x00" ) {
173
                return null; // @codeCoverageIgnore
174
            }
175
 
176
            return 'unix://' . $address;
177
        }
178
 
179
        // check if this is an IPv6 address which includes multiple colons but no square brackets
180
        $pos = \strrpos($address, ':');
181
        if ($pos !== false && \strpos($address, ':') < $pos && \substr($address, 0, 1) !== '[') {
182
            $address = '[' . \substr($address, 0, $pos) . ']:' . \substr($address, $pos + 1); // @codeCoverageIgnore
183
        }
184
 
185
        return ($this->encryptionEnabled ? 'tls' : 'tcp') . '://' . $address;
186
    }
187
}