Skip to content

node:http — 100-continue flow (Expect: 100-continue / checkContinue / writeContinue / 'continue' event) produces no output #5080

Description

@proggeramlug

Summary

The HTTP 100-continue handshake does not work end-to-end. A client sending Expect: 100-continue never receives the 'continue' event, and a server's 'checkContinue' listener / res.writeContinue() path never drives the exchange. The program produces no output where node completes the request.

Repro

import http from "node:http";

const server = http.createServer((req, res) => {
  let b = "";
  req.on("data", (c: any) => (b += c));
  req.on("end", () => res.end("got:" + b));
});
server.on("checkContinue", (req: any, res: any) => {
  res.writeContinue();
  let b = "";
  req.on("data", (c: any) => (b += c));
  req.on("end", () => res.end("continue:" + b));
});
server.listen(0, () => {
  const port = (server.address() as any).port;
  const req = http.request(
    { port, method: "POST", headers: { Expect: "100-continue" } },
    (res: any) => {
      let b = "";
      res.on("data", (c: any) => (b += c));
      res.on("end", () => {
        console.log("body", b);
        server.close(() => console.log("closed"));
      });
    },
  );
  req.on("continue", () => {
    console.log("continue event");
    req.end("payload");
  });
});
setTimeout(() => {}, 1500);
# node v26
continue event
body continue:payload
closed

# perry
(no output)

Expected surface (node parity)

  • Client: when Expect: 100-continue is set, the body is withheld until the server's interim 100 Continue arrives, at which point the request emits 'continue'. (req.on('continue', …) then sends the body.)
  • Server: an incoming Expect: 100-continue fires server.on('checkContinue', (req, res) => …) instead of the normal 'request' handler; res.writeContinue() writes the HTTP/1.1 100 Continue\r\n\r\n interim response, after which the body is read normally. If no 'checkContinue' listener is registered, node auto-sends 100 Continue and dispatches 'request'.

Notes

This needs both the client-side interim-response handling (reqwest/hyper may auto-consume 100 Continue, so the raw-socket trailer-aware path in plain_client.rs or a dedicated client path may be required to observe it) and the server-side checkContinue/writeContinue wiring (perry-ext-http-server). Relevant pieces: client PendingHttpEvent stream, ServerResponse.writeContinue, and the server request-dispatch branch on the Expect header.

Found while fixing client transport errors (#5078); out of scope for that PR.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions