Skip to content

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

@proggeramlug

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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions