UNPKG

3.88 kBJavaScriptView Raw
1
2/**
3 * Module dependencies.
4 */
5
6var url = require('url');
7var LRU = require('lru-cache');
8var HttpProxyAgent = require('http-proxy-agent');
9var HttpsProxyAgent = require('https-proxy-agent');
10var PacProxyAgent = require('pac-proxy-agent');
11var SocksProxyAgent = require('socks-proxy-agent');
12
13/**
14 * Module exports.
15 */
16
17exports = module.exports = proxy;
18
19/**
20 * Number of `http.Agent` instances to cache.
21 *
22 * This value was arbitrarily chosen... a better
23 * value could be conceived with some benchmarks.
24 */
25
26var cacheSize = 20;
27
28/**
29 * Cache for `http.Agent` instances.
30 */
31
32exports.cache = new LRU(cacheSize);
33
34/**
35 * The built-in proxy types.
36 */
37
38exports.proxies = Object.create(null);
39exports.proxies.http = httpOrHttpsProxy;
40exports.proxies.https = httpOrHttpsProxy;
41exports.proxies.socks = socksProxy;
42
43PacProxyAgent.protocols.forEach(function (protocol) {
44 exports.proxies['pac+' + protocol] = pacProxy;
45});
46
47/**
48 * Attempts to get an `http.Agent` instance based off of the given proxy URI
49 * information, and the `secure` flag.
50 *
51 * An LRU cache is used, to prevent unnecessary creation of proxy
52 * `http.Agent` instances.
53 *
54 * @param {String} uri proxy url
55 * @param {Boolean} secure true if this is for an HTTPS request, false for HTTP
56 * @return {http.Agent}
57 * @api public
58 */
59
60function proxy (uri, secure) {
61
62 if (!uri) {
63 throw new TypeError('You must pass a proxy "uri" to connect to');
64 }
65
66 // parse the URI into an opts object if it's a String
67 var proxyParsed = uri;
68 if ('string' == typeof uri) {
69 proxyParsed = url.parse(uri);
70 }
71
72 // get the requested proxy "protocol"
73 var protocol = proxyParsed.protocol;
74 if (!protocol) {
75 throw new TypeError('You must specify a string "protocol" for the ' +
76 'proxy type (' + types().join(', ') + ')');
77 }
78
79 // strip the trailing ":" if present
80 if (':' == protocol[protocol.length - 1]) {
81 protocol = protocol.substring(0, protocol.length - 1);
82 }
83
84 // get the proxy `http.Agent` creation function
85 var proxyFn = exports.proxies[protocol];
86 if ('function' != typeof proxyFn) {
87 throw new TypeError('unsupported proxy protocol: "' + protocol + '"');
88 }
89
90 // format the proxy info back into a URI, since an opts object
91 // could have been passed in originally. This generated URI is used
92 // as part of the "key" for the LRU cache
93 var proxyUri = url.format({
94 protocol: protocol + ':',
95 slashes: true,
96 hostname: proxyParsed.hostname || proxyParsed.host,
97 port: proxyParsed.port
98 });
99
100 // create the "key" for the LRU cache
101 var key = proxyUri;
102 if (secure) key += ' secure';
103
104 // attempt to get a cached `http.Agent` instance first
105 var agent = exports.cache.get(key);
106 if (!agent) {
107 // get an `http.Agent` instance from protocol-specific agent function
108 agent = proxyFn(proxyParsed, secure);
109 if (agent) exports.cache.set(key, agent);
110 } else {
111 //console.error('cache hit! %j', key);
112 }
113
114 return agent;
115}
116
117/**
118 * Default "http" and "https" proxy URI handlers.
119 *
120 * @api protected
121 */
122
123function httpOrHttpsProxy (proxy, secure) {
124 if (secure) {
125 // HTTPS
126 return new HttpsProxyAgent(proxy);
127 } else {
128 // HTTP
129 return new HttpProxyAgent(proxy);
130 }
131}
132
133/**
134 * Default "socks" proxy URI handler.
135 *
136 * @api protected
137 */
138
139function socksProxy (proxy, secure) {
140 return new SocksProxyAgent(proxy, secure);
141}
142
143/**
144 * Default "pac+*" proxy URI handler.
145 *
146 * @api protected
147 */
148
149function pacProxy (proxy, secure) {
150 var agent = new PacProxyAgent(proxy, secure);
151 agent.secureEndpoint = secure;
152 return agent;
153}
154
155/**
156 * Returns an Array of supported protocol string names.
157 *
158 * @return {Array}
159 * @api private
160 */
161
162function types () {
163 var rtn = [];
164 // not using Object.keys() so that we get any
165 // potential prototype values as well
166 for (var type in exports.proxies) rtn.push(type);
167 return rtn;
168}