Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit fd0f568

Browse filesBrowse files
fix(websocket): handle errors in handleUpgrade (#823)
* fix(websocket): handle errors in handleUpgrade * test(websocket): add test for error handling * fix(websocket): handle sockets in error-response-plugin --------- Co-authored-by: Steven Chim <655241+chimurai@users.noreply.github.com>
1 parent e94087e commit fd0f568
Copy full SHA for fd0f568

File tree

Expand file treeCollapse file tree

3 files changed

+62
-18
lines changed
Filter options
Expand file treeCollapse file tree

3 files changed

+62
-18
lines changed

‎src/http-proxy-middleware.ts

Copy file name to clipboardExpand all lines: src/http-proxy-middleware.ts
+10-4Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,10 +98,16 @@ export class HttpProxyMiddleware<TReq, TRes> {
9898
};
9999

100100
private handleUpgrade = async (req: http.IncomingMessage, socket: net.Socket, head: Buffer) => {
101-
if (this.shouldProxy(this.proxyOptions.pathFilter, req)) {
102-
const activeProxyOptions = await this.prepareProxyRequest(req);
103-
this.proxy.ws(req, socket, head, activeProxyOptions);
104-
debug('server upgrade event received. Proxying WebSocket');
101+
try {
102+
if (this.shouldProxy(this.proxyOptions.pathFilter, req)) {
103+
const activeProxyOptions = await this.prepareProxyRequest(req);
104+
this.proxy.ws(req, socket, head, activeProxyOptions);
105+
debug('server upgrade event received. Proxying WebSocket');
106+
}
107+
} catch (err) {
108+
// This error does not include the URL as the fourth argument as we won't
109+
// have the URL if `this.prepareProxyRequest` throws an error.
110+
this.proxy.emit('error', err, req, socket);
105111
}
106112
};
107113

+20-6Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,33 @@
1+
import type * as http from 'http';
2+
import type { Socket } from 'net';
13
import { getStatusCode } from '../../status-code';
24
import { Plugin } from '../../types';
35

6+
function isResponseLike(obj: any): obj is http.ServerResponse {
7+
return obj && typeof obj.writeHead === 'function';
8+
}
9+
10+
function isSocketLike(obj: any): obj is Socket {
11+
return obj && typeof obj.write === 'function' && !('writeHead' in obj);
12+
}
13+
414
export const errorResponsePlugin: Plugin = (proxyServer, options) => {
515
proxyServer.on('error', (err, req, res, target?) => {
616
// Re-throw error. Not recoverable since req & res are empty.
717
if (!req && !res) {
818
throw err; // "Error: Must provide a proper URL as target"
919
}
1020

11-
if ('writeHead' in res && !res.headersSent) {
12-
const statusCode = getStatusCode((err as unknown as any).code);
13-
res.writeHead(statusCode);
14-
}
21+
if (isResponseLike(res)) {
22+
if (!res.headersSent) {
23+
const statusCode = getStatusCode((err as unknown as any).code);
24+
res.writeHead(statusCode);
25+
}
1526

16-
const host = req.headers && req.headers.host;
17-
res.end(`Error occurred while trying to proxy: ${host}${req.url}`);
27+
const host = req.headers && req.headers.host;
28+
res.end(`Error occurred while trying to proxy: ${host}${req.url}`);
29+
} else if (isSocketLike(res)) {
30+
res.destroy();
31+
}
1832
});
1933
};

‎test/e2e/websocket.spec.ts

Copy file name to clipboardExpand all lines: test/e2e/websocket.spec.ts
+32-8Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -97,15 +97,14 @@ describe('E2E WebSocket proxy', () => {
9797

9898
describe('with router and pathRewrite', () => {
9999
beforeEach(() => {
100-
// override
101-
proxyServer = createApp(
100+
const proxyMiddleware = createProxyMiddleware({
102101
// cSpell:ignore notworkinghost
103-
createProxyMiddleware({
104-
target: 'ws://notworkinghost:6789',
105-
router: { '/socket': `ws://localhost:${WS_SERVER_PORT}` },
106-
pathRewrite: { '^/socket': '' },
107-
}),
108-
).listen(SERVER_PORT);
102+
target: 'ws://notworkinghost:6789',
103+
router: { '/socket': `ws://localhost:${WS_SERVER_PORT}` },
104+
pathRewrite: { '^/socket': '' },
105+
});
106+
107+
proxyServer = createApp(proxyMiddleware).listen(SERVER_PORT);
109108

110109
proxyServer.on('upgrade', proxyMiddleware.upgrade);
111110
});
@@ -124,4 +123,29 @@ describe('E2E WebSocket proxy', () => {
124123
ws.send('foobar');
125124
});
126125
});
126+
127+
describe('with error in router', () => {
128+
beforeEach(() => {
129+
const proxyMiddleware = createProxyMiddleware({
130+
// cSpell:ignore notworkinghost
131+
target: `http://notworkinghost:6789`,
132+
router: async () => {
133+
throw new Error('error');
134+
},
135+
});
136+
137+
proxyServer = createApp(proxyMiddleware).listen(SERVER_PORT);
138+
139+
proxyServer.on('upgrade', proxyMiddleware.upgrade);
140+
});
141+
142+
it('should handle error', (done) => {
143+
ws = new WebSocket(`ws://localhost:${SERVER_PORT}/socket`);
144+
145+
ws.on('error', (err) => {
146+
expect(err).toBeTruthy();
147+
done();
148+
});
149+
});
150+
});
127151
});

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.