Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ jobs:
name: PHPUnit (PHP ${{ matrix.php }})
runs-on: ubuntu-24.04
strategy:
fail-fast: false
matrix:
php:
- 8.4
Expand Down
5 changes: 2 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,16 @@
"php": ">=5.3.0",
"evenement/evenement": "^3.0 || ^2.0 || ^1.0",
"fig/http-message-util": "^1.1",
"psr/http-message": "^1.0",
"psr/http-message": "^2.0 || ^1.0",
"react/event-loop": "^1.2",
"react/promise": "^3.2 || ^2.3 || ^1.2.1",
"react/socket": "^1.16",
"react/stream": "^1.4"
},
"require-dev": {
"clue/http-proxy-react": "^1.8",
"clue/reactphp-ssh-proxy": "^1.4",
"clue/socks-react": "^1.4",
"phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36",
"phpunit/phpunit": "^9.6 || ^8.5 || ^5.7 || ^4.8.36",
"react/async": "^4.2 || ^3 || ^2",
"react/promise-stream": "^1.4",
"react/promise-timer": "^1.11"
Expand Down
167 changes: 5 additions & 162 deletions src/Io/AbstractMessage.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,171 +2,14 @@

namespace React\Http\Io;

use Psr\Http\Message\MessageInterface;
use Psr\Http\Message\StreamInterface;
use Composer\InstalledVersions;

/**
* [Internal] Abstract HTTP message base class (PSR-7)
*
* @internal
* @see MessageInterface
*/
abstract class AbstractMessage implements MessageInterface
{
/**
* [Internal] Regex used to match all request header fields into an array, thanks to @kelunik for checking the HTTP specs and coming up with this regex
*
* @internal
* @var string
*/
const REGEX_HEADERS = '/^([^()<>@,;:\\\"\/\[\]?={}\x00-\x20\x7F]++):[\x20\x09]*+((?:[\x20\x09]*+[\x21-\x7E\x80-\xFF]++)*+)[\x20\x09]*+[\r]?+\n/m';

/** @var array<string,string[]> */
private $headers = array();

/** @var array<string,string> */
private $headerNamesLowerCase = array();

/** @var string */
private $protocolVersion;

/** @var StreamInterface */
private $body;

/**
* @param string $protocolVersion
* @param array<string,string|string[]> $headers
* @param StreamInterface $body
*/
protected function __construct($protocolVersion, array $headers, StreamInterface $body)
{
foreach ($headers as $name => $value) {
if ($value !== array()) {
if (\is_array($value)) {
foreach ($value as &$one) {
$one = (string) $one;
}
} else {
$value = array((string) $value);
}

$lower = \strtolower($name);
if (isset($this->headerNamesLowerCase[$lower])) {
$value = \array_merge($this->headers[$this->headerNamesLowerCase[$lower]], $value);
unset($this->headers[$this->headerNamesLowerCase[$lower]]);
}

$this->headers[$name] = $value;
$this->headerNamesLowerCase[$lower] = $name;
}
}

$this->protocolVersion = (string) $protocolVersion;
$this->body = $body;
}

public function getProtocolVersion()
{
return $this->protocolVersion;
}

public function withProtocolVersion($version)
{
if ((string) $version === $this->protocolVersion) {
return $this;
}

$message = clone $this;
$message->protocolVersion = (string) $version;

return $message;
}

public function getHeaders()
{
return $this->headers;
}

public function hasHeader($name)
{
return isset($this->headerNamesLowerCase[\strtolower($name)]);
}

public function getHeader($name)
if (version_compare(InstalledVersions::getVersion('psr/http-message'), '2.0.0', '<')) {
abstract class AbstractMessage extends V1\AbstractMessage
{
$lower = \strtolower($name);
return isset($this->headerNamesLowerCase[$lower]) ? $this->headers[$this->headerNamesLowerCase[$lower]] : array();
}

public function getHeaderLine($name)
{
return \implode(', ', $this->getHeader($name));
}

public function withHeader($name, $value)
} else {
abstract class AbstractMessage extends V2\AbstractMessage
{
if ($value === array()) {
return $this->withoutHeader($name);
} elseif (\is_array($value)) {
foreach ($value as &$one) {
$one = (string) $one;
}
} else {
$value = array((string) $value);
}

$lower = \strtolower($name);
if (isset($this->headerNamesLowerCase[$lower]) && $this->headerNamesLowerCase[$lower] === (string) $name && $this->headers[$this->headerNamesLowerCase[$lower]] === $value) {
return $this;
}

$message = clone $this;
if (isset($message->headerNamesLowerCase[$lower])) {
unset($message->headers[$message->headerNamesLowerCase[$lower]]);
}

$message->headers[$name] = $value;
$message->headerNamesLowerCase[$lower] = $name;

return $message;
}

public function withAddedHeader($name, $value)
{
if ($value === array()) {
return $this;
}

return $this->withHeader($name, \array_merge($this->getHeader($name), \is_array($value) ? $value : array($value)));
}

public function withoutHeader($name)
{
$lower = \strtolower($name);
if (!isset($this->headerNamesLowerCase[$lower])) {
return $this;
}

$message = clone $this;
unset($message->headers[$message->headerNamesLowerCase[$lower]], $message->headerNamesLowerCase[$lower]);

return $message;
}

public function getBody()
{
return $this->body;
}

public function withBody(StreamInterface $body)
{
if ($body === $this->body) {
return $this;
}

$message = clone $this;
$message->body = $body;

return $message;
}
}
151 changes: 5 additions & 146 deletions src/Io/AbstractRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,155 +2,14 @@

namespace React\Http\Io;

use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\StreamInterface;
use Psr\Http\Message\UriInterface;
use React\Http\Message\Uri;
use Composer\InstalledVersions;

/**
* [Internal] Abstract HTTP request base class (PSR-7)
*
* @internal
* @see RequestInterface
*/
abstract class AbstractRequest extends AbstractMessage implements RequestInterface
{
/** @var ?string */
private $requestTarget;

/** @var string */
private $method;

/** @var UriInterface */
private $uri;

/**
* @param string $method
* @param string|UriInterface $uri
* @param array<string,string|string[]> $headers
* @param StreamInterface $body
* @param string unknown $protocolVersion
*/
protected function __construct(
$method,
$uri,
array $headers,
StreamInterface $body,
$protocolVersion
) {
if (\is_string($uri)) {
$uri = new Uri($uri);
} elseif (!$uri instanceof UriInterface) {
throw new \InvalidArgumentException(
'Argument #2 ($uri) expected string|Psr\Http\Message\UriInterface'
);
}

// assign default `Host` request header from URI unless already given explicitly
$host = $uri->getHost();
if ($host !== '') {
foreach ($headers as $name => $value) {
if (\strtolower($name) === 'host' && $value !== array()) {
$host = '';
break;
}
}
if ($host !== '') {
$port = $uri->getPort();
if ($port !== null && (!($port === 80 && $uri->getScheme() === 'http') || !($port === 443 && $uri->getScheme() === 'https'))) {
$host .= ':' . $port;
}

$headers = array('Host' => $host) + $headers;
}
}

parent::__construct($protocolVersion, $headers, $body);

$this->method = $method;
$this->uri = $uri;
}

public function getRequestTarget()
if (version_compare(InstalledVersions::getVersion('psr/http-message'), '2.0.0', '<')) {
abstract class AbstractRequest extends V1\AbstractRequest
{
if ($this->requestTarget !== null) {
return $this->requestTarget;
}

$target = $this->uri->getPath();
if ($target === '') {
$target = '/';
}
if (($query = $this->uri->getQuery()) !== '') {
$target .= '?' . $query;
}

return $target;
}

public function withRequestTarget($requestTarget)
{
if ((string) $requestTarget === $this->requestTarget) {
return $this;
}

$request = clone $this;
$request->requestTarget = (string) $requestTarget;

return $request;
}

public function getMethod()
} else {
abstract class AbstractRequest extends V2\AbstractRequest
{
return $this->method;
}

public function withMethod($method)
{
if ((string) $method === $this->method) {
return $this;
}

$request = clone $this;
$request->method = (string) $method;

return $request;
}

public function getUri()
{
return $this->uri;
}

public function withUri(UriInterface $uri, $preserveHost = false)
{
if ($uri === $this->uri) {
return $this;
}

$request = clone $this;
$request->uri = $uri;

$host = $uri->getHost();
$port = $uri->getPort();
if ($port !== null && $host !== '' && (!($port === 80 && $uri->getScheme() === 'http') || !($port === 443 && $uri->getScheme() === 'https'))) {
$host .= ':' . $port;
}

// update `Host` request header if URI contains a new host and `$preserveHost` is false
if ($host !== '' && (!$preserveHost || $request->getHeaderLine('Host') === '')) {
// first remove all headers before assigning `Host` header to ensure it always comes first
foreach (\array_keys($request->getHeaders()) as $name) {
$request = $request->withoutHeader($name);
}

// add `Host` header first, then all other original headers
$request = $request->withHeader('Host', $host);
foreach ($this->withoutHeader('Host')->getHeaders() as $name => $value) {
$request = $request->withHeader($name, $value);
}
}

return $request;
}
}
Loading
Loading