From 36c7f09f0ef97c82e08d5417056bcab791781872 Mon Sep 17 00:00:00 2001 From: Joel Wurtz Date: Sun, 31 Jul 2016 15:48:42 +0200 Subject: [PATCH 01/58] Create specific exception for each error --- src/Client.php | 21 +++++++++++---------- src/Exception/BrokenPipeException.php | 9 +++++++++ src/Exception/ConnectionException.php | 9 +++++++++ src/Exception/InvalidRequestException.php | 9 +++++++++ src/Exception/SSLConnectionException.php | 9 +++++++++ src/RequestWriter.php | 10 +++++----- src/ResponseReader.php | 9 ++++++--- 7 files changed, 58 insertions(+), 18 deletions(-) create mode 100644 src/Exception/BrokenPipeException.php create mode 100644 src/Exception/ConnectionException.php create mode 100644 src/Exception/InvalidRequestException.php create mode 100644 src/Exception/SSLConnectionException.php diff --git a/src/Client.php b/src/Client.php index 4ab7bc5..1fff2eb 100644 --- a/src/Client.php +++ b/src/Client.php @@ -4,6 +4,9 @@ use Http\Client\Exception\NetworkException; use Http\Client\HttpClient; +use Http\Client\Socket\Exception\ConnectionException; +use Http\Client\Socket\Exception\InvalidRequestException; +use Http\Client\Socket\Exception\SSLConnectionException; use Http\Discovery\MessageFactoryDiscovery; use Http\Message\ResponseFactory; use Psr\Http\Message\RequestInterface; @@ -98,7 +101,7 @@ public function sendRequest(RequestInterface $request) * @param string $remote Entrypoint for the connection * @param bool $useSsl Whether to use ssl or not * - * @throws NetworkException When the connection fail + * @throws ConnectionException|SSLConnectionException When the connection fail * * @return resource Socket resource */ @@ -109,15 +112,13 @@ protected function createSocket(RequestInterface $request, $remote, $useSsl) $socket = @stream_socket_client($remote, $errNo, $errMsg, floor($this->config['timeout'] / 1000), STREAM_CLIENT_CONNECT, $this->config['stream_context']); if (false === $socket) { - throw new NetworkException($errMsg, $request); + throw new ConnectionException($errMsg, $request); } stream_set_timeout($socket, floor($this->config['timeout'] / 1000), $this->config['timeout'] % 1000); - if ($useSsl) { - if (false === @stream_socket_enable_crypto($socket, true, $this->config['ssl_method'])) { - throw new NetworkException(sprintf('Cannot enable tls: %s', error_get_last()['message']), $request); - } + 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']), $request); } return $socket; @@ -163,18 +164,18 @@ protected function configure(array $config = []) * * @param RequestInterface $request * - * @throws NetworkException When no remote can be determined from the request + * @throws InvalidRequestException When no remote can be determined from the request * * @return string */ private function determineRemoteFromRequest(RequestInterface $request) { - if ($request->getUri()->getHost() == '' && !$request->hasHeader('Host')) { - throw new NetworkException('Cannot find connection endpoint for this request', $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() ?: ($request->getUri()->getScheme() == 'https' ? 443 : 80); + $port = $request->getUri()->getPort() ?: ($request->getUri()->getScheme() === 'https' ? 443 : 80); $endpoint = sprintf('%s:%s', $host, $port); // If use the host header if present for the endpoint diff --git a/src/Exception/BrokenPipeException.php b/src/Exception/BrokenPipeException.php new file mode 100644 index 0000000..58eb1bb --- /dev/null +++ b/src/Exception/BrokenPipeException.php @@ -0,0 +1,9 @@ +fwrite($socket, $this->transformRequestHeadersToString($request))) { - throw new NetworkException('Failed to send request, underlying socket not accessible, (BROKEN EPIPE)', $request); + throw new BrokenPipeException('Failed to send request, underlying socket not accessible, (BROKEN EPIPE)', $request); } if ($request->getBody()->isReadable()) { @@ -41,8 +42,7 @@ protected function writeRequest($socket, RequestInterface $request, $bufferSize * @param RequestInterface $request * @param int $bufferSize * - * @throws \Http\Client\Exception\NetworkException - * @throws \Http\Client\Exception\RequestException + * @throws BrokenPipeException */ protected function writeBody($socket, RequestInterface $request, $bufferSize = 8192) { @@ -56,7 +56,7 @@ protected function writeBody($socket, RequestInterface $request, $bufferSize = 8 $buffer = $body->read($bufferSize); if (false === $this->fwrite($socket, $buffer)) { - throw new NetworkException('An error occur when writing request to client (BROKEN EPIPE)', $request); + throw new BrokenPipeException('An error occur when writing request to client (BROKEN EPIPE)', $request); } } } diff --git a/src/ResponseReader.php b/src/ResponseReader.php index 202d1bd..67a7878 100644 --- a/src/ResponseReader.php +++ b/src/ResponseReader.php @@ -3,6 +3,8 @@ namespace Http\Client\Socket; use Http\Client\Exception\NetworkException; +use Http\Client\Socket\Exception\BrokenPipeException; +use Http\Client\Socket\Exception\TimeoutException; use Http\Message\ResponseFactory; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; @@ -27,7 +29,8 @@ trait ResponseReader * @param RequestInterface $request * @param resource $socket * - * @throws NetworkException When the response cannot be read + * @throws TimeoutException When the socket timed out + * @throws BrokenPipeException When the response cannot be read * * @return ResponseInterface */ @@ -46,13 +49,13 @@ protected function readResponse(RequestInterface $request, $socket) $metadatas = stream_get_meta_data($socket); if (array_key_exists('timed_out', $metadatas) && true === $metadatas['timed_out']) { - throw new NetworkException('Error while reading response, stream timed out', $request); + throw new TimeoutException('Error while reading response, stream timed out', $request); } $parts = explode(' ', array_shift($headers), 3); if (count($parts) <= 1) { - throw new NetworkException('Cannot read the response', $request); + throw new BrokenPipeException('Cannot read the response', $request); } $protocol = substr($parts[0], -3); From d94d556c626a84eef6a320f0052fa0be480809dc Mon Sep 17 00:00:00 2001 From: Joel Wurtz Date: Sun, 31 Jul 2016 16:11:33 +0200 Subject: [PATCH 02/58] Fix cs --- src/Client.php | 1 - src/RequestWriter.php | 1 - src/ResponseReader.php | 1 - 3 files changed, 3 deletions(-) diff --git a/src/Client.php b/src/Client.php index 1fff2eb..6422a11 100644 --- a/src/Client.php +++ b/src/Client.php @@ -2,7 +2,6 @@ namespace Http\Client\Socket; -use Http\Client\Exception\NetworkException; use Http\Client\HttpClient; use Http\Client\Socket\Exception\ConnectionException; use Http\Client\Socket\Exception\InvalidRequestException; diff --git a/src/RequestWriter.php b/src/RequestWriter.php index 5e4566c..7a8751b 100644 --- a/src/RequestWriter.php +++ b/src/RequestWriter.php @@ -2,7 +2,6 @@ namespace Http\Client\Socket; -use Http\Client\Exception\NetworkException; use Http\Client\Socket\Exception\BrokenPipeException; use Psr\Http\Message\RequestInterface; diff --git a/src/ResponseReader.php b/src/ResponseReader.php index 67a7878..6826078 100644 --- a/src/ResponseReader.php +++ b/src/ResponseReader.php @@ -2,7 +2,6 @@ namespace Http\Client\Socket; -use Http\Client\Exception\NetworkException; use Http\Client\Socket\Exception\BrokenPipeException; use Http\Client\Socket\Exception\TimeoutException; use Http\Message\ResponseFactory; From 7459b39de1f7ff89c4bab93b64846197a3983da5 Mon Sep 17 00:00:00 2001 From: Tobias Nyholm Date: Fri, 7 Jul 2017 21:34:38 +0200 Subject: [PATCH 03/58] Fixed broken master (#27) * Generate password on the fly * Sleep for longer --- .travis.yml | 6 +++++- README.md | 10 +++++++++- composer.json | 3 +-- tests/BaseTestCase.php | 6 ++++-- tests/StreamTest.php | 3 ++- tests/server/ssl/ca.pem | 19 ------------------- tests/server/ssl/client-and-key.pem | 29 ----------------------------- tests/server/ssl/client.key | 15 --------------- tests/server/ssl/client.pem | 14 -------------- tests/server/ssl/client.req | 11 ----------- tests/server/ssl/file.srl | 2 +- tests/server/ssl/generate.sh | 13 ++++++------- tests/server/ssl/privkey.pem | 18 ------------------ tests/server/ssl/server-and-key.pem | 29 ----------------------------- tests/server/ssl/server.key | 15 --------------- tests/server/ssl/server.pem | 14 -------------- tests/server/ssl/server.req | 11 ----------- 17 files changed, 28 insertions(+), 190 deletions(-) delete mode 100644 tests/server/ssl/ca.pem delete mode 100644 tests/server/ssl/client-and-key.pem delete mode 100644 tests/server/ssl/client.key delete mode 100644 tests/server/ssl/client.pem delete mode 100644 tests/server/ssl/client.req delete mode 100644 tests/server/ssl/privkey.pem delete mode 100644 tests/server/ssl/server-and-key.pem delete mode 100644 tests/server/ssl/server.key delete mode 100644 tests/server/ssl/server.pem delete mode 100644 tests/server/ssl/server.req diff --git a/.travis.yml b/.travis.yml index a820d0c..157e9dd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,7 +10,7 @@ php: - 5.5 - 5.6 - 7.0 - - hhvm + - 7.1 env: global: @@ -23,10 +23,13 @@ branches: matrix: allow_failures: - php: hhvm + dist: trusty fast_finish: true include: - php: 5.5 env: COMPOSER_FLAGS="--prefer-stable --prefer-lowest" COVERAGE=true TEST_COMMAND="composer test-ci" + - php: hhvm + dist: trusty before_install: - travis_retry composer self-update @@ -38,6 +41,7 @@ before_script: - vendor/bin/http_test_server > /dev/null 2>&1 & script: + - cd ./tests/server/ssl && ./generate.sh && pwd && ls -la && cd ../../../ - $TEST_COMMAND - ./vendor/bin/phpunit tests/SocketClientFeatureTest.php --printer Http\\Client\\Tests\\FeatureTestListener || echo "" diff --git a/README.md b/README.md index 392f655..d5620f4 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,15 @@ First launch the http server: $ ./vendor/bin/http_test_server > /dev/null 2>&1 & ``` -Then the test suite: +Then generate ssh certificates: + +```bash +$ cd ./tests/server/ssl +$ ./generate.sh +$ cd ../../../ +``` + +Now run the test suite: ``` bash $ composer test diff --git a/composer.json b/composer.json index 3f4e9bb..a1e5f8c 100644 --- a/composer.json +++ b/composer.json @@ -16,9 +16,8 @@ "php-http/discovery": "^1.0" }, "require-dev": { - "phpunit/phpunit": "^4.8", "guzzlehttp/psr7": "^1.2", - "php-http/client-integration-tests": "^0.5.1", + "php-http/client-integration-tests": "^0.6", "php-http/message": "^1.0", "php-http/client-common": "^1.0" }, diff --git a/tests/BaseTestCase.php b/tests/BaseTestCase.php index d049251..50b4fe3 100644 --- a/tests/BaseTestCase.php +++ b/tests/BaseTestCase.php @@ -2,7 +2,9 @@ namespace Http\Client\Socket\Tests; -class BaseTestCase extends \PHPUnit_Framework_TestCase +use PHPUnit\Framework\TestCase; + +class BaseTestCase extends TestCase { private $servers = []; @@ -11,7 +13,7 @@ public function startServer($name) $filename = __DIR__ . '/server/' . $name . '.php'; $pipes = []; $this->servers[$name] = proc_open('php '. $filename, [], $pipes); - usleep(30000); + sleep(1); } public function stopServer($name) diff --git a/tests/StreamTest.php b/tests/StreamTest.php index 9d5ea10..910c6e2 100644 --- a/tests/StreamTest.php +++ b/tests/StreamTest.php @@ -3,8 +3,9 @@ namespace Http\Client\Socket\Tests; use Http\Client\Socket\Stream; +use PHPUnit\Framework\TestCase; -class StreamTest extends \PHPUnit_Framework_TestCase +class StreamTest extends TestCase { public function createSocket($body, $useSize = true) { diff --git a/tests/server/ssl/ca.pem b/tests/server/ssl/ca.pem deleted file mode 100644 index aa477d0..0000000 --- a/tests/server/ssl/ca.pem +++ /dev/null @@ -1,19 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDBDCCAm2gAwIBAgIJAKf70F8G74WBMA0GCSqGSIb3DQEBBQUAMGAxCzAJBgNV -BAYTAkZSMRYwFAYDVQQIEw1JbGUtZGUtRnJhbmNlMQ4wDAYDVQQHEwVQYXJpczER -MA8GA1UEChMIUEhQLUhUVFAxFjAUBgNVBAMTDXNvY2tldC1zZXJ2ZXIwHhcNMTYw -NzE4MTAwMDIzWhcNMTYwODE3MTAwMDIzWjBgMQswCQYDVQQGEwJGUjEWMBQGA1UE -CBMNSWxlLWRlLUZyYW5jZTEOMAwGA1UEBxMFUGFyaXMxETAPBgNVBAoTCFBIUC1I -VFRQMRYwFAYDVQQDEw1zb2NrZXQtc2VydmVyMIGfMA0GCSqGSIb3DQEBAQUAA4GN -ADCBiQKBgQDZbuJ4bebB+9mER7bHl2kizLWP0gWwv5l0Kvr2m0ajZ7MZpxH19Hmo -I5XXIdnM3JSIajW8+FZ23z4s1FawRrn9EnJc/o0KP7NB0yREcO3XiuqAo5K8gkFA -wQPeaxl1JDpAVjKPAfP35bnMoIz55MW/UJXH+QBv1/95T3r9xvX+PQIDAQABo4HF -MIHCMB0GA1UdDgQWBBQpMG5EbNuv8sNLO/MbPbyp3DMLezCBkgYDVR0jBIGKMIGH -gBQpMG5EbNuv8sNLO/MbPbyp3DMLe6FkpGIwYDELMAkGA1UEBhMCRlIxFjAUBgNV -BAgTDUlsZS1kZS1GcmFuY2UxDjAMBgNVBAcTBVBhcmlzMREwDwYDVQQKEwhQSFAt -SFRUUDEWMBQGA1UEAxMNc29ja2V0LXNlcnZlcoIJAKf70F8G74WBMAwGA1UdEwQF -MAMBAf8wDQYJKoZIhvcNAQEFBQADgYEAFfJlrEhQ4o1sroNsq/gWBP9rg4jzU4Bm -HcWieWQyWg5sXNCJgCr9ntZ4w29POtlIpHXCoH3UnEDEq7BXGiVRd7vAbxKn6Yt/ -+oKTxvcmBNXkkOgEbH/3uD2sGkcfndYkJieRGcUr69wThhjSxfgUE1Gnynjl7SIF -UlZlufPto3I= ------END CERTIFICATE----- diff --git a/tests/server/ssl/client-and-key.pem b/tests/server/ssl/client-and-key.pem deleted file mode 100644 index fb27231..0000000 --- a/tests/server/ssl/client-and-key.pem +++ /dev/null @@ -1,29 +0,0 @@ ------BEGIN CERTIFICATE----- -MIICNzCCAaACASowDQYJKoZIhvcNAQEFBQAwYDELMAkGA1UEBhMCRlIxFjAUBgNV -BAgTDUlsZS1kZS1GcmFuY2UxDjAMBgNVBAcTBVBhcmlzMREwDwYDVQQKEwhQSFAt -SFRUUDEWMBQGA1UEAxMNc29ja2V0LXNlcnZlcjAeFw0xNjA3MTgxMDAwMzFaFw0x -NjA4MTcxMDAwMzFaMGgxCzAJBgNVBAYTAkZSMRYwFAYDVQQIEw1JbGUtZGUtRnJh -bmNlMQ4wDAYDVQQHEwVQYXJpczERMA8GA1UEChMIUEhQLUhUVFAxHjAcBgNVBAMT -FXNvY2tldC1hZGFwdGVyLWNsaWVudDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkC -gYEA4iXSjiZWYKmGfEluM0z+bY6r2AGaZgApvOehQMjom1mORllMVkfIanuenbQE -ay9UIihRCO9gYZpFTSDt8Mgobl8x1i9gGd1bFf5Vy/9fubBD0k8Sss3GHNfTe5az -mls7D8pqTD7KOdBe1000DmpNKbV4b5WZGI/dsliUqvEQJDcCAwEAATANBgkqhkiG -9w0BAQUFAAOBgQDRjvy010S62JD20ZrK60svmqI6Z9a3jJ6dVmWS1YM1u8DRrUKf -StsEbIuNMuLmdGQ9FDoLuURuzuqtYlsKyjnUq7oyjq8xeIKr2oAPxftwBBeEydQF -7GBcdokMOMWl9bGLI7YMbRiqyshw8y/rqVCAF+5/65jhPMJuPEPVa2pCJQ== ------END CERTIFICATE----- ------BEGIN RSA PRIVATE KEY----- -MIICXgIBAAKBgQDiJdKOJlZgqYZ8SW4zTP5tjqvYAZpmACm856FAyOibWY5GWUxW -R8hqe56dtARrL1QiKFEI72BhmkVNIO3wyChuXzHWL2AZ3VsV/lXL/1+5sEPSTxKy -zcYc19N7lrOaWzsPympMPso50F7XTTQOak0ptXhvlZkYj92yWJSq8RAkNwIDAQAB -AoGBAKR3eU+kFgGqiIHvLVPl1Mt8jQFE+TLhP3mAw0dIVdJVKSGEY/4Dqn+2c76h -Yu1IuFk3FOf4nxGcjnXvUtxf1PZB/t1xHMHuspYHkc9ElDQDfuYqgimAPzMHsUfm -St47YNNLA6pzPUtpaw7qRA7v54y8qi3GdHf4pLW1/PSuuoLhAkEA9T62mody3EDG -33jks1vCGoFtADlU3J2ShJiFG44HsWueJ933KRgP2NlAwtumO+Om4CaUUzIiOSzT -uibjOEyl7wJBAOwQtfERZfvpVLoqVF9NADvy4KpGbv+lXq9gTia9hSMumH2h5+Jh -nVXhN9m/TRQiQ74XB3tpC0I5H4fozCj17jkCQQDY/gNN2Vjc3wfYBTy+IgstrqTG -VPPsPBO+9MCZr+LIrgMDHVeqaZrj8N5nSzfbt8MYg6orrqkvU6WHCQ906gTLAkAc -6dMPCr1+BE8vWh3pakn8Dbx8vpG72Cpnupj338Cd1Ygx4xG+xYv6x9IKZobvT18S -ukldUTlizBJTmCzhW3XpAkEA5tYfC72Ia761g6ild8HaILMK30+wYCWIu3Ieb+G1 -uaJWO+VDKKtWVntkPfnckVlfPQMmuMDaIGh9G2vWRYdtDQ== ------END RSA PRIVATE KEY----- diff --git a/tests/server/ssl/client.key b/tests/server/ssl/client.key deleted file mode 100644 index 8f7d7c7..0000000 --- a/tests/server/ssl/client.key +++ /dev/null @@ -1,15 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIICXgIBAAKBgQDiJdKOJlZgqYZ8SW4zTP5tjqvYAZpmACm856FAyOibWY5GWUxW -R8hqe56dtARrL1QiKFEI72BhmkVNIO3wyChuXzHWL2AZ3VsV/lXL/1+5sEPSTxKy -zcYc19N7lrOaWzsPympMPso50F7XTTQOak0ptXhvlZkYj92yWJSq8RAkNwIDAQAB -AoGBAKR3eU+kFgGqiIHvLVPl1Mt8jQFE+TLhP3mAw0dIVdJVKSGEY/4Dqn+2c76h -Yu1IuFk3FOf4nxGcjnXvUtxf1PZB/t1xHMHuspYHkc9ElDQDfuYqgimAPzMHsUfm -St47YNNLA6pzPUtpaw7qRA7v54y8qi3GdHf4pLW1/PSuuoLhAkEA9T62mody3EDG -33jks1vCGoFtADlU3J2ShJiFG44HsWueJ933KRgP2NlAwtumO+Om4CaUUzIiOSzT -uibjOEyl7wJBAOwQtfERZfvpVLoqVF9NADvy4KpGbv+lXq9gTia9hSMumH2h5+Jh -nVXhN9m/TRQiQ74XB3tpC0I5H4fozCj17jkCQQDY/gNN2Vjc3wfYBTy+IgstrqTG -VPPsPBO+9MCZr+LIrgMDHVeqaZrj8N5nSzfbt8MYg6orrqkvU6WHCQ906gTLAkAc -6dMPCr1+BE8vWh3pakn8Dbx8vpG72Cpnupj338Cd1Ygx4xG+xYv6x9IKZobvT18S -ukldUTlizBJTmCzhW3XpAkEA5tYfC72Ia761g6ild8HaILMK30+wYCWIu3Ieb+G1 -uaJWO+VDKKtWVntkPfnckVlfPQMmuMDaIGh9G2vWRYdtDQ== ------END RSA PRIVATE KEY----- diff --git a/tests/server/ssl/client.pem b/tests/server/ssl/client.pem deleted file mode 100644 index 801a918..0000000 --- a/tests/server/ssl/client.pem +++ /dev/null @@ -1,14 +0,0 @@ ------BEGIN CERTIFICATE----- -MIICNzCCAaACASowDQYJKoZIhvcNAQEFBQAwYDELMAkGA1UEBhMCRlIxFjAUBgNV -BAgTDUlsZS1kZS1GcmFuY2UxDjAMBgNVBAcTBVBhcmlzMREwDwYDVQQKEwhQSFAt -SFRUUDEWMBQGA1UEAxMNc29ja2V0LXNlcnZlcjAeFw0xNjA3MTgxMDAwMzFaFw0x -NjA4MTcxMDAwMzFaMGgxCzAJBgNVBAYTAkZSMRYwFAYDVQQIEw1JbGUtZGUtRnJh -bmNlMQ4wDAYDVQQHEwVQYXJpczERMA8GA1UEChMIUEhQLUhUVFAxHjAcBgNVBAMT -FXNvY2tldC1hZGFwdGVyLWNsaWVudDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkC -gYEA4iXSjiZWYKmGfEluM0z+bY6r2AGaZgApvOehQMjom1mORllMVkfIanuenbQE -ay9UIihRCO9gYZpFTSDt8Mgobl8x1i9gGd1bFf5Vy/9fubBD0k8Sss3GHNfTe5az -mls7D8pqTD7KOdBe1000DmpNKbV4b5WZGI/dsliUqvEQJDcCAwEAATANBgkqhkiG -9w0BAQUFAAOBgQDRjvy010S62JD20ZrK60svmqI6Z9a3jJ6dVmWS1YM1u8DRrUKf -StsEbIuNMuLmdGQ9FDoLuURuzuqtYlsKyjnUq7oyjq8xeIKr2oAPxftwBBeEydQF -7GBcdokMOMWl9bGLI7YMbRiqyshw8y/rqVCAF+5/65jhPMJuPEPVa2pCJQ== ------END CERTIFICATE----- diff --git a/tests/server/ssl/client.req b/tests/server/ssl/client.req deleted file mode 100644 index 545173d..0000000 --- a/tests/server/ssl/client.req +++ /dev/null @@ -1,11 +0,0 @@ ------BEGIN CERTIFICATE REQUEST----- -MIIBqDCCARECAQAwaDELMAkGA1UEBhMCRlIxFjAUBgNVBAgTDUlsZS1kZS1GcmFu -Y2UxDjAMBgNVBAcTBVBhcmlzMREwDwYDVQQKEwhQSFAtSFRUUDEeMBwGA1UEAxMV -c29ja2V0LWFkYXB0ZXItY2xpZW50MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB -gQDiJdKOJlZgqYZ8SW4zTP5tjqvYAZpmACm856FAyOibWY5GWUxWR8hqe56dtARr -L1QiKFEI72BhmkVNIO3wyChuXzHWL2AZ3VsV/lXL/1+5sEPSTxKyzcYc19N7lrOa -WzsPympMPso50F7XTTQOak0ptXhvlZkYj92yWJSq8RAkNwIDAQABoAAwDQYJKoZI -hvcNAQEFBQADgYEA3G8c/4gB/eEme+1U99SKZlxHGzKhj28Qz9aiunfGRVnPDgnF -wFrnah8KjBwAv2Eh58SwTomenSYtpuuuT6W3YQ4YGn5BtoTb2l4qpW8cncqQR5Bq -g91l/fXtuZKU5hSOkI5vAKCsHY3Z+R8Raxxd0s6pzeca+qkkEmUmKedEnPM= ------END CERTIFICATE REQUEST----- diff --git a/tests/server/ssl/file.srl b/tests/server/ssl/file.srl index 8676c24..fe909bb 100644 --- a/tests/server/ssl/file.srl +++ b/tests/server/ssl/file.srl @@ -1 +1 @@ -2A +2D diff --git a/tests/server/ssl/generate.sh b/tests/server/ssl/generate.sh index b99f4b4..696b340 100755 --- a/tests/server/ssl/generate.sh +++ b/tests/server/ssl/generate.sh @@ -1,4 +1,4 @@ -#/bin/bash +#!/bin/bash C=FR ST=Ile-de-France @@ -6,15 +6,14 @@ L=Paris O="PHP-HTTP" CN="socket-adapter" -openssl req -out ca.pem -new -x509 -subj "/C=$C/ST=$ST/L=$L/O=$O/CN=socket-server" - +openssl req -out ca.pem -new -x509 -subj "/C=$C/ST=$ST/L=$L/O=$O/CN=socket-server" -passout pass:password openssl genrsa -out server.key 1024 -subj "/C=$C/ST=$ST/L=$L/O=$O/CN=socket-adapter" -openssl req -key server.key -new -out server.req -subj "/C=$C/ST=$ST/L=$L/O=$O/CN=socket-adapter" -openssl x509 -req -in server.req -CA ca.pem -CAkey privkey.pem -CAserial file.srl -out server.pem +openssl req -key server.key -new -out server.req -subj "/C=$C/ST=$ST/L=$L/O=$O/CN=socket-adapter" -passout pass:password +openssl x509 -req -in server.req -CA ca.pem -CAkey privkey.pem -CAserial file.srl -out server.pem -passin pass:password openssl genrsa -out client.key 1024 -subj "/C=$C/ST=$ST/L=$L/O=$O/CN=socket-adapter-client" -openssl req -key client.key -new -out client.req -subj "/C=$C/ST=$ST/L=$L/O=$O/CN=socket-adapter-client" -openssl x509 -req -in client.req -CA ca.pem -CAkey privkey.pem -CAserial file.srl -out client.pem +openssl req -key client.key -new -out client.req -subj "/C=$C/ST=$ST/L=$L/O=$O/CN=socket-adapter-client" -passout pass:password +openssl x509 -req -in client.req -CA ca.pem -CAkey privkey.pem -CAserial file.srl -out client.pem -passin pass:password cat client.pem client.key > client-and-key.pem cat server.pem server.key > server-and-key.pem diff --git a/tests/server/ssl/privkey.pem b/tests/server/ssl/privkey.pem deleted file mode 100644 index 31c9834..0000000 --- a/tests/server/ssl/privkey.pem +++ /dev/null @@ -1,18 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -Proc-Type: 4,ENCRYPTED -DEK-Info: DES-EDE3-CBC,7BB9811913388905 - -UHN46XyFU48x2IQAkYjrLDzcFLU6xZh1rPGYgIEy6Dacg6QqxO90Nlm2GD8phvY6 -UVj2E2S3GlviBegs/8AJtJ3Ly9LFGKoM5zFrQ24zPp1QAKqMhLBpbq/iFi6oyWv4 -cEB2d7fBZETtg1ZXhvjzS9muvc3uZfIALmoPxWlTYv0m4ErE4GBezKO+Y7Mexl6U -K9Mg0fAXNW8GQsUpqjs+9IA/COS7GKsHRKV7Bk0Ar/Kd/xjFBv0Vf4mdyYe/fAu1 -ary20XZsYNsO/RdfUtWMzsTKfnMgG1csQcO7xgYHGKHC6U+35yjxwmQxw7F3xP4W -8i5t2J3QfnX9PlnYOLTYucj74na+U6raE2S+ejO3YDF8nIpJaDvzpA4N35T4hDDL -Z/SpFtol8Uf33GuH/uiz7x2ss7vyPe1hk96nirT14Bsx1ZEczua/CCTs+iQtoB5F -breSVhlyMCq6rC47xd69IYVnyEe11EigHn/sQkdUae3kjqL4pgMGBdlONuV63gmX -yNSFIaE+gh0mFnOVdCtiFxS8XP5BkJ11IcSLIG4ZygycX/IOTRQFh/TTJ4HYDcPZ -j3zJvZm/OI8xiq5jhEqfkgJElQd/pFuLHESEa8+Q4wiOhYwQEBjKDIBboWXF8CDE -QFIVBf/o+w3YN700p0xKjGqhaPnKQwCEfY5Lh44pPf7yQX7LiE8tG9kqAfEu25Dc -kYl1Rw8SFP6OVLqCNMjRTKeyFubkoAFXz828dzZdGd6H8zPH51FqsxIqJepwH6sT -NWHvnX5LwC5mblF4+VSc7TTgaE16Nq/g1oEXNBuZS6oJq2OnwtH4yQ== ------END RSA PRIVATE KEY----- diff --git a/tests/server/ssl/server-and-key.pem b/tests/server/ssl/server-and-key.pem deleted file mode 100644 index bd112d2..0000000 --- a/tests/server/ssl/server-and-key.pem +++ /dev/null @@ -1,29 +0,0 @@ ------BEGIN CERTIFICATE----- -MIICMDCCAZkCASkwDQYJKoZIhvcNAQEFBQAwYDELMAkGA1UEBhMCRlIxFjAUBgNV -BAgTDUlsZS1kZS1GcmFuY2UxDjAMBgNVBAcTBVBhcmlzMREwDwYDVQQKEwhQSFAt -SFRUUDEWMBQGA1UEAxMNc29ja2V0LXNlcnZlcjAeFw0xNjA3MTgxMDAwMjhaFw0x -NjA4MTcxMDAwMjhaMGExCzAJBgNVBAYTAkZSMRYwFAYDVQQIEw1JbGUtZGUtRnJh -bmNlMQ4wDAYDVQQHEwVQYXJpczERMA8GA1UEChMIUEhQLUhUVFAxFzAVBgNVBAMT -DnNvY2tldC1hZGFwdGVyMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDZHYLV -1mpoiG2AW5VebIB0nCaT8EIryeUk57swa5nqW9fj/KD4hIaoz19Ivt7/LrVTCOag -jFiwEcm4kEC8h0DJvW2IKNDWIpObHBSFFFOlLxhJkq+US1CJjKgSDvU5X3ElXb99 -L1EHN/+M8ZA4Qw3fFAbO2ajENeoxHH9KKZCQLQIDAQABMA0GCSqGSIb3DQEBBQUA -A4GBAM1TFIPTwbG0x//yMhPD9ZGWb2mYfA00Lri198YVmG61PtgDI5hG2dxaDVDa -6Ig6DnrwTBMc51p+NiTgci3OqnJ4FuwBdY5d1moyQre0MHr+sg8STJr5tmAEithL -GUGq3Uv3RuN3OetaPrlxaccJM1nB63caoH5Rl+VqAsJ4S4LI ------END CERTIFICATE----- ------BEGIN RSA PRIVATE KEY----- -MIICXQIBAAKBgQDZHYLV1mpoiG2AW5VebIB0nCaT8EIryeUk57swa5nqW9fj/KD4 -hIaoz19Ivt7/LrVTCOagjFiwEcm4kEC8h0DJvW2IKNDWIpObHBSFFFOlLxhJkq+U -S1CJjKgSDvU5X3ElXb99L1EHN/+M8ZA4Qw3fFAbO2ajENeoxHH9KKZCQLQIDAQAB -AoGAHcaqVuFdHpD3U4874QNIarWvpMJikNbTkkglp/JZ3YZTCXVwLiUewrAOBVAI -DT8+pvClkIcu8pa97435EuIJxGB9C0Nudzpknm3xucc1pfYsq9fIa6F/+xTBIaTu -3FcA1vJ/fodhcPPwp6CAQJV7pLfQ5m+DLuwp5ICyy5v2yRECQQD0YgkbGLyKIM0K -qcNbDE3fNrRQ99ydQ1QXrB+4M8Lc/49XLvd5l9QwMN6BHvEpGf4qvBUYBFYroSVs -wogOWfmvAkEA42+lxrETE7mIJ/h+QDAQ6JX9fUuda96YU7Xs7OaRWAc77RazxP2p -XTV1U9l+FHpAvdauWgi7h+0zCEfbjEb24wJBALDJ4vB5OptqEyqtmKdbBVJ+zn7s -b+pMdJ8TY3br6wHY1qcpUNnjnhqmn7Ak5CzpotbjbRcMvBGf5+T/7VEEgf0CQGlE -/hgvUcMpdun5TIZtOQZOllvuKdkJ2Lu2Uwl71v5FqukcPu0I8+FeowXCf2b/BwRX -X6/wpsxNb/d/OxF8rz0CQQDPPxYM5+FSFzun831QSfxWwGx2LLFPQKYmixKfwpQB -3eIq3gPYN3A/qlVCZlGhyKLH2WiW/bY3HTQ4ZSCgTYzd ------END RSA PRIVATE KEY----- diff --git a/tests/server/ssl/server.key b/tests/server/ssl/server.key deleted file mode 100644 index 5a49a70..0000000 --- a/tests/server/ssl/server.key +++ /dev/null @@ -1,15 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIICXQIBAAKBgQDZHYLV1mpoiG2AW5VebIB0nCaT8EIryeUk57swa5nqW9fj/KD4 -hIaoz19Ivt7/LrVTCOagjFiwEcm4kEC8h0DJvW2IKNDWIpObHBSFFFOlLxhJkq+U -S1CJjKgSDvU5X3ElXb99L1EHN/+M8ZA4Qw3fFAbO2ajENeoxHH9KKZCQLQIDAQAB -AoGAHcaqVuFdHpD3U4874QNIarWvpMJikNbTkkglp/JZ3YZTCXVwLiUewrAOBVAI -DT8+pvClkIcu8pa97435EuIJxGB9C0Nudzpknm3xucc1pfYsq9fIa6F/+xTBIaTu -3FcA1vJ/fodhcPPwp6CAQJV7pLfQ5m+DLuwp5ICyy5v2yRECQQD0YgkbGLyKIM0K -qcNbDE3fNrRQ99ydQ1QXrB+4M8Lc/49XLvd5l9QwMN6BHvEpGf4qvBUYBFYroSVs -wogOWfmvAkEA42+lxrETE7mIJ/h+QDAQ6JX9fUuda96YU7Xs7OaRWAc77RazxP2p -XTV1U9l+FHpAvdauWgi7h+0zCEfbjEb24wJBALDJ4vB5OptqEyqtmKdbBVJ+zn7s -b+pMdJ8TY3br6wHY1qcpUNnjnhqmn7Ak5CzpotbjbRcMvBGf5+T/7VEEgf0CQGlE -/hgvUcMpdun5TIZtOQZOllvuKdkJ2Lu2Uwl71v5FqukcPu0I8+FeowXCf2b/BwRX -X6/wpsxNb/d/OxF8rz0CQQDPPxYM5+FSFzun831QSfxWwGx2LLFPQKYmixKfwpQB -3eIq3gPYN3A/qlVCZlGhyKLH2WiW/bY3HTQ4ZSCgTYzd ------END RSA PRIVATE KEY----- diff --git a/tests/server/ssl/server.pem b/tests/server/ssl/server.pem deleted file mode 100644 index 5c9542a..0000000 --- a/tests/server/ssl/server.pem +++ /dev/null @@ -1,14 +0,0 @@ ------BEGIN CERTIFICATE----- -MIICMDCCAZkCASkwDQYJKoZIhvcNAQEFBQAwYDELMAkGA1UEBhMCRlIxFjAUBgNV -BAgTDUlsZS1kZS1GcmFuY2UxDjAMBgNVBAcTBVBhcmlzMREwDwYDVQQKEwhQSFAt -SFRUUDEWMBQGA1UEAxMNc29ja2V0LXNlcnZlcjAeFw0xNjA3MTgxMDAwMjhaFw0x -NjA4MTcxMDAwMjhaMGExCzAJBgNVBAYTAkZSMRYwFAYDVQQIEw1JbGUtZGUtRnJh -bmNlMQ4wDAYDVQQHEwVQYXJpczERMA8GA1UEChMIUEhQLUhUVFAxFzAVBgNVBAMT -DnNvY2tldC1hZGFwdGVyMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDZHYLV -1mpoiG2AW5VebIB0nCaT8EIryeUk57swa5nqW9fj/KD4hIaoz19Ivt7/LrVTCOag -jFiwEcm4kEC8h0DJvW2IKNDWIpObHBSFFFOlLxhJkq+US1CJjKgSDvU5X3ElXb99 -L1EHN/+M8ZA4Qw3fFAbO2ajENeoxHH9KKZCQLQIDAQABMA0GCSqGSIb3DQEBBQUA -A4GBAM1TFIPTwbG0x//yMhPD9ZGWb2mYfA00Lri198YVmG61PtgDI5hG2dxaDVDa -6Ig6DnrwTBMc51p+NiTgci3OqnJ4FuwBdY5d1moyQre0MHr+sg8STJr5tmAEithL -GUGq3Uv3RuN3OetaPrlxaccJM1nB63caoH5Rl+VqAsJ4S4LI ------END CERTIFICATE----- diff --git a/tests/server/ssl/server.req b/tests/server/ssl/server.req deleted file mode 100644 index cc19ec7..0000000 --- a/tests/server/ssl/server.req +++ /dev/null @@ -1,11 +0,0 @@ ------BEGIN CERTIFICATE REQUEST----- -MIIBoTCCAQoCAQAwYTELMAkGA1UEBhMCRlIxFjAUBgNVBAgTDUlsZS1kZS1GcmFu -Y2UxDjAMBgNVBAcTBVBhcmlzMREwDwYDVQQKEwhQSFAtSFRUUDEXMBUGA1UEAxMO -c29ja2V0LWFkYXB0ZXIwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANkdgtXW -amiIbYBblV5sgHScJpPwQivJ5STnuzBrmepb1+P8oPiEhqjPX0i+3v8utVMI5qCM -WLARybiQQLyHQMm9bYgo0NYik5scFIUUU6UvGEmSr5RLUImMqBIO9TlfcSVdv30v -UQc3/4zxkDhDDd8UBs7ZqMQ16jEcf0opkJAtAgMBAAGgADANBgkqhkiG9w0BAQUF -AAOBgQCWD+DqC2HAiKCZh5j1QdKbzVF2rNV0Gfn2RM/cswMGHEGQyqeI1JH6R3ec -Reo7XG7Sx/qswpr8SBe7TrgGhgHm1U9757+Vw2U64Mvm0m6MvW5+PIwvr1k+f1iD -zJsvFhdS0ijRKTerAhBO1rpw1A1O7a8zJP/g0mie2OE8PnWQPQ== ------END CERTIFICATE REQUEST----- From a2b5cfb040fcb6bd0268b867af9946cc024399b1 Mon Sep 17 00:00:00 2001 From: Tobias Nyholm Date: Fri, 7 Jul 2017 23:58:31 +0200 Subject: [PATCH 04/58] Make sure __toString does not throw exception (#28) * Make sure __toString does not throw exception * Bugfix --- src/Stream.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Stream.php b/src/Stream.php index 9f5677f..507f135 100644 --- a/src/Stream.php +++ b/src/Stream.php @@ -59,7 +59,11 @@ public function __construct($socket, $size = null) */ public function __toString() { - return (string) $this->getContents(); + try { + return $this->getContents(); + } catch (\Exception $e) { + return ''; + } } /** From bd07d7eb3bd8bc5c29ca9b062a4d27d7728de671 Mon Sep 17 00:00:00 2001 From: Tobias Nyholm Date: Sat, 8 Jul 2017 00:46:17 +0200 Subject: [PATCH 05/58] Added changelog (#29) --- CHANGELOG.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e59903e..d262127 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,18 @@ # Change Log -## Unreleased +## 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 From 0acdac37474b31ce1fedf20c0c431946d2aa5c6f Mon Sep 17 00:00:00 2001 From: Tobias Nyholm Date: Fri, 7 Jul 2017 22:07:19 +0200 Subject: [PATCH 06/58] Sleep mor The tests randomly fails time to time trying to increase the the sleep time --- .travis.yml | 2 +- tests/BaseTestCase.php | 7 +++++ tests/Semaphore.php | 40 ++++++++++++++++++++++++++ tests/StreamTest.php | 13 ++++----- tests/server/ssl/file.srl | 2 +- tests/server/tcp-bugous-server.php | 3 ++ tests/server/tcp-server.php | 5 +++- tests/server/tcp-ssl-server-client.php | 3 ++ tests/server/tcp-ssl-server.php | 6 ++-- tests/server/unix-domain-server.php | 3 ++ 10 files changed, 72 insertions(+), 12 deletions(-) create mode 100644 tests/Semaphore.php diff --git a/.travis.yml b/.travis.yml index 157e9dd..0922767 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ language: php -sudo: false +sudo: required cache: directories: diff --git a/tests/BaseTestCase.php b/tests/BaseTestCase.php index 50b4fe3..3bc67d2 100644 --- a/tests/BaseTestCase.php +++ b/tests/BaseTestCase.php @@ -12,6 +12,11 @@ public function startServer($name) { $filename = __DIR__ . '/server/' . $name . '.php'; $pipes = []; + + if (!Semaphore::acquire()) { + $this->fail('Could not connect to server'); + } + $this->servers[$name] = proc_open('php '. $filename, [], $pipes); sleep(1); } @@ -28,5 +33,7 @@ public function tearDown() foreach (array_keys($this->servers) as $name) { $this->stopServer($name); } + + Semaphore::release(); } } diff --git a/tests/Semaphore.php b/tests/Semaphore.php new file mode 100644 index 0000000..ea83010 --- /dev/null +++ b/tests/Semaphore.php @@ -0,0 +1,40 @@ + + */ +final class Semaphore +{ + private static $openConnections = 0; + + /** + * Busy waiting for my turn to go. + * + * @return bool + */ + public static function acquire() + { + $tries = 1; + while (self::$openConnections > 0) { + sleep($tries++); + if ($tries > 5) { + return false; + } + } + self::$openConnections++; + + return true; + } + + /** + * Signal that you are done. + */ + public static function release() + { + // Do no be too quick + usleep(500000); + self::$openConnections--; + } +} diff --git a/tests/StreamTest.php b/tests/StreamTest.php index 910c6e2..2421fb1 100644 --- a/tests/StreamTest.php +++ b/tests/StreamTest.php @@ -2,6 +2,7 @@ namespace Http\Client\Socket\Tests; +use Http\Client\Socket\Exception\TimeoutException; use Http\Client\Socket\Stream; use PHPUnit\Framework\TestCase; @@ -108,18 +109,16 @@ public function testIsReadable() $this->assertTrue($stream->isReadable()); } + /** + * @expectedException \Http\Client\Socket\Exception\TimeoutException + */ public function testTimeout() { $socket = fsockopen("php.net", 80); socket_set_timeout($socket, 0, 100); - $stream = new Stream($socket); - - try { - $stream->getContents(); - } catch (\Exception $e) { - $this->assertInstanceOf('Http\Socket\Exception\TimeoutException', $e); - } + $stream = new Stream($socket, 50); + $stream->getContents(); } public function testMetadatas() diff --git a/tests/server/ssl/file.srl b/tests/server/ssl/file.srl index fe909bb..da51c42 100644 --- a/tests/server/ssl/file.srl +++ b/tests/server/ssl/file.srl @@ -1 +1 @@ -2D +2F diff --git a/tests/server/tcp-bugous-server.php b/tests/server/tcp-bugous-server.php index 72b0946..7f0d91f 100644 --- a/tests/server/tcp-bugous-server.php +++ b/tests/server/tcp-bugous-server.php @@ -1,6 +1,9 @@ [ 'local_cert' => __DIR__ . '/ssl/server-and-key.pem', @@ -44,3 +46,4 @@ while (!@feof($client)) { @fread($client, 1000); } +\Http\Client\Socket\Tests\Semaphore::release(); diff --git a/tests/server/tcp-ssl-server.php b/tests/server/tcp-ssl-server.php index de3af5a..2d5a22e 100644 --- a/tests/server/tcp-ssl-server.php +++ b/tests/server/tcp-ssl-server.php @@ -1,5 +1,7 @@ [ 'local_cert' => __DIR__ . '/ssl/server-and-key.pem' @@ -30,8 +32,8 @@ } - - while (!@feof($client)) { @fread($client, 1000); } + +\Http\Client\Socket\Tests\Semaphore::release(); diff --git a/tests/server/unix-domain-server.php b/tests/server/unix-domain-server.php index d6954b0..6221b98 100644 --- a/tests/server/unix-domain-server.php +++ b/tests/server/unix-domain-server.php @@ -1,5 +1,7 @@ Date: Sat, 18 Nov 2017 12:10:39 +0200 Subject: [PATCH 07/58] Symfony 4 compatibility --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index a1e5f8c..ab43a37 100644 --- a/composer.json +++ b/composer.json @@ -10,7 +10,7 @@ ], "require": { "php": "^5.5 || ^7.0", - "symfony/options-resolver": "^2.6 || ^3.0", + "symfony/options-resolver": "^2.6 || ^3.0 || ^4.0", "php-http/httplug": "^1.0", "php-http/message-factory": "^1.0.2", "php-http/discovery": "^1.0" From cbdb176f12c842c3cbfd1556061c171a07381eb7 Mon Sep 17 00:00:00 2001 From: Tobias Nyholm Date: Sat, 18 Nov 2017 12:17:48 +0200 Subject: [PATCH 08/58] Style fixes --- src/Client.php | 6 +++--- src/RequestWriter.php | 4 ++-- src/ResponseReader.php | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Client.php b/src/Client.php index 6422a11..cb84123 100644 --- a/src/Client.php +++ b/src/Client.php @@ -76,7 +76,7 @@ public function sendRequest(RequestInterface $request) } if (null === $useSsl) { - $useSsl = ($request->getUri()->getScheme() === 'https'); + $useSsl = ('https' === $request->getUri()->getScheme()); } $socket = $this->createSocket($request, $remote, $useSsl); @@ -169,12 +169,12 @@ protected function configure(array $config = []) */ private function determineRemoteFromRequest(RequestInterface $request) { - if (!$request->hasHeader('Host') && $request->getUri()->getHost() === '') { + 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() ?: ($request->getUri()->getScheme() === 'https' ? 443 : 80); + $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 diff --git a/src/RequestWriter.php b/src/RequestWriter.php index 7a8751b..3e8ac88 100644 --- a/src/RequestWriter.php +++ b/src/RequestWriter.php @@ -100,7 +100,7 @@ private function fwrite($stream, $bytes) return 0; } $result = @fwrite($stream, $bytes); - if ($result !== 0) { + 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. @@ -124,7 +124,7 @@ private function fwrite($stream, $bytes) // perform a write. If the write also fails, conclude that these failures are // EPIPE or some other permanent failure. $result = @fwrite($stream, $bytes); - if ($result !== 0) { + if (0 !== $result) { // The write worked or failed explicitly. This value is fine to return. return $result; } diff --git a/src/ResponseReader.php b/src/ResponseReader.php index 6826078..ba4442e 100644 --- a/src/ResponseReader.php +++ b/src/ResponseReader.php @@ -38,8 +38,8 @@ protected function readResponse(RequestInterface $request, $socket) $headers = []; $reason = null; - while (($line = fgets($socket)) !== false) { - if (rtrim($line) === '') { + while (false !== ($line = fgets($socket))) { + if ('' === rtrim($line)) { break; } $headers[] = trim($line); From 26075bf4f98234d41d082a3bdd112a64c4d8699b Mon Sep 17 00:00:00 2001 From: Tobias Nyholm Date: Wed, 29 Nov 2017 21:46:45 +0100 Subject: [PATCH 09/58] Added change log (#34) --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d262127..8aaef69 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Change Log +## 1.4.0 + + * Support for Symfony 4 + ## 1.3.0 * Make sure `Stream::__toString` never throws exception From e2833e0242c5c4aba1bfbab1ad403019adefd4d4 Mon Sep 17 00:00:00 2001 From: Gerhard Alfanz Date: Thu, 30 Nov 2017 14:30:09 +0100 Subject: [PATCH 10/58] Fixes #31: Wrong parameters error when TimeoutException is thrown (#32) * Fixes #31: Wrong parameters error when TimeoutException is thrown --- src/Exception/StreamException.php | 29 +++++++++++++++++++++++++++++ src/ResponseReader.php | 2 +- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/Exception/StreamException.php b/src/Exception/StreamException.php index d40a6ce..6437cec 100644 --- a/src/Exception/StreamException.php +++ b/src/Exception/StreamException.php @@ -3,7 +3,36 @@ namespace Http\Client\Socket\Exception; use Http\Client\Exception; +use Psr\Http\Message\RequestInterface; class StreamException extends \RuntimeException implements Exception { + /** + * The request object. + * + * @var RequestInterface + */ + private $request; + + /** + * Accepts an optional request object as 4th param. + * + * @param string $message + * @param int $code + * @param Exception $previous + * @param RequestInterface $request + */ + public function __construct($message = null, $code = null, $previous = null, RequestInterface $request = null) + { + $this->request = $request; + parent::__construct($message, $code, $previous); + } + + /** + * @return \Psr\Http\Message\RequestInterface|null + */ + final public function getRequest() + { + return $this->request; + } } diff --git a/src/ResponseReader.php b/src/ResponseReader.php index ba4442e..f5fd083 100644 --- a/src/ResponseReader.php +++ b/src/ResponseReader.php @@ -48,7 +48,7 @@ protected function readResponse(RequestInterface $request, $socket) $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); + throw new TimeoutException('Error while reading response, stream timed out', null, null, $request); } $parts = explode(' ', array_shift($headers), 3); From 65c2afd5f2e04fe3a1392d4dab492fff7c38bd92 Mon Sep 17 00:00:00 2001 From: Joel Wurtz Date: Mon, 21 Jan 2019 15:26:47 +0100 Subject: [PATCH 11/58] Fix cs --- .gitignore | 4 ++ .php_cs | 27 +++++++---- .travis.yml | 2 + composer.json | 3 ++ src/Client.php | 1 - src/RequestWriter.php | 11 ++--- src/ResponseReader.php | 4 +- tests/BaseTestCase.php | 6 +-- tests/Semaphore.php | 4 +- tests/SocketClientFeatureTest.php | 1 - tests/SocketHttpAdapterTest.php | 1 - tests/SocketHttpClientTest.php | 64 +++++++++++++------------- tests/StreamTest.php | 55 +++++++++++----------- tests/server/ssl/file.srl | 2 +- tests/server/ssl/generate.sh | 4 +- tests/server/tcp-bugous-server.php | 4 +- tests/server/tcp-server.php | 2 +- tests/server/tcp-ssl-server-client.php | 20 ++++---- tests/server/tcp-ssl-server.php | 13 +++--- tests/server/unix-domain-server.php | 4 +- 20 files changed, 120 insertions(+), 112 deletions(-) diff --git a/.gitignore b/.gitignore index da734f1..33793eb 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,7 @@ vendor/ composer.lock phpspec.yml phpunit.xml +/.php_cs.cache +/tests/server/ssl/*.pem +/tests/server/ssl/*.key +/tests/server/ssl/*.req diff --git a/.php_cs b/.php_cs index 23ba165..55d9b0b 100644 --- a/.php_cs +++ b/.php_cs @@ -1,13 +1,22 @@ setRules([ + '@PSR2' => true, + '@Symfony' => true, + 'array_syntax' => [ + 'syntax' => 'short', + ], + 'no_empty_phpdoc' => true, + 'no_superfluous_phpdoc_tags' => true, +]); -use SLLH\StyleCIBridge\ConfigBridge; +$finder = PhpCsFixer\Finder::create(); +$finder->in([ + 'src', + 'tests' +]); -return ConfigBridge::create(); +$config->setFinder($finder); + +return $config; diff --git a/.travis.yml b/.travis.yml index 0922767..cbde20e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,6 +11,8 @@ php: - 5.6 - 7.0 - 7.1 + - 7.2 + - 7.3 env: global: diff --git a/composer.json b/composer.json index ab43a37..afcf404 100644 --- a/composer.json +++ b/composer.json @@ -16,6 +16,7 @@ "php-http/discovery": "^1.0" }, "require-dev": { + "friendsofphp/php-cs-fixer": "^2.2", "guzzlehttp/psr7": "^1.2", "php-http/client-integration-tests": "^0.6", "php-http/message": "^1.0", @@ -35,6 +36,8 @@ } }, "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" }, diff --git a/src/Client.php b/src/Client.php index cb84123..7333fbb 100644 --- a/src/Client.php +++ b/src/Client.php @@ -161,7 +161,6 @@ protected function configure(array $config = []) /** * Return remote socket from the request. * - * @param RequestInterface $request * * @throws InvalidRequestException When no remote can be determined from the request * diff --git a/src/RequestWriter.php b/src/RequestWriter.php index 3e8ac88..405649f 100644 --- a/src/RequestWriter.php +++ b/src/RequestWriter.php @@ -17,9 +17,8 @@ trait RequestWriter /** * Write a request to a socket. * - * @param resource $socket - * @param RequestInterface $request - * @param int $bufferSize + * @param resource $socket + * @param int $bufferSize * * @throws BrokenPipeException */ @@ -37,9 +36,8 @@ protected function writeRequest($socket, RequestInterface $request, $bufferSize /** * Write Body of the request. * - * @param resource $socket - * @param RequestInterface $request - * @param int $bufferSize + * @param resource $socket + * @param int $bufferSize * * @throws BrokenPipeException */ @@ -63,7 +61,6 @@ protected function writeBody($socket, RequestInterface $request, $bufferSize = 8 /** * Produce the header of request as a string based on a PSR Request. * - * @param RequestInterface $request * * @return string */ diff --git a/src/ResponseReader.php b/src/ResponseReader.php index f5fd083..df4ba50 100644 --- a/src/ResponseReader.php +++ b/src/ResponseReader.php @@ -25,8 +25,7 @@ trait ResponseReader /** * Read a response from a socket. * - * @param RequestInterface $request - * @param resource $socket + * @param resource $socket * * @throws TimeoutException When the socket timed out * @throws BrokenPipeException When the response cannot be read @@ -89,7 +88,6 @@ protected function readResponse(RequestInterface $request, $socket) * Create the stream. * * @param $socket - * @param ResponseInterface $response * * @return Stream */ diff --git a/tests/BaseTestCase.php b/tests/BaseTestCase.php index 3bc67d2..a6ffda7 100644 --- a/tests/BaseTestCase.php +++ b/tests/BaseTestCase.php @@ -10,14 +10,14 @@ class BaseTestCase extends TestCase public function startServer($name) { - $filename = __DIR__ . '/server/' . $name . '.php'; - $pipes = []; + $filename = __DIR__.'/server/'.$name.'.php'; + $pipes = []; if (!Semaphore::acquire()) { $this->fail('Could not connect to server'); } - $this->servers[$name] = proc_open('php '. $filename, [], $pipes); + $this->servers[$name] = proc_open('php '.$filename, [], $pipes); sleep(1); } diff --git a/tests/Semaphore.php b/tests/Semaphore.php index ea83010..9ff92a7 100644 --- a/tests/Semaphore.php +++ b/tests/Semaphore.php @@ -23,7 +23,7 @@ public static function acquire() return false; } } - self::$openConnections++; + ++self::$openConnections; return true; } @@ -35,6 +35,6 @@ public static function release() { // Do no be too quick usleep(500000); - self::$openConnections--; + --self::$openConnections; } } diff --git a/tests/SocketClientFeatureTest.php b/tests/SocketClientFeatureTest.php index 487944b..d02dea8 100644 --- a/tests/SocketClientFeatureTest.php +++ b/tests/SocketClientFeatureTest.php @@ -3,7 +3,6 @@ namespace Http\Client\Socket\Tests; use Http\Client\Tests\HttpFeatureTest; -use Http\Message\MessageFactory\GuzzleMessageFactory; use Http\Client\Socket\Client as SocketHttpClient; class SocketClientFeatureTest extends HttpFeatureTest diff --git a/tests/SocketHttpAdapterTest.php b/tests/SocketHttpAdapterTest.php index 3d90e70..143d4b4 100644 --- a/tests/SocketHttpAdapterTest.php +++ b/tests/SocketHttpAdapterTest.php @@ -3,7 +3,6 @@ namespace Http\Client\Socket\Tests; use Http\Client\Tests\HttpClientTest; -use Http\Message\MessageFactory\GuzzleMessageFactory; use Http\Client\Socket\Client as SocketHttpClient; class SocketHttpAdapterTest extends HttpClientTest diff --git a/tests/SocketHttpClientTest.php b/tests/SocketHttpClientTest.php index c3660ec..e8981e6 100644 --- a/tests/SocketHttpClientTest.php +++ b/tests/SocketHttpClientTest.php @@ -8,7 +8,7 @@ class SocketHttpClientTest extends BaseTestCase { - public function createClient($options = array()) + public function createClient($options = []) { $messageFactory = new GuzzleMessageFactory(); @@ -18,7 +18,7 @@ public function createClient($options = array()) public function testTcpSocketDomain() { $this->startServer('tcp-server'); - $client = $this->createClient(['remote_socket' => '127.0.0.1:19999']); + $client = $this->createClient(['remote_socket' => '127.0.0.1:19999']); $response = $client->get('/', []); $this->assertInstanceOf('Psr\Http\Message\ResponseInterface', $response); @@ -30,14 +30,14 @@ public function testTcpSocketDomain() */ public function testNoRemote() { - $client = $this->createClient(); + $client = $this->createClient(); $client->get('/', []); } public function testRemoteInUri() { $this->startServer('tcp-server'); - $client = $this->createClient(); + $client = $this->createClient(); $response = $client->get('http://127.0.0.1:19999/', []); $this->assertInstanceOf('Psr\Http\Message\ResponseInterface', $response); @@ -47,7 +47,7 @@ public function testRemoteInUri() public function testRemoteInHostHeader() { $this->startServer('tcp-server'); - $client = $this->createClient(); + $client = $this->createClient(); $response = $client->get('/', ['Host' => '127.0.0.1:19999']); $this->assertInstanceOf('Psr\Http\Message\ResponseInterface', $response); @@ -67,13 +67,13 @@ public function testBrokenSocket() public function testSslRemoteInUri() { $this->startServer('tcp-ssl-server'); - $client = $this->createClient([ + $client = $this->createClient([ 'stream_context_options' => [ 'ssl' => [ 'peer_name' => 'socket-adapter', - 'cafile' => __DIR__ . '/server/ssl/ca.pem' - ] - ] + 'cafile' => __DIR__.'/server/ssl/ca.pem', + ], + ], ]); $response = $client->get('https://127.0.0.1:19999/', []); @@ -85,8 +85,8 @@ public function testUnixSocketDomain() { $this->startServer('unix-domain-server'); - $client = $this->createClient([ - 'remote_socket' => 'unix://'.__DIR__.'/server/server.sock' + $client = $this->createClient([ + 'remote_socket' => 'unix://'.__DIR__.'/server/server.sock', ]); $response = $client->get('/', []); @@ -99,7 +99,7 @@ public function testUnixSocketDomain() */ public function testNetworkExceptionOnConnectError() { - $client = $this->createClient(['remote_socket' => '127.0.0.1:19999']); + $client = $this->createClient(['remote_socket' => '127.0.0.1:19999']); $client->get('/', []); } @@ -107,15 +107,15 @@ public function testSslConnection() { $this->startServer('tcp-ssl-server'); - $client = $this->createClient([ + $client = $this->createClient([ 'remote_socket' => '127.0.0.1:19999', - 'ssl' => true, + 'ssl' => true, 'stream_context_options' => [ 'ssl' => [ 'peer_name' => 'socket-adapter', - 'cafile' => __DIR__ . '/server/ssl/ca.pem' - ] - ] + 'cafile' => __DIR__.'/server/ssl/ca.pem', + ], + ], ]); $response = $client->get('/', []); @@ -131,16 +131,16 @@ public function testSslConnectionWithClientCertificate() $this->startServer('tcp-ssl-server-client'); - $client = $this->createClient([ + $client = $this->createClient([ 'remote_socket' => '127.0.0.1:19999', - 'ssl' => true, + 'ssl' => true, 'stream_context_options' => [ 'ssl' => [ - 'peer_name' => 'socket-adapter', - 'cafile' => __DIR__ . '/server/ssl/ca.pem', - 'local_cert' => __DIR__ . '/server/ssl/client-and-key.pem' - ] - ] + 'peer_name' => 'socket-adapter', + 'cafile' => __DIR__.'/server/ssl/ca.pem', + 'local_cert' => __DIR__.'/server/ssl/client-and-key.pem', + ], + ], ]); $response = $client->get('/', []); @@ -156,15 +156,15 @@ public function testInvalidSslConnectionWithClientCertificate() $this->startServer('tcp-ssl-server-client'); - $client = $this->createClient([ + $client = $this->createClient([ 'remote_socket' => '127.0.0.1:19999', - 'ssl' => true, + 'ssl' => true, 'stream_context_options' => [ 'ssl' => [ - 'peer_name' => 'socket-adapter', - 'cafile' => __DIR__ . '/server/ssl/ca.pem' - ] - ] + 'peer_name' => 'socket-adapter', + 'cafile' => __DIR__.'/server/ssl/ca.pem', + ], + ], ]); $response = $client->get('/', []); @@ -179,7 +179,7 @@ public function testNetworkExceptionOnSslError() { $this->startServer('tcp-server'); - $client = $this->createClient(['remote_socket' => '127.0.0.1:19999', 'ssl' => true]); + $client = $this->createClient(['remote_socket' => '127.0.0.1:19999', 'ssl' => true]); $client->get('/', []); } @@ -188,7 +188,7 @@ public function testNetworkExceptionOnSslError() */ public function testNetworkExceptionOnTimeout() { - $client = $this->createClient(['timeout' => 10]); + $client = $this->createClient(['timeout' => 10]); $client->get('http://php.net', []); } } diff --git a/tests/StreamTest.php b/tests/StreamTest.php index 2421fb1..0bd21e6 100644 --- a/tests/StreamTest.php +++ b/tests/StreamTest.php @@ -2,7 +2,6 @@ namespace Http\Client\Socket\Tests; -use Http\Client\Socket\Exception\TimeoutException; use Http\Client\Socket\Stream; use PHPUnit\Framework\TestCase; @@ -19,24 +18,24 @@ public function createSocket($body, $useSize = true) public function testToString() { - $stream = $this->createSocket("Body"); + $stream = $this->createSocket('Body'); - $this->assertEquals("Body", $stream->__toString()); + $this->assertEquals('Body', $stream->__toString()); $stream->close(); } public function testSubsequentCallIsEmpty() { - $stream = $this->createSocket("Body"); + $stream = $this->createSocket('Body'); - $this->assertEquals("Body", $stream->getContents()); + $this->assertEquals('Body', $stream->getContents()); $this->assertEmpty($stream->getContents()); $stream->close(); } public function testDetach() { - $stream = $this->createSocket("Body"); + $stream = $this->createSocket('Body'); $socket = $stream->detach(); $this->assertTrue(is_resource($socket)); @@ -45,21 +44,21 @@ public function testDetach() public function testTell() { - $stream = $this->createSocket("Body"); + $stream = $this->createSocket('Body'); $this->assertEquals(0, $stream->tell()); - $this->assertEquals("Body", $stream->getContents()); + $this->assertEquals('Body', $stream->getContents()); $this->assertEquals(4, $stream->tell()); } public function testEof() { $socket = fopen('php://memory', 'rw+'); - fwrite($socket, "Body"); + fwrite($socket, 'Body'); fseek($socket, 0); $stream = new Stream($socket); - $this->assertEquals("Body", $stream->getContents()); + $this->assertEquals('Body', $stream->getContents()); fwrite($socket, "\0"); $this->assertTrue($stream->eof()); $stream->close(); @@ -67,7 +66,7 @@ public function testEof() public function testNotSeekable() { - $stream = $this->createSocket("Body"); + $stream = $this->createSocket('Body'); $this->assertFalse($stream->isSeekable()); @@ -80,7 +79,7 @@ public function testNotSeekable() public function testNoRewing() { - $stream = $this->createSocket("Body"); + $stream = $this->createSocket('Body'); try { $stream->rewind(); @@ -91,12 +90,12 @@ public function testNoRewing() public function testNotWritable() { - $stream = $this->createSocket("Body"); + $stream = $this->createSocket('Body'); $this->assertFalse($stream->isWritable()); try { - $stream->write("Test"); + $stream->write('Test'); } catch (\Exception $e) { $this->assertInstanceOf('Http\Client\Socket\Exception\StreamException', $e); } @@ -104,7 +103,7 @@ public function testNotWritable() public function testIsReadable() { - $stream = $this->createSocket("Body"); + $stream = $this->createSocket('Body'); $this->assertTrue($stream->isReadable()); } @@ -114,7 +113,7 @@ public function testIsReadable() */ public function testTimeout() { - $socket = fsockopen("php.net", 80); + $socket = fsockopen('php.net', 80); socket_set_timeout($socket, 0, 100); $stream = new Stream($socket, 50); @@ -123,20 +122,20 @@ public function testTimeout() public function testMetadatas() { - $stream = $this->createSocket("Body", false); - - $this->assertEquals("PHP", $stream->getMetadata("wrapper_type")); - $this->assertEquals("MEMORY", $stream->getMetadata("stream_type")); - $this->assertEquals("php://memory", $stream->getMetadata("uri")); - $this->assertFalse($stream->getMetadata("timed_out")); - $this->assertFalse($stream->getMetadata("eof")); - $this->assertTrue($stream->getMetadata("blocked")); + $stream = $this->createSocket('Body', false); + + $this->assertEquals('PHP', $stream->getMetadata('wrapper_type')); + $this->assertEquals('MEMORY', $stream->getMetadata('stream_type')); + $this->assertEquals('php://memory', $stream->getMetadata('uri')); + $this->assertFalse($stream->getMetadata('timed_out')); + $this->assertFalse($stream->getMetadata('eof')); + $this->assertTrue($stream->getMetadata('blocked')); } public function testClose() { $socket = fopen('php://memory', 'rw+'); - fwrite($socket, "Body"); + fwrite($socket, 'Body'); fseek($socket, 0); $stream = new Stream($socket); @@ -147,10 +146,10 @@ public function testClose() public function testRead() { - $stream = $this->createSocket("Body"); + $stream = $this->createSocket('Body'); - $this->assertEquals("Bod", $stream->read(3)); - $this->assertEquals("y", $stream->read(3)); + $this->assertEquals('Bod', $stream->read(3)); + $this->assertEquals('y', $stream->read(3)); $stream->close(); } diff --git a/tests/server/ssl/file.srl b/tests/server/ssl/file.srl index da51c42..f5c8955 100644 --- a/tests/server/ssl/file.srl +++ b/tests/server/ssl/file.srl @@ -1 +1 @@ -2F +32 diff --git a/tests/server/ssl/generate.sh b/tests/server/ssl/generate.sh index 696b340..eb83bc2 100755 --- a/tests/server/ssl/generate.sh +++ b/tests/server/ssl/generate.sh @@ -7,11 +7,11 @@ O="PHP-HTTP" CN="socket-adapter" openssl req -out ca.pem -new -x509 -subj "/C=$C/ST=$ST/L=$L/O=$O/CN=socket-server" -passout pass:password -openssl genrsa -out server.key 1024 -subj "/C=$C/ST=$ST/L=$L/O=$O/CN=socket-adapter" +openssl genrsa -out server.key openssl req -key server.key -new -out server.req -subj "/C=$C/ST=$ST/L=$L/O=$O/CN=socket-adapter" -passout pass:password openssl x509 -req -in server.req -CA ca.pem -CAkey privkey.pem -CAserial file.srl -out server.pem -passin pass:password -openssl genrsa -out client.key 1024 -subj "/C=$C/ST=$ST/L=$L/O=$O/CN=socket-adapter-client" +openssl genrsa -out client.key openssl req -key client.key -new -out client.req -subj "/C=$C/ST=$ST/L=$L/O=$O/CN=socket-adapter-client" -passout pass:password openssl x509 -req -in client.req -CA ca.pem -CAkey privkey.pem -CAserial file.srl -out client.pem -passin pass:password diff --git a/tests/server/tcp-bugous-server.php b/tests/server/tcp-bugous-server.php index 7f0d91f..d1a55ae 100644 --- a/tests/server/tcp-bugous-server.php +++ b/tests/server/tcp-bugous-server.php @@ -1,9 +1,9 @@ [ - 'local_cert' => __DIR__ . '/ssl/server-and-key.pem', - 'cafile' => __DIR__ . '/ssl/ca.pem', - 'capture_peer_cert' => true - ] + 'local_cert' => __DIR__.'/ssl/server-and-key.pem', + 'cafile' => __DIR__.'/ssl/ca.pem', + 'capture_peer_cert' => true, + ], ]); -$socketServer = stream_socket_server('tcp://127.0.0.1:19999', $errNo, $errStr, STREAM_SERVER_BIND|STREAM_SERVER_LISTEN, $context); +$socketServer = stream_socket_server('tcp://127.0.0.1:19999', $errNo, $errStr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $context); stream_socket_enable_crypto($socketServer, false); -$client = stream_socket_accept($socketServer); +$client = stream_socket_accept($socketServer); stream_set_blocking($client, true); stream_socket_enable_crypto($client, true, STREAM_CRYPTO_METHOD_TLS_SERVER); @@ -22,10 +22,10 @@ if (isset(stream_context_get_options($context)['ssl']['peer_certificate'])) { $client_cert = stream_context_get_options($context)['ssl']['peer_certificate']; - $name = openssl_x509_parse($client_cert)["subject"]["CN"]; + $name = openssl_x509_parse($client_cert)['subject']['CN']; } -if ($name == "socket-adapter-client") { +if ('socket-adapter-client' == $name) { fwrite($client, str_replace("\n", "\r\n", << [ - 'local_cert' => __DIR__ . '/ssl/server-and-key.pem' - ] + 'local_cert' => __DIR__.'/ssl/server-and-key.pem', + ], ]); -$socketServer = stream_socket_server('tcp://127.0.0.1:19999', $errNo, $errStr, STREAM_SERVER_BIND|STREAM_SERVER_LISTEN, $context); +$socketServer = stream_socket_server('tcp://127.0.0.1:19999', $errNo, $errStr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $context); stream_socket_enable_crypto($socketServer, false); -$client = stream_socket_accept($socketServer); +$client = stream_socket_accept($socketServer); stream_set_blocking($client, true); if (@stream_socket_enable_crypto($client, true, STREAM_CRYPTO_METHOD_TLS_SERVER)) { fwrite($client, str_replace("\n", "\r\n", << Date: Mon, 21 Jan 2019 15:29:47 +0100 Subject: [PATCH 12/58] Remove support for old version of php --- .travis.yml | 10 +--------- composer.json | 2 +- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/.travis.yml b/.travis.yml index cbde20e..ad51702 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,9 +7,6 @@ cache: - $HOME/.composer/cache php: - - 5.5 - - 5.6 - - 7.0 - 7.1 - 7.2 - 7.3 @@ -23,15 +20,10 @@ branches: - /^analysis-.*$/ matrix: - allow_failures: - - php: hhvm - dist: trusty fast_finish: true include: - - php: 5.5 + - php: 7.1 env: COMPOSER_FLAGS="--prefer-stable --prefer-lowest" COVERAGE=true TEST_COMMAND="composer test-ci" - - php: hhvm - dist: trusty before_install: - travis_retry composer self-update diff --git a/composer.json b/composer.json index afcf404..1ef4262 100644 --- a/composer.json +++ b/composer.json @@ -9,7 +9,7 @@ } ], "require": { - "php": "^5.5 || ^7.0", + "php": "^7.1", "symfony/options-resolver": "^2.6 || ^3.0 || ^4.0", "php-http/httplug": "^1.0", "php-http/message-factory": "^1.0.2", From e8135a2f3a8fc39b83fa559ceae455db75f2483e Mon Sep 17 00:00:00 2001 From: Joel Wurtz Date: Mon, 21 Jan 2019 15:46:44 +0100 Subject: [PATCH 13/58] Make it work with psr18 and httplug 2.0 --- composer.json | 10 +++++----- src/Client.php | 3 ++- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/composer.json b/composer.json index 1ef4262..8eb246e 100644 --- a/composer.json +++ b/composer.json @@ -11,16 +11,16 @@ "require": { "php": "^7.1", "symfony/options-resolver": "^2.6 || ^3.0 || ^4.0", - "php-http/httplug": "^1.0", - "php-http/message-factory": "^1.0.2", + "php-http/httplug": "^1.0 || ^2.0", + "php-http/message-factory": "^1.0.2 || ^2.0", "php-http/discovery": "^1.0" }, "require-dev": { "friendsofphp/php-cs-fixer": "^2.2", "guzzlehttp/psr7": "^1.2", - "php-http/client-integration-tests": "^0.6", + "php-http/client-integration-tests": "^1.0 || ^2.0", "php-http/message": "^1.0", - "php-http/client-common": "^1.0" + "php-http/client-common": "^1.0 || ^2.0" }, "provide": { "php-http/client-implementation": "1.0" @@ -43,7 +43,7 @@ }, "extra": { "branch-alias": { - "dev-master": "1.1-dev" + "dev-master": "2.0-dev" } }, "prefer-stable": true, diff --git a/src/Client.php b/src/Client.php index 7333fbb..d085043 100644 --- a/src/Client.php +++ b/src/Client.php @@ -9,6 +9,7 @@ use Http\Discovery\MessageFactoryDiscovery; use Http\Message\ResponseFactory; use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\ResponseInterface; use Symfony\Component\OptionsResolver\Options; use Symfony\Component\OptionsResolver\OptionsResolver; @@ -62,7 +63,7 @@ public function __construct(ResponseFactory $responseFactory = null, array $conf /** * {@inheritdoc} */ - public function sendRequest(RequestInterface $request) + public function sendRequest(RequestInterface $request): ResponseInterface { $remote = $this->config['remote_socket']; $useSsl = $this->config['ssl']; From 4a1b34a21a7c325b0c121d2ce8dc0f2313803ab0 Mon Sep 17 00:00:00 2001 From: Joel Wurtz Date: Mon, 21 Jan 2019 15:49:13 +0100 Subject: [PATCH 14/58] Add changelog --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8aaef69..0d64672 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Change Log +## 2.0.0 (unreleased) + + * PSR18 and HTTPlug 2 support + * Remove support for php 5.5, 5.6 and 7.0 + ## 1.4.0 * Support for Symfony 4 From 6636c00bb5542bd0f609e5d66fe1a460e7bb14c7 Mon Sep 17 00:00:00 2001 From: Bastien Jaillot Date: Tue, 29 May 2018 15:01:15 +0200 Subject: [PATCH 15/58] TLS v1 is not accepted anymore I got errors like that all the time since the servers I contact updated their lib. > OpenSSL Error messages: error:1409442E:SSL routines:SSL3_READ_BYTES:tlsv1 alert protocol version --- src/Client.php | 4 ++-- tests/server/tcp-ssl-server-client.php | 2 +- tests/server/tcp-ssl-server.php | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Client.php b/src/Client.php index d085043..0d2a6c8 100644 --- a/src/Client.php +++ b/src/Client.php @@ -32,7 +32,7 @@ class Client implements HttpClient 'stream_context_param' => [], 'ssl' => null, 'write_buffer_size' => 8192, - 'ssl_method' => STREAM_CRYPTO_METHOD_TLS_CLIENT, + 'ssl_method' => STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT, ]; /** @@ -47,7 +47,7 @@ class Client implements HttpClient * @var array $stream_context_param Context params as defined in the PHP documentation * @var bool $ssl Use ssl, default to scheme from request, false if not present * @var int $write_buffer_size Buffer when writing the request body, defaults to 8192 - * @var int $ssl_method Crypto method for ssl/tls, see PHP doc, defaults to STREAM_CRYPTO_METHOD_TLS_CLIENT + * @var int $ssl_method Crypto method for ssl/tls, see PHP doc, defaults to STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT * } */ public function __construct(ResponseFactory $responseFactory = null, array $config = []) diff --git a/tests/server/tcp-ssl-server-client.php b/tests/server/tcp-ssl-server-client.php index 29ee43c..1e06cd3 100644 --- a/tests/server/tcp-ssl-server-client.php +++ b/tests/server/tcp-ssl-server-client.php @@ -15,7 +15,7 @@ $client = stream_socket_accept($socketServer); stream_set_blocking($client, true); -stream_socket_enable_crypto($client, true, STREAM_CRYPTO_METHOD_TLS_SERVER); +stream_socket_enable_crypto($client, true, STREAM_CRYPTO_METHOD_TLSv1_2_SERVER); // Verify client certificate $name = null; diff --git a/tests/server/tcp-ssl-server.php b/tests/server/tcp-ssl-server.php index 808717e..fb15359 100644 --- a/tests/server/tcp-ssl-server.php +++ b/tests/server/tcp-ssl-server.php @@ -13,7 +13,7 @@ $client = stream_socket_accept($socketServer); stream_set_blocking($client, true); -if (@stream_socket_enable_crypto($client, true, STREAM_CRYPTO_METHOD_TLS_SERVER)) { +if (@stream_socket_enable_crypto($client, true, STREAM_CRYPTO_METHOD_TLSv1_2_SERVER)) { fwrite($client, str_replace("\n", "\r\n", << Date: Mon, 21 Jan 2019 15:54:31 +0100 Subject: [PATCH 16/58] Add changelog for new ssl default --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d64672..5da849c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ * PSR18 and HTTPlug 2 support * Remove support for php 5.5, 5.6 and 7.0 + * SSL Method now defaults to `STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT` ## 1.4.0 From d5f46a1e4abc8079456b73f203de019b1170d2f4 Mon Sep 17 00:00:00 2001 From: Joel Wurtz Date: Mon, 21 Jan 2019 16:07:27 +0100 Subject: [PATCH 17/58] Remove styleci integration --- .styleci.yml | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 .styleci.yml diff --git a/.styleci.yml b/.styleci.yml deleted file mode 100644 index 296cab4..0000000 --- a/.styleci.yml +++ /dev/null @@ -1,10 +0,0 @@ -preset: symfony - -finder: - exclude: - - "spec" - path: - - "src" - -enabled: - - short_array_syntax From 3298ae57d2f168c1109b75e8699f9bbd877d8527 Mon Sep 17 00:00:00 2001 From: Joel Wurtz Date: Mon, 21 Jan 2019 17:24:54 +0100 Subject: [PATCH 18/58] Add 7.2 with prefer lowest to ensure httplug 1.0 compatiblity --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index ad51702..28a8944 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,6 +24,8 @@ matrix: include: - php: 7.1 env: COMPOSER_FLAGS="--prefer-stable --prefer-lowest" COVERAGE=true TEST_COMMAND="composer test-ci" + - php: 7.2 + env: COMPOSER_FLAGS="--prefer-stable --prefer-lowest" before_install: - travis_retry composer self-update From ed215638578a925c9e1bbaeea0587c2f48e2bcfc Mon Sep 17 00:00:00 2001 From: Joel Wurtz Date: Mon, 21 Jan 2019 17:28:55 +0100 Subject: [PATCH 19/58] Add type hinting --- src/Client.php | 2 +- src/RequestWriter.php | 14 ++++---------- src/ResponseReader.php | 10 +++------- src/Stream.php | 2 +- 4 files changed, 9 insertions(+), 19 deletions(-) diff --git a/src/Client.php b/src/Client.php index 0d2a6c8..22125c3 100644 --- a/src/Client.php +++ b/src/Client.php @@ -105,7 +105,7 @@ public function sendRequest(RequestInterface $request): ResponseInterface * * @return resource Socket resource */ - protected function createSocket(RequestInterface $request, $remote, $useSsl) + protected function createSocket(RequestInterface $request, string $remote, bool $useSsl) { $errNo = null; $errMsg = null; diff --git a/src/RequestWriter.php b/src/RequestWriter.php index 405649f..7f7bbca 100644 --- a/src/RequestWriter.php +++ b/src/RequestWriter.php @@ -18,11 +18,10 @@ trait RequestWriter * Write a request to a socket. * * @param resource $socket - * @param int $bufferSize * * @throws BrokenPipeException */ - protected function writeRequest($socket, RequestInterface $request, $bufferSize = 8192) + 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); @@ -37,11 +36,10 @@ protected function writeRequest($socket, RequestInterface $request, $bufferSize * Write Body of the request. * * @param resource $socket - * @param int $bufferSize * * @throws BrokenPipeException */ - protected function writeBody($socket, RequestInterface $request, $bufferSize = 8192) + protected function writeBody($socket, RequestInterface $request, int $bufferSize = 8192) { $body = $request->getBody(); @@ -60,11 +58,8 @@ protected function writeBody($socket, RequestInterface $request, $bufferSize = 8 /** * Produce the header of request as a string based on a PSR Request. - * - * - * @return string */ - protected function transformRequestHeadersToString(RequestInterface $request) + protected function transformRequestHeadersToString(RequestInterface $request): string { $message = vsprintf('%s %s HTTP/%s', [ strtoupper($request->getMethod()), @@ -87,11 +82,10 @@ protected function transformRequestHeadersToString(RequestInterface $request) * @see https://secure.phabricator.com/rPHU69490c53c9c2ef2002bc2dd4cecfe9a4b080b497 * * @param resource $stream The stream resource - * @param string $bytes Bytes written in the stream * * @return bool|int false if pipe is broken, number of bytes written otherwise */ - private function fwrite($stream, $bytes) + private function fwrite($stream, string $bytes) { if (!strlen($bytes)) { return 0; diff --git a/src/ResponseReader.php b/src/ResponseReader.php index df4ba50..e631426 100644 --- a/src/ResponseReader.php +++ b/src/ResponseReader.php @@ -29,10 +29,8 @@ trait ResponseReader * * @throws TimeoutException When the socket timed out * @throws BrokenPipeException When the response cannot be read - * - * @return ResponseInterface */ - protected function readResponse(RequestInterface $request, $socket) + protected function readResponse(RequestInterface $request, $socket): ResponseInterface { $headers = []; $reason = null; @@ -87,11 +85,9 @@ protected function readResponse(RequestInterface $request, $socket) /** * Create the stream. * - * @param $socket - * - * @return Stream + * @param resource $socket */ - protected function createStream($socket, ResponseInterface $response) + protected function createStream($socket, ResponseInterface $response): Stream { $size = null; diff --git a/src/Stream.php b/src/Stream.php index 507f135..00f2503 100644 --- a/src/Stream.php +++ b/src/Stream.php @@ -48,7 +48,7 @@ class Stream implements StreamInterface * @param resource $socket * @param int $size */ - public function __construct($socket, $size = null) + public function __construct($socket, ?int $size = null) { $this->socket = $socket; $this->size = $size; From 0243e0c63b41a038e88ed511bb13e05cf956b7d2 Mon Sep 17 00:00:00 2001 From: Joel Wurtz Date: Mon, 21 Jan 2019 18:16:40 +0100 Subject: [PATCH 20/58] Use only psr18 and not httplug anymore, fix psr7 implementation --- composer.json | 12 ++++----- src/Client.php | 12 ++------- src/Exception/BrokenPipeException.php | 2 -- src/Exception/ConnectionException.php | 2 -- src/Exception/InvalidRequestException.php | 2 -- src/Exception/NetworkException.php | 23 ++++++++++++++++ src/Exception/SSLConnectionException.php | 2 -- src/Exception/StreamException.php | 33 ++--------------------- src/Exception/TimeoutException.php | 2 +- src/ResponseReader.php | 9 ++++--- src/Stream.php | 12 ++++++--- tests/SocketHttpClientTest.php | 12 ++++----- tests/StreamTest.php | 9 ++++--- 13 files changed, 58 insertions(+), 74 deletions(-) create mode 100644 src/Exception/NetworkException.php diff --git a/composer.json b/composer.json index 8eb246e..f3e0bb1 100644 --- a/composer.json +++ b/composer.json @@ -10,17 +10,15 @@ ], "require": { "php": "^7.1", - "symfony/options-resolver": "^2.6 || ^3.0 || ^4.0", - "php-http/httplug": "^1.0 || ^2.0", - "php-http/message-factory": "^1.0.2 || ^2.0", - "php-http/discovery": "^1.0" + "nyholm/psr7": "^1.0", + "psr/http-client": "^1.0", + "symfony/options-resolver": "^2.6 || ^3.0 || ^4.0" }, "require-dev": { "friendsofphp/php-cs-fixer": "^2.2", - "guzzlehttp/psr7": "^1.2", - "php-http/client-integration-tests": "^1.0 || ^2.0", + "php-http/client-integration-tests": "^2.0", "php-http/message": "^1.0", - "php-http/client-common": "^1.0 || ^2.0" + "php-http/client-common": "^2.0" }, "provide": { "php-http/client-implementation": "1.0" diff --git a/src/Client.php b/src/Client.php index 22125c3..435ac18 100644 --- a/src/Client.php +++ b/src/Client.php @@ -6,8 +6,6 @@ use Http\Client\Socket\Exception\ConnectionException; use Http\Client\Socket\Exception\InvalidRequestException; use Http\Client\Socket\Exception\SSLConnectionException; -use Http\Discovery\MessageFactoryDiscovery; -use Http\Message\ResponseFactory; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; use Symfony\Component\OptionsResolver\Options; @@ -38,8 +36,7 @@ class Client implements HttpClient /** * Constructor. * - * @param ResponseFactory $responseFactory Response factory for creating response - * @param array $config { + * @param array $config { * * @var string $remote_socket Remote entrypoint (can be a tcp or unix domain address) * @var int $timeout Timeout before canceling request @@ -50,13 +47,8 @@ class Client implements HttpClient * @var int $ssl_method Crypto method for ssl/tls, see PHP doc, defaults to STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT * } */ - public function __construct(ResponseFactory $responseFactory = null, array $config = []) + public function __construct(array $config = []) { - if (null === $responseFactory) { - $responseFactory = MessageFactoryDiscovery::find(); - } - - $this->responseFactory = $responseFactory; $this->config = $this->configure($config); } diff --git a/src/Exception/BrokenPipeException.php b/src/Exception/BrokenPipeException.php index 58eb1bb..4cf4b62 100644 --- a/src/Exception/BrokenPipeException.php +++ b/src/Exception/BrokenPipeException.php @@ -2,8 +2,6 @@ namespace Http\Client\Socket\Exception; -use Http\Client\Exception\NetworkException; - class BrokenPipeException extends NetworkException { } diff --git a/src/Exception/ConnectionException.php b/src/Exception/ConnectionException.php index 88dc252..67b628d 100644 --- a/src/Exception/ConnectionException.php +++ b/src/Exception/ConnectionException.php @@ -2,8 +2,6 @@ namespace Http\Client\Socket\Exception; -use Http\Client\Exception\NetworkException; - class ConnectionException extends NetworkException { } diff --git a/src/Exception/InvalidRequestException.php b/src/Exception/InvalidRequestException.php index fe77314..227375b 100644 --- a/src/Exception/InvalidRequestException.php +++ b/src/Exception/InvalidRequestException.php @@ -2,8 +2,6 @@ namespace Http\Client\Socket\Exception; -use Http\Client\Exception\NetworkException; - class InvalidRequestException extends NetworkException { } diff --git a/src/Exception/NetworkException.php b/src/Exception/NetworkException.php new file mode 100644 index 0000000..f3a6b3c --- /dev/null +++ b/src/Exception/NetworkException.php @@ -0,0 +1,23 @@ +request = $request; + + parent::__construct($message, 0, $previous); + } + + public function getRequest(): RequestInterface + { + return $this->request; + } +} diff --git a/src/Exception/SSLConnectionException.php b/src/Exception/SSLConnectionException.php index 534a487..b1f85ef 100644 --- a/src/Exception/SSLConnectionException.php +++ b/src/Exception/SSLConnectionException.php @@ -2,8 +2,6 @@ namespace Http\Client\Socket\Exception; -use Http\Client\Exception\NetworkException; - class SSLConnectionException extends NetworkException { } diff --git a/src/Exception/StreamException.php b/src/Exception/StreamException.php index 6437cec..472472d 100644 --- a/src/Exception/StreamException.php +++ b/src/Exception/StreamException.php @@ -2,37 +2,8 @@ namespace Http\Client\Socket\Exception; -use Http\Client\Exception; -use Psr\Http\Message\RequestInterface; +use Psr\Http\Client\ClientExceptionInterface; -class StreamException extends \RuntimeException implements Exception +class StreamException extends \RuntimeException implements ClientExceptionInterface { - /** - * The request object. - * - * @var RequestInterface - */ - private $request; - - /** - * Accepts an optional request object as 4th param. - * - * @param string $message - * @param int $code - * @param Exception $previous - * @param RequestInterface $request - */ - public function __construct($message = null, $code = null, $previous = null, RequestInterface $request = null) - { - $this->request = $request; - parent::__construct($message, $code, $previous); - } - - /** - * @return \Psr\Http\Message\RequestInterface|null - */ - final public function getRequest() - { - return $this->request; - } } diff --git a/src/Exception/TimeoutException.php b/src/Exception/TimeoutException.php index 9d05407..a821ab5 100644 --- a/src/Exception/TimeoutException.php +++ b/src/Exception/TimeoutException.php @@ -2,6 +2,6 @@ namespace Http\Client\Socket\Exception; -class TimeoutException extends StreamException +class TimeoutException extends NetworkException { } diff --git a/src/ResponseReader.php b/src/ResponseReader.php index e631426..f8f94c5 100644 --- a/src/ResponseReader.php +++ b/src/ResponseReader.php @@ -5,6 +5,7 @@ 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; @@ -76,8 +77,8 @@ protected function readResponse(RequestInterface $request, $socket): ResponseInt : ''; } - $response = $this->responseFactory->createResponse($status, $reason, $responseHeaders, null, $protocol); - $stream = $this->createStream($socket, $response); + $response = new Response($status, $responseHeaders, null, $protocol, $reason); + $stream = $this->createStream($socket, $request, $response); return $response->withBody($stream); } @@ -87,7 +88,7 @@ protected function readResponse(RequestInterface $request, $socket): ResponseInt * * @param resource $socket */ - protected function createStream($socket, ResponseInterface $response): Stream + protected function createStream($socket, RequestInterface $request, ResponseInterface $response): Stream { $size = null; @@ -95,6 +96,6 @@ protected function createStream($socket, ResponseInterface $response): Stream $size = (int) $response->getHeaderLine('Content-Length'); } - return new Stream($socket, $size); + return new Stream($request, $socket, $size); } } diff --git a/src/Stream.php b/src/Stream.php index 00f2503..f98533a 100644 --- a/src/Stream.php +++ b/src/Stream.php @@ -4,6 +4,7 @@ use Http\Client\Socket\Exception\StreamException; use Http\Client\Socket\Exception\TimeoutException; +use Psr\Http\Message\RequestInterface; use Psr\Http\Message\StreamInterface; /** @@ -42,16 +43,21 @@ class Stream implements StreamInterface */ private $readed = 0; + /** + * @var RequestInterface request associated to this stream + */ + private $request; + /** * Create the stream. * * @param resource $socket - * @param int $size */ - public function __construct($socket, ?int $size = null) + public function __construct(RequestInterface $request, $socket, ?int $size = null) { $this->socket = $socket; $this->size = $size; + $this->request = $request; } /** @@ -175,7 +181,7 @@ public function read($length) $read = fread($this->socket, $length); if ($this->getMetadata('timed_out')) { - throw new TimeoutException('Stream timed out while reading data'); + throw new TimeoutException('Stream timed out while reading data', $this->request); } $this->readed += strlen($read); diff --git a/tests/SocketHttpClientTest.php b/tests/SocketHttpClientTest.php index e8981e6..3a957b3 100644 --- a/tests/SocketHttpClientTest.php +++ b/tests/SocketHttpClientTest.php @@ -12,7 +12,7 @@ public function createClient($options = []) { $messageFactory = new GuzzleMessageFactory(); - return new HttpMethodsClient(new SocketHttpClient($messageFactory, $options), $messageFactory); + return new HttpMethodsClient(new SocketHttpClient($options), $messageFactory); } public function testTcpSocketDomain() @@ -26,7 +26,7 @@ public function testTcpSocketDomain() } /** - * @expectedException \Http\Client\Exception\NetworkException + * @expectedException \Http\Client\Socket\Exception\NetworkException */ public function testNoRemote() { @@ -55,7 +55,7 @@ public function testRemoteInHostHeader() } /** - * @expectedException \Http\Client\Exception\NetworkException + * @expectedException \Http\Client\Socket\Exception\NetworkException */ public function testBrokenSocket() { @@ -95,7 +95,7 @@ public function testUnixSocketDomain() } /** - * @expectedException \Http\Client\Exception\NetworkException + * @expectedException \Http\Client\Socket\Exception\NetworkException */ public function testNetworkExceptionOnConnectError() { @@ -173,7 +173,7 @@ public function testInvalidSslConnectionWithClientCertificate() } /** - * @expectedException \Http\Client\Exception\NetworkException + * @expectedException \Http\Client\Socket\Exception\NetworkException */ public function testNetworkExceptionOnSslError() { @@ -184,7 +184,7 @@ public function testNetworkExceptionOnSslError() } /** - * @expectedException \Http\Client\Exception\NetworkException + * @expectedException \Http\Client\Socket\Exception\NetworkException */ public function testNetworkExceptionOnTimeout() { diff --git a/tests/StreamTest.php b/tests/StreamTest.php index 0bd21e6..11ad293 100644 --- a/tests/StreamTest.php +++ b/tests/StreamTest.php @@ -3,6 +3,7 @@ namespace Http\Client\Socket\Tests; use Http\Client\Socket\Stream; +use Nyholm\Psr7\Request; use PHPUnit\Framework\TestCase; class StreamTest extends TestCase @@ -13,7 +14,7 @@ public function createSocket($body, $useSize = true) fwrite($socket, $body); fseek($socket, 0); - return new Stream($socket, $useSize ? strlen($body) : null); + return new Stream(new Request('GET', '/'), $socket, $useSize ? strlen($body) : null); } public function testToString() @@ -56,7 +57,7 @@ public function testEof() $socket = fopen('php://memory', 'rw+'); fwrite($socket, 'Body'); fseek($socket, 0); - $stream = new Stream($socket); + $stream = new Stream(new Request('GET', '/'), $socket); $this->assertEquals('Body', $stream->getContents()); fwrite($socket, "\0"); @@ -116,7 +117,7 @@ public function testTimeout() $socket = fsockopen('php.net', 80); socket_set_timeout($socket, 0, 100); - $stream = new Stream($socket, 50); + $stream = new Stream(new Request('GET', '/'), $socket, 50); $stream->getContents(); } @@ -138,7 +139,7 @@ public function testClose() fwrite($socket, 'Body'); fseek($socket, 0); - $stream = new Stream($socket); + $stream = new Stream(new Request('GET', '/'), $socket); $stream->close(); $this->assertFalse(is_resource($socket)); From 9a182a9d66345d8571c1623c183ce49fd69e38ec Mon Sep 17 00:00:00 2001 From: Joel Wurtz Date: Sat, 16 Feb 2019 22:59:47 +0100 Subject: [PATCH 21/58] Fix tests --- composer.json | 2 +- tests/SocketClientFeatureTest.php | 3 ++- tests/SocketHttpAdapterTest.php | 3 ++- tests/server/ssl/file.srl | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/composer.json b/composer.json index f3e0bb1..19b3ff7 100644 --- a/composer.json +++ b/composer.json @@ -16,7 +16,7 @@ }, "require-dev": { "friendsofphp/php-cs-fixer": "^2.2", - "php-http/client-integration-tests": "^2.0", + "php-http/client-integration-tests": "dev-master", "php-http/message": "^1.0", "php-http/client-common": "^2.0" }, diff --git a/tests/SocketClientFeatureTest.php b/tests/SocketClientFeatureTest.php index d02dea8..aa614c4 100644 --- a/tests/SocketClientFeatureTest.php +++ b/tests/SocketClientFeatureTest.php @@ -4,10 +4,11 @@ use Http\Client\Tests\HttpFeatureTest; use Http\Client\Socket\Client as SocketHttpClient; +use Psr\Http\Client\ClientInterface; class SocketClientFeatureTest extends HttpFeatureTest { - protected function createClient() + protected function createClient(): ClientInterface { return new SocketHttpClient(); } diff --git a/tests/SocketHttpAdapterTest.php b/tests/SocketHttpAdapterTest.php index 143d4b4..7aa785b 100644 --- a/tests/SocketHttpAdapterTest.php +++ b/tests/SocketHttpAdapterTest.php @@ -4,13 +4,14 @@ use Http\Client\Tests\HttpClientTest; use Http\Client\Socket\Client as SocketHttpClient; +use Psr\Http\Client\ClientInterface; class SocketHttpAdapterTest extends HttpClientTest { /** * {@inheritdoc} */ - protected function createHttpAdapter() + protected function createHttpAdapter(): ClientInterface { return new SocketHttpClient(); } diff --git a/tests/server/ssl/file.srl b/tests/server/ssl/file.srl index f5c8955..a787364 100644 --- a/tests/server/ssl/file.srl +++ b/tests/server/ssl/file.srl @@ -1 +1 @@ -32 +34 From 927aabb3b51e3ddc0be68da7129bccdf98355bd5 Mon Sep 17 00:00:00 2001 From: Joel Wurtz Date: Sat, 16 Feb 2019 23:09:57 +0100 Subject: [PATCH 22/58] Readd httplug deps as we use interface in Client --- composer.json | 1 + src/Client.php | 10 +++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 19b3ff7..59d5098 100644 --- a/composer.json +++ b/composer.json @@ -11,6 +11,7 @@ "require": { "php": "^7.1", "nyholm/psr7": "^1.0", + "php-http/httplug": "^2.0", "psr/http-client": "^1.0", "symfony/options-resolver": "^2.6 || ^3.0 || ^4.0" }, diff --git a/src/Client.php b/src/Client.php index 435ac18..37df5b9 100644 --- a/src/Client.php +++ b/src/Client.php @@ -47,8 +47,16 @@ class Client implements HttpClient * @var int $ssl_method Crypto method for ssl/tls, see PHP doc, defaults to STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT * } */ - public function __construct(array $config = []) + public function __construct($config1 = [], $config2 = null, array $config = []) { + if (\is_array($config1)) { + $this->config = $this->configure($config1); + + return; + } + + @trigger_error(E_USER_DEPRECATED, 'Passing a Psr\Http\Message\ResponseFactoryInterface and a Psr\Http\Message\StreamFactoryInterface to SocketClient is deprecated, and will be removed in 3.0, you should only pass config options.'); + $this->config = $this->configure($config); } From fddb55d32b035783948bf934fec4ef1ff1022bd7 Mon Sep 17 00:00:00 2001 From: Joel Wurtz Date: Mon, 21 Jan 2019 16:37:32 +0100 Subject: [PATCH 23/58] Add async support --- .travis.yml | 7 ++++++- composer.json | 1 + src/Client.php | 8 ++++++++ src/ResponseReader.php | 2 +- tests/AsyncTest.php | 37 ++++++++++++++++++++++++++++++++++ tests/SocketHttpClientTest.php | 11 ++++++---- 6 files changed, 60 insertions(+), 6 deletions(-) create mode 100644 tests/AsyncTest.php diff --git a/.travis.yml b/.travis.yml index 28a8944..1344f3f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -26,12 +26,17 @@ matrix: env: COMPOSER_FLAGS="--prefer-stable --prefer-lowest" COVERAGE=true TEST_COMMAND="composer test-ci" - php: 7.2 env: COMPOSER_FLAGS="--prefer-stable --prefer-lowest" + - php: 7.3 + env: USE_ASYNC=true before_install: - travis_retry composer self-update + - if [[ "$USE_ASYNC" = true ]]; then git clone https://github.com/concurrent-php/ext-async.git /tmp/async; fi + - if [[ "$USE_ASYNC" = true ]]; then cd /tmp/async && phpize && ./configure && sudo make install; fi + - if [[ "$USE_ASYNC" = true ]]; then echo "extension = async.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini && php -m && cd $TRAVIS_BUILD_DIR; fi install: - - travis_retry composer update ${COMPOSER_FLAGS} --prefer-source --no-interaction + - travis_retry composer update ${COMPOSER_FLAGS} --prefer-source --no-interaction --ignore-platform-reqs before_script: - vendor/bin/http_test_server > /dev/null 2>&1 & diff --git a/composer.json b/composer.json index 59d5098..db2eccf 100644 --- a/composer.json +++ b/composer.json @@ -16,6 +16,7 @@ "symfony/options-resolver": "^2.6 || ^3.0 || ^4.0" }, "require-dev": { + "concurrent-php/async-api": "dev-master", "friendsofphp/php-cs-fixer": "^2.2", "php-http/client-integration-tests": "dev-master", "php-http/message": "^1.0", diff --git a/src/Client.php b/src/Client.php index 37df5b9..60b6f4e 100644 --- a/src/Client.php +++ b/src/Client.php @@ -33,6 +33,8 @@ class Client implements HttpClient 'ssl_method' => STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT, ]; + private $hasAsync; + /** * Constructor. * @@ -49,6 +51,8 @@ class Client implements HttpClient */ public function __construct($config1 = [], $config2 = null, array $config = []) { + $this->hasAsync = PHP_VERSION_ID >= 70300 && \extension_loaded('async'); + if (\is_array($config1)) { $this->config = $this->configure($config1); @@ -182,6 +186,10 @@ private function determineRemoteFromRequest(RequestInterface $request) $endpoint = $request->getHeaderLine('Host'); } + if ($this->hasAsync) { + return sprintf('async-tcp://%s', $endpoint); + } + return sprintf('tcp://%s', $endpoint); } } diff --git a/src/ResponseReader.php b/src/ResponseReader.php index f8f94c5..97bd314 100644 --- a/src/ResponseReader.php +++ b/src/ResponseReader.php @@ -46,7 +46,7 @@ protected function readResponse(RequestInterface $request, $socket): ResponseInt $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', null, null, $request); + throw new TimeoutException('Error while reading response, stream timed out', $request, null); } $parts = explode(' ', array_shift($headers), 3); diff --git a/tests/AsyncTest.php b/tests/AsyncTest.php new file mode 100644 index 0000000..58f62d8 --- /dev/null +++ b/tests/AsyncTest.php @@ -0,0 +1,37 @@ +markTestSkipped('Test need async extension'); + } + + $client = new Client(); + $request = new Request('GET', 'https://httpbin.org/delay/1'); + + $timeStart = microtime(true); + $task1 = Task::async(function () use ($request, $client) { + return $client->sendRequest($request); + }); + $task2 = Task::async(function () use ($request, $client) { + return $client->sendRequest($request); + }); + + [$response1, $response2] = Task::await(all([$task1, $task2])); + $timeTaken = microtime(true) - $timeStart; + + self::assertLessThan(2, $timeTaken); + self::assertEquals(200, $response1->getStatusCode()); + self::assertEquals(200, $response2->getStatusCode()); + } +} diff --git a/tests/SocketHttpClientTest.php b/tests/SocketHttpClientTest.php index 3a957b3..cfec8ce 100644 --- a/tests/SocketHttpClientTest.php +++ b/tests/SocketHttpClientTest.php @@ -68,6 +68,8 @@ public function testSslRemoteInUri() { $this->startServer('tcp-ssl-server'); $client = $this->createClient([ + 'remote_socket' => 'tcp://127.0.0.1:19999', + 'ssl' => true, 'stream_context_options' => [ 'ssl' => [ 'peer_name' => 'socket-adapter', @@ -75,7 +77,7 @@ public function testSslRemoteInUri() ], ], ]); - $response = $client->get('https://127.0.0.1:19999/', []); + $response = $client->get('/', []); $this->assertInstanceOf('Psr\Http\Message\ResponseInterface', $response); $this->assertEquals(200, $response->getStatusCode()); @@ -184,11 +186,12 @@ public function testNetworkExceptionOnSslError() } /** - * @expectedException \Http\Client\Socket\Exception\NetworkException + * @expectedException \Http\Client\Socket\Exception\TimeoutException */ public function testNetworkExceptionOnTimeout() { - $client = $this->createClient(['timeout' => 10]); - $client->get('http://php.net', []); + $client = $this->createClient(['timeout' => 1]); + $response = $client->get('https://php.net', []); + $response->getBody()->getContents(); } } From ccb26020087dabff7d4c998c2a93c884f5d16fbb Mon Sep 17 00:00:00 2001 From: Joel Wurtz Date: Sat, 16 Feb 2019 23:30:33 +0100 Subject: [PATCH 24/58] Update changelog with last changes --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5da849c..f92a44d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## 2.0.0 (unreleased) + * Remove response and stream factory, use direct implementation of nyholm/psr7 + * Async support with ext-async extension: see https://github.com/concurrent-php/ext-async * PSR18 and HTTPlug 2 support * Remove support for php 5.5, 5.6 and 7.0 * SSL Method now defaults to `STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT` From 3bdc0a2fa0e5fd4ada9c8914b4fdcff0a29c5aa3 Mon Sep 17 00:00:00 2001 From: Joel Wurtz Date: Wed, 20 Feb 2019 10:12:23 +0100 Subject: [PATCH 25/58] Fix travis file --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 1344f3f..8b584a9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,7 +24,7 @@ matrix: include: - php: 7.1 env: COMPOSER_FLAGS="--prefer-stable --prefer-lowest" COVERAGE=true TEST_COMMAND="composer test-ci" - - php: 7.2 + - php: 7.2 env: COMPOSER_FLAGS="--prefer-stable --prefer-lowest" - php: 7.3 env: USE_ASYNC=true From 6717d0cc3d00657cdbd00473af023ff7a74fbd7d Mon Sep 17 00:00:00 2001 From: Joel Wurtz Date: Wed, 20 Feb 2019 10:26:32 +0100 Subject: [PATCH 26/58] Use correct exception on connect --- src/Client.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Client.php b/src/Client.php index 60b6f4e..9c5a3d6 100644 --- a/src/Client.php +++ b/src/Client.php @@ -6,6 +6,7 @@ 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\ResponseInterface; use Symfony\Component\OptionsResolver\Options; @@ -116,6 +117,10 @@ protected function createSocket(RequestInterface $request, string $remote, bool $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); } From b34385cdc6681191de363920bee10165613c0f38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Javier=20Mart=C3=ADn=20Torres?= Date: Sat, 23 Nov 2019 14:37:15 +0100 Subject: [PATCH 27/58] Add Symfony 5 version constraint (#46) --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index db2eccf..6a7dbdd 100644 --- a/composer.json +++ b/composer.json @@ -13,7 +13,7 @@ "nyholm/psr7": "^1.0", "php-http/httplug": "^2.0", "psr/http-client": "^1.0", - "symfony/options-resolver": "^2.6 || ^3.0 || ^4.0" + "symfony/options-resolver": "^2.6 || ^3.0 || ^4.0 || ^5.0" }, "require-dev": { "concurrent-php/async-api": "dev-master", From 7770fd30f98f33c97de9632c79266463a69e8fe0 Mon Sep 17 00:00:00 2001 From: Graham Campbell Date: Wed, 11 Dec 2019 16:29:11 +0000 Subject: [PATCH 28/58] Support symfony 5 (#45) From b7008a59b8d605c0fbcde590133eab37a9d8ea5e Mon Sep 17 00:00:00 2001 From: Joel Wurtz Date: Tue, 22 Sep 2020 08:23:37 +0200 Subject: [PATCH 29/58] Remove support for async extension --- .travis.yml | 8 ++------ CHANGELOG.md | 4 ++-- composer.json | 3 ++- src/Client.php | 8 -------- tests/AsyncTest.php | 37 ------------------------------------- 5 files changed, 6 insertions(+), 54 deletions(-) delete mode 100644 tests/AsyncTest.php diff --git a/.travis.yml b/.travis.yml index 8b584a9..25f354a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,9 +7,10 @@ cache: - $HOME/.composer/cache php: - - 7.1 - 7.2 - 7.3 + - 7.4 + - 8.0 env: global: @@ -26,14 +27,9 @@ matrix: env: COMPOSER_FLAGS="--prefer-stable --prefer-lowest" COVERAGE=true TEST_COMMAND="composer test-ci" - php: 7.2 env: COMPOSER_FLAGS="--prefer-stable --prefer-lowest" - - php: 7.3 - env: USE_ASYNC=true before_install: - travis_retry composer self-update - - if [[ "$USE_ASYNC" = true ]]; then git clone https://github.com/concurrent-php/ext-async.git /tmp/async; fi - - if [[ "$USE_ASYNC" = true ]]; then cd /tmp/async && phpize && ./configure && sudo make install; fi - - if [[ "$USE_ASYNC" = true ]]; then echo "extension = async.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini && php -m && cd $TRAVIS_BUILD_DIR; fi install: - travis_retry composer update ${COMPOSER_FLAGS} --prefer-source --no-interaction --ignore-platform-reqs diff --git a/CHANGELOG.md b/CHANGELOG.md index f92a44d..b4ec355 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Change Log -## 2.0.0 (unreleased) +## 2.0.0 * Remove response and stream factory, use direct implementation of nyholm/psr7 * Async support with ext-async extension: see https://github.com/concurrent-php/ext-async @@ -20,7 +20,7 @@ * `ConnectionException` * `InvalidRequestException` * `SSLConnectionException` - + ## 1.2.0 * Dropped PHP 5.4 support diff --git a/composer.json b/composer.json index 6a7dbdd..eece4a0 100644 --- a/composer.json +++ b/composer.json @@ -23,7 +23,8 @@ "php-http/client-common": "^2.0" }, "provide": { - "php-http/client-implementation": "1.0" + "php-http/client-implementation": "1.0", + "psr/http-client": "1.0" }, "autoload": { "psr-4": { diff --git a/src/Client.php b/src/Client.php index 9c5a3d6..188026c 100644 --- a/src/Client.php +++ b/src/Client.php @@ -34,8 +34,6 @@ class Client implements HttpClient 'ssl_method' => STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT, ]; - private $hasAsync; - /** * Constructor. * @@ -52,8 +50,6 @@ class Client implements HttpClient */ public function __construct($config1 = [], $config2 = null, array $config = []) { - $this->hasAsync = PHP_VERSION_ID >= 70300 && \extension_loaded('async'); - if (\is_array($config1)) { $this->config = $this->configure($config1); @@ -191,10 +187,6 @@ private function determineRemoteFromRequest(RequestInterface $request) $endpoint = $request->getHeaderLine('Host'); } - if ($this->hasAsync) { - return sprintf('async-tcp://%s', $endpoint); - } - return sprintf('tcp://%s', $endpoint); } } diff --git a/tests/AsyncTest.php b/tests/AsyncTest.php deleted file mode 100644 index 58f62d8..0000000 --- a/tests/AsyncTest.php +++ /dev/null @@ -1,37 +0,0 @@ -markTestSkipped('Test need async extension'); - } - - $client = new Client(); - $request = new Request('GET', 'https://httpbin.org/delay/1'); - - $timeStart = microtime(true); - $task1 = Task::async(function () use ($request, $client) { - return $client->sendRequest($request); - }); - $task2 = Task::async(function () use ($request, $client) { - return $client->sendRequest($request); - }); - - [$response1, $response2] = Task::await(all([$task1, $task2])); - $timeTaken = microtime(true) - $timeStart; - - self::assertLessThan(2, $timeTaken); - self::assertEquals(200, $response1->getStatusCode()); - self::assertEquals(200, $response2->getStatusCode()); - } -} From 1e3b3b9dab3f430207c13cec5eb1901df3b56af9 Mon Sep 17 00:00:00 2001 From: Joel Wurtz Date: Tue, 22 Sep 2020 08:25:39 +0200 Subject: [PATCH 30/58] Fix cs --- src/Client.php | 1 - tests/SocketClientFeatureTest.php | 2 +- tests/SocketHttpAdapterTest.php | 2 +- tests/SocketHttpClientTest.php | 2 +- 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Client.php b/src/Client.php index 188026c..9480549 100644 --- a/src/Client.php +++ b/src/Client.php @@ -167,7 +167,6 @@ protected function configure(array $config = []) /** * Return remote socket from the request. * - * * @throws InvalidRequestException When no remote can be determined from the request * * @return string diff --git a/tests/SocketClientFeatureTest.php b/tests/SocketClientFeatureTest.php index aa614c4..e6e698b 100644 --- a/tests/SocketClientFeatureTest.php +++ b/tests/SocketClientFeatureTest.php @@ -2,8 +2,8 @@ namespace Http\Client\Socket\Tests; -use Http\Client\Tests\HttpFeatureTest; use Http\Client\Socket\Client as SocketHttpClient; +use Http\Client\Tests\HttpFeatureTest; use Psr\Http\Client\ClientInterface; class SocketClientFeatureTest extends HttpFeatureTest diff --git a/tests/SocketHttpAdapterTest.php b/tests/SocketHttpAdapterTest.php index 7aa785b..f95b28e 100644 --- a/tests/SocketHttpAdapterTest.php +++ b/tests/SocketHttpAdapterTest.php @@ -2,8 +2,8 @@ namespace Http\Client\Socket\Tests; -use Http\Client\Tests\HttpClientTest; use Http\Client\Socket\Client as SocketHttpClient; +use Http\Client\Tests\HttpClientTest; use Psr\Http\Client\ClientInterface; class SocketHttpAdapterTest extends HttpClientTest diff --git a/tests/SocketHttpClientTest.php b/tests/SocketHttpClientTest.php index cfec8ce..cca4fd6 100644 --- a/tests/SocketHttpClientTest.php +++ b/tests/SocketHttpClientTest.php @@ -3,8 +3,8 @@ namespace Http\Client\Socket\Tests; use Http\Client\Common\HttpMethodsClient; -use Http\Message\MessageFactory\GuzzleMessageFactory; use Http\Client\Socket\Client as SocketHttpClient; +use Http\Message\MessageFactory\GuzzleMessageFactory; class SocketHttpClientTest extends BaseTestCase { From 3c76d7b4d454a0b3a6b454fc831f41a7ea1eea5a Mon Sep 17 00:00:00 2001 From: Joel Wurtz Date: Tue, 22 Sep 2020 08:28:02 +0200 Subject: [PATCH 31/58] Remove php 7.1 for coverage --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 25f354a..c447f5c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,7 +23,7 @@ branches: matrix: fast_finish: true include: - - php: 7.1 + - php: 7.2 env: COMPOSER_FLAGS="--prefer-stable --prefer-lowest" COVERAGE=true TEST_COMMAND="composer test-ci" - php: 7.2 env: COMPOSER_FLAGS="--prefer-stable --prefer-lowest" From e27facb5ac65aab27aa202e59b0cf448dbf05d9c Mon Sep 17 00:00:00 2001 From: Joel Wurtz Date: Tue, 22 Sep 2020 08:30:26 +0200 Subject: [PATCH 32/58] Remove async package --- composer.json | 1 - 1 file changed, 1 deletion(-) diff --git a/composer.json b/composer.json index eece4a0..178985f 100644 --- a/composer.json +++ b/composer.json @@ -16,7 +16,6 @@ "symfony/options-resolver": "^2.6 || ^3.0 || ^4.0 || ^5.0" }, "require-dev": { - "concurrent-php/async-api": "dev-master", "friendsofphp/php-cs-fixer": "^2.2", "php-http/client-integration-tests": "dev-master", "php-http/message": "^1.0", From d730acdc248f981c2d3c7c8854f5cda4d31bd1cc Mon Sep 17 00:00:00 2001 From: Joel Wurtz Date: Tue, 22 Sep 2020 08:36:10 +0200 Subject: [PATCH 33/58] Remove feature test and 8.0 php version --- .travis.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index c447f5c..deadd3c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,7 +10,6 @@ php: - 7.2 - 7.3 - 7.4 - - 8.0 env: global: @@ -40,7 +39,6 @@ before_script: script: - cd ./tests/server/ssl && ./generate.sh && pwd && ls -la && cd ../../../ - $TEST_COMMAND - - ./vendor/bin/phpunit tests/SocketClientFeatureTest.php --printer Http\\Client\\Tests\\FeatureTestListener || echo "" after_success: - if [[ "$COVERAGE" = true ]]; then wget https://scrutinizer-ci.com/ocular.phar; fi From 4cd5462dd2d3dbd4ff863ade089be83cc730b744 Mon Sep 17 00:00:00 2001 From: Joel Wurtz Date: Thu, 24 Sep 2020 11:01:25 +0200 Subject: [PATCH 34/58] Fix changelog with last changes on 2.0.0 --- CHANGELOG.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b4ec355..d493d4a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,9 +3,8 @@ ## 2.0.0 * Remove response and stream factory, use direct implementation of nyholm/psr7 - * Async support with ext-async extension: see https://github.com/concurrent-php/ext-async * PSR18 and HTTPlug 2 support - * Remove support for php 5.5, 5.6 and 7.0 + * 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 From 9d1dba00e6e95e0965750f27896c44f09336190b Mon Sep 17 00:00:00 2001 From: Dennis Riehle Date: Mon, 12 Oct 2020 11:33:12 +0200 Subject: [PATCH 35/58] fixed wrong call of trigger_error() --- src/Client.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Client.php b/src/Client.php index 9480549..902e3f9 100644 --- a/src/Client.php +++ b/src/Client.php @@ -56,7 +56,7 @@ public function __construct($config1 = [], $config2 = null, array $config = []) return; } - @trigger_error(E_USER_DEPRECATED, 'Passing a Psr\Http\Message\ResponseFactoryInterface and a Psr\Http\Message\StreamFactoryInterface to SocketClient is deprecated, and will be removed in 3.0, you should only pass config options.'); + @trigger_error('Passing a Psr\Http\Message\ResponseFactoryInterface and a Psr\Http\Message\StreamFactoryInterface to SocketClient is deprecated, and will be removed in 3.0, you should only pass config options.', E_USER_DEPRECATED); $this->config = $this->configure($config); } From 3db137a526c467b52d8d743f5c2f7fdcc00362ef Mon Sep 17 00:00:00 2001 From: Joel Wurtz Date: Fri, 16 Oct 2020 15:31:02 +0200 Subject: [PATCH 36/58] Prepare 2.0.1 --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d493d4a..8a35de0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Change Log +## 2.0.1 + + * Fix wrong call to trigger_error + ## 2.0.0 * Remove response and stream factory, use direct implementation of nyholm/psr7 From feb7b0b215f5c08514b6a103c19dcc3e0e308028 Mon Sep 17 00:00:00 2001 From: Christophe Coevoet Date: Thu, 22 Oct 2020 10:26:36 +0200 Subject: [PATCH 37/58] Fix broken provide rule (#50) This package provides the client implementation. But it does not provide the interfaces from psr/http-client --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 178985f..3b419ce 100644 --- a/composer.json +++ b/composer.json @@ -23,7 +23,7 @@ }, "provide": { "php-http/client-implementation": "1.0", - "psr/http-client": "1.0" + "psr/http-client-implementation": "1.0" }, "autoload": { "psr-4": { From 8c7991b79a4a6e7c5cbf8df3d592fa580e5c7bf5 Mon Sep 17 00:00:00 2001 From: Tobias Nyholm Date: Thu, 22 Oct 2020 10:53:40 +0200 Subject: [PATCH 38/58] Fixing broken tests (#51) * Fixing broken tests * Bump deps * Use PHPunit 8.5 --- composer.json | 11 ++++++----- tests/BaseTestCase.php | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/composer.json b/composer.json index 3b419ce..3d0eefa 100644 --- a/composer.json +++ b/composer.json @@ -10,16 +10,17 @@ ], "require": { "php": "^7.1", - "nyholm/psr7": "^1.0", + "nyholm/psr7": "^1.3", "php-http/httplug": "^2.0", "psr/http-client": "^1.0", - "symfony/options-resolver": "^2.6 || ^3.0 || ^4.0 || ^5.0" + "symfony/options-resolver": "^2.6 || ^3.4 || ^4.4 || ^5.0" }, "require-dev": { "friendsofphp/php-cs-fixer": "^2.2", - "php-http/client-integration-tests": "dev-master", - "php-http/message": "^1.0", - "php-http/client-common": "^2.0" + "php-http/client-integration-tests": "^3.0", + "php-http/message": "^1.9", + "php-http/client-common": "^2.3", + "phpunit/phpunit": "^8.5.8" }, "provide": { "php-http/client-implementation": "1.0", diff --git a/tests/BaseTestCase.php b/tests/BaseTestCase.php index a6ffda7..43d26a6 100644 --- a/tests/BaseTestCase.php +++ b/tests/BaseTestCase.php @@ -28,7 +28,7 @@ public function stopServer($name) } } - public function tearDown() + public function tearDown(): void { foreach (array_keys($this->servers) as $name) { $this->stopServer($name); From a53d9c25e34ae3c39d07158b7ae7bf25b83b300a Mon Sep 17 00:00:00 2001 From: Tobias Nyholm Date: Thu, 22 Oct 2020 11:00:03 +0200 Subject: [PATCH 39/58] Prepare for 2.0.2 (#52) --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a35de0..3d78f50 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Change Log +## 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 From d32bf4e575386436e4590a922fb563b92862eb78 Mon Sep 17 00:00:00 2001 From: Rodrigo Aguilera Date: Mon, 9 Nov 2020 11:10:37 +0100 Subject: [PATCH 40/58] Add php 8 support. --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 3d0eefa..a876b15 100644 --- a/composer.json +++ b/composer.json @@ -9,7 +9,7 @@ } ], "require": { - "php": "^7.1", + "php": ">=7.1 ", "nyholm/psr7": "^1.3", "php-http/httplug": "^2.0", "psr/http-client": "^1.0", From de0cd1a4751cc7ae82236cc75b9ca9f24aef9237 Mon Sep 17 00:00:00 2001 From: Rodrigo Aguilera Date: Mon, 9 Nov 2020 11:12:53 +0100 Subject: [PATCH 41/58] Update changelog. --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d78f50..371e057 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Change Log +## 2.0.3 + + * Add php 8 compatibility + ## 2.0.2 * Fixed composer "provide" section to say that we provide `psr/http-client-implementation` From 35338fc220b34cd9aed4bc7b78c24b1b5a58e6df Mon Sep 17 00:00:00 2001 From: Flavio Heleno Date: Tue, 29 Dec 2020 20:13:06 -0300 Subject: [PATCH 42/58] (*) Added instructions for generating ssl files on macOS --- README.md | 6 ++++-- ssl-macOS.md | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 ssl-macOS.md diff --git a/README.md b/README.md index d5620f4..43bb988 100644 --- a/README.md +++ b/README.md @@ -31,11 +31,13 @@ $ ./vendor/bin/http_test_server > /dev/null 2>&1 & Then generate ssh certificates: ```bash -$ cd ./tests/server/ssl +$ cd ./tests/server/ssl $ ./generate.sh -$ cd ../../../ +$ cd ../../../ ``` +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 diff --git a/ssl-macOS.md b/ssl-macOS.md new file mode 100644 index 0000000..c37468d --- /dev/null +++ b/ssl-macOS.md @@ -0,0 +1,58 @@ +# 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. From 93df2cc24c1b59a40ab7fc9d416e32964cdcc839 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Richard=20D=C3=A9loge?= Date: Mon, 15 Aug 2022 08:26:42 +0200 Subject: [PATCH 43/58] Support SF6 (#61) * Support SF6 * Support SF6 --- .travis.yml | 2 ++ CHANGELOG.md | 4 ++++ composer.json | 6 +++--- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index deadd3c..89b97e5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,6 +10,8 @@ php: - 7.2 - 7.3 - 7.4 + - 8.0 + - 8.1 env: global: diff --git a/CHANGELOG.md b/CHANGELOG.md index 371e057..71467bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Change Log +## 2.0.4 + + * Add Symfony 6 compatibility + ## 2.0.3 * Add php 8 compatibility diff --git a/composer.json b/composer.json index a876b15..c1b66da 100644 --- a/composer.json +++ b/composer.json @@ -13,14 +13,14 @@ "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" + "symfony/options-resolver": "^2.6 || ^3.4 || ^4.4 || ^5.0 || ^6.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^2.2", + "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.8" + "phpunit/phpunit": "^8.5.8 || ~9.5" }, "provide": { "php-http/client-implementation": "1.0", From ee4c5553a8ae3b22b635acbdba33020fb189261f Mon Sep 17 00:00:00 2001 From: David Buchmann Date: Sun, 31 Jul 2022 12:33:26 +0200 Subject: [PATCH 44/58] switch to github actions --- .github/CONTRIBUTING.md | 3 + .github/ISSUE_TEMPLATE.md | 27 +++++++++ .github/PULL_REQUEST_TEMPLATE.md | 43 ++++++++++++++ .github/workflows/.editorconfig | 2 + .github/workflows/checks.yml | 30 ++++++++++ .github/workflows/ci.yml | 99 ++++++++++++++++++++++++++++++++ .github/workflows/static.yml | 36 ++++++++++++ .gitignore | 14 ++--- .php-cs-fixer.dist.php | 16 ++++++ .php_cs | 22 ------- .scrutinizer.yml | 8 --- .travis.yml | 47 --------------- composer.json | 2 +- phpstan.neon.dist | 7 +++ 14 files changed, 271 insertions(+), 85 deletions(-) create mode 100644 .github/CONTRIBUTING.md create mode 100644 .github/ISSUE_TEMPLATE.md create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 .github/workflows/.editorconfig create mode 100644 .github/workflows/checks.yml create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/static.yml create mode 100644 .php-cs-fixer.dist.php delete mode 100644 .php_cs delete mode 100644 .scrutinizer.yml delete mode 100644 .travis.yml create mode 100644 phpstan.neon.dist diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md new file mode 100644 index 0000000..fb288d9 --- /dev/null +++ b/.github/CONTRIBUTING.md @@ -0,0 +1,3 @@ +# Contributing + +Please see our [contributing guide](http://docs.php-http.org/en/latest/development/contributing.html). diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 0000000..d4ecf20 --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,27 @@ +| Q | A +| ------------ | --- +| Bug? | no|yes +| New Feature? | no|yes +| Version | Specific version or SHA of a commit + + +#### Actual Behavior + +What is the actual behavior? + + +#### Expected Behavior + +What is the behavior you expect? + + +#### Steps to Reproduce + +What are the steps to reproduce this bug? Please add code examples, +screenshots or links to GitHub repositories that reproduce the problem. + + +#### Possible Solutions + +If you have already ideas how to solve the issue, add them here. +(remove this section if not needed) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..323987b --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,43 @@ +| Q | A +| --------------- | --- +| Bug fix? | no|yes +| New feature? | no|yes +| BC breaks? | no|yes +| Deprecations? | no|yes +| Related tickets | fixes #X, partially #Y, mentioned in #Z +| Documentation | if this is a new feature, link to pull request in https://github.com/php-http/documentation that adds relevant documentation +| License | MIT + + +#### What's in this PR? + +Explain what the changes in this PR do. + + +#### Why? + +Which problem does the PR fix? (remove this section if you linked an issue above) + + +#### Example Usage + +``` php +// If you added new features, show examples of how to use them here +// (remove this section if not a new feature) + +$foo = new Foo(); + +// Now we can do +$foo->doSomething(); +``` + + +#### Checklist + +- [ ] Updated CHANGELOG.md to describe BC breaks / deprecations | new feature | bugfix +- [ ] Documentation pull request created (if not simply a bugfix) + + +#### To Do + +- [ ] If the PR is not complete but you want to discuss the approach, list what remains to be done here diff --git a/.github/workflows/.editorconfig b/.github/workflows/.editorconfig new file mode 100644 index 0000000..7bd3346 --- /dev/null +++ b/.github/workflows/.editorconfig @@ -0,0 +1,2 @@ +[*.yml] +indent_size = 2 diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml new file mode 100644 index 0000000..3a3dfee --- /dev/null +++ b/.github/workflows/checks.yml @@ -0,0 +1,30 @@ +name: Checks + +on: + push: + branches: + - master + pull_request: + +jobs: + composer-normalize: + name: Composer Normalize + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Composer normalize + uses: docker://ergebnis/composer-normalize-action + + roave-bc-check: + name: Roave BC Check + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Roave BC Check + uses: docker://nyholm/roave-bc-check-ga diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..14df2f2 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,99 @@ +name: CI + +on: + push: + branches: + - master + pull_request: + +jobs: + supported-versions-matrix: + name: Supported Versions Matrix + runs-on: ubuntu-latest + outputs: + version: ${{ steps.supported-versions-matrix.outputs.version }} + steps: + - uses: actions/checkout@v3 + - id: supported-versions-matrix + uses: WyriHaximus/github-action-composer-php-versions-in-range@v1 + latest: + name: PHP ${{ matrix.php }} Latest + runs-on: ubuntu-latest + needs: + - supported-versions-matrix + strategy: + matrix: + php: ${{ fromJson(needs.supported-versions-matrix.outputs.version) }} + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + tools: composer + coverage: none + + - name: Install dependencies + run: composer update --prefer-dist --no-interaction --no-progress + + - name: boot test server + run: vendor/bin/http_test_server > /dev/null 2>&1 & + + - name: Execute tests + run: composer test + + lowest: + name: PHP ${{ matrix.php }} Lowest + runs-on: ubuntu-latest + needs: + - supported-versions-matrix + strategy: + matrix: + php: ${{ fromJson(needs.supported-versions-matrix.outputs.version) }} + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + tools: composer + coverage: none + + - name: Install dependencies + run: composer update --prefer-dist --prefer-stable --prefer-lowest --no-interaction --no-progress + + - name: boot test server + run: vendor/bin/http_test_server > /dev/null 2>&1 & + + - name: Execute tests + run: composer test + + coverage: + name: Code Coverage + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: 7.4 + tools: composer + coverage: xdebug + + - name: Install dependencies + run: composer update --prefer-dist --no-interaction --no-progress + + - name: generate ssl + run: cd ./tests/server/ssl && ./generate.sh && pwd && ls -la && cd ../../../ + + - name: Execute tests + run: composer test-ci diff --git a/.github/workflows/static.yml b/.github/workflows/static.yml new file mode 100644 index 0000000..f4aa6f6 --- /dev/null +++ b/.github/workflows/static.yml @@ -0,0 +1,36 @@ +name: Static analysis + +on: + push: + branches: + - master + pull_request: + +jobs: + phpstan: + name: PHPStan + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: PHPStan + uses: docker://oskarstark/phpstan-ga + env: + REQUIRE_DEV: true + with: + args: analyze --no-progress + + php-cs-fixer: + name: PHP-CS-Fixer + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: PHP-CS-Fixer + uses: docker://oskarstark/php-cs-fixer-ga + with: + args: --dry-run --diff diff --git a/.gitignore b/.gitignore index 33793eb..322ef74 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,10 @@ -.puli/ -build/ -vendor/ -composer.lock -phpspec.yml -phpunit.xml -/.php_cs.cache +/.php-cs-fixer.cache +/.puli/ +/build/ +/composer.lock +/phpstan.neon +/phpunit.xml /tests/server/ssl/*.pem /tests/server/ssl/*.key /tests/server/ssl/*.req +/vendor/ diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php new file mode 100644 index 0000000..bcb9a69 --- /dev/null +++ b/.php-cs-fixer.dist.php @@ -0,0 +1,16 @@ +exclude('vendor') + ->in(__DIR__) +; + +$config = (new PhpCsFixer\Config()) + ->setRiskyAllowed(true) + ->setRules([ + '@Symfony' => true, + ]) + ->setFinder($finder) +; + +return $config; diff --git a/.php_cs b/.php_cs deleted file mode 100644 index 55d9b0b..0000000 --- a/.php_cs +++ /dev/null @@ -1,22 +0,0 @@ -setRules([ - '@PSR2' => true, - '@Symfony' => true, - 'array_syntax' => [ - 'syntax' => 'short', - ], - 'no_empty_phpdoc' => true, - 'no_superfluous_phpdoc_tags' => true, -]); - -$finder = PhpCsFixer\Finder::create(); -$finder->in([ - 'src', - 'tests' -]); - -$config->setFinder($finder); - -return $config; diff --git a/.scrutinizer.yml b/.scrutinizer.yml deleted file mode 100644 index 46a7560..0000000 --- a/.scrutinizer.yml +++ /dev/null @@ -1,8 +0,0 @@ -filter: - paths: [src/*] -checks: - php: - code_rating: true - duplication: true -tools: - external_code_coverage: true diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 89b97e5..0000000 --- a/.travis.yml +++ /dev/null @@ -1,47 +0,0 @@ -language: php - -sudo: required - -cache: - directories: - - $HOME/.composer/cache - -php: - - 7.2 - - 7.3 - - 7.4 - - 8.0 - - 8.1 - -env: - global: - - TEST_COMMAND="composer test" - -branches: - except: - - /^analysis-.*$/ - -matrix: - fast_finish: true - include: - - php: 7.2 - env: COMPOSER_FLAGS="--prefer-stable --prefer-lowest" COVERAGE=true TEST_COMMAND="composer test-ci" - - php: 7.2 - env: COMPOSER_FLAGS="--prefer-stable --prefer-lowest" - -before_install: - - travis_retry composer self-update - -install: - - travis_retry composer update ${COMPOSER_FLAGS} --prefer-source --no-interaction --ignore-platform-reqs - -before_script: - - vendor/bin/http_test_server > /dev/null 2>&1 & - -script: - - cd ./tests/server/ssl && ./generate.sh && pwd && ls -la && cd ../../../ - - $TEST_COMMAND - -after_success: - - if [[ "$COVERAGE" = true ]]; then wget https://scrutinizer-ci.com/ocular.phar; fi - - if [[ "$COVERAGE" = true ]]; then php ocular.phar code-coverage:upload --format=php-clover build/coverage.xml; fi diff --git a/composer.json b/composer.json index c1b66da..17d4cb4 100644 --- a/composer.json +++ b/composer.json @@ -9,7 +9,7 @@ } ], "require": { - "php": ">=7.1 ", + "php": "^7.1 | ^8.0", "nyholm/psr7": "^1.3", "php-http/httplug": "^2.0", "psr/http-client": "^1.0", diff --git a/phpstan.neon.dist b/phpstan.neon.dist new file mode 100644 index 0000000..d6f0fbe --- /dev/null +++ b/phpstan.neon.dist @@ -0,0 +1,7 @@ +parameters: + level: max + paths: + - src + ignoreErrors: + - message: '#^Method Http\\Adapter\\React\\Client::sendRequest\(\) should return Psr\\Http\\Message\\ResponseInterface but returns mixed.$#' + path: src/Client.php From 4b1b15b2b5501d8355d1d2cc0dfae7e65958e2b4 Mon Sep 17 00:00:00 2001 From: David Buchmann Date: Tue, 2 Aug 2022 08:41:26 +0200 Subject: [PATCH 45/58] use phpstan --- .github/workflows/ci.yml | 9 ++++ composer.json | 4 +- phpstan.neon.dist | 37 +++++++++++++- phpunit.xml.dist | 30 ++++++------ src/Client.php | 54 +++++++++++---------- src/Exception/NetworkException.php | 3 ++ src/RequestWriter.php | 4 ++ src/ResponseReader.php | 9 ++-- src/Stream.php | 78 ++++++++++++++++++++++++++---- tests/SocketHttpClientTest.php | 58 +++++++--------------- tests/StreamTest.php | 67 +++++++++++-------------- 11 files changed, 218 insertions(+), 135 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 14df2f2..68df131 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,6 +39,9 @@ jobs: - name: Install dependencies run: composer update --prefer-dist --no-interaction --no-progress + - name: generate ssl + run: cd ./tests/server/ssl && ./generate.sh && pwd && ls -la && cd ../../../ + - name: boot test server run: vendor/bin/http_test_server > /dev/null 2>&1 & @@ -68,6 +71,9 @@ jobs: - name: Install dependencies run: composer update --prefer-dist --prefer-stable --prefer-lowest --no-interaction --no-progress + - name: generate ssl + run: cd ./tests/server/ssl && ./generate.sh && pwd && ls -la && cd ../../../ + - name: boot test server run: vendor/bin/http_test_server > /dev/null 2>&1 & @@ -95,5 +101,8 @@ jobs: - name: generate ssl run: cd ./tests/server/ssl && ./generate.sh && pwd && ls -la && cd ../../../ + - name: boot test server + run: vendor/bin/http_test_server > /dev/null 2>&1 & + - name: Execute tests run: composer test-ci diff --git a/composer.json b/composer.json index 17d4cb4..8c68505 100644 --- a/composer.json +++ b/composer.json @@ -9,7 +9,7 @@ } ], "require": { - "php": "^7.1 | ^8.0", + "php": "^7.2 || ^8.0", "nyholm/psr7": "^1.3", "php-http/httplug": "^2.0", "psr/http-client": "^1.0", @@ -20,7 +20,7 @@ "php-http/client-integration-tests": "^3.0", "php-http/message": "^1.9", "php-http/client-common": "^2.3", - "phpunit/phpunit": "^8.5.8 || ~9.5" + "phpunit/phpunit": "^8.5.23 || ~9.5" }, "provide": { "php-http/client-implementation": "1.0", diff --git a/phpstan.neon.dist b/phpstan.neon.dist index d6f0fbe..f8f78b7 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -3,5 +3,38 @@ parameters: paths: - src ignoreErrors: - - message: '#^Method Http\\Adapter\\React\\Client::sendRequest\(\) should return Psr\\Http\\Message\\ResponseInterface but returns mixed.$#' - path: src/Client.php + # 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: "#^Constructor of class Http\\\\Client\\\\Socket\\\\Client has an unused parameter \\$config2\\.$#" + count: 1 + path: src/Client.php + + - + message: "#^Method Http\\\\Client\\\\Socket\\\\Client\\:\\:__construct\\(\\) has parameter \\$config1 with no type specified\\.$#" + count: 1 + path: src/Client.php + + - + message: "#^Method Http\\\\Client\\\\Socket\\\\Client\\:\\:__construct\\(\\) has parameter \\$config2 with no type specified\\.$#" + count: 1 + path: src/Client.php + + - + message: "#^Method Http\\\\Client\\\\Socket\\\\Client\\:\\:configure\\(\\) should return array\\{remote_socket\\: string\\|null, timeout\\: int, stream_context\\: resource, stream_context_options\\: array\\, stream_context_param\\: array\\, 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 diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 2ab0582..034d22e 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,17 +1,17 @@ - - - - tests/ - tests/SocketClientFeatureTest.php - - - - - - - - src/ - - + + + + src/ + + + + + tests/ + tests/SocketClientFeatureTest.php + + + + + diff --git a/src/Client.php b/src/Client.php index 902e3f9..bb4494f 100644 --- a/src/Client.php +++ b/src/Client.php @@ -24,29 +24,24 @@ class Client implements HttpClient use RequestWriter; use ResponseReader; - private $config = [ - '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, - ]; + /** + * @var array{remote_socket: string|null, timeout: int, stream_context: resource, stream_context_options: array, stream_context_param: array, ssl: ?boolean, write_buffer_size: int, ssl_method: int} + */ + private $config; /** * Constructor. * - * @param array $config { + * @param array{remote_socket?: string|null, timeout?: int, stream_context?: resource, stream_context_options?: array, stream_context_param?: array, ssl?: ?boolean, write_buffer_size?: int, ssl_method?: int} $config * - * @var string $remote_socket Remote entrypoint (can be a tcp or unix domain address) - * @var int $timeout Timeout before canceling request - * @var array $stream_context_options Context options as defined in the PHP documentation - * @var array $stream_context_param Context params as defined in the PHP documentation - * @var bool $ssl Use ssl, default to scheme from request, false if not present - * @var int $write_buffer_size Buffer when writing the request body, defaults to 8192 - * @var int $ssl_method Crypto method for ssl/tls, see PHP doc, defaults to STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT - * } + * 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 stream_context_options Context options as defined in the PHP documentation + * array 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 = []) { @@ -120,10 +115,10 @@ protected function createSocket(RequestInterface $request, string $remote, bool throw new ConnectionException($errMsg, $request); } - stream_set_timeout($socket, floor($this->config['timeout'] / 1000), $this->config['timeout'] % 1000); + 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']), $request); + throw new SSLConnectionException(sprintf('Cannot enable tls: %s', error_get_last()['message'] ?? 'no error reported'), $request); } return $socket; @@ -133,6 +128,8 @@ protected function createSocket(RequestInterface $request, string $remote, bool * Close the socket, used when having an error. * * @param resource $socket + * + * @return void */ protected function closeSocket($socket) { @@ -142,19 +139,26 @@ protected function closeSocket($socket) /** * Return configuration for the socket client. * - * @param array $config Configuration from user + * @param array{remote_socket?: string|null, timeout?: int, stream_context?: resource, stream_context_options?: array, stream_context_param?: array, ssl?: ?boolean, write_buffer_size?: int, ssl_method?: int} $config * - * @return array Configuration resolved + * @return array{remote_socket: string|null, timeout: int, stream_context: resource, stream_context_options: array, stream_context_param: array, ssl: ?boolean, write_buffer_size: int, ssl_method: int} */ protected function configure(array $config = []) { $resolver = new OptionsResolver(); - $resolver->setDefaults($this->config); + $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', ini_get('default_socket_timeout') * 1000); + $resolver->setDefault('timeout', ((int) ini_get('default_socket_timeout')) * 1000); $resolver->setAllowedTypes('stream_context_options', 'array'); $resolver->setAllowedTypes('stream_context_param', 'array'); diff --git a/src/Exception/NetworkException.php b/src/Exception/NetworkException.php index f3a6b3c..363af23 100644 --- a/src/Exception/NetworkException.php +++ b/src/Exception/NetworkException.php @@ -7,6 +7,9 @@ class NetworkException extends \RuntimeException implements NetworkExceptionInterface { + /** + * @var RequestInterface + */ private $request; public function __construct(string $message, RequestInterface $request, \Exception $previous = null) diff --git a/src/RequestWriter.php b/src/RequestWriter.php index 7f7bbca..4e740b8 100644 --- a/src/RequestWriter.php +++ b/src/RequestWriter.php @@ -19,6 +19,8 @@ trait RequestWriter * * @param resource $socket * + * @return void + * * @throws BrokenPipeException */ protected function writeRequest($socket, RequestInterface $request, int $bufferSize = 8192) @@ -37,6 +39,8 @@ protected function writeRequest($socket, RequestInterface $request, int $bufferS * * @param resource $socket * + * @return void + * * @throws BrokenPipeException */ protected function writeBody($socket, RequestInterface $request, int $bufferSize = 8192) diff --git a/src/ResponseReader.php b/src/ResponseReader.php index 97bd314..66431b0 100644 --- a/src/ResponseReader.php +++ b/src/ResponseReader.php @@ -48,8 +48,8 @@ protected function readResponse(RequestInterface $request, $socket): ResponseInt if (array_key_exists('timed_out', $metadatas) && true === $metadatas['timed_out']) { throw new TimeoutException('Error while reading response, stream timed out', $request, null); } - - $parts = explode(' ', array_shift($headers), 3); + $header = array_shift($headers); + $parts = null !== $header ? explode(' ', $header, 3) : []; if (count($parts) <= 1) { throw new BrokenPipeException('Cannot read the response', $request); @@ -77,7 +77,7 @@ protected function readResponse(RequestInterface $request, $socket): ResponseInt : ''; } - $response = new Response($status, $responseHeaders, null, $protocol, $reason); + $response = new Response((int) $status, $responseHeaders, null, $protocol, $reason); $stream = $this->createStream($socket, $request, $response); return $response->withBody($stream); @@ -95,6 +95,9 @@ protected function createStream($socket, RequestInterface $request, ResponseInte if ($response->hasHeader('Content-Length')) { $size = (int) $response->getHeaderLine('Content-Length'); } + if ($size < 0) { + $size = null; + } return new Stream($request, $socket, $size); } diff --git a/src/Stream.php b/src/Stream.php index f98533a..c2475df 100644 --- a/src/Stream.php +++ b/src/Stream.php @@ -25,7 +25,7 @@ */ class Stream implements StreamInterface { - /** @var resource Underlying socket */ + /** @var resource|null Underlying socket */ private $socket; /** @@ -34,12 +34,12 @@ class Stream implements StreamInterface private $isDetached = false; /** - * @var int|null Size of the stream, so we know what we must read, null if not available (i.e. a chunked stream) + * @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 Size of the stream readed, to avoid reading more than available and have the user blocked + * @var int<0, max> Size of the stream readed, to avoid reading more than available and have the user blocked */ private $readed = 0; @@ -51,7 +51,8 @@ class Stream implements StreamInterface /** * Create the stream. * - * @param resource $socket + * @param resource $socket + * @param int<0, max>|null $size */ public function __construct(RequestInterface $request, $socket, ?int $size = null) { @@ -77,6 +78,9 @@ public function __toString() */ public function close() { + if ($this->isDetached || null === $this->socket) { + throw new StreamException('Stream is detached'); + } fclose($this->socket); } @@ -85,6 +89,9 @@ public function close() */ public function detach() { + if ($this->isDetached) { + return null; + } $this->isDetached = true; $socket = $this->socket; $this->socket = null; @@ -94,6 +101,8 @@ public function detach() /** * {@inheritdoc} + * + * @return int<0, max>|null */ public function getSize() { @@ -105,7 +114,15 @@ public function getSize() */ public function tell() { - return ftell($this->socket); + 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; } /** @@ -113,6 +130,10 @@ public function tell() */ public function eof() { + if ($this->isDetached || null === $this->socket) { + throw new StreamException('Stream is detached'); + } + return feof($this->socket); } @@ -126,6 +147,8 @@ public function isSeekable() /** * {@inheritdoc} + * + * @return void */ public function seek($offset, $whence = SEEK_SET) { @@ -134,6 +157,8 @@ public function seek($offset, $whence = SEEK_SET) /** * {@inheritdoc} + * + * @return void */ public function rewind() { @@ -166,11 +191,21 @@ public function isReadable() /** * {@inheritdoc} + * + * @param int<0, max> $length */ public function read($length) { + if ($this->isDetached || null === $this->socket) { + throw new StreamException('Stream is detached'); + } if (null === $this->getSize()) { - return fread($this->socket, $length); + $read = fread($this->socket, $length); + if (false === $read) { + throw new StreamException('Failed to read from stream'); + } + + return $read; } if ($this->getSize() === $this->readed) { @@ -179,7 +214,15 @@ public function read($length) // 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); } @@ -194,15 +237,26 @@ public function read($length) */ public function getContents() { + if ($this->isDetached || null === $this->socket) { + throw new StreamException('Stream is detached'); + } + if (null === $this->getSize()) { - return stream_get_contents($this->socket); + $contents = stream_get_contents($this->socket); + if (false === $contents) { + throw new StreamException('failed to get contents of stream'); + } + + return $contents; } $contents = ''; - do { - $contents .= $this->read($this->getSize() - $this->readed); - } while ($this->readed < $this->getSize()); + $toread = $this->getSize() - $this->readed; + while ($toread > 0) { + $contents .= $this->read($toread); + $toread = $this->getSize() - $this->readed; + } return $contents; } @@ -212,6 +266,10 @@ public function getContents() */ 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) { diff --git a/tests/SocketHttpClientTest.php b/tests/SocketHttpClientTest.php index cca4fd6..c3e5da2 100644 --- a/tests/SocketHttpClientTest.php +++ b/tests/SocketHttpClientTest.php @@ -4,6 +4,8 @@ use Http\Client\Common\HttpMethodsClient; use Http\Client\Socket\Client as SocketHttpClient; +use Http\Client\Socket\Exception\NetworkException; +use Http\Client\Socket\Exception\TimeoutException; use Http\Message\MessageFactory\GuzzleMessageFactory; class SocketHttpClientTest extends BaseTestCase @@ -21,30 +23,26 @@ public function testTcpSocketDomain() $client = $this->createClient(['remote_socket' => '127.0.0.1:19999']); $response = $client->get('/', []); - $this->assertInstanceOf('Psr\Http\Message\ResponseInterface', $response); $this->assertEquals(200, $response->getStatusCode()); } - /** - * @expectedException \Http\Client\Socket\Exception\NetworkException - */ - public function testNoRemote() + public function testNoRemote(): void { $client = $this->createClient(); + $this->expectException(NetworkException::class); $client->get('/', []); } - public function testRemoteInUri() + public function testRemoteInUri(): void { $this->startServer('tcp-server'); $client = $this->createClient(); $response = $client->get('http://127.0.0.1:19999/', []); - $this->assertInstanceOf('Psr\Http\Message\ResponseInterface', $response); $this->assertEquals(200, $response->getStatusCode()); } - public function testRemoteInHostHeader() + public function testRemoteInHostHeader(): void { $this->startServer('tcp-server'); $client = $this->createClient(); @@ -54,17 +52,15 @@ public function testRemoteInHostHeader() $this->assertEquals(200, $response->getStatusCode()); } - /** - * @expectedException \Http\Client\Socket\Exception\NetworkException - */ - public function testBrokenSocket() + public function testBrokenSocket(): void { $this->startServer('tcp-bugous-server'); $client = $this->createClient(['remote_socket' => '127.0.0.1:19999']); + $this->expectException(NetworkException::class); $client->get('/', []); } - public function testSslRemoteInUri() + public function testSslRemoteInUri(): void { $this->startServer('tcp-ssl-server'); $client = $this->createClient([ @@ -83,7 +79,7 @@ public function testSslRemoteInUri() $this->assertEquals(200, $response->getStatusCode()); } - public function testUnixSocketDomain() + public function testUnixSocketDomain(): void { $this->startServer('unix-domain-server'); @@ -92,16 +88,13 @@ public function testUnixSocketDomain() ]); $response = $client->get('/', []); - $this->assertInstanceOf('Psr\Http\Message\ResponseInterface', $response); $this->assertEquals(200, $response->getStatusCode()); } - /** - * @expectedException \Http\Client\Socket\Exception\NetworkException - */ - public function testNetworkExceptionOnConnectError() + public function testNetworkExceptionOnConnectError(): void { $client = $this->createClient(['remote_socket' => '127.0.0.1:19999']); + $this->expectException(NetworkException::class); $client->get('/', []); } @@ -121,16 +114,11 @@ public function testSslConnection() ]); $response = $client->get('/', []); - $this->assertInstanceOf('Psr\Http\Message\ResponseInterface', $response); $this->assertEquals(200, $response->getStatusCode()); } - public function testSslConnectionWithClientCertificate() + public function testSslConnectionWithClientCertificate(): void { - if (version_compare(PHP_VERSION, '5.6', '<')) { - $this->markTestSkipped('Test can only run on php 5.6 and superior (for capturing peer certificate)'); - } - $this->startServer('tcp-ssl-server-client'); $client = $this->createClient([ @@ -146,16 +134,11 @@ public function testSslConnectionWithClientCertificate() ]); $response = $client->get('/', []); - $this->assertInstanceOf('Psr\Http\Message\ResponseInterface', $response); $this->assertEquals(200, $response->getStatusCode()); } - public function testInvalidSslConnectionWithClientCertificate() + public function testInvalidSslConnectionWithClientCertificate(): void { - if (version_compare(PHP_VERSION, '5.6', '<')) { - $this->markTestSkipped('Test can only run on php 5.6 and superior (for capturing peer certificate)'); - } - $this->startServer('tcp-ssl-server-client'); $client = $this->createClient([ @@ -170,27 +153,22 @@ public function testInvalidSslConnectionWithClientCertificate() ]); $response = $client->get('/', []); - $this->assertInstanceOf('Psr\Http\Message\ResponseInterface', $response); $this->assertEquals(403, $response->getStatusCode()); } - /** - * @expectedException \Http\Client\Socket\Exception\NetworkException - */ - public function testNetworkExceptionOnSslError() + public function testNetworkExceptionOnSslError(): void { $this->startServer('tcp-server'); $client = $this->createClient(['remote_socket' => '127.0.0.1:19999', 'ssl' => true]); + $this->expectException(NetworkException::class); $client->get('/', []); } - /** - * @expectedException \Http\Client\Socket\Exception\TimeoutException - */ - public function testNetworkExceptionOnTimeout() + public function testNetworkExceptionOnTimeout(): void { $client = $this->createClient(['timeout' => 1]); + $this->expectException(TimeoutException::class); $response = $client->get('https://php.net', []); $response->getBody()->getContents(); } diff --git a/tests/StreamTest.php b/tests/StreamTest.php index 11ad293..ff40991 100644 --- a/tests/StreamTest.php +++ b/tests/StreamTest.php @@ -2,22 +2,24 @@ namespace Http\Client\Socket\Tests; +use Http\Client\Socket\Exception\StreamException; +use Http\Client\Socket\Exception\TimeoutException; use Http\Client\Socket\Stream; use Nyholm\Psr7\Request; use PHPUnit\Framework\TestCase; class StreamTest extends TestCase { - public function createSocket($body, $useSize = true) + public function createSocket($body, $useSize = true): Stream { - $socket = fopen('php://memory', 'rw'); + $socket = fopen('php://memory', 'rwb'); fwrite($socket, $body); fseek($socket, 0); return new Stream(new Request('GET', '/'), $socket, $useSize ? strlen($body) : null); } - public function testToString() + public function testToString(): void { $stream = $this->createSocket('Body'); @@ -25,7 +27,7 @@ public function testToString() $stream->close(); } - public function testSubsequentCallIsEmpty() + public function testSubsequentCallIsEmpty(): void { $stream = $this->createSocket('Body'); @@ -34,16 +36,16 @@ public function testSubsequentCallIsEmpty() $stream->close(); } - public function testDetach() + public function testDetach(): void { $stream = $this->createSocket('Body'); $socket = $stream->detach(); - $this->assertTrue(is_resource($socket)); + $this->assertIsResource($socket); $this->assertNull($stream->detach()); } - public function testTell() + public function testTell(): void { $stream = $this->createSocket('Body'); @@ -52,9 +54,9 @@ public function testTell() $this->assertEquals(4, $stream->tell()); } - public function testEof() + public function testEof(): void { - $socket = fopen('php://memory', 'rw+'); + $socket = fopen('php://memory', 'rwb+'); fwrite($socket, 'Body'); fseek($socket, 0); $stream = new Stream(new Request('GET', '/'), $socket); @@ -65,63 +67,52 @@ public function testEof() $stream->close(); } - public function testNotSeekable() + public function testNotSeekable(): void { $stream = $this->createSocket('Body'); $this->assertFalse($stream->isSeekable()); - try { - $stream->seek(0); - } catch (\Exception $e) { - $this->assertInstanceOf('Http\Client\Socket\Exception\StreamException', $e); - } + $this->expectException(StreamException::class); + $stream->seek(0); } - public function testNoRewing() + public function testNoRewind(): void { $stream = $this->createSocket('Body'); - try { - $stream->rewind(); - } catch (\Exception $e) { - $this->assertInstanceOf('Http\Client\Socket\Exception\StreamException', $e); - } + $this->expectException(StreamException::class); + $stream->rewind(); } - public function testNotWritable() + public function testNotWritable(): void { $stream = $this->createSocket('Body'); $this->assertFalse($stream->isWritable()); - try { - $stream->write('Test'); - } catch (\Exception $e) { - $this->assertInstanceOf('Http\Client\Socket\Exception\StreamException', $e); - } + $this->expectException(StreamException::class); + $stream->write('Test'); } - public function testIsReadable() + public function testIsReadable(): void { $stream = $this->createSocket('Body'); $this->assertTrue($stream->isReadable()); } - /** - * @expectedException \Http\Client\Socket\Exception\TimeoutException - */ - public function testTimeout() + public function testTimeout(): void { $socket = fsockopen('php.net', 80); - socket_set_timeout($socket, 0, 100); + stream_set_timeout($socket, 0, 100); $stream = new Stream(new Request('GET', '/'), $socket, 50); + $this->expectException(TimeoutException::class); $stream->getContents(); } - public function testMetadatas() + public function testMetadatas(): void { $stream = $this->createSocket('Body', false); @@ -133,19 +124,19 @@ public function testMetadatas() $this->assertTrue($stream->getMetadata('blocked')); } - public function testClose() + public function testClose(): void { - $socket = fopen('php://memory', 'rw+'); + $socket = fopen('php://memory', 'rwb+'); fwrite($socket, 'Body'); fseek($socket, 0); $stream = new Stream(new Request('GET', '/'), $socket); $stream->close(); - $this->assertFalse(is_resource($socket)); + $this->assertFalse(is_resource($socket)); // phpstorm thinks we could assertNotIsResource, but closed resources seem to behave differently } - public function testRead() + public function testRead(): void { $stream = $this->createSocket('Body'); From 9b4b793f28c8899be926c8ae026f8ca48fda6c9f Mon Sep 17 00:00:00 2001 From: Flavio Heleno Date: Tue, 29 Dec 2020 20:24:06 -0300 Subject: [PATCH 46/58] Relative/Absolute path improvement for generate.sh --- README.md | 4 +--- composer.json | 3 ++- tests/server/ssl/generate.sh | 4 ++++ 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 43bb988..3cb4541 100644 --- a/README.md +++ b/README.md @@ -31,9 +31,7 @@ $ ./vendor/bin/http_test_server > /dev/null 2>&1 & Then generate ssh certificates: ```bash -$ cd ./tests/server/ssl -$ ./generate.sh -$ cd ../../../ +$ 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. diff --git a/composer.json b/composer.json index 8c68505..0a9e57b 100644 --- a/composer.json +++ b/composer.json @@ -40,7 +40,8 @@ "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" + "test-ci": "vendor/bin/phpunit --coverage-clover build/coverage.xml", + "gen-ssl": "tests/server/ssl/generate.sh" }, "extra": { "branch-alias": { diff --git a/tests/server/ssl/generate.sh b/tests/server/ssl/generate.sh index eb83bc2..8f1be48 100755 --- a/tests/server/ssl/generate.sh +++ b/tests/server/ssl/generate.sh @@ -1,5 +1,9 @@ #!/bin/bash +set -eo pipefail + +cd $(dirname $0) + C=FR ST=Ile-de-France L=Paris From 0df8689f9caf1e7e2b59dd97b01519535e4eb092 Mon Sep 17 00:00:00 2001 From: David Buchmann Date: Fri, 19 Aug 2022 17:32:50 +0200 Subject: [PATCH 47/58] rename branch to semantic name --- CHANGELOG.md | 5 +++++ composer.json | 5 ----- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 71467bb..7318a4b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Change Log +## 2.0.5 (unreleased) + +* Fixed PHP 8 compatibility for stream timeouts +* Renamed `master` branch to `2.x` for semantic branch naming. + ## 2.0.4 * Add Symfony 6 compatibility diff --git a/composer.json b/composer.json index 0a9e57b..47ba408 100644 --- a/composer.json +++ b/composer.json @@ -43,11 +43,6 @@ "test-ci": "vendor/bin/phpunit --coverage-clover build/coverage.xml", "gen-ssl": "tests/server/ssl/generate.sh" }, - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, "prefer-stable": true, "minimum-stability": "dev" } From b573b9c550726f4c3990a8fe291837508c03802c Mon Sep 17 00:00:00 2001 From: Quentin Dreyer Date: Sat, 30 Jul 2022 13:38:19 +0200 Subject: [PATCH 48/58] fix: allow configuration of php-http/socket-client >=2.0 --- CHANGELOG.md | 1 + phpstan.neon.dist | 15 --------------- src/Client.php | 9 ++++++--- 3 files changed, 7 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7318a4b..4ba6103 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## 2.0.5 (unreleased) +* 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. diff --git a/phpstan.neon.dist b/phpstan.neon.dist index f8f78b7..f88ed86 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -9,21 +9,6 @@ parameters: count: 1 path: src/RequestWriter.php - - - message: "#^Constructor of class Http\\\\Client\\\\Socket\\\\Client has an unused parameter \\$config2\\.$#" - count: 1 - path: src/Client.php - - - - message: "#^Method Http\\\\Client\\\\Socket\\\\Client\\:\\:__construct\\(\\) has parameter \\$config1 with no type specified\\.$#" - count: 1 - path: src/Client.php - - - - message: "#^Method Http\\\\Client\\\\Socket\\\\Client\\:\\:__construct\\(\\) has parameter \\$config2 with no type specified\\.$#" - count: 1 - path: src/Client.php - - message: "#^Method Http\\\\Client\\\\Socket\\\\Client\\:\\:configure\\(\\) should return array\\{remote_socket\\: string\\|null, timeout\\: int, stream_context\\: resource, stream_context_options\\: array\\, stream_context_param\\: array\\, ssl\\: bool\\|null, write_buffer_size\\: int, ssl_method\\: int\\} but returns array\\.$#" count: 1 diff --git a/src/Client.php b/src/Client.php index bb4494f..6f57816 100644 --- a/src/Client.php +++ b/src/Client.php @@ -8,6 +8,7 @@ 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; @@ -32,7 +33,9 @@ class Client implements HttpClient /** * Constructor. * - * @param array{remote_socket?: string|null, timeout?: int, stream_context?: resource, stream_context_options?: array, stream_context_param?: array, ssl?: ?boolean, write_buffer_size?: int, ssl_method?: int} $config + * @param array{remote_socket?: string|null, timeout?: int, stream_context?: resource, stream_context_options?: array, stream_context_param?: array, 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, stream_context_param?: array, 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, stream_context_param?: array, 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 @@ -51,9 +54,9 @@ public function __construct($config1 = [], $config2 = null, array $config = []) return; } - @trigger_error('Passing a Psr\Http\Message\ResponseFactoryInterface and a Psr\Http\Message\StreamFactoryInterface to SocketClient is deprecated, and will be removed in 3.0, you should only pass config options.', E_USER_DEPRECATED); + @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($config); + $this->config = $this->configure($config2 ?: $config); } /** From 0ee6766f2231fb844230634ddd30ac7c3676557e Mon Sep 17 00:00:00 2001 From: David Buchmann Date: Fri, 19 Aug 2022 17:39:20 +0200 Subject: [PATCH 49/58] adjust status badge to github actions --- .github/workflows/checks.yml | 2 +- .github/workflows/ci.yml | 2 +- .github/workflows/static.yml | 2 +- README.md | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 3a3dfee..374b251 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -3,7 +3,7 @@ name: Checks on: push: branches: - - master + - 2.x pull_request: jobs: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 68df131..6579133 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,7 +3,7 @@ name: CI on: push: branches: - - master + - 2.x pull_request: jobs: diff --git a/.github/workflows/static.yml b/.github/workflows/static.yml index f4aa6f6..1f2cbe9 100644 --- a/.github/workflows/static.yml +++ b/.github/workflows/static.yml @@ -3,7 +3,7 @@ name: Static analysis on: push: branches: - - master + - 2.x pull_request: jobs: diff --git a/README.md b/README.md index 3cb4541..b1b872b 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![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://img.shields.io/travis/php-http/socket-client.svg?branch=master&style=flat-square)](https://travis-ci.org/php-http/socket-client) +[![Build Status](https://github.com/php-http/socket-client/actions/workflows/ci.yml/badge.svg)](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) From 7fa1a5a75720736319a604455674c1c6faceb060 Mon Sep 17 00:00:00 2001 From: David Buchmann Date: Fri, 19 Aug 2022 17:40:48 +0200 Subject: [PATCH 50/58] show ci badge only for the 2.x branch, not any build --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b1b872b..193ff9b 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![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)](https://github.com/php-http/socket-client/actions/workflows/ci.yml) +[![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) From e780f5b14397fa7347209de8d36236d6106ba080 Mon Sep 17 00:00:00 2001 From: David Buchmann Date: Fri, 19 Aug 2022 18:03:05 +0200 Subject: [PATCH 51/58] prepare release --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ba6103..7093a1e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Change Log -## 2.0.5 (unreleased) +## 2.0.5 * Fixed constructor to work nicely with version 1 style arguments (e.g. HttplugBundle) * Fixed PHP 8 compatibility for stream timeouts From ce5c126431b39b1eea6f4b1d27ba2d1f9387421a Mon Sep 17 00:00:00 2001 From: David Buchmann Date: Fri, 19 Aug 2022 18:04:35 +0200 Subject: [PATCH 52/58] prepare release --- CHANGELOG.md | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7093a1e..0909b69 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,16 +1,13 @@ # Change Log -## 2.0.5 - -* 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. - -## 2.0.4 +## 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.0.3 +## 2.1.0 * Add php 8 compatibility From e72d5b5e4cb21792318c8647b86c1da1cde76910 Mon Sep 17 00:00:00 2001 From: Mateusz Dudek Date: Wed, 14 Feb 2024 10:10:43 +0100 Subject: [PATCH 53/58] Symfony 7 support --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 47ba408..11542e4 100644 --- a/composer.json +++ b/composer.json @@ -13,7 +13,7 @@ "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" + "symfony/options-resolver": "^2.6 || ^3.4 || ^4.4 || ^5.0 || ^6.0 || ^7.0" }, "require-dev": { "friendsofphp/php-cs-fixer": "^2.2 || ^3.0", From 84b0da51a8e730c1486dfb08a42723f28a9f4058 Mon Sep 17 00:00:00 2001 From: David Buchmann Date: Mon, 19 Feb 2024 18:18:49 +0100 Subject: [PATCH 54/58] prepare release --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0909b69..b030fcb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Change Log +## 2.2.0 + + * Allow installation with Symfony 7 + ## 2.1.1 * Fixed constructor to work nicely with version 1 style arguments (e.g. HttplugBundle) From d7f73879a112e8c2cbe7d4f5d145c773102872b9 Mon Sep 17 00:00:00 2001 From: Christopher Georg Date: Mon, 4 Mar 2024 20:16:40 +0100 Subject: [PATCH 55/58] chore: fix ci deprecations --- .github/workflows/checks.yml | 4 ++-- .github/workflows/ci.yml | 8 ++++---- .github/workflows/static.yml | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 374b251..3b29468 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -13,7 +13,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Composer normalize uses: docker://ergebnis/composer-normalize-action @@ -24,7 +24,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Roave BC Check uses: docker://nyholm/roave-bc-check-ga diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6579133..b0e4f6a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,7 +13,7 @@ jobs: outputs: version: ${{ steps.supported-versions-matrix.outputs.version }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - id: supported-versions-matrix uses: WyriHaximus/github-action-composer-php-versions-in-range@v1 latest: @@ -27,7 +27,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Setup PHP uses: shivammathur/setup-php@v2 @@ -59,7 +59,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Setup PHP uses: shivammathur/setup-php@v2 @@ -86,7 +86,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Setup PHP uses: shivammathur/setup-php@v2 diff --git a/.github/workflows/static.yml b/.github/workflows/static.yml index 1f2cbe9..cfa74b1 100644 --- a/.github/workflows/static.yml +++ b/.github/workflows/static.yml @@ -13,7 +13,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: PHPStan uses: docker://oskarstark/phpstan-ga @@ -28,7 +28,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: PHP-CS-Fixer uses: docker://oskarstark/php-cs-fixer-ga From ef22c600572f6a47fe207532de2ad0519106d1bb Mon Sep 17 00:00:00 2001 From: David Buchmann Date: Mon, 4 Mar 2024 23:37:35 +0100 Subject: [PATCH 56/58] fix cs --- .github/workflows/checks.yml | 10 ------- src/Client.php | 11 +++---- src/Exception/NetworkException.php | 2 +- src/RequestWriter.php | 1 + src/Stream.php | 41 -------------------------- tests/SocketHttpAdapterTest.php | 3 -- tests/server/tcp-bugous-server.php | 2 +- tests/server/tcp-server.php | 2 +- tests/server/tcp-ssl-server-client.php | 2 +- tests/server/tcp-ssl-server.php | 2 +- tests/server/unix-domain-server.php | 2 +- 11 files changed, 11 insertions(+), 67 deletions(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 3b29468..8e3f23e 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -18,13 +18,3 @@ jobs: - name: Composer normalize uses: docker://ergebnis/composer-normalize-action - roave-bc-check: - name: Roave BC Check - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Roave BC Check - uses: docker://nyholm/roave-bc-check-ga diff --git a/src/Client.php b/src/Client.php index 6f57816..e6a96c0 100644 --- a/src/Client.php +++ b/src/Client.php @@ -59,9 +59,6 @@ public function __construct($config1 = [], $config2 = null, array $config = []) $this->config = $this->configure($config2 ?: $config); } - /** - * {@inheritdoc} - */ public function sendRequest(RequestInterface $request): ResponseInterface { $remote = $this->config['remote_socket']; @@ -100,9 +97,9 @@ public function sendRequest(RequestInterface $request): ResponseInterface * @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 + * + * @throws ConnectionException|SSLConnectionException When the connection fail */ protected function createSocket(RequestInterface $request, string $remote, bool $useSsl) { @@ -174,9 +171,9 @@ protected function configure(array $config = []) /** * Return remote socket from the request. * - * @throws InvalidRequestException When no remote can be determined from the request - * * @return string + * + * @throws InvalidRequestException When no remote can be determined from the request */ private function determineRemoteFromRequest(RequestInterface $request) { diff --git a/src/Exception/NetworkException.php b/src/Exception/NetworkException.php index 363af23..62d08a3 100644 --- a/src/Exception/NetworkException.php +++ b/src/Exception/NetworkException.php @@ -12,7 +12,7 @@ class NetworkException extends \RuntimeException implements NetworkExceptionInte */ private $request; - public function __construct(string $message, RequestInterface $request, \Exception $previous = null) + public function __construct(string $message, RequestInterface $request, ?\Exception $previous = null) { $this->request = $request; diff --git a/src/RequestWriter.php b/src/RequestWriter.php index 4e740b8..0ac0430 100644 --- a/src/RequestWriter.php +++ b/src/RequestWriter.php @@ -123,6 +123,7 @@ private function fwrite($stream, string $bytes) // 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`. diff --git a/src/Stream.php b/src/Stream.php index c2475df..8d26ca0 100644 --- a/src/Stream.php +++ b/src/Stream.php @@ -61,9 +61,6 @@ public function __construct(RequestInterface $request, $socket, ?int $size = nul $this->request = $request; } - /** - * {@inheritdoc} - */ public function __toString() { try { @@ -73,9 +70,6 @@ public function __toString() } } - /** - * {@inheritdoc} - */ public function close() { if ($this->isDetached || null === $this->socket) { @@ -84,9 +78,6 @@ public function close() fclose($this->socket); } - /** - * {@inheritdoc} - */ public function detach() { if ($this->isDetached) { @@ -100,8 +91,6 @@ public function detach() } /** - * {@inheritdoc} - * * @return int<0, max>|null */ public function getSize() @@ -109,9 +98,6 @@ public function getSize() return $this->size; } - /** - * {@inheritdoc} - */ public function tell() { if ($this->isDetached || null === $this->socket) { @@ -125,9 +111,6 @@ public function tell() return $tell; } - /** - * {@inheritdoc} - */ public function eof() { if ($this->isDetached || null === $this->socket) { @@ -137,17 +120,12 @@ public function eof() return feof($this->socket); } - /** - * {@inheritdoc} - */ public function isSeekable() { return false; } /** - * {@inheritdoc} - * * @return void */ public function seek($offset, $whence = SEEK_SET) @@ -156,8 +134,6 @@ public function seek($offset, $whence = SEEK_SET) } /** - * {@inheritdoc} - * * @return void */ public function rewind() @@ -165,33 +141,22 @@ public function rewind() throw new StreamException('This stream is not seekable'); } - /** - * {@inheritdoc} - */ public function isWritable() { return false; } - /** - * {@inheritdoc} - */ public function write($string) { throw new StreamException('This stream is not writable'); } - /** - * {@inheritdoc} - */ public function isReadable() { return true; } /** - * {@inheritdoc} - * * @param int<0, max> $length */ public function read($length) @@ -232,9 +197,6 @@ public function read($length) return $read; } - /** - * {@inheritdoc} - */ public function getContents() { if ($this->isDetached || null === $this->socket) { @@ -261,9 +223,6 @@ public function getContents() return $contents; } - /** - * {@inheritdoc} - */ public function getMetadata($key = null) { if ($this->isDetached || null === $this->socket) { diff --git a/tests/SocketHttpAdapterTest.php b/tests/SocketHttpAdapterTest.php index f95b28e..72ed4bb 100644 --- a/tests/SocketHttpAdapterTest.php +++ b/tests/SocketHttpAdapterTest.php @@ -8,9 +8,6 @@ class SocketHttpAdapterTest extends HttpClientTest { - /** - * {@inheritdoc} - */ protected function createHttpAdapter(): ClientInterface { return new SocketHttpClient(); diff --git a/tests/server/tcp-bugous-server.php b/tests/server/tcp-bugous-server.php index d1a55ae..723a6eb 100644 --- a/tests/server/tcp-bugous-server.php +++ b/tests/server/tcp-bugous-server.php @@ -6,4 +6,4 @@ $client = stream_socket_accept($socketServer); fclose($client); -\Http\Client\Socket\Tests\Semaphore::release(); +Http\Client\Socket\Tests\Semaphore::release(); diff --git a/tests/server/tcp-server.php b/tests/server/tcp-server.php index 573da2a..4929984 100644 --- a/tests/server/tcp-server.php +++ b/tests/server/tcp-server.php @@ -16,4 +16,4 @@ while (!@feof($client)) { @fread($client, 1000); } -\Http\Client\Socket\Tests\Semaphore::release(); +Http\Client\Socket\Tests\Semaphore::release(); diff --git a/tests/server/tcp-ssl-server-client.php b/tests/server/tcp-ssl-server-client.php index 1e06cd3..6c7bf79 100644 --- a/tests/server/tcp-ssl-server-client.php +++ b/tests/server/tcp-ssl-server-client.php @@ -46,4 +46,4 @@ while (!@feof($client)) { @fread($client, 1000); } -\Http\Client\Socket\Tests\Semaphore::release(); +Http\Client\Socket\Tests\Semaphore::release(); diff --git a/tests/server/tcp-ssl-server.php b/tests/server/tcp-ssl-server.php index fb15359..7b63db1 100644 --- a/tests/server/tcp-ssl-server.php +++ b/tests/server/tcp-ssl-server.php @@ -35,4 +35,4 @@ @fread($client, 1000); } -\Http\Client\Socket\Tests\Semaphore::release(); +Http\Client\Socket\Tests\Semaphore::release(); diff --git a/tests/server/unix-domain-server.php b/tests/server/unix-domain-server.php index a051ffe..8a3368b 100644 --- a/tests/server/unix-domain-server.php +++ b/tests/server/unix-domain-server.php @@ -22,4 +22,4 @@ } unlink(__DIR__.'/server.sock'); -\Http\Client\Socket\Tests\Semaphore::release(); +Http\Client\Socket\Tests\Semaphore::release(); From 1f8fecc2bcbfdaf090c89d7175688b2e6a8d9b7d Mon Sep 17 00:00:00 2001 From: Andrii Dembitskyi Date: Sun, 1 Sep 2024 14:46:23 +0300 Subject: [PATCH 57/58] Make package compatible with stream interface Compatibility stream interface v2 (#75) * make compatible with stream interface * :rotating_light: Explicitly specify what features of HTTP client is not supported by current implementation to avoid having failed tests * :package: Explicitly declare compatibility versions for `psr/http-message` There is implementation for interface from this package * :package: `fread` requires to pass only `int<1, max>` - early return empty string in case of `0` length --------- Co-authored-by: David Buchmann --- .github/workflows/ci.yml | 6 +++-- CHANGELOG.md | 5 +++++ README.md | 2 +- composer.json | 18 ++++++++------- phpunit.xml.dist | 1 - src/ResponseReader.php | 6 ----- src/Stream.php | 37 +++++++++++++++---------------- tests/SocketClientFeatureTest.php | 25 +++++++++++++++++++++ tests/SocketHttpClientTest.php | 6 ++--- 9 files changed, 65 insertions(+), 41 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b0e4f6a..1231aa2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,7 +3,9 @@ name: CI on: push: branches: - - 2.x + - '[0-9]+.x' + - '[0-9]+.[0-9]+' + - '[0-9]+.[0-9]+.x' pull_request: jobs: @@ -91,7 +93,7 @@ jobs: - name: Setup PHP uses: shivammathur/setup-php@v2 with: - php-version: 7.4 + php-version: 8.3 tools: composer coverage: xdebug diff --git a/CHANGELOG.md b/CHANGELOG.md index b030fcb..b766856 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Change Log +## 2.3.0 + + * Fixed compatibility with `psr/http-message` v2 + * The `Http\Client\Socket\Stream` has BC breaks if you extended it. It is not meant to be extended, declaring it as `@internal` now. + ## 2.2.0 * Allow installation with Symfony 7 diff --git a/README.md b/README.md index 193ff9b..27be400 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ First launch the http server: $ ./vendor/bin/http_test_server > /dev/null 2>&1 & ``` -Then generate ssh certificates: +Then generate SSL certificates: ```bash $ composer gen-ssl diff --git a/composer.json b/composer.json index 11542e4..0689754 100644 --- a/composer.json +++ b/composer.json @@ -9,18 +9,20 @@ } ], "require": { - "php": "^7.2 || ^8.0", - "nyholm/psr7": "^1.3", - "php-http/httplug": "^2.0", + "php": "^8.1", + "nyholm/psr7": "^1.8.1", + "php-http/httplug": "^2.4", "psr/http-client": "^1.0", + "psr/http-message": "^1.0 || ^2.0", "symfony/options-resolver": "^2.6 || ^3.4 || ^4.4 || ^5.0 || ^6.0 || ^7.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" + "friendsofphp/php-cs-fixer": "^3.51", + "php-http/client-integration-tests": "^3.1.1", + "php-http/message": "^1.16", + "php-http/client-common": "^2.7", + "phpunit/phpunit": "^8.5.23 || ~9.5", + "php-http/message-factory": "^1.1" }, "provide": { "php-http/client-implementation": "1.0", diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 034d22e..f12146d 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -8,7 +8,6 @@ tests/ - tests/SocketClientFeatureTest.php diff --git a/src/ResponseReader.php b/src/ResponseReader.php index 66431b0..d5beae6 100644 --- a/src/ResponseReader.php +++ b/src/ResponseReader.php @@ -4,7 +4,6 @@ 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; @@ -18,11 +17,6 @@ */ trait ResponseReader { - /** - * @var ResponseFactory For creating response - */ - protected $responseFactory; - /** * Read a response from a socket. * diff --git a/src/Stream.php b/src/Stream.php index 8d26ca0..afe6dd6 100644 --- a/src/Stream.php +++ b/src/Stream.php @@ -61,7 +61,7 @@ public function __construct(RequestInterface $request, $socket, ?int $size = nul $this->request = $request; } - public function __toString() + public function __toString(): string { try { return $this->getContents(); @@ -70,7 +70,7 @@ public function __toString() } } - public function close() + public function close(): void { if ($this->isDetached || null === $this->socket) { throw new StreamException('Stream is detached'); @@ -93,12 +93,12 @@ public function detach() /** * @return int<0, max>|null */ - public function getSize() + public function getSize(): ?int { return $this->size; } - public function tell() + public function tell(): int { if ($this->isDetached || null === $this->socket) { throw new StreamException('Stream is detached'); @@ -111,7 +111,7 @@ public function tell() return $tell; } - public function eof() + public function eof(): bool { if ($this->isDetached || null === $this->socket) { throw new StreamException('Stream is detached'); @@ -120,38 +120,32 @@ public function eof() return feof($this->socket); } - public function isSeekable() + public function isSeekable(): bool { return false; } - /** - * @return void - */ - public function seek($offset, $whence = SEEK_SET) + public function seek($offset, $whence = SEEK_SET): void { throw new StreamException('This stream is not seekable'); } - /** - * @return void - */ - public function rewind() + public function rewind(): void { throw new StreamException('This stream is not seekable'); } - public function isWritable() + public function isWritable(): bool { return false; } - public function write($string) + public function write($string): int { throw new StreamException('This stream is not writable'); } - public function isReadable() + public function isReadable(): bool { return true; } @@ -159,11 +153,16 @@ public function isReadable() /** * @param int<0, max> $length */ - public function read($length) + public function read($length): string { + if (0 === $length) { + return ''; + } + if ($this->isDetached || null === $this->socket) { throw new StreamException('Stream is detached'); } + if (null === $this->getSize()) { $read = fread($this->socket, $length); if (false === $read) { @@ -197,7 +196,7 @@ public function read($length) return $read; } - public function getContents() + public function getContents(): string { if ($this->isDetached || null === $this->socket) { throw new StreamException('Stream is detached'); diff --git a/tests/SocketClientFeatureTest.php b/tests/SocketClientFeatureTest.php index e6e698b..f1fe854 100644 --- a/tests/SocketClientFeatureTest.php +++ b/tests/SocketClientFeatureTest.php @@ -12,4 +12,29 @@ protected function createClient(): ClientInterface { return new SocketHttpClient(); } + + public function testAutoSetContentLength(): void + { + $this->markTestSkipped('Feature is unsupported'); + } + + public function testGzip(): void + { + $this->markTestSkipped('Feature is unsupported'); + } + + public function testDeflate(): void + { + $this->markTestSkipped('Feature is unsupported'); + } + + public function testChunked(): void + { + $this->markTestSkipped('Feature is unsupported'); + } + + public function testRedirect(): void + { + $this->markTestSkipped('Feature is unsupported'); + } } diff --git a/tests/SocketHttpClientTest.php b/tests/SocketHttpClientTest.php index c3e5da2..657f2ff 100644 --- a/tests/SocketHttpClientTest.php +++ b/tests/SocketHttpClientTest.php @@ -6,15 +6,13 @@ use Http\Client\Socket\Client as SocketHttpClient; use Http\Client\Socket\Exception\NetworkException; use Http\Client\Socket\Exception\TimeoutException; -use Http\Message\MessageFactory\GuzzleMessageFactory; +use Nyholm\Psr7\Factory\Psr17Factory; class SocketHttpClientTest extends BaseTestCase { public function createClient($options = []) { - $messageFactory = new GuzzleMessageFactory(); - - return new HttpMethodsClient(new SocketHttpClient($options), $messageFactory); + return new HttpMethodsClient(new SocketHttpClient($options), new Psr17Factory()); } public function testTcpSocketDomain() From 94290f4b3aaf50c63d4f8530c448da5be253adba Mon Sep 17 00:00:00 2001 From: David Buchmann Date: Sun, 1 Sep 2024 13:47:45 +0200 Subject: [PATCH 58/58] prepare release --- src/Client.php | 2 ++ src/Stream.php | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Client.php b/src/Client.php index e6a96c0..116b621 100644 --- a/src/Client.php +++ b/src/Client.php @@ -19,6 +19,8 @@ * Use stream and socket capabilities of the core of PHP to send HTTP requests * * @author Joel Wurtz + * + * @final */ class Client implements HttpClient { diff --git a/src/Stream.php b/src/Stream.php index afe6dd6..e49844a 100644 --- a/src/Stream.php +++ b/src/Stream.php @@ -21,7 +21,9 @@ * * Writing and seeking is disable to avoid weird behaviors. * - * @author Joel Wurtz + * @author Joel Wurtz */ class Stream implements StreamInterface {