Skip to content

Commit c70200e

Browse files
authored
fix(ws): fix concurrent ws requests (#344)
1 parent 021b03f commit c70200e

File tree

4 files changed

+74
-64
lines changed

4 files changed

+74
-64
lines changed

dist/http-proxy-middleware.js

+10-13
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ const Router = require("./router");
1919
class HttpProxyMiddleware {
2020
constructor(context, opts) {
2121
this.logger = logger_1.getInstance();
22-
this.wsInitialized = false;
22+
this.wsInternalSubscribed = false;
2323
// https://github.com/Microsoft/TypeScript/wiki/'this'-in-TypeScript#red-flags-for-this
2424
this.middleware = (req, res, next) => __awaiter(this, void 0, void 0, function* () {
2525
if (this.shouldProxy(this.config.context, req)) {
@@ -35,16 +35,12 @@ class HttpProxyMiddleware {
3535
}
3636
});
3737
this.catchUpgradeRequest = server => {
38-
// subscribe once; don't subscribe on every request...
39-
// https://github.com/chimurai/http-proxy-middleware/issues/113
40-
if (!this.wsInitialized) {
41-
server.on('upgrade', this.wsUpgradeDebounced);
42-
this.wsInitialized = true;
43-
}
38+
server.on('upgrade', this.handleUpgrade);
39+
// prevent duplicate upgrade handling;
40+
// in case external upgrade is also configured
41+
this.wsInternalSubscribed = true;
4442
};
4543
this.handleUpgrade = (req, socket, head) => {
46-
// set to initialized when used externally
47-
this.wsInitialized = true;
4844
if (this.shouldProxy(this.config.context, req)) {
4945
const activeProxyOptions = this.prepareProxyRequest(req);
5046
this.proxy.ws(req, socket, head, activeProxyOptions);
@@ -120,8 +116,6 @@ class HttpProxyMiddleware {
120116
const errReference = 'https://nodejs.org/api/errors.html#errors_common_system_errors'; // link to Node Common Systems Errors page
121117
this.logger.error(errorMessage, req.url, hostname, target, err.code || err, errReference);
122118
};
123-
// https://github.com/chimurai/http-proxy-middleware/issues/57
124-
this.wsUpgradeDebounced = _.debounce(this.handleUpgrade);
125119
this.config = config_factory_1.createConfig(context, opts);
126120
this.proxyOptions = this.config.options;
127121
// create proxy
@@ -134,8 +128,11 @@ class HttpProxyMiddleware {
134128
this.proxy.on('error', this.logError);
135129
// https://github.com/chimurai/http-proxy-middleware/issues/19
136130
// expose function to upgrade externally
137-
// middleware.upgrade = wsUpgradeDebounced
138-
this.middleware.upgrade = this.wsUpgradeDebounced;
131+
this.middleware.upgrade = (req, socket, head) => {
132+
if (!this.wsInternalSubscribed) {
133+
this.handleUpgrade(req, socket, head);
134+
}
135+
};
139136
}
140137
}
141138
exports.HttpProxyMiddleware = HttpProxyMiddleware;

examples/websocket/index.html

+49-32
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,15 @@ <h2>WebSocket demo</h2>
3030
<p>
3131
<label for="location">location:</label>
3232
<input id="location" type="text" value="ws://localhost:3000">
33-
<button id="connect">connect</button>
34-
<button id="disconnect" disabled="disabled">disconnect</button>
33+
<button id="connectBtn">connect</button>
34+
<button id="disconnectBtn" disabled="disabled">disconnect</button>
3535
</p>
3636
</fieldset>
3737
<fieldset id="messaging" disabled="disabled">
3838
<p>
3939
<label for="message">message:</label>
4040
<input type="text" id="message" value="Hello WebSocket">
41-
<button id="send">send</button>
41+
<button id="sendBtn">send</button>
4242
</p>
4343
<p>
4444
<label for="logger">log:</label>
@@ -49,44 +49,61 @@ <h2>WebSocket demo</h2>
4949
<script>
5050
window.onload = function () {
5151
// elements
52-
var configuration = document.getElementById('configuration');
53-
var location = document.getElementById('location');
54-
var connect = document.getElementById('connect');
55-
var disconnect = document.getElementById('disconnect');
56-
var messaging = document.getElementById('messaging');
57-
var message = document.getElementById('message');
58-
var send = document.getElementById('send');
59-
var logger = document.getElementById('logger');
52+
const configuration = document.getElementById('configuration');
53+
const location = document.getElementById('location');
54+
const connectBtn = document.getElementById('connectBtn');
55+
const disconnectBtn = document.getElementById('disconnectBtn');
56+
const messaging = document.getElementById('messaging');
57+
const message = document.getElementById('message');
58+
const sendBtn = document.getElementById('sendBtn');
59+
const logger = document.getElementById('logger');
6060

6161
// ws
62-
var socket;
63-
64-
connect.onclick = function () {
65-
connect.disabled = true;
66-
disconnect.disabled = false;
67-
messaging.disabled = false;
68-
69-
socket = new WebSocket(location.value);
70-
socket.onopen = function () { log('CONNECTED'); };
71-
socket.onclose = function () { log('DISCONNECTED'); };
72-
socket.onerror = function () { log('SOCKET ERROR OCCURED'); };
73-
socket.onmessage = function (msg) { log('RECEIVED:' + msg.data); };
62+
let socket;
63+
64+
connectBtn.onclick = () => { connect(); }
65+
disconnectBtn.onclick = () => { disconnect(); }
66+
sendBtn.onclick = () => { sendMessage(message.value); }
67+
68+
function connect() {
69+
setupSocket(location.value);
7470
}
7571

76-
disconnect.onclick = function () {
77-
connect.disabled = false;
78-
disconnect.disabled = true;
79-
messaging.disabled = true;
80-
socket.close();
72+
function disconnect() {
73+
socket.close();
74+
socket = undefined;
8175
}
8276

83-
send.onclick = function () {
84-
log('SEND: ' + message.value);
85-
socket.send(message.value);
77+
function sendMessage(val) {
78+
log('SEND: ' + val);
79+
socket.send(val);
8680
};
8781

82+
function setupSocket(url) {
83+
socket = new WebSocket(url);
84+
socket.addEventListener('open', () => {
85+
log('CONNECTED');
86+
toggleControls();
87+
})
88+
socket.addEventListener('close', () => {
89+
log('DISCONNECTED');
90+
toggleControls()
91+
})
92+
socket.addEventListener('error', () => { log('SOCKET ERROR OCCURED'); })
93+
socket.addEventListener('message', (msg) => { log('RECEIVED:' + msg.data); })
94+
}
95+
8896
function log (message) {
89-
logger.value = logger.value + message + '\n'
97+
logger.value = logger.value + message + '\n';
98+
logger.scrollTop = logger.scrollHeight; // scroll to bottom
99+
}
100+
101+
function toggleControls() {
102+
[connectBtn, disconnectBtn, messaging].forEach(el => toggleEnabled(el))
103+
}
104+
105+
function toggleEnabled(el) {
106+
el.disabled = (el.disabled) ? false : true;
90107
}
91108

92109
}

package.json

+5-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "http-proxy-middleware",
3-
"version": "0.19.1",
3+
"version": "0.20.0-beta.2",
44
"description": "The one-liner node.js proxy middleware for connect, express and browser-sync",
55
"main": "dist/index.js",
66
"files": [
@@ -15,11 +15,12 @@
1515
"build": "tsc",
1616
"pretest": "yarn build",
1717
"test": "jest --runInBand",
18-
"precover": "yarn clean && npm run build",
18+
"precover": "yarn clean && yarn build",
1919
"cover": "jest --runInBand --coverage",
20-
"precoveralls": "yarn clean && npm run build",
20+
"precoveralls": "yarn clean && yarn build",
2121
"coveralls": "jest --runInBand --coverage --coverageReporters=text-lcov | coveralls",
22-
"postcoveralls": "yarn clean"
22+
"postcoveralls": "yarn clean",
23+
"prepublish": "yarn build"
2324
},
2425
"repository": {
2526
"type": "git",

src/http-proxy-middleware.ts

+10-15
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,13 @@ import * as Router from './router';
99

1010
export class HttpProxyMiddleware {
1111
private logger = getInstance();
12-
private wsUpgradeDebounced;
1312
private config;
14-
private wsInitialized = false;
13+
private wsInternalSubscribed = false;
1514
private proxyOptions;
1615
private proxy;
1716
private pathRewriter;
1817

1918
constructor(context, opts) {
20-
// https://github.com/chimurai/http-proxy-middleware/issues/57
21-
this.wsUpgradeDebounced = _.debounce(this.handleUpgrade);
2219
this.config = createConfig(context, opts);
2320
this.proxyOptions = this.config.options;
2421

@@ -42,8 +39,11 @@ export class HttpProxyMiddleware {
4239

4340
// https://github.com/chimurai/http-proxy-middleware/issues/19
4441
// expose function to upgrade externally
45-
// middleware.upgrade = wsUpgradeDebounced
46-
(this.middleware as any).upgrade = this.wsUpgradeDebounced;
42+
(this.middleware as any).upgrade = (req, socket, head) => {
43+
if (!this.wsInternalSubscribed) {
44+
this.handleUpgrade(req, socket, head);
45+
}
46+
};
4747
}
4848

4949
// https://github.com/Microsoft/TypeScript/wiki/'this'-in-TypeScript#red-flags-for-this
@@ -62,18 +62,13 @@ export class HttpProxyMiddleware {
6262
};
6363

6464
private catchUpgradeRequest = server => {
65-
// subscribe once; don't subscribe on every request...
66-
// https://github.com/chimurai/http-proxy-middleware/issues/113
67-
if (!this.wsInitialized) {
68-
server.on('upgrade', this.wsUpgradeDebounced);
69-
this.wsInitialized = true;
70-
}
65+
server.on('upgrade', this.handleUpgrade);
66+
// prevent duplicate upgrade handling;
67+
// in case external upgrade is also configured
68+
this.wsInternalSubscribed = true;
7169
};
7270

7371
private handleUpgrade = (req, socket, head) => {
74-
// set to initialized when used externally
75-
this.wsInitialized = true;
76-
7772
if (this.shouldProxy(this.config.context, req)) {
7873
const activeProxyOptions = this.prepareProxyRequest(req);
7974
this.proxy.ws(req, socket, head, activeProxyOptions);

0 commit comments

Comments
 (0)