get-it
Version:
Generic HTTP request library for node, browsers and workers
167 lines (143 loc) • 4.24 kB
text/typescript
/**
* Code borrowed from https://github.com/request/request
* Modified to be less request-specific, more functional
* Apache License 2.0
*/
import * as tunnel from 'tunnel-agent'
import url from 'url'
const uriParts = [
'protocol',
'slashes',
'auth',
'host',
'port',
'hostname',
'hash',
'search',
'query',
'pathname',
'path',
'href',
]
const defaultProxyHeaderWhiteList = [
'accept',
'accept-charset',
'accept-encoding',
'accept-language',
'accept-ranges',
'cache-control',
'content-encoding',
'content-language',
'content-location',
'content-md5',
'content-range',
'content-type',
'connection',
'date',
'expect',
'max-forwards',
'pragma',
'referer',
'te',
'user-agent',
'via',
]
const defaultProxyHeaderExclusiveList = ['proxy-authorization']
export function shouldEnable(options: any) {
// Tunnel HTTPS by default. Allow the user to override this setting.
// If user has specified a specific tunnel override...
if (typeof options.tunnel !== 'undefined') {
return Boolean(options.tunnel)
}
// If the destination is HTTPS, tunnel.
const uri = url.parse(options.url)
if (uri.protocol === 'https:') {
return true
}
// Otherwise, do not use tunnel.
return false
}
export function applyAgent(opts: any = {}, proxy: any) {
const options = Object.assign({}, opts)
// Setup proxy header exclusive list and whitelist
const proxyHeaderWhiteList = defaultProxyHeaderWhiteList
.concat(options.proxyHeaderWhiteList || [])
.map((header) => header.toLowerCase())
const proxyHeaderExclusiveList = defaultProxyHeaderExclusiveList
.concat(options.proxyHeaderExclusiveList || [])
.map((header) => header.toLowerCase())
// Get the headers we should send to the proxy
const proxyHeaders = getAllowedProxyHeaders(options.headers, proxyHeaderWhiteList)
proxyHeaders.host = constructProxyHost(options)
// Reduce headers to the ones not exclusive for the proxy
options.headers = Object.keys(options.headers || {}).reduce((headers, header) => {
const isAllowed = proxyHeaderExclusiveList.indexOf(header.toLowerCase()) === -1
if (isAllowed) {
headers[header] = options.headers[header]
}
return headers
}, {} as any)
const tunnelFn = getTunnelFn(options, proxy)
const tunnelOptions = constructTunnelOptions(options, proxy, proxyHeaders)
options.agent = tunnelFn(tunnelOptions)
return options
}
function getTunnelFn(options: any, proxy: any) {
const uri = getUriParts(options)
const tunnelFnName = constructTunnelFnName(uri, proxy)
return tunnel[tunnelFnName]
}
function getUriParts(options: any) {
return uriParts.reduce((uri, part) => {
uri[part] = options[part]
return uri
}, {} as any)
}
type UriProtocol = `http` | `https`
type ProxyProtocol = `Http` | `Https`
function constructTunnelFnName(uri: any, proxy: any): `${UriProtocol}Over${ProxyProtocol}` {
const uriProtocol = uri.protocol === 'https:' ? 'https' : 'http'
const proxyProtocol = proxy.protocol === 'https:' ? 'Https' : 'Http'
return `${uriProtocol}Over${proxyProtocol}`
}
function constructProxyHost(uri: any) {
const port = uri.port
const protocol = uri.protocol
let proxyHost = `${uri.hostname}:`
if (port) {
proxyHost += port
} else if (protocol === 'https:') {
proxyHost += '443'
} else {
proxyHost += '80'
}
return proxyHost
}
function getAllowedProxyHeaders(headers: any, whiteList: any): any {
return Object.keys(headers)
.filter((header) => whiteList.indexOf(header.toLowerCase()) !== -1)
.reduce((set: any, header: any) => {
set[header] = headers[header]
return set
}, {})
}
function constructTunnelOptions(options: any, proxy: any, proxyHeaders: any) {
return {
proxy: {
host: proxy.hostname,
port: +proxy.port,
proxyAuth: proxy.auth,
headers: proxyHeaders,
},
headers: options.headers,
ca: options.ca,
cert: options.cert,
key: options.key,
passphrase: options.passphrase,
pfx: options.pfx,
ciphers: options.ciphers,
rejectUnauthorized: options.rejectUnauthorized,
secureOptions: options.secureOptions,
secureProtocol: options.secureProtocol,
}
}