This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
| 1 | <?php |
||
| 2 | |||
| 3 | namespace Helix\Socket\WebSocket; |
||
| 4 | |||
| 5 | use Helix\Socket\ReactiveInterface; |
||
| 6 | use Helix\Socket\StreamClient; |
||
| 7 | use JsonException; |
||
| 8 | use Throwable; |
||
| 9 | |||
| 10 | /** |
||
| 11 | * Wraps a WebSocket peer. |
||
| 12 | * |
||
| 13 | * @see https://tools.ietf.org/html/rfc6455 |
||
| 14 | */ |
||
| 15 | class WebSocketClient extends StreamClient implements ReactiveInterface { |
||
| 16 | |||
| 17 | /** |
||
| 18 | * The peer has connected but hasn't negotiated a session yet. |
||
| 19 | */ |
||
| 20 | const STATE_HANDSHAKE = 0; |
||
| 21 | |||
| 22 | /** |
||
| 23 | * The session is active and the client can perform frame I/O with the peer. |
||
| 24 | */ |
||
| 25 | const STATE_OK = 1; |
||
| 26 | |||
| 27 | /** |
||
| 28 | * The peer has disconnected. |
||
| 29 | */ |
||
| 30 | const STATE_CLOSED = 2; |
||
| 31 | |||
| 32 | /** |
||
| 33 | * @var FrameHandler |
||
| 34 | */ |
||
| 35 | protected $frameHandler; |
||
| 36 | |||
| 37 | /** |
||
| 38 | * @var Handshake |
||
| 39 | */ |
||
| 40 | protected $handshake; |
||
| 41 | |||
| 42 | /** |
||
| 43 | * @var WebSocketServer |
||
| 44 | */ |
||
| 45 | protected $server; |
||
| 46 | |||
| 47 | /** |
||
| 48 | * @var int |
||
| 49 | */ |
||
| 50 | protected $state = self::STATE_HANDSHAKE; |
||
| 51 | |||
| 52 | /** |
||
| 53 | * @param resource $resource |
||
| 54 | * @param WebSocketServer $server |
||
| 55 | */ |
||
| 56 | public function __construct ($resource, WebSocketServer $server) { |
||
| 57 | parent::__construct($resource); |
||
| 58 | $this->server = $server; |
||
| 59 | $this->handshake = new Handshake($this); |
||
| 60 | $this->frameHandler = new FrameHandler($this); |
||
| 61 | } |
||
| 62 | |||
| 63 | /** |
||
| 64 | * Closes, optionally with a code and reason sent to the peer. |
||
| 65 | * |
||
| 66 | * https://tools.ietf.org/html/rfc6455#section-5.5.1 |
||
| 67 | * > The application MUST NOT send any more data frames after sending a |
||
| 68 | * > Close frame. |
||
| 69 | * > |
||
| 70 | * > After both sending and receiving a Close message, an endpoint |
||
| 71 | * > considers the WebSocket connection closed and MUST close the |
||
| 72 | * > underlying TCP connection. |
||
| 73 | * |
||
| 74 | * https://tools.ietf.org/html/rfc6455#section-7.4.2 |
||
| 75 | * > Status codes in the range 0-999 are not used. |
||
| 76 | * |
||
| 77 | * @param int|null $code Sent to the peer if >= 1000 |
||
| 78 | * @param string $reason Sent to the peer, if code is >= 1000 |
||
| 79 | * @return $this |
||
| 80 | */ |
||
| 81 | public function close (int $code = null, string $reason = '') { |
||
| 82 | try { |
||
| 83 | if ($code >= 1000 and $this->isOk()) { |
||
| 84 | $this->frameHandler->writeClose($code, $reason); |
||
| 85 | } |
||
| 86 | } |
||
| 87 | finally { |
||
| 88 | $this->server->remove($this); |
||
| 89 | parent::close(); |
||
| 90 | $this->state = self::STATE_CLOSED; |
||
| 91 | } |
||
| 92 | return $this; |
||
| 93 | } |
||
| 94 | |||
| 95 | /** |
||
| 96 | * @return FrameHandler |
||
| 97 | */ |
||
| 98 | public function getFrameHandler (): FrameHandler { |
||
| 99 | return $this->frameHandler; |
||
| 100 | } |
||
| 101 | |||
| 102 | /** |
||
| 103 | * @return WebSocketServer |
||
| 104 | */ |
||
| 105 | public function getServer (): WebSocketServer { |
||
| 106 | return $this->server; |
||
| 107 | } |
||
| 108 | |||
| 109 | /** |
||
| 110 | * @return int |
||
| 111 | */ |
||
| 112 | public function getState (): int { |
||
| 113 | return $this->state; |
||
| 114 | } |
||
| 115 | |||
| 116 | final public function isNegotiating (): bool { |
||
| 117 | return $this->state === self::STATE_HANDSHAKE; |
||
| 118 | } |
||
| 119 | |||
| 120 | /** |
||
| 121 | * @return bool |
||
| 122 | */ |
||
| 123 | final public function isOk (): bool { |
||
| 124 | return $this->state === self::STATE_OK; |
||
| 125 | } |
||
| 126 | |||
| 127 | /** |
||
| 128 | * Called when a complete `BINARY` payload is received from the peer. |
||
| 129 | * |
||
| 130 | * Throws by default. |
||
| 131 | * |
||
| 132 | * @param string $binary |
||
| 133 | * @throws WebSocketError |
||
| 134 | */ |
||
| 135 | public function onBinary (string $binary): void { |
||
| 136 | unset($binary); |
||
| 137 | throw new WebSocketError(Frame::CLOSE_UNHANDLED_DATA, "I don't handle binary data."); |
||
| 138 | } |
||
| 139 | |||
| 140 | /** |
||
| 141 | * Called when a `CLOSE` frame is received from the peer. |
||
| 142 | * |
||
| 143 | * @param int $code |
||
| 144 | * @param string $reason |
||
| 145 | */ |
||
| 146 | public function onClose (int $code, string $reason): void { |
||
| 147 | unset($code, $reason); |
||
| 148 | $this->close(); |
||
| 149 | } |
||
| 150 | |||
| 151 | /** |
||
| 152 | * WebSockets do not use the out-of-band channel. |
||
| 153 | * |
||
| 154 | * The RFC says the connection must be dropped if any unsupported activity occurs. |
||
| 155 | * |
||
| 156 | * Closes the connection with a protocol-error frame. |
||
| 157 | */ |
||
| 158 | final public function onOutOfBand (): void { |
||
| 159 | $this->close(Frame::CLOSE_PROTOCOL_ERROR, "Received out-of-band data."); |
||
| 160 | } |
||
| 161 | |||
| 162 | /** |
||
| 163 | * Called when a `PING` is received from the peer. |
||
| 164 | * |
||
| 165 | * Automatically PONGs back the payload back by default. |
||
| 166 | * |
||
| 167 | * @param string $message |
||
| 168 | */ |
||
| 169 | public function onPing (string $message): void { |
||
| 170 | $this->frameHandler->writePong($message); |
||
| 171 | } |
||
| 172 | |||
| 173 | /** |
||
| 174 | * Called when a `PONG` is received from the peer. |
||
| 175 | * |
||
| 176 | * Does nothing by default. |
||
| 177 | * |
||
| 178 | * @param string $message |
||
| 179 | */ |
||
| 180 | public function onPong (string $message): void { |
||
|
0 ignored issues
–
show
|
|||
| 181 | // stub |
||
| 182 | } |
||
| 183 | |||
| 184 | /** |
||
| 185 | * Delegates the read-channel to handlers. |
||
| 186 | * |
||
| 187 | * @throws WebSocketError |
||
| 188 | * @throws Throwable |
||
| 189 | */ |
||
| 190 | public function onReadable (): void { |
||
| 191 | try { |
||
| 192 | if ($this->isNegotiating()) { |
||
| 193 | if ($this->handshake->onReadable()) { |
||
| 194 | $this->state = self::STATE_OK; |
||
| 195 | $this->onStateOk(); |
||
| 196 | } |
||
| 197 | } |
||
| 198 | elseif ($this->isOk()) { |
||
| 199 | $this->frameHandler->onReadable(); |
||
| 200 | } |
||
| 201 | } |
||
| 202 | catch (WebSocketError $e) { |
||
| 203 | $this->close($e->getCode(), $e->getMessage()); |
||
| 204 | throw $e; |
||
| 205 | } |
||
| 206 | catch (Throwable $e) { |
||
| 207 | $this->close(Frame::CLOSE_INTERNAL_ERROR); |
||
| 208 | throw $e; |
||
| 209 | } |
||
| 210 | } |
||
| 211 | |||
| 212 | /** |
||
| 213 | * Called when the initial connection handshake succeeds and frame I/O can occur. |
||
| 214 | * |
||
| 215 | * Does nothing by default. |
||
| 216 | * |
||
| 217 | * If you have negotiated an extension during {@link Handshake}, |
||
| 218 | * claim the RSV bits here via {@link FrameReader::setRsv()} |
||
| 219 | */ |
||
| 220 | protected function onStateOk (): void { |
||
| 221 | // stub |
||
| 222 | } |
||
| 223 | |||
| 224 | /** |
||
| 225 | * Called when a complete `TEXT` payload is received from the peer. |
||
| 226 | * |
||
| 227 | * Throws by default. |
||
| 228 | * |
||
| 229 | * @param string $text |
||
| 230 | * @throws WebSocketError |
||
| 231 | */ |
||
| 232 | public function onText (string $text): void { |
||
| 233 | unset($text); |
||
| 234 | throw new WebSocketError(Frame::CLOSE_UNHANDLED_DATA, "I don't handle text."); |
||
| 235 | } |
||
| 236 | |||
| 237 | /** |
||
| 238 | * Forwards to the {@link FrameHandler} |
||
| 239 | * |
||
| 240 | * @param string $binary |
||
| 241 | */ |
||
| 242 | public function writeBinary (string $binary): void { |
||
| 243 | $this->frameHandler->writeBinary($binary); |
||
| 244 | } |
||
| 245 | |||
| 246 | /** |
||
| 247 | * JSON-encodes and sends as `TEXT`. |
||
| 248 | * |
||
| 249 | * @param mixed $data |
||
| 250 | * @throws JsonException |
||
| 251 | */ |
||
| 252 | public function writeJSON ($data): void { |
||
| 253 | $this->frameHandler->writeText(json_encode($data, JSON_THROW_ON_ERROR)); |
||
| 254 | } |
||
| 255 | |||
| 256 | /** |
||
| 257 | * Forwards to the {@link FrameHandler} |
||
| 258 | * |
||
| 259 | * @param string $text |
||
| 260 | */ |
||
| 261 | public function writeText (string $text): void { |
||
| 262 | $this->frameHandler->writeText($text); |
||
| 263 | } |
||
| 264 | |||
| 265 | } |
This check looks from parameters that have been defined for a function or method, but which are not used in the method body.