Compare commits

...

1 Commits

Author SHA1 Message Date
Claude Bot
a5a2571a81 fix(node:http): socket.write not sending data in upgrade event handler
Enable streaming on the socket before emitting the "upgrade" event, matching
the existing behavior for CONNECT requests. Without this, handle.ondrain was
undefined so _write() silently skipped calling handle.write().

Also skip socket.cork() for upgrade connections since the socket is handed
off to user code and data should flow immediately.

Closes #9882

Co-Authored-By: Claude <noreply@anthropic.com>
2026-02-20 04:41:53 +00:00
2 changed files with 166 additions and 1 deletions

View File

@@ -602,6 +602,7 @@ Server.prototype[kRealListen] = function (tls, port, host, socketPath, reusePort
http_res.end();
socket.destroy();
} else if (is_upgrade) {
socket[kEnableStreaming](true);
server.emit("upgrade", http_req, socket, kEmptyBuffer);
if (!socket._httpMessage) {
if (canUseInternalAssignSocket) {
@@ -629,7 +630,9 @@ Server.prototype[kRealListen] = function (tls, port, host, socketPath, reusePort
server.emit("request", http_req, http_res);
}
socket.cork();
if (!is_upgrade) {
socket.cork();
}
if (handle.finished || didFinish) {
handle = undefined;

View File

@@ -0,0 +1,162 @@
import { expect, test } from "bun:test";
import http from "node:http";
import net from "node:net";
test("socket.write sends data in http upgrade event handler", async () => {
const { promise, resolve, reject } = Promise.withResolvers<void>();
const server = http.createServer();
server.on("upgrade", (_req, socket) => {
socket.write("x", () => {
// After the write completes, close the socket
socket.end();
});
});
server.listen(0, "127.0.0.1", () => {
const addr = server.address() as net.AddressInfo;
const client = net.createConnection(addr.port, "127.0.0.1", () => {
client.write(
"GET / HTTP/1.1\r\n" +
"Host: 127.0.0.1\r\n" +
"Upgrade: websocket\r\n" +
"Connection: Upgrade\r\n" +
"Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n" +
"Sec-WebSocket-Version: 13\r\n" +
"\r\n",
);
});
let received = "";
client.on("data", (data: Buffer) => {
received += data.toString();
});
client.on("end", () => {
try {
expect(received).toBe("x");
resolve();
} catch (e) {
reject(e);
} finally {
server.close();
}
});
client.on("error", (err: Error) => {
server.close();
reject(err);
});
});
await promise;
});
test("socket.write with 101 handshake in http upgrade event handler", async () => {
const { promise, resolve, reject } = Promise.withResolvers<void>();
const server = http.createServer();
server.on("upgrade", (_req, socket) => {
socket.write(
"HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\n\r\nhello from upgrade",
() => {
socket.end();
},
);
});
server.listen(0, "127.0.0.1", () => {
const addr = server.address() as net.AddressInfo;
const client = net.createConnection(addr.port, "127.0.0.1", () => {
client.write(
"GET / HTTP/1.1\r\n" +
"Host: 127.0.0.1\r\n" +
"Upgrade: websocket\r\n" +
"Connection: Upgrade\r\n" +
"Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n" +
"Sec-WebSocket-Version: 13\r\n" +
"\r\n",
);
});
let received = "";
client.on("data", (data: Buffer) => {
received += data.toString();
});
client.on("end", () => {
try {
expect(received).toContain("HTTP/1.1 101 Switching Protocols");
expect(received).toContain("hello from upgrade");
resolve();
} catch (e) {
reject(e);
} finally {
server.close();
}
});
client.on("error", (err: Error) => {
server.close();
reject(err);
});
});
await promise;
});
test("multiple socket.write calls in http upgrade event handler", async () => {
const { promise, resolve, reject } = Promise.withResolvers<void>();
const server = http.createServer();
server.on("upgrade", (_req, socket) => {
socket.write("first");
socket.write("second");
socket.write("third", () => {
socket.end();
});
});
server.listen(0, "127.0.0.1", () => {
const addr = server.address() as net.AddressInfo;
const client = net.createConnection(addr.port, "127.0.0.1", () => {
client.write(
"GET / HTTP/1.1\r\n" +
"Host: 127.0.0.1\r\n" +
"Upgrade: websocket\r\n" +
"Connection: Upgrade\r\n" +
"Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n" +
"Sec-WebSocket-Version: 13\r\n" +
"\r\n",
);
});
let received = "";
client.on("data", (data: Buffer) => {
received += data.toString();
});
client.on("end", () => {
try {
expect(received).toContain("first");
expect(received).toContain("second");
expect(received).toContain("third");
resolve();
} catch (e) {
reject(e);
} finally {
server.close();
}
});
client.on("error", (err: Error) => {
server.close();
reject(err);
});
});
await promise;
});