This commit is contained in:
cutemeli
2025-12-22 10:35:30 +00:00
parent 0bfc6c8425
commit 5ce7ca2c5d
38927 changed files with 0 additions and 4594700 deletions

View File

@@ -1,16 +0,0 @@
<?php
$finder = PhpCsFixer\Finder::create()
->exclude('vendor')
->in(__DIR__)
;
$config = (new PhpCsFixer\Config())
->setRiskyAllowed(true)
->setRules([
'@Symfony' => true,
])
->setFinder($finder)
;
return $config;

View File

@@ -1,54 +0,0 @@
# Change Log
## 2.1.1
* Fixed constructor to work nicely with version 1 style arguments (e.g. HttplugBundle)
* Fixed PHP 8 compatibility for stream timeouts
* Renamed `master` branch to `2.x` for semantic branch naming.
* Add Symfony 6 compatibility
## 2.1.0
* Add php 8 compatibility
## 2.0.2
* Fixed composer "provide" section to say that we provide `psr/http-client-implementation`
## 2.0.1
* Fix wrong call to trigger_error
## 2.0.0
* Remove response and stream factory, use direct implementation of nyholm/psr7
* PSR18 and HTTPlug 2 support
* Remove support for php 5.5, 5.6, 7.0 and 7.1
* SSL Method now defaults to `STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT`
## 1.4.0
* Support for Symfony 4
## 1.3.0
* Make sure `Stream::__toString` never throws exception
* Added more exception
* `BrokenPipeException`
* `ConnectionException`
* `InvalidRequestException`
* `SSLConnectionException`
## 1.2.0
* Dropped PHP 5.4 support
* Using stable version of `php-http/discovery`
## 1.1.0
* Added discovery as hard dependency
* Reading more bytes than expected in a stream now returns the remaining content instead of throwing an Error
## 1.0.0
Initial release

View File

@@ -1,19 +0,0 @@
Copyright (c) 2015-2016 PHP HTTP Team <team@php-http.org>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -1,58 +0,0 @@
# Socket Client for PHP HTTP
[![Latest Version](https://img.shields.io/github/release/php-http/socket-client.svg?style=flat-square)](https://github.com/php-http/socket-client/releases)
[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE)
[![Build Status](https://github.com/php-http/socket-client/actions/workflows/ci.yml/badge.svg?branch=2.x)](https://github.com/php-http/socket-client/actions/workflows/ci.yml)
[![Code Coverage](https://img.shields.io/scrutinizer/coverage/g/php-http/socket-client.svg?style=flat-square)](https://scrutinizer-ci.com/g/php-http/socket-client)
[![Quality Score](https://img.shields.io/scrutinizer/g/php-http/socket-client.svg?style=flat-square)](https://scrutinizer-ci.com/g/php-http/socket-client)
[![Total Downloads](https://img.shields.io/packagist/dt/php-http/socket-client.svg?style=flat-square)](https://packagist.org/packages/php-http/socket-client)
The socket client use the stream extension from PHP, which is integrated into the core.
## Features
* TCP Socket Domain (tcp://hostname:port)
* UNIX Socket Domain (unix:///path/to/socket.sock)
* TLS / SSL Encyrption
* Client Certificate (only for php > 5.6)
## Installation and Usage
[Read the documentation at http://docs.php-http.org/en/latest/clients/socket-client.html](http://docs.php-http.org/en/latest/clients/socket-client.html)
## Testing
First launch the http server:
```bash
$ ./vendor/bin/http_test_server > /dev/null 2>&1 &
```
Then generate ssh certificates:
```bash
$ composer gen-ssl
```
Note: If you are running this on macOS and get the following error: "Error opening CA Private Key privkey.pem", check [this](ssl-macOS.md) file.
Now run the test suite:
``` bash
$ composer test
```
## Contributing
Please see our [contributing guide](http://docs.php-http.org/en/latest/development/contributing.html).
## Security
If you discover any security related issues, please contact us at [security@php-http.org](mailto:security@php-http.org).
## License
The MIT License (MIT). Please see [License File](LICENSE) for more information.

View File

@@ -1,48 +0,0 @@
{
"name": "plesk/socket-client",
"description": "Socket client for PHP-HTTP (Plesk)",
"license": "MIT",
"authors": [
{
"name": "Joel Wurtz",
"email": "jwurtz@jolicode.com"
}
],
"require": {
"php": "^7.2 || ^8.0",
"nyholm/psr7": "^1.3",
"php-http/httplug": "^2.0",
"psr/http-client": "^1.0",
"symfony/options-resolver": "^2.6 || ^3.4 || ^4.4 || ^5.0 || ^6.0"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^2.2 || ^3.0",
"php-http/client-integration-tests": "^3.0",
"php-http/message": "^1.9",
"php-http/client-common": "^2.3",
"phpunit/phpunit": "^8.5.23 || ~9.5"
},
"provide": {
"php-http/client-implementation": "1.0",
"psr/http-client-implementation": "1.0"
},
"autoload": {
"psr-4": {
"Http\\Client\\Socket\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Http\\Client\\Socket\\Tests\\": "tests/"
}
},
"scripts": {
"cs-check": "vendor/bin/php-cs-fixer fix --dry-run",
"cs-fix": "vendor/bin/php-cs-fixer fix",
"test": "vendor/bin/phpunit",
"test-ci": "vendor/bin/phpunit --coverage-clover build/coverage.xml",
"gen-ssl": "tests/server/ssl/generate.sh"
},
"prefer-stable": true,
"minimum-stability": "dev"
}

View File

@@ -1,25 +0,0 @@
parameters:
level: max
paths:
- src
ignoreErrors:
# phpstan seems confused by passing a variable by reference to stream_select
-
message: '#^Negated boolean expression is always false.$#'
count: 1
path: src/RequestWriter.php
-
message: "#^Method Http\\\\Client\\\\Socket\\\\Client\\:\\:configure\\(\\) should return array\\{remote_socket\\: string\\|null, timeout\\: int, stream_context\\: resource, stream_context_options\\: array\\<string, mixed\\>, stream_context_param\\: array\\<string, mixed\\>, ssl\\: bool\\|null, write_buffer_size\\: int, ssl_method\\: int\\} but returns array\\.$#"
count: 1
path: src/Client.php
-
message: "#^Parameter \\#1 \\$options of function stream_context_create expects array\\|null, mixed given\\.$#"
count: 1
path: src/Client.php
-
message: "#^Parameter \\#2 \\$params of function stream_context_create expects array\\|null, mixed given\\.$#"
count: 1
path: src/Client.php

View File

@@ -1,11 +0,0 @@
{
"version": "1.0",
"name": "php-http/socket-client",
"bindings": {
"bbd41ece-94a0-4ddc-942d-a1491d74c5fc": {
"_class": "Puli\\Discovery\\Binding\\ClassBinding",
"class": "Http\\Client\\Socket\\Client",
"type": "Http\\Client\\HttpClient"
}
}
}

View File

@@ -1,198 +0,0 @@
<?php
namespace Http\Client\Socket;
use Http\Client\HttpClient;
use Http\Client\Socket\Exception\ConnectionException;
use Http\Client\Socket\Exception\InvalidRequestException;
use Http\Client\Socket\Exception\SSLConnectionException;
use Http\Client\Socket\Exception\TimeoutException;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseFactoryInterface;
use Psr\Http\Message\ResponseInterface;
use Symfony\Component\OptionsResolver\Options;
use Symfony\Component\OptionsResolver\OptionsResolver;
/**
* Socket Http Client.
*
* Use stream and socket capabilities of the core of PHP to send HTTP requests
*
* @author Joel Wurtz <joel.wurtz@gmail.com>
*/
class Client implements HttpClient
{
use RequestWriter;
use ResponseReader;
/**
* @var array{remote_socket: string|null, timeout: int, stream_context: resource, stream_context_options: array<string, mixed>, stream_context_param: array<string, mixed>, ssl: ?boolean, write_buffer_size: int, ssl_method: int}
*/
private $config;
/**
* Constructor.
*
* @param array{remote_socket?: string|null, timeout?: int, stream_context?: resource, stream_context_options?: array<string, mixed>, stream_context_param?: array<string, mixed>, ssl?: ?boolean, write_buffer_size?: int, ssl_method?: int}|ResponseFactoryInterface $config1
* @param array{remote_socket?: string|null, timeout?: int, stream_context?: resource, stream_context_options?: array<string, mixed>, stream_context_param?: array<string, mixed>, ssl?: ?boolean, write_buffer_size?: int, ssl_method?: int}|null $config2 Mistake when refactoring the constructor from version 1 to version 2 - used as $config if set and $configOrResponseFactory is a response factory instance
* @param array{remote_socket?: string|null, timeout?: int, stream_context?: resource, stream_context_options?: array<string, mixed>, stream_context_param?: array<string, mixed>, ssl?: ?boolean, write_buffer_size?: int, ssl_method?: int} $config intended for version 1 BC, used as $config if $config2 is not set and $configOrResponseFactory is a response factory instance
*
* string|null remote_socket Remote entrypoint (can be a tcp or unix domain address)
* int timeout Timeout before canceling request
* stream resource The initialized stream context, if not set the context is created from the options and param.
* array<string, mixed> stream_context_options Context options as defined in the PHP documentation
* array<string, mixed> stream_context_param Context params as defined in the PHP documentation
* boolean ssl Use ssl, default to scheme from request, false if not present
* int write_buffer_size Buffer when writing the request body, defaults to 8192
* int ssl_method Crypto method for ssl/tls, see PHP doc, defaults to STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT
*/
public function __construct($config1 = [], $config2 = null, array $config = [])
{
if (\is_array($config1)) {
$this->config = $this->configure($config1);
return;
}
@trigger_error('Passing a Psr\Http\Message\ResponseFactoryInterface to SocketClient is deprecated, and will be removed in 3.0, you should only pass config options.', E_USER_DEPRECATED);
$this->config = $this->configure($config2 ?: $config);
}
/**
* {@inheritdoc}
*/
public function sendRequest(RequestInterface $request): ResponseInterface
{
$remote = $this->config['remote_socket'];
$useSsl = $this->config['ssl'];
if (!$request->hasHeader('Connection')) {
$request = $request->withHeader('Connection', 'close');
}
if (null === $remote) {
$remote = $this->determineRemoteFromRequest($request);
}
if (null === $useSsl) {
$useSsl = ('https' === $request->getUri()->getScheme());
}
$socket = $this->createSocket($request, $remote, $useSsl);
try {
$this->writeRequest($socket, $request, $this->config['write_buffer_size']);
$response = $this->readResponse($request, $socket);
} catch (\Exception $e) {
$this->closeSocket($socket);
throw $e;
}
return $response;
}
/**
* Create the socket to write request and read response on it.
*
* @param RequestInterface $request Request for
* @param string $remote Entrypoint for the connection
* @param bool $useSsl Whether to use ssl or not
*
* @throws ConnectionException|SSLConnectionException When the connection fail
*
* @return resource Socket resource
*/
protected function createSocket(RequestInterface $request, string $remote, bool $useSsl)
{
$errNo = null;
$errMsg = null;
$socket = @stream_socket_client($remote, $errNo, $errMsg, floor($this->config['timeout'] / 1000), STREAM_CLIENT_CONNECT, $this->config['stream_context']);
if (false === $socket) {
if (110 === $errNo) {
throw new TimeoutException($errMsg, $request);
}
throw new ConnectionException($errMsg, $request);
}
stream_set_timeout($socket, (int) floor($this->config['timeout'] / 1000), $this->config['timeout'] % 1000);
if ($useSsl && false === @stream_socket_enable_crypto($socket, true, $this->config['ssl_method'])) {
throw new SSLConnectionException(sprintf('Cannot enable tls: %s', error_get_last()['message'] ?? 'no error reported'), $request);
}
return $socket;
}
/**
* Close the socket, used when having an error.
*
* @param resource $socket
*
* @return void
*/
protected function closeSocket($socket)
{
fclose($socket);
}
/**
* Return configuration for the socket client.
*
* @param array{remote_socket?: string|null, timeout?: int, stream_context?: resource, stream_context_options?: array<string, mixed>, stream_context_param?: array<string, mixed>, ssl?: ?boolean, write_buffer_size?: int, ssl_method?: int} $config
*
* @return array{remote_socket: string|null, timeout: int, stream_context: resource, stream_context_options: array<string, mixed>, stream_context_param: array<string, mixed>, ssl: ?boolean, write_buffer_size: int, ssl_method: int}
*/
protected function configure(array $config = [])
{
$resolver = new OptionsResolver();
$resolver->setDefaults([
'remote_socket' => null,
'timeout' => null,
'stream_context_options' => [],
'stream_context_param' => [],
'ssl' => null,
'write_buffer_size' => 8192,
'ssl_method' => STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT,
]);
$resolver->setDefault('stream_context', function (Options $options) {
return stream_context_create($options['stream_context_options'], $options['stream_context_param']);
});
$resolver->setDefault('timeout', ((int) ini_get('default_socket_timeout')) * 1000);
$resolver->setAllowedTypes('stream_context_options', 'array');
$resolver->setAllowedTypes('stream_context_param', 'array');
$resolver->setAllowedTypes('stream_context', 'resource');
$resolver->setAllowedTypes('ssl', ['bool', 'null']);
return $resolver->resolve($config);
}
/**
* Return remote socket from the request.
*
* @throws InvalidRequestException When no remote can be determined from the request
*
* @return string
*/
private function determineRemoteFromRequest(RequestInterface $request)
{
if (!$request->hasHeader('Host') && '' === $request->getUri()->getHost()) {
throw new InvalidRequestException('Remote is not defined and we cannot determine a connection endpoint for this request (no Host header)', $request);
}
$host = $request->getUri()->getHost();
$port = $request->getUri()->getPort() ?: ('https' === $request->getUri()->getScheme() ? 443 : 80);
$endpoint = sprintf('%s:%s', $host, $port);
// If use the host header if present for the endpoint
if (empty($host) && $request->hasHeader('Host')) {
$endpoint = $request->getHeaderLine('Host');
}
return sprintf('tcp://%s', $endpoint);
}
}

View File

@@ -1,7 +0,0 @@
<?php
namespace Http\Client\Socket\Exception;
class BrokenPipeException extends NetworkException
{
}

View File

@@ -1,7 +0,0 @@
<?php
namespace Http\Client\Socket\Exception;
class ConnectionException extends NetworkException
{
}

View File

@@ -1,7 +0,0 @@
<?php
namespace Http\Client\Socket\Exception;
class InvalidRequestException extends NetworkException
{
}

View File

@@ -1,26 +0,0 @@
<?php
namespace Http\Client\Socket\Exception;
use Psr\Http\Client\NetworkExceptionInterface;
use Psr\Http\Message\RequestInterface;
class NetworkException extends \RuntimeException implements NetworkExceptionInterface
{
/**
* @var RequestInterface
*/
private $request;
public function __construct(string $message, RequestInterface $request, ?\Exception $previous = null)
{
$this->request = $request;
parent::__construct($message, 0, $previous);
}
public function getRequest(): RequestInterface
{
return $this->request;
}
}

View File

@@ -1,7 +0,0 @@
<?php
namespace Http\Client\Socket\Exception;
class SSLConnectionException extends NetworkException
{
}

View File

@@ -1,9 +0,0 @@
<?php
namespace Http\Client\Socket\Exception;
use Psr\Http\Client\ClientExceptionInterface;
class StreamException extends \RuntimeException implements ClientExceptionInterface
{
}

View File

@@ -1,7 +0,0 @@
<?php
namespace Http\Client\Socket\Exception;
class TimeoutException extends NetworkException
{
}

View File

@@ -1,131 +0,0 @@
<?php
namespace Http\Client\Socket;
use Http\Client\Socket\Exception\BrokenPipeException;
use Psr\Http\Message\RequestInterface;
/**
* Method for writing request.
*
* Mainly used by SocketHttpClient
*
* @author Joel Wurtz <joel.wurtz@gmail.com>
*/
trait RequestWriter
{
/**
* Write a request to a socket.
*
* @param resource $socket
*
* @return void
*
* @throws BrokenPipeException
*/
protected function writeRequest($socket, RequestInterface $request, int $bufferSize = 8192)
{
if (false === $this->fwrite($socket, $this->transformRequestHeadersToString($request))) {
throw new BrokenPipeException('Failed to send request, underlying socket not accessible, (BROKEN EPIPE)', $request);
}
if ($request->getBody()->isReadable()) {
$this->writeBody($socket, $request, $bufferSize);
}
}
/**
* Write Body of the request.
*
* @param resource $socket
*
* @return void
*
* @throws BrokenPipeException
*/
protected function writeBody($socket, RequestInterface $request, int $bufferSize = 8192)
{
$body = $request->getBody();
if ($body->isSeekable()) {
$body->rewind();
}
while (!$body->eof()) {
$buffer = $body->read($bufferSize);
if (false === $this->fwrite($socket, $buffer)) {
throw new BrokenPipeException('An error occur when writing request to client (BROKEN EPIPE)', $request);
}
}
}
/**
* Produce the header of request as a string based on a PSR Request.
*/
protected function transformRequestHeadersToString(RequestInterface $request): string
{
$message = vsprintf('%s %s HTTP/%s', [
strtoupper($request->getMethod()),
$request->getRequestTarget(),
$request->getProtocolVersion(),
])."\r\n";
foreach ($request->getHeaders() as $name => $values) {
$message .= $name.': '.implode(', ', $values)."\r\n";
}
$message .= "\r\n";
return $message;
}
/**
* Replace fwrite behavior as api is broken in PHP.
*
* @see https://secure.phabricator.com/rPHU69490c53c9c2ef2002bc2dd4cecfe9a4b080b497
*
* @param resource $stream The stream resource
*
* @return bool|int false if pipe is broken, number of bytes written otherwise
*/
private function fwrite($stream, string $bytes)
{
if (!strlen($bytes)) {
return 0;
}
$result = @fwrite($stream, $bytes);
if (0 !== $result) {
// In cases where some bytes are witten (`$result > 0`) or
// an error occurs (`$result === false`), the behavior of fwrite() is
// correct. We can return the value as-is.
return $result;
}
// If we make it here, we performed a 0-length write. Try to distinguish
// between EAGAIN and EPIPE. To do this, we're going to `stream_select()`
// the stream, write to it again if PHP claims that it's writable, and
// consider the pipe broken if the write fails.
$read = [];
$write = [$stream];
$except = [];
@stream_select($read, $write, $except, 0);
if (!$write) {
// The stream isn't writable, so we conclude that it probably really is
// blocked and the underlying error was EAGAIN. Return 0 to indicate that
// no data could be written yet.
return 0;
}
// If we make it here, PHP **just** claimed that this stream is writable, so
// perform a write. If the write also fails, conclude that these failures are
// EPIPE or some other permanent failure.
$result = @fwrite($stream, $bytes);
if (0 !== $result) {
// The write worked or failed explicitly. This value is fine to return.
return $result;
}
// We performed a 0-length write, were told that the stream was writable, and
// then immediately performed another 0-length write. Conclude that the pipe
// is broken and return `false`.
return false;
}
}

View File

@@ -1,104 +0,0 @@
<?php
namespace Http\Client\Socket;
use Http\Client\Socket\Exception\BrokenPipeException;
use Http\Client\Socket\Exception\TimeoutException;
use Http\Message\ResponseFactory;
use Nyholm\Psr7\Response;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
/**
* Method for reading response.
*
* Mainly used by SocketHttpClient
*
* @author Joel Wurtz <joel.wurtz@gmail.com>
*/
trait ResponseReader
{
/**
* @var ResponseFactory For creating response
*/
protected $responseFactory;
/**
* Read a response from a socket.
*
* @param resource $socket
*
* @throws TimeoutException When the socket timed out
* @throws BrokenPipeException When the response cannot be read
*/
protected function readResponse(RequestInterface $request, $socket): ResponseInterface
{
$headers = [];
$reason = null;
while (false !== ($line = fgets($socket))) {
if ('' === rtrim($line)) {
break;
}
$headers[] = trim($line);
}
$metadatas = stream_get_meta_data($socket);
if (array_key_exists('timed_out', $metadatas) && true === $metadatas['timed_out']) {
throw new TimeoutException('Error while reading response, stream timed out', $request, null);
}
$header = array_shift($headers);
$parts = null !== $header ? explode(' ', $header, 3) : [];
if (count($parts) <= 1) {
throw new BrokenPipeException('Cannot read the response', $request);
}
$protocol = substr($parts[0], -3);
$status = $parts[1];
if (isset($parts[2])) {
$reason = $parts[2];
}
// Set the size on the stream if it was returned in the response
$responseHeaders = [];
foreach ($headers as $header) {
$headerParts = explode(':', $header, 2);
if (!array_key_exists(trim($headerParts[0]), $responseHeaders)) {
$responseHeaders[trim($headerParts[0])] = [];
}
$responseHeaders[trim($headerParts[0])][] = isset($headerParts[1])
? trim($headerParts[1])
: '';
}
$response = new Response((int) $status, $responseHeaders, null, $protocol, $reason);
$stream = $this->createStream($socket, $request, $response);
return $response->withBody($stream);
}
/**
* Create the stream.
*
* @param resource $socket
*/
protected function createStream($socket, RequestInterface $request, ResponseInterface $response): Stream
{
$size = null;
if ($response->hasHeader('Content-Length')) {
$size = (int) $response->getHeaderLine('Content-Length');
}
if ($size < 0) {
$size = null;
}
return new Stream($request, $socket, $size);
}
}

View File

@@ -1,281 +0,0 @@
<?php
namespace Http\Client\Socket;
use Http\Client\Socket\Exception\StreamException;
use Http\Client\Socket\Exception\TimeoutException;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\StreamInterface;
/**
* Stream implementation for Socket Client.
*
* This implementation is used to have a Stream which react better to the Socket Client behavior.
*
* The main advantage is you can get the response of a request even if it's not finish, the response is available
* as soon as all headers are received, this stream will have the remaining socket used for the request / response
* call.
*
* It is only readable once, if you want to read the content multiple times, you can store contents of this
* stream into a variable or encapsulate it in a buffered stream.
*
* Writing and seeking is disable to avoid weird behaviors.
*
* @author Joel Wurtz <joel.wurtz@gmail.com>
*/
class Stream implements StreamInterface
{
/** @var resource|null Underlying socket */
private $socket;
/**
* @var bool Is stream detached
*/
private $isDetached = false;
/**
* @var int<0, max>|null Size of the stream, so we know what we must read, null if not available (i.e. a chunked stream)
*/
private $size;
/**
* @var int<0, max> Size of the stream readed, to avoid reading more than available and have the user blocked
*/
private $readed = 0;
/**
* @var RequestInterface request associated to this stream
*/
private $request;
/**
* Create the stream.
*
* @param resource $socket
* @param int<0, max>|null $size
*/
public function __construct(RequestInterface $request, $socket, ?int $size = null)
{
$this->socket = $socket;
$this->size = $size;
$this->request = $request;
}
/**
* {@inheritdoc}
*/
public function __toString(): string
{
try {
return $this->getContents();
} catch (\Exception $e) {
return '';
}
}
/**
* {@inheritdoc}
*/
public function close(): void
{
if ($this->isDetached || null === $this->socket) {
throw new StreamException('Stream is detached');
}
fclose($this->socket);
}
/**
* {@inheritdoc}
*/
public function detach()
{
if ($this->isDetached) {
return null;
}
$this->isDetached = true;
$socket = $this->socket;
$this->socket = null;
return $socket;
}
/**
* {@inheritdoc}
*
* @return int<0, max>|null
*/
public function getSize(): ?int
{
return $this->size;
}
/**
* {@inheritdoc}
*/
public function tell(): int
{
if ($this->isDetached || null === $this->socket) {
throw new StreamException('Stream is detached');
}
$tell = ftell($this->socket);
if (false === $tell) {
throw new StreamException('ftell returned false');
}
return $tell;
}
/**
* {@inheritdoc}
*/
public function eof(): bool
{
if ($this->isDetached || null === $this->socket) {
throw new StreamException('Stream is detached');
}
return feof($this->socket);
}
/**
* {@inheritdoc}
*/
public function isSeekable(): bool
{
return false;
}
/**
* {@inheritdoc}
*
* @return void
*/
public function seek($offset, $whence = SEEK_SET): void
{
throw new StreamException('This stream is not seekable');
}
/**
* {@inheritdoc}
*
* @return void
*/
public function rewind(): void
{
throw new StreamException('This stream is not seekable');
}
/**
* {@inheritdoc}
*/
public function isWritable(): bool
{
return false;
}
/**
* {@inheritdoc}
*/
public function write($string): int
{
throw new StreamException('This stream is not writable');
}
/**
* {@inheritdoc}
*/
public function isReadable(): bool
{
return true;
}
/**
* {@inheritdoc}
*
* @param int<0, max> $length
*/
public function read($length): string
{
if ($this->isDetached || null === $this->socket) {
throw new StreamException('Stream is detached');
}
if (null === $this->getSize()) {
$read = fread($this->socket, $length);
if (false === $read) {
throw new StreamException('Failed to read from stream');
}
return $read;
}
if ($this->getSize() === $this->readed) {
return '';
}
// Even if we request a length a non blocking stream can return less data than asked
$read = fread($this->socket, $length);
if (false === $read) {
// PHP 8
if ($this->getMetadata('timed_out')) {
throw new TimeoutException('Stream timed out while reading data', $this->request);
}
throw new StreamException('Failed to read from stream');
}
// PHP 7: fread does not return false when timing out
if ($this->getMetadata('timed_out')) {
throw new TimeoutException('Stream timed out while reading data', $this->request);
}
$this->readed += strlen($read);
return $read;
}
/**
* {@inheritdoc}
*/
public function getContents(): string
{
if ($this->isDetached || null === $this->socket) {
throw new StreamException('Stream is detached');
}
if (null === $this->getSize()) {
$contents = stream_get_contents($this->socket);
if (false === $contents) {
throw new StreamException('failed to get contents of stream');
}
return $contents;
}
$contents = '';
$toread = $this->getSize() - $this->readed;
while ($toread > 0) {
$contents .= $this->read($toread);
$toread = $this->getSize() - $this->readed;
}
return $contents;
}
/**
* {@inheritdoc}
*/
public function getMetadata($key = null)
{
if ($this->isDetached || null === $this->socket) {
throw new StreamException('Stream is detached');
}
$meta = stream_get_meta_data($this->socket);
if (null === $key) {
return $meta;
}
return $meta[$key];
}
}

View File

@@ -1,58 +0,0 @@
# Generating SSL Certificates on macOS
When generating SSL Certificates on macOS, you must ensure that you're using brew's openssl binary and not the one provided by the OS.
To do that, find out where your openssl is installed by running:
```bash
$ brew info openssl
```
You should see something like this:
```
openssl@1.1: stable 1.1.1i (bottled) [keg-only]
Cryptography and SSL/TLS Toolkit
https://openssl.org/
/usr/local/Cellar/openssl@1.1/1.1.1i (8,067 files, 18.5MB)
Poured from bottle on 2020-12-11 at 11:31:46
From: https://github.com/Homebrew/homebrew-core/blob/HEAD/Formula/openssl@1.1.rb
License: OpenSSL
==> Caveats
A CA file has been bootstrapped using certificates from the system
keychain. To add additional certificates, place .pem files in
/usr/local/etc/openssl@1.1/certs
and run
/usr/local/opt/openssl@1.1/bin/c_rehash
openssl@1.1 is keg-only, which means it was not symlinked into /usr/local,
because macOS provides LibreSSL.
If you need to have openssl@1.1 first in your PATH run:
echo 'export PATH="/usr/local/opt/openssl@1.1/bin:$PATH"' >> /Users/flavio/.bash_profile
For compilers to find openssl@1.1 you may need to set:
export LDFLAGS="-L/usr/local/opt/openssl@1.1/lib"
export CPPFLAGS="-I/usr/local/opt/openssl@1.1/include"
For pkg-config to find openssl@1.1 you may need to set:
export PKG_CONFIG_PATH="/usr/local/opt/openssl@1.1/lib/pkgconfig"
==> Analytics
install: 855,315 (30 days), 2,356,331 (90 days), 7,826,269 (365 days)
install-on-request: 139,236 (30 days), 373,801 (90 days), 1,120,685 (365 days)
build-error: 0 (30 days)
```
The important part is this:
> echo 'export PATH="/usr/local/opt/openssl@1.1/bin:$PATH"' >> /Users/flavio/.bash_profile
Instead of running `./tests/server/ssl/generate.sh`, you should instead run:
```bash
$ PATH="/usr/local/opt/openssl@1.1/bin ./tests/server/ssl/generate.sh
```
You should now be good to go.