• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

sirn-se / websocket-php / 6517331921

14 Oct 2023 11:18AM UTC coverage: 94.946% (+12.4%) from 82.538%
6517331921

push

github

sirn-se
Client listener

120 of 120 new or added lines in 5 files covered. (100.0%)

789 of 831 relevant lines covered (94.95%)

18.95 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.7
/src/Connection.php
1
<?php
2

3
/**
4
 * Copyright (C) 2014-2023 Textalk and contributors.
5
 *
6
 * This file is part of Websocket PHP and is free software under the ISC License.
7
 * License text: https://raw.githubusercontent.com/sirn-se/websocket-php/master/COPYING.md
8
 */
9

10
namespace WebSocket;
11

12
use Phrity\Net\SocketStream;
13
use Psr\Log\{
14
    LoggerAwareInterface,
15
    LoggerInterface,
16
    NullLogger
17
};
18
use Stringable;
19
use Throwable;
20
use WebSocket\Frame\FrameHandler;
21
use WebSocket\Http\{
22
    HttpHandler,
23
    Message as HttpMessage,
24
    Request,
25
    Response
26
};
27
use WebSocket\Exception\{
28
    ConnectionClosedException,
29
    ConnectionFailureException,
30
    ConnectionTimeoutException,
31
    Exception
32
};
33
use WebSocket\Message\{
34
    Message,
35
    MessageHandler
36
};
37
use WebSocket\Middleware\{
38
    MiddlewareHandler,
39
    MiddlewareInterface
40
};
41
use WebSocket\Trait\{
42
    OpcodeTrait,
43
    SendMethodsTrait
44
};
45

46
/**
47
 * WebSocket\Connection class.
48
 * A client/server connection, wrapping socket stream.
49
 */
