diff options
author | Roeland Jago Douma <roeland@famdouma.nl> | 2019-05-15 16:20:16 +0300 |
---|---|---|
committer | Roeland Jago Douma <roeland@famdouma.nl> | 2019-05-15 16:20:16 +0300 |
commit | 793150ff5c84584b3d2fba9ddbdf9069d65d0d19 (patch) | |
tree | 4f746e05d0102e5d81790ab1a0bfd96c259c8c62 /guzzlehttp | |
parent | 35087f0ce01cfb32ad75f9db53196e5818d449f6 (diff) |
Vendor in aws-sdk-php
Signed-off-by: Roeland Jago Douma <roeland@famdouma.nl>
Diffstat (limited to 'guzzlehttp')
-rw-r--r-- | guzzlehttp/psr7/CHANGELOG.md | 231 | ||||
-rw-r--r-- | guzzlehttp/psr7/README.md | 8 | ||||
-rw-r--r-- | guzzlehttp/psr7/composer.json | 14 | ||||
-rw-r--r-- | guzzlehttp/psr7/src/AppendStream.php | 16 | ||||
-rw-r--r-- | guzzlehttp/psr7/src/FnStream.php | 9 | ||||
-rw-r--r-- | guzzlehttp/psr7/src/InflateStream.php | 2 | ||||
-rw-r--r-- | guzzlehttp/psr7/src/Request.php | 4 | ||||
-rw-r--r-- | guzzlehttp/psr7/src/Response.php | 4 | ||||
-rw-r--r-- | guzzlehttp/psr7/src/Rfc7230.php | 18 | ||||
-rw-r--r-- | guzzlehttp/psr7/src/ServerRequest.php | 32 | ||||
-rw-r--r-- | guzzlehttp/psr7/src/Stream.php | 39 | ||||
-rw-r--r-- | guzzlehttp/psr7/src/StreamWrapper.php | 46 | ||||
-rw-r--r-- | guzzlehttp/psr7/src/Uri.php | 88 | ||||
-rw-r--r-- | guzzlehttp/psr7/src/functions.php | 130 |
14 files changed, 492 insertions, 149 deletions
diff --git a/guzzlehttp/psr7/CHANGELOG.md b/guzzlehttp/psr7/CHANGELOG.md index 5c252b3a..27b65f09 100644 --- a/guzzlehttp/psr7/CHANGELOG.md +++ b/guzzlehttp/psr7/CHANGELOG.md @@ -1,32 +1,82 @@ -# CHANGELOG +# Change Log -## 1.4.2 - 2017-03-20 -* Reverted BC break to `Uri::resolve` and `Uri::removeDotSegments` by removing +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) +and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). + + +## [Unreleased] + + +## [1.5.2] - 2018-12-04 + +### Fixed + +- Check body size when getting the message summary + + +## [1.5.1] - 2018-12-04 + +### Fixed + +- Get the summary of a body only if it is readable + + +## [1.5.0] - 2018-12-03 + +### Added + +- Response first-line to response string exception (fixes #145) +- A test for #129 behavior +- `get_message_body_summary` function in order to get the message summary +- `3gp` and `mkv` mime types + +### Changed + +- Clarify exception message when stream is detached + +### Deprecated + +- Deprecated parsing folded header lines as per RFC 7230 + +### Fixed + +- Fix `AppendStream::detach` to not close streams +- `InflateStream` preserves `isSeekable` attribute of the underlying stream +- `ServerRequest::getUriFromGlobals` to support URLs in query parameters + + +Several other fixes and improvements. + + +## [1.4.2] - 2017-03-20 + +### Fixed + +- Reverted BC break to `Uri::resolve` and `Uri::removeDotSegments` by removing calls to `trigger_error` when deprecated methods are invoked. -## 1.4.1 - 2017-02-27 -* Reverted BC break by reintroducing behavior to automagically fix a URI with a +## [1.4.1] - 2017-02-27 + +### Added + +- Rriggering of silenced deprecation warnings. + +### Fixed + +- Reverted BC break by reintroducing behavior to automagically fix a URI with a relative path and an authority by adding a leading slash to the path. It's only deprecated now. -* Added triggering of silenced deprecation warnings. -## 1.4.0 - 2017-02-21 -* Fix `Stream::read` when length parameter <= 0. -* `copy_to_stream` reads bytes in chunks instead of `maxLen` into memory. -* Fix `ServerRequest::getUriFromGlobals` when `Host` header contains port. -* Ensure `ServerRequest::getUriFromGlobals` returns a URI in absolute form. -* Allow `parse_response` to parse a response without delimiting space and reason. -* Ensure each URI modification results in a valid URI according to PSR-7 discussions. - Invalid modifications will throw an exception instead of returning a wrong URI or - doing some magic. - - `(new Uri)->withPath('foo')->withHost('example.com')` will throw an exception - because the path of a URI with an authority must start with a slash "/" or be empty - - `(new Uri())->withScheme('http')` will return `'http://localhost'` -* Fix compatibility of URIs with `file` scheme and empty host. -* Added common URI utility methods based on RFC 3986 (see documentation in the readme): +## [1.4.0] - 2017-02-21 + +### Added + +- Added common URI utility methods based on RFC 3986 (see documentation in the readme): - `Uri::isDefaultPort` - `Uri::isAbsolute` - `Uri::isNetworkPathReference` @@ -37,69 +87,117 @@ - `UriNormalizer::normalize` - `UriNormalizer::isEquivalent` - `UriResolver::relativize` -* Deprecated `Uri::resolve` in favor of `UriResolver::resolve` -* Deprecated `Uri::removeDotSegments` in favor of `UriResolver::removeDotSegments` -## 1.3.1 - 2016-06-25 +### Changed + +- Ensure `ServerRequest::getUriFromGlobals` returns a URI in absolute form. +- Allow `parse_response` to parse a response without delimiting space and reason. +- Ensure each URI modification results in a valid URI according to PSR-7 discussions. + Invalid modifications will throw an exception instead of returning a wrong URI or + doing some magic. + - `(new Uri)->withPath('foo')->withHost('example.com')` will throw an exception + because the path of a URI with an authority must start with a slash "/" or be empty + - `(new Uri())->withScheme('http')` will return `'http://localhost'` + +### Deprecated + +- `Uri::resolve` in favor of `UriResolver::resolve` +- `Uri::removeDotSegments` in favor of `UriResolver::removeDotSegments` + +### Fixed + +- `Stream::read` when length parameter <= 0. +- `copy_to_stream` reads bytes in chunks instead of `maxLen` into memory. +- `ServerRequest::getUriFromGlobals` when `Host` header contains port. +- Compatibility of URIs with `file` scheme and empty host. + -* Fix `Uri::__toString` for network path references, e.g. `//example.org`. -* Fix missing lowercase normalization for host. -* Fix handling of URI components in case they are `'0'` in a lot of places, +## [1.3.1] - 2016-06-25 + +### Fixed + +- `Uri::__toString` for network path references, e.g. `//example.org`. +- Missing lowercase normalization for host. +- Handling of URI components in case they are `'0'` in a lot of places, e.g. as a user info password. -* Fix `Uri::withAddedHeader` to correctly merge headers with different case. -* Fix trimming of header values in `Uri::withAddedHeader`. Header values may +- `Uri::withAddedHeader` to correctly merge headers with different case. +- Trimming of header values in `Uri::withAddedHeader`. Header values may be surrounded by whitespace which should be ignored according to RFC 7230 Section 3.2.4. This does not apply to header names. -* Fix `Uri::withAddedHeader` with an array of header values. -* Fix `Uri::resolve` when base path has no slash and handling of fragment. -* Fix handling of encoding in `Uri::with(out)QueryValue` so one can pass the +- `Uri::withAddedHeader` with an array of header values. +- `Uri::resolve` when base path has no slash and handling of fragment. +- Handling of encoding in `Uri::with(out)QueryValue` so one can pass the key/value both in encoded as well as decoded form to those methods. This is consistent with withPath, withQuery etc. -* Fix `ServerRequest::withoutAttribute` when attribute value is null. +- `ServerRequest::withoutAttribute` when attribute value is null. + + +## [1.3.0] - 2016-04-13 -## 1.3.0 - 2016-04-13 +### Added -* Added remaining interfaces needed for full PSR7 compatibility +- Remaining interfaces needed for full PSR7 compatibility (ServerRequestInterface, UploadedFileInterface, etc.). -* Added support for stream_for from scalars. -* Can now extend Uri. -* Fixed a bug in validating request methods by making it more permissive. +- Support for stream_for from scalars. -## 1.2.3 - 2016-02-18 +### Changed -* Fixed support in `GuzzleHttp\Psr7\CachingStream` for seeking forward on remote +- Can now extend Uri. + +### Fixed +- A bug in validating request methods by making it more permissive. + + +## [1.2.3] - 2016-02-18 + +### Fixed + +- Support in `GuzzleHttp\Psr7\CachingStream` for seeking forward on remote streams, which can sometimes return fewer bytes than requested with `fread`. -* Fixed handling of gzipped responses with FNAME headers. +- Handling of gzipped responses with FNAME headers. + + +## [1.2.2] - 2016-01-22 + +### Added + +- Support for URIs without any authority. +- Support for HTTP 451 'Unavailable For Legal Reasons.' +- Support for using '0' as a filename. +- Support for including non-standard ports in Host headers. -## 1.2.2 - 2016-01-22 -* Added support for URIs without any authority. -* Added support for HTTP 451 'Unavailable For Legal Reasons.' -* Added support for using '0' as a filename. -* Added support for including non-standard ports in Host headers. +## [1.2.1] - 2015-11-02 -## 1.2.1 - 2015-11-02 +### Changes -* Now supporting negative offsets when seeking to SEEK_END. +- Now supporting negative offsets when seeking to SEEK_END. -## 1.2.0 - 2015-08-15 -* Body as `"0"` is now properly added to a response. -* Now allowing forward seeking in CachingStream. -* Now properly parsing HTTP requests that contain proxy targets in +## [1.2.0] - 2015-08-15 + +### Changed + +- Body as `"0"` is now properly added to a response. +- Now allowing forward seeking in CachingStream. +- Now properly parsing HTTP requests that contain proxy targets in `parse_request`. -* functions.php is now conditionally required. -* user-info is no longer dropped when resolving URIs. +- functions.php is now conditionally required. +- user-info is no longer dropped when resolving URIs. + + +## [1.1.0] - 2015-06-24 -## 1.1.0 - 2015-06-24 +### Changed -* URIs can now be relative. -* `multipart/form-data` headers are now overridden case-insensitively. -* URI paths no longer encode the following characters because they are allowed +- URIs can now be relative. +- `multipart/form-data` headers are now overridden case-insensitively. +- URI paths no longer encode the following characters because they are allowed in URIs: "(", ")", "*", "!", "'" -* A port is no longer added to a URI when the scheme is missing and no port is +- A port is no longer added to a URI when the scheme is missing and no port is present. + ## 1.0.0 - 2015-05-19 Initial release. @@ -108,3 +206,20 @@ Currently unsupported: - `Psr\Http\Message\ServerRequestInterface` - `Psr\Http\Message\UploadedFileInterface` + + + +[Unreleased]: https://github.com/guzzle/psr7/compare/1.5.2...HEAD +[1.5.2]: https://github.com/guzzle/psr7/compare/1.5.1...1.5.2 +[1.5.1]: https://github.com/guzzle/psr7/compare/1.5.0...1.5.1 +[1.5.0]: https://github.com/guzzle/psr7/compare/1.4.2...1.5.0 +[1.4.2]: https://github.com/guzzle/psr7/compare/1.4.1...1.4.2 +[1.4.1]: https://github.com/guzzle/psr7/compare/1.4.0...1.4.1 +[1.4.0]: https://github.com/guzzle/psr7/compare/1.3.1...1.4.0 +[1.3.1]: https://github.com/guzzle/psr7/compare/1.3.0...1.3.1 +[1.3.0]: https://github.com/guzzle/psr7/compare/1.2.3...1.3.0 +[1.2.3]: https://github.com/guzzle/psr7/compare/1.2.2...1.2.3 +[1.2.2]: https://github.com/guzzle/psr7/compare/1.2.1...1.2.2 +[1.2.1]: https://github.com/guzzle/psr7/compare/1.2.0...1.2.1 +[1.2.0]: https://github.com/guzzle/psr7/compare/1.1.0...1.2.0 +[1.1.0]: https://github.com/guzzle/psr7/compare/1.0.0...1.1.0 diff --git a/guzzlehttp/psr7/README.md b/guzzlehttp/psr7/README.md index 16499358..c60a6a38 100644 --- a/guzzlehttp/psr7/README.md +++ b/guzzlehttp/psr7/README.md @@ -372,7 +372,7 @@ This method accepts the following `$resource` types: $stream = GuzzleHttp\Psr7\stream_for('foo'); $stream = GuzzleHttp\Psr7\stream_for(fopen('/path/to/file', 'r')); -$generator function ($bytes) { +$generator = function ($bytes) { for ($i = 0; $i < $bytes; $i++) { yield ' '; } @@ -606,6 +606,12 @@ Creates a new URI with a specific query string value. Any existing query string provided key are removed and replaced with the given key value pair. A value of null will set the query string key without a value, e.g. "key" instead of "key=value". +### `GuzzleHttp\Psr7\Uri::withQueryValues` + +`public static function withQueryValues(UriInterface $uri, array $keyValueArray): UriInterface` + +Creates a new URI with multiple query string values. It has the same behavior as `withQueryValue()` but for an +associative array of key => value. ### `GuzzleHttp\Psr7\Uri::withoutQueryValue` diff --git a/guzzlehttp/psr7/composer.json b/guzzlehttp/psr7/composer.json index b1c5a90b..2add153e 100644 --- a/guzzlehttp/psr7/composer.json +++ b/guzzlehttp/psr7/composer.json @@ -2,7 +2,7 @@ "name": "guzzlehttp/psr7", "type": "library", "description": "PSR-7 message implementation that also provides common utility methods", - "keywords": ["request", "response", "message", "stream", "http", "uri", "url"], + "keywords": ["request", "response", "message", "stream", "http", "uri", "url", "psr-7"], "license": "MIT", "authors": [ { @@ -17,10 +17,11 @@ ], "require": { "php": ">=5.4.0", - "psr/http-message": "~1.0" + "psr/http-message": "~1.0", + "ralouphie/getallheaders": "^2.0.5" }, "require-dev": { - "phpunit/phpunit": "~4.0" + "phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.8" }, "provide": { "psr/http-message-implementation": "1.0" @@ -31,9 +32,14 @@ }, "files": ["src/functions_include.php"] }, + "autoload-dev": { + "psr-4": { + "GuzzleHttp\\Tests\\Psr7\\": "tests/" + } + }, "extra": { "branch-alias": { - "dev-master": "1.4-dev" + "dev-master": "1.5-dev" } } } diff --git a/guzzlehttp/psr7/src/AppendStream.php b/guzzlehttp/psr7/src/AppendStream.php index 23039fd7..472a0d61 100644 --- a/guzzlehttp/psr7/src/AppendStream.php +++ b/guzzlehttp/psr7/src/AppendStream.php @@ -16,7 +16,6 @@ class AppendStream implements StreamInterface private $seekable = true; private $current = 0; private $pos = 0; - private $detached = false; /** * @param StreamInterface[] $streams Streams to decorate. Each stream must @@ -73,6 +72,7 @@ class AppendStream implements StreamInterface public function close() { $this->pos = $this->current = 0; + $this->seekable = true; foreach ($this->streams as $stream) { $stream->close(); @@ -82,14 +82,22 @@ class AppendStream implements StreamInterface } /** - * Detaches each attached stream + * Detaches each attached stream. + * + * Returns null as it's not clear which underlying stream resource to return. * * {@inheritdoc} */ public function detach() { - $this->close(); - $this->detached = true; + $this->pos = $this->current = 0; + $this->seekable = true; + + foreach ($this->streams as $stream) { + $stream->detach(); + } + + $this->streams = []; } public function tell() diff --git a/guzzlehttp/psr7/src/FnStream.php b/guzzlehttp/psr7/src/FnStream.php index cc9b4453..73daea6f 100644 --- a/guzzlehttp/psr7/src/FnStream.php +++ b/guzzlehttp/psr7/src/FnStream.php @@ -53,6 +53,15 @@ class FnStream implements StreamInterface } /** + * An unserialize would allow the __destruct to run when the unserialized value goes out of scope. + * @throws \LogicException + */ + public function __wakeup() + { + throw new \LogicException('FnStream should never be unserialized'); + } + + /** * Adds custom functionality to an underlying stream by intercepting * specific method calls. * diff --git a/guzzlehttp/psr7/src/InflateStream.php b/guzzlehttp/psr7/src/InflateStream.php index 0051d3fe..5e4f6028 100644 --- a/guzzlehttp/psr7/src/InflateStream.php +++ b/guzzlehttp/psr7/src/InflateStream.php @@ -27,7 +27,7 @@ class InflateStream implements StreamInterface $stream = new LimitStream($stream, -1, 10 + $filenameHeaderLength); $resource = StreamWrapper::getResource($stream); stream_filter_append($resource, 'zlib.inflate', STREAM_FILTER_READ); - $this->stream = new Stream($resource); + $this->stream = $stream->isSeekable() ? new Stream($resource) : new NoSeekStream(new Stream($resource)); } /** diff --git a/guzzlehttp/psr7/src/Request.php b/guzzlehttp/psr7/src/Request.php index 08285484..00066424 100644 --- a/guzzlehttp/psr7/src/Request.php +++ b/guzzlehttp/psr7/src/Request.php @@ -45,7 +45,7 @@ class Request implements RequestInterface $this->setHeaders($headers); $this->protocol = $version; - if (!$this->hasHeader('Host')) { + if (!isset($this->headerNames['host'])) { $this->updateHostFromUri(); } @@ -110,7 +110,7 @@ class Request implements RequestInterface $new = clone $this; $new->uri = $uri; - if (!$preserveHost) { + if (!$preserveHost || !isset($this->headerNames['host'])) { $new->updateHostFromUri(); } diff --git a/guzzlehttp/psr7/src/Response.php b/guzzlehttp/psr7/src/Response.php index 2830c6c9..6e72c06b 100644 --- a/guzzlehttp/psr7/src/Response.php +++ b/guzzlehttp/psr7/src/Response.php @@ -93,6 +93,10 @@ class Response implements ResponseInterface $version = '1.1', $reason = null ) { + if (filter_var($status, FILTER_VALIDATE_INT) === false) { + throw new \InvalidArgumentException('Status code must be an integer value.'); + } + $this->statusCode = (int) $status; if ($body !== '' && $body !== null) { diff --git a/guzzlehttp/psr7/src/Rfc7230.php b/guzzlehttp/psr7/src/Rfc7230.php new file mode 100644 index 00000000..505e4742 --- /dev/null +++ b/guzzlehttp/psr7/src/Rfc7230.php @@ -0,0 +1,18 @@ +<?php + +namespace GuzzleHttp\Psr7; + +final class Rfc7230 +{ + /** + * Header related regular expressions (copied from amphp/http package) + * (Note: once we require PHP 7.x we could just depend on the upstream package) + * + * Note: header delimiter (\r\n) is modified to \r?\n to accept line feed only delimiters for BC reasons. + * + * @link https://github.com/amphp/http/blob/v1.0.1/src/Rfc7230.php#L12-L15 + * @license https://github.com/amphp/http/blob/v1.0.1/LICENSE + */ + const HEADER_REGEX = "(^([^()<>@,;:\\\"/[\]?={}\x01-\x20\x7F]++):[ \t]*+((?:[ \t]*+[\x21-\x7E\x80-\xFF]++)*+)[ \t]*+\r?\n)m"; + const HEADER_FOLD_REGEX = "(\r?\n[ \t]++)"; +} diff --git a/guzzlehttp/psr7/src/ServerRequest.php b/guzzlehttp/psr7/src/ServerRequest.php index 575aab84..99f453a5 100644 --- a/guzzlehttp/psr7/src/ServerRequest.php +++ b/guzzlehttp/psr7/src/ServerRequest.php @@ -166,7 +166,7 @@ class ServerRequest extends Request implements ServerRequestInterface public static function fromGlobals() { $method = isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : 'GET'; - $headers = function_exists('getallheaders') ? getallheaders() : []; + $headers = getallheaders(); $uri = self::getUriFromGlobals(); $body = new LazyOpenStream('php://input', 'r+'); $protocol = isset($_SERVER['SERVER_PROTOCOL']) ? str_replace('HTTP/', '', $_SERVER['SERVER_PROTOCOL']) : '1.1'; @@ -180,23 +180,41 @@ class ServerRequest extends Request implements ServerRequestInterface ->withUploadedFiles(self::normalizeFiles($_FILES)); } + private static function extractHostAndPortFromAuthority($authority) + { + $uri = 'http://'.$authority; + $parts = parse_url($uri); + if (false === $parts) { + return [null, null]; + } + + $host = isset($parts['host']) ? $parts['host'] : null; + $port = isset($parts['port']) ? $parts['port'] : null; + + return [$host, $port]; + } + /** * Get a Uri populated with values from $_SERVER. * * @return UriInterface */ - public static function getUriFromGlobals() { + public static function getUriFromGlobals() + { $uri = new Uri(''); $uri = $uri->withScheme(!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' ? 'https' : 'http'); $hasPort = false; if (isset($_SERVER['HTTP_HOST'])) { - $hostHeaderParts = explode(':', $_SERVER['HTTP_HOST']); - $uri = $uri->withHost($hostHeaderParts[0]); - if (isset($hostHeaderParts[1])) { + list($host, $port) = self::extractHostAndPortFromAuthority($_SERVER['HTTP_HOST']); + if ($host !== null) { + $uri = $uri->withHost($host); + } + + if ($port !== null) { $hasPort = true; - $uri = $uri->withPort($hostHeaderParts[1]); + $uri = $uri->withPort($port); } } elseif (isset($_SERVER['SERVER_NAME'])) { $uri = $uri->withHost($_SERVER['SERVER_NAME']); @@ -210,7 +228,7 @@ class ServerRequest extends Request implements ServerRequestInterface $hasQuery = false; if (isset($_SERVER['REQUEST_URI'])) { - $requestUriParts = explode('?', $_SERVER['REQUEST_URI']); + $requestUriParts = explode('?', $_SERVER['REQUEST_URI'], 2); $uri = $uri->withPath($requestUriParts[0]); if (isset($requestUriParts[1])) { $hasQuery = true; diff --git a/guzzlehttp/psr7/src/Stream.php b/guzzlehttp/psr7/src/Stream.php index e3366287..111795eb 100644 --- a/guzzlehttp/psr7/src/Stream.php +++ b/guzzlehttp/psr7/src/Stream.php @@ -24,11 +24,11 @@ class Stream implements StreamInterface 'r' => true, 'w+' => true, 'r+' => true, 'x+' => true, 'c+' => true, 'rb' => true, 'w+b' => true, 'r+b' => true, 'x+b' => true, 'c+b' => true, 'rt' => true, 'w+t' => true, 'r+t' => true, - 'x+t' => true, 'c+t' => true, 'a+' => true + 'x+t' => true, 'c+t' => true, 'a+' => true, 'rb+' => true, ], 'write' => [ 'w' => true, 'w+' => true, 'rw' => true, 'r+' => true, 'x+' => true, - 'c+' => true, 'wb' => true, 'w+b' => true, 'r+b' => true, + 'c+' => true, 'wb' => true, 'w+b' => true, 'r+b' => true, 'rb+' => true, 'x+b' => true, 'c+b' => true, 'w+t' => true, 'r+t' => true, 'x+t' => true, 'c+t' => true, 'a' => true, 'a+' => true ] @@ -70,15 +70,6 @@ class Stream implements StreamInterface $this->uri = $this->getMetadata('uri'); } - public function __get($name) - { - if ($name == 'stream') { - throw new \RuntimeException('The stream is detached'); - } - - throw new \BadMethodCallException('No value for ' . $name); - } - /** * Closes the stream when the destructed */ @@ -99,6 +90,10 @@ class Stream implements StreamInterface public function getContents() { + if (!isset($this->stream)) { + throw new \RuntimeException('Stream is detached'); + } + $contents = stream_get_contents($this->stream); if ($contents === false) { @@ -173,11 +168,19 @@ class Stream implements StreamInterface public function eof() { - return !$this->stream || feof($this->stream); + if (!isset($this->stream)) { + throw new \RuntimeException('Stream is detached'); + } + + return feof($this->stream); } public function tell() { + if (!isset($this->stream)) { + throw new \RuntimeException('Stream is detached'); + } + $result = ftell($this->stream); if ($result === false) { @@ -194,9 +197,13 @@ class Stream implements StreamInterface public function seek($offset, $whence = SEEK_SET) { + if (!isset($this->stream)) { + throw new \RuntimeException('Stream is detached'); + } if (!$this->seekable) { throw new \RuntimeException('Stream is not seekable'); - } elseif (fseek($this->stream, $offset, $whence) === -1) { + } + if (fseek($this->stream, $offset, $whence) === -1) { throw new \RuntimeException('Unable to seek to stream position ' . $offset . ' with whence ' . var_export($whence, true)); } @@ -204,6 +211,9 @@ class Stream implements StreamInterface public function read($length) { + if (!isset($this->stream)) { + throw new \RuntimeException('Stream is detached'); + } if (!$this->readable) { throw new \RuntimeException('Cannot read from non-readable stream'); } @@ -225,6 +235,9 @@ class Stream implements StreamInterface public function write($string) { + if (!isset($this->stream)) { + throw new \RuntimeException('Stream is detached'); + } if (!$this->writable) { throw new \RuntimeException('Cannot write to a non-writable stream'); } diff --git a/guzzlehttp/psr7/src/StreamWrapper.php b/guzzlehttp/psr7/src/StreamWrapper.php index cf7b2232..0f3a2856 100644 --- a/guzzlehttp/psr7/src/StreamWrapper.php +++ b/guzzlehttp/psr7/src/StreamWrapper.php @@ -38,9 +38,21 @@ class StreamWrapper . 'writable, or both.'); } - return fopen('guzzle://stream', $mode, null, stream_context_create([ + return fopen('guzzle://stream', $mode, null, self::createStreamContext($stream)); + } + + /** + * Creates a stream context that can be used to open a stream as a php stream resource. + * + * @param StreamInterface $stream + * + * @return resource + */ + public static function createStreamContext(StreamInterface $stream) + { + return stream_context_create([ 'guzzle' => ['stream' => $stream] - ])); + ]); } /** @@ -94,12 +106,21 @@ class StreamWrapper return true; } + public function stream_cast($cast_as) + { + $stream = clone($this->stream); + + return $stream->detach(); + } + public function stream_stat() { static $modeMap = [ 'r' => 33060, + 'rb' => 33060, 'r+' => 33206, - 'w' => 33188 + 'w' => 33188, + 'wb' => 33188 ]; return [ @@ -118,4 +139,23 @@ class StreamWrapper 'blocks' => 0 ]; } + + public function url_stat($path, $flags) + { + return [ + 'dev' => 0, + 'ino' => 0, + 'mode' => 0, + 'nlink' => 0, + 'uid' => 0, + 'gid' => 0, + 'rdev' => 0, + 'size' => 0, + 'atime' => 0, + 'mtime' => 0, + 'ctime' => 0, + 'blksize' => 0, + 'blocks' => 0 + ]; + } } diff --git a/guzzlehttp/psr7/src/Uri.php b/guzzlehttp/psr7/src/Uri.php index f46c1db9..36219568 100644 --- a/guzzlehttp/psr7/src/Uri.php +++ b/guzzlehttp/psr7/src/Uri.php @@ -301,15 +301,7 @@ class Uri implements UriInterface */ public static function withoutQueryValue(UriInterface $uri, $key) { - $current = $uri->getQuery(); - if ($current === '') { - return $uri; - } - - $decodedKey = rawurldecode($key); - $result = array_filter(explode('&', $current), function ($part) use ($decodedKey) { - return rawurldecode(explode('=', $part)[0]) !== $decodedKey; - }); + $result = self::getFilteredQueryString($uri, [$key]); return $uri->withQuery(implode('&', $result)); } @@ -331,26 +323,29 @@ class Uri implements UriInterface */ public static function withQueryValue(UriInterface $uri, $key, $value) { - $current = $uri->getQuery(); + $result = self::getFilteredQueryString($uri, [$key]); - if ($current === '') { - $result = []; - } else { - $decodedKey = rawurldecode($key); - $result = array_filter(explode('&', $current), function ($part) use ($decodedKey) { - return rawurldecode(explode('=', $part)[0]) !== $decodedKey; - }); - } + $result[] = self::generateQueryString($key, $value); - // Query string separators ("=", "&") within the key or value need to be encoded - // (while preventing double-encoding) before setting the query string. All other - // chars that need percent-encoding will be encoded by withQuery(). - $key = strtr($key, self::$replaceQuery); + return $uri->withQuery(implode('&', $result)); + } - if ($value !== null) { - $result[] = $key . '=' . strtr($value, self::$replaceQuery); - } else { - $result[] = $key; + /** + * Creates a new URI with multiple specific query string values. + * + * It has the same behavior as withQueryValue() but for an associative array of key => value. + * + * @param UriInterface $uri URI to use as a base. + * @param array $keyValueArray Associative array of key and values + * + * @return UriInterface + */ + public static function withQueryValues(UriInterface $uri, array $keyValueArray) + { + $result = self::getFilteredQueryString($uri, array_keys($keyValueArray)); + + foreach ($keyValueArray as $key => $value) { + $result[] = self::generateQueryString($key, $value); } return $uri->withQuery(implode('&', $result)); @@ -620,6 +615,47 @@ class Uri implements UriInterface return $port; } + /** + * @param UriInterface $uri + * @param array $keys + * + * @return array + */ + private static function getFilteredQueryString(UriInterface $uri, array $keys) + { + $current = $uri->getQuery(); + + if ($current === '') { + return []; + } + + $decodedKeys = array_map('rawurldecode', $keys); + + return array_filter(explode('&', $current), function ($part) use ($decodedKeys) { + return !in_array(rawurldecode(explode('=', $part)[0]), $decodedKeys, true); + }); + } + + /** + * @param string $key + * @param string|null $value + * + * @return string + */ + private static function generateQueryString($key, $value) + { + // Query string separators ("=", "&") within the key or value need to be encoded + // (while preventing double-encoding) before setting the query string. All other + // chars that need percent-encoding will be encoded by withQuery(). + $queryString = strtr($key, self::$replaceQuery); + + if ($value !== null) { + $queryString .= '=' . strtr($value, self::$replaceQuery); + } + + return $queryString; + } + private function removeDefaultPort() { if ($this->port !== null && self::isDefaultPort($this)) { diff --git a/guzzlehttp/psr7/src/functions.php b/guzzlehttp/psr7/src/functions.php index e40348d6..957bfb42 100644 --- a/guzzlehttp/psr7/src/functions.php +++ b/guzzlehttp/psr7/src/functions.php @@ -69,10 +69,10 @@ function uri_for($uri) * - metadata: Array of custom metadata. * - size: Size of the stream. * - * @param resource|string|null|int|float|bool|StreamInterface|callable $resource Entity body data - * @param array $options Additional options + * @param resource|string|null|int|float|bool|StreamInterface|callable|\Iterator $resource Entity body data + * @param array $options Additional options * - * @return Stream + * @return StreamInterface * @throws \InvalidArgumentException if the $resource arg is not valid. */ function stream_for($resource = '', array $options = []) @@ -238,7 +238,7 @@ function modify_request(RequestInterface $request, array $changes) } if ($request instanceof ServerRequestInterface) { - return new ServerRequest( + return (new ServerRequest( isset($changes['method']) ? $changes['method'] : $request->getMethod(), $uri, $headers, @@ -247,7 +247,11 @@ function modify_request(RequestInterface $request, array $changes) ? $changes['version'] : $request->getProtocolVersion(), $request->getServerParams() - ); + )) + ->withParsedBody($request->getParsedBody()) + ->withQueryParams($request->getQueryParams()) + ->withCookieParams($request->getCookieParams()) + ->withUploadedFiles($request->getUploadedFiles()); } return new Request( @@ -431,7 +435,7 @@ function hash( * @param StreamInterface $stream Stream to read from * @param int $maxLength Maximum buffer length * - * @return string|bool + * @return string */ function readline(StreamInterface $stream, $maxLength = null) { @@ -495,7 +499,7 @@ function parse_response($message) // 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'); + throw new \InvalidArgumentException('Invalid response string: ' . $data['start-line']); } $parts = explode(' ', $data['start-line'], 3); @@ -516,8 +520,8 @@ function parse_response($message) * PHP style arrays into an associative array (e.g., foo[a]=1&foo[b]=2 will * be parsed into ['foo[a]' => '1', 'foo[b]' => '2']). * - * @param string $str Query string to parse - * @param bool|string $urlEncoding How the query string is encoded + * @param string $str Query string to parse + * @param int|bool $urlEncoding How the query string is encoded * * @return array */ @@ -533,9 +537,9 @@ function parse_query($str, $urlEncoding = true) $decoder = function ($value) { return rawurldecode(str_replace('+', ' ', $value)); }; - } elseif ($urlEncoding == PHP_QUERY_RFC3986) { + } elseif ($urlEncoding === PHP_QUERY_RFC3986) { $decoder = 'rawurldecode'; - } elseif ($urlEncoding == PHP_QUERY_RFC1738) { + } elseif ($urlEncoding === PHP_QUERY_RFC1738) { $decoder = 'urldecode'; } else { $decoder = function ($str) { return $str; }; @@ -633,6 +637,7 @@ function mimetype_from_filename($filename) function mimetype_from_extension($extension) { static $mimetypes = [ + '3gp' => 'video/3gpp', '7z' => 'application/x-7z-compressed', 'aac' => 'audio/x-aac', 'ai' => 'application/postscript', @@ -680,6 +685,7 @@ function mimetype_from_extension($extension) 'mid' => 'audio/midi', 'midi' => 'audio/midi', 'mov' => 'video/quicktime', + 'mkv' => 'video/x-matroska', 'mp3' => 'audio/mpeg', 'mp4' => 'video/mp4', 'mp4a' => 'audio/mp4', @@ -758,29 +764,53 @@ function _parse_message($message) throw new \InvalidArgumentException('Invalid message'); } - // Iterate over each line in the message, accounting for line endings - $lines = preg_split('/(\\r?\\n)/', $message, -1, PREG_SPLIT_DELIM_CAPTURE); - $result = ['start-line' => array_shift($lines), 'headers' => [], 'body' => '']; - array_shift($lines); + $message = ltrim($message, "\r\n"); - for ($i = 0, $totalLines = count($lines); $i < $totalLines; $i += 2) { - $line = $lines[$i]; - // If two line breaks were encountered, then this is the end of body - if (empty($line)) { - if ($i < $totalLines - 1) { - $result['body'] = implode('', array_slice($lines, $i + 2)); - } - break; - } - if (strpos($line, ':')) { - $parts = explode(':', $line, 2); - $key = trim($parts[0]); - $value = isset($parts[1]) ? trim($parts[1]) : ''; - $result['headers'][$key][] = $value; + $messageParts = preg_split("/\r?\n\r?\n/", $message, 2); + + if ($messageParts === false || count($messageParts) !== 2) { + throw new \InvalidArgumentException('Invalid message: Missing header delimiter'); + } + + list($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'); + } + + list($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(Rfc7230::HEADER_FOLD_REGEX, ' ', $rawHeaders); + } + + /** @var array[] $headerLines */ + $count = preg_match_all(Rfc7230::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(Rfc7230::HEADER_FOLD_REGEX, $rawHeaders)) { + throw new \InvalidArgumentException('Invalid header syntax: Obsolete line folding'); } + + throw new \InvalidArgumentException('Invalid header syntax'); } - return $result; + $headers = []; + + foreach ($headerLines as $headerLine) { + $headers[$headerLine[1]][] = $headerLine[2]; + } + + return [ + 'start-line' => $startLine, + 'headers' => $headers, + 'body' => $body, + ]; } /** @@ -809,6 +839,46 @@ function _parse_request_uri($path, array $headers) return $scheme . '://' . $host . '/' . ltrim($path, '/'); } +/** + * Get a short summary of the message body + * + * Will return `null` if the response is not printable. + * + * @param MessageInterface $message The message to get the body summary + * @param int $truncateAt The maximum allowed size of the summary + * + * @return null|string + */ +function get_message_body_summary(MessageInterface $message, $truncateAt = 120) +{ + $body = $message->getBody(); + + if (!$body->isSeekable() || !$body->isReadable()) { + return null; + } + + $size = $body->getSize(); + + if ($size === 0) { + return null; + } + + $summary = $body->read($truncateAt); + $body->rewind(); + + if ($size > $truncateAt) { + $summary .= ' (truncated...)'; + } + + // Matches any printable character, including unicode characters: + // letters, marks, numbers, punctuation, spacing, and separators. + if (preg_match('/[^\pL\pM\pN\pP\pS\pZ\n\r\t]/', $summary)) { + return null; + } + + return $summary; +} + /** @internal */ function _caseless_remove($keys, array $data) { |