-
Notifications
You must be signed in to change notification settings - Fork 9
/
proxy.js
163 lines (138 loc) · 4.33 KB
/
proxy.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
/**
* Creates a proxy server.
*/
// Core.
import http from 'http';
// NPM.
import request from 'request';
// Local.
import * as injector from './injector';
import * as logger from './logger';
import { MatchTypes } from './enums';
import { trimSlash } from './utils';
// Members.
const DEFAULT_PORT = 8063;
const ERROR_MISSING_PARAMS = 'Missing one or more required parameters';
/**
* Adds a proxy target.
*
* @param {String} target - A string to test URLs against.
* If the URL matches according to its options.matchType rule,
* options.content will be injected into the response.
*
* @param {Object | Array} options - Required options or list of options.
*
* @param {String} options.selector - The cheerio selector to use.
* @see https://github.com/cheeriojs/cheerio#selectors
*
* @param {String} options.manipulation - The cheerio manipulation method to use.
* @see enums.js.
*
* @param {String} options.content - The HTML / CSS / JS content to inject.
*
* @returns {void}
*/
export function proxy (target, options) {
// A target is required.
if (!target) {
throw new Error(`${ERROR_MISSING_PARAMS}: 'url'`);
}
// Lowercase our target url for storage
target = trimSlash(target.toLowerCase());
const defaultMatchType = MatchTypes.EXACT;
// Ensure that options is an array.
options = [].concat(options);
const missingOptions = [];
for (let optionEntry of options) {
if (!optionEntry.selector) {
missingOptions.push('selector');
}
if (!optionEntry.manipulation) {
missingOptions.push('manipulation');
}
if (!optionEntry.content) {
missingOptions.push('content');
}
optionEntry.matchType = optionEntry.matchType || defaultMatchType;
// All options parameters are required.
if (missingOptions.length > 0) {
throw new Error(`${ERROR_MISSING_PARAMS}: '${missingOptions.join('\', \'')}'`);
}
// Add the desired proxy target.
injector.addProxyTarget(target, optionEntry);
}
}
/**
* Removes a proxy target.
*
* @param {String} target - A string to test URLs against.
*
* @param {Object | Array} options - options to be removed, or list of options to be removed.
*
* @returns {void}
*/
export function removeProxy (target, options) {
// A target is required.
if (!target) {
throw new Error(`${ERROR_MISSING_PARAMS}: 'url'`);
}
// Lowercase our target url for comparison to storage
target = trimSlash(target.toLowerCase());
if (options) {
// Ensure that options is an array.
options = [].concat(options);
for (let optionEntry of options) {
// Remove the desired proxy target.
injector.removeProxyTarget(target, optionEntry);
}
}
else {
injector.removeProxyTarget(target);
}
}
/**
* Creates and starts the proxy server.
*
* @param {Object} options - Optional options.
*
* @param {Number} options.port - The port to start the proxy server on.
* Defaults to 8063.
*
* @param {Boolean} options.debugMode - Enables logging to help debug problems.
*
* @returns {void}
*/
export function start (options) {
// Enable the logger if requested to do so.
if (options && !!options.debugMode) {
logger.enableLogging();
}
let proxyServer = http.createServer((clientRequest, clientResponse) => {
clientRequest.pause();
logger.log(`Making a request to ${clientRequest.url}.`);
// Actually make the request to the desired endpoint to get the initial HTML.
request({
url: clientRequest.url,
encoding: null
}, (err, response, endpointHTML) => {
// Possibly inject things into this response.
let alteredResponse = endpointHTML;
if (endpointHTML) {
alteredResponse = injector.injectInto(clientRequest.url, endpointHTML);
}
// Preserve the server headers.
if (response && response.headers) {
for (let header in response.headers) {
clientResponse.setHeader(header, response.headers[header]);
}
}
// Send the possibly modified response back to the client.
clientResponse.end(alteredResponse);
});
clientRequest.resume();
});
// Start the proxy server.
const realPort = (options && options.port) ? options.port : DEFAULT_PORT;
console.log(`Proxy server listening on port ${realPort}.`); //eslint-disable-line no-console
proxyServer.listen(realPort);
}