-
-
Notifications
You must be signed in to change notification settings - Fork 75
/
request.js
124 lines (114 loc) · 3.29 KB
/
request.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
'use strict'
const semver = require('semver')
const http = require('http')
const https = require('https')
const eos = require('end-of-stream')
const pump = require('pump')
const { stripHttp1ConnectionHeaders } = require('./utils')
function buildRequest (opts) {
const isHttp2 = !!opts.http2
const requests = {
'http:': http,
'https:': https
}
const baseUrl = opts.base
const rejectUnauthorized = opts.rejectUnauthorized
var http2Client
var agents
var http2
if (isHttp2) {
if (semver.lt(process.version, '9.0.0')) {
throw new Error('Http2 support requires Node version >= 9.0.0')
}
if (!opts.base) throw new Error('Option base is required when http2 is true')
} else {
agents = {
'http:': new http.Agent(agentOption(opts)),
'https:': new https.Agent(agentOption(opts))
}
}
if (isHttp2) {
http2 = getHttp2()
return { request: handleHttp2Req, close }
} else {
return { request: handleHttp1Req, close }
}
function close () {
if (!isHttp2) {
agents['http:'].destroy()
agents['https:'].destroy()
} else if (http2Client) {
http2Client.destroy()
}
}
function handleHttp1Req (opts, done) {
const req = requests[opts.url.protocol].request({
method: opts.method,
port: opts.url.port,
path: opts.url.pathname + opts.qs,
hostname: opts.url.hostname,
headers: opts.headers,
agent: agents[opts.url.protocol]
})
req.on('error', done)
req.on('response', res => {
done(null, { statusCode: res.statusCode, headers: res.headers, stream: res })
})
end(req, opts.body, done)
}
function handleHttp2Req (opts, done) {
if (!http2Client || http2Client.destroyed) {
http2Client = http2.connect(baseUrl, { rejectUnauthorized })
http2Client.once('error', done)
// we might enqueue a large number of requests in this connection
// before it's connected
http2Client.setMaxListeners(0)
http2Client.on('connect', () => {
// reset the max listener to 10 on connect
http2Client.setMaxListeners(10)
http2Client.removeListener('error', done)
})
}
const req = http2Client.request({
':method': opts.method,
':path': opts.url.pathname + opts.qs,
...stripHttp1ConnectionHeaders(opts.headers)
})
const isGet = opts.method === 'GET' || opts.method === 'get'
if (!isGet) {
end(req, opts.body, done)
}
eos(req, err => {
if (err) done(err)
})
req.on('response', headers => {
const statusCode = headers[':status']
done(null, { statusCode, headers, stream: req })
})
}
}
module.exports = buildRequest
function agentOption (opts) {
return {
keepAlive: true,
keepAliveMsecs: opts.keepAliveMsecs || 60 * 1000, // 1 minute
maxSockets: opts.maxSockets || 2048,
maxFreeSockets: opts.maxFreeSockets || 2048,
rejectUnauthorized: opts.rejectUnauthorized
}
}
function end (req, body, cb) {
if (!body || typeof body === 'string' || body instanceof Uint8Array) {
req.end(body)
} else if (body.pipe) {
pump(body, req, err => {
if (err) cb(err)
})
} else {
cb(new Error(`type unsupported for body: ${body.constructor}`))
}
}
// neede to avoid the experimental warning
function getHttp2 () {
return require('http2')
}