bare-http1 4.2.4 → 4.3.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.
Files changed (3) hide show
  1. package/index.d.ts +4 -1
  2. package/lib/agent.js +108 -52
  3. package/package.json +1 -1
package/index.d.ts CHANGED
@@ -99,6 +99,9 @@ export interface HTTPAgentOptions {
99
99
  }
100
100
 
101
101
  export interface HTTPAgent {
102
+ readonly sockets: IterableIterator<TCPSocket>
103
+ readonly freeSockets: IterableIterator<TCPSocket>
104
+
102
105
  createConnection(opts?: TCPSocketOptions & TCPSocketConnectOptions): TCPSocket
103
106
 
104
107
  reuseSocket(socket: TCPSocket, req?: HTTPClientRequest): void
@@ -115,7 +118,7 @@ export interface HTTPAgent {
115
118
  class HTTPAgent {
116
119
  constructor(opts?: HTTPAgentOptions & TCPSocketOptions & TCPSocketConnectOptions)
117
120
 
118
- static global: HTTPAgent
121
+ static readonly global: HTTPAgent
119
122
  }
120
123
 
121
124
  export const globalAgent: HTTPAgent
package/lib/agent.js CHANGED
@@ -2,20 +2,88 @@ const EventEmitter = require('bare-events')
2
2
  const tcp = require('bare-tcp')
3
3
  const HTTPClientConnection = require('./client-connection')
4
4
 
5
- module.exports = class HTTPAgent extends EventEmitter {
5
+ class HTTPSocketSet {
6
+ constructor() {
7
+ this._sockets = new Map()
8
+ this._size = 0
9
+ }
10
+
11
+ get size() {
12
+ return this._size
13
+ }
14
+
15
+ add(name, socket) {
16
+ const sockets = this._sockets.get(name)
17
+
18
+ this._size++
19
+
20
+ if (sockets === undefined) this._sockets.set(name, [socket])
21
+ else sockets.push(socket)
22
+ }
23
+
24
+ pop(name) {
25
+ const sockets = this._sockets.get(name)
26
+ if (sockets === undefined || sockets.length === 0) return null
27
+
28
+ this._size--
29
+
30
+ const last = sockets.pop()
31
+
32
+ if (sockets.length === 0) this._sockets.delete(name)
33
+
34
+ return last
35
+ }
36
+
37
+ delete(name, socket) {
38
+ const sockets = this._sockets.get(name)
39
+ if (sockets === undefined) return
40
+
41
+ const i = sockets.indexOf(socket)
42
+ if (i === -1) return
43
+
44
+ this._size--
45
+
46
+ const last = sockets.pop()
47
+ if (last !== socket) sockets[i] = last
48
+
49
+ if (sockets.length === 0) this._sockets.delete(name)
50
+ }
51
+
52
+ *sockets() {
53
+ for (const sockets of this._sockets.values()) {
54
+ yield* sockets
55
+ }
56
+ }
57
+
58
+ *[Symbol.iterator]() {
59
+ for (const [name, sockets] of this._sockets) {
60
+ for (const socket of sockets) yield [name, socket]
61
+ }
62
+ }
63
+ }
64
+
65
+ class HTTPAgent extends EventEmitter {
6
66
  constructor(opts = {}) {
7
67
  super()
8
68
 
9
69
  const { keepAlive = false, keepAliveMsecs = 1000 } = opts
10
70
 
11
- this._sockets = new Map()
12
- this._freeSockets = new Map()
71
+ this._sockets = new HTTPSocketSet()
72
+ this._freeSockets = new HTTPSocketSet()
13
73
 
14
74
  this._keepAlive = typeof keepAlive === 'number' ? keepAlive : keepAlive ? keepAliveMsecs : -1
15
75
 
16
76
  this._opts = { ...opts }
17
77
  }
18
78
 
79
+ get sockets() {
80
+ return this._sockets.sockets()
81
+ }
82
+
83
+ get freeSockets() {
84
+ return this._freeSockets.sockets()
85
+ }
86
+
19
87
  createConnection(opts) {
20
88
  return tcp.createConnection(opts)
21
89
  }
@@ -42,33 +110,26 @@ module.exports = class HTTPAgent extends EventEmitter {
42
110
 
43
111
  const name = this.getName(opts)
44
112
 
45
- let socket
46
-
47
- if (this._freeSockets.has(name)) {
48
- const sockets = this._freeSockets.get(name)
113
+ let socket = this._freeSockets.pop(name)
49
114
 
50
- socket = sockets.pop()
51
-
52
- if (sockets.length === 0) this._freeSockets.delete(name)
53
-
54
- this.reuseSocket(socket, req)
55
- } else {
115
+ if (socket) this.reuseSocket(socket, req)
116
+ else {
56
117
  const agent = this
57
118
 
58
119
  socket = this.createConnection(opts)
59
120
 
60
- socket.on('free', onfree).on('end', onremove).on('finish', onremove).on('timeout', ontimeout)
121
+ socket
122
+ .on('free', onfree)
123
+ .on('timeout', ontimeout)
124
+ .on('end', onremove)
125
+ .on('finish', onremove)
126
+ .on('close', onremove)
61
127
 
62
128
  function onfree() {
63
129
  if (socket.destroyed) return
64
130
 
65
131
  if (agent.keepSocketAlive(socket)) {
66
- onremove(false)
67
-
68
- const sockets = agent._freeSockets.get(name)
69
-
70
- if (sockets === undefined) agent._freeSockets.set(name, [socket])
71
- else sockets.push(socket)
132
+ agent._freeSockets.add(name, socket)
72
133
  } else {
73
134
  socket.end()
74
135
  }
@@ -76,41 +137,25 @@ module.exports = class HTTPAgent extends EventEmitter {
76
137
  agent.emit('free', socket)
77
138
  }
78
139
 
79
- function onremove(destroy = true) {
80
- if (destroy) socket.off('free', onfree)
81
-
82
- for (const set of destroy ? [agent._sockets, agent._freeSockets] : [agent._sockets]) {
83
- const sockets = set.get(name)
84
- if (sockets === undefined) continue
85
-
86
- const i = sockets.indexOf(socket)
87
- if (i === -1) continue
88
-
89
- const last = sockets.pop()
90
- if (last !== socket) sockets[i] = last
140
+ function ontimeout() {
141
+ socket.destroy()
91
142
 
92
- if (sockets.length === 0) set.delete(name)
93
- }
143
+ agent._freeSockets.delete(name, socket)
94
144
  }
95
145
 
96
- function ontimeout() {
97
- const sockets = agent._freeSockets.get(name)
98
- if (sockets === undefined) return
99
-
100
- const i = sockets.indexOf(socket)
101
- if (i === -1) return
146
+ function onremove() {
147
+ socket.off('free', onfree)
102
148
 
103
- const last = sockets.pop()
104
- if (last !== socket) sockets[i] = last
149
+ agent._sockets.delete(name, socket)
150
+ agent._freeSockets.delete(name, socket)
105
151
 
106
- if (sockets.length === 0) agent._freeSockets.delete(name)
152
+ if (agent._sockets.size === 0) HTTPAgent._agents.delete(agent)
107
153
  }
108
154
  }
109
155
 
110
- const sockets = this._sockets.get(name)
156
+ if (this._sockets.size === 0) HTTPAgent._agents.add(this)
111
157
 
112
- if (sockets === undefined) this._sockets.set(name, [socket])
113
- else sockets.push(socket)
158
+ this._sockets.add(name, socket)
114
159
 
115
160
  req.socket = socket
116
161
 
@@ -120,12 +165,23 @@ module.exports = class HTTPAgent extends EventEmitter {
120
165
  }
121
166
 
122
167
  destroy() {
123
- for (const set of [this._sockets, this._freeSockets]) {
124
- for (const [, sockets] of set) {
125
- for (const socket of sockets) socket.destroy()
126
- }
127
- }
168
+ for (const socket of this._sockets.sockets()) socket.destroy()
128
169
  }
129
170
 
130
- static global = new this({ keepAlive: 1000, timeout: 5000 })
171
+ static _global = new this({ keepAlive: 1000, timeout: 5000 })
172
+ static _agents = new Set()
173
+
174
+ static get global() {
175
+ return this._global
176
+ }
177
+
178
+ static _onidle() {
179
+ for (const agent of this._agents) {
180
+ for (const socket of agent._sockets.sockets()) socket.destroy()
181
+ }
182
+ }
131
183
  }
184
+
185
+ module.exports = HTTPAgent
186
+
187
+ Bare.on('idle', HTTPAgent._onidle.bind(HTTPAgent))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bare-http1",
3
- "version": "4.2.4",
3
+ "version": "4.3.1",
4
4
  "description": "Native HTTP/1 library for JavaScript",
5
5
  "exports": {
6
6
  "./package": "./package.json",