php-http / guzzle6-adapter Goto Github PK
View Code? Open in Web Editor NEWGuzzle 6 HTTP adapter
Home Page: http://httplug.io
License: MIT License
Guzzle 6 HTTP adapter
Home Page: http://httplug.io
License: MIT License
Considering the previous six conversations about this topic have been closed as won't fix, I was reluctant to bring this up again. But not all facts have been brought up in the previous discussions and I believe explicitly disallowing PHP 8 is not the right decision. Please let me explain.
It's true the version constraints on Guzzle 5 and 6 were wrong, they unintentionally allow PHP 8. Guzzle 5 does not work on PHP 8 and the version constraints have been updated [1] accordingly. Guzzle 6 is in security-only mode so did not receive the PHP 8 fixes Guzzle 7 had and PHP 8 is not officially supported. But accidentally, it does work. Looking at the changes done to Guzzle 7 to support PHP 8 [2], they only touch annotations and tests.
While nobody will guarantee every part of the library works on PHP 8, in practice Guzzle 6 runs fine on PHP 8, including the async functions used by this adapter. Also, Drupal 9.1 officially supports PHP 8 and they do that using Guzzle 6.5.5.
So, yes it's good advice to tell people to upgrade to Guzzle 7. A warning about using Guzzle 6 on PHP 8 might be appropriate too. But I don't see why people depending on Guzzle 6 should be forced to fork this repository if they have a depedency on this adapter.
If there actually is an incompatibility I'd appreciate it if someone could point out what part is incompatible.
[1] guzzle/guzzle#2834
[2] https://github.com/guzzle/guzzle/pull/2715/files
Description
It would be great if this adapter could support PHP 8. Guzzle 6 allows it in composer.json
Q | A |
---|---|
Bug? | no |
New Feature? | yes |
If you want to use this Adapter, then you have to instantiate the Guzzle Client and pass through the constructor.
Use the php-http/discovery
package to auto discover the Guzzle Client and create it.
Add the php-http/discovery
package as requirement.
Add documentation.
Hi,
I am building an API Client at the moment and I am trying to send a POST request with multipart data.
The data I am trying to send looks like the following.
array:1 [▼
"multipart" => array:2 [▼
0 => array:2 [▼
"name" => "file"
"contents" => stream resource @135 ▶} // contents is fopen($file, 'r')
]
1 => array:2 [▼
"name" => "apikey"
"contents" => "mysecretapikey"
]
]
]
Using Guzzle 6 Standalone I would do it like this and this works just fine.
$guzzle = new \GuzzleHttp\Client($options);
$response = $guzzle->request('POST', $uri, ['multipart' => $body]);
dump($response->getBody()->getContents());
Now when trying to use the adapter I came up with this.
$guzzle = new \GuzzleHttp\Client($options);
$adapter = new \Http\Adapter\Guzzle6\Client($guzzle);
// For form_params I could do ...
// $body = \GuzzleHttp\Psr7\stream_for(http_build_query($params));
// but how am I going to add multipart data to a request?
$request = new \GuzzleHttp\Psr7\Request($method, $uri, $headers, $body);
$pluginClient = new HttpMethodsClient(
new PluginClient($adapter, $this->plugins),
$this->messageFactory
);
$response = $adapter->sendRequest($request);
Sending the request with the adapter always results in a 403 Forbidden because the multipart data gets useless after building it with stream_for
. So how do I send a multipart
request with this adapter since there is no request method?
Hi there,
When using the Guzzle 6 adapter, the response body of some URLs will be empty, whereas if I just use the Guzzle HTTP client alone, the content comes through just fine.
Before you ask, I'm using the sendRequest()
method, not the sendAsyncRequest()
one, so I don't have to explicitly do Promise
handling.
Steps to reproduce:
Add the following to a composer.json
file and run composer install
:
{
"require": {
"php-http/message": "^1.2",
"php-http/guzzle6-adapter": "^1.1"
}
}
Proof of concept code:
<?php
require 'vendor/autoload.php';
use GuzzleHttp\Client;
use Http\Adapter\Guzzle6\Client as HttpClient;
use Http\Message\MessageFactory\GuzzleMessageFactory as MessageFactory;
$sources = [
// Only the Guzzle HTTP client can fetch the contents of this URL
'http://www.artstation.com/artwork.rss',
// Both methods can fetch the contents of this URL
'http://feeds2.feedburner.com/webdesignerdepot',
];
$timeout = 6;
foreach ($sources as $url) {
//
// Guzzle HTTP client
//
$client = new Client([
'timeout' => $timeout,
'connect_timeout' => $timeout,
]);
$response = $client->request('GET', $url);
$guzzle = $response->getBody()->getContents();
//
// Guzzle 6 HTTPlug adapter
//
$client = HttpClient::createWithConfig([
'timeout' => $timeout,
'connect_timeout' => $timeout,
]);
$message = new MessageFactory;
$request = $message->createRequest('GET', $url);
$response = $client->sendRequest($request);
$httplug = $response->getBody()->getContents();
// Uncomment to see the actual contents
// var_dump($guzzle, $httplug);
// Show the fetched URL and the MD5 hash of the content we got
// d41d8cd98f00b204e9800998ecf8427e is the MD5 hash of an empty string
echo $url.':'.PHP_EOL.md5($guzzle).PHP_EOL.md5($httplug).PHP_EOL.PHP_EOL;
}
Edited:
Versions tested:
php-http/guzzle6-adapter
1.1.1guzzlehttp/guzzle
6.0.0, 6.0.1, 6.0.2, 6.1.0, 6.1.1, 6.2.0, 6.2.1PHP version: PHP 7.4.8 (But should happen in any other I guess)
Description
When I use guzzle6, it follows redirects, when I use this adapter, it doesn't follow redirects by default.
How to reproduce
<?php
require "vendor/autoload.php";
$uri = new \GuzzleHttp\Psr7\Uri('https://embed.spotify.com/oembed?url=https%3A%2F%2Fopen.spotify.com%2Fplaylist%2F37i9dQZF1DWYtg7TV07mgz%3Fsi%3DOTx1DyDhSPK5q5V1CTR5NQ&format=json');
$request = new \GuzzleHttp\Psr7\Request('GET',$uri);
$goodOldGuzzle = new \GuzzleHttp\Client();
$guzzleAdapter = \Http\Adapter\Guzzle6\Client::createWithConfig([]);
var_dump(
$goodOldGuzzle->send($request)->getStatusCode(),
$guzzleAdapter->sendRequest($request)->getStatusCode(),
);
Responses will be 200
and 304
.
e.g.: https://phpsandbox.io/n/bitter-frog-l2q1
Possible Solution
Sorry, I didn't dig much into this library so no idea. My guess is to not change the default options of guzzle maybe ? 🤷
Additional context
I made this one sandbox thingy to prove my problem.
https://phpsandbox.io/n/bitter-frog-l2q1
PHP 7.4
Receiving this error in the laravel log
[2020-07-06 09:20:11] production.ERROR: Interface 'Http\Client\HttpClient' not found {"exception":"[object] (Error(code: 0): Interface 'Http\Client\HttpClient' not found at /home/livefreshr/public_html/livefreshr.com/releases/113/vendor/php-http/guzzle6-adapter/src/Client.php:21)
[stacktrace]
Any idea how to resolve?
Q | A |
---|---|
Bug? | yes |
New Feature? | no |
Version | v1.1.1 |
Documentation says:
By default clients will always return a PSR-7 response instead of throwing a HttpException. Write your application to check the response status or use the Error Plugin to make sure the HttpException is thrown.
However both sendRequest()
and sendAsyncRequest()
throw an HttpException
instead of returning the response. See example script below for comparing behaviour of different clients and 404
response.
https://gist.github.com/tuupola/527823d9a37848a6b35621204ec7b121
Call to sendRequest()
should return response object with 404
status. Promise from sendAsyncRequest()
call should resolve to fulfilled promise and receive response object with 404
status.
use Http\Adapter\Guzzle6\Client as Guzzle;
use GuzzleHttp\Client as GuzzleClient;
use Http\Message\MessageFactory\DiactorosMessageFactory;
use Http\Message\StreamFactory\DiactorosStreamFactory;
use Psr\Http\Message\ResponseInterface;
$nosuch = (new DiactorosMessageFactory)->createRequest("GET", "http://google.com/nosuch");
$guzzle = new Guzzle(new GuzzleClient);
$promise = $guzzle
->sendAsyncRequest($nosuch)
->then(function (ResponseInterface $response) {
print "Guzzle async status: ". $response->getStatusCode() . "\n";
return $response;
});
try {
$promise->wait();
print "Guzzle promise state: " . $promise->getState() . "\n";
} catch (\Exception $exception) {
print "Guzzle async exception: " . get_class($exception) . "\n";
}
try {
$response = $guzzle->sendRequest($nosuch);
print "Guzzle sync status: " . $response->getStatusCode() . "\n";
} catch (\Exception $exception) {
print "Guzzle sync exception: " . get_class($exception) . "\n";
}
/*
$ php guzzle.php
Guzzle async exception: Http\Client\Exception\HttpException
Guzzle sync exception: Http\Client\Exception\HttpException
*/
Description
Guzzle 7 is now released. The differences between 6 and 7 are minimal, and having updated a few private libraries/projects it's generally trivial to support both.
PHP8 has just been released. According to composer.json, this repo requires "php": "^7.1"
. Any plans to make it work with PHP8?
Please express your acceptance/rejection with a +/-1
it seems to set up guzzle in a way that it throws exceptions when receiving a 403 response. that exception is correctly converted to a php-http exception, but still not expected if i understand correctly how we wanted things to be set up. (i did not add any plugins, so the plugin to convert error responses to exceptions is not there.)
it seems that we have the Middleware::httpErrors in the guzzle stack. looking at https://github.com/php-http/guzzle6-adapter/blob/master/src/Guzzle6HttpAdapter.php#L32 and https://github.com/guzzle/guzzle/blob/master/src/Client.php#L64-L65 i think we would need to provide an empty or reduced handler stack. this in turn means that using php-http results in a very bare-bone - but the alternative is inconsistent behaviour.
found this while working on FriendsOfSymfony/FOSHttpCache#257 - to see a stack trace, look for Http\Client\Exception\HttpException: Client error:
GET http://localhost:6181/user_context.php`resulted in a403 Forbidden
response:` in the travis output.
Q | A |
---|---|
Bug? | yes |
New Feature? | no |
Version | v1.1.1 |
When chaining a promise and throwing an exception, the exception is wrapped inside an Exception
with the message Invalid exception returned from Guzzle6
.
That my exception is thrown directly.
$httpClient->sendAsyncRequest(new Request('GET', 'https://www.example.com/'))
->then(function (ResponseInterface $response) {
throw new \Exception('Some problem');
})
->wait();
... results in a RuntimeException
with the message Invalid exception returned from Guzzle6
(with my Exception
as previous
).
The exception wrapping only makes sense when the actual request is being made (anything beyond that is not Guzzle). Either then return a different promise type in Promise::then()
guzzle6-adapter/src/Promise.php
Line 79 in a56941f
guzzle6-adapter/src/Promise.php
Lines 64 to 68 in a56941f
(Related, this will also hide any fatal errors (eg TypeError
) behind the meaningless 'Reason returned from Guzzle6 must be an Exception' exception.)
Description
Somewhat by design, the Guzzle6-adapter is incompatible with Guzzle7. Unfortunately it means that a dependent library/module cannot work with with Guzzle6 and 7 at the same time if it requires the features from the guzzle6-adapter.
So instead, I propose creating an empty version (v3) that requires Guzzle7. This will allow downstream users of this library to work with both versions of Guzzle. (require: guzzle6-adapter: ^2|^3; Guzzle: ^6|^7)
Description
While using the guzzle6-adapter in opentelemetry-php I noticed that PHP 8.0 is not yet supported. I think the version constraint here might need to be changed to >=7.1
, but there might be some additional testing necessary.
Example
You should be able to see this dependency resolution in this github action installation attempt:
https://github.com/bobstrecansky/opentelemetry-php/pull/6/checks?check_run_id=1364216037#step:6:14
Currently either Http or Network exceptions are thrown, however if a response is not available, it is not necessarily a network error.
Defining the php-http/client-implementation
virtual package dependency interactively does not work, as it is unable to resolve a package.
Setting up both values manually in composer.json
like so:
"require": {
...
"php-http/client-implementation": "^1.0"
},
"require-dev": {
...
"php-http/guzzle6-adapter": "^1.0"
},
also fails with the following errors:
Problem 1
- The requested package php-http/client-implementation could not be found in any version, there may be a typo in the package name.
Problem 2
- The requested package php-http/guzzle6-adapter could not be found in any version, there may be a typo in the package name.
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.
@dbu says it's due to the adapter being outdated and providing the wrong thing.
FYI I am using php-http to replace the concrete Guzzle 5 dependency in this lib. The whole thing will result in a post on SitePoint.
Please add support 8.0 to composer.json
Q | A |
---|---|
Bug? | yes |
New Feature? | no |
Version | 2.0 |
Hello,
According to PSR-18
A Client MUST throw an instance of Psr\Http\Client\ClientExceptionInterface if and only if it is unable to send the HTTP request at all or if the HTTP response could not be parsed into a PSR-7 response object.
There was a tag made today that Guzzle-6 adapter is PSR-18 compliant https://github.com/php-http/guzzle6-adapter/releases/tag/v2.0.0
I am trying to understand how this works since there are cases that guzzle throws non PSR exception.
https://github.com/guzzle/guzzle/blob/master/src/Handler/StreamHandler.php#L55
https://github.com/guzzle/guzzle/blob/master/src/Handler/StreamHandler.php#L252
https://github.com/guzzle/guzzle/blob/master/src/Handler/CurlFactory.php#L458
https://github.com/guzzle/guzzle/blob/master/src/Handler/CurlFactory.php#L531
Maybe I am wrong or I miss something..
Q | A |
---|---|
Version | 1.1.1 |
Post is absolutely useless via package, because not sending any options, so is it expected behaviour?
first server has such code:
var_export($_POST);die;
on second server such code:
$config = [
'connect_timeout' => 10,
'timeout' => 10,
];
$guzzle = new \GuzzleHttp\Client($config);
$adapter = new \Http\Adapter\Guzzle6\Client($guzzle);
$d["data1"] = rand(1, 1000);
$d["data2"] = rand(1, 1000);
$request = new \GuzzleHttp\Psr7\Request('POST', 'http://testing.dev', ['form_params' => $d]);
$response = $adapter->sendRequest($request);
var_dump($response->getBody()->getContents());
output:
array (
)
Taking from original class, \guzzlehttp\guzzle\src\Client.php:92
public function sendAsync(RequestInterface $request, array $options = [])
there is an option parameter, which catches any additional info, including form_data. But your code
public function sendAsyncRequest(RequestInterface $request)
{
$promise = $this->client->sendAsync($request);
return new Promise($promise, $request);
}
does not pass such info. Neither in sendRequest
method
Q | A |
---|---|
Bug? | no |
New Feature? | no |
Version | master |
Is there an ETA for stabilisation of the 1.2 minor version? I have a project that is currently relying on the unstable 1.2.x-dev branch, but I would love to use a stable tag.
Description
Due to some conflics with guzzlehttp/guzzle version I cannot update other packages like google/cloud.
php-http/guzzle6-adapter v2.0.2 requires guzzlehttp/guzzle ^6.0
Possible Solution
Please add guzzlehttp/guzzle ^7.0 to the composer dependencies.
Before releasing 2.0, I think we should release 1.2 as the last version in the 1.x release branch.
Q | A |
---|---|
New Feature? | no |
Version | v2.0.0 |
Composer is giving me a error return then the package isn't intalled.
Your requirements could not be resolved to an installable set of packages.
Problem 1
- php-http/guzzle6-adapter v2.0.0 requires php-http/httplug ^2.0 -> satisfiable by php-http/httplug[v2.0.0].
- php-http/guzzle6-adapter v2.0.1 requires php-http/httplug ^2.0 -> satisfiable by php-http/httplug[v2.0.0].
- Conclusion: don't install php-http/httplug v2.0.0
- Installation request for php-http/guzzle6-adapter ^2.0 -> satisfiable by php-http/guzzle6-adapter[v2.0.0, v2.0.1].
Installation failed, reverting ./composer.json to its original content.
Just exec the command: composer require php-http/guzzle6-adapter
Could someone help me with this issue, please?
Q | A |
---|---|
Bug? | yes |
New Feature? | no |
Version | Specific version or SHA of a commit |
What is the actual behavior?
Downstream requirement of package breaks suddenly, as PHP version change was not announced previously, and there isn't a jump in version number.
What is the behavior you expect?
Version number increment to prevent packages requiring this library from breaking.
What are the steps to reproduce this bug? Please add code examples,
screenshots or links to GitHub repositories that reproduce the problem.
Many packages, for example SparkPost/php-sparkpost have a reliance on this package.
Recently, without warning the PHP requirements for this package were arbitrarily raised without increasing the version number for the package correspondingly. This lead to unexpected issues with composer unable to work out of the blue on PHP versions 5.5, 5.6 and 7.0 branches.
While I don't personally agree with the idea of raising PHP version requirements just for the sake of doing so (as there doesn't appear to be any sort of code requirement for this), what's harmful here is a package that wasn't updated in over a year suddenly updated, and particularly without increasing their version number), so that new installs of the same version number suddenly start failing.
This commit should be reverted, and then a new minor release tagged, so that there is a final version with PHP 5.5 - 7.0 compat. Then if desired it could be re-added with a corresponding tag of a new major release, so that people running composer update don't find things suddenly broken.
Thought this might be a good place to mention it.
You could always switch over to Github pages?
Q | A |
---|---|
Bug? | yes |
New Feature? | no |
Version | guzzlehttp/guzzle 6.3.3 guzzlehttp/promises v1.3.1 guzzlehttp/psr7 1.4.2 php-http/guzzle6-adapter v1.1.1 |
If the first HTTP call (promise) returns HTTP >= 400 (which triggers and exception in Guzzle by default) then the adapter throws the exception for the first HTTP call instead it would return the result of all promises.
<?php
require_once "vendor/autoload.php";
use GuzzleHttp\Client;
use GuzzleHttp\Promise;
use GuzzleHttp\Psr7\Request;
$client = new Client(['base_uri' => 'http://httpbin.org/']);
$pluginClient = new \Http\Client\Common\PluginClient(
new Http\Adapter\Guzzle6\Client($client), []
);
// Initiate each request but do not block
$promises = [
'image' => $pluginClient->sendAsyncRequest(new Request('GET', 'status/404')),
'png' => $pluginClient->sendAsyncRequest(new Request('GET', 'status/200')),
'jpeg' => $pluginClient->sendAsyncRequest(new Request('GET', 'status/200')),
'webp' => $pluginClient->sendAsyncRequest(new Request('GET', 'status/404'))
];
try {
// Wait for the requests to complete, even if some of them fail
$results = Promise\settle($promises)->wait();
}
catch (\Exception $e) {
echo $e->getMessage();
}
exit(0);
settle() should "Wait for the requests to complete, even if some of them fail". So even if the first HTTP call returns HTTP >= 400 an exception should not be thrown.
http://docs.guzzlephp.org/en/stable/quickstart.html#concurrent-requests
<?php
require_once "vendor/autoload.php";
use GuzzleHttp\Client;
use GuzzleHttp\Promise;
$client = new Client(['base_uri' => 'http://httpbin.org/']);
// Initiate each request but do not block
$promises = [
'image' => $client->getAsync('status/404'),
'png' => $client->getAsync('status/200'),
'jpeg' => $client->getAsync('status/200'),
'webp' => $client->getAsync('status/404')
];
try {
// Wait for the requests to complete, even if some of them fail
$results = Promise\settle($promises)->wait();
}
catch (\Exception $e) {
echo $e->getMessage();
}
exit(0);
See the code snippets above.
For the first sight it seems the adapter should also wrap the result of all promises to a Promise object and return that instead of returning the results directly. At least this is what Guzzle Promise's working code indicates for me.
https://github.com/guzzle/promises/blob/v1.3.1/src/Promise.php#L69
PHP version: x.y.z (hint: php --version
)
Description
How to reproduce
conflict with pusher package.
Possible Solution
just replace "guzzlehttp/guzzle": "^7.2" add this line into the composer.json
Additional context
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.