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