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

sirn-se / websocket-php / 8346487915

19 Mar 2024 04:15PM UTC coverage: 22.584% (-77.4%) from 100.0%
8346487915

push

github

sirn-se
Temp test verification

2 of 2 new or added lines in 1 file covered. (100.0%)

742 existing lines in 32 files now uncovered.

222 of 983 relevant lines covered (22.58%)

0.23 hits per line

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

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

3
/**
4
 * Copyright (C) 2014-2024 Textalk and contributors.
5
 * This file is part of Websocket PHP and is free software under the ISC License.
6
 */
7

8
namespace WebSocket;
9

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

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

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

67

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

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

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

88
    public function __toString(): string
89
    {
UNCOV
90
        return $this->stringable('%s:%s', $this->localName, $this->remoteName);
×
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;
1✔
104
        $this->httpHandler->setLogger($logger);
1✔
105
        $this->messageHandler->setLogger($logger);
1✔
106
        $this->middlewareHandler->setLogger($logger);
1✔
107
        $this->logger->debug("[connection] Setting logger: " . get_class($logger));
1✔
108
        return $this;
1✔
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;
1✔
119
        $this->stream->setTimeout($seconds, 0);
1✔
120
        $this->logger->debug("[connection] Setting timeout: {$seconds} seconds");
1✔
121
        return $this;
1✔
122
    }
123

124
    /**
125
     * Get timeout.
126
     * @return int Timeout in seconds.
127
     */
128
    public function getTimeout(): int
129
    {
UNCOV
130
        return $this->timeout;
×
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;
1✔
141
        return $this;
1✔
142
    }
143

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

153
    /**
154
     * Add a middleware.
155
     * @param MiddlewareInterface $middleware
156
     * @return self.
157
     */
158
    public function addMiddleware(MiddlewareInterface $middleware): self
159
    {
UNCOV
160
        $this->middlewareHandler->add($middleware);
×
UNCOV
161
        $this->logger->debug("[connection] Added middleware: {$middleware}");
×
UNCOV
162
        return $this;
×
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();
1✔
175
    }
176

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

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

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

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

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

229

230
    /* ---------- Connection state --------------------------------------------------------------------------------- */
231

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

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

250
    /**
251
     * Set meta value on connection.
252
     * @param string $key Meta key
253
     * @param mixed $value Meta value
254
     */
255
    public function setMeta(string $key, mixed $value): void
256
    {
UNCOV
257
        $this->meta[$key] = $value;
×
258
    }
259

260
    /**
261
     * Get meta value on connection.
262
     * @param string $key Meta key
263
     * @return mixed Meta value
264
     */
265
    public function getMeta(string $key): mixed
266
    {
UNCOV
267
        return $this->meta[$key] ?? null;
×
268
    }
269

270
    /**
271
     * Tick operation on connection.
272
     */
273
    public function tick(): void
274
    {
UNCOV
275
        $this->middlewareHandler->processTick($this);
×
276
    }
277

278

279
    /* ---------- WebSocket Message methods ------------------------------------------------------------------------ */
280

281
    public function send(Message $message): Message
282
    {
UNCOV
283
        return $this->pushMessage($message);
×
284
    }
285

286
    // Push a message to stream
287
    public function pushMessage(Message $message): Message
288
    {
289
        try {
UNCOV
290
            return $this->middlewareHandler->processOutgoing($this, $message);
×
UNCOV
291
        } catch (Throwable $e) {
×
UNCOV
292
            $this->throwException($e);
×
293
        }
294
    }
295

296
    // Pull a message from stream
297
    public function pullMessage(): Message
298
    {
299
        try {
UNCOV
300
            return $this->middlewareHandler->processIncoming($this);
×
UNCOV
301
        } catch (Throwable $e) {
×
UNCOV
302
            $this->throwException($e);
×
303
        }
304
    }
305

306

307
    /* ---------- HTTP Message methods ----------------------------------------------------------------------------- */
308

309
    public function pushHttp(HttpMessage $message): HttpMessage
310
    {
311
        try {
312
            return $this->middlewareHandler->processHttpOutgoing($this, $message);
1✔
UNCOV
313
        } catch (Throwable $e) {
×
UNCOV
314
            $this->throwException($e);
×
315
        }
316
    }
317

318
    public function pullHttp(): HttpMessage
319
    {
320
        try {
321
            return $this->middlewareHandler->processHttpIncoming($this);
1✔
322
        } catch (Throwable $e) {
1✔
323
            $this->throwException($e);
1✔
324
        }
325
    }
326

327
    public function setHandshakeRequest(Request $request): self
328
    {
329
        $this->handshakeRequest = $request;
1✔
330
        return $this;
1✔
331
    }
332

333
    public function getHandshakeRequest(): Request|null
334
    {
UNCOV
335
        return $this->handshakeRequest;
×
336
    }
337

338
    public function setHandshakeResponse(Response $response): self
339
    {
340
        $this->handshakeResponse = $response;
1✔
341
        return $this;
1✔
342
    }
343

344
    public function getHandshakeResponse(): Response|null
345
    {
346
        return $this->handshakeResponse;
1✔
347
    }
348

349

350
    /* ---------- Internal helper methods -------------------------------------------------------------------------- */
351

352
    protected function throwException(Throwable $e): never
353
    {
354
        // Internal exceptions are handled and re-thrown
355
        if ($e instanceof Exception) {
1✔
356
            $this->logger->error("[connection] {$e->getMessage()}");
1✔
357
            throw $e;
1✔
358
        }
359
        // External exceptions are converted to internal
UNCOV
360
        $exception = get_class($e);
×
UNCOV
361
        $json = '';
×
UNCOV
362
        if ($this->isConnected()) {
×
UNCOV
363
            $meta = $this->stream->getMetadata();
×
UNCOV
364
            $json = json_encode($meta);
×
UNCOV
365
            if (!empty($meta['timed_out'])) {
×
UNCOV
366
                $this->logger->error("[connection] {$e->getMessage()} original: {$exception} {$json}");
×
UNCOV
367
                throw new ConnectionTimeoutException();
×
368
            }
UNCOV
369
            if (!empty($meta['eof'])) {
×
UNCOV
370
                $this->logger->error("[connection] {$e->getMessage()} original: {$exception} {$json}");
×
UNCOV
371
                throw new ConnectionClosedException();
×
372
            }
373
        }
UNCOV
374
        $this->logger->error("[connection] {$e->getMessage()} original: {$exception} {$json}");
×
UNCOV
375
        throw new ConnectionFailureException();
×
376
    }
377
}
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