1 | 'use strict'
|
2 |
|
3 | var url = require('url')
|
4 | , tunnel = require('tunnel-agent')
|
5 |
|
6 | var defaultProxyHeaderWhiteList = [
|
7 | 'accept',
|
8 | 'accept-charset',
|
9 | 'accept-encoding',
|
10 | 'accept-language',
|
11 | 'accept-ranges',
|
12 | 'cache-control',
|
13 | 'content-encoding',
|
14 | 'content-language',
|
15 | 'content-location',
|
16 | 'content-md5',
|
17 | 'content-range',
|
18 | 'content-type',
|
19 | 'connection',
|
20 | 'date',
|
21 | 'expect',
|
22 | 'max-forwards',
|
23 | 'pragma',
|
24 | 'referer',
|
25 | 'te',
|
26 | 'user-agent',
|
27 | 'via'
|
28 | ]
|
29 |
|
30 | var defaultProxyHeaderExclusiveList = [
|
31 | 'proxy-authorization'
|
32 | ]
|
33 |
|
34 | function constructProxyHost(uriObject) {
|
35 | var port = uriObject.port
|
36 | , protocol = uriObject.protocol
|
37 | , proxyHost = uriObject.hostname + ':'
|
38 |
|
39 | if (port) {
|
40 | proxyHost += port
|
41 | } else if (protocol === 'https:') {
|
42 | proxyHost += '443'
|
43 | } else {
|
44 | proxyHost += '80'
|
45 | }
|
46 |
|
47 | return proxyHost
|
48 | }
|
49 |
|
50 | function constructProxyHeaderWhiteList(headers, proxyHeaderWhiteList) {
|
51 | var whiteList = proxyHeaderWhiteList
|
52 | .reduce(function (set, header) {
|
53 | set[header.toLowerCase()] = true
|
54 | return set
|
55 | }, {})
|
56 |
|
57 | return Object.keys(headers)
|
58 | .filter(function (header) {
|
59 | return whiteList[header.toLowerCase()]
|
60 | })
|
61 | .reduce(function (set, header) {
|
62 | set[header] = headers[header]
|
63 | return set
|
64 | }, {})
|
65 | }
|
66 |
|
67 | function constructTunnelOptions (request, proxyHeaders) {
|
68 | var proxy = request.proxy
|
69 |
|
70 | var tunnelOptions = {
|
71 | proxy : {
|
72 | host : proxy.hostname,
|
73 | port : +proxy.port,
|
74 | proxyAuth : proxy.auth,
|
75 | headers : proxyHeaders
|
76 | },
|
77 | headers : request.headers,
|
78 | ca : request.ca,
|
79 | cert : request.cert,
|
80 | key : request.key,
|
81 | passphrase : request.passphrase,
|
82 | pfx : request.pfx,
|
83 | ciphers : request.ciphers,
|
84 | rejectUnauthorized : request.rejectUnauthorized,
|
85 | secureOptions : request.secureOptions,
|
86 | secureProtocol : request.secureProtocol
|
87 | }
|
88 |
|
89 | return tunnelOptions
|
90 | }
|
91 |
|
92 | function constructTunnelFnName(uri, proxy) {
|
93 | var uriProtocol = (uri.protocol === 'https:' ? 'https' : 'http')
|
94 | var proxyProtocol = (proxy.protocol === 'https:' ? 'Https' : 'Http')
|
95 | return [uriProtocol, proxyProtocol].join('Over')
|
96 | }
|
97 |
|
98 | function getTunnelFn(request) {
|
99 | var uri = request.uri
|
100 | var proxy = request.proxy
|
101 | var tunnelFnName = constructTunnelFnName(uri, proxy)
|
102 | return tunnel[tunnelFnName]
|
103 | }
|
104 |
|
105 |
|
106 | function Tunnel (request) {
|
107 | this.request = request
|
108 | this.proxyHeaderWhiteList = defaultProxyHeaderWhiteList
|
109 | this.proxyHeaderExclusiveList = []
|
110 | if (typeof request.tunnel !== 'undefined') {
|
111 | this.tunnelOverride = request.tunnel
|
112 | }
|
113 | }
|
114 |
|
115 | Tunnel.prototype.isEnabled = function () {
|
116 | var self = this
|
117 | , request = self.request
|
118 |
|
119 |
|
120 |
|
121 | if (typeof self.tunnelOverride !== 'undefined') {
|
122 | return self.tunnelOverride
|
123 | }
|
124 |
|
125 |
|
126 | if (request.uri.protocol === 'https:') {
|
127 | return true
|
128 | }
|
129 |
|
130 |
|
131 | return false
|
132 | }
|
133 |
|
134 | Tunnel.prototype.setup = function (options) {
|
135 | var self = this
|
136 | , request = self.request
|
137 |
|
138 | options = options || {}
|
139 |
|
140 | if (typeof request.proxy === 'string') {
|
141 | request.proxy = url.parse(request.proxy)
|
142 | }
|
143 |
|
144 | if (!request.proxy || !request.tunnel) {
|
145 | return false
|
146 | }
|
147 |
|
148 |
|
149 | if (options.proxyHeaderWhiteList) {
|
150 | self.proxyHeaderWhiteList = options.proxyHeaderWhiteList
|
151 | }
|
152 | if (options.proxyHeaderExclusiveList) {
|
153 | self.proxyHeaderExclusiveList = options.proxyHeaderExclusiveList
|
154 | }
|
155 |
|
156 | var proxyHeaderExclusiveList = self.proxyHeaderExclusiveList.concat(defaultProxyHeaderExclusiveList)
|
157 | var proxyHeaderWhiteList = self.proxyHeaderWhiteList.concat(proxyHeaderExclusiveList)
|
158 |
|
159 |
|
160 |
|
161 | var proxyHeaders = constructProxyHeaderWhiteList(request.headers, proxyHeaderWhiteList)
|
162 | proxyHeaders.host = constructProxyHost(request.uri)
|
163 |
|
164 | proxyHeaderExclusiveList.forEach(request.removeHeader, request)
|
165 |
|
166 |
|
167 | var tunnelFn = getTunnelFn(request)
|
168 | var tunnelOptions = constructTunnelOptions(request, proxyHeaders)
|
169 | request.agent = tunnelFn(tunnelOptions)
|
170 |
|
171 | return true
|
172 | }
|
173 |
|
174 | Tunnel.defaultProxyHeaderWhiteList = defaultProxyHeaderWhiteList
|
175 | Tunnel.defaultProxyHeaderExclusiveList = defaultProxyHeaderExclusiveList
|
176 | exports.Tunnel = Tunnel
|