bare-http1 4.3.1 → 4.5.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.d.ts +4 -0
- package/lib/agent.js +49 -16
- package/lib/client-connection.js +50 -29
- package/lib/client-request.js +42 -17
- package/lib/errors.d.ts +1 -4
- package/lib/errors.js +4 -0
- package/lib/incoming-message.js +40 -12
- package/lib/outgoing-message.js +29 -13
- package/lib/server-connection.js +60 -37
- package/lib/server-response.js +35 -21
- package/package.json +2 -2
package/index.d.ts
CHANGED
|
@@ -99,6 +99,8 @@ export interface HTTPAgentOptions {
|
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
export interface HTTPAgent {
|
|
102
|
+
readonly suspended: boolean
|
|
103
|
+
readonly resumed: Promise<void> | null
|
|
102
104
|
readonly sockets: IterableIterator<TCPSocket>
|
|
103
105
|
readonly freeSockets: IterableIterator<TCPSocket>
|
|
104
106
|
|
|
@@ -112,6 +114,8 @@ export interface HTTPAgent {
|
|
|
112
114
|
|
|
113
115
|
addRequest(req: HTTPClientRequest, opts: TCPSocketOptions & TCPSocketConnectOptions): void
|
|
114
116
|
|
|
117
|
+
suspend(): void
|
|
118
|
+
resume(): void
|
|
115
119
|
destroy(): void
|
|
116
120
|
}
|
|
117
121
|
|
package/lib/agent.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const EventEmitter = require('bare-events')
|
|
2
2
|
const tcp = require('bare-tcp')
|
|
3
3
|
const HTTPClientConnection = require('./client-connection')
|
|
4
|
+
const errors = require('./errors')
|
|
4
5
|
|
|
5
6
|
class HTTPSocketSet {
|
|
6
7
|
constructor() {
|
|
@@ -66,16 +67,28 @@ class HTTPAgent extends EventEmitter {
|
|
|
66
67
|
constructor(opts = {}) {
|
|
67
68
|
super()
|
|
68
69
|
|
|
69
|
-
const { keepAlive = false, keepAliveMsecs = 1000 } = opts
|
|
70
|
+
const { keepAlive = false, keepAliveMsecs = 1000, defaultPort = 80 } = opts
|
|
71
|
+
|
|
72
|
+
this._suspended = false
|
|
73
|
+
this._resuming = null
|
|
70
74
|
|
|
71
75
|
this._sockets = new HTTPSocketSet()
|
|
72
76
|
this._freeSockets = new HTTPSocketSet()
|
|
73
77
|
|
|
74
78
|
this._keepAlive = typeof keepAlive === 'number' ? keepAlive : keepAlive ? keepAliveMsecs : -1
|
|
79
|
+
this._defaultPort = defaultPort
|
|
75
80
|
|
|
76
81
|
this._opts = { ...opts }
|
|
77
82
|
}
|
|
78
83
|
|
|
84
|
+
get suspended() {
|
|
85
|
+
return this._suspended
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
get resumed() {
|
|
89
|
+
return this._resuming ? this._resuming.promise : null
|
|
90
|
+
}
|
|
91
|
+
|
|
79
92
|
get sockets() {
|
|
80
93
|
return this._sockets.sockets()
|
|
81
94
|
}
|
|
@@ -84,7 +97,13 @@ class HTTPAgent extends EventEmitter {
|
|
|
84
97
|
return this._freeSockets.sockets()
|
|
85
98
|
}
|
|
86
99
|
|
|
100
|
+
get defaultPort() {
|
|
101
|
+
return this._defaultPort
|
|
102
|
+
}
|
|
103
|
+
|
|
87
104
|
createConnection(opts) {
|
|
105
|
+
if (this._suspended) throw errors.AGENT_SUSPENDED()
|
|
106
|
+
|
|
88
107
|
return tcp.createConnection(opts)
|
|
89
108
|
}
|
|
90
109
|
|
|
@@ -120,10 +139,10 @@ class HTTPAgent extends EventEmitter {
|
|
|
120
139
|
|
|
121
140
|
socket
|
|
122
141
|
.on('free', onfree)
|
|
123
|
-
.on('timeout', ontimeout)
|
|
124
142
|
.on('end', onremove)
|
|
125
143
|
.on('finish', onremove)
|
|
126
144
|
.on('close', onremove)
|
|
145
|
+
.on('timeout', ontimeout)
|
|
127
146
|
|
|
128
147
|
function onfree() {
|
|
129
148
|
if (socket.destroyed) return
|
|
@@ -137,12 +156,6 @@ class HTTPAgent extends EventEmitter {
|
|
|
137
156
|
agent.emit('free', socket)
|
|
138
157
|
}
|
|
139
158
|
|
|
140
|
-
function ontimeout() {
|
|
141
|
-
socket.destroy()
|
|
142
|
-
|
|
143
|
-
agent._freeSockets.delete(name, socket)
|
|
144
|
-
}
|
|
145
|
-
|
|
146
159
|
function onremove() {
|
|
147
160
|
socket.off('free', onfree)
|
|
148
161
|
|
|
@@ -151,37 +164,57 @@ class HTTPAgent extends EventEmitter {
|
|
|
151
164
|
|
|
152
165
|
if (agent._sockets.size === 0) HTTPAgent._agents.delete(agent)
|
|
153
166
|
}
|
|
167
|
+
|
|
168
|
+
function ontimeout() {
|
|
169
|
+
socket.destroy()
|
|
170
|
+
|
|
171
|
+
agent._freeSockets.delete(name, socket)
|
|
172
|
+
}
|
|
154
173
|
}
|
|
155
174
|
|
|
156
175
|
if (this._sockets.size === 0) HTTPAgent._agents.add(this)
|
|
157
176
|
|
|
158
177
|
this._sockets.add(name, socket)
|
|
159
178
|
|
|
160
|
-
req.
|
|
179
|
+
req._socket = socket
|
|
161
180
|
|
|
162
181
|
const connection = HTTPClientConnection.from(socket, opts)
|
|
163
182
|
|
|
164
|
-
connection.
|
|
183
|
+
connection._req = req
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
suspend() {
|
|
187
|
+
if (this._suspended) return
|
|
188
|
+
|
|
189
|
+
this._resuming = Promise.withResolvers()
|
|
190
|
+
this._suspended = true
|
|
191
|
+
|
|
192
|
+
this.destroy()
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
resume() {
|
|
196
|
+
if (this._resuming === null) return
|
|
197
|
+
|
|
198
|
+
this._resuming.resolve()
|
|
199
|
+
this._resuming = null
|
|
200
|
+
this._suspended = false
|
|
165
201
|
}
|
|
166
202
|
|
|
167
203
|
destroy() {
|
|
168
204
|
for (const socket of this._sockets.sockets()) socket.destroy()
|
|
169
205
|
}
|
|
170
206
|
|
|
171
|
-
static _global = new this({ keepAlive: 1000, timeout: 5000 })
|
|
172
207
|
static _agents = new Set()
|
|
173
208
|
|
|
174
|
-
static get global() {
|
|
175
|
-
return this._global
|
|
176
|
-
}
|
|
177
|
-
|
|
178
209
|
static _onidle() {
|
|
179
210
|
for (const agent of this._agents) {
|
|
180
|
-
|
|
211
|
+
agent.destroy()
|
|
181
212
|
}
|
|
182
213
|
}
|
|
183
214
|
}
|
|
184
215
|
|
|
216
|
+
HTTPAgent.global = new HTTPAgent({ keepAlive: 1000, timeout: 5000 })
|
|
217
|
+
|
|
185
218
|
module.exports = HTTPAgent
|
|
186
219
|
|
|
187
220
|
Bare.on('idle', HTTPAgent._onidle.bind(HTTPAgent))
|
package/lib/client-connection.js
CHANGED
|
@@ -22,10 +22,10 @@ module.exports = class HTTPClientConnection {
|
|
|
22
22
|
constructor(socket, opts = {}) {
|
|
23
23
|
const { IncomingMessage = HTTPIncomingMessage } = opts
|
|
24
24
|
|
|
25
|
-
this.
|
|
25
|
+
this._socket = socket
|
|
26
26
|
|
|
27
|
-
this.
|
|
28
|
-
this.
|
|
27
|
+
this._req = null
|
|
28
|
+
this._res = null
|
|
29
29
|
|
|
30
30
|
this._IncomingMessage = IncomingMessage
|
|
31
31
|
|
|
@@ -50,20 +50,32 @@ module.exports = class HTTPClientConnection {
|
|
|
50
50
|
HTTPClientConnection._connections.set(socket, this)
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
+
get socket() {
|
|
54
|
+
return this._socket
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
get req() {
|
|
58
|
+
return this._req
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
get res() {
|
|
62
|
+
return this._res
|
|
63
|
+
}
|
|
64
|
+
|
|
53
65
|
get idle() {
|
|
54
66
|
return this._idle
|
|
55
67
|
}
|
|
56
68
|
|
|
57
69
|
_onerror(err) {
|
|
58
|
-
if (this.
|
|
70
|
+
if (this._req) this._req.destroy(err)
|
|
59
71
|
}
|
|
60
72
|
|
|
61
73
|
_onclose() {
|
|
62
|
-
if (this.
|
|
74
|
+
if (this._req) this._req.destroy()
|
|
63
75
|
}
|
|
64
76
|
|
|
65
77
|
_onend() {
|
|
66
|
-
if (this.
|
|
78
|
+
if (this._req) this._req.destroy(errors.CONNECTION_LOST())
|
|
67
79
|
}
|
|
68
80
|
|
|
69
81
|
_ondata(data) {
|
|
@@ -73,68 +85,77 @@ module.exports = class HTTPClientConnection {
|
|
|
73
85
|
for (const op of this._parser.push(data)) {
|
|
74
86
|
switch (op.type) {
|
|
75
87
|
case RESPONSE:
|
|
76
|
-
this.
|
|
77
|
-
this.
|
|
88
|
+
this._req.on('close', () => {
|
|
89
|
+
this._req = null
|
|
78
90
|
})
|
|
79
91
|
|
|
80
|
-
this.
|
|
92
|
+
this._res = new this._IncomingMessage(this._socket, op.headers, {
|
|
81
93
|
statusCode: op.code,
|
|
82
94
|
statusMessage: op.reason
|
|
83
95
|
})
|
|
84
96
|
|
|
85
|
-
this.
|
|
86
|
-
this.
|
|
87
|
-
|
|
97
|
+
this._res.on('close', () => {
|
|
98
|
+
this._res = null
|
|
88
99
|
this._idle = true
|
|
89
100
|
|
|
90
|
-
this.
|
|
101
|
+
this._socket.emit('free')
|
|
91
102
|
})
|
|
92
103
|
|
|
93
104
|
if (op.headers.connection && op.headers.connection.toLowerCase() === 'upgrade') {
|
|
94
105
|
return this._onupgrade(this._parser.end())
|
|
95
106
|
}
|
|
96
107
|
|
|
97
|
-
this.
|
|
108
|
+
this._req.emit('response', this._res)
|
|
98
109
|
break
|
|
99
110
|
|
|
100
111
|
case DATA:
|
|
101
|
-
this.
|
|
112
|
+
this._res.push(op.data)
|
|
102
113
|
break
|
|
103
114
|
|
|
104
115
|
case END:
|
|
105
|
-
if (this.
|
|
106
|
-
|
|
116
|
+
if (this._res) {
|
|
117
|
+
this._res._socket = null
|
|
118
|
+
this._res.push(null)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (this._req) {
|
|
122
|
+
this._req._socket = null
|
|
123
|
+
this._req.destroy()
|
|
124
|
+
}
|
|
107
125
|
break
|
|
108
126
|
}
|
|
109
127
|
}
|
|
110
128
|
} catch (err) {
|
|
111
|
-
this.
|
|
129
|
+
this._socket.destroy(err)
|
|
112
130
|
}
|
|
113
131
|
}
|
|
114
132
|
|
|
115
133
|
_onupgrade(data) {
|
|
116
|
-
this.
|
|
134
|
+
this._detach()
|
|
117
135
|
|
|
118
|
-
const
|
|
136
|
+
const res = this._res
|
|
137
|
+
const req = this._req
|
|
119
138
|
|
|
120
|
-
req.
|
|
121
|
-
|
|
139
|
+
res._upgrade = req._upgrade = true
|
|
140
|
+
|
|
141
|
+
const upgraded = req.emit('upgrade', res, this._socket, data || EMPTY)
|
|
122
142
|
|
|
123
|
-
|
|
143
|
+
res.push(null)
|
|
144
|
+
req.destroy()
|
|
124
145
|
|
|
125
|
-
this.
|
|
146
|
+
if (!upgraded) this._socket.destroy()
|
|
126
147
|
}
|
|
127
148
|
|
|
128
149
|
_ontimeout() {
|
|
129
|
-
if (this.
|
|
150
|
+
if (this._req) this._req.emit('timeout')
|
|
130
151
|
}
|
|
131
152
|
|
|
132
153
|
_ondrain() {
|
|
133
|
-
if (this.
|
|
154
|
+
if (this._req) this._req._continueWrite()
|
|
134
155
|
}
|
|
135
156
|
|
|
136
|
-
|
|
137
|
-
this.
|
|
157
|
+
_detach() {
|
|
158
|
+
this._socket
|
|
138
159
|
.off('error', this._onerror)
|
|
139
160
|
.off('close', this._onclose)
|
|
140
161
|
.off('end', this._onend)
|
|
@@ -142,6 +163,6 @@ module.exports = class HTTPClientConnection {
|
|
|
142
163
|
.off('drain', this._ondrain)
|
|
143
164
|
.off('timeout', this._ontimeout)
|
|
144
165
|
|
|
145
|
-
HTTPClientConnection._connections.delete(this.
|
|
166
|
+
HTTPClientConnection._connections.delete(this._socket)
|
|
146
167
|
}
|
|
147
168
|
}
|
package/lib/client-request.js
CHANGED
|
@@ -16,38 +16,48 @@ module.exports = class HTTPClientRequest extends HTTPOutgoingMessage {
|
|
|
16
16
|
const agent = opts.agent === false ? new HTTPAgent() : opts.agent || HTTPAgent.global
|
|
17
17
|
const method = opts.method || 'GET'
|
|
18
18
|
const path = opts.path || '/'
|
|
19
|
+
const defaultPort = opts.defaultPort || (agent && agent.defaultPort) || 80
|
|
19
20
|
const host = (opts.host = opts.host || 'localhost')
|
|
20
|
-
const port = (opts.port = opts.port ||
|
|
21
|
-
const headers = { host: host
|
|
21
|
+
const port = (opts.port = opts.port || defaultPort)
|
|
22
|
+
const headers = { host: hostHeader(host, port, defaultPort), ...opts.headers }
|
|
22
23
|
|
|
23
24
|
super()
|
|
24
25
|
|
|
25
26
|
agent.addRequest(this, opts)
|
|
26
27
|
|
|
27
|
-
this.
|
|
28
|
-
this.
|
|
29
|
-
this.
|
|
28
|
+
this._headers = headers
|
|
29
|
+
this._method = method
|
|
30
|
+
this._path = path
|
|
30
31
|
|
|
31
32
|
this._chunked = method !== 'GET' && method !== 'HEAD'
|
|
32
33
|
|
|
34
|
+
this._pendingWrite = null
|
|
33
35
|
this._pendingFinal = null
|
|
34
36
|
|
|
35
37
|
if (onresponse) this.once('response', onresponse)
|
|
36
38
|
}
|
|
37
39
|
|
|
40
|
+
get method() {
|
|
41
|
+
return this._method
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
get path() {
|
|
45
|
+
return this._path
|
|
46
|
+
}
|
|
47
|
+
|
|
38
48
|
// For Node.js compatibility
|
|
39
49
|
abort() {
|
|
40
50
|
return this.destroy()
|
|
41
51
|
}
|
|
42
52
|
|
|
43
53
|
_header() {
|
|
44
|
-
let h = `${this.
|
|
54
|
+
let h = `${this._method} ${this._path} HTTP/1.1\r\n`
|
|
45
55
|
|
|
46
56
|
let upgrade = false
|
|
47
57
|
|
|
48
|
-
for (const name of Object.keys(this.
|
|
58
|
+
for (const name of Object.keys(this._headers)) {
|
|
49
59
|
const n = name.toLowerCase()
|
|
50
|
-
const v = this.
|
|
60
|
+
const v = this._headers[name]
|
|
51
61
|
|
|
52
62
|
if (n === 'content-length') this._chunked = false
|
|
53
63
|
if (n === 'connection' && v && v.toLowerCase() === 'upgrade') upgrade = true
|
|
@@ -65,33 +75,34 @@ module.exports = class HTTPClientRequest extends HTTPOutgoingMessage {
|
|
|
65
75
|
}
|
|
66
76
|
|
|
67
77
|
_write(data, encoding, cb) {
|
|
68
|
-
if (this.
|
|
78
|
+
if (this._headersSent === false) this.flushHeaders()
|
|
69
79
|
|
|
70
80
|
if (this._chunked) {
|
|
71
|
-
this.
|
|
72
|
-
this.
|
|
81
|
+
this._socket.write(Buffer.from(data.byteLength.toString(16)))
|
|
82
|
+
this._socket.write(CHUNK_DELIMITER)
|
|
73
83
|
}
|
|
74
84
|
|
|
75
|
-
let flushed = this.
|
|
85
|
+
let flushed = this._socket.write(data)
|
|
76
86
|
|
|
77
|
-
if (this._chunked) flushed = this.
|
|
87
|
+
if (this._chunked) flushed = this._socket.write(CHUNK_DELIMITER)
|
|
78
88
|
|
|
79
89
|
if (flushed) cb(null)
|
|
80
90
|
else this._pendingWrite = cb
|
|
81
91
|
}
|
|
82
92
|
|
|
83
93
|
_final(cb) {
|
|
84
|
-
if (this.
|
|
94
|
+
if (this._headersSent === false) this.flushHeaders()
|
|
85
95
|
|
|
86
|
-
if (this._chunked) this.
|
|
96
|
+
if (this._chunked) this._socket.write(CHUNK_TERMINATOR)
|
|
87
97
|
|
|
88
98
|
this._pendingFinal = cb
|
|
89
99
|
}
|
|
90
100
|
|
|
91
101
|
_predestroy() {
|
|
92
|
-
|
|
102
|
+
super._predestroy()
|
|
93
103
|
|
|
94
|
-
this.
|
|
104
|
+
this._continueWrite()
|
|
105
|
+
this._continueFinal()
|
|
95
106
|
}
|
|
96
107
|
|
|
97
108
|
_continueWrite() {
|
|
@@ -116,3 +127,17 @@ function httpCase(n) {
|
|
|
116
127
|
}
|
|
117
128
|
return s
|
|
118
129
|
}
|
|
130
|
+
|
|
131
|
+
function hostHeader(host, port, defaultPort) {
|
|
132
|
+
const i = host.indexOf(':')
|
|
133
|
+
|
|
134
|
+
if (i !== -1 && host.includes(':', i + 1) && host.charCodeAt(0) !== 91 /* [ */) {
|
|
135
|
+
host = `[${host}]`
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (port && +port !== defaultPort) {
|
|
139
|
+
host += ':' + port
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return host
|
|
143
|
+
}
|
package/lib/errors.d.ts
CHANGED
package/lib/errors.js
CHANGED
|
@@ -19,4 +19,8 @@ module.exports = class HTTPError extends Error {
|
|
|
19
19
|
static CONNECTION_LOST(msg = 'Socket hung up') {
|
|
20
20
|
return new HTTPError(msg, HTTPError.CONNECTION_LOST)
|
|
21
21
|
}
|
|
22
|
+
|
|
23
|
+
static AGENT_SUSPENDED(msg = 'Agent is suspended') {
|
|
24
|
+
return new HTTPError(msg, HTTPError.AGENT_SUSPENDED)
|
|
25
|
+
}
|
|
22
26
|
}
|
package/lib/incoming-message.js
CHANGED
|
@@ -4,17 +4,45 @@ module.exports = class HTTPIncomingMessage extends Readable {
|
|
|
4
4
|
constructor(socket = null, headers = {}, opts = {}) {
|
|
5
5
|
super()
|
|
6
6
|
|
|
7
|
-
this.
|
|
8
|
-
this.
|
|
9
|
-
this.
|
|
7
|
+
this._socket = socket
|
|
8
|
+
this._headers = headers
|
|
9
|
+
this._upgrade = false
|
|
10
10
|
|
|
11
11
|
// Server options
|
|
12
|
-
this.
|
|
13
|
-
this.
|
|
12
|
+
this._method = opts.method || ''
|
|
13
|
+
this._url = opts.url || ''
|
|
14
14
|
|
|
15
15
|
// Client options
|
|
16
|
-
this.
|
|
17
|
-
this.
|
|
16
|
+
this._statusCode = opts.statusCode || 0
|
|
17
|
+
this._statusMessage = opts.statusMessage || ''
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
get socket() {
|
|
21
|
+
return this._socket
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
get headers() {
|
|
25
|
+
return this._headers
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
get upgrade() {
|
|
29
|
+
return this._upgrade
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
get method() {
|
|
33
|
+
return this._method
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
get url() {
|
|
37
|
+
return this._url
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
get statusCode() {
|
|
41
|
+
return this._statusCode
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
get statusMessage() {
|
|
45
|
+
return this._statusMessage
|
|
18
46
|
}
|
|
19
47
|
|
|
20
48
|
get httpVersion() {
|
|
@@ -22,26 +50,26 @@ module.exports = class HTTPIncomingMessage extends Readable {
|
|
|
22
50
|
}
|
|
23
51
|
|
|
24
52
|
getHeader(name) {
|
|
25
|
-
return this.
|
|
53
|
+
return this._headers[name.toLowerCase()]
|
|
26
54
|
}
|
|
27
55
|
|
|
28
56
|
getHeaders() {
|
|
29
|
-
return { ...this.
|
|
57
|
+
return { ...this._headers }
|
|
30
58
|
}
|
|
31
59
|
|
|
32
60
|
hasHeader(name) {
|
|
33
|
-
return name.toLowerCase() in this.
|
|
61
|
+
return name.toLowerCase() in this._headers
|
|
34
62
|
}
|
|
35
63
|
|
|
36
64
|
setTimeout(ms, ontimeout) {
|
|
37
65
|
if (ontimeout) this.once('timeout', ontimeout)
|
|
38
66
|
|
|
39
|
-
this.
|
|
67
|
+
this._socket.setTimeout(ms)
|
|
40
68
|
|
|
41
69
|
return this
|
|
42
70
|
}
|
|
43
71
|
|
|
44
72
|
_predestroy() {
|
|
45
|
-
if (this.
|
|
73
|
+
if (this._upgrade === false && this._socket !== null) this._socket.destroy()
|
|
46
74
|
}
|
|
47
75
|
}
|
package/lib/outgoing-message.js
CHANGED
|
@@ -5,39 +5,55 @@ module.exports = class HTTPOutgoingMessage extends Writable {
|
|
|
5
5
|
constructor(socket = null) {
|
|
6
6
|
super()
|
|
7
7
|
|
|
8
|
-
this.
|
|
9
|
-
this.
|
|
10
|
-
this.
|
|
11
|
-
this.
|
|
8
|
+
this._socket = socket
|
|
9
|
+
this._headers = {}
|
|
10
|
+
this._headersSent = false
|
|
11
|
+
this._upgrade = false
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
get socket() {
|
|
15
|
+
return this._socket
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
get headers() {
|
|
19
|
+
return this._headers
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
get headersSent() {
|
|
23
|
+
return this._headersSent
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
get upgrade() {
|
|
27
|
+
return this._upgrade
|
|
12
28
|
}
|
|
13
29
|
|
|
14
30
|
getHeader(name) {
|
|
15
|
-
return this.
|
|
31
|
+
return this._headers[name.toLowerCase()]
|
|
16
32
|
}
|
|
17
33
|
|
|
18
34
|
getHeaders() {
|
|
19
|
-
return { ...this.
|
|
35
|
+
return { ...this._headers }
|
|
20
36
|
}
|
|
21
37
|
|
|
22
38
|
hasHeader(name) {
|
|
23
|
-
return name.toLowerCase() in this.
|
|
39
|
+
return name.toLowerCase() in this._headers
|
|
24
40
|
}
|
|
25
41
|
|
|
26
42
|
setHeader(name, value) {
|
|
27
|
-
this.
|
|
43
|
+
this._headers[name.toLowerCase()] = value
|
|
28
44
|
}
|
|
29
45
|
|
|
30
46
|
flushHeaders() {
|
|
31
|
-
if (this.
|
|
47
|
+
if (this._headersSent === true || this._socket === null) return
|
|
32
48
|
|
|
33
|
-
this.
|
|
34
|
-
this.
|
|
49
|
+
this._socket.write(Buffer.from(this._header()))
|
|
50
|
+
this._headersSent = true
|
|
35
51
|
}
|
|
36
52
|
|
|
37
53
|
setTimeout(ms, ontimeout) {
|
|
38
54
|
if (ontimeout) this.once('timeout', ontimeout)
|
|
39
55
|
|
|
40
|
-
this.
|
|
56
|
+
this._socket.setTimeout(ms)
|
|
41
57
|
|
|
42
58
|
return this
|
|
43
59
|
}
|
|
@@ -47,6 +63,6 @@ module.exports = class HTTPOutgoingMessage extends Writable {
|
|
|
47
63
|
}
|
|
48
64
|
|
|
49
65
|
_predestroy() {
|
|
50
|
-
if (this.
|
|
66
|
+
if (this._upgrade === false && this._socket !== null) this._socket.destroy()
|
|
51
67
|
}
|
|
52
68
|
}
|
package/lib/server-connection.js
CHANGED
|
@@ -19,11 +19,11 @@ module.exports = class HTTPServerConnection {
|
|
|
19
19
|
constructor(server, socket, opts = {}) {
|
|
20
20
|
const { IncomingMessage = HTTPIncomingMessage, ServerResponse = HTTPServerResponse } = opts
|
|
21
21
|
|
|
22
|
-
this.
|
|
23
|
-
this.
|
|
22
|
+
this._server = server
|
|
23
|
+
this._socket = socket
|
|
24
24
|
|
|
25
|
-
this.
|
|
26
|
-
this.
|
|
25
|
+
this._req = null
|
|
26
|
+
this._res = null
|
|
27
27
|
|
|
28
28
|
this._IncomingMessage = IncomingMessage
|
|
29
29
|
this._ServerResponse = ServerResponse
|
|
@@ -45,7 +45,23 @@ module.exports = class HTTPServerConnection {
|
|
|
45
45
|
|
|
46
46
|
HTTPServerConnection._connections.set(socket, this)
|
|
47
47
|
|
|
48
|
-
if (this.
|
|
48
|
+
if (this._server.timeout) socket.setTimeout(this._server.timeout)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
get server() {
|
|
52
|
+
return this._server
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
get socket() {
|
|
56
|
+
return this._socket
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
get req() {
|
|
60
|
+
return this._req
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
get res() {
|
|
64
|
+
return this._res
|
|
49
65
|
}
|
|
50
66
|
|
|
51
67
|
get idle() {
|
|
@@ -53,10 +69,11 @@ module.exports = class HTTPServerConnection {
|
|
|
53
69
|
}
|
|
54
70
|
|
|
55
71
|
_onclose() {
|
|
56
|
-
if (this.
|
|
57
|
-
if (this.
|
|
58
|
-
|
|
59
|
-
|
|
72
|
+
if (this._req && !isEnded(this._req)) this._req.destroy()
|
|
73
|
+
if (this._res && !isFinished(this._res)) this._res.destroy()
|
|
74
|
+
|
|
75
|
+
const err = getStreamError(this._socket)
|
|
76
|
+
if (err) this._socket.destroy(err)
|
|
60
77
|
}
|
|
61
78
|
|
|
62
79
|
_ondata(data) {
|
|
@@ -66,86 +83,92 @@ module.exports = class HTTPServerConnection {
|
|
|
66
83
|
for (const op of this._parser.push(data)) {
|
|
67
84
|
switch (op.type) {
|
|
68
85
|
case REQUEST:
|
|
69
|
-
this.
|
|
86
|
+
this._req = new this._IncomingMessage(this._socket, op.headers, {
|
|
70
87
|
method: op.method,
|
|
71
88
|
url: op.url
|
|
72
89
|
})
|
|
73
90
|
|
|
74
|
-
this.
|
|
75
|
-
this.
|
|
91
|
+
this._req.on('close', () => {
|
|
92
|
+
this._req = null
|
|
76
93
|
|
|
77
94
|
this._idle = true
|
|
78
95
|
|
|
79
|
-
if (this.
|
|
96
|
+
if (this._server.closing) this._socket.destroy()
|
|
80
97
|
})
|
|
81
98
|
|
|
82
99
|
// Eagerly open the request stream
|
|
83
|
-
this.
|
|
84
|
-
this.
|
|
100
|
+
this._req.resume()
|
|
101
|
+
this._req.pause()
|
|
85
102
|
|
|
86
103
|
if (op.headers.connection && op.headers.connection.toLowerCase() === 'upgrade') {
|
|
87
104
|
return this._onupgrade(this._parser.end())
|
|
88
105
|
}
|
|
89
106
|
|
|
90
|
-
this.
|
|
91
|
-
this.
|
|
92
|
-
this.
|
|
107
|
+
this._res = new this._ServerResponse(
|
|
108
|
+
this._socket,
|
|
109
|
+
this._req,
|
|
93
110
|
op.headers.connection === 'close'
|
|
94
111
|
)
|
|
95
112
|
|
|
96
|
-
this.
|
|
97
|
-
this.
|
|
113
|
+
this._res.on('close', () => {
|
|
114
|
+
this._res = null
|
|
98
115
|
})
|
|
99
116
|
|
|
100
|
-
this.
|
|
117
|
+
this._server.emit('request', this._req, this._res)
|
|
101
118
|
break
|
|
102
119
|
|
|
103
120
|
case DATA:
|
|
104
|
-
this.
|
|
121
|
+
this._req.push(op.data)
|
|
105
122
|
break
|
|
106
123
|
|
|
107
124
|
case END:
|
|
108
|
-
if (this.
|
|
125
|
+
if (this._req) {
|
|
126
|
+
this._req._socket = null
|
|
127
|
+
this._req.push(null)
|
|
128
|
+
}
|
|
109
129
|
break
|
|
110
130
|
}
|
|
111
131
|
}
|
|
112
132
|
} catch (err) {
|
|
113
|
-
this.
|
|
133
|
+
this._socket.destroy(err)
|
|
114
134
|
}
|
|
115
135
|
}
|
|
116
136
|
|
|
117
137
|
_onupgrade(data) {
|
|
118
|
-
this.
|
|
138
|
+
this._detach()
|
|
139
|
+
|
|
140
|
+
const req = this._req
|
|
141
|
+
|
|
142
|
+
req._upgrade = true
|
|
119
143
|
|
|
120
|
-
const
|
|
144
|
+
const upgraded = this._server.emit('upgrade', req, this._socket, data || EMPTY)
|
|
121
145
|
|
|
122
|
-
req.
|
|
123
|
-
req.destroy()
|
|
146
|
+
req.push(null)
|
|
124
147
|
|
|
125
|
-
this.
|
|
148
|
+
if (!upgraded) this._socket.destroy()
|
|
126
149
|
}
|
|
127
150
|
|
|
128
151
|
_ontimeout() {
|
|
129
|
-
const reqTimeout = this.
|
|
130
|
-
const resTimeout = this.
|
|
131
|
-
const serverTimeout = this.
|
|
152
|
+
const reqTimeout = this._req && this._req.emit('timeout')
|
|
153
|
+
const resTimeout = this._res && this._res.emit('timeout')
|
|
154
|
+
const serverTimeout = this._server.emit('timeout', this._socket)
|
|
132
155
|
|
|
133
|
-
if (!reqTimeout && !resTimeout && !serverTimeout) this.
|
|
156
|
+
if (!reqTimeout && !resTimeout && !serverTimeout) this._socket.destroy()
|
|
134
157
|
}
|
|
135
158
|
|
|
136
159
|
_ondrain() {
|
|
137
|
-
if (this.
|
|
160
|
+
if (this._res) this._res._continueWrite()
|
|
138
161
|
}
|
|
139
162
|
|
|
140
|
-
|
|
141
|
-
this.
|
|
163
|
+
_detach() {
|
|
164
|
+
this._socket
|
|
142
165
|
.off('error', noop)
|
|
143
166
|
.off('close', this._onclose)
|
|
144
167
|
.off('data', this._ondata)
|
|
145
168
|
.off('drain', this._ondrain)
|
|
146
169
|
.off('timeout', this._ontimeout)
|
|
147
170
|
|
|
148
|
-
HTTPServerConnection._connections.delete(this.
|
|
171
|
+
HTTPServerConnection._connections.delete(this._socket)
|
|
149
172
|
}
|
|
150
173
|
}
|
|
151
174
|
|
package/lib/server-response.js
CHANGED
|
@@ -8,10 +8,10 @@ module.exports = class HTTPServerResponse extends HTTPOutgoingMessage {
|
|
|
8
8
|
constructor(socket, req, close) {
|
|
9
9
|
super(socket)
|
|
10
10
|
|
|
11
|
-
this.
|
|
11
|
+
this._req = req
|
|
12
12
|
|
|
13
|
-
this.
|
|
14
|
-
this.
|
|
13
|
+
this._statusCode = 200
|
|
14
|
+
this._statusMessage = null
|
|
15
15
|
|
|
16
16
|
this._chunked = true
|
|
17
17
|
this._close = close
|
|
@@ -21,8 +21,21 @@ module.exports = class HTTPServerResponse extends HTTPOutgoingMessage {
|
|
|
21
21
|
this._pendingWrite = null
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
get req() {
|
|
25
|
+
return this._req
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
get statusCode() {
|
|
29
|
+
return this._statusCode
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
get statusMessage() {
|
|
33
|
+
return this._statusMessage
|
|
34
|
+
}
|
|
35
|
+
|
|
24
36
|
end(data) {
|
|
25
37
|
this._finishing = true
|
|
38
|
+
|
|
26
39
|
return super.end(data)
|
|
27
40
|
}
|
|
28
41
|
|
|
@@ -32,22 +45,23 @@ module.exports = class HTTPServerResponse extends HTTPOutgoingMessage {
|
|
|
32
45
|
statusMessage = null
|
|
33
46
|
}
|
|
34
47
|
|
|
35
|
-
this.
|
|
36
|
-
this.
|
|
37
|
-
|
|
48
|
+
this._statusCode = statusCode
|
|
49
|
+
this._statusMessage = statusMessage || null
|
|
50
|
+
|
|
51
|
+
if (headers) this._headers = { ...this._headers, ...headers }
|
|
38
52
|
}
|
|
39
53
|
|
|
40
54
|
_header() {
|
|
41
55
|
let h =
|
|
42
56
|
'HTTP/1.1 ' +
|
|
43
|
-
this.
|
|
57
|
+
this._statusCode +
|
|
44
58
|
' ' +
|
|
45
|
-
(this.
|
|
59
|
+
(this._statusMessage === null ? constants.status[this._statusCode] : this._statusMessage) +
|
|
46
60
|
'\r\n'
|
|
47
61
|
|
|
48
|
-
for (const name of Object.keys(this.
|
|
62
|
+
for (const name of Object.keys(this._headers)) {
|
|
49
63
|
const n = name.toLowerCase()
|
|
50
|
-
const v = this.
|
|
64
|
+
const v = this._headers[name]
|
|
51
65
|
|
|
52
66
|
if (n === 'content-length') this._chunked = false
|
|
53
67
|
if (n === 'connection' && v && v.toLowerCase() === 'close') this._close = true
|
|
@@ -63,7 +77,7 @@ module.exports = class HTTPServerResponse extends HTTPOutgoingMessage {
|
|
|
63
77
|
}
|
|
64
78
|
|
|
65
79
|
_write(data, encoding, cb) {
|
|
66
|
-
if (this.
|
|
80
|
+
if (this._headersSent === false) {
|
|
67
81
|
if (this._finishing) {
|
|
68
82
|
this.setHeader(
|
|
69
83
|
'Content-Length',
|
|
@@ -77,36 +91,36 @@ module.exports = class HTTPServerResponse extends HTTPOutgoingMessage {
|
|
|
77
91
|
if (this._onlyHeaders === true) return cb(null)
|
|
78
92
|
|
|
79
93
|
if (this._chunked) {
|
|
80
|
-
this.
|
|
81
|
-
this.
|
|
94
|
+
this._socket.write(Buffer.from(data.byteLength.toString(16)))
|
|
95
|
+
this._socket.write(CHUNK_DELIMITER)
|
|
82
96
|
}
|
|
83
97
|
|
|
84
|
-
let flushed = this.
|
|
98
|
+
let flushed = this._socket.write(data)
|
|
85
99
|
|
|
86
|
-
if (this._chunked) flushed = this.
|
|
100
|
+
if (this._chunked) flushed = this._socket.write(CHUNK_DELIMITER)
|
|
87
101
|
|
|
88
102
|
if (flushed) cb(null)
|
|
89
103
|
else this._pendingWrite = cb
|
|
90
104
|
}
|
|
91
105
|
|
|
92
106
|
_final(cb) {
|
|
93
|
-
if (this.
|
|
107
|
+
if (this._headersSent === false) {
|
|
94
108
|
this.setHeader('Content-Length', '0')
|
|
95
109
|
this.flushHeaders()
|
|
96
110
|
}
|
|
97
111
|
|
|
98
|
-
if (this._chunked && this._onlyHeaders === false)
|
|
99
|
-
this.socket.write(CHUNK_TERMINATOR)
|
|
100
|
-
}
|
|
112
|
+
if (this._chunked && this._onlyHeaders === false) this._socket.write(CHUNK_TERMINATOR)
|
|
101
113
|
|
|
102
|
-
if (this._close) this.
|
|
114
|
+
if (this._close) this._socket.end()
|
|
103
115
|
|
|
104
116
|
cb(null)
|
|
105
117
|
}
|
|
106
118
|
|
|
107
119
|
_predestroy() {
|
|
108
120
|
super._predestroy()
|
|
109
|
-
|
|
121
|
+
|
|
122
|
+
this._req.destroy()
|
|
123
|
+
|
|
110
124
|
this._continueWrite()
|
|
111
125
|
}
|
|
112
126
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bare-http1",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.5.1",
|
|
4
4
|
"description": "Native HTTP/1 library for JavaScript",
|
|
5
5
|
"exports": {
|
|
6
6
|
"./package": "./package.json",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"homepage": "https://github.com/holepunchto/bare-http1#readme",
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"bare-events": "^2.6.0",
|
|
40
|
-
"bare-http-parser": "^1.
|
|
40
|
+
"bare-http-parser": "^1.1.1",
|
|
41
41
|
"bare-stream": "^2.3.0",
|
|
42
42
|
"bare-tcp": "^2.2.0"
|
|
43
43
|
},
|