| Server IP : 35.80.110.71 / Your IP : 216.73.216.221 Web Server : Apache/2.4.58 (Ubuntu) System : Linux ip-172-31-21-44 6.17.0-1019-aws #19~24.04.1-Ubuntu SMP Tue Jun 23 18:53:06 UTC 2026 x86_64 User : ubuntu ( 1000) PHP Version : 8.3.31 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : OFF Directory : /var/www/splitstream/current/vendor/spiral/roadrunner-http/src/ |
Upload File : |
<?php
declare(strict_types=1);
namespace Spiral\RoadRunner\Http;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestFactoryInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\StreamFactoryInterface;
use Psr\Http\Message\StreamInterface;
use Psr\Http\Message\UploadedFileFactoryInterface;
use Psr\Http\Message\UploadedFileInterface;
use Spiral\RoadRunner\WorkerInterface;
/**
* Manages PSR-7 request and response.
*
* @psalm-import-type UploadedFile from Request
* @psalm-import-type UploadedFilesList from Request
*
* @api
*/
class PSR7Worker implements PSR7WorkerInterface
{
/**
* @var int Preferred chunk size for streaming output.
* if not greater than 0, then streaming response is turned off
* @internal
*/
public int $chunkSize = 0;
private readonly HttpWorker $httpWorker;
/**
* @var string[] Valid values for HTTP protocol version
*/
private static array $allowedVersions = ['1.0', '1.1', '2'];
public function __construct(
WorkerInterface $worker,
private readonly ServerRequestFactoryInterface $requestFactory,
private readonly StreamFactoryInterface $streamFactory,
private readonly UploadedFileFactoryInterface $uploadsFactory,
) {
$this->httpWorker = new HttpWorker($worker);
}
public function getWorker(): WorkerInterface
{
return $this->httpWorker->getWorker();
}
public function getHttpWorker(): HttpWorker
{
return $this->httpWorker;
}
/**
* @psalm-suppress DeprecatedMethod
*
* @param bool $populateServer Whether to populate $_SERVER superglobal.
*
* @throws \JsonException
*/
public function waitRequest(bool $populateServer = true): ?ServerRequestInterface
{
$httpRequest = $this->httpWorker->waitRequest();
if ($httpRequest === null) {
return null;
}
$vars = $this->configureServer($httpRequest);
if ($populateServer) {
$_SERVER = $vars;
}
return $this->mapRequest($httpRequest, $vars);
}
/**
* Send response to the application server.
*
* @throws \JsonException
*/
public function respond(ResponseInterface $response): void
{
$this->httpWorker->respond(
$response->getStatusCode(),
$this->chunkSize > 0
? $this->streamToGenerator($response->getBody())
: (string) $response->getBody(),
$response->getHeaders(),
);
}
/**
* Returns altered copy of _SERVER variable. Sets ip-address,
* request-time and other values.
*
* @return non-empty-array<array-key|string, mixed|string>
*/
protected function configureServer(Request $request): array
{
return GlobalState::enrichServerVars($request);
}
/**
* @deprecated
*/
protected function timeInt(): int
{
return \time();
}
/**
* @deprecated
*/
protected function timeFloat(): float
{
return \microtime(true);
}
/**
* @throws \JsonException
*/
protected function mapRequest(Request $httpRequest, array $server): ServerRequestInterface
{
$request = $this->requestFactory->createServerRequest(
$httpRequest->method,
$httpRequest->uri,
$server,
);
$request = $request
->withProtocolVersion(static::fetchProtocolVersion($httpRequest->protocol))
->withCookieParams($httpRequest->cookies)
->withQueryParams($httpRequest->query)
->withUploadedFiles($this->wrapUploads($httpRequest->uploads))
;
/** @psalm-suppress MixedAssignment */
foreach ($httpRequest->attributes as $name => $value) {
$request = $request->withAttribute($name, $value);
}
foreach ($httpRequest->headers as $name => $value) {
$request = $request->withHeader($name, $value);
}
if ($httpRequest->parsed) {
$request = $request->withParsedBody($httpRequest->getParsedBody());
}
if ($httpRequest->body !== '') {
return $request->withBody($this->streamFactory->createStream($httpRequest->body));
}
return $request;
}
/**
* Wraps all uploaded files with UploadedFile.
*
* @param UploadedFilesList $files
* @return UploadedFileInterface[]|mixed[]
*/
protected function wrapUploads(array $files): array
{
$result = [];
foreach ($files as $index => $file) {
if (! isset($file['name'])) {
/** @psalm-var UploadedFilesList $file */
$result[$index] = $this->wrapUploads($file);
continue;
}
if ($file['error'] === \UPLOAD_ERR_OK) {
$stream = $this->streamFactory->createStreamFromFile($file['tmpName']);
} else {
$stream = $this->streamFactory->createStream();
}
$result[$index] = $this->uploadsFactory->createUploadedFile(
$stream,
$file['size'],
$file['error'],
$file['name'],
$file['mime'],
);
}
return $result;
}
/**
* Normalize HTTP protocol version to valid values
*/
private static function fetchProtocolVersion(string $version): string
{
$v = \substr($version, 5);
if ($v === '2.0') {
return '2';
}
// Fallback for values outside of valid protocol versions
if (! \in_array($v, static::$allowedVersions, true)) {
return '1.1';
}
return $v;
}
/**
* @return \Generator<mixed, scalar|\Stringable, mixed, \Stringable|scalar|null> Compatible
* with {@see HttpWorker::respondStream}.
*/
private function streamToGenerator(StreamInterface $stream): \Generator
{
$stream->rewind();
$size = $stream->getSize();
if ($size !== null && $size < $this->chunkSize) {
return (string) $stream;
}
$sum = 0;
while (!$stream->eof()) {
if ($size === null) {
$chunk = $stream->read($this->chunkSize);
} else {
$left = $size - $sum;
$chunk = $stream->read(\min($this->chunkSize, $left));
if ($left <= $this->chunkSize && \strlen($chunk) === $left) {
return $chunk;
}
}
$sum += \strlen($chunk);
yield $chunk;
}
}
}