Skip to content

Ensure CURLE_RECV_ERROR (56) throws ClientException #43197

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from

Conversation

pluk77
Copy link
Contributor

@pluk77 pluk77 commented Sep 27, 2021

Fix issue #42806

Q A
Branch? 5.3
Bug fix? Yes
New feature? No
Deprecations? No
Tickets Fix #42806
License MIT
Doc PR symfony/symfony-docs#...

CURLE_RECV_ERROR (56) caused by proxy throws an TransportException instead of ClientException preventing further debugging

@carsonbot
Copy link

Hey!

To help keep things organized, we don't allow "Draft" pull requests. Could you please click the "ready for review" button or close this PR and open a new one when you are done?

Note that a pull request does not have to be "perfect" or "ready for merge" when you first open it. We just want it to be ready for a first review.

Cheers!

Carsonbot

@pluk77 pluk77 marked this pull request as ready for review September 27, 2021 13:03
@carsonbot carsonbot added this to the 5.3 milestone Sep 27, 2021
@nicolas-grekas
Copy link
Member

nicolas-grekas commented Sep 27, 2021

I don't think this is the correct patch. What might happen is that the proxy responds with a 407 and then it abruptly closes the connection. The 407 is recorded by the client, but the unexpectedly closed connection takes over and generates this TransportException.

In order to ignore the RECV error, we need to check other states of the client when it happens.
I would be interested in knowing what curl_getinfo() returns and what the $waitFor variable contains when this happens. Can you please share that?

@pluk77
Copy link
Contributor Author

pluk77 commented Sep 28, 2021

Thank you @Nicals for the speedy feedback.

The values requested were dumped just before the updated line.

Use case: Use authenticated proxy with incorrect or blank username and password:

Response headers from proxy:

"response_headers" => ["HTTP/1.1 407 Proxy Authentication Required",
"Server: squid",
"Mime-Version: 1.0",
"Date: Tue, 28 Sep 2021 05:37:28 GMT",
"Content-Type: text/html;charset=utf-8",
"Content-Length: 3670",
"X-Squid-Error: ERR_CACHE_ACCESS_DENIED 0",
"Vary: Accept-Language",
"Content-Language: en",
"Proxy-Authenticate: Negotiate",
"Proxy-Authenticate: Basic realm="CAPETOWN"","X-Cache: MISS from proxyserver.com","X-Cache-Lookup: NONE from proxyserver.com:8080"]

Requested trace data:

$waitFor = "H0"

curl_getinfo() = array:37 [
  "url" => "https://laburl"
  "content_type" => null
  "http_code" => 0
  "header_size" => 442
  "request_size" => 129
  "filetime" => -1
  "ssl_verify_result" => 0
  "redirect_count" => 0
  "total_time" => 0.177375
  "namelookup_time" => 2.8E-5
  "connect_time" => 0.019476
  "pretransfer_time" => 0.0
  "size_upload" => 0.0
  "size_download" => 0.0
  "speed_download" => 0.0
  "speed_upload" => 0.0
  "download_content_length" => -1.0
  "upload_content_length" => -1.0
  "starttransfer_time" => 0.0
  "redirect_time" => 0.0
  "redirect_url" => ""
  "primary_ip" => "xx.xx.xx.xx"
  "certinfo" => []
  "primary_port" => 8080
  "local_ip" => "xx.xx.xx.xx"
  "local_port" => 57445
  "http_version" => 0
  "protocol" => 2
  "ssl_verifyresult" => 0
  "scheme" => "HTTPS"
  "appconnect_time_us" => 0
  "connect_time_us" => 19476
  "namelookup_time_us" => 28
  "pretransfer_time_us" => 0
  "redirect_time_us" => 0
  "starttransfer_time_us" => 0
  "total_time_us" => 177375
]

Use case: Use authenticated proxy with correct username and password:

$waitFor = "C0"

curl_getinfo() = array:37 [
  "url" => "https://laburl"
  "content_type" => "application/text"
  "http_code" => 200
  "header_size" => 258
  "request_size" => 756
  "filetime" => -1
  "ssl_verify_result" => 0
  "redirect_count" => 0
  "total_time" => 0.790059
  "namelookup_time" => 0.008492
  "connect_time" => 0.017672
  "pretransfer_time" => 0.226968
  "size_upload" => 360.0
  "size_download" => 208.0
  "speed_download" => 263.0
  "speed_upload" => 455.0
  "download_content_length" => 208.0
  "upload_content_length" => 360.0
  "starttransfer_time" => 0.789863
  "redirect_time" => 0.0
  "redirect_url" => ""
  "primary_ip" => "xx.xx.xx.xx"
  "certinfo" => []
  "primary_port" => 8080
  "local_ip" => "xx.xx.xx.xx"
  "local_port" => 58088
  "http_version" => 2
  "protocol" => 2
  "ssl_verifyresult" => 0
  "scheme" => "HTTPS"
  "appconnect_time_us" => 226776
  "connect_time_us" => 17672
  "namelookup_time_us" => 8492
  "pretransfer_time_us" => 226968
  "redirect_time_us" => 0
  "starttransfer_time_us" => 789863
  "total_time_us" => 790059
]

@nicolas-grekas
Copy link
Member

Thanks. How did you get the first dump, the one that mentions response_headers?

@pluk77
Copy link
Contributor Author

pluk77 commented Sep 28, 2021

I kept my changes as per the PR and used this code to initiate the request:

$response = $this->httpClient->request('POST', $url, $requestOptions);
$responseCode = $response->getStatusCode();
if (200 !== $responseCode) {
    $this->logger->error('response code', [$responseCode]);
    $this->logger->error('response info', [$response->getInfo()]);
}

The PR results in the 407 resulting in a ClientException instead of a TransportException and therefore the response info in the response is populated with the actual headers from the proxy. Without the patch, there is no way to retrieve the actual headers returned by the proxy.

@nicolas-grekas
Copy link
Member

nicolas-grekas commented Sep 28, 2021

Can you please try the following patch and let me know how it works?

--- a/src/Symfony/Component/HttpClient/Response/CurlResponse.php
+++ b/src/Symfony/Component/HttpClient/Response/CurlResponse.php
@@ -313,6 +313,10 @@ final class CurlResponse implements ResponseInterface, StreamableInterface
                     }
                 }
 
+                if (\CURLE_RECV_ERROR === $result && 400 <= ($responses[(int) $ch]->info['http_code'] ?? 0)) {
+                    $multi->handlesActivity[$id][] = new FirstChunk();
+                }
+
                 $multi->handlesActivity[$id][] = null;

Also, can you give a try to how AmpHttpClient behaves in the same situation?

@pluk77
Copy link
Contributor Author

pluk77 commented Sep 28, 2021

Thank you for the quick response.

I removed my patch and applied yours and that worked. The response code back to my application is 407 and the proxy response headers are now populated in $response->getInfo()

It also handled a Gateway time-out (504 error) as well as a fully authenticated and working request.

From what I can tell, your patch is indeed fixing the problem I experienced.

As for AmpHttpClient, I will have to do some more testing and provide feedback on that at a later stage.

Anything you would like me to do with this PR? Should I update it to reflect your patch, or will you file a new one?

@nicolas-grekas
Copy link
Member

Please update this PR, that's fine. I'm just not sure if/how this can be tested.

@nicolas-grekas
Copy link
Member

Closing in favor of #43243
Thanks for the help

@pluk77 pluk77 deleted the patch-2 branch September 29, 2021 07:03
@pluk77
Copy link
Contributor Author

pluk77 commented Sep 29, 2021

Its a pleasure.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants