1 | 'use strict'
|
2 |
|
3 | var url = require('url')
|
4 | var 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 | var protocol = uriObject.protocol
|
37 | var 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 | function Tunnel (request) {
|
106 | this.request = request
|
107 | this.proxyHeaderWhiteList = defaultProxyHeaderWhiteList
|
108 | this.proxyHeaderExclusiveList = []
|
109 | if (typeof request.tunnel !== 'undefined') {
|
110 | this.tunnelOverride = request.tunnel
|
111 | }
|
112 | }
|
113 |
|
114 | Tunnel.prototype.isEnabled = function () {
|
115 | var self = this
|
116 | var request = self.request
|
117 |
|
118 |
|
119 |
|
120 | if (typeof self.tunnelOverride !== 'undefined') {
|
121 | return self.tunnelOverride
|
122 | }
|
123 |
|
124 |
|
125 | if (request.uri.protocol === 'https:') {
|
126 | return true
|
127 | }
|
128 |
|
129 |
|
130 | return false
|
131 | }
|
132 |
|
133 | Tunnel.prototype.setup = function (options) {
|
134 | var self = this
|
135 | var request = self.request
|
136 |
|
137 | options = options || {}
|
138 |
|
139 | if (typeof request.proxy === 'string') {
|
140 | request.proxy = url.parse(request.proxy)
|
141 | }
|
142 |
|
143 | if (!request.proxy || !request.tunnel) {
|
144 | return false
|
145 | }
|
146 |
|
147 |
|
148 | if (options.proxyHeaderWhiteList) {
|
149 | self.proxyHeaderWhiteList = options.proxyHeaderWhiteList
|
150 | }
|
151 | if (options.proxyHeaderExclusiveList) {
|
152 | self.proxyHeaderExclusiveList = options.proxyHeaderExclusiveList
|
153 | }
|
154 |
|
155 | var proxyHeaderExclusiveList = self.proxyHeaderExclusiveList.concat(defaultProxyHeaderExclusiveList)
|
156 | var proxyHeaderWhiteList = self.proxyHeaderWhiteList.concat(proxyHeaderExclusiveList)
|
157 |
|
158 |
|
159 |
|
160 | var proxyHeaders = constructProxyHeaderWhiteList(request.headers, proxyHeaderWhiteList)
|
161 | proxyHeaders.host = constructProxyHost(request.uri)
|
162 |
|
163 | proxyHeaderExclusiveList.forEach(request.removeHeader, request)
|
164 |
|
165 |
|
166 | var tunnelFn = getTunnelFn(request)
|
167 | var tunnelOptions = constructTunnelOptions(request, proxyHeaders)
|
168 | request.agent = tunnelFn(tunnelOptions)
|
169 |
|
170 | return true
|
171 | }
|
172 |
|
173 | Tunnel.defaultProxyHeaderWhiteList = defaultProxyHeaderWhiteList
|
174 | Tunnel.defaultProxyHeaderExclusiveList = defaultProxyHeaderExclusiveList
|
175 | exports.Tunnel = Tunnel
|