Skip to content

Commit da11ed0

Browse files
committed
fix(websocket): fixes websocket upgrade when both config.ws and external .upgrade() are used (chimurai#80)
* fix(websocket): fixes websocket upgrade when both config.ws and external .upgrade() are used.
1 parent cbb2f61 commit da11ed0

File tree

4 files changed

+133
-128
lines changed

4 files changed

+133
-128
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
## [v0.15.x](https://github.com/chimurai/http-proxy-middleware/releases/tag/v0.15.x)
44
- feat(pathRewrite): expose `req` object to pathRewrite function.
5+
- fix(websocket): fixes websocket upgrade when both config.ws and external .upgrade() are used.
56

67
## [v0.15.0](https://github.com/chimurai/http-proxy-middleware/releases/tag/v0.15.0)
78
- feat(pathRewrite): support for custom pathRewrite function.

index.js

Lines changed: 3 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -1,129 +1,5 @@
1-
var httpProxy = require('http-proxy');
2-
var configFactory = require('./lib/config-factory');
3-
var handlers = require('./lib/handlers');
4-
var contextMatcher = require('./lib/context-matcher');
5-
var PathRewriter = require('./lib/path-rewriter');
6-
var ProxyTable = require('./lib/proxy-table');
7-
var logger = require('./lib/logger').getInstance();
8-
var getArrow = require('./lib/logger').getArrow;
9-
10-
var httpProxyMiddleware = function(context, opts) {
11-
var isWsUpgradeListened = false;
12-
var config = configFactory.createConfig(context, opts);
13-
var proxyOptions = config.options;
14-
15-
// create proxy
16-
var proxy = httpProxy.createProxyServer(proxyOptions);
17-
logger.info('[HPM] Proxy created:', config.context, ' -> ', proxyOptions.target);
18-
19-
var pathRewriter = PathRewriter.create(proxyOptions.pathRewrite); // returns undefined when "pathRewrite" is not provided
20-
21-
// attach handler to http-proxy events
22-
handlers.init(proxy, proxyOptions);
23-
24-
// log errors for debug purpose
25-
proxy.on('error', logError);
26-
27-
// https://github.com/chimurai/http-proxy-middleware/issues/19
28-
// expose function to upgrade externally
29-
middleware.upgrade = function(req, socket, head) {
30-
handleUpgrade(req, socket, head);
31-
isWsUpgradeListened = true;
32-
};
33-
34-
return middleware;
35-
36-
function middleware(req, res, next) {
37-
// https://github.com/chimurai/http-proxy-middleware/issues/17
38-
req.url = req.originalUrl;
39-
40-
if (contextMatcher.match(config.context, req.url, req)) {
41-
var activeProxyOptions = prepareProxyRequest(req);
42-
proxy.web(req, res, activeProxyOptions);
43-
} else {
44-
next();
45-
}
46-
47-
if (proxyOptions.ws === true) {
48-
catchUpgradeRequest(req.connection.server);
49-
}
50-
}
51-
52-
function catchUpgradeRequest(server) {
53-
// make sure only 1 handle listens to server's upgrade request.
54-
if (isWsUpgradeListened === true) {
55-
return;
56-
}
57-
58-
server.on('upgrade', handleUpgrade);
59-
isWsUpgradeListened = true;
60-
}
61-
62-
function handleUpgrade(req, socket, head) {
63-
if (contextMatcher.match(config.context, req.url, req)) {
64-
var activeProxyOptions = prepareProxyRequest(req);
65-
proxy.ws(req, socket, head, activeProxyOptions);
66-
logger.info('[HPM] Upgrading to WebSocket');
67-
}
68-
}
69-
70-
/**
71-
* Apply option.proxyTable and option.pathRewrite
72-
* Order matters:
73-
ProxyTable uses original path for routing;
74-
NOT the modified path, after it has been rewritten by pathRewrite
75-
*/
76-
function prepareProxyRequest(req) {
77-
// store uri before it gets rewritten for logging
78-
var originalPath = req.url;
79-
80-
// Apply in order:
81-
// 1. option.proxyTable
82-
// 2. option.pathRewrite
83-
var alteredProxyOptions = __applyProxyTableOption(req, proxyOptions);
84-
__applyPathRewrite(pathRewriter, req);
85-
86-
// debug logging for both http(s) and websockets
87-
if (proxyOptions.logLevel === 'debug') {
88-
var arrow = getArrow(originalPath, req.url, proxyOptions.target, alteredProxyOptions.target);
89-
logger.debug('[HPM] %s %s %s %s', req.method, originalPath, arrow, alteredProxyOptions.target);
90-
}
91-
92-
return alteredProxyOptions;
93-
}
94-
95-
// Modify option.target when proxyTable present.
96-
// return altered options
97-
function __applyProxyTableOption(req) {
98-
var result = proxyOptions;
99-
100-
if (proxyOptions.proxyTable) {
101-
result = ProxyTable.createProxyOptions(req, proxyOptions);
102-
}
103-
104-
return result;
105-
}
106-
107-
// rewrite path
108-
function __applyPathRewrite(pathRewriter, req) {
109-
if (pathRewriter) {
110-
var path = pathRewriter(req.url, req);
111-
112-
if (path) {
113-
req.url = path;
114-
} else {
115-
logger.info('[HPM] pathRewrite: No rewritten path found. (%s)', req.url);
116-
}
117-
}
118-
}
119-
120-
function logError(err, req, res) {
121-
var hostname = (req.hostname || req.host) || (req.headers && req.headers.host); // (node0.10 || node 4/5) || (websocket)
122-
var targetUri = (proxyOptions.target.host || proxyOptions.target) + req.url;
123-
124-
logger.error('[HPM] PROXY ERROR: %s. %s -> %s', err.code, hostname, targetUri);
125-
}
1+
var HPM = require('./lib');
1262

3+
module.exports = function(context, opts) {
4+
return new HPM(context, opts);
1275
};
128-
129-
module.exports = httpProxyMiddleware;

lib/index.js

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
var httpProxy = require('http-proxy');
2+
var configFactory = require('./config-factory');
3+
var handlers = require('./handlers');
4+
var contextMatcher = require('./context-matcher');
5+
var PathRewriter = require('./path-rewriter');
6+
var ProxyTable = require('./proxy-table');
7+
var logger = require('./logger').getInstance();
8+
var getArrow = require('./logger').getArrow;
9+
10+
module.exports = HttpProxyMiddleware;
11+
12+
function HttpProxyMiddleware(context, opts) {
13+
var isWsUpgradeListened = false;
14+
var config = configFactory.createConfig(context, opts);
15+
var proxyOptions = config.options;
16+
17+
// create proxy
18+
var proxy = httpProxy.createProxyServer(proxyOptions);
19+
logger.info('[HPM] Proxy created:', config.context, ' -> ', proxyOptions.target);
20+
21+
var pathRewriter = PathRewriter.create(proxyOptions.pathRewrite); // returns undefined when "pathRewrite" is not provided
22+
23+
// attach handler to http-proxy events
24+
handlers.init(proxy, proxyOptions);
25+
26+
// log errors for debug purpose
27+
proxy.on('error', logError);
28+
29+
// https://github.com/chimurai/http-proxy-middleware/issues/19
30+
// expose function to upgrade externally
31+
middleware.upgrade = function(req, socket, head) {
32+
handleUpgrade(req, socket, head);
33+
isWsUpgradeListened = true;
34+
};
35+
36+
return middleware;
37+
38+
function middleware(req, res, next) {
39+
// https://github.com/chimurai/http-proxy-middleware/issues/17
40+
req.url = req.originalUrl;
41+
42+
if (contextMatcher.match(config.context, req.url, req)) {
43+
var activeProxyOptions = prepareProxyRequest(req);
44+
proxy.web(req, res, activeProxyOptions);
45+
} else {
46+
next();
47+
}
48+
49+
if (proxyOptions.ws === true) {
50+
catchUpgradeRequest(req.connection.server);
51+
}
52+
}
53+
54+
function catchUpgradeRequest(server) {
55+
// make sure only 1 handle listens to server's upgrade request.
56+
if (isWsUpgradeListened === true) {
57+
return;
58+
}
59+
60+
server.on('upgrade', handleUpgrade);
61+
isWsUpgradeListened = true;
62+
}
63+
64+
function handleUpgrade(req, socket, head) {
65+
if (contextMatcher.match(config.context, req.url, req)) {
66+
var activeProxyOptions = prepareProxyRequest(req);
67+
proxy.ws(req, socket, head, activeProxyOptions);
68+
logger.info('[HPM] Upgrading to WebSocket');
69+
}
70+
}
71+
72+
/**
73+
* Apply option.proxyTable and option.pathRewrite
74+
* Order matters:
75+
ProxyTable uses original path for routing;
76+
NOT the modified path, after it has been rewritten by pathRewrite
77+
*/
78+
function prepareProxyRequest(req) {
79+
// store uri before it gets rewritten for logging
80+
var originalPath = req.url;
81+
82+
// Apply in order:
83+
// 1. option.proxyTable
84+
// 2. option.pathRewrite
85+
var alteredProxyOptions = __applyProxyTableOption(req, proxyOptions);
86+
__applyPathRewrite(pathRewriter, req);
87+
88+
// debug logging for both http(s) and websockets
89+
if (proxyOptions.logLevel === 'debug') {
90+
var arrow = getArrow(originalPath, req.url, proxyOptions.target, alteredProxyOptions.target);
91+
logger.debug('[HPM] %s %s %s %s', req.method, originalPath, arrow, alteredProxyOptions.target);
92+
}
93+
94+
return alteredProxyOptions;
95+
}
96+
97+
// Modify option.target when proxyTable present.
98+
// return altered options
99+
function __applyProxyTableOption(req) {
100+
var result = proxyOptions;
101+
102+
if (proxyOptions.proxyTable) {
103+
result = ProxyTable.createProxyOptions(req, proxyOptions);
104+
}
105+
106+
return result;
107+
}
108+
109+
// rewrite path
110+
function __applyPathRewrite(pathRewriter, req) {
111+
if (pathRewriter) {
112+
var path = pathRewriter(req.url, req);
113+
114+
if (path) {
115+
req.url = path;
116+
} else {
117+
logger.info('[HPM] pathRewrite: No rewritten path found. (%s)', req.url);
118+
}
119+
}
120+
}
121+
122+
function logError(err, req, res) {
123+
var hostname = (req.hostname || req.host) || (req.headers && req.headers.host); // (node0.10 || node 4/5) || (websocket)
124+
var targetUri = (proxyOptions.target.host || proxyOptions.target) + req.url;
125+
126+
logger.error('[HPM] PROXY ERROR: %s. %s -> %s', err.code, hostname, targetUri);
127+
}
128+
};

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "http-proxy-middleware",
3-
"version": "0.15.0",
3+
"version": "0.15.1-beta",
44
"description": "The one-liner node.js proxy middleware for connect, express and browser-sync",
55
"main": "index.js",
66
"scripts": {

0 commit comments

Comments
 (0)