Giter Club home page Giter Club logo

curl-client's Introduction

Curl client for PHP HTTP

Latest Version Software License Tests Total Downloads

The cURL client use the cURL PHP extension which must be activated in your php.ini.

Install

Via Composer

$ composer require php-http/curl-client

Documentation

Please see the official documentation.

Testing

$ composer test

Contributing

Please see CONTRIBUTING and CONDUCT for details.

Security

If you discover any security related issues, please contact us at [email protected].

License

The MIT License (MIT). Please see License File for more information.

curl-client's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

curl-client's Issues

Minor changes

Repository:

  • Please use this .gitattributes file
  • Please use this .gitignore file
  • Please use this .travis.yml file (Don't forget to add the before_script which starts the PHP server, scripts should be on new line as yaml struct)
  • Please add Change Log info
  • Please use this CONTRIBUTING guide (note the extra email address)
  • Please update the security section based on the php-http/httplug repo (note the extra email address in the security section)
  • Please remove the VERSION file. Are there any reasons it is there?
  • Documentation should be added instead of the Usage section

CS:

  • Please remove the file header right after the php opening tag, I see no value in it
  • SPL Exceptions should not be imported, but used from the root namespace to avoid too much importing
  • Not sure if @api makes sense here
  • Please add more newlines in the code. Some parts of it is hard to read
  • Please use line comments instead of block comments

@joelwurtz SocketClient might also be affected by the repository issues, haven't checked though.

PHP 8 Support

This will require some work, since the curl functions no longer return resources.

Async requests are randomly failing

Super dummy script that confirms this behaviour:

curl.php

<?php

require_once "vendor/autoload.php";

use GuzzleHttp\Promise;
use GuzzleHttp\Psr7\Request;

$pluginClient = new \Http\Client\Common\PluginClient(
  new \Http\Client\Curl\Client(\Http\Discovery\MessageFactoryDiscovery::find(), \Http\Discovery\StreamFactoryDiscovery::find())
);

$iteration = 1;
$exit = 0;
while (TRUE) {
  if ($iteration > 20) {
    echo('Passed' . PHP_EOL);
    break;
  }

// Initiate each request but do not block
$promises = [
  $pluginClient->sendAsyncRequest(new Request('GET', 'http://httpbin.org/status/500')),
  $pluginClient->sendAsyncRequest(new Request('GET', 'http://httpbin.org/status/404')),
  $pluginClient->sendAsyncRequest(new Request('GET', 'http://httpbin.org/status/404')),
  $pluginClient->sendAsyncRequest(new Request('GET', 'http://httpbin.org/status/404')),
  $pluginClient->sendAsyncRequest(new Request('GET', 'http://httpbin.org/status/404')),
  $pluginClient->sendAsyncRequest(new Request('GET', 'http://httpbin.org/status/404')),
  $pluginClient->sendAsyncRequest(new Request('GET', 'http://httpbin.org/status/404')),
  $pluginClient->sendAsyncRequest(new Request('GET', 'http://httpbin.org/status/200')),
];

  try {
    // Wait for the requests to complete, even if some of them fail
    $results = Promise\settle($promises)->wait();
    foreach ($results as $result) {
      if ($result['state'] === Promise\Promise::REJECTED && is_string($result['reason'])) {
        printf("Iteration: %d. %s\n", $iteration, $result['reason']);
        $exit = 1;
        break 2;
      }
    }
    $iteration++;
  }
  catch (\Exception $e) {
    printf("Exception should not be thrown. %s\n", get_class($e));
    $exit = 1;
    break;
  }
}

exit($exit);

testrunner.sh

#!/usr/bin/env bash

COUNTER=1;
while true; do
    php curl.php
    if [ $? -eq 1 ]; then
        echo Failed after: $COUNTER restarts.;
        break
    fi
    let COUNTER=COUNTER+1
    sleep 1
done

Outputs:

[email protected]:/var/www/html $ ./testrunner.sh 
Passed
Passed
Passed
Passed
Passed
Passed
Passed
Passed
Passed
Iteration: 1. Invoking the wait callback did not resolve the promise
Failed after: 10 restarts.
[email protected]:/var/www/html $ ./testrunner.sh 
Iteration: 1. Invoking the wait callback did not resolve the promise
Failed after: 1 restarts.

PHP version:
PHP 7.1.17 (cli) (built: Apr 27 2018 07:21:42) ( NTS ) Copyright (c) 1997-2018 The PHP Group Zend Engine v3.1.0, Copyright (c) 1998-2018 Zend Technologies with Zend OPcache v7.1.17, Copyright (c) 1999-2018, by Zend Technologies with Xdebug v2.6.0, Copyright (c) 2002-2018, by Derick Rethans
I have tested this with enabled and disabled xDebug.

As you can see the tester either fails in the first iteration or it does not fail at all.

ErrorPlugin and sendAsyncRequest() incompatibility

When using and async requests CurlPromise seems to ignore the ErrorPlugin and throws the original exception instead. For example in case of 404 response ErrorPlugin is supposed to thrown ClientErrorException.

use Http\Client\Curl\Client as Curl;
use Http\Message\MessageFactory\DiactorosMessageFactory;
use Http\Message\StreamFactory\DiactorosStreamFactory;
use Http\Client\Common\Plugin\ErrorPlugin;
use Http\Client\Common\PluginClient;
use Psr\Http\Message\ResponseInterface;

$nosuch = (new DiactorosMessageFactory)->createRequest("GET", "http://google.com/nosuch");
$curl = new Curl(new DiactorosMessageFactory(), new DiactorosStreamFactory());
$client = new PluginClient($curl, [new ErrorPlugin]);

$promise = $client
    ->sendAsyncRequest($nosuch)
    ->then(function (ResponseInterface $response) {
        print date("H:s") . " got foo\n";
        return $response;
    });

try {
    $promise->wait();
} catch (\Exception $exception) {
    print "async: " . get_class($exception) . "\n";
}

try {
    $client->sendRequest($nosuch);
} catch (\Exception $exception) {
    print "sync: " . get_class($exception) . "\n";
}

/* 
async: Http\Client\Exception\RequestException
sync: Http\Client\Common\Exception\ClientErrorException
*/

Requests with a custom port seem to be ignored?

I'm not sure where this gets swallowed, but I'm trying following:

use Http\Client\Curl\Client;
use Http\Client\HttpClient;
use Http\Message\MessageFactory\DiactorosMessageFactory;
use Http\Message\StreamFactory\DiactorosStreamFactory;
use Zend\Diactoros\Request;

$client = new Client(new DiactorosMessageFactory(), new DiactorosStreamFactory());

$url = 'http://localhost:8085/api/v2/messages?start=0&limit=50';
$response = $this->client->sendRequest(
    (new Request($url))->withAddedHeader('Accept', 'application/json')
);

var_dump((string) $response->getBody()); // 404 page not found
file_get_contents($url); // the JSON I wanted

This is while interacting with MailHog.

While debugging, it seems like the curl options are as following:

$options = [
    42 => true,
    19913 => true,
    52 => false,
    84 => 2,
    10002 => 'http://localhost:8025/api/v2/messages?start=0&limit=50',
    11036 => '',
    10023 => [
        0 => 'Host: localhost:8025',
    ],
];

Translated in curl options constants, that would be:

$options = [
    CURLOPT_HEADER => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_FOLLOWLOCATION => false,
    CURLOPT_HTTP_VERSION => 2,
    CURLOPT_URL => 'http://localhost:8025/api/v2/messages?start=0&limit=50',
    CURLOPT_CUSTOMREQUEST => '',
    CURLOPT_HTTPHEADER => [
        0 => 'Host: localhost:8025',
    ],
];

Does anybody have a clue on why this behavior may happen? Note that requests seem to go to port 80.

Can`t install curl-client

Hello. I try to install php-http/curl-client but it fails

composer require php-http/curl-client           
Using version ^0.3.0 for php-http/curl-client
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
Your requirements could not be resolved to an installable set of packages.

  Problem 1
    - Installation request for php-http/curl-client ^0.3.0 -> satisfiable by php-http/curl-client[v0.3.0].
    - php-http/curl-client v0.3.0 requires php-http/message-factory ^0.4@dev -> no matching package found.

Potential causes:
 - A typo in the package name
 - The package is not available in a stable-enough version according to your minimum-stability setting
   see <https://groups.google.com/d/topic/composer-dev/_g3ASeIFlrc/discussion> for more details.

Read <https://getcomposer.org/doc/articles/troubleshooting.md> for further common problems.

Installation failed, reverting ./composer.json to its original content.

process open fd table is full

During sendRequest the Client checks for the existence of a curl handle in the handle property.
If there is one, it calls curl_reset on it and continues with that handle.
The only way to destroy a curl handle is with the destructor of the Client.

The problem with this is how curl_resetworks.
This function is only resetting the options, but not the underlying socket.
From the curl manual

It does not change the following information kept in the handle: live connections, the Session ID cache, the DNS cache, the cookies and shares.

In long running scripts this leads to more and more open sockets in CLOSE_WAIT status.
Depending on the remote servers.
Until all allowed file descriptors are in use.

The workaround is to construct a new Client object for every request, so the destructor gets called when a request is finished.
A better way would be to explicitely close the handle with curl_close after each request and use a new one for the next request.

New release

First of all: thanks for your amazing job! <3

Would you mind releasing a new version in order to have php-httplug ^2.0 support in a stable release?

Thanks a lot!

should we archive this project?

This implementation used to be a demo case for the Httplug interfaces and PSR-18. Meanwhile, guzzle and buzz both implement PSR-18 and symfony also provides an HTTP client that comes with a PSR-18 wrapper.

There are a couple of problems with this client: #55 and #59

Without wanting to devalue the effort put into this, i want to raise the question if we still need to maintain this, or better focus energy on making guzzle | buzz | symfony/http-client as good as possible?

Cannot create the client using `HttpClientDiscovery`

I'm trying to create a small library usign php-http.

Here the composer.json

{
    "require": {
        "php-http/message": "^1.2",
        "php-http/client-common": "^1.1",
        "php-http/discovery": "^0.8.0",
        "puli/composer-plugin": "^1.0@beta,>=1.0.0-beta9"
    },
    "require-dev": {
        "php-http/curl-client": "^1.4"
    },
    "minimum-stability": "beta"
}

I run composer install and all vendors are installed
I've then created a simple demo.php file with this code:

include_once('vendor/autoload.php');

use Http\Discovery\HttpClientDiscovery;
use Http\Discovery\MessageFactoryDiscovery;

$httpClient = HttpClientDiscovery::find();
echo get_class($httpClient);

but I get

PHP Catchable fatal error:  Argument 1 passed to Http\Client\Curl\Client::__construct() must be an instance of Http\Message\MessageFactory, none given, called in /var/www/demo-library/vendor/php-http/discovery/src/HttpClientDiscovery.php on line 23 and defined in /var/www/demo-library/vendor/php-http/curl-client/src/Client.php on line 72

That seems correct because Http\Client\Curl\Client is declared as

    public function __construct(
        MessageFactory $messageFactory,
        StreamFactory $streamFactory,
        array $options = []
    ) {
        $this->messageFactory = $messageFactory;
        $this->streamFactory = $streamFactory;
        $this->options = $options;
    }

Am I doing something wrong ?

Dependencies

@mekras can you tell me why 0.4 message-factory is required? Ever since the 0.3 release we didn't have a single commit.

I also noticed that both the common psr7 implementations are required in the project. Why is that?

Promise logic is broken

  • the return value of rejection callback should reinject to fulfill the promise and recover from the error - this is not supported
  • chaining promises/callbacks is wrong, see https://blog.domenic.me/youre-missing-the-point-of-promises/
  • return from a promise can be a promise too, in which case it should we unwrapped - detailed in #32 already

I'd recommend relying on guzzle/promise to fix all these issues as the implementation there is correct.
See also https://promisesaplus.com/

CURLOPT_FOLLOWLOCATION is ignored

It appears CURLOPT_FOLLOWLOCATION is hardcoded to false here for some strange reason.

Obviously this makes it impossible to directly interact with an entire category of HTTP responses (3xx) in any way with this library.

Was this done intentionally or is it simply an accidental addition? If the latter I can submit a PR to fix.

Note: I realise I'm possibly missing something - is there a way to set this setting at the request stage?

"Out of memory" sending large files

I'm sending a request method PUT

Fatal error: Out of memory (allocated 801898496) (tried to allocate 800528777 bytes) in php-http\curl-client\src\Client.php on line 183

why CURLOPT_POSTFIELDS ?

sample code

$stream = new Stream($file_path, 'rb');
$request = new Request('http://localhost/test.php', 'PUT', $stream);
$response = $client->sendRequest($request);

Description PSR-7 Streams written

Attempting to represent the body of a message as a string can easily consume more memory than intended because the body must be stored completely in memory. Attempting to store the body of a request or response in memory would preclude the use of that implementation from being able to work with large message bodies.

but Client.php reading full body in variable (in memory).
what to do?

Content-Length header is misleading

I'm using the curl-client to run SOAP requests from Phpro/SoapClient.
It includes a debug method that brings up request and response headers, so we can inspect what's going on. I also instantiated a PluginClient with a LoggerPlugin, just to be on the safe side. I'm debugging a supposedly missing Content-Length header my server is complaining about.

A request I'm running reports on those two types of logs the header Content-Length: 1150, while the body actually has 1596 chars. After a lot of digging around, I found that those loggers refer to headers inside the RequestInterface object, while the Content-Length header is silently changed inside Http\Client\Curl\Client::createHeaders() - but only passed as cURL options, and not upwards as well.

For the sanity of other developers, this value should also be changed on the request object. I'm not doing a PR as I don't even know if that object is supposed to be modified, or the best way to do it around here.

CurlMutlirunner burn CPU

given the following snippet

#!/usr/bin/env php
<?php

require_once "vendor/autoload.php";

use Http\Client\Curl\Client;
use Http\Discovery\MessageFactoryDiscovery;
use Http\Discovery\StreamFactoryDiscovery;
$messageFactory = MessageFactoryDiscovery::find();
$client = new Client($messageFactory, StreamFactoryDiscovery::find());

$client->sendAsyncRequest(
    $messageFactory->createRequest('GET', 'https://httpstat.us/200?sleep=3000')
)->wait(true)->getBody()->getContents();

the service" https://httpstat.us/200?sleep=3000 simulate a slow request waiting for 3 seconds.

Where running the script, my application consume A LOT of CPU while waiting for the request

$ time ./index.php                                                                                                                                                                                                                                                                                
real    0m3.190s
user    0m1.638s
sys     0m1.523s

Here CPU is used 1.638s + 1.523s = 3.161s to process this 3.190s script

when applying this durty patch

diff --git a/src/MultiRunner.php b/src/MultiRunner.php
index 545f39a..5872804 100644
--- a/src/MultiRunner.php
+++ b/src/MultiRunner.php
@@ -82,6 +82,7 @@ class MultiRunner
     public function wait(PromiseCore $targetCore = null)
     {
         do {
+            usleep(1);
             $status = curl_multi_exec($this->multiHandle, $active);
             $info = curl_multi_info_read($this->multiHandle);
             if (false !== $info) {

I get

$ time ./index.php
real    0m3.163s
user    0m0.234s
sys     0m0.211s

now, the CPU is used 0.445s to process the same request.

Return value of Http\Client\Curl\PromiseCore::getException() must implement interface Throwable, instance of Nyholm\Psr7\Response

PHP message: PHP Fatal error: Uncaught TypeError: Return value of Http\Client\Curl\PromiseCore::getException() must implement interface Throwable, instance of Nyholm\Psr7\Response returned in /var/www/releases/20191015093018/vendor/php-http/curl-client/src/PromiseCore.php:194

Stack trace:
#0 /var/www/releases/20191015093018/vendor/php-http/curl-client/src/CurlPromise.php(103): Http\Client\Curl\PromiseCore->getException()
#1 /var/www/releases/20191015093018/vendor/sentry/sentry/src/Transport/HttpTransport.php(110): Http\Client\Curl\CurlPromise->wait()
#2 /var/www/releases/20191015093018/vendor/sentry/sentry/src/Client.php(117): Sentry\Transport\HttpTransport->send(Object(Sentry\Event))
#3 /var/www/releases/20191015093018/vendor/sentry/sentry/src/Client.php(103): Sentry\Client->captureEvent(Array, Object(Sentry\State\Scope))

Promise chaining is broken

Chaining promises appears to be either broken or unsupported. The documentation claims general Promise/A+ compliance, so I'm assuming the former. Regardless, I noticed that the standard ErrorPlugin was not behaving properly when curl-client was used as the client implementation. Execution of the plugin stack simply stopped when ErrorPlugin threw an exception.

Here's an example of the issue:

use Http\Client\Common\PluginClient;
use Http\Client\Common\Plugin\ErrorPlugin;
use Http\Client\Curl\Client;
use Zend\Diactoros\Request;
use Zend\Diactoros\Response\TextResponse;

$client = new PluginClient(new Client(), [new ErrorPlugin()]);
$promise = $client->sendAsyncRequest(new Request('http://localhost:10000/bogus.php', 'GET'))->then(null, function () {
    return new TextResponse('test');
});

// Results in ClientErrorException, which should be averted by the onRejected callback above.
var_dump((string) $promise->wait()->getBody());

After digging through the code, it looks like the root problem is that all promises in a chain share the same PromiseCore object. PromiseCore simply executes all onFulfilled callbacks from all promises in the order they were created when a valid HTTP response is received, or all onRejected callbacks if the request was unsuccessful. As such, all promises that share that core return the same response or throw the same exception. This approach has a number of problems:

  • Promise results are not immutable.
  • Either all onFulfilled callbacks are run, or all onRejected callbacks are run. There is no evaluation of which callback to execute at each step of the chain.
  • Multiple chains branching off the same promise interfere with each other.
  • Exceptions thrown by onFulfilled callbacks (like with ErrorPlugin) cause catastrophic failures.

Point 4 also leads to a far more insidious error that can occur when issuing multiple asynchronous requests at once. This might technically qualify as a separate issue since it stems from the handling of curl_multi_info_read, but I'm mentioning it here since it's a consequence of exceptions thrown from onFulfilled callbacks. Exceptions triggered during the resolution of one response can be thrown when requesting the resolution of a completely different response.

use Http\Client\Common\PluginClient;
use Http\Client\Common\Plugin\ErrorPlugin;
use Http\Client\Curl\Client;
use Zend\Diactoros\Request;

$client = new PluginClient(new Client(), [new ErrorPlugin()]);
$bad = $client->sendAsyncRequest(new Request('http://localhost:10000/bogus.php', 'GET'));
$good = $client->sendAsyncRequest(new Request('http://localhost:10000/server.php', 'GET'));

// Results in ClientErrorException from failure of $bad request.
var_dump((string) $good->wait()->getBody());

I'm presently working on a fix, but I wanted to make the issue known.

Add async support to the cURL client

Just found some examples using the curl_multi_init the perform async requests here: https://github.com/stil/curl-easy/

The request queue is defined in the file: https://github.com/stil/curl-easy/blob/master/src/cURL/RequestsQueue.php

I think that the idea is here but we can't use the whole logic here. In cURL sync processing and async processing is something different and maybe must be handled in different objects...

If the curl_reset function is used in the client, we must ensure that there is no workaround with async mode (see #1).

Incompatibility with `php-http/message` package

http://docs.php-http.org/en/latest/clients/curl-client.html
I just followed steps on the page.

And there are 2 packages now in my composer.json:

"require": {
    "php-http/curl-client": "^2.0",
    "php-http/message": "^1.7"
}

http://docs.php-http.org/en/latest/clients/curl-client.html#usage
I've copied one of these code snippets and I noticed there is an incompatibility.

use Http\Client\Curl\Client;
use Http\Message\MessageFactory\DiactorosMessageFactory;
use Http\Message\StreamFactory\DiactorosStreamFactory;

$client = new Client(new DiactorosMessageFactory(), new DiactorosStreamFactory());

php-http/curl-client package is working with Psr\Http\Message\... interfaces. But php-http/message package implements Http\Message\... interfaces.

When I changed the version "php-http/curl-client": "^2.0" to "^1.7", the incompatibility is solved. Curl client works with Http\Message\... interfaces on ^1.7 version.

So, what is standard?

Which versions of php-http packages work compatible with each other?

Request not using CURLOPT_POSTFIELDS have content-length set to 0

I don't know if this is a normal behaviour but if I send a large body (a file > 1MB) the CURLOPT_POSTFIELDS option is not used but the CURLOPT_READFUNCTION instead and in the createHeaders method the content-length header is set to 0.

Doing this, my server doest not reveive data, all is empty.

So 2 possible scenario :

  • my server is not well configured and does not deal correctly with the request received
  • the content-length should be set with the body size if present.

If I set the content-length header with the body, my server receive all the data and have an expected behaviour, I put this for my test :

if ('content-length' === $header) {
                $values = [0];
                if (array_key_exists(CURLOPT_POSTFIELDS, $options)) {
                    $values = [strlen($options[CURLOPT_POSTFIELDS])];
                } else {
                    $body = $request->getBody();
                    $bodySize = $body->getSize();
                    if ($bodySize > 0) {
                        $values = [(string) $bodySize];
                    }
                }
            }

but maybe the CURLOPT_INFILESIZE is a better option

Invalid "Expect" header

I would like to add the ability to handle a 100-continue status from the server as I just hit a bug where I would get the following response:

HTTP/1.1 100 Continue

HTTP/1.1 200 OK
Date: Tue, 08 Mar 2016 10:34:43 GMT
Server: Apache/2.2.22 (Debian)
X-Powered-By: PHP/5.4.45-0+deb7u2
etc

It gets the initial header line and then throws an exception parsing HTTP/1.1 200 OK as a header due to the missing colon.

I fixed it by setting an empty "Expect" header. Somewhere an "Expect 100-continue" was being added to my request (I wasn't explicitly setting this).

Could we check the initial status line for this status, and then discard the first 2 lines and resume parsing the actual response?

coverage / scrutinizer build no longer working

in #71 we switched from travis-ci to github actions. scrutinizer starts to fail, but looking at the last travis-ci builds, scrutinizer did not happen for a while. the shields on the README that rely on scrutinizer also don't work.

is it ok if I remove the coverage flag and scrutinizer shields?

Combining CurlClient with StopwatchPlugin causes Promise onRejected handler to never be invoked

Not sure which project to file this under, but curl-client seems the most appropriate as this behavior does not occur with guzzle6-adapter.

Basically, if the Stopwatch plugin is attached to the client (as it would be in a default configuration running under Symfony with httplug-bundle), the CurlClient ignores the reject handler on any request.

Test script:

use Http\Client\Curl\Client;
use Http\Client\Common\PluginClient;
use Http\Client\Common\Plugin\ErrorPlugin;
use Http\Client\Common\Plugin\StopwatchPlugin;
use GuzzleHttp\Psr7\Request;
use Symfony\Component\Stopwatch\Stopwatch;

$clients = [
    'normal_client' => new PluginClient(new Client(), [
        new ErrorPlugin()
    ]),
    'stopwatch_client' => new PluginClient(new Client(), [
        new ErrorPlugin(),
        new StopwatchPlugin(new Stopwatch()),
    ]),
];

$urls = [
    'http://www.google.com/',
    'http://localhost:8000/asdfasdfasddf',
    'http://www.nosuch.domain/foo'
];
$promises = [];

foreach ($clients as $clientName => $client) {
    foreach ($urls as $url) {
        $promises[] = $client->sendAsyncRequest(new Request('GET', $url))
               ->then(
                    function ($response) use ($clientName, $url) {
                        echo "[$clientName] $url SUCCESS " . $response->getStatusCode() . ' ' . $response->getReasonPhrase() . "\n";
                    }, function ($e) use ($clientName, $url) {
                        echo "[$clientName] $url ERROR " . $e->getMessage() . "\n";
                    }
                );
    }
}

foreach ($promises as $promise) {
    try {
        $promise->wait();
    } catch (\Exception $e) {
    }
}

Expected output:

[normal_client] http://www.nosuch.domain/foo ERROR Could not resolve host: www.nosuch.domain
[normal_client] http://localhost:8000/asdfasdfasddf ERROR Not Found
[normal_client] http://www.google.com/ SUCCESS 200 OK
[stopwatch_client] http://www.nosuch.domain/foo ERROR Could not resolve host: www.nosuch.domain
[stopwatch_client] http://localhost:8000/asdfasdfasddf ERROR Not Found
[stopwatch_client] http://www.google.com/ SUCCESS 200 OK

Actual output:

[normal_client] http://www.nosuch.domain/foo ERROR Could not resolve host: www.nosuch.domain
[normal_client] http://localhost:8000/asdfasdfasddf ERROR Not Found
[normal_client] http://www.google.com/ SUCCESS 200 OK
[stopwatch_client] http://www.google.com/ SUCCESS 200 OK

If you swap out Curl\Client for Guzzle6\Client, the expected output (with slightly different error messages) is seen.

Response body not available on 4XX and 5XX errors when using ErrorPlugin

When consuming API's the response body ofter includes additional information on 4XX and 5XX errors. Currently both sendRequest() and wait() throw RequestException which does not allow accessing the response. This would be resolved by throwing HttpException instead.

Although I am not sure if sendRequest() actually ever throws RequestException even though code is there. Documentation says clients always return response and ErrorPlugin should be used to throw exceptions.

Different clients seem to be behaving differently. For example React adapter returns response with 404 status instead of throwing.

https://gist.github.com/tuupola/9862b11cc8fb6f7131545fffea900ee5

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.