feat: better subprotocol support and tests

This commit is contained in:
Siddharth Gelera
2025-09-30 18:45:24 +05:30
parent c7758428e8
commit bad8be6857
4 changed files with 103 additions and 4 deletions

View File

@@ -88,11 +88,20 @@ class WsClient {
try {
// Create WebSocket connection
const wsConnection = new ws.WebSocket(parsedUrl.fullUrl, {
const protocols = [].concat([headers['Sec-WebSocket-Protocol'], headers['sec-websocket-protocol']]).filter(Boolean);
const protocolVersion = headers['Sec-WebSocket-Version'] || headers['sec-websocket-version'];
const wsOptions = {
headers,
handshakeTimeout: timeout,
followRedirects: true,
});
};
if (protocolVersion) {
wsOptions.protocolVersion = protocolVersion;
}
const wsConnection = new ws.WebSocket(parsedUrl.fullUrl, protocols, wsOptions);
// Set up event handlers
this.#setupWsEventHandlers(wsConnection, requestId, collectionUid, { keepAlive, keepAliveInterval });

View File

@@ -5,7 +5,16 @@ const onSocketError = (err) => {
};
const wss = new ws.Server({
noServer: true
noServer: true,
handleProtocols: (protocols, request) => {
if (request.url == '/ws/sub-proto') {
if (protocols.has("soap")) {
return 'soap'
}
return false
}
return false
}
});
wss.on('connection', function connection(ws, request) {
@@ -31,7 +40,7 @@ wss.on('connection', function connection(ws, request) {
const wsRouter = (request, socket, head) => {
socket.on('error', onSocketError);
if (request.url !== '/ws') {
if (!request.url.startsWith('/ws')) {
socket.write('HTTP/1.1 404 Not Found\r\n\r\n');
socket.destroy();
@@ -39,6 +48,24 @@ const wsRouter = (request, socket, head) => {
return;
}
if (request.url == '/ws/sub-proto') {
const subproto = request.headers["sec-websocket-protocol"] || request.headers["Sec-WebSocket-Protocol"]
if (subproto != "soap") {
const message = "Unsupported WebSocket subprotocol"
socket.write(
'HTTP/1.1 400 Bad Request\r\n' +
'Content-Type: text/plain\r\n' +
`Content-Length: ${Buffer.byteLength(message)}\r\n` +
'Connection: close\r\n' +
'\r\n' +
message
);
socket.destroy();
socket.removeListener('error', onSocketError);
return
}
}
wss.handleUpgrade(request, socket, head, function done(ws) {
wss.emit('connection', ws, request);
});

View File

@@ -0,0 +1,22 @@
meta {
name: ws-test-request-with-subproto
type: ws
seq: 3
}
ws {
url: ws://localhost:8081/ws/sub-proto
body: ws
auth: inherit
}
headers {
Sec-WebSocket-Protocol: soap
}
body:ws {
name: message 1
content: '''
{}
'''
}

View File

@@ -0,0 +1,41 @@
import { test, expect } from '../../playwright';
import { buildCommonLocators } from './lib/locators';
const BRU_FILE_NAME = /^ws-test-request-with-subproto$/;
test.describe.serial('headers', () => {
test('headers are returned if passed', async ({ pageWithUserData: page, restartApp }) => {
const locators = buildCommonLocators(page);
const clearText = async (text: string) => {
for (let i = text.length; i > 0; i--) {
await page.keyboard.press('Backspace');
}
};
const originalProtocol = 'soap';
const wrongProtocol = 'wap';
await page.locator('#sidebar-collection-name').click();
await page.getByTitle(BRU_FILE_NAME).click();
await page.getByRole('tab', { name: 'Headers1' }).click();
await expect(page.locator('pre').filter({ hasText: originalProtocol })).toBeAttached();
await locators.runner().click();
const messages = await locators.messages();
expect(await messages[0].locator('.text-ellipsis').innerText()).toMatch(/^(Connected to)/);
await locators.connectionControls.disconnect().click();
await page.locator('pre').filter({ hasText: originalProtocol }).click();
await clearText(originalProtocol);
await page.keyboard.insertText(wrongProtocol);
await locators.runner().click();
expect(await messages[0].locator('.text-ellipsis').innerText()).toMatch(/^(Unexpected server response: 400)/);
await page.locator('pre').filter({ hasText: wrongProtocol }).click();
await clearText(wrongProtocol);
await page.keyboard.insertText(originalProtocol);
await locators.saveButton().click();
});
});