Description
Overview
proxy-chain
is observed to block indefinitely when certain conditions are met:
- It works in the forward mode (plain HTTP).
- There is
content-disposition
header that contains certain non-ASCII content. - There is
content-length
header.
The root cause of issue is believed to originate from Node.js. Before an upstream fix is made (which might take time) proxy-chain
here could apply some mitigation.
Upstream Issue: ServerResponse.writeHead
may throw an error
There are already several upstream issue reports, one of which is nodejs/node#50213
Mostly relevant is this section of code https://github.com/nodejs/node/blob/v20.16.0/lib/_http_outgoing.js#L554-L567 where the improper type change later involves implicit type conversion and unexpected charset decoding at https://github.com/nodejs/node/blob/v20.16.0/lib/_http_common.js#L216-L225
A minimal example for the decoding error: /[^\t\x20-\x7e\x80-\xff]/v.test(Buffer.from('à', 'latin1'))
This then result in an invalid character error to be thrown (code) during ServerResponse.writeHead
.
Impact to proxy-chain
As of the current revision, said issue would cause an error thrown from this call:
Lines 97 to 101 in a1ac3f2
It is then silently ignore here therefore the observed blocking:
Lines 110 to 113 in a1ac3f2
In earlier versions of proxy-chain
(that is used when I noticed this issue), said error is not caught thus exposed. This leads to the error as reported here: #20
Sample Steps to Reproduce the Issue
Target web server:
$ socat -v -v TCP-LISTEN:8080,fork,reuseaddr,crlf SYSTEM:'echo HTTP/1.1 200 OK; echo "content-length: 6"; echo -e "content-disposition: \\\\xe0"; echo; echo -n "foobar"'
proxy-chain
instance (just the minimal example from README.md
):
const ProxyChain = require('proxy-chain');
const server = new ProxyChain.Server({ port: 8000 });
server.listen(() => {
console.log(`Proxy server is listening on port ${8000}`);
});
Sending the request:
$ curl -v -x localhost:8000 'http://localhost:8080'
Expected result: The request shall finish. (see also: curl -v 'http://localhost:8080'
)
Observed result: The request blocks indefinitely.
(checked with Node.js v20.16.0 which is the active LTS)
Proposed Actions
I'm afraid that it's unlikely that the upstream will make a fix soon, considering how long their tickets have been around (e.g. nodejs/node#50213).
There are a few proposals on what could be done here:
- To manipulate the
content-disposition
header ahead, so that the call ofServerResponse.writeHead
no longer fails. - Or, alter the response handler upon forwarding, so that the upstream error no longer blocks the request. Perhaps letting it fail is (slightly?) better than blocking.
- Also, maybe expose the error message to users so that they may have an idea on what's going on.