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 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 (URL.isURL(url)) {
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
- this._maxSockets = maxSockets
16
- this._maxTotalSockets = maxTotalSockets
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 })
@@ -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.socket
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
  }
@@ -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
- const {
21
- connection = new HTTPClientConnection(agent.createConnection(opts), opts)
22
- } = opts
19
+ super()
23
20
 
24
- super(connection.socket)
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
@@ -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.socket
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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bare-http1",
3
- "version": "3.7.0",
3
+ "version": "3.8.1",
4
4
  "description": "Native HTTP/1 library for JavaScript",
5
5
  "exports": {
6
6
  ".": "./index.js",