Skip to content

Commit

Permalink
Update for amphp/http v2
Browse files Browse the repository at this point in the history
  • Loading branch information
trowski committed Apr 26, 2023
1 parent 89f6a4a commit 24daf3c
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 42 deletions.
4 changes: 2 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,13 @@
"php": ">=8.1",
"amphp/amp": "^3",
"amphp/byte-stream": "^2",
"amphp/http": "^2-dev",
"amphp/http": "^2",
"amphp/http-client": "^5",
"amphp/socket": "^2",
"amphp/websocket": "^2",
"league/uri": "^6",
"psr/http-message": "^1",
"revolt/event-loop": "^1 || ^0.2.4"
"revolt/event-loop": "^1"
},
"require-dev": {
"amphp/http-server": "^3",
Expand Down
4 changes: 2 additions & 2 deletions src/Rfc6455Connector.php
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ public function connect(WebsocketHandshake $handshake, ?Cancellation $cancellati
return;
}

$extensions = \array_column(Http\parseFieldValueComponents($response, 'sec-websocket-extensions') ?? [], 0, 0);
$extensions = Http\splitHeader($response, 'sec-websocket-extensions') ?? [];

foreach ($extensions as $extension) {
if ($compressionContext = $compressionContextFactory?->fromServerHeader($extension)) {
Expand Down Expand Up @@ -111,7 +111,7 @@ private function generateRequest(WebsocketHandshake $handshake, string $key): Re
$request->setTlsHandshakeTimeout($handshake->getTlsHandshakeTimeout());
$request->setHeaderSizeLimit($handshake->getHeaderSizeLimit());

$extensions = \array_column(Http\parseFieldValueComponents($request, 'sec-websocket-extensions') ?? [], 0, 0);
$extensions = Http\splitHeader($request, 'sec-websocket-extensions');

if ($this->compressionContextFactory && \extension_loaded('zlib')) {
$extensions[] = $this->compressionContextFactory->createRequestHeader();
Expand Down
122 changes: 85 additions & 37 deletions src/WebsocketHandshake.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,38 +5,36 @@
use Amp\ForbidSerialization;
use Amp\Http\Client\Request;
use Amp\Http\HttpMessage;
use Amp\Http\HttpRequest;
use League\Uri;
use Psr\Http\Message\UriInterface as PsrUri;

final class WebsocketHandshake extends HttpMessage
/**
* @psalm-import-type HeaderParamArrayType from HttpMessage
* @psalm-import-type HeaderParamValueType from HttpMessage
* @psalm-import-type QueryArrayType from HttpRequest
* @psalm-import-type QueryValueType from HttpRequest
*/
final class WebsocketHandshake extends HttpRequest
{
use ForbidSerialization;

private PsrUri $uri;

private float $tcpConnectTimeout = 10;

private float $tlsHandshakeTimeout = 10;

private int $headerSizeLimit = Request::DEFAULT_HEADER_SIZE_LIMIT;

/**
* @param string|PsrUri $uri target address of websocket (e.g. ws://foo.bar/bar or
* @param PsrUri|string $uri Target address of websocket (e.g. ws://foo.bar/bar or
* wss://crypto.example/?secureConnection) or a PsrUri instance.
* @param string[]|string[][] $headers
* @param HeaderParamArrayType $headers
*/
public function __construct(PsrUri|string $uri, array $headers = [])
{
$this->uri = $this->makeUri($uri);
$this->setHeaders($headers);
}
parent::__construct('GET', self::makeUri($uri));

/**
* @return PsrUri Websocket URI (scheme will be either ws or wss).
*/
public function getUri(): PsrUri
{
return $this->uri;
$this->setHeaders($headers);
}

/**
Expand All @@ -45,7 +43,7 @@ public function getUri(): PsrUri
public function withUri(PsrUri|string $uri): self
{
$clone = clone $this;
$clone->uri = $clone->makeUri($uri);
$clone->setUri(self::makeUri($uri));

return $clone;
}
Expand Down Expand Up @@ -98,18 +96,13 @@ public function withHeaderSizeLimit(int $headerSizeLimit): self
/**
* Replaces all headers in the returned instance.
*
* @param array<non-empty-string, string|array<string>> $headers
* @param HeaderParamArrayType $headers
*
* @return self Cloned object.
*/
public function withHeaders(array $headers): self
{
$clone = clone $this;

foreach ($clone->getRawHeaders() as [$field]) {
$clone->removeHeader($field);
}

$clone->setHeaders($headers);

return $clone;
Expand All @@ -119,7 +112,7 @@ public function withHeaders(array $headers): self
* Replaces the given header in the returned instance.
*
* @param non-empty-string $name
* @param string|array<string> $value
* @param HeaderParamValueType $value
*
* @return self Cloned object.
*/
Expand All @@ -135,7 +128,7 @@ public function withHeader(string $name, string|array $value): self
* Adds the given header in the returned instance.
*
* @param non-empty-string $name
* @param string|array<string> $value
* @param HeaderParamValueType $value
*
* @return self Cloned object.
*/
Expand All @@ -160,7 +153,7 @@ public function withoutHeader(string $name): self
return $clone;
}

protected function setHeader(string $name, $value): void
protected function setHeader(string $name, array|string $value): void
{
if (($name[0] ?? ':') === ':') {
throw new \Error("Header name cannot be empty or start with a colon (:)");
Expand All @@ -169,7 +162,7 @@ protected function setHeader(string $name, $value): void
parent::setHeader($name, $value);
}

protected function addHeader(string $name, $value): void
protected function addHeader(string $name, array|string $value): void
{
if (($name[0] ?? ':') === ':') {
throw new \Error("Header name cannot be empty or start with a colon (:)");
Expand All @@ -178,25 +171,80 @@ protected function addHeader(string $name, $value): void
parent::addHeader($name, $value);
}

private function makeUri(PsrUri|string $uri): PsrUri
/**
* @param QueryArrayType $parameters
*
* @return self Cloned object.
*/
public function withQueryParameters(array $parameters): self
{
$clone = clone $this;
$clone->setQueryParameters($parameters);

return $clone;
}

/**
* @param QueryValueType $value
*
* @return self Cloned object.
*/
public function withQueryParameter(string $key, array|string|null $value): self
{
$clone = clone $this;
$clone->setQueryParameter($key, $value);

return $clone;
}

/**
* @param QueryValueType $value
*
* @return self Cloned object.
*/
public function withAddedQueryParameter(string $key, array|string|null $value): self
{
$clone = clone $this;
$clone->addQueryParameter($key, $value);

return $clone;
}

/**
* @return self Cloned object.
*/
public function withoutQueryParameter(string $key): self
{
$clone = clone $this;
$clone->removeQueryParameter($key);

return $clone;
}

/**
* @return self Cloned object.
*/
public function withoutQuery(): self
{
$clone = clone $this;
$clone->removeQuery();

return $clone;
}

private static function makeUri(PsrUri|string $uri): PsrUri
{
if (\is_string($uri)) {
try {
$uri = Uri\Http::createFromString($uri);
} catch (\Exception $exception) {
throw new \Error('Invalid Websocket URI provided', 0, $exception);
throw new \ValueError('Invalid Websocket URI provided', 0, $exception);
}
}

switch ($uri->getScheme()) {
case 'ws':
case 'wss':
break;

default:
throw new \Error('The URI scheme must be ws or wss: \'' . $uri->getScheme() . '\'');
}

return $uri;
return match ($uri->getScheme()) {
'ws', 'wss' => $uri,
default => throw new \ValueError('The URI scheme must be ws or wss, got "' . $uri->getScheme() . '"'),
};
}
}
2 changes: 1 addition & 1 deletion test/WebsocketConnectionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ protected function createServer(WebsocketClientHandler $clientHandler): array
{
$logger = new NullLogger;

$httpServer = new SocketHttpServer($logger);
$httpServer = SocketHttpServer::createForDirectAccess($logger);
$httpServer->expose(new InternetAddress('127.0.0.1', 0));
$httpServer->start(
new Websocket($logger, new EmptyWebsocketHandshakeHandler(), $clientHandler),
Expand Down

0 comments on commit 24daf3c

Please sign in to comment.