Skip to content

Commit

Permalink
fix(ws): fix concurrent ws requests
Browse files Browse the repository at this point in the history
  • Loading branch information
chimurai committed Jul 9, 2019
1 parent 021b03f commit bef1d49
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 64 deletions.
23 changes: 10 additions & 13 deletions dist/http-proxy-middleware.js
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
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
@@ -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
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

0 comments on commit bef1d49

Please sign in to comment.