From 21fc3483024f166bc6129368d9129b84cbc39684 Mon Sep 17 00:00:00 2001 From: recca0120 Date: Sun, 21 Aug 2022 18:43:03 +0800 Subject: [PATCH] remove guzzlehttp/psr7 package --- composer.json | 3 +- src/ResponseIdentifier.php | 84 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 82 insertions(+), 5 deletions(-) diff --git a/composer.json b/composer.json index 7224dff..08f35db 100644 --- a/composer.json +++ b/composer.json @@ -17,8 +17,7 @@ "type": "library", "require": { "ext-json": "*", - "guzzlehttp/promises": "^1.3.1|^1.4", - "guzzlehttp/psr7": "^1.8|^2.4" + "guzzlehttp/promises": "^1.3.1|^1.4" }, "require-dev": { "mockery/mockery": "^0.9|^1.3", diff --git a/src/ResponseIdentifier.php b/src/ResponseIdentifier.php index 907e60f..087a3d4 100644 --- a/src/ResponseIdentifier.php +++ b/src/ResponseIdentifier.php @@ -2,12 +2,15 @@ namespace Recca0120\LaravelParallel; -use GuzzleHttp\Psr7\Message; use Illuminate\Http\Response; +use InvalidArgumentException; use Symfony\Component\HttpFoundation\Response as SymfonyResponse; class ResponseIdentifier { + private const HEADER_REGEX = "(^([^()<>@,;:\\\"/[\]?={}\x01-\x20\x7F]++):[ \t]*+((?:[ \t]*+[\x21-\x7E\x80-\xFF]++)*+)[ \t]*+\r?\n)m"; + private const HEADER_FOLD_REGEX = "(\r?\n[ \t]++)"; + /** * @var string */ @@ -40,9 +43,84 @@ public static function fromSymfonyResponse(SymfonyResponse $response): self public static function fromMessage(string $message): self { - $response = Message::parseResponse(PreventEcho::prevent($message)); + $data = self::parseMessage(PreventEcho::prevent($message)); + // According to https://tools.ietf.org/html/rfc7230#section-3.1.2 the space + // between status-code and reason-phrase is required. But browsers accept + // responses without space and reason as well. + if (! preg_match('/^HTTP\/.* [0-9]{3}( .*|$)/', $data['start-line'])) { + throw new InvalidArgumentException('Invalid response string: '.$data['start-line']); + } + $parts = explode(' ', $data['start-line'], 3); + + return new self($data['body'], (int) $parts[1], $data['headers']); + } + + /** + * Parses an HTTP message into an associative array. + * + * The array contains the "start-line" key containing the start line of + * the message, "headers" key containing an associative array of header + * array values, and a "body" key containing the body of the message. + * + * @link https://github.com/guzzle/psr7/blob/2.4.0/src/Message.php#L114-L167 + * + * @license https://github.com/guzzle/psr7/blob/2.4.0/LICENSE + * + * @param string $message HTTP request or response to parse. + */ + public static function parseMessage(string $message): array + { + if (! $message) { + throw new InvalidArgumentException('Invalid message'); + } + + $message = ltrim($message, "\r\n"); + + $messageParts = preg_split("/\r?\n\r?\n/", $message, 2); + + if ($messageParts === false || count($messageParts) !== 2) { + throw new InvalidArgumentException('Invalid message: Missing header delimiter'); + } + + [$rawHeaders, $body] = $messageParts; + $rawHeaders .= "\r\n"; // Put back the delimiter we split previously + $headerParts = preg_split("/\r?\n/", $rawHeaders, 2); + + if ($headerParts === false || count($headerParts) !== 2) { + throw new InvalidArgumentException('Invalid message: Missing status line'); + } + + [$startLine, $rawHeaders] = $headerParts; + + if (preg_match("/(?:^HTTP\/|^[A-Z]+ \S+ HTTP\/)(\d+(?:\.\d+)?)/i", $startLine, $matches) && $matches[1] === '1.0') { + // Header folding is deprecated for HTTP/1.1, but allowed in HTTP/1.0 + $rawHeaders = preg_replace(self::HEADER_FOLD_REGEX, ' ', $rawHeaders); + } + + /** @var array[] $headerLines */ + $count = preg_match_all(self::HEADER_REGEX, $rawHeaders, $headerLines, PREG_SET_ORDER); + + // If these aren't the same, then one line didn't match and there's an invalid header. + if ($count !== substr_count($rawHeaders, "\n")) { + // Folding is deprecated, see https://tools.ietf.org/html/rfc7230#section-3.2.4 + if (preg_match(self::HEADER_FOLD_REGEX, $rawHeaders)) { + throw new InvalidArgumentException('Invalid header syntax: Obsolete line folding'); + } + + throw new InvalidArgumentException('Invalid header syntax'); + } + + $headers = []; + + foreach ($headerLines as $headerLine) { + $headers[$headerLine[1]][] = $headerLine[2]; + } - return new self((string) $response->getBody(), $response->getStatusCode(), $response->getHeaders()); + return [ + 'start-line' => $startLine, + 'headers' => $headers, + 'body' => $body, + ]; } public function toMessage(): string