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,242 @@
|
|
|
1
|
+
import http, {
|
|
2
|
+
IncomingMessage,
|
|
3
|
+
Server as HttpServer,
|
|
4
|
+
ServerResponse,
|
|
5
|
+
} from 'http';
|
|
6
|
+
import https from 'https';
|
|
7
|
+
import { Duplex } from 'stream';
|
|
8
|
+
import { parse as parse_url } from 'url';
|
|
9
|
+
import EE3 from 'eventemitter3';
|
|
10
|
+
import web from './passes/web-incoming';
|
|
11
|
+
import ws from './passes/ws-incoming';
|
|
12
|
+
import { ServerOptions } from '../types';
|
|
13
|
+
|
|
14
|
+
interface PassInterface {
|
|
15
|
+
(this: ProxyServerNew, req: IncomingMessage, res: ServerResponse):
|
|
16
|
+
| boolean
|
|
17
|
+
| void;
|
|
18
|
+
(this: ProxyServerNew, req: IncomingMessage, res: Duplex, head: Buffer):
|
|
19
|
+
| boolean
|
|
20
|
+
| void;
|
|
21
|
+
(
|
|
22
|
+
this: ProxyServerNew,
|
|
23
|
+
req: IncomingMessage,
|
|
24
|
+
res: ServerResponse,
|
|
25
|
+
options: ServerOptions,
|
|
26
|
+
head: Buffer,
|
|
27
|
+
server: ProxyServerNew,
|
|
28
|
+
errorCallback: (
|
|
29
|
+
err: Error,
|
|
30
|
+
req: IncomingMessage,
|
|
31
|
+
res: ServerResponse,
|
|
32
|
+
url: ServerOptions['target'],
|
|
33
|
+
) => void,
|
|
34
|
+
): boolean | void;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Returns a function that creates the loader for
|
|
39
|
+
* either `ws` or `web`'s passes.
|
|
40
|
+
*
|
|
41
|
+
* Examples:
|
|
42
|
+
*
|
|
43
|
+
* createRightProxy('ws')
|
|
44
|
+
* // => [Function]
|
|
45
|
+
*
|
|
46
|
+
* @param type
|
|
47
|
+
*
|
|
48
|
+
* @return Loader function that when called returns an iterator for the right passes
|
|
49
|
+
*
|
|
50
|
+
* @api private
|
|
51
|
+
*/
|
|
52
|
+
function createRightProxy(type: 'web' | 'ws') {
|
|
53
|
+
return function (options: ServerOptions) {
|
|
54
|
+
return function (
|
|
55
|
+
this: ProxyServerNew,
|
|
56
|
+
req: IncomingMessage,
|
|
57
|
+
resOrSocket: ServerResponse | Duplex,
|
|
58
|
+
): void {
|
|
59
|
+
const passes = type === 'ws' ? this.wsPasses : this.webPasses,
|
|
60
|
+
// TODO: Migrate away from arguments.
|
|
61
|
+
// eslint-disable-next-line prefer-rest-params
|
|
62
|
+
args = [].slice.call(arguments) as any[];
|
|
63
|
+
let cntr = args.length - 1,
|
|
64
|
+
head,
|
|
65
|
+
cbl;
|
|
66
|
+
|
|
67
|
+
/* optional args parse begin */
|
|
68
|
+
if (typeof args[cntr] === 'function') {
|
|
69
|
+
cbl = args[cntr];
|
|
70
|
+
|
|
71
|
+
cntr--;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
let requestOptions = options;
|
|
75
|
+
if (!(args[cntr] instanceof Buffer) && args[cntr] !== resOrSocket) {
|
|
76
|
+
//Copy global options
|
|
77
|
+
requestOptions = Object.assign({}, options);
|
|
78
|
+
//Overwrite with request options
|
|
79
|
+
Object.assign(requestOptions, args[cntr]);
|
|
80
|
+
|
|
81
|
+
cntr--;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (args[cntr] instanceof Buffer) {
|
|
85
|
+
head = args[cntr];
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/* optional args parse end */
|
|
89
|
+
|
|
90
|
+
(['target', 'forward'] as const).forEach(function (e) {
|
|
91
|
+
if (typeof requestOptions[e] === 'string')
|
|
92
|
+
requestOptions[e] = parse_url(requestOptions[e] as string);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
if (!requestOptions.target && !requestOptions.forward) {
|
|
96
|
+
this.emit('error', new Error('Must provide a proper URL as target'));
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
for (let i = 0; i < passes.length; i++) {
|
|
101
|
+
/**
|
|
102
|
+
* Call of passes functions
|
|
103
|
+
* pass(req, res, options, head)
|
|
104
|
+
*
|
|
105
|
+
* In WebSockets case the `res` variable
|
|
106
|
+
* refer to the connection socket
|
|
107
|
+
* pass(req, socket, options, head)
|
|
108
|
+
*/
|
|
109
|
+
// TODO: Figure out the typing here.
|
|
110
|
+
// @ts-ignore
|
|
111
|
+
if (passes[i](req, resOrSocket, requestOptions, head, this, cbl)) {
|
|
112
|
+
// passes can return a truthy value to halt the loop
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export class ProxyServerNew extends EE3 {
|
|
121
|
+
options: ServerOptions;
|
|
122
|
+
web: PassInterface;
|
|
123
|
+
ws: PassInterface;
|
|
124
|
+
webPasses: PassInterface[];
|
|
125
|
+
wsPasses: PassInterface[];
|
|
126
|
+
|
|
127
|
+
_server: HttpServer | undefined;
|
|
128
|
+
|
|
129
|
+
constructor(options: ServerOptions) {
|
|
130
|
+
super();
|
|
131
|
+
options = options || {};
|
|
132
|
+
options.prependPath = options.prependPath !== false;
|
|
133
|
+
|
|
134
|
+
this.web = createRightProxy('web')(options);
|
|
135
|
+
this.ws = createRightProxy('ws')(options);
|
|
136
|
+
this.options = options;
|
|
137
|
+
|
|
138
|
+
this.webPasses = Object.keys(web).map(function (pass) {
|
|
139
|
+
// TODO: Figure out the typing here.
|
|
140
|
+
return web[pass as keyof typeof web] as PassInterface;
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
this.wsPasses = Object.keys(ws).map(function (pass) {
|
|
144
|
+
// TODO: Figure out the typing here.
|
|
145
|
+
return ws[pass as keyof typeof ws] as PassInterface;
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
this.on('error', this.onError, this);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
after(type: 'web' | 'ws', passName: string, callback: PassInterface) {
|
|
152
|
+
const passes = type === 'ws' ? this.wsPasses : this.webPasses;
|
|
153
|
+
let i = -1;
|
|
154
|
+
|
|
155
|
+
passes.forEach((v, idx) => {
|
|
156
|
+
if (v.name === passName) i = idx;
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
if (i === -1) throw new Error('No such pass');
|
|
160
|
+
|
|
161
|
+
passes.splice(i++, 0, callback);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
before(type: 'web' | 'ws', passName: string, callback: PassInterface) {
|
|
165
|
+
const passes = type === 'ws' ? this.wsPasses : this.webPasses;
|
|
166
|
+
let i = -1;
|
|
167
|
+
|
|
168
|
+
passes.forEach((v, idx) => {
|
|
169
|
+
if (v.name === passName) i = idx;
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
if (i === -1) throw new Error('No such pass');
|
|
173
|
+
|
|
174
|
+
passes.splice(i, 0, callback);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
close(callback?: () => void) {
|
|
178
|
+
if (this._server) {
|
|
179
|
+
this._server.close(() => {
|
|
180
|
+
this._server = undefined;
|
|
181
|
+
callback?.();
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
listen(port: number, hostname: string) {
|
|
187
|
+
const closure = (req: IncomingMessage, res: ServerResponse) => {
|
|
188
|
+
this.web(req, res);
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
const server = this.options.ssl
|
|
192
|
+
? https.createServer(this.options.ssl, closure)
|
|
193
|
+
: http.createServer(closure);
|
|
194
|
+
|
|
195
|
+
if (this.options.ws) {
|
|
196
|
+
server.on(
|
|
197
|
+
'upgrade',
|
|
198
|
+
(req: IncomingMessage, socket: Duplex, head: Buffer) => {
|
|
199
|
+
this.ws(req, socket, head);
|
|
200
|
+
},
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
server.listen(port, hostname);
|
|
205
|
+
|
|
206
|
+
this._server = server;
|
|
207
|
+
|
|
208
|
+
return this;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
onError(err: Error) {
|
|
212
|
+
//
|
|
213
|
+
// Remark: Replicate node core behavior using EE3
|
|
214
|
+
// so we force people to handle their own errors
|
|
215
|
+
//
|
|
216
|
+
if (super.listeners('error').length === 1) {
|
|
217
|
+
throw err;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Creates the proxy server.
|
|
224
|
+
*
|
|
225
|
+
* Examples:
|
|
226
|
+
*
|
|
227
|
+
* httpProxy.createServer({ .. }, 8000)
|
|
228
|
+
* // => '{ web: [Function], ws: [Function] ... }'
|
|
229
|
+
*
|
|
230
|
+
* @param options Config object passed to the proxy
|
|
231
|
+
*
|
|
232
|
+
* @return Proxy object with handlers for `ws` and `web` requests
|
|
233
|
+
*
|
|
234
|
+
* @api public
|
|
235
|
+
*/
|
|
236
|
+
export function createProxyServer(options: ServerOptions) {
|
|
237
|
+
return new ProxyServerNew(options);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
export const createProxy = createProxyServer;
|
|
241
|
+
|
|
242
|
+
export const createServer = createProxyServer;
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
const httpNative = require('http'),
|
|
2
|
+
httpsNative = require('https'),
|
|
3
|
+
common = require('../common'),
|
|
4
|
+
followRedirects = require('follow-redirects'),
|
|
5
|
+
web_o = Object.values(require('./web-outgoing'));
|
|
6
|
+
|
|
7
|
+
const nativeAgents = { http: httpNative, https: httpsNative };
|
|
8
|
+
|
|
9
|
+
/*!
|
|
10
|
+
* Array of passes.
|
|
11
|
+
*
|
|
12
|
+
* A `pass` is just a function that is executed on `req, res, options`
|
|
13
|
+
* so that you can easily add new checks while still keeping the base
|
|
14
|
+
* flexible.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
module.exports = {
|
|
18
|
+
/**
|
|
19
|
+
* Sets `content-length` to '0' if request is of DELETE type.
|
|
20
|
+
*
|
|
21
|
+
* @param {ClientRequest} req Request object
|
|
22
|
+
* @param {IncomingMessage} res Response object
|
|
23
|
+
* @param {object} options Config object passed to the proxy
|
|
24
|
+
*
|
|
25
|
+
* @api private
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
deleteLength: function deleteLength(req, res, options) {
|
|
29
|
+
if (
|
|
30
|
+
(req.method === 'DELETE' || req.method === 'OPTIONS') &&
|
|
31
|
+
!req.headers['content-length']
|
|
32
|
+
) {
|
|
33
|
+
req.headers['content-length'] = '0';
|
|
34
|
+
delete req.headers['transfer-encoding'];
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Sets timeout in request socket if it was specified in options.
|
|
40
|
+
*
|
|
41
|
+
* @param {ClientRequest} req Request object
|
|
42
|
+
* @param {IncomingMessage} res Response object
|
|
43
|
+
* @param {object} options Config object passed to the proxy
|
|
44
|
+
*
|
|
45
|
+
* @api private
|
|
46
|
+
*/
|
|
47
|
+
|
|
48
|
+
timeout: function timeout(req, res, options) {
|
|
49
|
+
if (options.timeout) {
|
|
50
|
+
req.socket.setTimeout(options.timeout);
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Sets `x-forwarded-*` headers if specified in config.
|
|
56
|
+
*
|
|
57
|
+
* @param {ClientRequest} req Request object
|
|
58
|
+
* @param {IncomingMessage} res Response object
|
|
59
|
+
* @param {object} options Config object passed to the proxy
|
|
60
|
+
*
|
|
61
|
+
* @api private
|
|
62
|
+
*/
|
|
63
|
+
|
|
64
|
+
XHeaders: function XHeaders(req, res, options) {
|
|
65
|
+
if (!options.xfwd) return;
|
|
66
|
+
|
|
67
|
+
const encrypted = req.isSpdy || common.hasEncryptedConnection(req);
|
|
68
|
+
const values = {
|
|
69
|
+
for: req.connection.remoteAddress || req.socket.remoteAddress,
|
|
70
|
+
port: common.getPort(req),
|
|
71
|
+
proto: encrypted ? 'https' : 'http',
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
['for', 'port', 'proto'].forEach(function (header) {
|
|
75
|
+
req.headers['x-forwarded-' + header] =
|
|
76
|
+
(req.headers['x-forwarded-' + header] || '') +
|
|
77
|
+
(req.headers['x-forwarded-' + header] ? ',' : '') +
|
|
78
|
+
values[header];
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
req.headers['x-forwarded-host'] =
|
|
82
|
+
req.headers['x-forwarded-host'] || req.headers['host'] || '';
|
|
83
|
+
},
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Does the actual proxying. If `forward` is enabled fires up
|
|
87
|
+
* a ForwardStream, same happens for ProxyStream. The request
|
|
88
|
+
* just dies otherwise.
|
|
89
|
+
*
|
|
90
|
+
* @param {ClientRequest} req Request object
|
|
91
|
+
* @param {IncomingMessage} res Response object
|
|
92
|
+
* @param {object} options Config object passed to the proxy
|
|
93
|
+
*
|
|
94
|
+
* @api private
|
|
95
|
+
*/
|
|
96
|
+
|
|
97
|
+
stream: function stream(req, res, options, _, server, clb) {
|
|
98
|
+
// And we begin!
|
|
99
|
+
server.emit('start', req, res, options.target || options.forward);
|
|
100
|
+
|
|
101
|
+
const agents = options.followRedirects ? followRedirects : nativeAgents;
|
|
102
|
+
const http = agents.http;
|
|
103
|
+
const https = agents.https;
|
|
104
|
+
|
|
105
|
+
if (options.forward) {
|
|
106
|
+
// If forward enable, so just pipe the request
|
|
107
|
+
const forwardReq = (
|
|
108
|
+
options.forward.protocol === 'https:' ? https : http
|
|
109
|
+
).request(
|
|
110
|
+
common.setupOutgoing(options.ssl || {}, options, req, 'forward'),
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
// error handler (e.g. ECONNRESET, ECONNREFUSED)
|
|
114
|
+
// Handle errors on incoming request as well as it makes sense to
|
|
115
|
+
const forwardError = createErrorHandler(forwardReq, options.forward);
|
|
116
|
+
req.on('error', forwardError);
|
|
117
|
+
forwardReq.on('error', forwardError);
|
|
118
|
+
|
|
119
|
+
(options.buffer || req).pipe(forwardReq);
|
|
120
|
+
if (!options.target) {
|
|
121
|
+
return res.end();
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Request initalization
|
|
126
|
+
const proxyReq = (
|
|
127
|
+
options.target.protocol === 'https:' ? https : http
|
|
128
|
+
).request(common.setupOutgoing(options.ssl || {}, options, req));
|
|
129
|
+
|
|
130
|
+
// Enable developers to modify the proxyReq before headers are sent
|
|
131
|
+
proxyReq.on('socket', function (socket) {
|
|
132
|
+
if (server && !proxyReq.getHeader('expect')) {
|
|
133
|
+
server.emit('proxyReq', proxyReq, req, res, options);
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
// allow outgoing socket to timeout so that we could
|
|
138
|
+
// show an error page at the initial request
|
|
139
|
+
if (options.proxyTimeout) {
|
|
140
|
+
proxyReq.setTimeout(options.proxyTimeout, function () {
|
|
141
|
+
proxyReq.destroy();
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Ensure we abort proxy if request is aborted
|
|
146
|
+
// Need to use different methods due to node changes in 15.0.0.
|
|
147
|
+
|
|
148
|
+
// In node < 15.0.0, listen for request abort and destroy the proxy request.
|
|
149
|
+
req.on('aborted', function () {
|
|
150
|
+
proxyReq.destroy();
|
|
151
|
+
});
|
|
152
|
+
// In node > 15.0.0, listen for the close event on the response. If it was
|
|
153
|
+
// destroyed then destroy the proxy request.
|
|
154
|
+
res.on('close', function () {
|
|
155
|
+
if (res.destroyed) {
|
|
156
|
+
proxyReq.destroy();
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
// handle errors in proxy and incoming request, just like for forward proxy
|
|
161
|
+
const proxyError = createErrorHandler(proxyReq, options.target);
|
|
162
|
+
req.on('error', proxyError);
|
|
163
|
+
proxyReq.on('error', proxyError);
|
|
164
|
+
|
|
165
|
+
function createErrorHandler(proxyReq, url) {
|
|
166
|
+
return function proxyError(err) {
|
|
167
|
+
if (req.socket.destroyed && err.code === 'ECONNRESET') {
|
|
168
|
+
server.emit('econnreset', err, req, res, url);
|
|
169
|
+
proxyReq.destroy();
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (clb) {
|
|
174
|
+
clb(err, req, res, url);
|
|
175
|
+
} else {
|
|
176
|
+
server.emit('error', err, req, res, url);
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
(options.buffer || req).pipe(proxyReq);
|
|
182
|
+
|
|
183
|
+
proxyReq.on('response', function (proxyRes) {
|
|
184
|
+
if (server) {
|
|
185
|
+
server.emit('proxyRes', proxyRes, req, res);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (!res.headersSent && !options.selfHandleResponse) {
|
|
189
|
+
for (let i = 0; i < web_o.length; i++) {
|
|
190
|
+
if (web_o[i](req, res, proxyRes, options)) {
|
|
191
|
+
break;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (!res.finished) {
|
|
197
|
+
// Allow us to listen when the proxy has completed
|
|
198
|
+
proxyRes.on('end', function () {
|
|
199
|
+
if (server) server.emit('end', req, res, proxyRes);
|
|
200
|
+
});
|
|
201
|
+
// We pipe to the response unless its expected to be handled by the user
|
|
202
|
+
if (!options.selfHandleResponse) proxyRes.pipe(res);
|
|
203
|
+
} else {
|
|
204
|
+
if (server) server.emit('end', req, res, proxyRes);
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
},
|
|
208
|
+
};
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
const url = require('url'),
|
|
2
|
+
common = require('../common');
|
|
3
|
+
|
|
4
|
+
const redirectRegex = /^201|30(1|2|7|8)$/;
|
|
5
|
+
|
|
6
|
+
/*!
|
|
7
|
+
* Array of passes.
|
|
8
|
+
*
|
|
9
|
+
* A `pass` is just a function that is executed on `req, res, options`
|
|
10
|
+
* so that you can easily add new checks while still keeping the base
|
|
11
|
+
* flexible.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
module.exports = {
|
|
15
|
+
// <--
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* If is a HTTP 1.0 request, remove chunk headers
|
|
19
|
+
*
|
|
20
|
+
* @param {ClientRequest} req Request object
|
|
21
|
+
* @param {IncomingMessage} res Response object
|
|
22
|
+
* @param {proxyResponse} proxyRes Response object from the proxy request
|
|
23
|
+
*
|
|
24
|
+
* @api private
|
|
25
|
+
*/
|
|
26
|
+
removeChunked: function removeChunked(req, res, proxyRes) {
|
|
27
|
+
if (req.httpVersion === '1.0') {
|
|
28
|
+
delete proxyRes.headers['transfer-encoding'];
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* If is a HTTP 1.0 request, set the correct connection header
|
|
34
|
+
* or if connection header not present, then use `keep-alive`
|
|
35
|
+
*
|
|
36
|
+
* @param {ClientRequest} req Request object
|
|
37
|
+
* @param {IncomingMessage} res Response object
|
|
38
|
+
* @param {proxyResponse} proxyRes Response object from the proxy request
|
|
39
|
+
*
|
|
40
|
+
* @api private
|
|
41
|
+
*/
|
|
42
|
+
setConnection: function setConnection(req, res, proxyRes) {
|
|
43
|
+
if (req.httpVersion === '1.0') {
|
|
44
|
+
proxyRes.headers.connection = req.headers.connection || 'close';
|
|
45
|
+
} else if (req.httpVersion !== '2.0' && !proxyRes.headers.connection) {
|
|
46
|
+
proxyRes.headers.connection = req.headers.connection || 'keep-alive';
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
|
|
50
|
+
setRedirectHostRewrite: function setRedirectHostRewrite(
|
|
51
|
+
req,
|
|
52
|
+
res,
|
|
53
|
+
proxyRes,
|
|
54
|
+
options,
|
|
55
|
+
) {
|
|
56
|
+
if (
|
|
57
|
+
(options.hostRewrite || options.autoRewrite || options.protocolRewrite) &&
|
|
58
|
+
proxyRes.headers['location'] &&
|
|
59
|
+
redirectRegex.test(proxyRes.statusCode)
|
|
60
|
+
) {
|
|
61
|
+
const target = url.parse(options.target);
|
|
62
|
+
const u = url.parse(proxyRes.headers['location']);
|
|
63
|
+
|
|
64
|
+
// make sure the redirected host matches the target host before rewriting
|
|
65
|
+
if (target.host != u.host) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (options.hostRewrite) {
|
|
70
|
+
u.host = options.hostRewrite;
|
|
71
|
+
} else if (options.autoRewrite) {
|
|
72
|
+
u.host = req.headers['host'];
|
|
73
|
+
}
|
|
74
|
+
if (options.protocolRewrite) {
|
|
75
|
+
u.protocol = options.protocolRewrite;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
proxyRes.headers['location'] = u.format();
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
/**
|
|
82
|
+
* Copy headers from proxyResponse to response
|
|
83
|
+
* set each header in response object.
|
|
84
|
+
*
|
|
85
|
+
* @param {ClientRequest} req Request object
|
|
86
|
+
* @param {IncomingMessage} res Response object
|
|
87
|
+
* @param {proxyResponse} proxyRes Response object from the proxy request
|
|
88
|
+
* @param {Object} options options.cookieDomainRewrite: Config to rewrite cookie domain
|
|
89
|
+
*
|
|
90
|
+
* @api private
|
|
91
|
+
*/
|
|
92
|
+
writeHeaders: function writeHeaders(req, res, proxyRes, options) {
|
|
93
|
+
let rewriteCookieDomainConfig = options.cookieDomainRewrite,
|
|
94
|
+
rewriteCookiePathConfig = options.cookiePathRewrite,
|
|
95
|
+
rawHeaderKeyMap;
|
|
96
|
+
const preserveHeaderKeyCase = options.preserveHeaderKeyCase,
|
|
97
|
+
setHeader = function (key, header) {
|
|
98
|
+
if (header == undefined) return;
|
|
99
|
+
if (rewriteCookieDomainConfig && key.toLowerCase() === 'set-cookie') {
|
|
100
|
+
header = common.rewriteCookieProperty(
|
|
101
|
+
header,
|
|
102
|
+
rewriteCookieDomainConfig,
|
|
103
|
+
'domain',
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
if (rewriteCookiePathConfig && key.toLowerCase() === 'set-cookie') {
|
|
107
|
+
header = common.rewriteCookieProperty(
|
|
108
|
+
header,
|
|
109
|
+
rewriteCookiePathConfig,
|
|
110
|
+
'path',
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
res.setHeader(String(key).trim(), header);
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
if (typeof rewriteCookieDomainConfig === 'string') {
|
|
117
|
+
//also test for ''
|
|
118
|
+
rewriteCookieDomainConfig = { '*': rewriteCookieDomainConfig };
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (typeof rewriteCookiePathConfig === 'string') {
|
|
122
|
+
//also test for ''
|
|
123
|
+
rewriteCookiePathConfig = { '*': rewriteCookiePathConfig };
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// message.rawHeaders is added in: v0.11.6
|
|
127
|
+
// https://nodejs.org/api/http.html#http_message_rawheaders
|
|
128
|
+
if (preserveHeaderKeyCase && proxyRes.rawHeaders != undefined) {
|
|
129
|
+
rawHeaderKeyMap = {};
|
|
130
|
+
for (let i = 0; i < proxyRes.rawHeaders.length; i += 2) {
|
|
131
|
+
const key = proxyRes.rawHeaders[i];
|
|
132
|
+
rawHeaderKeyMap[key.toLowerCase()] = key;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
Object.keys(proxyRes.headers).forEach(function (key) {
|
|
137
|
+
const header = proxyRes.headers[key];
|
|
138
|
+
if (preserveHeaderKeyCase && rawHeaderKeyMap) {
|
|
139
|
+
key = rawHeaderKeyMap[key] || key;
|
|
140
|
+
}
|
|
141
|
+
setHeader(key, header);
|
|
142
|
+
});
|
|
143
|
+
},
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Set the statusCode from the proxyResponse
|
|
147
|
+
*
|
|
148
|
+
* @param {ClientRequest} req Request object
|
|
149
|
+
* @param {IncomingMessage} res Response object
|
|
150
|
+
* @param {proxyResponse} proxyRes Response object from the proxy request
|
|
151
|
+
*
|
|
152
|
+
* @api private
|
|
153
|
+
*/
|
|
154
|
+
writeStatusCode: function writeStatusCode(req, res, proxyRes) {
|
|
155
|
+
// From Node.js docs: response.writeHead(statusCode[, statusMessage][, headers])
|
|
156
|
+
if (proxyRes.statusMessage) {
|
|
157
|
+
res.statusCode = proxyRes.statusCode;
|
|
158
|
+
res.statusMessage = proxyRes.statusMessage;
|
|
159
|
+
} else {
|
|
160
|
+
res.statusCode = proxyRes.statusCode;
|
|
161
|
+
}
|
|
162
|
+
},
|
|
163
|
+
};
|