Skip to content
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

Getting uncaughtException Error: write EPIPE when sending http requests over unix socket #55952

Open
oleg-slapdash opened this issue Nov 21, 2024 · 0 comments

Comments

@oleg-slapdash
Copy link

oleg-slapdash commented Nov 21, 2024

Version

v23.3.0

Platform

Darwin local 23.6.0 Darwin Kernel Version 23.6.0: Thu Sep 12 23:35:29 PDT 2024; root:xnu-10063.141.1.701.1~1/RELEASE_ARM64_T6000 arm64

Subsystem

No response

What steps will reproduce the bug?

Create server.js

/* eslint-disable no-console */
const { unlinkSync } = require("fs");
const { createServer } = require("net");

const SOCKET_PATH = "/tmp/epipe-server.sock";

setTimeout(() => {
  console.log("Server timeout reached, exiting");
  process.exit(0);
}, 10000);

let connectionCount = 0;
const server = createServer();
server.on("listening", () => console.log("listening", server.address()));
server.on("error", (err) => console.error("server error", err));
server.on("connection", (socket) => {
  socket.on("error", (err) => console.error("socket error", err));

  if (connectionCount++ === 0) {
    console.log("first connection, play nice!");
    socket.on("data", (chunk) => {
      console.log("first socket received chunk:", chunk.length);
      socket.write("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", () => {
        console.log("first socket response ok.");
        socket.end();
      });
    });
  }
});

try {
  unlinkSync(SOCKET_PATH);
} catch {}
server.listen(SOCKET_PATH);

process.on("uncaughtExceptionMonitor", (reason, origin) => {
  console.error("!!!! SERVER IS CRASHING !!!!", origin, reason);
  process.exit(1);
});

Createclient.js

/* eslint-disable no-console */
const http = require("http");

setTimeout(() => {
  console.log("Client timeout reached, exiting");
  process.exit(0);
}, 10000);

const SOCKET_PATH = "/tmp/epipe-server.sock";

function request(callback) {
  const req = http.request(
    {
      headers: {},
      url: "unix://" + SOCKET_PATH,
      socketPath: SOCKET_PATH,
    },
    (res) => {
      res.on("error", (err) => console.error("response error", err));
      res.on("data", () => {});
      res.on("end", () => {
        console.log("end, statusCode =", res.statusCode);
        callback();
      });
    },
  );
  req.on("error", (err) => {
    console.log("handled request error", err);
    process.exit(0);
  });
  req.write("dummy payload".repeat(1000));
  req.write("dummy payload".repeat(1000));
  req.end();
}
process.on("uncaughtExceptionMonitor", (reason, origin) => {
  console.error("!!!! CLIENT IS CRASHING !!!!", origin, reason);
  process.exit(0);
});

async function main() {
  console.log("request 1");
  await new Promise((resolve) => request(resolve));

  console.log("request 2");
  await new Promise((resolve) => request(resolve));
}

main().then(console.log).catch(console.error);

Run

nvm install 23
nvm use 23
node -v
node server.js &; node client.js

Observe:

!!!! CLIENT IS CRASHING !!!! uncaughtException Error: write EPIPE
    at WriteWrap.onWriteComplete [as oncomplete] (node:internal/stream_base_commons:87:19) {
  errno: -32,
  code: 'EPIPE',
  syscall: 'write'
}

How often does it reproduce? Is there a required condition?

This is a synthetic test case that is reproducible consistently.

On the production system which experienced this error - the issue did not reproduce consistently.

Please note, that the server.js implementation is intentionally broken to simulate bad http server. However, even in this case, the client.js should not crash.

A process with NODE_DEBUG=net,stream,http produces the following output before a crash:

 11-20 21:49:34 STREAM 46: emitReadable_
 11-20 21:49:34 STREAM 46: flow
 11-20 21:49:34 STREAM 46: read undefined
 11-20 21:49:34 STREAM 46: endReadable
 11-20 21:49:34 STREAM 46: read 0
 11-20 21:49:34 STREAM 46: endReadable
 11-20 21:49:34 STREAM 46: endReadableNT
 11-20 21:49:34 STREAM 46: endReadableNTc
 11-20 21:49:34 STREAM 46: onWriteComplete -32 undefined
 11-20 21:49:34 HTTP 46: AGENT socket keep-alive
 11-20 21:49:34 NET 46: destroy
 11-20 21:49:34 NET 46: close
 11-20 21:49:34 NET 46: close handle
 11-20 21:49:34 HTTP 46: CLIENT socket onFree
 11-20 21:49:34 HTTP 46: agent.on(free) localhost:80::/var/run/datadog/apm.socket
 11-20 21:49:34 [pid=46] Node is now CRASHING with uncaughtException. See the error around. -- This is custom error handler, not `NODE_DEBUG` output.
 11-20 21:49:34 Error: write EPIPE
 11-20 21:49:34     at WriteWrap.onWriteComplete [as oncomplete] (node:internal/stream_base_commons:95:16)
 11-20 21:49:34     at WriteWrap.callbackTrampoline (node:internal/async_hooks:130:17)

What is the expected behavior? Why is that the expected behavior?

The node process should not crash with unhandled exception and instead should use error handler

What do you see instead?

An unhandled exception crash.

Additional information

I think it's related to the following comments:
https://github.com/nodejs/node/blob/v20.x/lib/_http_agent.js#L118-L128
https://github.com/nodejs/node/blob/v20.x/lib/_http_client.js#L730-L731

@oleg-slapdash oleg-slapdash changed the title Getting uncaughtException Error: write when sending http requests over unix socket Getting uncaughtException Error: write EPIPE when sending http requests over unix socket Nov 21, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant