bare-http1 3.7.0 → 3.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +18 -2
- package/lib/agent.js +97 -10
- package/lib/client-connection.js +35 -9
- package/lib/client-request.js +2 -8
- package/lib/server-connection.js +32 -5
- package/lib/server.js +12 -0
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -27,14 +27,18 @@ exports.request = function request (url, opts, onresponse) {
|
|
|
27
27
|
|
|
28
28
|
if (typeof url === 'string') url = new URL(url)
|
|
29
29
|
|
|
30
|
-
if (
|
|
30
|
+
if (isURL(url)) {
|
|
31
31
|
opts = opts ? { ...opts } : {}
|
|
32
32
|
|
|
33
33
|
opts.host = url.hostname
|
|
34
34
|
opts.path = url.pathname + url.search
|
|
35
35
|
opts.port = url.port ? parseInt(url.port, 10) : defaultPort(url)
|
|
36
36
|
} else {
|
|
37
|
-
opts = url
|
|
37
|
+
opts = url ? { ...url } : {}
|
|
38
|
+
|
|
39
|
+
// For Node.js compatibility
|
|
40
|
+
opts.host = opts.hostname || opts.host
|
|
41
|
+
opts.port = typeof opts.port === 'string' ? parseInt(opts.port, 10) : opts.port
|
|
38
42
|
}
|
|
39
43
|
|
|
40
44
|
return new Request(opts, onresponse)
|
|
@@ -52,3 +56,15 @@ function defaultPort (url) {
|
|
|
52
56
|
|
|
53
57
|
return null
|
|
54
58
|
}
|
|
59
|
+
|
|
60
|
+
// https://url.spec.whatwg.org/#api
|
|
61
|
+
function isURL (url) {
|
|
62
|
+
return (
|
|
63
|
+
url !== null &&
|
|
64
|
+
typeof url === 'object' &&
|
|
65
|
+
typeof url.protocol === 'string' &&
|
|
66
|
+
typeof url.hostname === 'string' &&
|
|
67
|
+
typeof url.pathname === 'string' &&
|
|
68
|
+
typeof url.search === 'string'
|
|
69
|
+
)
|
|
70
|
+
}
|
package/lib/agent.js
CHANGED
|
@@ -1,21 +1,19 @@
|
|
|
1
1
|
const tcp = require('bare-tcp')
|
|
2
|
+
const HTTPClientConnection = require('./client-connection')
|
|
2
3
|
|
|
3
4
|
module.exports = class HTTPAgent {
|
|
4
5
|
constructor (opts = {}) {
|
|
5
6
|
const {
|
|
6
7
|
keepAlive = false,
|
|
7
|
-
keepAliveMsecs = 1000
|
|
8
|
-
maxSockets = Infinity,
|
|
9
|
-
maxTotalSockets = Infinity,
|
|
10
|
-
maxFreeSockets = 256,
|
|
11
|
-
timeout = -1
|
|
8
|
+
keepAliveMsecs = 1000
|
|
12
9
|
} = opts
|
|
13
10
|
|
|
11
|
+
this._sockets = new Map()
|
|
12
|
+
this._freeSockets = new Map()
|
|
13
|
+
|
|
14
14
|
this._keepAlive = typeof keepAlive === 'number' ? keepAlive : keepAlive ? keepAliveMsecs : -1
|
|
15
|
-
|
|
16
|
-
this.
|
|
17
|
-
this._maxFreeSockets = maxFreeSockets
|
|
18
|
-
this._timeout = timeout
|
|
15
|
+
|
|
16
|
+
this._opts = { ...opts }
|
|
19
17
|
}
|
|
20
18
|
|
|
21
19
|
createConnection (opts) {
|
|
@@ -27,7 +25,96 @@ module.exports = class HTTPAgent {
|
|
|
27
25
|
}
|
|
28
26
|
|
|
29
27
|
keepSocketAlive (socket) {
|
|
30
|
-
return false
|
|
28
|
+
if (this._keepAlive === -1) return false
|
|
29
|
+
|
|
30
|
+
socket.setKeepAlive(true, this._keepAlive)
|
|
31
|
+
socket.unref()
|
|
32
|
+
|
|
33
|
+
return true
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
getName (opts) {
|
|
37
|
+
return `${opts.host}:${opts.port}`
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
addRequest (req, opts) {
|
|
41
|
+
opts = { ...opts, ...this._opts }
|
|
42
|
+
|
|
43
|
+
const name = this.getName(opts)
|
|
44
|
+
|
|
45
|
+
let socket
|
|
46
|
+
|
|
47
|
+
if (this._freeSockets.has(name)) {
|
|
48
|
+
const sockets = this._freeSockets.get(name)
|
|
49
|
+
socket = sockets.values().next().value
|
|
50
|
+
sockets.delete(socket)
|
|
51
|
+
if (sockets.size === 0) this._freeSockets.delete(name)
|
|
52
|
+
|
|
53
|
+
this.reuseSocket(socket, req)
|
|
54
|
+
} else {
|
|
55
|
+
socket = this.createConnection(opts)
|
|
56
|
+
|
|
57
|
+
socket
|
|
58
|
+
.on('free', () => this._onfree(socket, name))
|
|
59
|
+
.on('close', () => this._onremove(socket, name))
|
|
60
|
+
.on('timeout', () => this._ontimeout(socket, name))
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
let sockets = this._sockets.get(name)
|
|
64
|
+
if (sockets === undefined) {
|
|
65
|
+
sockets = new Set()
|
|
66
|
+
this._sockets.set(name, sockets)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
sockets.add(socket)
|
|
70
|
+
|
|
71
|
+
req.socket = socket
|
|
72
|
+
|
|
73
|
+
const connection = HTTPClientConnection.from(socket, opts)
|
|
74
|
+
|
|
75
|
+
connection.req = req
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
destroy () {
|
|
79
|
+
for (const set of [this._sockets, this._freeSockets]) {
|
|
80
|
+
for (const [, sockets] of set) {
|
|
81
|
+
for (const socket of sockets) socket.destroy()
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
_onfree (socket, name) {
|
|
87
|
+
if (this.keepSocketAlive(socket)) {
|
|
88
|
+
this._onremove(socket, name, false)
|
|
89
|
+
|
|
90
|
+
let sockets = this._freeSockets.get(name)
|
|
91
|
+
if (sockets === undefined) {
|
|
92
|
+
sockets = new Set()
|
|
93
|
+
this._freeSockets.set(name, sockets)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
sockets.add(socket)
|
|
97
|
+
} else {
|
|
98
|
+
socket.end()
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
_onremove (socket, name, all = true) {
|
|
103
|
+
for (const set of all ? [this._sockets, this._freeSockets] : [this._sockets]) {
|
|
104
|
+
const sockets = set.get(name)
|
|
105
|
+
if (sockets === undefined) continue
|
|
106
|
+
|
|
107
|
+
sockets.delete(socket)
|
|
108
|
+
if (sockets.size === 0) set.delete(name)
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
_ontimeout (socket, name) {
|
|
113
|
+
const sockets = this._freeSockets.get(name)
|
|
114
|
+
if (!sockets) return
|
|
115
|
+
|
|
116
|
+
if (sockets.delete(socket)) socket.destroy()
|
|
117
|
+
if (sockets.size === 0) this._freeSockets.delete(name)
|
|
31
118
|
}
|
|
32
119
|
|
|
33
120
|
static global = new this({ keepAlive: 1000, timeout: 5000 })
|
package/lib/client-connection.js
CHANGED
|
@@ -5,6 +5,16 @@ const errors = require('./errors')
|
|
|
5
5
|
const empty = Buffer.alloc(0)
|
|
6
6
|
|
|
7
7
|
module.exports = class HTTPClientConnection {
|
|
8
|
+
static _connections = new WeakMap()
|
|
9
|
+
|
|
10
|
+
static for (socket) {
|
|
11
|
+
return this._connections.get(socket) || null
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
static from (socket, opts) {
|
|
15
|
+
return this.for(socket) || new this(socket, opts)
|
|
16
|
+
}
|
|
17
|
+
|
|
8
18
|
constructor (socket, opts = {}) {
|
|
9
19
|
const {
|
|
10
20
|
IncomingMessage = HTTPIncomingMessage
|
|
@@ -21,6 +31,7 @@ module.exports = class HTTPClientConnection {
|
|
|
21
31
|
this._length = -1
|
|
22
32
|
this._read = 0
|
|
23
33
|
this._buffer = null
|
|
34
|
+
this._idle = true
|
|
24
35
|
|
|
25
36
|
this._onerror = this._onerror.bind(this)
|
|
26
37
|
this._onclose = this._onclose.bind(this)
|
|
@@ -36,6 +47,12 @@ module.exports = class HTTPClientConnection {
|
|
|
36
47
|
.on('data', this._ondata)
|
|
37
48
|
.on('drain', this._ondrain)
|
|
38
49
|
.on('timeout', this._ontimeout)
|
|
50
|
+
|
|
51
|
+
HTTPClientConnection._connections.set(socket, this)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
get idle () {
|
|
55
|
+
return this._idle
|
|
39
56
|
}
|
|
40
57
|
|
|
41
58
|
_onerror (err) {
|
|
@@ -51,6 +68,8 @@ module.exports = class HTTPClientConnection {
|
|
|
51
68
|
}
|
|
52
69
|
|
|
53
70
|
_ondata (data) {
|
|
71
|
+
this._idle = false
|
|
72
|
+
|
|
54
73
|
if (this._state === constants.state.IN_BODY) return this._onbody(data)
|
|
55
74
|
|
|
56
75
|
if (this._buffer !== null) {
|
|
@@ -181,13 +200,7 @@ module.exports = class HTTPClientConnection {
|
|
|
181
200
|
}
|
|
182
201
|
|
|
183
202
|
_onupgrade (head) {
|
|
184
|
-
this.
|
|
185
|
-
.off('error', this._onerror)
|
|
186
|
-
.off('close', this._onclose)
|
|
187
|
-
.off('end', this._onend)
|
|
188
|
-
.off('data', this._ondata)
|
|
189
|
-
.off('drain', this._ondrain)
|
|
190
|
-
.off('timeout', this._ontimeout)
|
|
203
|
+
this._ondetach()
|
|
191
204
|
|
|
192
205
|
const req = this.req
|
|
193
206
|
|
|
@@ -206,8 +219,6 @@ module.exports = class HTTPClientConnection {
|
|
|
206
219
|
_onfinished () {
|
|
207
220
|
if (this.res) this.res.push(null)
|
|
208
221
|
if (this.req) this.req._continueFinal()
|
|
209
|
-
|
|
210
|
-
this.socket.end()
|
|
211
222
|
}
|
|
212
223
|
|
|
213
224
|
_onreset () {
|
|
@@ -215,9 +226,24 @@ module.exports = class HTTPClientConnection {
|
|
|
215
226
|
this._length = -1
|
|
216
227
|
this._read = 0
|
|
217
228
|
this._buffer = null
|
|
229
|
+
this._idle = true
|
|
230
|
+
|
|
231
|
+
this.socket.emit('free')
|
|
218
232
|
}
|
|
219
233
|
|
|
220
234
|
_ondrain () {
|
|
221
235
|
if (this.req) this.req._continueWrite()
|
|
222
236
|
}
|
|
237
|
+
|
|
238
|
+
_ondetach () {
|
|
239
|
+
this.socket
|
|
240
|
+
.off('error', this._onerror)
|
|
241
|
+
.off('close', this._onclose)
|
|
242
|
+
.off('end', this._onend)
|
|
243
|
+
.off('data', this._ondata)
|
|
244
|
+
.off('drain', this._ondrain)
|
|
245
|
+
.off('timeout', this._ontimeout)
|
|
246
|
+
|
|
247
|
+
HTTPClientConnection._connections.delete(this.socket)
|
|
248
|
+
}
|
|
223
249
|
}
|
package/lib/client-request.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
const HTTPAgent = require('./agent')
|
|
2
2
|
const HTTPOutgoingMessage = require('./outgoing-message')
|
|
3
|
-
const HTTPClientConnection = require('./client-connection')
|
|
4
3
|
|
|
5
4
|
module.exports = class HTTPClientRequest extends HTTPOutgoingMessage {
|
|
6
5
|
constructor (opts = {}, onresponse = null) {
|
|
@@ -17,19 +16,14 @@ module.exports = class HTTPClientRequest extends HTTPOutgoingMessage {
|
|
|
17
16
|
const host = opts.host = opts.host || 'localhost'
|
|
18
17
|
const port = opts.port = opts.port || 80
|
|
19
18
|
|
|
20
|
-
|
|
21
|
-
connection = new HTTPClientConnection(agent.createConnection(opts), opts)
|
|
22
|
-
} = opts
|
|
19
|
+
super()
|
|
23
20
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
connection.req = this
|
|
21
|
+
agent.addRequest(this, opts)
|
|
27
22
|
|
|
28
23
|
this.method = method
|
|
29
24
|
this.path = path
|
|
30
25
|
this.headers = { host: host + ':' + port, ...opts.headers }
|
|
31
26
|
|
|
32
|
-
this._connection = connection
|
|
33
27
|
this._chunked = method !== 'GET' && method !== 'HEAD'
|
|
34
28
|
|
|
35
29
|
this._pendingFinal = null
|
package/lib/server-connection.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const tcp = require('bare-tcp')
|
|
1
2
|
const HTTPIncomingMessage = require('./incoming-message')
|
|
2
3
|
const HTTPServerResponse = require('./server-response')
|
|
3
4
|
const constants = require('./constants')
|
|
@@ -5,6 +6,12 @@ const constants = require('./constants')
|
|
|
5
6
|
const empty = Buffer.alloc(0)
|
|
6
7
|
|
|
7
8
|
module.exports = class HTTPServerConnection {
|
|
9
|
+
static _connections = new WeakMap()
|
|
10
|
+
|
|
11
|
+
static for (socket) {
|
|
12
|
+
return this._connections.get(socket) || null
|
|
13
|
+
}
|
|
14
|
+
|
|
8
15
|
constructor (server, socket, opts = {}) {
|
|
9
16
|
const {
|
|
10
17
|
IncomingMessage = HTTPIncomingMessage,
|
|
@@ -24,6 +31,7 @@ module.exports = class HTTPServerConnection {
|
|
|
24
31
|
this._length = -1
|
|
25
32
|
this._read = 0
|
|
26
33
|
this._buffer = null
|
|
34
|
+
this._idle = true
|
|
27
35
|
|
|
28
36
|
this._onerror = this._onerror.bind(this)
|
|
29
37
|
this._ondata = this._ondata.bind(this)
|
|
@@ -36,14 +44,22 @@ module.exports = class HTTPServerConnection {
|
|
|
36
44
|
.on('drain', this._ondrain)
|
|
37
45
|
.on('timeout', this._ontimeout)
|
|
38
46
|
|
|
47
|
+
HTTPServerConnection._connections.set(socket, this)
|
|
48
|
+
|
|
39
49
|
if (this.server.timeout) socket.setTimeout(this.server.timeout)
|
|
40
50
|
}
|
|
41
51
|
|
|
52
|
+
get idle () {
|
|
53
|
+
return this._idle
|
|
54
|
+
}
|
|
55
|
+
|
|
42
56
|
_onerror (err) {
|
|
43
57
|
this.socket.destroy(err)
|
|
44
58
|
}
|
|
45
59
|
|
|
46
60
|
_ondata (data) {
|
|
61
|
+
this._idle = false
|
|
62
|
+
|
|
47
63
|
if (this._state === constants.state.IN_BODY) return this._onbody(data)
|
|
48
64
|
|
|
49
65
|
if (this._buffer !== null) {
|
|
@@ -176,11 +192,7 @@ module.exports = class HTTPServerConnection {
|
|
|
176
192
|
}
|
|
177
193
|
|
|
178
194
|
_onupgrade (head) {
|
|
179
|
-
this.
|
|
180
|
-
.off('error', this._onerror)
|
|
181
|
-
.off('data', this._ondata)
|
|
182
|
-
.off('drain', this._ondrain)
|
|
183
|
-
.off('timeout', this._ontimeout)
|
|
195
|
+
this._ondetach()
|
|
184
196
|
|
|
185
197
|
const req = this.req
|
|
186
198
|
|
|
@@ -207,9 +219,24 @@ module.exports = class HTTPServerConnection {
|
|
|
207
219
|
this._length = -1
|
|
208
220
|
this._read = 0
|
|
209
221
|
this._buffer = null
|
|
222
|
+
this._idle = true
|
|
223
|
+
|
|
224
|
+
if (this.server._state & tcp.constants.state.CLOSING) {
|
|
225
|
+
this.socket.destroy()
|
|
226
|
+
}
|
|
210
227
|
}
|
|
211
228
|
|
|
212
229
|
_ondrain () {
|
|
213
230
|
if (this.res) this.res._continueWrite()
|
|
214
231
|
}
|
|
232
|
+
|
|
233
|
+
_ondetach () {
|
|
234
|
+
this.socket
|
|
235
|
+
.off('error', this._onerror)
|
|
236
|
+
.off('data', this._ondata)
|
|
237
|
+
.off('drain', this._ondrain)
|
|
238
|
+
.off('timeout', this._ontimeout)
|
|
239
|
+
|
|
240
|
+
HTTPServerConnection._connections.delete(this.socket)
|
|
241
|
+
}
|
|
215
242
|
}
|
package/lib/server.js
CHANGED
|
@@ -28,4 +28,16 @@ module.exports = class HTTPServer extends TCPServer {
|
|
|
28
28
|
|
|
29
29
|
return this
|
|
30
30
|
}
|
|
31
|
+
|
|
32
|
+
close (onclose) {
|
|
33
|
+
super.close(onclose)
|
|
34
|
+
|
|
35
|
+
for (const socket of this._connections) {
|
|
36
|
+
const connection = HTTPServerConnection.for(socket)
|
|
37
|
+
|
|
38
|
+
if (connection && connection.idle) {
|
|
39
|
+
socket.destroy()
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
31
43
|
}
|