Skip to content

Commit 105e2c1

Browse files
committed
watcher, step 1
1 parent 2afef0d commit 105e2c1

21 files changed

Lines changed: 370 additions & 476 deletions

composer.json

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
},
2929
"require": {
3030
"php": "^8.1",
31-
"phrity/http": "dev-main",
31+
"phrity/http": "^1.0",
3232
"phrity/net-uri": "^2.1",
3333
"phrity/net-stream": "dev-ws4 as 2.4.0",
3434
"psr/http-message": "^1.1 | ^2.0",
@@ -48,11 +48,5 @@
4848
"ext-zlib": "Required for per-message deflate compression",
4949
"psr/http-factory-implementation": "Use a complete http-factory implementation",
5050
"psr/log-implementation": "Use a logger implementation"
51-
},
52-
"repositories": [
53-
{
54-
"type": "path",
55-
"url": "../phrity-http"
56-
}
57-
]
51+
}
5852
}

docs/Client.md

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -201,13 +201,22 @@ $client->getContext(); // => currently used Phrity\Net\Context
201201
### HTTP factories
202202

203203
By default the Client uses a minimal [PSR-7 HTTP message](https://www.php-fig.org/psr/psr-7/) implementation.
204-
To use complete implementations such as `nyholm/psr7` or `guzzlehttp/psr7`, add their [PSR-17 HTTP factories](https://www.php-fig.org/psr/psr-17/) during setup.
204+
Other (more complete) implementations can be used by setting [PSR-17 HTTP factories](https://www.php-fig.org/psr/psr-17/) on the CLient.
205+
206+
Set a configured HttpFactory class on the Client.
205207
```php
206-
$httpFactory = new Nyholm\Psr7\Factory\Psr17Factory(); // Or any other PSR-17 factory
207-
$client
208-
->setResponseFactory($httpFactory)
209-
->setRequestFactory($httpFactory)
210-
->setUriFactory($httpFactory);
208+
$factory = new Phrity\Http\HttpFactory(
209+
requestFactory: $myRequestFactory,
210+
responseFactory: $myResponseFactory,
211+
uriFactory: $myUriFactory,
212+
);
213+
$client->setHttpFactory($factory);
214+
215+
```
216+
Or if you use factories that support multiple interfaces, available in libraries such as `nyholm/psr7` or `guzzlehttp/psr7`.
217+
```php
218+
$psrFactory = new Nyholm\Psr7\Factory\Psr17Factory(); // Or any other PSR-17 factory
219+
$client->setHttpFactory(Phrity\Http\HttpFactory::create($psrFactory));
211220
```
212221

213222
### Handshake headers

docs/Server.md

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -192,13 +192,22 @@ $server->getContext(); // => currently used Phrity\Net\Context
192192
### HTTP factories
193193

194194
By default the Server uses a minimal [PSR-7 HTTP message](https://www.php-fig.org/psr/psr-7/) implementation.
195-
To use complete implementations such as `nyholm/psr7` or `guzzlehttp/psr7`, add their [PSR-17 HTTP factories](https://www.php-fig.org/psr/psr-17/) during setup.
195+
Other (more complete) implementations can be used by setting [PSR-17 HTTP factories](https://www.php-fig.org/psr/psr-17/) on the Server.
196+
197+
Set a configured HttpFactory class on the Client.
196198
```php
197-
$httpFactory = new Nyholm\Psr7\Factory\Psr17Factory(); // Or any other PSR-17 factory
198-
$server
199-
->setResponseFactory($httpFactory)
200-
->setServerRequestFactory($httpFactory)
201-
->setUriFactory($httpFactory);
199+
$factory = new Phrity\Http\HttpFactory(
200+
serverRequestFactory: $myServerRequestFactory,
201+
responseFactory: $myResponseFactory,
202+
uriFactory: $myUriFactory,
203+
);
204+
$server->setHttpFactory($factory);
205+
```
206+
207+
Or if you use factories that support multiple interfaces, available in libraries such as `nyholm/psr7` or `guzzlehttp/psr7`.
208+
```php
209+
$psrFactory = new Nyholm\Psr7\Factory\Psr17Factory(); // Or any other PSR-17 factory
210+
$server->setHttpFactory(Phrity\Http\HttpFactory::create($psrFactory));
202211
```
203212

204213
### Max connections

src/Client.php

Lines changed: 68 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,18 @@
88
namespace WebSocket;
99

1010
use InvalidArgumentException;
11+
use Phrity\Http\HttpFactory;
1112
use Phrity\Net\{
1213
Context,
14+
SocketStream,
1315
StreamCollection,
1416
StreamFactory,
1517
Uri
1618
};
1719
use Psr\Http\Message\{
18-
RequestFactoryInterface,
1920
RequestInterface,
20-
ResponseFactoryInterface,
2121
ResponseInterface,
22-
ServerRequestFactoryInterface,
2322
UriInterface,
24-
UriFactoryInterface,
2523
};
2624
use Psr\Log\{
2725
LoggerAwareInterface,
@@ -48,6 +46,7 @@
4846
SendMethodsTrait,
4947
StringableTrait
5048
};
49+
use WebSocket\Runtime\Watcher;
5150

5251
/**
5352
* WebSocket\Client class.
@@ -72,36 +71,41 @@ class Client implements LoggerAwareInterface, Stringable
7271
private array $headers = [];
7372

7473
// Internal resources
75-
private StreamFactory $streamFactory;
7674
private Uri $socketUri;
7775
private Connection|null $connection = null;
7876
/** @var array<MiddlewareInterface> $middlewares */
7977
private array $middlewares = [];
80-
private StreamCollection|null $streams = null;
8178
private bool $running = false;
8279

83-
private RequestFactoryInterface $requestFactory;
84-
private ResponseFactoryInterface $responseFactory;
85-
private ServerRequestFactoryInterface $serverRequestFactory;
86-
private UriFactoryInterface $uriFactory;
80+
private StreamFactory $streamFactory;
81+
private HttpFactory $httpFactory;
82+
private Watcher $watcher;
8783

8884

8985
/* ---------- Magic methods ------------------------------------------------------------------------------------ */
9086

9187
/**
9288
* @param UriInterface|string $uri A ws/wss-URI
93-
*/
94-
public function __construct(UriInterface|string $uri)
95-
{
89+
* @param LoggerInterface|null $logger
90+
* @param Context|null $context $logger
91+
* @param StreamFactory|null $streamFactory
92+
* @param HttpFactory|null $httpFactory
93+
* @param Watcher|null $watcher
94+
*/
95+
public function __construct(
96+
UriInterface|string $uri,
97+
LoggerInterface|null $logger = null,
98+
Context|null $context = null,
99+
StreamFactory|null $streamFactory = null,
100+
HttpFactory|null $httpFactory = null,
101+
Watcher|null $watcher = null,
102+
) {
96103
$this->socketUri = $this->parseUri($uri);
97-
$this->initLogger();
98-
$this->context = new Context();
99-
$this->setStreamFactory(new StreamFactory());
100-
$this->requestFactory
101-
= $this->responseFactory
102-
= $this->serverRequestFactory
103-
= $this->uriFactory
104-
= new DefaultHttpFactory();
104+
$this->initLogger($logger);
105+
$this->context = $context ?? new Context();
106+
$this->streamFactory = $streamFactory ?? new StreamFactory();
107+
$this->httpFactory = $httpFactory ?? new DefaultHttpFactory();
108+
$this->watcher = $watcher ?? new Watcher($this->streamFactory->createStreamCollection());
105109
}
106110

107111
/**
@@ -127,6 +131,17 @@ public function setStreamFactory(StreamFactory $streamFactory): self
127131
return $this;
128132
}
129133

134+
/**
135+
* Set HTTP factory to use.
136+
* @param HttpFactory $httpFactory
137+
* @return self
138+
*/
139+
public function setHttpFactory(HttpFactory $httpFactory): self
140+
{
141+
$this->httpFactory = $httpFactory;
142+
return $this;
143+
}
144+
130145
/**
131146
* Set logger.
132147
* @param LoggerInterface $logger Logger implementation
@@ -139,36 +154,6 @@ public function setLogger(LoggerInterface $logger): void
139154
}
140155
}
141156

142-
/**
143-
* Set ResponseFactory.
144-
* @param ResponseFactoryInterface $responseFactory ResponseFactory to use
145-
*/
146-
public function setResponseFactory(ResponseFactoryInterface $responseFactory): self
147-
{
148-
$this->responseFactory = $responseFactory;
149-
return $this;
150-
}
151-
152-
/**
153-
* Set RequestFactory.
154-
* @param RequestFactoryInterface $requestFactory RequestFactory to use
155-
*/
156-
public function setRequestFactory(RequestFactoryInterface $requestFactory): self
157-
{
158-
$this->requestFactory = $requestFactory;
159-
return $this;
160-
}
161-
162-
/**
163-
* Set UriFactory.
164-
* @param UriFactoryInterface $uriFactory UriFactory to use
165-
*/
166-
public function setUriFactory(UriFactoryInterface $uriFactory): self
167-
{
168-
$this->uriFactory = $uriFactory;
169-
return $this;
170-
}
171-
172157
/**
173158
* Set timeout.
174159
* @param int<0, max>|float $timeout Timeout in seconds
@@ -328,27 +313,8 @@ public function start(int|float|null $timeout = null): void
328313

329314
// Run handler
330315
while ($this->running) {
331-
/** @var StreamCollection */
332-
$streams = $this->streams;
333316
try {
334-
// Get streams with readable content
335-
$readables = $streams->waitRead($timeout ?? $this->timeout);
336-
foreach ($readables as $key => $readable) {
337-
try {
338-
// Read from connection
339-
$message = $connection->pullMessage();
340-
$this->dispatch($message->getOpcode(), [$this, $connection, $message]);
341-
} catch (MessageLevelInterface $e) {
342-
// Error, but keep connection open
343-
$this->logger->error("[client] {$e->getMessage()}", ['exception' => $e]);
344-
$this->dispatch('error', [$this, $connection, $e]);
345-
} catch (ConnectionLevelInterface $e) {
346-
// Error, disconnect connection
347-
$this->disconnect();
348-
$this->logger->error("[client] {$e->getMessage()}", ['exception' => $e]);
349-
$this->dispatch('error', [$this, $connection, $e]);
350-
}
351-
}
317+
$this->watcher->watch($timeout ?? $this->timeout);
352318
if (!$connection->isConnected()) {
353319
$this->running = false;
354320
}
@@ -392,6 +358,27 @@ public function start(int|float|null $timeout = null): void
392358
}
393359
}
394360

361+
private function selectHandler(string $key, SocketStream $stream): void
362+
{
363+
/** @var Connection $connection */
364+
$connection = $this->connection;
365+
try {
366+
// Read from connection
367+
$message = $connection->pullMessage();
368+
$this->dispatch($message->getOpcode(), [$this, $connection, $message]);
369+
} catch (MessageLevelInterface $e) {
370+
// Error, but keep connection open
371+
$this->logger->error("[client] {$e->getMessage()}", ['exception' => $e]);
372+
$this->dispatch('error', [$this, $connection, $e]);
373+
} catch (ConnectionLevelInterface $e) {
374+
// Error, disconnect connection
375+
$this->disconnect();
376+
$this->logger->error("[client] {$e->getMessage()}", ['exception' => $e]);
377+
$this->dispatch('error', [$this, $connection, $e]);
378+
}
379+
}
380+
381+
395382
/**
396383
* Stop client listener (resumable).
397384
*/
@@ -448,7 +435,6 @@ public function isWritable(): bool
448435
public function connect(): void
449436
{
450437
$this->disconnect();
451-
$this->streams = $this->streamFactory->createStreamCollection();
452438

453439
$hostUri = (new Uri())
454440
->withScheme(match ($this->socketUri->getScheme()) {
@@ -471,16 +457,16 @@ public function connect(): void
471457
$this->logger->error("[client] {$error}", ['exception' => $e]);
472458
throw new ClientException($error);
473459
}
474-
$name = $stream->getRemoteName();
475-
$this->streams->attach($stream, $name);
460+
$name = $stream->getRemoteName() ?? 'unknown';
461+
$this->watcher->attach($name, $stream, function (string $key, SocketStream $stream) {
462+
$this->selectHandler($key, $stream);
463+
});
476464
$this->connection = new Connection(
477465
$stream,
478466
true,
479467
false,
480468
$hostUri->getScheme() === 'ssl',
481-
$this->responseFactory,
482-
$this->serverRequestFactory,
483-
$this->uriFactory
469+
$this->httpFactory
484470
);
485471
$this->connection->setFrameSize($this->frameSize);
486472
$this->connection->setTimeout($this->timeout);
@@ -492,6 +478,7 @@ public function connect(): void
492478
if (!$this->isConnected()) {
493479
$error = "Invalid stream on \"{$hostUri}\".";
494480
$this->logger->error("[client] {$error}");
481+
$this->disconnect();
495482
throw new ClientException($error);
496483
}
497484
try {
@@ -522,6 +509,9 @@ public function connect(): void
522509
*/
523510
public function disconnect(): void
524511
{
512+
if ($this->connection) {
513+
$this->watcher->detach($this->connection->getRemoteName());
514+
}
525515
if ($this->connection && $this->isConnected()) {
526516
$this->connection->disconnect();
527517
$this->logger->info('[client] Client disconnected');
@@ -571,7 +561,7 @@ protected function performHandshake(Uri $uri, Connection $connection): ResponseI
571561
// Generate the WebSocket key.
572562
$key = $this->generateKey();
573563

574-
$request = $this->requestFactory->createRequest('GET', $uri);
564+
$request = $this->httpFactory->createRequest('GET', $uri);
575565

576566
$request = $request
577567
->withHeader('User-Agent', 'websocket-client-php')

0 commit comments

Comments
 (0)