50
class Connection implements LoggerAwareInterface, Stringable
51
{
52
    use OpcodeTrait;
53
    use SendMethodsTrait;
54

55
    private $stream;
56
    private $httpHandler;
57
    private $messageHandler;
58
    private $middlewareHandler;
59
    private $logger;
60
    private $frameSize = 4096;
61
    private $timeout = 60;
62
    private $localName;
63
    private $remoteName;
64
    private $handshakeRequest;
65
    private $handshakeResponse;
66

67

68
    /* ---------- Magic methods ------------------------------------------------------------------------------------ */
69

70
    public function __construct(SocketStream $stream, bool $pushMasked, bool $pullMaskedRequired)
71
    {
72
        $this->stream = $stream;
75✔
73
        $this->httpHandler = new HttpHandler($this->stream);
75✔
74
        $this->messageHandler = new MessageHandler(new FrameHandler($this->stream, $pushMasked, $pullMaskedRequired));
75✔
75
        $this->middlewareHandler = new MiddlewareHandler($this->messageHandler);
75✔
76
        $this->setLogger(new NullLogger());
75✔
77
        $this->localName = $this->stream->getLocalName();
75✔
78
        $this->remoteName = $this->stream->getRemoteName();
75✔
79
    }
80

81
    public function __destruct()
82
    {
83
        if ($this->isConnected()) {
75✔
84
            $this->stream->close();
50✔
85
        }
86
    }
87

88
    public function __toString(): string
89
    {
90
        return "Connection({$this->localName})";
1✔
91
    }
92

93

94
    /* ---------- Configuration ------------------------------------------------------------------------------------ */
95

96
    /**
97
     * Set logger.
98
     * @param Psr\Log\LoggerInterface $logger Logger implementation
99
     * @return self.
100
     */
101
    public function setLogger(LoggerInterface $logger): self
102
    {
103
        $this->logger = $logger;
75✔
104
        $this->httpHandler->setLogger($logger);
75✔
105
        $this->messageHandler->setLogger($logger);
75✔
106
        $this->middlewareHandler->setLogger($logger);
75✔
107
        $this->logger->debug("[connection] Setting logger: " . get_class($logger));
75✔
108
        return $this;
75✔
109
    }
110

111
    /**
112
     * Set time out on connection.
113
     * @param int $seconds Timeout part in seconds
114
     * @return self.
115
     */
116
    public function setTimeout(int $seconds): self
117
    {
118
        $this->timeout = $seconds;
52✔
119
        $this->stream->setTimeout($seconds, 0);
52✔
120
        $this->logger->debug("[connection] Setting timeout: {$seconds} seconds");
52✔
121
        return $this;
52✔
122
    }
123

124
    /**
125
     * Get timeout.
126
     * @return int Timeout in seconds.
127
     */
128
    public function getTimeout(): int
129
    {
130
        return $this->timeout;
1✔
131
    }
132

133
    /**
134
     * Set frame size.
135
     * @param int $frameSize Frame size in bytes.
136
     * @return self.
137
     */
138
    public function setFrameSize(int $frameSize): self
139
    {
140
        $this->frameSize = $frameSize;
52✔
141
        return $this;
52✔
142
    }
143

144
    /**
145
     * Get frame size.
146
     * @return int Frame size in bytes
147
     */
148
    public function getFrameSize(): int
149
    {
150
        return $this->frameSize;
29✔
151
    }
152

153
    /**
154
     * Add a middleware.
155
     * @param MiddlewareInterface $middleware
156
     * @return self.
157
     */
158
    public function addMiddleware(MiddlewareInterface $middleware): self
159
    {
160
        $this->middlewareHandler->add($middleware);
15✔
161
        $this->logger->debug("[connection] Addded middleware: {$middleware}");
15✔
162
        return $this;
15✔
163
    }
164

165

166
    /* ---------- Connection management ---------------------------------------------------------------------------- */
167

168
    /**
169
     * If connected to stream.
170
     * @return bool
171
     */
172
    public function isConnected(): bool
173
    {
174
        return $this->stream->isConnected();
75✔
175
    }
176

177
    /**
178
     * If connection is readable.
179
     * @return bool
180
     */
181
    public function isReadable(): bool
182
    {
183
        return $this->stream->isReadable();
7✔
184
    }
185

186
    /**
187
     * If connection is writable.
188
     * @return bool
189
     */
190
    public function isWritable(): bool
191
    {
192
        return $this->stream->isWritable();
9✔
193
    }
194

195
    /**
196
     * Close connection stream.
197
     * @return self.
198
     */
199
    public function disconnect(): self
200
    {
201
        $this->logger->info('[connection] Closing connection');
25✔
202
        $this->stream->close();
25✔
203
        return $this;
25✔
204
    }
205

206
    /**
207
     * Close connection stream reading.
208
     * @return self.
209
     */
210
    public function closeRead(): self
211
    {
212
        $this->logger->info('[connection] Closing further reading');
4✔
213
        $this->stream->closeRead();
4✔
214
        return $this;
4✔
215
    }
216

217
    /**
218
     * Close connection stream writing.
219
     * @return self.
220
     */
221
    public function closeWrite(): self
222
    {
223
        $this->logger->info('[connection] Closing further writing');
4✔
224
        $this->stream->closeWrite();
4✔
225
        return $this;
4✔
226
    }
227

228

229
    /* ---------- Connection state --------------------------------------------------------------------------------- */
230

231
    /**
232
     * Get name of local socket, or null if not connected.
233
     * @return string|null
234
     */
235
    public function getName(): string|null
236
    {
237
        return $this->localName;
2✔
238
    }
239

240
    /**
241
     * Get name of remote socket, or null if not connected.
242
     * @return string|null
243
     */
244
    public function getRemoteName(): string|null
245
    {
246
        return $this->remoteName;
2✔
247
    }
248

249

250
    /* ---------- WebSocket Message methods ------------------------------------------------------------------------ */
251

252
    public function send(Message $message): Message
253
    {
254
        return $this->pushMessage($message);
20✔
255
    }
256

257
    public function receive(): Message
258
    {
259
        return $this->pullMessage();
×
260
    }
261

262
    // Push a message to stream
263
    public function pushMessage(Message $message): Message
264
    {
265
        try {
266
            return $this->middlewareHandler->processOutgoing($this, $message);
28✔
267
        } catch (Throwable $e) {
11✔
268
            $this->throwException($e);
11✔
269
        }
270
    }
271

272
    // Pull a message from stream
273
    public function pullMessage(): Message
274
    {
275
        try {
276
            return $this->middlewareHandler->processIncoming($this);
22✔
277
        } catch (Throwable $e) {
8✔
278
            $this->throwException($e);
8✔
279
        }
280
    }
281

282

283
    /* ---------- HTTP Message methods ----------------------------------------------------------------------------- */
284

285
    public function pushHttp(HttpMessage $message): HttpMessage
286
    {
287
        try {
288
            return $this->httpHandler->push($message);
51✔
289
        } catch (Throwable $e) {
2✔
290
            $this->throwException($e);
2✔
291
        }
292
    }
293

294
    public function pullHttp(): HttpMessage
295
    {
296
        try {
297
            return $this->httpHandler->pull();
52✔
298
        } catch (Throwable $e) {
3✔
299
            $this->throwException($e);
3✔
300
        }
301
    }
302

303
    public function setHandshakeRequest(Request $request): self
304
    {
305
        $this->handshakeRequest = $request;
39✔
306
        return $this;
39✔
307
    }
308

309
    public function getHandshakeRequest(): Request|null
310
    {
311
        return $this->handshakeRequest;
1✔
312
    }
313

314
    public function setHandshakeResponse(Response $response): self
315
    {
316
        $this->handshakeResponse = $response;
39✔
317
        return $this;
39✔
318
    }
319

320
    public function getHandshakeResponse(): Response|null
321
    {
322
        return $this->handshakeResponse;
2✔
323
    }
324

325

326
    /* ---------- Internal helper methods -------------------------------------------------------------------------- */
327

328
    protected function throwException(Throwable $e): void
329
    {
330
        // Internal exceptions are handled and re-thrown
331
        if ($e instanceof Exception) {
24✔
332
            $this->logger->error("[connection] {$e->getMessage()}");
14✔
333
            throw $e;
14✔
334
        }
335
        // External exceptions are converted to internal
336
        $exception = get_class($e);
10✔
337
        $json = '';
10✔
338
        if ($this->isConnected()) {
10✔
339
            $meta = $this->stream->getMetadata();
9✔
340
            $json = json_encode($meta);
9✔
341
            if (!empty($meta['timed_out'])) {
9✔
342
                $this->logger->error("[connection] {$e->getMessage()} original: {$exception} {$json}");
3✔
343
                throw new ConnectionTimeoutException();
3✔
344
            }
345
            if (!empty($meta['eof'])) {
6✔
346
                $this->logger->error("[connection] {$e->getMessage()} original: {$exception} {$json}");
2✔
347
                throw new ConnectionClosedException();
2✔
348
            }
349
        }
350
        $this->logger->error("[connection] {$e->getMessage()} original: {$exception} {$json}");
5✔
351
        throw new ConnectionFailureException();
5✔
352
    }
353
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc