Skip to content

fix(ws): fix concurrent ws requests #344

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 12, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 10 additions & 13 deletions dist/http-proxy-middleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const Router = require("./router");
class HttpProxyMiddleware {
constructor(context, opts) {
this.logger = logger_1.getInstance();
this.wsInitialized = false;
this.wsInternalSubscribed = false;
// https://github.com/Microsoft/TypeScript/wiki/'this'-in-TypeScript#red-flags-for-this
this.middleware = (req, res, next) => __awaiter(this, void 0, void 0, function* () {
if (this.shouldProxy(this.config.context, req)) {
Expand All @@ -35,16 +35,12 @@ class HttpProxyMiddleware {
}
});
this.catchUpgradeRequest = server => {
// subscribe once; don't subscribe on every request...
// https://github.com/chimurai/http-proxy-middleware/issues/113
if (!this.wsInitialized) {
server.on('upgrade', this.wsUpgradeDebounced);
this.wsInitialized = true;
}
server.on('upgrade', this.handleUpgrade);
// prevent duplicate upgrade handling;
// in case external upgrade is also configured
this.wsInternalSubscribed = true;
};
this.handleUpgrade = (req, socket, head) => {
// set to initialized when used externally
this.wsInitialized = true;
if (this.shouldProxy(this.config.context, req)) {
const activeProxyOptions = this.prepareProxyRequest(req);
this.proxy.ws(req, socket, head, activeProxyOptions);
Expand Down Expand Up @@ -120,8 +116,6 @@ class HttpProxyMiddleware {
const errReference = 'https://nodejs.org/api/errors.html#errors_common_system_errors'; // link to Node Common Systems Errors page
this.logger.error(errorMessage, req.url, hostname, target, err.code || err, errReference);
};
// https://github.com/chimurai/http-proxy-middleware/issues/57
this.wsUpgradeDebounced = _.debounce(this.handleUpgrade);
this.config = config_factory_1.createConfig(context, opts);
this.proxyOptions = this.config.options;
// create proxy
Expand All @@ -134,8 +128,11 @@ class HttpProxyMiddleware {
this.proxy.on('error', this.logError);
// https://github.com/chimurai/http-proxy-middleware/issues/19
// expose function to upgrade externally
// middleware.upgrade = wsUpgradeDebounced
this.middleware.upgrade = this.wsUpgradeDebounced;
this.middleware.upgrade = (req, socket, head) => {
if (!this.wsInternalSubscribed) {
this.handleUpgrade(req, socket, head);
}
};
}
}
exports.HttpProxyMiddleware = HttpProxyMiddleware;
81 changes: 49 additions & 32 deletions examples/websocket/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,15 @@ <h2>WebSocket demo</h2>
<p>
<label for="location">location:</label>
<input id="location" type="text" value="ws://localhost:3000">
<button id="connect">connect</button>
<button id="disconnect" disabled="disabled">disconnect</button>
<button id="connectBtn">connect</button>
<button id="disconnectBtn" disabled="disabled">disconnect</button>
</p>
</fieldset>
<fieldset id="messaging" disabled="disabled">
<p>
<label for="message">message:</label>
<input type="text" id="message" value="Hello WebSocket">
<button id="send">send</button>
<button id="sendBtn">send</button>
</p>
<p>
<label for="logger">log:</label>
Expand All @@ -49,44 +49,61 @@ <h2>WebSocket demo</h2>
<script>
window.onload = function () {
// elements
var configuration = document.getElementById('configuration');
var location = document.getElementById('location');
var connect = document.getElementById('connect');
var disconnect = document.getElementById('disconnect');
var messaging = document.getElementById('messaging');
var message = document.getElementById('message');
var send = document.getElementById('send');
var logger = document.getElementById('logger');
const configuration = document.getElementById('configuration');
const location = document.getElementById('location');
const connectBtn = document.getElementById('connectBtn');
const disconnectBtn = document.getElementById('disconnectBtn');
const messaging = document.getElementById('messaging');
const message = document.getElementById('message');
const sendBtn = document.getElementById('sendBtn');
const logger = document.getElementById('logger');

// ws
var socket;

connect.onclick = function () {
connect.disabled = true;
disconnect.disabled = false;
messaging.disabled = false;

socket = new WebSocket(location.value);
socket.onopen = function () { log('CONNECTED'); };
socket.onclose = function () { log('DISCONNECTED'); };
socket.onerror = function () { log('SOCKET ERROR OCCURED'); };
socket.onmessage = function (msg) { log('RECEIVED:' + msg.data); };
let socket;

connectBtn.onclick = () => { connect(); }
disconnectBtn.onclick = () => { disconnect(); }
sendBtn.onclick = () => { sendMessage(message.value); }

function connect() {
setupSocket(location.value);
}

disconnect.onclick = function () {
connect.disabled = false;
disconnect.disabled = true;
messaging.disabled = true;
socket.close();
function disconnect() {
socket.close();
socket = undefined;
}

send.onclick = function () {
log('SEND: ' + message.value);
socket.send(message.value);
function sendMessage(val) {
log('SEND: ' + val);
socket.send(val);
};

function setupSocket(url) {
socket = new WebSocket(url);
socket.addEventListener('open', () => {
log('CONNECTED');
toggleControls();
})
socket.addEventListener('close', () => {
log('DISCONNECTED');
toggleControls()
})
socket.addEventListener('error', () => { log('SOCKET ERROR OCCURED'); })
socket.addEventListener('message', (msg) => { log('RECEIVED:' + msg.data); })
}

function log (message) {
logger.value = logger.value + message + '\n'
logger.value = logger.value + message + '\n';
logger.scrollTop = logger.scrollHeight; // scroll to bottom
}

function toggleControls() {
[connectBtn, disconnectBtn, messaging].forEach(el => toggleEnabled(el))
}

function toggleEnabled(el) {
el.disabled = (el.disabled) ? false : true;
}

}
Expand Down
9 changes: 5 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "http-proxy-middleware",
"version": "0.19.1",
"version": "0.20.0-beta.2",
"description": "The one-liner node.js proxy middleware for connect, express and browser-sync",
"main": "dist/index.js",
"files": [
Expand All @@ -15,11 +15,12 @@
"build": "tsc",
"pretest": "yarn build",
"test": "jest --runInBand",
"precover": "yarn clean && npm run build",
"precover": "yarn clean && yarn build",
"cover": "jest --runInBand --coverage",
"precoveralls": "yarn clean && npm run build",
"precoveralls": "yarn clean && yarn build",
"coveralls": "jest --runInBand --coverage --coverageReporters=text-lcov | coveralls",
"postcoveralls": "yarn clean"
"postcoveralls": "yarn clean",
"prepublish": "yarn build"
},
"repository": {
"type": "git",
Expand Down
25 changes: 10 additions & 15 deletions src/http-proxy-middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,13 @@ import * as Router from './router';

export class HttpProxyMiddleware {
private logger = getInstance();
private wsUpgradeDebounced;
private config;
private wsInitialized = false;
private wsInternalSubscribed = false;
private proxyOptions;
private proxy;
private pathRewriter;

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

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

// https://github.com/chimurai/http-proxy-middleware/issues/19
// expose function to upgrade externally
// middleware.upgrade = wsUpgradeDebounced
(this.middleware as any).upgrade = this.wsUpgradeDebounced;
(this.middleware as any).upgrade = (req, socket, head) => {
if (!this.wsInternalSubscribed) {
this.handleUpgrade(req, socket, head);
}
};
}

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

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

private handleUpgrade = (req, socket, head) => {
// set to initialized when used externally
this.wsInitialized = true;

if (this.shouldProxy(this.config.context, req)) {
const activeProxyOptions = this.prepareProxyRequest(req);
this.proxy.ws(req, socket, head, activeProxyOptions);
Expand Down