node/test/parallel/test-http-upgrade-server-callback.js
Tim Perry 65bee02cca
http: add shouldUpgradeCallback to let servers control HTTP upgrades
Previously, you could either add no 'upgrade' event handler, in which
case all upgrades were ignored, or add an 'upgrade' handler and all
upgrade attempts would effectively succeed and skip normal request
handling. This change adds a new shouldUpgradeCallback option to HTTP
servers, which receives the request details and returns a boolean that
controls whether the request should be upgraded.

PR-URL: https://github.com/nodejs/node/pull/59824
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
2025-09-17 15:14:01 +00:00

168 lines
4.0 KiB
JavaScript

'use strict';
const common = require('../common');
const assert = require('assert');
const net = require('net');
const http = require('http');
function testUpgradeCallbackTrue() {
const server = http.createServer({
shouldUpgradeCallback: common.mustCall((req) => {
assert.strictEqual(req.url, '/websocket');
assert.strictEqual(req.headers.upgrade, 'websocket');
return true;
})
});
server.on('upgrade', function(req, socket, upgradeHead) {
assert.strictEqual(req.url, '/websocket');
assert.strictEqual(req.headers.upgrade, 'websocket');
assert.ok(socket instanceof net.Socket);
assert.ok(upgradeHead instanceof Buffer);
socket.write('HTTP/1.1 101 Web Socket Protocol Handshake\r\n' +
'Upgrade: WebSocket\r\n' +
'Connection: Upgrade\r\n' +
'\r\n\r\n');
});
server.on('request', common.mustNotCall());
server.listen(0, common.mustCall(() => {
const req = http.request({
port: server.address().port,
path: '/websocket',
headers: {
'Upgrade': 'websocket',
'Connection': 'Upgrade'
}
});
req.on('upgrade', common.mustCall((res, socket, upgradeHead) => {
assert.strictEqual(res.statusCode, 101);
assert.ok(socket instanceof net.Socket);
assert.ok(upgradeHead instanceof Buffer);
socket.end();
server.close();
testUpgradeCallbackFalse();
}));
req.on('response', common.mustNotCall());
req.end();
}));
}
function testUpgradeCallbackFalse() {
const server = http.createServer({
shouldUpgradeCallback: common.mustCall(() => {
return false;
})
});
server.on('upgrade', common.mustNotCall());
server.on('request', common.mustCall((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.write('received but not upgraded');
res.end();
}));
server.listen(0, common.mustCall(() => {
const req = http.request({
port: server.address().port,
path: '/websocket',
headers: {
'Upgrade': 'websocket',
'Connection': 'Upgrade'
}
});
req.on('upgrade', common.mustNotCall());
req.on('response', common.mustCall((res) => {
assert.strictEqual(res.statusCode, 200);
let data = '';
res.on('data', (chunk) => { data += chunk; });
res.on('end', common.mustCall(() => {
assert.strictEqual(data, 'received but not upgraded');
server.close();
testUpgradeCallbackTrueWithoutHandler();
}));
}));
req.end();
}));
}
function testUpgradeCallbackTrueWithoutHandler() {
const server = http.createServer({
shouldUpgradeCallback: common.mustCall(() => {
return true;
})
});
// N.b: no 'upgrade' handler
server.on('request', common.mustNotCall());
server.listen(0, common.mustCall(() => {
const req = http.request({
port: server.address().port,
path: '/websocket',
headers: {
'Upgrade': 'websocket',
'Connection': 'Upgrade'
}
});
req.on('upgrade', common.mustNotCall());
req.on('response', common.mustNotCall());
req.on('error', common.mustCall((e) => {
assert.strictEqual(e.code, 'ECONNRESET');
server.close();
testUpgradeCallbackError();
}));
req.end();
}));
}
function testUpgradeCallbackError() {
const server = http.createServer({
shouldUpgradeCallback: common.mustCall(() => {
throw new Error('should upgrade callback failed');
})
});
server.on('upgrade', common.mustNotCall());
server.on('request', common.mustNotCall());
server.listen(0, common.mustCall(() => {
const req = http.request({
port: server.address().port,
path: '/websocket',
headers: {
'Upgrade': 'websocket',
'Connection': 'Upgrade'
}
});
req.on('upgrade', common.mustNotCall());
req.on('response', common.mustNotCall());
process.on('uncaughtException', common.mustCall(() => {
process.exit(0);
}));
req.end();
}));
}
testUpgradeCallbackTrue();