Skip to content

Commit

Permalink
Fix a bug where websocket subprotocols were not forwarded
Browse files Browse the repository at this point in the history
  • Loading branch information
pimterry committed Aug 28, 2023
1 parent 2f41556 commit 9a74971
Show file tree
Hide file tree
Showing 3 changed files with 20 additions and 9 deletions.
8 changes: 7 additions & 1 deletion src/rules/websockets/websocket-handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
} from '../../util/request-utils';
import {
findRawHeader,
findRawHeaders,
objectHeadersToRaw,
pairFlatRawHeaders,
rawHeadersToObjectPreservingCase
Expand Down Expand Up @@ -329,7 +330,12 @@ export class PassThroughWebSocketHandler extends PassThroughWebSocketHandlerDefi
// header object internally.
const headers = rawHeadersToObjectPreservingCase(rawHeaders);

const upstreamWebSocket = new WebSocket(wsUrl, {
// Subprotocols have to be handled explicitly. WS takes control of the headers itself,
// and checks the response, so we need to parse the client headers and use them manually:
const subprotocols = findRawHeaders(rawHeaders, 'sec-websocket-protocol')
.flatMap(([_k, value]) => value.split(',').map(p => p.trim()));

const upstreamWebSocket = new WebSocket(wsUrl, subprotocols, {
maxPayload: 0,
agent,
lookup: getDnsLookupFunction(this.lookupOptions),
Expand Down
2 changes: 1 addition & 1 deletion src/util/header-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export const findRawHeader = (rawHeaders: RawHeaders, targetKey: string) =>
export const findRawHeaderIndex = (rawHeaders: RawHeaders, targetKey: string) =>
rawHeaders.findIndex(([key]) => key.toLowerCase() === targetKey);

const findRawHeaders = (rawHeaders: RawHeaders, targetKey: string) =>
export const findRawHeaders = (rawHeaders: RawHeaders, targetKey: string) =>
rawHeaders.filter(([key]) => key.toLowerCase() === targetKey);

/**
Expand Down
19 changes: 12 additions & 7 deletions test/integration/websockets.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,13 +146,17 @@ nodeOnly(() => {
it("forwards the incoming requests's headers", async () => {
mockServer.forAnyWebSocket().thenPassThrough();

const ws = new WebSocket(`ws://localhost:${wsPort}`, {
agent: new HttpProxyAgent(`http://localhost:${mockServer.port}`),
headers: {
'echo-headers': 'true',
'Funky-HEADER-casing': 'Header-Value'
const ws = new WebSocket(
`ws://localhost:${wsPort}`,
['subprotocol-a', 'subprotocol-b'],
{
agent: new HttpProxyAgent(`http://localhost:${mockServer.port}`),
headers: {
'echo-headers': 'true',
'Funky-HEADER-casing': 'Header-Value'
}
}
});
);

const response = await new Promise<Buffer>((resolve, reject) => {
ws.on('message', resolve);
Expand All @@ -172,7 +176,8 @@ nodeOnly(() => {
[ 'Sec-WebSocket-Version', '13' ],
[ 'Connection', 'Upgrade' ],
[ 'Upgrade', 'websocket' ],
[ 'Sec-WebSocket-Extensions', 'permessage-deflate; client_max_window_bits' ]
[ 'Sec-WebSocket-Extensions', 'permessage-deflate; client_max_window_bits' ],
[ 'Sec-WebSocket-Protocol', 'subprotocol-a,subprotocol-b' ]
]);
});

Expand Down

0 comments on commit 9a74971

Please sign in to comment.