如何为Guzzle实现基于Swoole协程HttpClient的适配器?
Hey there, let's dig into why your custom handler only works with a MockServer proxy, and cover some practical fixes and ready-to-use alternatives.
First: Debugging the "No Response Without Proxy" Issue
The fact that it works via MockServer suggests your handler's core logic is functional, but there's a mismatch when talking directly to real endpoints. Here are the most common culprits and how to debug them:
1. Coroutine Compatibility Gaps
Guzzle's default handlers are built for synchronous PHP, but Swoole's coroutine environment requires non-blocking, context-aware code. Ask yourself:
- Are you using Swoole's
Co\Http\Clientinstead of traditionalcurlor stream functions? Mixing blocking calls will break coroutine context and lead to hanging requests. - Did you properly handle the Swoole client's lifecycle? Remember to call
$client->connect()before sending, and$client->recv()to fetch the response—missing either will result in no data.
2. Response Parsing Logic
MockServer responses are often simpler than real endpoints. Check if your handler correctly parses:
- Status codes: Real APIs might return non-200 codes (like 3xx redirects, 4xx errors) that your handler isn't handling (e.g., not reading the body on error responses).
- Headers: Real endpoints often send complex headers (chunked encoding, multiple Set-Cookie headers) that your parsing code might be ignoring or mangling.
- Body encoding: Are you correctly converting Swoole's raw response body to Guzzle's
StreamInterface? For example, usingGuzzleHttp\Psr7\Utils::streamFor($client->body)instead of passing raw strings directly.
3. Debugging Steps to Pinpoint the Issue
- Enable Swoole Debug Logs: Add this at the start of your script to get low-level connection/request details:
Check the log for errors like connection timeouts, DNS failures, or invalid response parsing.Co::set([ 'log_level' => SWOOLE_LOG_DEBUG, 'log_file' => '/tmp/swoole_debug.log' ]); - Add Logs to Your Handler: Insert debug prints at key stages:
// Before sending the request error_log("Sending request to: {$uri} with headers: " . json_encode($headers)); // After receiving response if ($response = $client->recv()) { error_log("Raw Swoole response: " . json_encode($response)); } else { error_log("Swoole client error: " . $client->errCode . " - " . swoole_strerror($client->errCode)); } - Use Packet Capture: Tools like
tcpdumpor Wireshark can confirm if your request is actually reaching the endpoint, and if a response is being sent back. If no response is coming, it's a network/endpoint issue; if it is coming but your handler ignores it, the problem is in your parsing logic.
Ready-Made Swoole Guzzle Adapters to Avoid Reinventing the Wheel
Instead of debugging your custom handler, there are battle-tested adapters that handle all the coroutine edge cases for you:
1. swooletw/guzzle-swoole
A lightweight, dedicated adapter for Guzzle 6+ that replaces the default handler with a Swoole coroutine-compatible one. It handles connection pooling, redirects, and error handling out of the box.
Usage Example:
use GuzzleHttp\Client; use SwooleTW\Http\Guzzle\CoroutineHandler; use GuzzleHttp\HandlerStack; $handler = new CoroutineHandler(); $stack = HandlerStack::create($handler); $client = new Client(['handler' => $stack]); // Use inside a coroutine go(function () use ($client) { $response = $client->get('https://api.example.com'); echo $response->getBody()->getContents(); });
2. hyperf/guzzle
Part of the Hyperf framework's ecosystem, this adapter includes advanced features like coroutine context isolation, connection pooling, and support for PSR-7 messages. It works seamlessly outside the Hyperf framework too.
Usage Example:
use GuzzleHttp\Client; use Hyperf\Guzzle\CoroutineHandler; use GuzzleHttp\HandlerStack; $stack = HandlerStack::create(new CoroutineHandler()); $client = new Client(['handler' => $stack]); go(function () use ($client) { $response = $client->post('https://api.example.com', [ 'json' => ['key' => 'value'] ]); var_dump($response->getStatusCode()); });
3. friendsofhyperf/guzzle
A community-maintained extension of hyperf/guzzle with additional features like retry middleware, circuit breakers, and more—great for production-grade applications.
Final Notes
Start by debugging your custom handler's coroutine compliance and response parsing using the steps above. If you're looking for a quick, production-ready solution, any of the adapters listed will save you time and avoid common pitfalls.
内容的提问来源于stack exchange,提问作者chluehr

