diff --git a/src/Client.php b/src/Client.php index f3db692..62e3849 100644 --- a/src/Client.php +++ b/src/Client.php @@ -203,6 +203,13 @@ public function process($address): void } $request->setOptions($options)->attachConnection($connection); + // Hand the in-flight Request to the caller (e.g. to close its connection for cancellation). + // Fires again with the new Request when a redirect is followed, so the caller always + // holds the live one. No behavior change unless the 'request' option is set. + if (isset($options['request'])) { + call_user_func($options['request'], $request); + } + $client = $this; $request->once('success', function($response) use ($task, $client, $request) { $client->recycleConnectionFromRequest($request, $response); @@ -279,8 +286,16 @@ public function recycleConnectionFromRequest(Request $request, ?Response $respon $connection->onConnect = $connection->onClose = $connection->onMessage = $connection->onError = null; $request_header_connection = strtolower($request->getHeaderLine('Connection')); $response_header_connection = $response ? strtolower($response->getHeaderLine('Connection')) : ''; - // Close Connection without header Connection: keep-alive - if ('keep-alive' !== $request_header_connection || 'keep-alive' !== $response_header_connection || $request->getProtocolVersion() !== '1.1') { + // RFC 7230 §6.3: HTTP/1.1 connections are persistent by default — servers usually OMIT + // the Connection header. Close only when either side explicitly says "close", the + // protocol is not 1.1, or there is no response (transport-level failure). + // (Previously required both sides to literally send "Connection: keep-alive", which + // almost never happens with real-world servers, so the pool never reused connections.) + $shouldClose = !$response + || $request->getProtocolVersion() !== '1.1' + || str_contains($request_header_connection, 'close') + || str_contains($response_header_connection, 'close'); + if ($shouldClose) { $connection->close(); } $request->detachConnection($connection);