Check the status code from the CONNECT response when the proxy is HTTP and the target is HTTPS. #61
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Issue
When the target is an HTTPS endpoint and the proxy server uses HTTP, global-agent does not parse the response coming from the proxy server on the CONNECT method.
As a result, it causes a confusing low-level exception (write EPROTO 281473125421184:error:1408F10B:SSL routines:ssl3_get_record:wrong version number) whenever the proxy server responds with an error status code (for example 403 if proxy has access control and denies the request, or 407 when the credentials are wrong).
It is easy to replicate this issue with a proxy server that has authentication or access filters. Unfortunately, anyproxy, the module used in the automated tests doesn't provide any of these out of the box, letting the users implement them via 'Rule module interface'.
Therefore, to replicate the issue locally, I used a simple squid docker container and a script based on global-agent:
Replicating the issue
I. Set up a proxy server with squid:
For example, I used:
mkdir -p ~/dev/global-proxy-issue-test && cd ~/dev/global-proxy-issue-test
Prepare a squid config that enables basic authentication with username
user
and passwordpassword
:2.1. Create the hashed basic authentication file:
echo "user:\$apr1\$rCCYyoxE\$aZmgzc9iF/uQgPl9Lx1291" >> squid_password
2.2. Create the squid.conf configuration file:
Run the squid docker container:
docker run -d -p 9998:9998 -v $(pwd):/etc/squid ubuntu/squid
The proxy server is now running at
http://user:[email protected]:9998/
II. Set up a simple test file with global-agent
npm i global-agent
touch script.js
Paste the following script into
script.js
(global-agent
is the only external dependency, but testing withgot
orrequest-promise
will have the same result, regardless of Node.js version):(notice that the correct proxy credentials are user:password, but first we will try with wrong credentials in order to receive 407 from proxy server)
III. Run the script and observe initial output:
The error is caused by the fact that the socket used for the CONNECT method is upgraded to TLS even if the response from the proxy server is not 200. In this case, the squid proxy server responds with
HTTP/1.1 407 Proxy Authentication Required
to the CONNECT request.This can be inspected with:
curl -v -x "http://WRONGUSER:[email protected]:9998" http://httpbin.org/headers
IV. Modify the script with the correct credentials and the request will now work:
Proposed change
My proposed change is to check the status code from the CONNECT response when the proxy is HTTP and the target is HTTPS. If the status code is >= 400, emit an error with the status line and don't go any further.
Since proxies with HTTPS as protocal are not allowed by
parseProxyUrl()
,it should be safe to assume that the response is plain text and parse it.