nodejs / node

Node.js JavaScript runtime ✨🐢🚀✨
https://nodejs.org
Other
107.08k stars 29.32k forks source link

connection reset when using https server/client combi without keepalive #23128

Open SanderDeWaal1992 opened 6 years ago

SanderDeWaal1992 commented 6 years ago

We noticed that there are unexpected RST packages when inspecting the connection with Wireshark. This are our findings for now:

Node does not trigger any error event. Data transfer goes well, but the connection is not closed correctly. The connection ends with: client -> server: ACK server -> client: FIN, ACK client -> server: ACK TLSv1.2: Encrypted Alert server -> Client: RST, ACK

Test code: client:

const https = require("https");
const options = {
    hostname: "localhost",
    port: 8000,
    rejectUnauthorized: false,
    agent: new https.Agent({ 
        keepAlive: false // bug will not happen if set on 'true' 
    })
};
const req = https.request(options, (res) => {
    res.on("data", (data) => {
        //
    });
    res.on("close", () => {
        console.log("response closed");
    });
});
req.end()

Server:

const https = require("https");
https.createServer({
    // add cert and key
}, (req, res) => {
    res.statusCode = 200;
    res.end("hello world\n");
}).listen(8000);
bzoz commented 6 years ago

Are you running Node inside a VM? What is the exact Windows version (build number)?

SanderDeWaal1992 commented 6 years ago

@bzoz No, I did not run it in a VM. Windows 10. Version 1803, build 17134.286.

Trott commented 5 years ago

@nodejs/http @nodejs/platform-windows

ottob commented 2 years ago

Possibly related. After we upgraded our servers from node 14 to node 16 we started gettings errors when connecting to an external API using mutual TLS that I suspect is running Windows:

 FetchError: request to https://ext-ws.statenspersonadressregister.se/spar-webservice/SPARPersonsokningService/20160213 failed, reason: socket hang up
      at ClientRequest.<anonymous> (/home/ottob/dev/myproject/node_modules/node-fetch/dist/index.cjs:1277:11)
      at ClientRequest.emit (node:events:390:28)
      at TLSSocket.socketOnEnd (node:_http_client:471:9)
      at TLSSocket.emit (node:events:402:35)
      at endReadableNT (node:internal/streams/readable:1343:12)
      at processTicksAndRejections (node:internal/process/task_queues:83:21) {
    type: 'system',
    errno: 'ECONNRESET',
    code: 'ECONNRESET',
    erroredSysCall: undefined
  }

We solved this and got the requests working again by enabling keepAlive:

const agent = new https.Agent({
    cert,
    key,
    keepAlive: true,
  })
const response = await fetch(url, {
    agent,
    method: 'post',

Hopefully this can help others having the same issue. I can probably put together a small test case if somebody is interested.

jsimonek commented 11 months ago

I think I'm seeing resets with same or similar root cause on Ubuntu and OSX on several node version ranging from Node 14 to Node 20. Here is an example that reproduces it node-tcp-resets.zip. I have also attached captured dumps from tcpdump with the resets.

Resets seems to always happen after the http communication is finished, so it doesn't bubble up to node as an error. Both server and client sends resets with varying rates across versions. Sometimes there are even TCP retransmissions going on. Node 20 seems to behave slightly differently -- resets are sent almost exclusively from the client to the server and also if the server doesn't read the incoming data, connection reset from the client is received in the event handler.

Another way how to prevent it seems to be https.Agent({ ..., maxSockets: 2 }) and not passing header 'Connection': 'close' from client to server. This seems to prevent resets for all Node versions that I tried except for Node 20.

We suspect that something has changed between Node 18.12.1 and 18.13.0 because we see a lot more connection resets in communication between two services in production. When the https client is running Node 18.12.1 we see low number of resets, when it's running 18.13.0 we see about 20x more resets. Everything, except the version of node on https client is equal. We are not certain at this point if there's the same cause for resets on prod and the example that I've attached or not.

Here is an output from the attached example:

Now using node v14.21.3 (npm v6.14.18)
  Finished 50 requests
  Resets client > server: 11
  Resets server > client: 49

Now using node v16.20.0 (npm v8.19.4)
  Finished 50 requests
  Resets client > server: 8
  Resets server > client: 32

Now using node v18.12.1 (npm v8.19.2)
  Finished 50 requests
  Resets client > server: 15
  Resets server > client: 20

Now using node v18.13.0 (npm v8.19.3)
  Finished 50 requests
  Resets client > server: 19
  Resets server > client: 49

Now using node v20.9.0 (npm v10.1.0)
  Finished 50 requests
  Resets client > server: 66
  Resets server > client: 0