cloudcms-server 0.9.270 → 0.9.271
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/middleware/proxy/proxy.js +5 -9
- package/package.json +2 -2
- package/temp/http-proxy/.auto-changelog +6 -0
- package/temp/http-proxy/.gitattributes +1 -0
- package/temp/http-proxy/CHANGELOG.md +1872 -0
- package/temp/http-proxy/CODE_OF_CONDUCT.md +74 -0
- package/temp/http-proxy/LICENSE +23 -0
- package/temp/http-proxy/README.md +568 -0
- package/temp/http-proxy/codecov.yml +10 -0
- package/temp/http-proxy/index.js +13 -0
- package/temp/http-proxy/lib/http-proxy/common.js +220 -0
- package/temp/http-proxy/lib/http-proxy/index.js +174 -0
- package/temp/http-proxy/lib/http-proxy/passes/web-incoming.js +174 -0
- package/temp/http-proxy/lib/http-proxy/passes/web-outgoing.js +135 -0
- package/temp/http-proxy/lib/http-proxy/passes/ws-incoming.js +141 -0
- package/temp/http-proxy/lib/index.js +13 -0
- package/temp/http-proxy/package.json +41 -0
- package/temp/http-proxy/renovate.json +19 -0
- package/temp/node-http-proxy/.eslintignore +3 -0
- package/temp/node-http-proxy/.eslintrc.js +21 -0
- package/temp/node-http-proxy/.github/workflows/ci.yml +30 -0
- package/temp/node-http-proxy/.prettierrc +7 -0
- package/temp/node-http-proxy/CODE_OF_CONDUCT.md +74 -0
- package/temp/node-http-proxy/LICENSE +23 -0
- package/temp/node-http-proxy/README.md +568 -0
- package/temp/node-http-proxy/codecov.yml +10 -0
- package/temp/node-http-proxy/dist/http-proxy/common.js +220 -0
- package/temp/node-http-proxy/dist/http-proxy/index.js +174 -0
- package/temp/node-http-proxy/dist/http-proxy/passes/web-incoming.js +174 -0
- package/temp/node-http-proxy/dist/http-proxy/passes/web-outgoing.js +135 -0
- package/temp/node-http-proxy/dist/http-proxy/passes/ws-incoming.js +141 -0
- package/temp/node-http-proxy/dist/index.js +13 -0
- package/temp/node-http-proxy/lib/http-proxy/common.js +265 -0
- package/temp/node-http-proxy/lib/http-proxy/index.ts +242 -0
- package/temp/node-http-proxy/lib/http-proxy/passes/web-incoming.js +208 -0
- package/temp/node-http-proxy/lib/http-proxy/passes/web-outgoing.js +163 -0
- package/temp/node-http-proxy/lib/http-proxy/passes/ws-incoming.js +179 -0
- package/temp/node-http-proxy/lib/index.ts +13 -0
- package/temp/node-http-proxy/lib/types.d.ts +277 -0
- package/temp/node-http-proxy/package-lock.json +5028 -0
- package/temp/node-http-proxy/package.json +47 -0
- package/temp/node-http-proxy/tsconfig.build.json +4 -0
- package/temp/node-http-proxy/tsconfig.json +115 -0
- package/temp/node-http-proxy/vitest.config.ts +9 -0
- package/util/proxy-factory.js +39 -128
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
const url = require('url'), common = require('../common');
|
|
3
|
+
const redirectRegex = /^201|30(1|2|7|8)$/;
|
|
4
|
+
/*!
|
|
5
|
+
* Array of passes.
|
|
6
|
+
*
|
|
7
|
+
* A `pass` is just a function that is executed on `req, res, options`
|
|
8
|
+
* so that you can easily add new checks while still keeping the base
|
|
9
|
+
* flexible.
|
|
10
|
+
*/
|
|
11
|
+
module.exports = {
|
|
12
|
+
// <--
|
|
13
|
+
/**
|
|
14
|
+
* If is a HTTP 1.0 request, remove chunk headers
|
|
15
|
+
*
|
|
16
|
+
* @param {ClientRequest} req Request object
|
|
17
|
+
* @param {IncomingMessage} res Response object
|
|
18
|
+
* @param {proxyResponse} proxyRes Response object from the proxy request
|
|
19
|
+
*
|
|
20
|
+
* @api private
|
|
21
|
+
*/
|
|
22
|
+
removeChunked: function removeChunked(req, res, proxyRes) {
|
|
23
|
+
if (req.httpVersion === '1.0') {
|
|
24
|
+
delete proxyRes.headers['transfer-encoding'];
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
/**
|
|
28
|
+
* If is a HTTP 1.0 request, set the correct connection header
|
|
29
|
+
* or if connection header not present, then use `keep-alive`
|
|
30
|
+
*
|
|
31
|
+
* @param {ClientRequest} req Request object
|
|
32
|
+
* @param {IncomingMessage} res Response object
|
|
33
|
+
* @param {proxyResponse} proxyRes Response object from the proxy request
|
|
34
|
+
*
|
|
35
|
+
* @api private
|
|
36
|
+
*/
|
|
37
|
+
setConnection: function setConnection(req, res, proxyRes) {
|
|
38
|
+
if (req.httpVersion === '1.0') {
|
|
39
|
+
proxyRes.headers.connection = req.headers.connection || 'close';
|
|
40
|
+
}
|
|
41
|
+
else if (req.httpVersion !== '2.0' && !proxyRes.headers.connection) {
|
|
42
|
+
proxyRes.headers.connection = req.headers.connection || 'keep-alive';
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
setRedirectHostRewrite: function setRedirectHostRewrite(req, res, proxyRes, options) {
|
|
46
|
+
if ((options.hostRewrite || options.autoRewrite || options.protocolRewrite) &&
|
|
47
|
+
proxyRes.headers['location'] &&
|
|
48
|
+
redirectRegex.test(proxyRes.statusCode)) {
|
|
49
|
+
const target = url.parse(options.target);
|
|
50
|
+
const u = url.parse(proxyRes.headers['location']);
|
|
51
|
+
// make sure the redirected host matches the target host before rewriting
|
|
52
|
+
if (target.host != u.host) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
if (options.hostRewrite) {
|
|
56
|
+
u.host = options.hostRewrite;
|
|
57
|
+
}
|
|
58
|
+
else if (options.autoRewrite) {
|
|
59
|
+
u.host = req.headers['host'];
|
|
60
|
+
}
|
|
61
|
+
if (options.protocolRewrite) {
|
|
62
|
+
u.protocol = options.protocolRewrite;
|
|
63
|
+
}
|
|
64
|
+
proxyRes.headers['location'] = u.format();
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
/**
|
|
68
|
+
* Copy headers from proxyResponse to response
|
|
69
|
+
* set each header in response object.
|
|
70
|
+
*
|
|
71
|
+
* @param {ClientRequest} req Request object
|
|
72
|
+
* @param {IncomingMessage} res Response object
|
|
73
|
+
* @param {proxyResponse} proxyRes Response object from the proxy request
|
|
74
|
+
* @param {Object} options options.cookieDomainRewrite: Config to rewrite cookie domain
|
|
75
|
+
*
|
|
76
|
+
* @api private
|
|
77
|
+
*/
|
|
78
|
+
writeHeaders: function writeHeaders(req, res, proxyRes, options) {
|
|
79
|
+
let rewriteCookieDomainConfig = options.cookieDomainRewrite, rewriteCookiePathConfig = options.cookiePathRewrite, rawHeaderKeyMap;
|
|
80
|
+
const preserveHeaderKeyCase = options.preserveHeaderKeyCase, setHeader = function (key, header) {
|
|
81
|
+
if (header == undefined)
|
|
82
|
+
return;
|
|
83
|
+
if (rewriteCookieDomainConfig && key.toLowerCase() === 'set-cookie') {
|
|
84
|
+
header = common.rewriteCookieProperty(header, rewriteCookieDomainConfig, 'domain');
|
|
85
|
+
}
|
|
86
|
+
if (rewriteCookiePathConfig && key.toLowerCase() === 'set-cookie') {
|
|
87
|
+
header = common.rewriteCookieProperty(header, rewriteCookiePathConfig, 'path');
|
|
88
|
+
}
|
|
89
|
+
res.setHeader(String(key).trim(), header);
|
|
90
|
+
};
|
|
91
|
+
if (typeof rewriteCookieDomainConfig === 'string') {
|
|
92
|
+
//also test for ''
|
|
93
|
+
rewriteCookieDomainConfig = { '*': rewriteCookieDomainConfig };
|
|
94
|
+
}
|
|
95
|
+
if (typeof rewriteCookiePathConfig === 'string') {
|
|
96
|
+
//also test for ''
|
|
97
|
+
rewriteCookiePathConfig = { '*': rewriteCookiePathConfig };
|
|
98
|
+
}
|
|
99
|
+
// message.rawHeaders is added in: v0.11.6
|
|
100
|
+
// https://nodejs.org/api/http.html#http_message_rawheaders
|
|
101
|
+
if (preserveHeaderKeyCase && proxyRes.rawHeaders != undefined) {
|
|
102
|
+
rawHeaderKeyMap = {};
|
|
103
|
+
for (let i = 0; i < proxyRes.rawHeaders.length; i += 2) {
|
|
104
|
+
const key = proxyRes.rawHeaders[i];
|
|
105
|
+
rawHeaderKeyMap[key.toLowerCase()] = key;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
Object.keys(proxyRes.headers).forEach(function (key) {
|
|
109
|
+
const header = proxyRes.headers[key];
|
|
110
|
+
if (preserveHeaderKeyCase && rawHeaderKeyMap) {
|
|
111
|
+
key = rawHeaderKeyMap[key] || key;
|
|
112
|
+
}
|
|
113
|
+
setHeader(key, header);
|
|
114
|
+
});
|
|
115
|
+
},
|
|
116
|
+
/**
|
|
117
|
+
* Set the statusCode from the proxyResponse
|
|
118
|
+
*
|
|
119
|
+
* @param {ClientRequest} req Request object
|
|
120
|
+
* @param {IncomingMessage} res Response object
|
|
121
|
+
* @param {proxyResponse} proxyRes Response object from the proxy request
|
|
122
|
+
*
|
|
123
|
+
* @api private
|
|
124
|
+
*/
|
|
125
|
+
writeStatusCode: function writeStatusCode(req, res, proxyRes) {
|
|
126
|
+
// From Node.js docs: response.writeHead(statusCode[, statusMessage][, headers])
|
|
127
|
+
if (proxyRes.statusMessage) {
|
|
128
|
+
res.statusCode = proxyRes.statusCode;
|
|
129
|
+
res.statusMessage = proxyRes.statusMessage;
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
res.statusCode = proxyRes.statusCode;
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
};
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
const http = require('http'), https = require('https'), common = require('../common');
|
|
3
|
+
/*!
|
|
4
|
+
* Array of passes.
|
|
5
|
+
*
|
|
6
|
+
* A `pass` is just a function that is executed on `req, socket, options`
|
|
7
|
+
* so that you can easily add new checks while still keeping the base
|
|
8
|
+
* flexible.
|
|
9
|
+
*/
|
|
10
|
+
/*
|
|
11
|
+
* Websockets Passes
|
|
12
|
+
*
|
|
13
|
+
*/
|
|
14
|
+
module.exports = {
|
|
15
|
+
/**
|
|
16
|
+
* WebSocket requests must have the `GET` method and
|
|
17
|
+
* the `upgrade:websocket` header
|
|
18
|
+
*
|
|
19
|
+
* @param {ClientRequest} req Request object
|
|
20
|
+
* @param {Socket} socket
|
|
21
|
+
*
|
|
22
|
+
* @api private
|
|
23
|
+
*/
|
|
24
|
+
checkMethodAndHeader: function checkMethodAndHeader(req, socket) {
|
|
25
|
+
if (req.method !== 'GET' || !req.headers.upgrade) {
|
|
26
|
+
socket.destroy();
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
if (req.headers.upgrade.toLowerCase() !== 'websocket') {
|
|
30
|
+
socket.destroy();
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
/**
|
|
35
|
+
* Sets `x-forwarded-*` headers if specified in config.
|
|
36
|
+
*
|
|
37
|
+
* @param {ClientRequest} req Request object
|
|
38
|
+
* @param {Socket} socket
|
|
39
|
+
* @param {Object} options Config object passed to the proxy
|
|
40
|
+
*
|
|
41
|
+
* @api private
|
|
42
|
+
*/
|
|
43
|
+
XHeaders: function XHeaders(req, socket, options) {
|
|
44
|
+
if (!options.xfwd)
|
|
45
|
+
return;
|
|
46
|
+
const values = {
|
|
47
|
+
for: req.connection.remoteAddress || req.socket.remoteAddress,
|
|
48
|
+
port: common.getPort(req),
|
|
49
|
+
proto: common.hasEncryptedConnection(req) ? 'wss' : 'ws',
|
|
50
|
+
};
|
|
51
|
+
['for', 'port', 'proto'].forEach(function (header) {
|
|
52
|
+
req.headers['x-forwarded-' + header] =
|
|
53
|
+
(req.headers['x-forwarded-' + header] || '') +
|
|
54
|
+
(req.headers['x-forwarded-' + header] ? ',' : '') +
|
|
55
|
+
values[header];
|
|
56
|
+
});
|
|
57
|
+
},
|
|
58
|
+
/**
|
|
59
|
+
* Does the actual proxying. Make the request and upgrade it
|
|
60
|
+
* send the Switching Protocols request and pipe the sockets.
|
|
61
|
+
*
|
|
62
|
+
* @param {ClientRequest} req Request object
|
|
63
|
+
* @param {Socket} socket
|
|
64
|
+
* @param {Object} options Config object passed to the proxy
|
|
65
|
+
*
|
|
66
|
+
* @api private
|
|
67
|
+
*/
|
|
68
|
+
stream: function stream(req, socket, options, head, server, clb) {
|
|
69
|
+
const createHttpHeader = function (line, headers) {
|
|
70
|
+
return (Object.keys(headers)
|
|
71
|
+
.reduce(function (head, key) {
|
|
72
|
+
const value = headers[key];
|
|
73
|
+
if (!Array.isArray(value)) {
|
|
74
|
+
head.push(key + ': ' + value);
|
|
75
|
+
return head;
|
|
76
|
+
}
|
|
77
|
+
for (let i = 0; i < value.length; i++) {
|
|
78
|
+
head.push(key + ': ' + value[i]);
|
|
79
|
+
}
|
|
80
|
+
return head;
|
|
81
|
+
}, [line])
|
|
82
|
+
.join('\r\n') + '\r\n\r\n');
|
|
83
|
+
};
|
|
84
|
+
common.setupSocket(socket);
|
|
85
|
+
if (head && head.length)
|
|
86
|
+
socket.unshift(head);
|
|
87
|
+
const proxyReq = (common.isSSL.test(options.target.protocol) ? https : http).request(common.setupOutgoing(options.ssl || {}, options, req));
|
|
88
|
+
// Enable developers to modify the proxyReq before headers are sent
|
|
89
|
+
if (server) {
|
|
90
|
+
server.emit('proxyReqWs', proxyReq, req, socket, options, head);
|
|
91
|
+
}
|
|
92
|
+
// Error Handler
|
|
93
|
+
proxyReq.on('error', onOutgoingError);
|
|
94
|
+
proxyReq.on('response', function (res) {
|
|
95
|
+
// if upgrade event isn't going to happen, close the socket
|
|
96
|
+
if (!res.upgrade) {
|
|
97
|
+
socket.write(createHttpHeader('HTTP/' +
|
|
98
|
+
res.httpVersion +
|
|
99
|
+
' ' +
|
|
100
|
+
res.statusCode +
|
|
101
|
+
' ' +
|
|
102
|
+
res.statusMessage, res.headers));
|
|
103
|
+
res.pipe(socket);
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
proxyReq.on('upgrade', function (proxyRes, proxySocket, proxyHead) {
|
|
107
|
+
proxySocket.on('error', onOutgoingError);
|
|
108
|
+
// Allow us to listen when the websocket has completed
|
|
109
|
+
proxySocket.on('end', function () {
|
|
110
|
+
server.emit('close', proxyRes, proxySocket, proxyHead);
|
|
111
|
+
});
|
|
112
|
+
// The pipe below will end proxySocket if socket closes cleanly, but not
|
|
113
|
+
// if it errors (eg, vanishes from the net and starts returning
|
|
114
|
+
// EHOSTUNREACH). We need to do that explicitly.
|
|
115
|
+
socket.on('error', function () {
|
|
116
|
+
proxySocket.end();
|
|
117
|
+
});
|
|
118
|
+
common.setupSocket(proxySocket);
|
|
119
|
+
if (proxyHead && proxyHead.length)
|
|
120
|
+
proxySocket.unshift(proxyHead);
|
|
121
|
+
//
|
|
122
|
+
// Remark: Handle writing the headers to the socket when switching protocols
|
|
123
|
+
// Also handles when a header is an array
|
|
124
|
+
//
|
|
125
|
+
socket.write(createHttpHeader('HTTP/1.1 101 Switching Protocols', proxyRes.headers));
|
|
126
|
+
proxySocket.pipe(socket).pipe(proxySocket);
|
|
127
|
+
server.emit('open', proxySocket);
|
|
128
|
+
server.emit('proxySocket', proxySocket); //DEPRECATED.
|
|
129
|
+
});
|
|
130
|
+
return proxyReq.end(); // XXX: CHECK IF THIS IS THIS CORRECT
|
|
131
|
+
function onOutgoingError(err) {
|
|
132
|
+
if (clb) {
|
|
133
|
+
clb(err, req, socket);
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
server.emit('error', err, req, socket);
|
|
137
|
+
}
|
|
138
|
+
socket.end();
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*!
|
|
3
|
+
* Caron dimonio, con occhi di bragia
|
|
4
|
+
* loro accennando, tutte le raccoglie;
|
|
5
|
+
* batte col remo qualunque s’adagia
|
|
6
|
+
*
|
|
7
|
+
* Charon the demon, with the eyes of glede,
|
|
8
|
+
* Beckoning to them, collects them all together,
|
|
9
|
+
* Beats with his oar whoever lags behind
|
|
10
|
+
*
|
|
11
|
+
* Dante - The Divine Comedy (Canto III)
|
|
12
|
+
*/
|
|
13
|
+
module.exports = './http-proxy';
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
const common = exports,
|
|
2
|
+
url = require('url'),
|
|
3
|
+
extend = require('util')._extend,
|
|
4
|
+
required = require('requires-port');
|
|
5
|
+
|
|
6
|
+
const upgradeHeader = /(^|,)\s*upgrade\s*($|,)/i,
|
|
7
|
+
isSSL = /^https|wss/;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Simple Regex for testing if protocol is https
|
|
11
|
+
*/
|
|
12
|
+
common.isSSL = isSSL;
|
|
13
|
+
/**
|
|
14
|
+
* Copies the right headers from `options` and `req` to
|
|
15
|
+
* `outgoing` which is then used to fire the proxied
|
|
16
|
+
* request.
|
|
17
|
+
*
|
|
18
|
+
* Examples:
|
|
19
|
+
*
|
|
20
|
+
* common.setupOutgoing(outgoing, options, req)
|
|
21
|
+
* // => { host: ..., hostname: ...}
|
|
22
|
+
*
|
|
23
|
+
* @param {Object} Outgoing Base object to be filled with required properties
|
|
24
|
+
* @param {Object} Options Config object passed to the proxy
|
|
25
|
+
* @param {ClientRequest} Req Request Object
|
|
26
|
+
* @param {String} Forward String to select forward or target
|
|
27
|
+
*
|
|
28
|
+
* @return {Object} Outgoing Object with all required properties set
|
|
29
|
+
*
|
|
30
|
+
* @api private
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
common.setupOutgoing = function (outgoing, options, req, forward) {
|
|
34
|
+
outgoing.port =
|
|
35
|
+
options[forward || 'target'].port ||
|
|
36
|
+
(isSSL.test(options[forward || 'target'].protocol) ? 443 : 80);
|
|
37
|
+
|
|
38
|
+
[
|
|
39
|
+
'host',
|
|
40
|
+
'hostname',
|
|
41
|
+
'socketPath',
|
|
42
|
+
'pfx',
|
|
43
|
+
'key',
|
|
44
|
+
'passphrase',
|
|
45
|
+
'cert',
|
|
46
|
+
'ca',
|
|
47
|
+
'ciphers',
|
|
48
|
+
'secureProtocol',
|
|
49
|
+
].forEach(function (e) {
|
|
50
|
+
outgoing[e] = options[forward || 'target'][e];
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
outgoing.method = options.method || req.method;
|
|
54
|
+
outgoing.headers = extend({}, req.headers);
|
|
55
|
+
|
|
56
|
+
if (options.headers) {
|
|
57
|
+
extend(outgoing.headers, options.headers);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (options.auth) {
|
|
61
|
+
outgoing.auth = options.auth;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (options.ca) {
|
|
65
|
+
outgoing.ca = options.ca;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (isSSL.test(options[forward || 'target'].protocol)) {
|
|
69
|
+
outgoing.rejectUnauthorized =
|
|
70
|
+
typeof options.secure === 'undefined' ? true : options.secure;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
outgoing.agent = options.agent || false;
|
|
74
|
+
outgoing.localAddress = options.localAddress;
|
|
75
|
+
|
|
76
|
+
//
|
|
77
|
+
// Remark: If we are false and not upgrading, set the connection: close. This is the right thing to do
|
|
78
|
+
// as node core doesn't handle this COMPLETELY properly yet.
|
|
79
|
+
//
|
|
80
|
+
if (!outgoing.agent) {
|
|
81
|
+
outgoing.headers = outgoing.headers || {};
|
|
82
|
+
if (
|
|
83
|
+
typeof outgoing.headers.connection !== 'string' ||
|
|
84
|
+
!upgradeHeader.test(outgoing.headers.connection)
|
|
85
|
+
) {
|
|
86
|
+
outgoing.headers.connection = 'close';
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// the final path is target path + relative path requested by user:
|
|
91
|
+
const target = options[forward || 'target'];
|
|
92
|
+
const targetPath =
|
|
93
|
+
target && options.prependPath !== false ? target.path || '' : '';
|
|
94
|
+
|
|
95
|
+
//
|
|
96
|
+
// Remark: Can we somehow not use url.parse as a perf optimization?
|
|
97
|
+
//
|
|
98
|
+
let outgoingPath = !options.toProxy ? url.parse(req.url).path || '' : req.url;
|
|
99
|
+
|
|
100
|
+
//
|
|
101
|
+
// Remark: ignorePath will just straight up ignore whatever the request's
|
|
102
|
+
// path is. This can be labeled as FOOT-GUN material if you do not know what
|
|
103
|
+
// you are doing and are using conflicting options.
|
|
104
|
+
//
|
|
105
|
+
outgoingPath = !options.ignorePath ? outgoingPath : '';
|
|
106
|
+
|
|
107
|
+
outgoing.path = common.urlJoin(targetPath, outgoingPath);
|
|
108
|
+
|
|
109
|
+
if (options.changeOrigin) {
|
|
110
|
+
outgoing.headers.host =
|
|
111
|
+
required(outgoing.port, options[forward || 'target'].protocol) &&
|
|
112
|
+
!hasPort(outgoing.host)
|
|
113
|
+
? outgoing.host + ':' + outgoing.port
|
|
114
|
+
: outgoing.host;
|
|
115
|
+
}
|
|
116
|
+
return outgoing;
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Set the proper configuration for sockets,
|
|
121
|
+
* set no delay and set keep alive, also set
|
|
122
|
+
* the timeout to 0.
|
|
123
|
+
*
|
|
124
|
+
* Examples:
|
|
125
|
+
*
|
|
126
|
+
* common.setupSocket(socket)
|
|
127
|
+
* // => Socket
|
|
128
|
+
*
|
|
129
|
+
* @param {Socket} Socket instance to setup
|
|
130
|
+
*
|
|
131
|
+
* @return {Socket} Return the configured socket.
|
|
132
|
+
*
|
|
133
|
+
* @api private
|
|
134
|
+
*/
|
|
135
|
+
|
|
136
|
+
common.setupSocket = function (socket) {
|
|
137
|
+
socket.setTimeout(0);
|
|
138
|
+
socket.setNoDelay(true);
|
|
139
|
+
|
|
140
|
+
socket.setKeepAlive(true, 0);
|
|
141
|
+
|
|
142
|
+
return socket;
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Get the port number from the host. Or guess it based on the connection type.
|
|
147
|
+
*
|
|
148
|
+
* @param {Request} req Incoming HTTP request.
|
|
149
|
+
*
|
|
150
|
+
* @return {String} The port number.
|
|
151
|
+
*
|
|
152
|
+
* @api private
|
|
153
|
+
*/
|
|
154
|
+
common.getPort = function (req) {
|
|
155
|
+
const res = req.headers.host ? req.headers.host.match(/:(\d+)/) : '';
|
|
156
|
+
|
|
157
|
+
return res ? res[1] : common.hasEncryptedConnection(req) ? '443' : '80';
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Check if the request has an encrypted connection.
|
|
162
|
+
*
|
|
163
|
+
* @param {Request} req Incoming HTTP request.
|
|
164
|
+
*
|
|
165
|
+
* @return {Boolean} Whether the connection is encrypted or not.
|
|
166
|
+
*
|
|
167
|
+
* @api private
|
|
168
|
+
*/
|
|
169
|
+
common.hasEncryptedConnection = function (req) {
|
|
170
|
+
return Boolean(req.connection.encrypted || req.connection.pair);
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* OS-agnostic join (doesn't break on URLs like path.join does on Windows)>
|
|
175
|
+
*
|
|
176
|
+
* @return {String} The generated path.
|
|
177
|
+
*
|
|
178
|
+
* @api private
|
|
179
|
+
*/
|
|
180
|
+
|
|
181
|
+
common.urlJoin = function () {
|
|
182
|
+
//
|
|
183
|
+
// We do not want to mess with the query string. All we want to touch is the path.
|
|
184
|
+
//
|
|
185
|
+
const args = Array.prototype.slice.call(arguments),
|
|
186
|
+
lastIndex = args.length - 1,
|
|
187
|
+
last = args[lastIndex],
|
|
188
|
+
lastSegs = last.split('?');
|
|
189
|
+
|
|
190
|
+
args[lastIndex] = lastSegs.shift();
|
|
191
|
+
|
|
192
|
+
//
|
|
193
|
+
// Join all strings, but remove empty strings so we don't get extra slashes from
|
|
194
|
+
// joining e.g. ['', 'am']
|
|
195
|
+
//
|
|
196
|
+
const retSegs = [
|
|
197
|
+
args
|
|
198
|
+
.filter(Boolean)
|
|
199
|
+
.join('/')
|
|
200
|
+
.replace(/\/+/g, '/')
|
|
201
|
+
.replace('http:/', 'http://')
|
|
202
|
+
.replace('https:/', 'https://'),
|
|
203
|
+
];
|
|
204
|
+
|
|
205
|
+
// Only join the query string if it exists so we don't have trailing a '?'
|
|
206
|
+
// on every request
|
|
207
|
+
|
|
208
|
+
// Handle case where there could be multiple ? in the URL.
|
|
209
|
+
retSegs.push.apply(retSegs, lastSegs);
|
|
210
|
+
|
|
211
|
+
return retSegs.join('?');
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Rewrites or removes the domain of a cookie header
|
|
216
|
+
*
|
|
217
|
+
* @param {String|Array} header
|
|
218
|
+
* @param {Object} config, mapping of domain to rewritten domain.
|
|
219
|
+
* '*' key to match any domain, null value to remove the domain.
|
|
220
|
+
*
|
|
221
|
+
* @api private
|
|
222
|
+
*/
|
|
223
|
+
common.rewriteCookieProperty = function rewriteCookieProperty(
|
|
224
|
+
header,
|
|
225
|
+
config,
|
|
226
|
+
property,
|
|
227
|
+
) {
|
|
228
|
+
if (Array.isArray(header)) {
|
|
229
|
+
return header.map(function (headerElement) {
|
|
230
|
+
return rewriteCookieProperty(headerElement, config, property);
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
return header.replace(
|
|
234
|
+
new RegExp('(;\\s*' + property + '=)([^;]+)', 'i'),
|
|
235
|
+
function (match, prefix, previousValue) {
|
|
236
|
+
let newValue;
|
|
237
|
+
if (previousValue in config) {
|
|
238
|
+
newValue = config[previousValue];
|
|
239
|
+
} else if ('*' in config) {
|
|
240
|
+
newValue = config['*'];
|
|
241
|
+
} else {
|
|
242
|
+
//no match, return previous value
|
|
243
|
+
return match;
|
|
244
|
+
}
|
|
245
|
+
if (newValue) {
|
|
246
|
+
//replace value
|
|
247
|
+
return prefix + newValue;
|
|
248
|
+
} else {
|
|
249
|
+
//remove value
|
|
250
|
+
return '';
|
|
251
|
+
}
|
|
252
|
+
},
|
|
253
|
+
);
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Check the host and see if it potentially has a port in it (keep it simple)
|
|
258
|
+
*
|
|
259
|
+
* @returns {Boolean} Whether we have one or not
|
|
260
|
+
*
|
|
261
|
+
* @api private
|
|
262
|
+
*/
|
|
263
|
+
function hasPort(host) {
|
|
264
|
+
return !!~host.indexOf(':');
|
|
265
|
+
}
|