From 55960b9cee3a216491a7c1ccc1cacf21502529b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren=20Jensen?= Date: Tue, 23 Dec 2025 00:29:46 +0100 Subject: [PATCH 1/2] Nyholm HTTP messages, intermediate --- composer.json | 8 +- src/Client.php | 2 +- src/Http/HttpHandler.php | 18 +- src/Http/Message.php | 185 ------------------ src/Http/Request.php | 119 +---------- src/Http/Response.php | 137 +------------ src/Http/ServerRequest.php | 137 +------------ src/Server.php | 18 +- tests/suites/client/ClientTest.php | 14 +- tests/suites/client/ConfigTest.php | 4 +- tests/suites/client/HandshakeTest.php | 14 +- tests/suites/connection/ConnectionTest.php | 15 +- tests/suites/http/HttpHandlerTest.php | 29 +-- tests/suites/http/RequestTest.php | 119 ++++------- tests/suites/http/ResponseTest.php | 63 ++---- tests/suites/http/ServerRequestTest.php | 128 +----------- tests/suites/middleware/CallbackTest.php | 18 +- .../middleware/DeflateCompressorTest.php | 26 +-- .../middleware/SubprotocolNegotiationTest.php | 24 ++- tests/suites/server/ConfigTest.php | 4 +- tests/suites/server/HandshakeTest.php | 9 +- tests/suites/server/ServerTest.php | 14 +- 22 files changed, 191 insertions(+), 914 deletions(-) delete mode 100644 src/Http/Message.php diff --git a/composer.json b/composer.json index fb2aa9b..0e5b797 100644 --- a/composer.json +++ b/composer.json @@ -27,21 +27,21 @@ }, "require": { "php": "^8.1", - "phrity/http": "^1.0", + "nyholm/psr7": "^1.4", + "phrity/http": "^1.1", "phrity/net-uri": "^2.1", "phrity/net-stream": "^2.3", - "psr/http-message": "^1.1 | ^2.0", + "psr/http-message": "^1.1 || ^2.0", "psr/log": "^1.0 || ^2.0 || ^3.0" }, "require-dev": { - "guzzlehttp/psr7": "^2.0", "phpstan/phpstan": "^2.0", "phpunit/phpunit": "^10.0 || ^11.0 || ^12.0", "phrity/logger-console": "^1.0", "phrity/net-mock": "^2.3", "phrity/util-errorhandler": "^1.1", "robiningelbrecht/phpunit-coverage-tools": "^1.9", - "squizlabs/php_codesniffer": "^3.5 || ^4.0" + "squizlabs/php_codesniffer": "^4.0" }, "suggests": { "ext-zlib": "Required for per-message deflate compression" diff --git a/src/Client.php b/src/Client.php index 95be629..0486e12 100644 --- a/src/Client.php +++ b/src/Client.php @@ -580,7 +580,6 @@ protected function performHandshake(Uri $uri, Connection $connection): ResponseI $request = $connection->pushHttp($request); /** @var ResponseInterface */ $response = $connection->pullHttp(); - if ($response->getStatusCode() != 101) { throw new HandshakeException("Invalid status code {$response->getStatusCode()}.", $response); } @@ -652,6 +651,7 @@ protected function parseUri(UriInterface|string $uri): Uri if (!$uriInstance->getHost()) { throw new BadUriException("Invalid URI host."); } + $uriInstance = $uriInstance->withPath($uriInstance->getPath(), Uri::ABSOLUTE_PATH | Uri::NORMALIZE_PATH); return $uriInstance; } diff --git a/src/Http/HttpHandler.php b/src/Http/HttpHandler.php index 41f4307..fdb16f3 100644 --- a/src/Http/HttpHandler.php +++ b/src/Http/HttpHandler.php @@ -7,13 +7,17 @@ namespace WebSocket\Http; -use Phrity\Http\HttpFactory; +use Phrity\Http\{ + HttpFactory, + Serializer, +}; use Phrity\Net\{ SocketStream, Uri }; use Psr\Http\Message\{ MessageInterface, + RequestInterface, ResponseFactoryInterface, ServerRequestFactoryInterface, UriFactoryInterface, @@ -38,6 +42,7 @@ class HttpHandler implements LoggerAwareInterface, Stringable private SocketStream $stream; private bool $ssl; private HttpFactory $httpFactory; + private Serializer $serializer; public function __construct( SocketStream $stream, @@ -47,6 +52,7 @@ public function __construct( $this->stream = $stream; $this->ssl = $ssl; $this->httpFactory = $httpFactory ?? new DefaultHttpFactory(); + $this->serializer = new Serializer(); } /** @@ -68,9 +74,9 @@ public function pull(): MessageInterface // Pulling server request preg_match('!^(?P[A-Z]+) (?P[^ ]*) HTTP/(?P[0-9/.]+)!', $status, $matches); if (!empty($matches)) { - $message = $this->httpFactory->createServerRequest($matches['method'], ''); $path = $matches['path']; $version = $matches['version']; + $message = $this->httpFactory->createServerRequest($matches['method'], $path); } // Pulling response @@ -98,7 +104,7 @@ public function pull(): MessageInterface } } } - if ($message instanceof Request) { + if ($message instanceof RequestInterface) { $scheme = $this->ssl ? 'wss' : 'ws'; $uri = $this->httpFactory->createUri("{$scheme}://{$message->getHeaderLine('Host')}{$path}"); $message = $message->withUri($uri, true); @@ -114,10 +120,8 @@ public function pull(): MessageInterface */ public function push(MessageInterface $message): MessageInterface { - if (!$message instanceof Message) { - throw new RuntimeException('Generic MessageInterface currently not supported.'); - } - $data = implode("\r\n", $message->getAsArray()) . "\r\n\r\n"; + $isRequest = $message instanceof RequestInterface; + $data = $this->serializer->message($message); $this->stream->write($data); return $message; } diff --git a/src/Http/Message.php b/src/Http/Message.php deleted file mode 100644 index 837e665..0000000 --- a/src/Http/Message.php +++ /dev/null @@ -1,185 +0,0 @@ -> $headers */ - private array $headers = []; - - /** - * Retrieves the HTTP protocol version as a string. - * @return string HTTP protocol version. - */ - public function getProtocolVersion(): string - { - return $this->version; - } - - /** - * Return an instance with the specified HTTP protocol version. - * @param string $version HTTP protocol version - * @return static - */ - public function withProtocolVersion(string $version): self - { - $new = clone $this; - $new->version = $version; - return $new; - } - - /** - * Retrieves all message header values. - * @return string[][] Returns an associative array of the message's headers. - */ - public function getHeaders(): array - { - return array_merge(...array_values($this->headers)); - } - - /** - * Checks if a header exists by the given case-insensitive name. - * @param string $name Case-insensitive header field name. - * @return bool Returns true if any header names match the given header. - */ - public function hasHeader(string $name): bool - { - return array_key_exists(strtolower($name), $this->headers); - } - - /** - * Retrieves a message header value by the given case-insensitive name. - * @param string $name Case-insensitive header field name. - * @return string[] An array of string values as provided for the given header. - */ - public function getHeader(string $name): array - { - return $this->hasHeader($name) - ? array_merge(...array_values($this->headers[strtolower($name)] ?: [])) - : []; - } - - /** - * Retrieves a comma-separated string of the values for a single header. - * @param string $name Case-insensitive header field name. - * @return string A string of values as provided for the given header. - */ - public function getHeaderLine(string $name): string - { - return implode(',', $this->getHeader($name)); - } - - /** - * Return an instance with the provided value replacing the specified header. - * @param string $name Case-insensitive header field name. - * @param string|string[] $value Header value(s). - * @return static - */ - public function withHeader(string $name, mixed $value): self - { - $new = clone $this; - $new->removeHeader($name); - $new->handleHeader($name, $value); - return $new; - } - - /** - * Return an instance with the specified header appended with the given value. - * @param string $name Case-insensitive header field name to add. - * @param string|string[] $value Header value(s). - * @return static - */ - public function withAddedHeader(string $name, mixed $value): self - { - $new = clone $this; - $new->handleHeader($name, $value); - return $new; - } - - /** - * Return an instance without the specified header. - * @param string $name Case-insensitive header field name to remove. - * @return static - */ - public function withoutHeader(string $name): self - { - $new = clone $this; - $new->removeHeader($name); - return $new; - } - - /** - * Not implemented, WebSocket only use headers. - * @throws BadMethodCallException - */ - public function getBody(): StreamInterface - { - throw new BadMethodCallException("Not implemented."); - } - - /** - * Not implemented, WebSocket only use headers. - * @throws BadMethodCallException - */ - public function withBody(StreamInterface $body): self - { - throw new BadMethodCallException("Not implemented."); - } - - /** @return array */ - public function getAsArray(): array - { - $lines = []; - foreach ($this->getHeaders() as $name => $values) { - foreach ($values as $value) { - $lines[] = "{$name}: {$value}"; - } - } - return $lines; - } - - /** - * @throws InvalidArgumentException - */ - protected function handleHeader(string $name, mixed $value): void - { - if (!preg_match('|^[0-9a-zA-Z#_-]+$|', $name)) { - throw new InvalidArgumentException("'{$name}' is not a valid header field name."); - } - $value = is_array($value) ? $value : [$value]; - foreach ($value as $content) { - if (!is_string($content) && !is_numeric($content)) { - throw new InvalidArgumentException("Invalid header value(s) provided."); - } - $this->headers[strtolower($name)][$name][] = trim((string)$content); - } - } - - protected function removeHeader(string $name): void - { - if ($this->hasHeader($name)) { - unset($this->headers[strtolower($name)]); - } - } -} diff --git a/src/Http/Request.php b/src/Http/Request.php index 37d7c6a..96ce820 100644 --- a/src/Http/Request.php +++ b/src/Http/Request.php @@ -7,131 +7,22 @@ namespace WebSocket\Http; -use InvalidArgumentException; -use Phrity\Net\Uri; +use Nyholm\Psr7\Request as NyholmRequest; use Psr\Http\Message\{ RequestInterface, UriInterface }; -use RuntimeException; /** * WebSocket\Http\Request class. * Only used for handshake procedure. + * @deprecated To be removed in v4, use Nyholm directly instead + * @phpstan-ignore class.extendsFinalByPhpDoc */ -class Request extends Message implements RequestInterface +class Request extends NyholmRequest implements RequestInterface { - /** @var array $methods */ - private static array $methods = ['GET', 'HEAD', 'OPTIONS', 'TRACE', 'PUT', 'DELETE', 'POST', 'PATCH', 'CONNECT']; - - private string $target = ''; - private string $method; - private UriInterface $uri; - public function __construct(string $method = 'GET', UriInterface|string $uri = '') { - if (!in_array($method, self::$methods)) { - throw new InvalidArgumentException("Invalid method '{$method}' provided."); - } - $this->uri = $uri instanceof UriInterface ? $uri : new Uri($uri); - $this->method = $method; - $this->handleHeader('Host', $this->formatHostHeader($this->uri)); - } - - /** - * Retrieves the message's request target. - * @return string - */ - public function getRequestTarget(): string - { - if ($this->target) { - return $this->target; - } - $uri = (new Uri())->withPath($this->uri->getPath())->withQuery($this->uri->getQuery()); - return $uri->toString(Uri::ABSOLUTE_PATH); - } - - /** - * Return an instance with the specific request-target. - * @param mixed $requestTarget - * @return static - */ - public function withRequestTarget(mixed $requestTarget): self - { - $new = clone $this; - $new->target = $requestTarget; - return $new; - } - - /** - * Retrieves the HTTP method of the request. - * @return string Returns the request method. - */ - public function getMethod(): string - { - return $this->method; - } - - /** - * Return an instance with the provided HTTP method. - * @param string $method Case-sensitive method. - * @return static - * @throws InvalidArgumentException for invalid HTTP methods. - */ - public function withMethod(string $method): self - { - if (!in_array($method, self::$methods)) { - throw new InvalidArgumentException("Invalid method '{$method}' provided."); - } - $new = clone $this; - $new->method = $method; - return $new; - } - - /** - * Retrieves the URI instance. - * This method MUST return a UriInterface instance. - * @return UriInterface Returns a UriInterface instance representing the URI of the request. - */ - public function getUri(): UriInterface - { - return $this->uri; - } - - /** - * Returns an instance with the provided URI. - * @param UriInterface $uri New request URI to use. - * @param bool $preserveHost Preserve the original state of the Host header. - * @return static - */ - public function withUri(UriInterface $uri, bool $preserveHost = false): self - { - $new = clone $this; - $new->uri = $uri instanceof Uri ? $uri : new Uri((string)$uri); - if (!$preserveHost || !$new->hasHeader('host')) { - $new->removeHeader('host'); - $new->handleHeader('Host', $this->formatHostHeader($uri)); - } - return $new; - } - - public function __toString(): string - { - return $this->stringable('%s %s', $this->getMethod(), $this->getUri()); - } - - /** @return array */ - public function getAsArray(): array - { - return array_merge([ - "{$this->getMethod()} {$this->getRequestTarget()} HTTP/{$this->getProtocolVersion()}", - ], parent::getAsArray()); - } - - private function formatHostHeader(UriInterface $uri): string - { - $host = $uri->getHost(); - $port = $uri->getPort(); - return $host && $port ? "{$host}:{$port}" : $host; + parent::__construct($method, $uri); } } diff --git a/src/Http/Response.php b/src/Http/Response.php index 15d7ec2..0074a41 100644 --- a/src/Http/Response.php +++ b/src/Http/Response.php @@ -7,144 +7,19 @@ namespace WebSocket\Http; -use InvalidArgumentException; -use Phrity\Net\Uri; -use Psr\Http\Message\{ - ResponseInterface, - UriInterface -}; -use RuntimeException; +use Nyholm\Psr7\Response as NyholmResponse; +use Psr\Http\Message\ResponseInterface; /** * Phrity\WebSocket\Http\Response class. * Only used for handshake procedure. + * @deprecated To be removed in v4, use Nyholm directly instead + * @phpstan-ignore class.extendsFinalByPhpDoc */ -class Response extends Message implements ResponseInterface +class Response extends NyholmResponse implements ResponseInterface { - /** @var array $codes */ - private static array $codes = [ - 100 => 'Continue', - 101 => 'Switching Protocols', - 102 => 'Processing', - 103 => 'Early Hints', - 200 => 'OK', - 201 => 'Created', - 202 => 'Accepted', - 203 => 'Non-Authoritative Information', - 204 => 'No Content', - 205 => 'Reset Content', - 206 => 'Partial Content', - 207 => 'Multi-Status', - 208 => 'Already Reported', - 226 => 'IM Used', - 300 => 'Multiple Choices', - 301 => 'Moved Permanently', - 302 => 'Found', - 303 => 'See Other', - 304 => 'Not Modified', - 305 => 'Use Proxy', - 307 => 'Temporary Redirect', - 308 => 'Permanent Redirect', - 400 => 'Bad Request', - 401 => 'Unauthorized', - 402 => 'Payment Required', - 403 => 'Forbidden', - 404 => 'Not Found', - 405 => 'Method Not Allowed', - 406 => 'Not Acceptable', - 407 => 'Proxy Authentication Required', - 408 => 'Request Timeout', - 409 => 'Conflict', - 410 => 'Gone', - 411 => 'Length Required', - 412 => 'Precondition Failed', - 413 => 'Content Too Large', - 414 => 'URI Too Long', - 415 => 'Unsupported Media Type', - 416 => 'Range Not Satisfiable', - 417 => 'Expectation Failed', - 421 => 'Misdirected Request', - 422 => 'Unprocessable Content', - 423 => 'Locked', - 424 => 'Failed Dependency', - 425 => 'Too Early', - 426 => 'Upgrade Required', - 428 => 'Precondition Required', - 429 => 'Too Many Requests', - 431 => 'Request Header Fields Too Large', - 451 => 'Unavailable For Legal Reasons', - 500 => 'Internal Server Error', - 501 => 'Not Implemented', - 502 => 'Bad Gateway', - 503 => 'Service Unavailable', - 504 => 'Gateway Timeout', - 505 => 'HTTP Version Not Supported', - 506 => 'Variant Also Negotiates', - 507 => 'Insufficient Storage', - 508 => 'Loop Detected', - 510 => 'Not Extended', - 511 => 'Network Authentication Required', - ]; - - private int $code; - private string $reason; - public function __construct(int $code = 200, string $reasonPhrase = '') { - if ($code < 100 || $code > 599) { - throw new InvalidArgumentException("Invalid status code '{$code}' provided."); - } - $this->code = $code; - $this->reason = $reasonPhrase; - } - - /** - * Gets the response status code. - * @return int Status code. - */ - public function getStatusCode(): int - { - return $this->code; - } - - /** - * Return an instance with the specified status code and, optionally, reason phrase. - * @param int $code The 3-digit integer result code to set. - * @param string $reasonPhrase The reason phrase to use. - * @return static - * @throws InvalidArgumentException For invalid status code arguments. - */ - public function withStatus(int $code, string $reasonPhrase = ''): self - { - if ($code < 100 || $code > 599) { - throw new InvalidArgumentException("Invalid status code '{$code}' provided."); - } - $new = clone $this; - $new->code = $code; - $new->reason = $reasonPhrase; - return $new; - } - - /** - * Gets the response reason phrase associated with the status code. - * @return string Reason phrase; must return an empty string if none present. - */ - public function getReasonPhrase(): string - { - $d = self::$codes[$this->code]; - return $this->reason ?: $d; - } - - public function __toString(): string - { - return $this->stringable('%s', $this->getStatusCode()); - } - - /** @return array */ - public function getAsArray(): array - { - return array_merge([ - "HTTP/{$this->getProtocolVersion()} {$this->getStatusCode()} {$this->getReasonPhrase()}", - ], parent::getAsArray()); + parent::__construct($code, reason: $reasonPhrase); } } diff --git a/src/Http/ServerRequest.php b/src/Http/ServerRequest.php index 7478c61..1018d2b 100644 --- a/src/Http/ServerRequest.php +++ b/src/Http/ServerRequest.php @@ -7,7 +7,7 @@ namespace WebSocket\Http; -use BadMethodCallException; +use Nyholm\Psr7\ServerRequest as NyholmServerRequest; use Psr\Http\Message\{ ServerRequestInterface, UriInterface @@ -16,138 +16,13 @@ /** * WebSocket\Http\ServerRequest class. * Only used for handshake procedure. + * @deprecated To be removed in v4, use Nyholm directly instead + * @phpstan-ignore class.extendsFinalByPhpDoc */ -class ServerRequest extends Request implements ServerRequestInterface +class ServerRequest extends NyholmServerRequest implements ServerRequestInterface { - /** - * Retrieve server parameters. - * @return array - */ - public function getServerParams(): array + public function __construct(string $method = 'GET', UriInterface|string $uri = '') { - throw new BadMethodCallException("Not implemented."); - } - - /** - * Retrieves cookies sent by the client to the server. - * @return array - */ - public function getCookieParams(): array - { - throw new BadMethodCallException("Not implemented."); - } - - /** - * Return an instance with the specified cookies. - * @param array $cookies Array of key/value pairs representing cookies. - * @return static - */ - public function withCookieParams(array $cookies): self - { - throw new BadMethodCallException("Not implemented."); - } - - /** - * Retrieves the deserialized query string arguments, if any. - * @return array - */ - public function getQueryParams(): array - { - parse_str($this->getUri()->getQuery(), $result); - return $result; - } - - /** - * Return an instance with the specified query string arguments. - * @param array $query Array of query string arguments - * @return static - */ - public function withQueryParams(array $query): self - { - throw new BadMethodCallException("Not implemented."); - } - - /** - * Retrieve normalized file upload data. - * @return array An array tree of UploadedFileInterface instances. - */ - public function getUploadedFiles(): array - { - throw new BadMethodCallException("Not implemented."); - } - - /** - * Create a new instance with the specified uploaded files. - * @param array $uploadedFiles An array tree of UploadedFileInterface instances. - * @return static - */ - public function withUploadedFiles(array $uploadedFiles): self - { - throw new BadMethodCallException("Not implemented."); - } - - /** - * Retrieve any parameters provided in the request body. - * @return null|array|object The deserialized body parameters, if any. - */ - public function getParsedBody() - { - throw new BadMethodCallException("Not implemented."); - } - - /** - * Return an instance with the specified body parameters. - * @param null|array|object $data The deserialized body data. - * @return static - */ - public function withParsedBody($data): self - { - throw new BadMethodCallException("Not implemented."); - } - - /** - * Retrieve attributes derived from the request. - * @return mixed[] Attributes derived from the request. - */ - public function getAttributes(): array - { - throw new BadMethodCallException("Not implemented."); - } - - /** - * Retrieve a single derived request attribute. - * @param string $name The attribute name. - * @param mixed $default Default value to return if the attribute does not exist. - * @return mixed - */ - public function getAttribute(string $name, $default = null) - { - throw new BadMethodCallException("Not implemented."); - } - - /** - * Return an instance with the specified derived request attribute. - * @param string $name The attribute name. - * @param mixed $value The value of the attribute. - * @return static - */ - public function withAttribute(string $name, $value): self - { - throw new BadMethodCallException("Not implemented."); - } - - /** - * Return an instance that removes the specified derived request attribute. - * @param string $name The attribute name. - * @return static - */ - public function withoutAttribute(string $name): self - { - throw new BadMethodCallException("Not implemented."); - } - - public function __toString(): string - { - return $this->stringable('%s %s', $this->getMethod(), $this->getRequestTarget()); + parent::__construct($method, $uri); } } diff --git a/src/Server.php b/src/Server.php index 6a9bf75..2db34c8 100644 --- a/src/Server.php +++ b/src/Server.php @@ -18,6 +18,10 @@ StreamFactory, Uri }; +use Psr\Http\Message\{ + ResponseInterface, + ServerRequestInterface, +}; use Psr\Log\{ LoggerAwareInterface, LoggerInterface, @@ -33,11 +37,7 @@ MessageLevelInterface, ServerException }; -use WebSocket\Http\{ - DefaultHttpFactory, - Response, - ServerRequest, -}; +use WebSocket\Http\DefaultHttpFactory; use WebSocket\Message\Message; use WebSocket\Middleware\MiddlewareInterface; use WebSocket\Trait\{ @@ -581,13 +581,13 @@ protected function detachUnconnected(): void } // Perform upgrade handshake on new connections. - protected function performHandshake(Connection $connection): ServerRequest + protected function performHandshake(Connection $connection): ServerRequestInterface { - $response = $this->httpFactory->createResponse(101); + $response = $this->httpFactory->createResponse(101, 'Switching Protocols'); $exception = null; // Read handshake request - /** @var ServerRequest */ + /** @var ServerRequestInterface */ $request = $connection->pullHttp(); // Verify handshake request @@ -645,7 +645,7 @@ protected function performHandshake(Connection $connection): ServerRequest } // Respond to handshake - /** @var Response */ + /** @var ResponseInterface */ $response = $connection->pushHttp($response); if ($response->getStatusCode() != 101) { $exception = new HandshakeException("Invalid status code {$response->getStatusCode()}", $response); diff --git a/tests/suites/client/ClientTest.php b/tests/suites/client/ClientTest.php index babbd9f..ef632fc 100644 --- a/tests/suites/client/ClientTest.php +++ b/tests/suites/client/ClientTest.php @@ -27,6 +27,10 @@ Uri, }; use Phrity\Util\ErrorHandler; +use Psr\Http\Message\{ + RequestInterface, + ResponseInterface, +}; use Stringable; use WebSocket\{ Client, @@ -39,10 +43,6 @@ CloseException, ReconnectException, }; -use WebSocket\Http\{ - Request, - Response -}; use WebSocket\Test\MockStreamTrait; use WebSocket\Message\{ Binary, @@ -812,15 +812,15 @@ public function testListeners(): void $client->onHandshake(function ($client, $connection, $request, $response) { $this->assertInstanceOf(Client::class, $client); $this->assertInstanceOf(Connection::class, $connection); - $this->assertInstanceOf(Request::class, $request); - $this->assertInstanceOf(Response::class, $response); + $this->assertInstanceOf(RequestInterface::class, $request); + $this->assertInstanceOf(ResponseInterface::class, $response); $this->assertTrue($client->isRunning()); }); $handler->withAll(function () use ($client) { $client->onConnect(function ($client, $connection, $response) { $this->assertInstanceOf(Client::class, $client); $this->assertInstanceOf(Connection::class, $connection); - $this->assertInstanceOf(Response::class, $response); + $this->assertInstanceOf(ResponseInterface::class, $response); $this->assertTrue($client->isRunning()); $client->stop(); }); diff --git a/tests/suites/client/ConfigTest.php b/tests/suites/client/ConfigTest.php index 12b00d0..bec6674 100644 --- a/tests/suites/client/ConfigTest.php +++ b/tests/suites/client/ConfigTest.php @@ -9,7 +9,7 @@ namespace WebSocket\Test\Client; -use GuzzleHttp\Psr7\HttpFactory as GuzzleFactory; +use Nyholm\Psr7\Factory\Psr17Factory; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Phrity\Http\HttpFactory; @@ -345,7 +345,7 @@ public function testConfigConnectedClient(): void public function testHttpFactories(): void { - $httpFactory = new GuzzleFactory(); + $httpFactory = new Psr17Factory(); $this->expectStreamFactory(); $client = new Client('ws://localhost:8000/my/mock/path'); $client->setStreamFactory(new StreamFactory()); diff --git a/tests/suites/client/HandshakeTest.php b/tests/suites/client/HandshakeTest.php index 769f09e..061935a 100644 --- a/tests/suites/client/HandshakeTest.php +++ b/tests/suites/client/HandshakeTest.php @@ -19,6 +19,7 @@ }; use Phrity\Net\StreamException; use Phrity\Net\Uri; +use Psr\Http\Message\ResponseInterface; use WebSocket\Client; use WebSocket\Exception\{ BadOpcodeException, @@ -30,7 +31,6 @@ HandshakeException, ReconnectException, }; -use WebSocket\Http\Response; use WebSocket\Test\MockStreamTrait; /** @@ -70,7 +70,7 @@ public function testHandshakeResponse(): void $client->connect(); $response = $client->getHandshakeResponse(); - $this->assertInstanceOf(Response::class, $response); + $this->assertInstanceOf(ResponseInterface::class, $response); $this->assertEquals(101, $response->getStatusCode()); $this->assertEquals('Switching Protocols', $response->getReasonPhrase()); @@ -97,7 +97,7 @@ function (string $method, array $params): void { } ); $this->expectSocketStreamReadLine()->setReturn(function (array $params) { - return "HTTP/1.1 101\r\n"; + return "HTTP/1.1 101 Switching Protocols\r\n"; }); $this->expectSocketStreamReadLine()->setReturn(function (array $params) { return "Upgrade: websocket\r\n"; @@ -115,7 +115,7 @@ function (string $method, array $params): void { $client->connect(); $response = $client->getHandshakeResponse(); - $this->assertInstanceOf(Response::class, $response); + $this->assertInstanceOf(ResponseInterface::class, $response); $this->assertEquals(101, $response->getStatusCode()); $this->assertEquals('Switching Protocols', $response->getReasonPhrase()); @@ -181,7 +181,7 @@ public function testHandshakeUpgradeHeadersFailure(): void return "HTTP/1.1 101 Switching Protocols\r\n"; }); $this->expectSocketStreamReadLine()->setReturn(function () { - return "Upgrade: websocket\r\nInvalid upgrade\r\n"; + return "Upgrade: Invalid upgrade\r\n"; }); $this->expectSocketStreamReadLine()->setReturn(function () { return "\r\n"; @@ -207,7 +207,7 @@ public function testHandshakeUpgradeKeyFailure(): void return "HTTP/1.1 101 Switching Protocols\r\n"; }); $this->expectSocketStreamReadLine()->setReturn(function () { - return "Upgrade: websocket\r\nInvalid upgrade\r\n"; + return "Upgrade: websocket\r\n"; }); $this->expectSocketStreamReadLine()->setReturn(function () { return "Sec-WebSocket-Accept: BAD_KEY\r\n"; @@ -243,7 +243,7 @@ public function testHandshakeReconnect(): void $client->connect(); $response = $client->getHandshakeResponse(); - $this->assertInstanceOf(Response::class, $response); + $this->assertInstanceOf(ResponseInterface::class, $response); $this->assertEquals(101, $response->getStatusCode()); $this->assertEquals('Switching Protocols', $response->getReasonPhrase()); diff --git a/tests/suites/connection/ConnectionTest.php b/tests/suites/connection/ConnectionTest.php index 628943a..a7a560c 100644 --- a/tests/suites/connection/ConnectionTest.php +++ b/tests/suites/connection/ConnectionTest.php @@ -9,6 +9,7 @@ namespace WebSocket\Test\Connection; +use Nyholm\Psr7\Factory\Psr17Factory; use PHPUnit\Framework\TestCase; use Phrity\Net\Context; use Phrity\Net\Mock\SocketStream; @@ -16,6 +17,7 @@ ExpectContextTrait, ExpectSocketStreamTrait, }; +use Psr\Http\Message\ResponseInterface; use Psr\Log\NullLogger; use Stringable; use WebSocket\Connection; @@ -26,10 +28,6 @@ ConnectionFailureException, ConnectionTimeoutException }; -use WebSocket\Http\{ - Request, - Response -}; use WebSocket\Message\{ Ping, Text @@ -44,10 +42,13 @@ class ConnectionTest extends TestCase use ExpectContextTrait; use ExpectSocketStreamTrait; + private Psr17Factory $psrFactory; + public function setUp(): void { error_reporting(-1); $this->setUpStack(); + $this->psrFactory = new Psr17Factory(); } public function tearDown(): void @@ -151,7 +152,7 @@ public function testHttpMessages(): void $this->expectSocketStreamGetLocalName(); $this->expectSocketStreamGetRemoteName(); $connection = new Connection($stream, false, false); - $request = new Request('GET', 'ws://test.com/path'); + $request = $this->psrFactory->createRequest('GET', 'ws://test.com/path'); $connection->setHandshakeRequest($request); $this->assertSame($request, $connection->getHandshakeRequest()); @@ -170,7 +171,7 @@ public function testHttpMessages(): void return "\r\n"; }); $response = $connection->pullHttp(); - $this->assertInstanceOf(Response::class, $response); + $this->assertInstanceOf(ResponseInterface::class, $response); $connection->setHandshakeResponse($response); $this->assertSame($response, $connection->getHandshakeResponse()); @@ -236,7 +237,7 @@ public function testSendHttpError(): void $this->expectExceptionMessage('Connection has unexpectedly closed'); $this->expectSocketStreamIsConnected(); $this->expectSocketStreamClose(); - $connection->pushHttp(new Request()); + $connection->pushHttp($this->psrFactory->createRequest('GET', '/')); unset($connection); } diff --git a/tests/suites/http/HttpHandlerTest.php b/tests/suites/http/HttpHandlerTest.php index 4ebf13a..df416af 100644 --- a/tests/suites/http/HttpHandlerTest.php +++ b/tests/suites/http/HttpHandlerTest.php @@ -11,6 +11,7 @@ use BadMethodCallException; use GuzzleHttp\Psr7\Request as GuzzleRequest; +use Nyholm\Psr7\Factory\Psr17Factory; use PHPUnit\Framework\TestCase; use Phrity\Logger\Console\ConsoleLogger; use Phrity\Net\Mock\SocketStream; @@ -23,17 +24,13 @@ use Phrity\Util\ErrorHandler; use Psr\Http\Message\{ RequestInterface, + ResponseInterface, + ServerRequestInterface, UriInterface }; use RuntimeException; use Stringable; -use WebSocket\Http\{ - HttpHandler, - Message, - Request, - Response, - ServerRequest -}; +use WebSocket\Http\HttpHandler; /** * Test case for WebSocket\Http\HttpHandler. @@ -43,10 +40,13 @@ class HttpHandlerTest extends TestCase use ExpectContextTrait; use ExpectSocketStreamTrait; + private Psr17Factory $psrFactory; + public function setUp(): void { error_reporting(-1); $this->setUpStack(); + $this->psrFactory = new Psr17Factory(); } public function tearDown(): void @@ -65,7 +65,7 @@ public function testPushRequest(): void $handler = new HttpHandler($stream); $this->assertInstanceOf(HttpHandler::class, $handler); - $request = new Request('GET', 'ws://test.com:123/a/path?a=b'); + $request = $this->psrFactory->createServerRequest('GET', 'ws://test.com:123/a/path?a=b'); $this->expectSocketStreamWrite()->addAssert(function ($method, $params) { $expect = "GET /a/path?a=b HTTP/1.1\r\nHost: test.com:123\r\n\r\n"; @@ -90,7 +90,7 @@ public function testPushServerRequest(): void $handler = new HttpHandler($stream); $this->assertInstanceOf(HttpHandler::class, $handler); - $request = new ServerRequest('GET', 'ws://test.com:123/a/path?a=b'); + $request = $this->psrFactory->createServerRequest('GET', 'ws://test.com:123/a/path?a=b'); $this->expectSocketStreamWrite()->addAssert(function ($method, $params) { $expect = "GET /a/path?a=b HTTP/1.1\r\nHost: test.com:123\r\n\r\n"; @@ -132,7 +132,7 @@ public function testPullServerRequest(): void return "\r\n"; }); $request = $handler->pull(); - $this->assertInstanceOf(ServerRequest::class, $request); + $this->assertInstanceOf(ServerRequestInterface::class, $request); $this->assertEquals('/a/path?a=b', $request->getRequestTarget()); $this->assertEquals('GET', $request->getMethod()); $this->assertEquals('1.1', $request->getProtocolVersion()); @@ -159,8 +159,7 @@ public function testPushResponse(): void $handler = new HttpHandler($stream); $this->assertInstanceOf(HttpHandler::class, $handler); - $response = new Response(200); - $response = $response->withHeader('Host', 'test.com:123'); + $response = $this->psrFactory->createResponse(200)->withHeader('Host', 'test.com:123'); $this->expectSocketStreamWrite()->addAssert(function ($method, $params) { $expect = "HTTP/1.1 200 OK\r\nHost: test.com:123\r\n\r\n"; @@ -193,7 +192,7 @@ public function testPullResponse(): void return "\r\n"; }); $response = $handler->pull(); - $this->assertInstanceOf(Response::class, $response); + $this->assertInstanceOf(ResponseInterface::class, $response); $this->assertEquals('1.1', $response->getProtocolVersion()); $this->assertEquals(['Host' => ['test.com:123']], $response->getHeaders()); $this->assertTrue($response->hasHeader('Host')); @@ -231,7 +230,7 @@ public function testFragmentedPullResponse(): void return "\n"; }); $response = $handler->pull(); - $this->assertInstanceOf(Response::class, $response); + $this->assertInstanceOf(ResponseInterface::class, $response); $this->assertEquals('1.1', $response->getProtocolVersion()); $this->assertEquals(['Host' => ['test.com:123']], $response->getHeaders()); $this->assertTrue($response->hasHeader('Host')); @@ -283,6 +282,7 @@ public function testPullEmptyReadError(): void fclose($temp); } +/* public function testPushUnsupported(): void { $temp = tmpfile(); @@ -301,6 +301,7 @@ public function testPushUnsupported(): void fclose($temp); } +*/ public function testDeprecatedLogger(): void { diff --git a/tests/suites/http/RequestTest.php b/tests/suites/http/RequestTest.php index 1aadade..8fe0381 100644 --- a/tests/suites/http/RequestTest.php +++ b/tests/suites/http/RequestTest.php @@ -20,11 +20,7 @@ RequestInterface, UriInterface }; -use Stringable; -use WebSocket\Http\{ - Message, - Request -}; +use WebSocket\Http\Request; /** * Test case for WebSocket\Http\Request. @@ -40,22 +36,15 @@ public function testEmptyRequest(): void { $request = new Request(); $this->assertInstanceOf(Request::class, $request); - $this->assertInstanceOf(Message::class, $request); $this->assertInstanceOf(RequestInterface::class, $request); $this->assertEquals('/', $request->getRequestTarget()); $this->assertEquals('GET', $request->getMethod()); $this->assertInstanceOf(UriInterface::class, $request->getUri()); $this->assertEquals('1.1', $request->getProtocolVersion()); - $this->assertEquals(['Host' => ['']], $request->getHeaders()); + $this->assertEquals([], $request->getHeaders()); $this->assertFalse($request->hasHeader('none')); $this->assertEquals([], $request->getHeader('none')); $this->assertEquals('', $request->getHeaderLine('none')); - $this->assertInstanceOf(Stringable::class, $request); - $this->assertEquals('WebSocket\Http\Request(GET )', "{$request}"); - $this->assertEquals([ - 'GET / HTTP/1.1', - 'Host: ', - ], $request->getAsArray()); } public function testUriInstanceRequest(): void @@ -69,11 +58,6 @@ public function testUriInstanceRequest(): void $this->assertTrue($request->hasHeader('Host')); $this->assertEquals(['test.com:123'], $request->getHeader('Host')); $this->assertEquals('test.com:123', $request->getHeaderLine('Host')); - $this->assertEquals('WebSocket\Http\Request(POST ws://test.com:123/a/path?a=b)', "{$request}"); - $this->assertEquals([ - 'POST /a/path?a=b HTTP/1.1', - 'Host: test.com:123', - ], $request->getAsArray()); } public function testUriStringRequest(): void @@ -86,11 +70,6 @@ public function testUriStringRequest(): void $this->assertTrue($request->hasHeader('Host')); $this->assertEquals(['test.com:123'], $request->getHeader('Host')); $this->assertEquals('test.com:123', $request->getHeaderLine('Host')); - $this->assertEquals('WebSocket\Http\Request(POST ws://test.com:123/a/path?a=b)', "{$request}"); - $this->assertEquals([ - 'POST /a/path?a=b HTTP/1.1', - 'Host: test.com:123', - ], $request->getAsArray()); } public function testImmutability(): void @@ -124,10 +103,6 @@ public function testHeaders(): void $this->assertEquals([ 'Host' => ['test.com:123'], ], $request1->getHeaders()); - $this->assertEquals([ - 'GET /a/path?a=b HTTP/1.1', - 'Host: test.com:123', - ], $request1->getAsArray()); $request2 = $request1->withHeader('Test-Header', 'Test-Value-1'); $this->assertNotSame($request2, $request1); @@ -135,11 +110,6 @@ public function testHeaders(): void 'Host' => ['test.com:123'], 'Test-Header' => ['Test-Value-1'], ], $request2->getHeaders()); - $this->assertEquals([ - 'GET /a/path?a=b HTTP/1.1', - 'Host: test.com:123', - 'Test-Header: Test-Value-1', - ], $request2->getAsArray()); $request3 = $request2->withHeader('Test-Header', 'Test-Value-2'); $this->assertNotSame($request3, $request2); @@ -147,11 +117,6 @@ public function testHeaders(): void 'Host' => ['test.com:123'], 'Test-Header' => ['Test-Value-2'], ], $request3->getHeaders()); - $this->assertEquals([ - 'GET /a/path?a=b HTTP/1.1', - 'Host: test.com:123', - 'Test-Header: Test-Value-2', - ], $request3->getAsArray()); $request4 = $request3->withAddedHeader('Test-Header', 'Test-Value-3'); $this->assertNotSame($request4, $request3); @@ -159,105 +124,94 @@ public function testHeaders(): void 'Host' => ['test.com:123'], 'Test-Header' => ['Test-Value-2', 'Test-Value-3'], ], $request4->getHeaders()); - $this->assertEquals([ - 'GET /a/path?a=b HTTP/1.1', - 'Host: test.com:123', - 'Test-Header: Test-Value-2', - 'Test-Header: Test-Value-3', - ], $request4->getAsArray()); $request5 = $request4->withoutHeader('Test-Header'); $this->assertNotSame($request5, $request4); $this->assertEquals([ 'Host' => ['test.com:123'], ], $request5->getHeaders()); - $this->assertEquals([ - 'GET /a/path?a=b HTTP/1.1', - 'Host: test.com:123', - ], $request5->getAsArray()); $request6 = $request5->withUri(new Uri('ws://another.com:456/new/path?a=b')); $this->assertNotSame($request6, $request5); $this->assertEquals([ 'Host' => ['another.com:456'], ], $request6->getHeaders()); - $this->assertEquals([ - 'GET /new/path?a=b HTTP/1.1', - 'Host: another.com:456', - ], $request6->getAsArray()); $request7 = $request6->withUri(new Uri('ws://yetanother.com:789/new/path?a=b'), true); $this->assertNotSame($request7, $request6); $this->assertEquals([ 'Host' => ['another.com:456'], ], $request7->getHeaders()); - $this->assertEquals([ - 'GET /new/path?a=b HTTP/1.1', - 'Host: another.com:456', - ], $request6->getAsArray()); } - public function testGetBodyError(): void + public function testGetBody(): void { $request = new Request(); - $this->expectException(BadMethodCallException::class); - $this->expectExceptionCode(0); - $this->expectExceptionMessage('Not implemented.'); - $request->getBody(); + $this->assertEmpty($request->getBody()->getContents()); } - public function testWithBodyError(): void + public function testWithBody(): void { $request = new Request(); $factory = new StreamFactory(); - $this->expectException(BadMethodCallException::class); - $this->expectExceptionCode(0); - $this->expectExceptionMessage('Not implemented.'); - $request->withBody($factory->createStream()); + $request_2 = $request->withBody($factory->createStream()); + $this->assertNotSame($request, $request_2); } - public function testConstructMethodError(): void + // @todo Should fail + public function testConstructMethod(): void { - $this->expectException(InvalidArgumentException::class); - $this->expectExceptionCode(0); - $this->expectExceptionMessage("Invalid method 'INVALID' provided"); $request = new Request('INVALID'); + $this->assertEquals('INVALID', $request->getMethod()); } - public function testWithMethodError(): void + // @todo Should fail + public function testWithMethod(): void { $request = new Request(); - $this->expectException(InvalidArgumentException::class); - $this->expectExceptionCode(0); - $this->expectExceptionMessage("Invalid method 'INVALID' provided"); $request->withMethod('INVALID'); + $this->assertEquals('GET', $request->getMethod()); } - public function testHeaderNameError(): void + // @todo Should fail + public function testHeaderName(): void { $request = new Request(); - $this->expectException(InvalidArgumentException::class); - $this->expectExceptionCode(0); - $this->expectExceptionMessage("'.' is not a valid header field name."); - $request->withHeader('.', 'invalid name'); + $request = $request->withHeader('.', 'invalid name'); + $this->assertEquals('invalid name', $request->getHeaderLine('.')); } - #[DataProvider('provideInvalidHeaderValues')] - public function testHeaderValueInvalidVariants(mixed $value): void + #[DataProvider('provideNotCompatibleValues')] + public function testHeaderValueNotCompatible(mixed $value): void { $request = new Request(); $this->expectException(InvalidArgumentException::class); $this->expectExceptionCode(0); - $this->expectExceptionMessage("Invalid header value(s) provided."); + $this->expectExceptionMessage("Header values must be RFC 7230 compatible strings"); $request->withHeader('name', $value); } - public static function provideInvalidHeaderValues(): Generator + public static function provideNotCompatibleValues(): Generator { yield [[null]]; yield [[[0]]]; } + #[DataProvider('provideEmptyValues')] + public function testHeaderValueEmpty(mixed $value): void + { + $request = new Request(); + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionCode(0); + $this->expectExceptionMessage("Header values must be a string or an array of strings, empty array given"); + $request->withHeader('name', $value); + } + + public static function provideEmptyValues(): Generator + { + yield [[]]; + } + /** @param array $expected */ #[DataProvider('provideValidHeaderValues')] public function testHeaderValueValidVariants(mixed $value, array $expected): void @@ -273,7 +227,6 @@ public static function provideValidHeaderValues(): Generator yield ['', ['']]; yield [' ', ['']]; yield [['0', ''], ['0', '']]; - yield [[], []]; yield ['null', ['null']]; yield ['0 ', ['0']]; yield [' 0', ['0']]; diff --git a/tests/suites/http/ResponseTest.php b/tests/suites/http/ResponseTest.php index a3df1bc..8045e19 100644 --- a/tests/suites/http/ResponseTest.php +++ b/tests/suites/http/ResponseTest.php @@ -18,11 +18,7 @@ ResponseInterface, UriInterface }; -use Stringable; -use WebSocket\Http\{ - Message, - Response -}; +use WebSocket\Http\Response; /** * Test case for WebSocket\Http\Response. @@ -38,31 +34,21 @@ public function testEmptyResponse(): void { $response = new Response(); $this->assertInstanceOf(Response::class, $response); - $this->assertInstanceOf(Message::class, $response); $this->assertInstanceOf(ResponseInterface::class, $response); $this->assertEquals(200, $response->getStatusCode()); - $this->assertEquals('OK', $response->getReasonPhrase()); + $this->assertEquals('', $response->getReasonPhrase()); $this->assertEquals('1.1', $response->getProtocolVersion()); $this->assertEquals([], $response->getHeaders()); $this->assertFalse($response->hasHeader('none')); $this->assertEquals([], $response->getHeader('none')); $this->assertEquals('', $response->getHeaderLine('none')); - $this->assertInstanceOf(Stringable::class, $response); - $this->assertEquals('WebSocket\Http\Response(200)', "{$response}"); - $this->assertEquals([ - 'HTTP/1.1 200 OK', - ], $response->getAsArray()); } public function testCodeResponse(): void { - $response = new Response(404); + $response = new Response(404, 'Not Found'); $this->assertEquals(404, $response->getStatusCode()); $this->assertEquals('Not Found', $response->getReasonPhrase()); - $this->assertEquals('WebSocket\Http\Response(404)', "{$response}"); - $this->assertEquals([ - 'HTTP/1.1 404 Not Found', - ], $response->getAsArray()); } public function testCodeReasonResponse(): void @@ -70,10 +56,6 @@ public function testCodeReasonResponse(): void $response = new Response(400, 'Custom reason phrase'); $this->assertEquals(400, $response->getStatusCode()); $this->assertEquals('Custom reason phrase', $response->getReasonPhrase()); - $this->assertEquals('WebSocket\Http\Response(400)', "{$response}"); - $this->assertEquals([ - 'HTTP/1.1 400 Custom reason phrase', - ], $response->getAsArray()); } public function testImmutability(): void @@ -92,18 +74,13 @@ public function testImmutability(): void $responseClone = $response->withHeader('Test-Header', 'Test-Value'); $this->assertNotSame($responseClone, $response); $this->assertEquals(['Test-Value'], $responseClone->getHeader('Test-Header')); - $this->assertEquals([ - 'HTTP/1.1 200 OK', - 'Test-Header: Test-Value', - ], $responseClone->getAsArray()); } - public function testConstructStatusError(): void + // @todo Should fail + public function testConstructStatus(): void { - $this->expectException(InvalidArgumentException::class); - $this->expectExceptionCode(0); - $this->expectExceptionMessage("Invalid status code '99' provided."); $response = new Response(99); + $this->assertEquals(99, $response->getStatusCode()); } public function testWithStatusError(): void @@ -111,35 +88,31 @@ public function testWithStatusError(): void $response = new Response(); $this->expectException(InvalidArgumentException::class); $this->expectExceptionCode(0); - $this->expectExceptionMessage("Invalid status code '99' provided."); + $this->expectExceptionMessage( + "Status code has to be an integer between 100 and 599. A status code of 99 was given" + ); $response->withStatus(99); } - public function testGetBodyError(): void + public function testGetBody(): void { $response = new Response(); - $this->expectException(BadMethodCallException::class); - $this->expectExceptionCode(0); - $this->expectExceptionMessage('Not implemented.'); - $response->getBody(); + $this->assertEmpty($response->getBody()->getContents()); } - public function testWithBodyError(): void + public function testWithBody(): void { $response = new Response(); $factory = new StreamFactory(); - $this->expectException(BadMethodCallException::class); - $this->expectExceptionCode(0); - $this->expectExceptionMessage('Not implemented.'); - $response->withBody($factory->createStream()); + $response_2 = $response->withBody($factory->createStream()); + $this->assertNotSame($response, $response_2); } - public function testHeaderNameError(): void + // @todo Should fail + public function testHeaderName(): void { $response = new Response(); - $this->expectException(InvalidArgumentException::class); - $this->expectExceptionCode(0); - $this->expectExceptionMessage("'.' is not a valid header field name."); - $response->withHeader('.', 'invalid name'); + $response = $response->withHeader('.', 'invalid name'); + $this->assertEquals('invalid name', $response->getHeaderLine('.')); } } diff --git a/tests/suites/http/ServerRequestTest.php b/tests/suites/http/ServerRequestTest.php index 71238db..a68caac 100644 --- a/tests/suites/http/ServerRequestTest.php +++ b/tests/suites/http/ServerRequestTest.php @@ -16,11 +16,7 @@ ServerRequestInterface, UriInterface }; -use Stringable; -use WebSocket\Http\{ - Message, - ServerRequest -}; +use WebSocket\Http\ServerRequest; /** * Test case for WebSocket\Http\ServerRequest. @@ -36,23 +32,16 @@ public function testEmptyRequest(): void { $request = new ServerRequest(); $this->assertInstanceOf(ServerRequest::class, $request); - $this->assertInstanceOf(Message::class, $request); $this->assertInstanceOf(ServerRequestInterface::class, $request); $this->assertEquals('/', $request->getRequestTarget()); $this->assertEquals('GET', $request->getMethod()); $this->assertInstanceOf(UriInterface::class, $request->getUri()); $this->assertEquals('1.1', $request->getProtocolVersion()); - $this->assertEquals(['Host' => ['']], $request->getHeaders()); + $this->assertEquals([], $request->getHeaders()); $this->assertFalse($request->hasHeader('none')); $this->assertEquals([], $request->getHeader('none')); $this->assertEquals('', $request->getHeaderLine('none')); $this->assertEquals([], $request->getQueryParams()); - $this->assertInstanceOf(Stringable::class, $request); - $this->assertEquals('WebSocket\Http\ServerRequest(GET /)', "{$request}"); - $this->assertEquals([ - 'GET / HTTP/1.1', - 'Host: ', - ], $request->getAsArray()); } public function testUriInstanceRequest(): void @@ -67,118 +56,5 @@ public function testUriInstanceRequest(): void $this->assertEquals(['test.com:123'], $request->getHeader('Host')); $this->assertEquals('test.com:123', $request->getHeaderLine('Host')); $this->assertEquals(['a' => 'b', 'c' => 'd'], $request->getQueryParams()); - $this->assertEquals('WebSocket\Http\ServerRequest(POST /a/path?a=b&c=d)', "{$request}"); - $this->assertEquals([ - 'POST /a/path?a=b&c=d HTTP/1.1', - 'Host: test.com:123', - ], $request->getAsArray()); - } - - public function testGetServerParamsError(): void - { - $request = new ServerRequest(); - $this->expectException(BadMethodCallException::class); - $this->expectExceptionCode(0); - $this->expectExceptionMessage('Not implemented.'); - $request->getServerParams(); - } - - public function testGetCookieParamsError(): void - { - $request = new ServerRequest(); - $this->expectException(BadMethodCallException::class); - $this->expectExceptionCode(0); - $this->expectExceptionMessage('Not implemented.'); - $request->getCookieParams(); - } - - public function testWithCookieParamsError(): void - { - $request = new ServerRequest(); - $this->expectException(BadMethodCallException::class); - $this->expectExceptionCode(0); - $this->expectExceptionMessage('Not implemented.'); - $request->withCookieParams([]); - } - - public function testWithQueryParamsError(): void - { - $request = new ServerRequest(); - $this->expectException(BadMethodCallException::class); - $this->expectExceptionCode(0); - $this->expectExceptionMessage('Not implemented.'); - $request->withQueryParams([]); - } - - public function testGetUploadedFilesError(): void - { - $request = new ServerRequest(); - $this->expectException(BadMethodCallException::class); - $this->expectExceptionCode(0); - $this->expectExceptionMessage('Not implemented.'); - $request->getUploadedFiles(); - } - - public function testWithUploadedFilesError(): void - { - $request = new ServerRequest(); - $this->expectException(BadMethodCallException::class); - $this->expectExceptionCode(0); - $this->expectExceptionMessage('Not implemented.'); - $request->withUploadedFiles([]); - } - - public function testGetParsedBodyError(): void - { - $request = new ServerRequest(); - $this->expectException(BadMethodCallException::class); - $this->expectExceptionCode(0); - $this->expectExceptionMessage('Not implemented.'); - $request->getParsedBody(); - } - - public function testWithParsedBodyError(): void - { - $request = new ServerRequest(); - $this->expectException(BadMethodCallException::class); - $this->expectExceptionCode(0); - $this->expectExceptionMessage('Not implemented.'); - $request->withParsedBody(null); - } - - public function testGetAttributesError(): void - { - $request = new ServerRequest(); - $this->expectException(BadMethodCallException::class); - $this->expectExceptionCode(0); - $this->expectExceptionMessage('Not implemented.'); - $request->getAttributes(); - } - - public function testGetAttributeError(): void - { - $request = new ServerRequest(); - $this->expectException(BadMethodCallException::class); - $this->expectExceptionCode(0); - $this->expectExceptionMessage('Not implemented.'); - $request->getAttribute('name'); - } - - public function testWithAttributeError(): void - { - $request = new ServerRequest(); - $this->expectException(BadMethodCallException::class); - $this->expectExceptionCode(0); - $this->expectExceptionMessage('Not implemented.'); - $request->withAttribute('name', 'value'); - } - - public function testWithoutAttributeError(): void - { - $request = new ServerRequest(); - $this->expectException(BadMethodCallException::class); - $this->expectExceptionCode(0); - $this->expectExceptionMessage('Not implemented.'); - $request->withoutAttribute('name'); } } diff --git a/tests/suites/middleware/CallbackTest.php b/tests/suites/middleware/CallbackTest.php index edb2c82..4418e27 100644 --- a/tests/suites/middleware/CallbackTest.php +++ b/tests/suites/middleware/CallbackTest.php @@ -9,15 +9,16 @@ namespace WebSocket\Test\Middleware; +use Nyholm\Psr7\Factory\Psr17Factory; use PHPUnit\Framework\TestCase; use Phrity\Net\Mock\SocketStream; use Psr\Log\NullLogger; +use Psr\Http\Message\{ + RequestInterface, + ResponseInterface, +}; use Stringable; use WebSocket\Connection; -use WebSocket\Http\{ - Request, - Response, -}; use WebSocket\Message\Text; use WebSocket\Middleware\Callback; use WebSocket\Test\MockStreamTrait; @@ -29,10 +30,13 @@ class CallbackTest extends TestCase { use MockStreamTrait; + private Psr17Factory $psrFactory; + public function setUp(): void { error_reporting(-1); $this->setUpStack(); + $this->psrFactory = new Psr17Factory(); } public function tearDown(): void @@ -134,7 +138,7 @@ public function testHttpIncoming(): void $this->expectSocketStreamReadLine()->setReturn(function () { return "\r\n"; }); - /** @var Request $message */ + /** @var RequestInterface $message */ $message = $connection->pullHttp(); $this->assertEquals('POST', $message->getMethod()); @@ -163,8 +167,8 @@ public function testHttpOutgoing(): void return $message; })); $this->expectSocketStreamWrite(); - /** @var Response $message */ - $message = $connection->pushHttp(new Response(200)); + /** @var ResponseInterface $message */ + $message = $connection->pushHttp($this->psrFactory->createResponse(200)); $this->assertEquals(400, $message->getStatusCode()); $this->expectSocketStreamIsConnected(); diff --git a/tests/suites/middleware/DeflateCompressorTest.php b/tests/suites/middleware/DeflateCompressorTest.php index 447a932..98085a2 100644 --- a/tests/suites/middleware/DeflateCompressorTest.php +++ b/tests/suites/middleware/DeflateCompressorTest.php @@ -9,16 +9,17 @@ namespace WebSocket\Test\Middleware; +use Nyholm\Psr7\Factory\Psr17Factory; use PHPUnit\Framework\TestCase; use Phrity\Net\Mock\SocketStream; +use Psr\Http\Message\{ + RequestInterface, + ResponseInterface, +}; use RangeException; use RuntimeException; use Stringable; use WebSocket\Connection; -use WebSocket\Http\{ - Request, - Response, -}; use WebSocket\Middleware\CompressionExtension; use WebSocket\Middleware\CompressionExtension\DeflateCompressor; use WebSocket\Test\MockStreamTrait; @@ -31,10 +32,13 @@ class DeflateCompressorTest extends TestCase { use MockStreamTrait; + private Psr17Factory $psrFactory; + public function setUp(): void { error_reporting(-1); $this->setUpStack(); + $this->psrFactory = new Psr17Factory(); } public function tearDown(): void @@ -73,7 +77,7 @@ function (string $method, array $params): void { ); } ); - $request = new Request('GET', 'ws://test.url'); + $request = $this->psrFactory->createRequest('GET', 'ws://test.url'); $request = $connection->pushHttp($request); $this->assertEquals(['permessage-deflate'], $request->getHeader('Sec-WebSocket-Extensions')); $this->assertNull($connection->getMeta('compressionExtension.compressor')); @@ -174,7 +178,7 @@ public function testServerDefault(): void $request = $connection->pullHttp(); $this->assertEquals(['permessage-deflate'], $request->getHeader('Sec-WebSocket-Extensions')); - $response = new Response(200); + $response = $this->psrFactory->createResponse(200); $this->expectSocketStreamWrite()->addAssert( function (string $method, array $params): void { $this->assertEquals( @@ -265,7 +269,7 @@ function (string $method, array $params): void { ); } ); - $request = new Request('GET', 'ws://test.url'); + $request = $this->psrFactory->createRequest('GET', 'ws://test.url'); $request = $connection->pushHttp($request); $this->assertEquals(['permessage-deflate'], $request->getHeader('Sec-WebSocket-Extensions')); @@ -346,7 +350,7 @@ function (string $method, array $params): void { ); } ); - $request = new Request('GET', 'ws://test.url'); + $request = $this->psrFactory->createRequest('GET', 'ws://test.url'); $request = $connection->pushHttp($request); $this->assertNull($connection->getMeta('compressionExtension.compressor')); $this->assertNull($connection->getMeta('compressionExtension.configuration')); @@ -451,7 +455,7 @@ public function testServerConfiguration(): void $request = $connection->pullHttp(); $this->assertEquals(['permessage-deflate'], $request->getHeader('Sec-WebSocket-Extensions')); - $response = new Response(200); + $response = $this->psrFactory->createResponse(200); $this->expectSocketStreamWrite()->addAssert( function (string $method, array $params): void { $this->assertEquals( @@ -545,7 +549,7 @@ function (string $method, array $params): void { ); } ); - $request = new Request('GET', 'ws://test.url'); + $request = $this->psrFactory->createRequest('GET', 'ws://test.url'); $request = $connection->pushHttp($request); // Receive heders from Server @@ -618,7 +622,7 @@ public function testServerConfigurationByServer(): void }); $request = $connection->pullHttp(); - $response = new Response(200); + $response = $this->psrFactory->createResponse(200); $this->expectSocketStreamWrite()->addAssert( function (string $method, array $params): void { $this->assertEquals( diff --git a/tests/suites/middleware/SubprotocolNegotiationTest.php b/tests/suites/middleware/SubprotocolNegotiationTest.php index 9a2b402..f0b1434 100644 --- a/tests/suites/middleware/SubprotocolNegotiationTest.php +++ b/tests/suites/middleware/SubprotocolNegotiationTest.php @@ -9,13 +9,14 @@ namespace WebSocket\Test\Middleware; +use Nyholm\Psr7\Factory\Psr17Factory; use PHPUnit\Framework\TestCase; use Phrity\Net\Mock\SocketStream; -use Stringable; -use WebSocket\Http\{ - Request, - Response, +use Psr\Http\Message\{ + RequestInterface, + ResponseInterface, }; +use Stringable; use WebSocket\Connection; use WebSocket\Exception\HandshakeException; use WebSocket\Middleware\SubprotocolNegotiation; @@ -28,10 +29,13 @@ class SubprotocolNegotiationTest extends TestCase { use MockStreamTrait; + private Psr17Factory $psrFactory; + public function setUp(): void { error_reporting(-1); $this->setUpStack(); + $this->psrFactory = new Psr17Factory(); } public function tearDown(): void @@ -68,7 +72,7 @@ function (string $method, array $params): void { ); } ); - $request = new Request('GET', 'ws://test.url'); + $request = $this->psrFactory->createRequest('GET', 'ws://test.url'); $request = $connection->pushHttp($request); $this->assertEquals(['sp-1', 'sp-2', 'sp-3'], $request->getHeader('Sec-WebSocket-Protocol')); $this->assertNull($connection->getMeta('subprotocolNegotiation.selected')); @@ -120,7 +124,7 @@ function (string $method, array $params): void { ); } ); - $request = new Request('GET', 'ws://test.url'); + $request = $this->psrFactory->createRequest('GET', 'ws://test.url'); $request = $connection->pushHttp($request); $this->assertEquals(['sp-1', 'sp-2', 'sp-3'], $request->getHeader('Sec-WebSocket-Protocol')); $this->assertNull($connection->getMeta('subprotocolNegotiation.selected')); @@ -169,7 +173,7 @@ function (string $method, array $params): void { ); } ); - $request = new Request('GET', 'ws://test.url'); + $request = $this->psrFactory->createRequest('GET', 'ws://test.url'); $request = $connection->pushHttp($request); $this->assertEquals(['sp-1', 'sp-2', 'sp-3'], $request->getHeader('Sec-WebSocket-Protocol')); $this->assertNull($connection->getMeta('subprotocolNegotiation.selected')); @@ -228,7 +232,7 @@ public function testServerProtocolMatch(): void $this->assertEquals(['sp-11', 'sp-2', 'sp-33'], $request->getHeader('Sec-WebSocket-Protocol')); $this->assertEquals('sp-2', $connection->getMeta('subprotocolNegotiation.selected')); - $response = new Response(200); + $response = $this->psrFactory->createResponse(200); $this->expectSocketStreamWrite()->addAssert( function (string $method, array $params): void { $this->assertEquals( @@ -287,7 +291,7 @@ public function testServerProtocolNoMatch(): void $this->assertEquals(['sp-11', 'sp-22', 'sp-33'], $request->getHeader('Sec-WebSocket-Protocol')); $this->assertNull($connection->getMeta('subprotocolNegotiation.selected')); - $response = new Response(200); + $response = $this->psrFactory->createResponse(200); $this->expectSocketStreamWrite()->addAssert( function (string $method, array $params): void { $this->assertEquals( @@ -346,7 +350,7 @@ public function testServerProtocolRequire(): void $this->assertEquals(['sp-11', 'sp-22', 'sp-33'], $request->getHeader('Sec-WebSocket-Protocol')); $this->assertNull($connection->getMeta('subprotocolNegotiation.selected')); - $response = new Response(200); + $response = $this->psrFactory->createResponse(200); $this->expectSocketStreamWrite()->addAssert( function (string $method, array $params): void { $this->assertEquals( diff --git a/tests/suites/server/ConfigTest.php b/tests/suites/server/ConfigTest.php index 6c32b48..b151add 100644 --- a/tests/suites/server/ConfigTest.php +++ b/tests/suites/server/ConfigTest.php @@ -9,7 +9,7 @@ namespace WebSocket\Test\Server; -use GuzzleHttp\Psr7\HttpFactory as GuzzleFactory; +use Nyholm\Psr7\Factory\Psr17Factory; use PHPUnit\Framework\TestCase; use Phrity\Http\HttpFactory; use Phrity\Net\Mock\{ @@ -226,7 +226,7 @@ public function testContextClass(): void public function testHttpFactories(): void { - $httpFactory = new GuzzleFactory(); + $httpFactory = new Psr17Factory(); $this->expectContext(); $context = new Context(); $this->expectContextSetOptions(); diff --git a/tests/suites/server/HandshakeTest.php b/tests/suites/server/HandshakeTest.php index 06fc6dc..397f38f 100644 --- a/tests/suites/server/HandshakeTest.php +++ b/tests/suites/server/HandshakeTest.php @@ -11,9 +11,11 @@ use PHPUnit\Framework\TestCase; use Phrity\Net\StreamException; -use Phrity\Net\Mock\SocketStream; -use Phrity\Net\Mock\StreamCollection; -use Phrity\Net\Mock\StreamFactory; +use Phrity\Net\Mock\{ + SocketStream, + StreamCollection, + StreamFactory, +}; use Phrity\Net\Mock\Stack\{ ExpectContextTrait, ExpectSocketServerTrait, @@ -25,7 +27,6 @@ ConnectionException, Server }; -use WebSocket\Http\ServerRequest; use WebSocket\Test\{ MockStreamTrait, MockUri diff --git a/tests/suites/server/ServerTest.php b/tests/suites/server/ServerTest.php index 752153e..ed9aaae 100644 --- a/tests/suites/server/ServerTest.php +++ b/tests/suites/server/ServerTest.php @@ -22,6 +22,10 @@ }; use Phrity\Net\StreamException; use Phrity\Util\ErrorHandler; +use Psr\Http\Message\{ + ResponseInterface, + ServerRequestInterface, +}; use Psr\Log\NullLogger; use Stringable; use WebSocket\{ @@ -34,10 +38,6 @@ ConnectionClosedException, ServerException }; -use WebSocket\Http\{ - Response, - ServerRequest -}; use WebSocket\Message\{ Binary, Close, @@ -89,14 +89,14 @@ public function testListeners(): void $server->onHandshake(function ($server, $connection, $request, $response) { $this->assertInstanceOf(Server::class, $server); $this->assertInstanceOf(Connection::class, $connection); - $this->assertInstanceOf(ServerRequest::class, $request); - $this->assertInstanceOf(Response::class, $response); + $this->assertInstanceOf(ServerRequestInterface::class, $request); + $this->assertInstanceOf(ResponseInterface::class, $response); }); $handler->withAll(function () use ($server) { $server->onConnect(function ($server, $connection, $request) { $this->assertInstanceOf(Server::class, $server); $this->assertInstanceOf(Connection::class, $connection); - $this->assertInstanceOf(ServerRequest::class, $request); + $this->assertInstanceOf(ServerRequestInterface::class, $request); $server->stop(); }); }, function (array $errors) { From bf3012e6fe2a02912b7951c117963ea31c577e86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren=20Jensen?= Date: Tue, 23 Dec 2025 11:05:55 +0100 Subject: [PATCH 2/2] fixup, docs --- docs/Class_Synopsis.md | 760 ++++++++++++++++++++------------------- src/Http/HttpHandler.php | 1 - 2 files changed, 392 insertions(+), 369 deletions(-) diff --git a/docs/Class_Synopsis.md b/docs/Class_Synopsis.md index 298974c..c72954f 100644 --- a/docs/Class_Synopsis.md +++ b/docs/Class_Synopsis.md @@ -2,490 +2,514 @@ # Class Synopsis -Incomplete list of public API of core classes. - -## WebSocket - -### Client +Public API of included classes. ```php -class WebSocket\Client implements Psr\Log\LoggerAwareInterface, Stringable -{ - // Magic methods - public function __construct(Psr\Http\Message\UriInterface|string $uri); - public function __toString(): string; - - // Configuration - public function setStreamFactory(Phrity\Net\StreamFactory $streamFactory): self; - public function setLogger(Psr\Log\LoggerInterface $logger): void; - public function setTimeout(int|float $timeout): self; - public function getTimeout(): int|float; - public function setFrameSize(int $frameSize): self; - public function getFrameSize(): int; - public function setPersistent(bool $persistent): self; - public function getContext(): Phrity\Net\Context; - public function setContext(Phrity\Net\Context|array $context): self; - public function addHeader(string $name, string $content): self; - public function addMiddleware(WebSocket\Middleware\MiddlewareInterface $middleware): self; - - // Messaging operations - public function send(WebSocket\Message\Message $message): WebSocket\Message\Message; - public function receive(): WebSocket\Message\Message; - public function text(string $message): WebSocket\Message\Text; - public function binary(string $message): WebSocket\Message\Binary; - public function ping(string $message = ''): WebSocket\Message\Ping; - public function pong(string $message = ''): WebSocket\Message\Pong; - public function close(int $status = 1000, string $message = 'ttfn'): WebSocket\Message\Close; - - // Listener operations - public function start(int|float|null $timeout = null): void; - public function stop(): void; - public function isRunning(): bool; - - // Connection management - public function isConnected(): bool; - public function isReadable(): bool; - public function isWritable(): bool; - public function connect(): void; - public function disconnect(): void; - - // Connection wrapper methods - public function getName(): string|null; - public function getRemoteName(): string|null; - public function getMeta(string $key): mixed; // @deprecated - public function getHandshakeResponse(): WebSocket\Http\Response|null; - - // Listener methods - public function onConnect(Closure $closure): self; // @deprecated - public function onDisconnect(Closure $closure): self; - public function onHandshake(Closure $closure): self; - public function onText(Closure $closure): self; - public function onBinary(Closure $closure): self; - public function onPing(Closure $closure): self; - public function onPong(Closure $closure): self; - public function onClose(Closure $closure): self; - public function onError(Closure $closure): self; - public function onTick(Closure $closure): self; +abstract class WebSocket\Exception\Exception extends RuntimeException imlements WebSocket\Exception\ExceptionInterface +{ } -``` - -### Server -```php -class WebSocket\Server implements Psr\Log\LoggerAwareInterface, Stringable -{ - // Magic methods - public function __construct(int $port = 80, bool $ssl = false); - public function __toString(): string; - - // Configuration - public function setStreamFactory(Phrity\Net\StreamFactory $streamFactory): self; - public function setLogger(Psr\Log\LoggerInterface $logger): void; - public function setTimeout(int|float $timeout): self; - public function getTimeout(): int|float; - public function setFrameSize(int $frameSize): self; - public function getFrameSize(): int; - public function getPort(): int; - public function getScheme(): string; - public function isSsl(): bool; - public function getContext(): Phrity\Net\Context; - public function setContext(Phrity\Net\Context|array $context): self; - public function getConnectionCount(): int; - public function getConnections(): array; - public function getReadableConnections(): array; - public function getWritableConnections(): array; - public function addMiddleware(WebSocket\Middleware\MiddlewareInterface $middleware): self; - public function setMaxConnections(int|null $maxConnections): self; - - // Messaging operations - public function send(WebSocket\Message\Message $message): WebSocket\Message\Message; - public function text(string $message): WebSocket\Message\Text; - public function binary(string $message): WebSocket\Message\Binary; - public function ping(string $message = ''): WebSocket\Message\Ping; - public function pong(string $message = ''): WebSocket\Message\Pong; - public function close(int $status = 1000, string $message = 'ttfn'): WebSocket\Message\Close; - - // Listener operations - public function start(int|float|null $timeout = null): void; - public function stop(): void; - public function isRunning(): bool; - - // Connection management - public function shutdown(int $closeStatus = 1001): void; - public function disconnect(): void; - - // Listener methods - public function onConnect(Closure $closure): self; // @deprecated - public function onDisconnect(Closure $closure): self; - public function onHandshake(Closure $closure): self; - public function onText(Closure $closure): self; - public function onBinary(Closure $closure): self; - public function onPing(Closure $closure): self; - public function onPong(Closure $closure): self; - public function onClose(Closure $closure): self; - public function onError(Closure $closure): self; - public function onTick(Closure $closure): self; +abstract class WebSocket\Message\Message imlements Stringable +{ + use WebSocket\Trait\StringableTrait; + + public method __construct(string $content = ""); + public method getContent(): string; + public method getFrames(int $frameSize = 4096): array; + public method getLength(): int; + public method getOpcode(): string; + public method getPayload(): string; + public method getTimestamp(): DateTimeInterface; + public method hasContent(): bool; + public method isCompressed(): bool; + public method setCompress(bool $compress): void; + public method setContent(string $content = ""): void; + public method setPayload(string $payload = ""): void; } -``` -### Connection - -```php -class WebSocket\Connection implements Psr\Log\LoggerAwareInterface, Stringable -{ - // Magic methods - public function __construct(Phrity\Net\SocketStream $stream, bool $pushMasked, bool $pullMaskedRequired, bool $ssl = false); - public function __destruct(); - public function __toString(): string; - - // Configuration - public function setLogger(Psr\Log\LoggerInterface $logger): void; - public function setTimeout(int|float $timeout): self; - public function getTimeout(): int|float; - public function setFrameSize(int $frameSize): self; - public function getFrameSize(): int; - public function getContext(): Phrity\Net\Context; - public function addMiddleware(WebSocket\Middleware\MiddlewareInterface $middleware): self; - - // Connection management - public function isConnected(): bool; - public function isReadable(): bool; - public function isWritable(): bool; - public function disconnect(): self; - public function closeRead(): self; - public function closeWrite(): self; - - // Connection state - public function getName(): string|null; - public function getRemoteName(): string|null; - public function setMeta(string $key, mixed $value): void; - public function getMeta(string $key): mixed; - public function tick(): void; - - // WebSocket Message methods - public function send(WebSocket\Message\Message $message): WebSocket\Message\Message; - public function pushMessage(WebSocket\Message\Message $message): WebSocket\Message\Message; - public function pullMessage(): WebSocket\Message\Message; - public function text(string $message): WebSocket\Message\Text; - public function binary(string $message): WebSocket\Message\Binary; - public function ping(string $message = ''): WebSocket\Message\Ping; - public function pong(string $message = ''): WebSocket\Message\Pong; - public function close(int $status = 1000, string $message = 'ttfn'): WebSocket\Message\Close; - - // HTTP Message methods - public function pushHttp(WebSocket\Http\Message $message): WebSocket\Http\Message; - public function pullHttp(): WebSocket\Http\Message; - public function setHandshakeRequest(WebSocket\Http\Request $request): self; - public function getHandshakeRequest(): WebSocket\Http\Request|null; - public function setHandshakeResponse(WebSocket\Http\Response $response): self; - public function getHandshakeResponse(): WebSocket\Http\Response|null; +class WebSocket\Client imlements Psr\Log\LoggerAwareInterface, Stringable +{ + use WebSocket\Trait\ListenerTrait; + use WebSocket\Trait\LoggerAwareTrait; + use WebSocket\Trait\SendMethodsTrait; + use WebSocket\Trait\StringableTrait; + + public method __construct(Psr\Http\Message\UriInterface|string $uri); + public method __toString(): string; + public method addHeader(string $name, string $content): self; + public method addMiddleware(WebSocket\Middleware\MiddlewareInterface $middleware): self; + public method connect(): void; + public method disconnect(): void; + public method getContext(): Phrity\Net\Context; + public method getFrameSize(): int; + public method getHandshakeResponse(): Psr\Http\Message\ResponseInterface|null; + public method getMeta(string $key): mixed; + public method getName(): string|null; + public method getRemoteName(): string|null; + public method getTimeout(): int|float; + public method isConnected(): bool; + public method isReadable(): bool; + public method isRunning(): bool; + public method isWritable(): bool; + public method receive(): WebSocket\Message\Message; + public method send(WebSocket\Message\Message $message): WebSocket\Message\Message; + public method setContext(Phrity\Net\Context|array $context): self; + public method setFrameSize(int $frameSize): self; + public method setHttpFactory(Phrity\Http\HttpFactory $httpFactory): self; + public method setLogger(Psr\Log\LoggerInterface $logger): void; + public method setPersistent(bool $persistent): self; + public method setStreamFactory(Phrity\Net\StreamFactory $streamFactory): self; + public method setTimeout(int|float $timeout): self; + public method start(int|float|null $timeout = null): void; + public method stop(): void; } -``` - -## WebSocket / Exception -### BadOpcodeException - -```php -class WebSocket\Exception\BadOpcodeException extends WebSocket\Exception\Exception implements WebSocket\Exception\MessageLevelInterface +class WebSocket\Connection imlements Psr\Log\LoggerAwareInterface, Stringable { - public function __construct(string $message = 'Bad Opcode'); + use WebSocket\Trait\LoggerAwareTrait; + use WebSocket\Trait\SendMethodsTrait; + use WebSocket\Trait\StringableTrait; + + public method __construct(Phrity\Net\SocketStream $stream, bool $pushMasked, bool $pullMaskedRequired, bool $ssl = false, Phrity\Http\HttpFactory|null $httpFactory = null); + public method __destruct(); + public method __toString(): string; + public method addMiddleware(WebSocket\Middleware\MiddlewareInterface $middleware): self; + public method closeRead(): self; + public method closeWrite(): self; + public method disconnect(): self; + public method getContext(): Phrity\Net\Context; + public method getFrameSize(): int; + public method getHandshakeRequest(): Psr\Http\Message\RequestInterface|null; + public method getHandshakeResponse(): Psr\Http\Message\ResponseInterface|null; + public method getMeta(string $key): mixed; + public method getName(): string|null; + public method getRemoteName(): string|null; + public method getTimeout(): int|float; + public method isConnected(): bool; + public method isReadable(): bool; + public method isWritable(): bool; + public method pullHttp(): Psr\Http\Message\MessageInterface; + public method pullMessage(): WebSocket\Message\Message; + public method pushHttp(Psr\Http\Message\MessageInterface $message): Psr\Http\Message\MessageInterface; + public method pushMessage(WebSocket\Message\Message $message): WebSocket\Message\Message; + public method send(WebSocket\Message\Message $message): WebSocket\Message\Message; + public method setFrameSize(int $frameSize): self; + public method setHandshakeRequest(Psr\Http\Message\RequestInterface $request): self; + public method setHandshakeResponse(Psr\Http\Message\ResponseInterface $response): self; + public method setLogger(Psr\Log\LoggerInterface $logger): void; + public method setMeta(string $key, mixed $value): void; + public method setTimeout(int|float $timeout): self; + public method tick(): void; } -``` -### BadUriException +class WebSocket\Exception\BadOpcodeException extends WebSocket\Exception\Exception imlements WebSocket\Exception\MessageLevelInterface +{ + public method __construct(string $message = "Bad Opcode"); +} -```php class WebSocket\Exception\BadUriException extends WebSocket\Exception\Exception { - public function __construct(string $message = 'Bad URI'); + public method __construct(string $message = "Bad URI"); } -``` - -### ClientException -```php class WebSocket\Exception\ClientException extends WebSocket\Exception\Exception { } -``` - -### CloseException -```php class WebSocket\Exception\CloseException extends WebSocket\Exception\Exception { - public function __construct(int|null $status = null, string $content = ''); - public function getCloseStatus(): int|null; + public method __construct(int|null $status = null, string $content = ""); + public method getCloseStatus(): int; } -``` -### ConnectionClosedException +class WebSocket\Exception\ConnectionClosedException extends WebSocket\Exception\Exception imlements WebSocket\Exception\ConnectionLevelInterface +{ + public method __construct(); +} -```php -class WebSocket\Exception\ConnectionClosedException extends WebSocket\Exception\Exception implements WebSocket\Exception\ConnectionLevelInterface +class WebSocket\Exception\ConnectionFailureException extends WebSocket\Exception\Exception imlements WebSocket\Exception\ConnectionLevelInterface { - public function __construct(); + public method __construct(string|null $message = null); } -``` -### ConnectionFailureException +class WebSocket\Exception\ConnectionTimeoutException extends WebSocket\Exception\Exception imlements WebSocket\Exception\MessageLevelInterface +{ + public method __construct(); +} -```php -class WebSocket\Exception\ConnectionFailureException extends WebSocket\Exception\Exception implements WebSocket\Exception\ConnectionLevelInterface +class WebSocket\Exception\HandshakeException extends WebSocket\Exception\Exception imlements WebSocket\Exception\ConnectionLevelInterface { - public function __construct(); + public method __construct(string $message, Psr\Http\Message\ResponseInterface $response); + public method getResponse(): Psr\Http\Message\ResponseInterface; } -``` -### ConnectionLevelInterface +class WebSocket\Exception\ReconnectException extends WebSocket\Exception\Exception +{ + public method __construct(Phrity\Net\Uri|null $uri = null); + public method getUri(): Phrity\Net\Uri|null; +} -```php -interface WebSocket\Exception\ConnectionLevelInterface extends WebSocket\Exception\ExceptionInterface +class WebSocket\Exception\ServerException extends WebSocket\Exception\Exception { } -``` -### ConnectionTimeoutException +class WebSocket\Frame\Frame imlements Stringable +{ + use WebSocket\Trait\StringableTrait; + + public method __construct(string $opcode, string $payload, bool $final, bool $rsv1 = false, bool $rsv2 = false, bool $rsv3 = false); + public method __toString(): string; + public method getOpcode(): string; + public method getPayload(): string; + public method getPayloadLength(): int; + public method getRsv1(): bool; + public method getRsv2(): bool; + public method getRsv3(): bool; + public method isContinuation(): bool; + public method isFinal(): bool; + public method setRsv1(bool $rsv1): void; +} -```php -class WebSocket\Exception\ConnectionTimeoutException extends WebSocket\Exception\Exception implements WebSocket\Exception\MessageLevelInterface +class WebSocket\Frame\FrameHandler imlements Psr\Log\LoggerAwareInterface, Stringable { - public function __construct(); + use WebSocket\Trait\LoggerAwareTrait; + use WebSocket\Trait\OpcodeTrait; + use WebSocket\Trait\StringableTrait; + + public method __construct(Phrity\Net\SocketStream $stream, bool $pushMasked, bool $pullMaskedRequired); + public method pull(): WebSocket\Frame\Frame; + public method push(WebSocket\Frame\Frame $frame): int; } -``` -### Exception +class WebSocket\Http\DefaultHttpFactory extends Phrity\Http\HttpFactory +{ + public method __construct(); + public method createRequest(string $method, mixed $uri): Psr\Http\Message\RequestInterface; + public method createResponse(int $code = 200, string $reasonPhrase = ""): Psr\Http\Message\ResponseInterface; + public method createServerRequest(string $method, mixed $uri, array $serverParams = []): Psr\Http\Message\ServerRequestInterface; + public method createUri(string $uri = ""): Psr\Http\Message\UriInterface; +} -```php -abstract class WebSocket\Exception\Exception extends RuntimeException implements WebSocket\Exception\ExceptionInterface +class WebSocket\Http\HttpHandler imlements Psr\Log\LoggerAwareInterface, Stringable { + use WebSocket\Trait\StringableTrait; + + public method __construct(Phrity\Net\SocketStream $stream, bool $ssl = false, Phrity\Http\HttpFactory|null $httpFactory = null); + public method pull(): Psr\Http\Message\MessageInterface; + public method push(Psr\Http\Message\MessageInterface $message): Psr\Http\Message\MessageInterface; + public method setLogger(Psr\Log\LoggerInterface $logger): void; } -``` -### ExceptionInterface +class WebSocket\Http\Request extends Nyholm\Psr7\Request imlements Psr\Http\Message\RequestInterface +{ + public method __construct(string $method = "GET", Psr\Http\Message\UriInterface|string $uri = ""); +} -```php -interface WebSocket\Exception\ExceptionInterface +class WebSocket\Http\Response extends Nyholm\Psr7\Response imlements Psr\Http\Message\ResponseInterface { + public method __construct(int $code = 200, string $reasonPhrase = ""); } -``` -### HandshakeException +class WebSocket\Http\ServerRequest extends Nyholm\Psr7\ServerRequest imlements Psr\Http\Message\ServerRequestInterface +{ + public method __construct(string $method = "GET", Psr\Http\Message\UriInterface|string $uri = ""); +} -```php -class WebSocket\Exception\HandshakeException extends WebSocket\Exception\Exception implements WebSocket\Exception\ConnectionLevelInterface +class WebSocket\Message\Binary extends WebSocket\Message\Message { - public function __construct(string $message, WebSocket\Http\Response $response); - public function getResponse(): WebSocket\Http\Response; + public method isCompressed(): bool; + public method setCompress(bool $compress): void; } -``` -### MessageLevelInterface +class WebSocket\Message\Close extends WebSocket\Message\Message +{ + public method __construct(int|null $status = null, string $content = ""); + public method getCloseStatus(): int|null; + public method getPayload(): string; + public method setCloseStatus(int|null $status): void; + public method setPayload(string $payload = ""): void; +} -```php -interface WebSocket\Exception\MessageLevelInterface extends WebSocket\Exception\ExceptionInterface +class WebSocket\Message\MessageHandler imlements Psr\Log\LoggerAwareInterface, Stringable { + use WebSocket\Trait\LoggerAwareTrait; + use WebSocket\Trait\StringableTrait; + + public method __construct(WebSocket\Frame\FrameHandler $frameHandler); + public method pull(): WebSocket\Message\Message; + public method push(WebSocket\Message\Message $message, int $size = WebSocket\Message\MessageHandler::DEFAULT_SIZE): WebSocket\Message\Message; + public method setLogger(Psr\Log\LoggerInterface $logger): void; } -``` -### ReconnectException +class WebSocket\Message\Ping extends WebSocket\Message\Message +{ +} -```php -class WebSocket\Exception\ReconnectException extends WebSocket\Exception\Exception +class WebSocket\Message\Pong extends WebSocket\Message\Message { - public function __construct(Uri|null $uri = null); } -``` -### ServerException +class WebSocket\Message\Text extends WebSocket\Message\Message +{ + public method isCompressed(): bool; + public method setCompress(bool $compress): void; +} -```php -class WebSocket\Exception\ServerException extends WebSocket\Exception\Exception +class WebSocket\Middleware\Callback imlements Psr\Log\LoggerAwareInterface, WebSocket\Middleware\ProcessHttpIncomingInterface, WebSocket\Middleware\ProcessHttpOutgoingInterface, WebSocket\Middleware\ProcessIncomingInterface, WebSocket\Middleware\ProcessOutgoingInterface, WebSocket\Middleware\ProcessTickInterface, Stringable { + use WebSocket\Trait\LoggerAwareTrait; + use WebSocket\Trait\StringableTrait; + + public method __construct(Closure|null $incoming = null, Closure|null $outgoing = null, Closure|null $httpIncoming = null, Closure|null $httpOutgoing = null, Closure|null $tick = null); + public method processHttpIncoming(WebSocket\Middleware\ProcessHttpStack $stack, WebSocket\Connection $connection): Psr\Http\Message\MessageInterface; + public method processHttpOutgoing(WebSocket\Middleware\ProcessHttpStack $stack, WebSocket\Connection $connection, Psr\Http\Message\MessageInterface $message): Psr\Http\Message\MessageInterface; + public method processIncoming(WebSocket\Middleware\ProcessStack $stack, WebSocket\Connection $connection): WebSocket\Message\Message; + public method processOutgoing(WebSocket\Middleware\ProcessStack $stack, WebSocket\Connection $connection, WebSocket\Message\Message $message): WebSocket\Message\Message; + public method processTick(WebSocket\Middleware\ProcessTickStack $stack, WebSocket\Connection $connection): void; } -``` -## WebSocket / Frame +class WebSocket\Middleware\CloseHandler imlements Psr\Log\LoggerAwareInterface, WebSocket\Middleware\ProcessIncomingInterface, WebSocket\Middleware\ProcessOutgoingInterface, Stringable +{ + use WebSocket\Trait\LoggerAwareTrait; + use WebSocket\Trait\StringableTrait; -### FrameHandler + public method __construct(); + public method processIncoming(WebSocket\Middleware\ProcessStack $stack, WebSocket\Connection $connection): WebSocket\Message\Message; + public method processOutgoing(WebSocket\Middleware\ProcessStack $stack, WebSocket\Connection $connection, WebSocket\Message\Message $message): WebSocket\Message\Message; +} -```php -class WebSocket\Frame\FrameHandler implements LoggerAwareInterface, Stringable +class WebSocket\Middleware\CompressionExtension imlements Psr\Log\LoggerAwareInterface, WebSocket\Middleware\ProcessHttpOutgoingInterface, WebSocket\Middleware\ProcessHttpIncomingInterface, WebSocket\Middleware\ProcessIncomingInterface, WebSocket\Middleware\ProcessOutgoingInterface, Stringable { - public function __construct(Phrity\Net\SocketStream $stream, bool $pushMasked, bool $pullMaskedRequired); - public function __toString(): string; - public function setLogger(Psr\Log\LoggerInterface $logger): void; // @deprecated - public function pull(): WebSocket\Frame\Frame; - public function push(WebSocket\Frame\Frame $frame): int; + use WebSocket\Trait\LoggerAwareTrait; + use WebSocket\Trait\StringableTrait; + + public method __construct(WebSocket\Middleware\CompressionExtension\CompressorInterface $compressors); + public method processHttpIncoming(WebSocket\Middleware\ProcessHttpStack $stack, WebSocket\Connection $connection): Psr\Http\Message\MessageInterface; + public method processHttpOutgoing(WebSocket\Middleware\ProcessHttpStack $stack, WebSocket\Connection $connection, Psr\Http\Message\MessageInterface $message): Psr\Http\Message\MessageInterface; + public method processIncoming(WebSocket\Middleware\ProcessStack $stack, WebSocket\Connection $connection): WebSocket\Message\Message; + public method processOutgoing(WebSocket\Middleware\ProcessStack $stack, WebSocket\Connection $connection, WebSocket\Message\Message $message): WebSocket\Message\Message; } -``` -### Frame +class WebSocket\Middleware\CompressionExtension\DeflateCompressor imlements WebSocket\Middleware\CompressionExtension\CompressorInterface, Stringable +{ + use WebSocket\Trait\StringableTrait; + + public method __construct(bool $serverNoContextTakeover = false, bool $clientNoContextTakeover = false, int $serverMaxWindowBits = WebSocket\Middleware\CompressionExtension\DeflateCompressor::MAX_WINDOW_SIZE, int $clientMaxWindowBits = WebSocket\Middleware\CompressionExtension\DeflateCompressor::MAX_WINDOW_SIZE, string $extension = "zlib"); + public method compress(WebSocket\Message\Binary|WebSocket\Message\Text $message, object $configuration): WebSocket\Message\Binary|WebSocket\Message\Text; + public method decompress(WebSocket\Message\Binary|WebSocket\Message\Text $message, object $configuration): WebSocket\Message\Binary|WebSocket\Message\Text; + public method getConfiguration(string $element, bool $isServer): object; + public method getRequestHeaderValue(): string; + public method getResponseHeaderValue(object $configuration): string; + public method isEligable(object $configuration): bool; +} -```php -class WebSocket\Frame\Frame implements Stringable -{ - public function __construct(string $opcode, string $payload, bool $final, bool $rsv1 = false, bool $rsv2 = false, bool $rsv3 = false); - public function __toString(): string; - public function isFinal(): bool; - public function getRsv1(): bool; - public function getRsv2(): bool; - public function getRsv3(): bool; - public function isContinuation(): bool; - public function getOpcode(): string; - public function getPayload(): string; - public function getPayloadLength(): int; +class WebSocket\Middleware\FollowRedirect imlements Psr\Log\LoggerAwareInterface, WebSocket\Middleware\ProcessHttpIncomingInterface, Stringable +{ + use WebSocket\Trait\LoggerAwareTrait; + use WebSocket\Trait\StringableTrait; + + public method __construct(int $limit = 10); + public method processHttpIncoming(WebSocket\Middleware\ProcessHttpStack $stack, WebSocket\Connection $connection): Psr\Http\Message\MessageInterface; } -``` -## WebSocket / Http +class WebSocket\Middleware\MiddlewareHandler imlements Psr\Log\LoggerAwareInterface, Stringable +{ + use WebSocket\Trait\LoggerAwareTrait; + use WebSocket\Trait\StringableTrait; + + public method __construct(WebSocket\Message\MessageHandler $messageHandler, WebSocket\Http\HttpHandler $httpHandler); + public method add(WebSocket\Middleware\MiddlewareInterface $middleware): self; + public method processHttpIncoming(WebSocket\Connection $connection): Psr\Http\Message\MessageInterface; + public method processHttpOutgoing(WebSocket\Connection $connection, Psr\Http\Message\MessageInterface $message): Psr\Http\Message\MessageInterface; + public method processIncoming(WebSocket\Connection $connection): WebSocket\Message\Message; + public method processOutgoing(WebSocket\Connection $connection, WebSocket\Message\Message $message): WebSocket\Message\Message; + public method processTick(WebSocket\Connection $connection): void; + public method setLogger(Psr\Log\LoggerInterface $logger): void; +} -### HttpHandler +class WebSocket\Middleware\PingInterval imlements Psr\Log\LoggerAwareInterface, WebSocket\Middleware\ProcessOutgoingInterface, WebSocket\Middleware\ProcessTickInterface, Stringable +{ + use WebSocket\Trait\LoggerAwareTrait; + use WebSocket\Trait\StringableTrait; -```php -class WebSocket\Http\HttpHandler implements LoggerAwareInterface, Stringable + public method __construct(int|float|null $interval = null); + public method processOutgoing(WebSocket\Middleware\ProcessStack $stack, WebSocket\Connection $connection, WebSocket\Message\Message $message): WebSocket\Message\Message; + public method processTick(WebSocket\Middleware\ProcessTickStack $stack, WebSocket\Connection $connection): void; +} + +class WebSocket\Middleware\PingResponder imlements Psr\Log\LoggerAwareInterface, WebSocket\Middleware\ProcessIncomingInterface, Stringable { - public function __construct(Phrity\Net\SocketStream $stream, bool $ssl = false); - public function __toString(): string; - public function pull(): Psr\Http\Message\MessageInterface; - public function push(Psr\Http\Message\MessageInterface $message): Psr\Http\Message\MessageInterface; + use WebSocket\Trait\LoggerAwareTrait; + use WebSocket\Trait\StringableTrait; + + public method __construct(); + public method processIncoming(WebSocket\Middleware\ProcessStack $stack, WebSocket\Connection $connection): WebSocket\Message\Message; } -``` -### Message +class WebSocket\Middleware\ProcessHttpStack imlements Stringable +{ + use WebSocket\Trait\StringableTrait; -```php -abstract class WebSocket\Http\Message implements Psr\Http\Message\MessageInterface, Stringable -{ - public function __toString(): string; - public function getProtocolVersion(): string; - public function withProtocolVersion(string $version): self; - public function getHeaders(): array; - public function hasHeader(string $name): bool; - public function getHeader(string $name): array; - public function getHeaderLine(string $name): string; - public function withHeader(string $name, mixed $value): self; - public function withAddedHeader(string $name, mixed $value): self; - public function withoutHeader(string $name): self; - public function getAsArray(): array; + public method __construct(WebSocket\Connection $connection, WebSocket\Http\HttpHandler $httpHandler, array $processors); + public method handleHttpIncoming(): Psr\Http\Message\MessageInterface; + public method handleHttpOutgoing(Psr\Http\Message\MessageInterface $message): Psr\Http\Message\MessageInterface; } -``` -### Request +class WebSocket\Middleware\ProcessStack imlements Stringable +{ + use WebSocket\Trait\StringableTrait; -```php -class WebSocket\Http\Request extends WebSocket\Http\Message implements Psr\Http\Message\RequestInterface -{ - public function __construct(string $method = 'GET', Psr\Http\Message\UriInterface|string|null $uri = null); - public function __toString(): string; - public function getRequestTarget(): string; - public function withRequestTarget(mixed $requestTarget): self; - public function getMethod(): string; - public function withMethod(string $method): self; - public function getUri(): Psr\Http\Message\UriInterface; - public function withUri(Psr\Http\Message\UriInterface $uri, bool $preserveHost = false): self; - public function getAsArray(): array; + public method __construct(WebSocket\Connection $connection, WebSocket\Message\MessageHandler $messageHandler, array $processors); + public method handleIncoming(): WebSocket\Message\Message; + public method handleOutgoing(WebSocket\Message\Message $message): WebSocket\Message\Message; } -``` -### Response +class WebSocket\Middleware\ProcessTickStack imlements Stringable +{ + use WebSocket\Trait\StringableTrait; -```php -class WebSocket\Http\Response extends WebSocket\Http\Message implements Psr\Http\Message\ResponseInterface + public method __construct(WebSocket\Connection $connection, array $processors); + public method handleTick(): void; +} + +class WebSocket\Middleware\SubprotocolNegotiation imlements Psr\Log\LoggerAwareInterface, WebSocket\Middleware\ProcessHttpOutgoingInterface, WebSocket\Middleware\ProcessHttpIncomingInterface, Stringable { - public function __construct(int $code = 200, string $reasonPhrase = ''); - public function __toString(): string; - public function getStatusCode(): int; - public function withStatus(int $code, string $reasonPhrase = ''): self; - public function getReasonPhrase(): string; - public function getAsArray(): array; + use WebSocket\Trait\LoggerAwareTrait; + use WebSocket\Trait\StringableTrait; + + public method __construct(array $subprotocols, bool $require = false); + public method processHttpIncoming(WebSocket\Middleware\ProcessHttpStack $stack, WebSocket\Connection $connection): Psr\Http\Message\MessageInterface; + public method processHttpOutgoing(WebSocket\Middleware\ProcessHttpStack $stack, WebSocket\Connection $connection, Psr\Http\Message\MessageInterface $message): Psr\Http\Message\MessageInterface; } -``` -### ServerRequest +class WebSocket\Server imlements Psr\Log\LoggerAwareInterface, Stringable +{ + use WebSocket\Trait\ListenerTrait; + use WebSocket\Trait\LoggerAwareTrait; + use WebSocket\Trait\SendMethodsTrait; + use WebSocket\Trait\StringableTrait; + + public method __construct(int $port = 80, bool $ssl = false); + public method __toString(): string; + public method addMiddleware(WebSocket\Middleware\MiddlewareInterface $middleware): self; + public method disconnect(): void; + public method getConnectionCount(): int; + public method getConnections(): array; + public method getContext(): Phrity\Net\Context; + public method getFrameSize(): int; + public method getPort(): int; + public method getReadableConnections(): array; + public method getScheme(): string; + public method getTimeout(): int|float; + public method getWritableConnections(): array; + public method isRunning(): bool; + public method isSsl(): bool; + public method send(WebSocket\Message\Message $message): WebSocket\Message\Message; + public method setContext(Phrity\Net\Context|array $context): self; + public method setFrameSize(int $frameSize): self; + public method setHttpFactory(Phrity\Http\HttpFactory $httpFactory): self; + public method setLogger(Psr\Log\LoggerInterface $logger): void; + public method setMaxConnections(int|null $maxConnections): self; + public method setStreamFactory(Phrity\Net\StreamFactory $streamFactory): self; + public method setTimeout(int|float $timeout): self; + public method shutdown(int $closeStatus = 1001): void; + public method start(int|float|null $timeout = null): void; + public method stop(): void; +} -```php -class WebSocket\Http\ServerRequest extends WebSocket\Http\Request implements Psr\Http\Message\ServerRequestInterface +inteface WebSocket\Constant { - public function __toString(): string; + public const GUID; } -``` -## WebSocket / Message +inteface WebSocket\Exception\ConnectionLevelInterface imlements WebSocket\Exception\ExceptionInterface +{ +} -### Binary +inteface WebSocket\Exception\ExceptionInterface imlements Throwable +{ + public method getMessage(): string; +} -```php -class WebSocket\Message\Binary extends WebSocket\Message\Message +inteface WebSocket\Exception\MessageLevelInterface imlements WebSocket\Exception\ExceptionInterface { } -``` -### Close +inteface WebSocket\Middleware\CompressionExtension\CompressorInterface imlements Stringable +{ + public method compress(WebSocket\Message\Binary|WebSocket\Message\Text $message, object $configuration): WebSocket\Message\Binary|WebSocket\Message\Text; + public method decompress(WebSocket\Message\Binary|WebSocket\Message\Text $message, object $configuration): WebSocket\Message\Binary|WebSocket\Message\Text; + public method getConfiguration(string $element, bool $isServer): object; + public method getRequestHeaderValue(): string; + public method getResponseHeaderValue(object $configuration): string; + public method isEligable(object $configuration): bool; +} -```php -class WebSocket\Message\Close extends WebSocket\Message\Message +inteface WebSocket\Middleware\MiddlewareInterface imlements Stringable { - public function __construct(int|null $status = null, string $content = ''); - public function getCloseStatus(): int|null; - public function setCloseStatus(int|null $status): void; } -``` -### Message +inteface WebSocket\Middleware\ProcessHttpIncomingInterface imlements WebSocket\Middleware\MiddlewareInterface +{ + public method processHttpIncoming(WebSocket\Middleware\ProcessHttpStack $stack, WebSocket\Connection $connection): Psr\Http\Message\MessageInterface; +} -```php -class WebSocket\Message\Message implements Stringable -{ - public function __construct(string $content = ''); - public function __toString(): string; - public function getOpcode(): string; - public function getLength(): int; - public function getTimestamp(): DateTimeInterface; - public function getContent(): string; - public function setContent(string $content = ''): void; - public function hasContent(): bool; - public function getPayload(): string; - public function setPayload(string $payload = ''): void; - public function getFrames(int $frameSize = 4096): array; +inteface WebSocket\Middleware\ProcessHttpOutgoingInterface imlements WebSocket\Middleware\MiddlewareInterface +{ + public method processHttpOutgoing(WebSocket\Middleware\ProcessHttpStack $stack, WebSocket\Connection $connection, Psr\Http\Message\MessageInterface $message): Psr\Http\Message\MessageInterface; } -``` -### MessageHandler +inteface WebSocket\Middleware\ProcessIncomingInterface imlements WebSocket\Middleware\MiddlewareInterface +{ + public method processIncoming(WebSocket\Middleware\ProcessStack $stack, WebSocket\Connection $connection): WebSocket\Message\Message; +} -```php -class WebSocket\Message\MessageHandler implements Psr\Log\LoggerAwareInterface, Stringable +inteface WebSocket\Middleware\ProcessOutgoingInterface imlements WebSocket\Middleware\MiddlewareInterface { - public function __construct(WebSocket\Frame\FrameHandler $frameHandler); - public function __toString(): string; - public function setLogger(Psr\Log\LoggerInterface $logger): void; - public function push(WebSocket\Message\Message $message, int $size = self::DEFAULT_SIZE): WebSocket\Message\Message; - public function pull(): WebSocket\Message\Message; + public method processOutgoing(WebSocket\Middleware\ProcessStack $stack, WebSocket\Connection $connection, WebSocket\Message\Message $message): WebSocket\Message\Message; } -``` -### Ping +inteface WebSocket\Middleware\ProcessTickInterface imlements WebSocket\Middleware\MiddlewareInterface +{ + public method processTick(WebSocket\Middleware\ProcessTickStack $stack, WebSocket\Connection $connection): void; +} -```php -class WebSocket\Message\Ping extends WebSocket\Message\Message +trait WebSocket\Trait\ListenerTrait { + public method onBinary(Closure $closure): self; + public method onClose(Closure $closure): self; + public method onConnect(Closure $closure): self; + public method onDisconnect(Closure $closure): self; + public method onError(Closure $closure): self; + public method onHandshake(Closure $closure): self; + public method onPing(Closure $closure): self; + public method onPong(Closure $closure): self; + public method onText(Closure $closure): self; + public method onTick(Closure $closure): self; } -``` -### Pong +trait WebSocket\Trait\LoggerAwareTrait +{ + public method attachLogger(mixed $instance): void; + public method initLogger(Psr\Log\LoggerInterface|null $logger = null): void; + public method setLogger(Psr\Log\LoggerInterface $logger): void; +} -```php -class WebSocket\Message\Pong extends WebSocket\Message\Message +trait WebSocket\Trait\OpcodeTrait { } -``` -### Text +trait WebSocket\Trait\SendMethodsTrait +{ + public method binary(string $message): WebSocket\Message\Binary; + public method close(int $status = 1000, string $message = "ttfn"): WebSocket\Message\Close; + public method ping(string $message = ""): WebSocket\Message\Ping; + public method pong(string $message = ""): WebSocket\Message\Pong; + public method text(string $message): WebSocket\Message\Text; +} -```php -class WebSocket\Message\Text extends WebSocket\Message\Message +trait WebSocket\Trait\StringableTrait { + public method __toString(): string; } ``` - diff --git a/src/Http/HttpHandler.php b/src/Http/HttpHandler.php index fdb16f3..3a6e3b3 100644 --- a/src/Http/HttpHandler.php +++ b/src/Http/HttpHandler.php @@ -120,7 +120,6 @@ public function pull(): MessageInterface */ public function push(MessageInterface $message): MessageInterface { - $isRequest = $message instanceof RequestInterface; $data = $this->serializer->message($message); $this->stream->write($data); return $message;