bare-http1 3.8.1 → 4.0.0
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 -14
- package/lib/agent.js +20 -16
- package/lib/client-connection.js +42 -27
- package/lib/client-request.js +14 -12
- package/lib/errors.js +4 -4
- package/lib/incoming-message.js +7 -7
- package/lib/outgoing-message.js +9 -9
- package/lib/server-connection.js +51 -25
- package/lib/server-response.js +25 -13
- package/lib/server.js +8 -5
- package/package.json +6 -5
package/index.js
CHANGED
|
@@ -1,25 +1,25 @@
|
|
|
1
1
|
exports.IncomingMessage = require('./lib/incoming-message')
|
|
2
2
|
exports.OutgoingMessage = require('./lib/outgoing-message')
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
exports.globalAgent = Agent.global
|
|
4
|
+
exports.Agent = require('./lib/agent')
|
|
5
|
+
exports.globalAgent = exports.Agent.global
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
exports.Server = require('./lib/server')
|
|
8
8
|
exports.ServerResponse = require('./lib/server-response')
|
|
9
9
|
exports.ServerConnection = require('./lib/server-connection')
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
exports.ClientRequest = require('./lib/client-request')
|
|
12
12
|
exports.ClientConnection = require('./lib/client-connection')
|
|
13
13
|
|
|
14
14
|
exports.constants = require('./lib/constants')
|
|
15
15
|
|
|
16
16
|
exports.STATUS_CODES = exports.constants.status // For Node.js compatibility
|
|
17
17
|
|
|
18
|
-
exports.createServer = function createServer
|
|
19
|
-
return new Server(opts, onrequest)
|
|
18
|
+
exports.createServer = function createServer(opts, onrequest) {
|
|
19
|
+
return new exports.Server(opts, onrequest)
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
exports.request = function request
|
|
22
|
+
exports.request = function request(url, opts, onresponse) {
|
|
23
23
|
if (typeof opts === 'function') {
|
|
24
24
|
onresponse = opts
|
|
25
25
|
opts = {}
|
|
@@ -38,27 +38,31 @@ exports.request = function request (url, opts, onresponse) {
|
|
|
38
38
|
|
|
39
39
|
// For Node.js compatibility
|
|
40
40
|
opts.host = opts.hostname || opts.host
|
|
41
|
-
opts.port =
|
|
41
|
+
opts.port =
|
|
42
|
+
typeof opts.port === 'string' ? parseInt(opts.port, 10) : opts.port
|
|
42
43
|
}
|
|
43
44
|
|
|
44
|
-
return new
|
|
45
|
+
return new exports.ClientRequest(opts, onresponse)
|
|
45
46
|
}
|
|
46
47
|
|
|
47
48
|
// https://url.spec.whatwg.org/#default-port
|
|
48
|
-
function defaultPort
|
|
49
|
+
function defaultPort(url) {
|
|
49
50
|
switch (url.protocol) {
|
|
50
|
-
case 'ftp:':
|
|
51
|
+
case 'ftp:':
|
|
52
|
+
return 21
|
|
51
53
|
case 'http:':
|
|
52
|
-
case 'ws:':
|
|
54
|
+
case 'ws:':
|
|
55
|
+
return 80
|
|
53
56
|
case 'https:':
|
|
54
|
-
case 'wss:':
|
|
57
|
+
case 'wss:':
|
|
58
|
+
return 443
|
|
55
59
|
}
|
|
56
60
|
|
|
57
61
|
return null
|
|
58
62
|
}
|
|
59
63
|
|
|
60
64
|
// https://url.spec.whatwg.org/#api
|
|
61
|
-
function isURL
|
|
65
|
+
function isURL(url) {
|
|
62
66
|
return (
|
|
63
67
|
url !== null &&
|
|
64
68
|
typeof url === 'object' &&
|
package/lib/agent.js
CHANGED
|
@@ -2,29 +2,31 @@ const tcp = require('bare-tcp')
|
|
|
2
2
|
const HTTPClientConnection = require('./client-connection')
|
|
3
3
|
|
|
4
4
|
module.exports = class HTTPAgent {
|
|
5
|
-
constructor
|
|
6
|
-
const {
|
|
7
|
-
keepAlive = false,
|
|
8
|
-
keepAliveMsecs = 1000
|
|
9
|
-
} = opts
|
|
5
|
+
constructor(opts = {}) {
|
|
6
|
+
const { keepAlive = false, keepAliveMsecs = 1000 } = opts
|
|
10
7
|
|
|
11
8
|
this._sockets = new Map()
|
|
12
9
|
this._freeSockets = new Map()
|
|
13
10
|
|
|
14
|
-
this._keepAlive =
|
|
11
|
+
this._keepAlive =
|
|
12
|
+
typeof keepAlive === 'number'
|
|
13
|
+
? keepAlive
|
|
14
|
+
: keepAlive
|
|
15
|
+
? keepAliveMsecs
|
|
16
|
+
: -1
|
|
15
17
|
|
|
16
18
|
this._opts = { ...opts }
|
|
17
19
|
}
|
|
18
20
|
|
|
19
|
-
createConnection
|
|
21
|
+
createConnection(opts) {
|
|
20
22
|
return tcp.createConnection(opts)
|
|
21
23
|
}
|
|
22
24
|
|
|
23
|
-
reuseSocket
|
|
25
|
+
reuseSocket(socket, req) {
|
|
24
26
|
socket.ref()
|
|
25
27
|
}
|
|
26
28
|
|
|
27
|
-
keepSocketAlive
|
|
29
|
+
keepSocketAlive(socket) {
|
|
28
30
|
if (this._keepAlive === -1) return false
|
|
29
31
|
|
|
30
32
|
socket.setKeepAlive(true, this._keepAlive)
|
|
@@ -33,11 +35,11 @@ module.exports = class HTTPAgent {
|
|
|
33
35
|
return true
|
|
34
36
|
}
|
|
35
37
|
|
|
36
|
-
getName
|
|
38
|
+
getName(opts) {
|
|
37
39
|
return `${opts.host}:${opts.port}`
|
|
38
40
|
}
|
|
39
41
|
|
|
40
|
-
addRequest
|
|
42
|
+
addRequest(req, opts) {
|
|
41
43
|
opts = { ...opts, ...this._opts }
|
|
42
44
|
|
|
43
45
|
const name = this.getName(opts)
|
|
@@ -75,7 +77,7 @@ module.exports = class HTTPAgent {
|
|
|
75
77
|
connection.req = req
|
|
76
78
|
}
|
|
77
79
|
|
|
78
|
-
destroy
|
|
80
|
+
destroy() {
|
|
79
81
|
for (const set of [this._sockets, this._freeSockets]) {
|
|
80
82
|
for (const [, sockets] of set) {
|
|
81
83
|
for (const socket of sockets) socket.destroy()
|
|
@@ -83,7 +85,7 @@ module.exports = class HTTPAgent {
|
|
|
83
85
|
}
|
|
84
86
|
}
|
|
85
87
|
|
|
86
|
-
_onfree
|
|
88
|
+
_onfree(socket, name) {
|
|
87
89
|
if (this.keepSocketAlive(socket)) {
|
|
88
90
|
this._onremove(socket, name, false)
|
|
89
91
|
|
|
@@ -99,8 +101,10 @@ module.exports = class HTTPAgent {
|
|
|
99
101
|
}
|
|
100
102
|
}
|
|
101
103
|
|
|
102
|
-
_onremove
|
|
103
|
-
for (const set of all
|
|
104
|
+
_onremove(socket, name, all = true) {
|
|
105
|
+
for (const set of all
|
|
106
|
+
? [this._sockets, this._freeSockets]
|
|
107
|
+
: [this._sockets]) {
|
|
104
108
|
const sockets = set.get(name)
|
|
105
109
|
if (sockets === undefined) continue
|
|
106
110
|
|
|
@@ -109,7 +113,7 @@ module.exports = class HTTPAgent {
|
|
|
109
113
|
}
|
|
110
114
|
}
|
|
111
115
|
|
|
112
|
-
_ontimeout
|
|
116
|
+
_ontimeout(socket, name) {
|
|
113
117
|
const sockets = this._freeSockets.get(name)
|
|
114
118
|
if (!sockets) return
|
|
115
119
|
|
package/lib/client-connection.js
CHANGED
|
@@ -7,18 +7,16 @@ const empty = Buffer.alloc(0)
|
|
|
7
7
|
module.exports = class HTTPClientConnection {
|
|
8
8
|
static _connections = new WeakMap()
|
|
9
9
|
|
|
10
|
-
static for
|
|
10
|
+
static for(socket) {
|
|
11
11
|
return this._connections.get(socket) || null
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
static from
|
|
14
|
+
static from(socket, opts) {
|
|
15
15
|
return this.for(socket) || new this(socket, opts)
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
constructor
|
|
19
|
-
const {
|
|
20
|
-
IncomingMessage = HTTPIncomingMessage
|
|
21
|
-
} = opts
|
|
18
|
+
constructor(socket, opts = {}) {
|
|
19
|
+
const { IncomingMessage = HTTPIncomingMessage } = opts
|
|
22
20
|
|
|
23
21
|
this.socket = socket
|
|
24
22
|
|
|
@@ -51,23 +49,23 @@ module.exports = class HTTPClientConnection {
|
|
|
51
49
|
HTTPClientConnection._connections.set(socket, this)
|
|
52
50
|
}
|
|
53
51
|
|
|
54
|
-
get idle
|
|
52
|
+
get idle() {
|
|
55
53
|
return this._idle
|
|
56
54
|
}
|
|
57
55
|
|
|
58
|
-
_onerror
|
|
56
|
+
_onerror(err) {
|
|
59
57
|
if (this.req) this.req.destroy(err)
|
|
60
58
|
}
|
|
61
59
|
|
|
62
|
-
_onclose
|
|
60
|
+
_onclose() {
|
|
63
61
|
if (this.req) this.req._continueFinal()
|
|
64
62
|
}
|
|
65
63
|
|
|
66
|
-
_onend
|
|
64
|
+
_onend() {
|
|
67
65
|
if (this.req) this.req.destroy(errors.CONNECTION_LOST())
|
|
68
66
|
}
|
|
69
67
|
|
|
70
|
-
_ondata
|
|
68
|
+
_ondata(data) {
|
|
71
69
|
this._idle = false
|
|
72
70
|
|
|
73
71
|
if (this._state === constants.state.IN_BODY) return this._onbody(data)
|
|
@@ -90,7 +88,10 @@ module.exports = class HTTPClientConnection {
|
|
|
90
88
|
|
|
91
89
|
if (this._state === constants.state.BEFORE_CHUNK) {
|
|
92
90
|
const head = this._buffer.subarray(0, i - 1)
|
|
93
|
-
this._buffer =
|
|
91
|
+
this._buffer =
|
|
92
|
+
i + 1 === this._buffer.byteLength
|
|
93
|
+
? null
|
|
94
|
+
: this._buffer.subarray(i + 1)
|
|
94
95
|
i = 0
|
|
95
96
|
hits = 0
|
|
96
97
|
this._onchunklength(head)
|
|
@@ -104,7 +105,10 @@ module.exports = class HTTPClientConnection {
|
|
|
104
105
|
continue
|
|
105
106
|
}
|
|
106
107
|
|
|
107
|
-
this._buffer =
|
|
108
|
+
this._buffer =
|
|
109
|
+
i + 1 === this._buffer.byteLength
|
|
110
|
+
? null
|
|
111
|
+
: this._buffer.subarray(i + 1)
|
|
108
112
|
i = 0
|
|
109
113
|
hits = 0
|
|
110
114
|
this._onchunk(chunk)
|
|
@@ -116,7 +120,10 @@ module.exports = class HTTPClientConnection {
|
|
|
116
120
|
} else if (hits === 3 && b === 10) {
|
|
117
121
|
if (this._state === constants.state.BEFORE_HEAD) {
|
|
118
122
|
const head = this._buffer.subarray(0, i - 3)
|
|
119
|
-
this._buffer =
|
|
123
|
+
this._buffer =
|
|
124
|
+
i + 1 === this._buffer.byteLength
|
|
125
|
+
? null
|
|
126
|
+
: this._buffer.subarray(i + 1)
|
|
120
127
|
i = 0
|
|
121
128
|
hits = 0
|
|
122
129
|
this._onhead(head)
|
|
@@ -129,7 +136,7 @@ module.exports = class HTTPClientConnection {
|
|
|
129
136
|
}
|
|
130
137
|
}
|
|
131
138
|
|
|
132
|
-
_onhead
|
|
139
|
+
_onhead(data) {
|
|
133
140
|
this._state = constants.state.IN_HEAD
|
|
134
141
|
|
|
135
142
|
const r = data.toString().split('\r\n')
|
|
@@ -145,11 +152,19 @@ module.exports = class HTTPClientConnection {
|
|
|
145
152
|
headers[name.toLowerCase()] = value
|
|
146
153
|
}
|
|
147
154
|
|
|
148
|
-
this.req.on('close', () => {
|
|
155
|
+
this.req.on('close', () => {
|
|
156
|
+
this.req = null
|
|
157
|
+
})
|
|
149
158
|
|
|
150
|
-
this.res = new this._IncomingMessage(this.socket, headers, {
|
|
159
|
+
this.res = new this._IncomingMessage(this.socket, headers, {
|
|
160
|
+
statusCode: parseInt(statusCode, 10),
|
|
161
|
+
statusMessage: statusMessage.join(' ')
|
|
162
|
+
})
|
|
151
163
|
|
|
152
|
-
this.res.on('close', () => {
|
|
164
|
+
this.res.on('close', () => {
|
|
165
|
+
this.res = null
|
|
166
|
+
this._onreset()
|
|
167
|
+
})
|
|
153
168
|
|
|
154
169
|
if (headers.connection && headers.connection.toLowerCase() === 'upgrade') {
|
|
155
170
|
const head = this._buffer
|
|
@@ -176,14 +191,14 @@ module.exports = class HTTPClientConnection {
|
|
|
176
191
|
}
|
|
177
192
|
}
|
|
178
193
|
|
|
179
|
-
_onchunklength
|
|
194
|
+
_onchunklength(data) {
|
|
180
195
|
this._length = parseInt(data.toString(), 16)
|
|
181
196
|
|
|
182
197
|
if (this._length === 0) this._onfinished()
|
|
183
198
|
else this._state = constants.state.IN_CHUNK
|
|
184
199
|
}
|
|
185
200
|
|
|
186
|
-
_onchunk
|
|
201
|
+
_onchunk(data) {
|
|
187
202
|
this._read += data.byteLength
|
|
188
203
|
|
|
189
204
|
this.res.push(data)
|
|
@@ -191,7 +206,7 @@ module.exports = class HTTPClientConnection {
|
|
|
191
206
|
this._state = constants.state.BEFORE_CHUNK
|
|
192
207
|
}
|
|
193
208
|
|
|
194
|
-
_onbody
|
|
209
|
+
_onbody(data) {
|
|
195
210
|
this._read += data.byteLength
|
|
196
211
|
|
|
197
212
|
this.res.push(data)
|
|
@@ -199,7 +214,7 @@ module.exports = class HTTPClientConnection {
|
|
|
199
214
|
if (this._read === this._length) this._onfinished()
|
|
200
215
|
}
|
|
201
216
|
|
|
202
|
-
_onupgrade
|
|
217
|
+
_onupgrade(head) {
|
|
203
218
|
this._ondetach()
|
|
204
219
|
|
|
205
220
|
const req = this.req
|
|
@@ -212,16 +227,16 @@ module.exports = class HTTPClientConnection {
|
|
|
212
227
|
this.socket.destroy()
|
|
213
228
|
}
|
|
214
229
|
|
|
215
|
-
_ontimeout
|
|
230
|
+
_ontimeout() {
|
|
216
231
|
if (this.req) this.req.emit('timeout')
|
|
217
232
|
}
|
|
218
233
|
|
|
219
|
-
_onfinished
|
|
234
|
+
_onfinished() {
|
|
220
235
|
if (this.res) this.res.push(null)
|
|
221
236
|
if (this.req) this.req._continueFinal()
|
|
222
237
|
}
|
|
223
238
|
|
|
224
|
-
_onreset
|
|
239
|
+
_onreset() {
|
|
225
240
|
this._state = constants.state.BEFORE_HEAD
|
|
226
241
|
this._length = -1
|
|
227
242
|
this._read = 0
|
|
@@ -231,11 +246,11 @@ module.exports = class HTTPClientConnection {
|
|
|
231
246
|
this.socket.emit('free')
|
|
232
247
|
}
|
|
233
248
|
|
|
234
|
-
_ondrain
|
|
249
|
+
_ondrain() {
|
|
235
250
|
if (this.req) this.req._continueWrite()
|
|
236
251
|
}
|
|
237
252
|
|
|
238
|
-
_ondetach
|
|
253
|
+
_ondetach() {
|
|
239
254
|
this.socket
|
|
240
255
|
.off('error', this._onerror)
|
|
241
256
|
.off('close', this._onclose)
|
package/lib/client-request.js
CHANGED
|
@@ -2,7 +2,7 @@ const HTTPAgent = require('./agent')
|
|
|
2
2
|
const HTTPOutgoingMessage = require('./outgoing-message')
|
|
3
3
|
|
|
4
4
|
module.exports = class HTTPClientRequest extends HTTPOutgoingMessage {
|
|
5
|
-
constructor
|
|
5
|
+
constructor(opts = {}, onresponse = null) {
|
|
6
6
|
if (typeof opts === 'function') {
|
|
7
7
|
onresponse = opts
|
|
8
8
|
opts = {}
|
|
@@ -10,11 +10,12 @@ module.exports = class HTTPClientRequest extends HTTPOutgoingMessage {
|
|
|
10
10
|
|
|
11
11
|
opts = opts ? { ...opts } : {}
|
|
12
12
|
|
|
13
|
-
const agent =
|
|
13
|
+
const agent =
|
|
14
|
+
opts.agent === false ? new HTTPAgent() : opts.agent || HTTPAgent.global
|
|
14
15
|
const method = opts.method || 'GET'
|
|
15
16
|
const path = opts.path || '/'
|
|
16
|
-
const host = opts.host = opts.host || 'localhost'
|
|
17
|
-
const port = opts.port = opts.port || 80
|
|
17
|
+
const host = (opts.host = opts.host || 'localhost')
|
|
18
|
+
const port = (opts.port = opts.port || 80)
|
|
18
19
|
|
|
19
20
|
super()
|
|
20
21
|
|
|
@@ -31,7 +32,7 @@ module.exports = class HTTPClientRequest extends HTTPOutgoingMessage {
|
|
|
31
32
|
if (onresponse) this.once('response', onresponse)
|
|
32
33
|
}
|
|
33
34
|
|
|
34
|
-
_header
|
|
35
|
+
_header() {
|
|
35
36
|
let h = `${this.method} ${this.path} HTTP/1.1\r\n`
|
|
36
37
|
|
|
37
38
|
let upgrade = false
|
|
@@ -41,7 +42,8 @@ module.exports = class HTTPClientRequest extends HTTPOutgoingMessage {
|
|
|
41
42
|
const v = this.headers[name]
|
|
42
43
|
|
|
43
44
|
if (n === 'content-length') this._chunked = false
|
|
44
|
-
if (n === 'connection' && v && v.toLowerCase() === 'upgrade')
|
|
45
|
+
if (n === 'connection' && v && v.toLowerCase() === 'upgrade')
|
|
46
|
+
upgrade = true
|
|
45
47
|
|
|
46
48
|
h += `${httpCase(n)}: ${v}\r\n`
|
|
47
49
|
}
|
|
@@ -55,7 +57,7 @@ module.exports = class HTTPClientRequest extends HTTPOutgoingMessage {
|
|
|
55
57
|
return h
|
|
56
58
|
}
|
|
57
59
|
|
|
58
|
-
_write
|
|
60
|
+
_write(data, encoding, cb) {
|
|
59
61
|
if (this.headersSent === false) this.flushHeaders()
|
|
60
62
|
|
|
61
63
|
if (this._chunked) {
|
|
@@ -70,7 +72,7 @@ module.exports = class HTTPClientRequest extends HTTPOutgoingMessage {
|
|
|
70
72
|
else this._pendingWrite = cb
|
|
71
73
|
}
|
|
72
74
|
|
|
73
|
-
_final
|
|
75
|
+
_final(cb) {
|
|
74
76
|
if (this.headersSent === false) this.flushHeaders()
|
|
75
77
|
|
|
76
78
|
if (this._chunked) this.socket.write(Buffer.from('0\r\n\r\n'))
|
|
@@ -78,20 +80,20 @@ module.exports = class HTTPClientRequest extends HTTPOutgoingMessage {
|
|
|
78
80
|
this._pendingFinal = cb
|
|
79
81
|
}
|
|
80
82
|
|
|
81
|
-
_predestroy
|
|
83
|
+
_predestroy() {
|
|
82
84
|
if (this.upgrade) return this._continueFinal()
|
|
83
85
|
|
|
84
86
|
this.socket.destroy()
|
|
85
87
|
}
|
|
86
88
|
|
|
87
|
-
_continueWrite
|
|
89
|
+
_continueWrite() {
|
|
88
90
|
if (this._pendingWrite === null) return
|
|
89
91
|
const cb = this._pendingWrite
|
|
90
92
|
this._pendingWrite = null
|
|
91
93
|
cb(null)
|
|
92
94
|
}
|
|
93
95
|
|
|
94
|
-
_continueFinal
|
|
96
|
+
_continueFinal() {
|
|
95
97
|
if (this._pendingFinal === null) return
|
|
96
98
|
const cb = this._pendingFinal
|
|
97
99
|
this._pendingFinal = null
|
|
@@ -99,7 +101,7 @@ module.exports = class HTTPClientRequest extends HTTPOutgoingMessage {
|
|
|
99
101
|
}
|
|
100
102
|
}
|
|
101
103
|
|
|
102
|
-
function httpCase
|
|
104
|
+
function httpCase(n) {
|
|
103
105
|
let s = ''
|
|
104
106
|
for (const part of n.split('-')) {
|
|
105
107
|
s += (s ? '-' : '') + part.slice(0, 1).toUpperCase() + part.slice(1)
|
package/lib/errors.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
module.exports = class HTTPError extends Error {
|
|
2
|
-
constructor
|
|
2
|
+
constructor(msg, code, fn = HTTPError) {
|
|
3
3
|
super(`${code}: ${msg}`)
|
|
4
4
|
this.code = code
|
|
5
5
|
|
|
@@ -8,15 +8,15 @@ module.exports = class HTTPError extends Error {
|
|
|
8
8
|
}
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
get name
|
|
11
|
+
get name() {
|
|
12
12
|
return 'HTTPError'
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
static NOT_IMPLEMENTED
|
|
15
|
+
static NOT_IMPLEMENTED(msg = 'Method not implemented') {
|
|
16
16
|
return new HTTPError(msg, 'NOT_IMPLEMENTED', HTTPError.NOT_IMPLEMENTED)
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
static CONNECTION_LOST
|
|
19
|
+
static CONNECTION_LOST(msg = 'Socket hung up') {
|
|
20
20
|
return new HTTPError(msg, 'CONNECTION_LOST', HTTPError.CONNECTION_LOST)
|
|
21
21
|
}
|
|
22
22
|
}
|
package/lib/incoming-message.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const { Readable } = require('bare-stream')
|
|
2
2
|
|
|
3
3
|
module.exports = class HTTPIncomingMessage extends Readable {
|
|
4
|
-
constructor
|
|
4
|
+
constructor(socket = null, headers = {}, opts = {}) {
|
|
5
5
|
super()
|
|
6
6
|
|
|
7
7
|
this.socket = socket
|
|
@@ -17,23 +17,23 @@ module.exports = class HTTPIncomingMessage extends Readable {
|
|
|
17
17
|
this.statusMessage = opts.statusMessage || ''
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
get httpVersion
|
|
20
|
+
get httpVersion() {
|
|
21
21
|
return '1.1'
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
getHeader
|
|
24
|
+
getHeader(name) {
|
|
25
25
|
return this.headers[name.toLowerCase()]
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
getHeaders
|
|
28
|
+
getHeaders() {
|
|
29
29
|
return { ...this.headers }
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
hasHeader
|
|
32
|
+
hasHeader(name) {
|
|
33
33
|
return name.toLowerCase() in this.headers
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
setTimeout
|
|
36
|
+
setTimeout(ms, ontimeout) {
|
|
37
37
|
if (ontimeout) this.once('timeout', ontimeout)
|
|
38
38
|
|
|
39
39
|
this.socket.setTimeout(ms)
|
|
@@ -41,7 +41,7 @@ module.exports = class HTTPIncomingMessage extends Readable {
|
|
|
41
41
|
return this
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
_predestroy
|
|
44
|
+
_predestroy() {
|
|
45
45
|
if (this.upgrade === false && this.socket !== null) this.socket.destroy()
|
|
46
46
|
}
|
|
47
47
|
}
|
package/lib/outgoing-message.js
CHANGED
|
@@ -2,7 +2,7 @@ const { Writable } = require('bare-stream')
|
|
|
2
2
|
const errors = require('./errors')
|
|
3
3
|
|
|
4
4
|
module.exports = class HTTPOutgoingMessage extends Writable {
|
|
5
|
-
constructor
|
|
5
|
+
constructor(socket = null) {
|
|
6
6
|
super()
|
|
7
7
|
|
|
8
8
|
this.socket = socket
|
|
@@ -11,30 +11,30 @@ module.exports = class HTTPOutgoingMessage extends Writable {
|
|
|
11
11
|
this.upgrade = false
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
getHeader
|
|
14
|
+
getHeader(name) {
|
|
15
15
|
return this.headers[name.toLowerCase()]
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
getHeaders
|
|
18
|
+
getHeaders() {
|
|
19
19
|
return { ...this.headers }
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
hasHeader
|
|
22
|
+
hasHeader(name) {
|
|
23
23
|
return name.toLowerCase() in this.headers
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
setHeader
|
|
26
|
+
setHeader(name, value) {
|
|
27
27
|
this.headers[name.toLowerCase()] = value
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
flushHeaders
|
|
30
|
+
flushHeaders() {
|
|
31
31
|
if (this.headersSent === true || this.socket === null) return
|
|
32
32
|
|
|
33
33
|
this.socket.write(Buffer.from(this._header()))
|
|
34
34
|
this.headersSent = true
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
setTimeout
|
|
37
|
+
setTimeout(ms, ontimeout) {
|
|
38
38
|
if (ontimeout) this.once('timeout', ontimeout)
|
|
39
39
|
|
|
40
40
|
this.socket.setTimeout(ms)
|
|
@@ -42,11 +42,11 @@ module.exports = class HTTPOutgoingMessage extends Writable {
|
|
|
42
42
|
return this
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
_header
|
|
45
|
+
_header() {
|
|
46
46
|
throw errors.NOT_IMPLEMENTED()
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
_predestroy
|
|
49
|
+
_predestroy() {
|
|
50
50
|
if (this.upgrade === false && this.socket !== null) this.socket.destroy()
|
|
51
51
|
}
|
|
52
52
|
}
|
package/lib/server-connection.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const tcp = require('bare-tcp')
|
|
2
|
+
const { isEnded, isFinished, getStreamError } = require('bare-stream')
|
|
2
3
|
const HTTPIncomingMessage = require('./incoming-message')
|
|
3
4
|
const HTTPServerResponse = require('./server-response')
|
|
4
5
|
const constants = require('./constants')
|
|
@@ -8,11 +9,11 @@ const empty = Buffer.alloc(0)
|
|
|
8
9
|
module.exports = class HTTPServerConnection {
|
|
9
10
|
static _connections = new WeakMap()
|
|
10
11
|
|
|
11
|
-
static for
|
|
12
|
+
static for(socket) {
|
|
12
13
|
return this._connections.get(socket) || null
|
|
13
14
|
}
|
|
14
15
|
|
|
15
|
-
constructor
|
|
16
|
+
constructor(server, socket, opts = {}) {
|
|
16
17
|
const {
|
|
17
18
|
IncomingMessage = HTTPIncomingMessage,
|
|
18
19
|
ServerResponse = HTTPServerResponse
|
|
@@ -33,13 +34,14 @@ module.exports = class HTTPServerConnection {
|
|
|
33
34
|
this._buffer = null
|
|
34
35
|
this._idle = true
|
|
35
36
|
|
|
36
|
-
this.
|
|
37
|
+
this._onclose = this._onclose.bind(this)
|
|
37
38
|
this._ondata = this._ondata.bind(this)
|
|
38
39
|
this._ondrain = this._ondrain.bind(this)
|
|
39
40
|
this._ontimeout = this._ontimeout.bind(this)
|
|
40
41
|
|
|
41
42
|
socket
|
|
42
|
-
.on('error',
|
|
43
|
+
.on('error', noop)
|
|
44
|
+
.on('close', this._onclose)
|
|
43
45
|
.on('data', this._ondata)
|
|
44
46
|
.on('drain', this._ondrain)
|
|
45
47
|
.on('timeout', this._ontimeout)
|
|
@@ -49,15 +51,18 @@ module.exports = class HTTPServerConnection {
|
|
|
49
51
|
if (this.server.timeout) socket.setTimeout(this.server.timeout)
|
|
50
52
|
}
|
|
51
53
|
|
|
52
|
-
get idle
|
|
54
|
+
get idle() {
|
|
53
55
|
return this._idle
|
|
54
56
|
}
|
|
55
57
|
|
|
56
|
-
|
|
57
|
-
this.
|
|
58
|
+
_onclose() {
|
|
59
|
+
if (this.req && !isEnded(this.req)) this.req.destroy()
|
|
60
|
+
if (this.res && !isFinished(this.res)) this.res.destroy()
|
|
61
|
+
const err = getStreamError(this.socket)
|
|
62
|
+
if (err) this.socket.destroy(err)
|
|
58
63
|
}
|
|
59
64
|
|
|
60
|
-
_ondata
|
|
65
|
+
_ondata(data) {
|
|
61
66
|
this._idle = false
|
|
62
67
|
|
|
63
68
|
if (this._state === constants.state.IN_BODY) return this._onbody(data)
|
|
@@ -80,7 +85,10 @@ module.exports = class HTTPServerConnection {
|
|
|
80
85
|
|
|
81
86
|
if (this._state === constants.state.BEFORE_CHUNK) {
|
|
82
87
|
const head = this._buffer.subarray(0, i - 1)
|
|
83
|
-
this._buffer =
|
|
88
|
+
this._buffer =
|
|
89
|
+
i + 1 === this._buffer.byteLength
|
|
90
|
+
? null
|
|
91
|
+
: this._buffer.subarray(i + 1)
|
|
84
92
|
i = 0
|
|
85
93
|
hits = 0
|
|
86
94
|
this._onchunklength(head)
|
|
@@ -94,7 +102,10 @@ module.exports = class HTTPServerConnection {
|
|
|
94
102
|
continue
|
|
95
103
|
}
|
|
96
104
|
|
|
97
|
-
this._buffer =
|
|
105
|
+
this._buffer =
|
|
106
|
+
i + 1 === this._buffer.byteLength
|
|
107
|
+
? null
|
|
108
|
+
: this._buffer.subarray(i + 1)
|
|
98
109
|
i = 0
|
|
99
110
|
hits = 0
|
|
100
111
|
this._onchunk(chunk)
|
|
@@ -106,7 +117,10 @@ module.exports = class HTTPServerConnection {
|
|
|
106
117
|
} else if (hits === 3 && b === 10) {
|
|
107
118
|
if (this._state === constants.state.BEFORE_HEAD) {
|
|
108
119
|
const head = this._buffer.subarray(0, i - 3)
|
|
109
|
-
this._buffer =
|
|
120
|
+
this._buffer =
|
|
121
|
+
i + 1 === this._buffer.byteLength
|
|
122
|
+
? null
|
|
123
|
+
: this._buffer.subarray(i + 1)
|
|
110
124
|
i = 0
|
|
111
125
|
hits = 0
|
|
112
126
|
this._onhead(head)
|
|
@@ -119,7 +133,7 @@ module.exports = class HTTPServerConnection {
|
|
|
119
133
|
}
|
|
120
134
|
}
|
|
121
135
|
|
|
122
|
-
_onhead
|
|
136
|
+
_onhead(data) {
|
|
123
137
|
this._state = constants.state.IN_HEAD
|
|
124
138
|
|
|
125
139
|
const r = data.toString().split('\r\n')
|
|
@@ -137,7 +151,10 @@ module.exports = class HTTPServerConnection {
|
|
|
137
151
|
|
|
138
152
|
this.req = new this._IncomingMessage(this.socket, headers, { method, url })
|
|
139
153
|
|
|
140
|
-
this.req.on('close', () => {
|
|
154
|
+
this.req.on('close', () => {
|
|
155
|
+
this.req = null
|
|
156
|
+
this._onreset()
|
|
157
|
+
})
|
|
141
158
|
|
|
142
159
|
if (headers.connection && headers.connection.toLowerCase() === 'upgrade') {
|
|
143
160
|
const head = this._buffer
|
|
@@ -145,9 +162,15 @@ module.exports = class HTTPServerConnection {
|
|
|
145
162
|
return this._onupgrade(head)
|
|
146
163
|
}
|
|
147
164
|
|
|
148
|
-
this.res = new this._ServerResponse(
|
|
165
|
+
this.res = new this._ServerResponse(
|
|
166
|
+
this.socket,
|
|
167
|
+
this.req,
|
|
168
|
+
headers.connection === 'close'
|
|
169
|
+
)
|
|
149
170
|
|
|
150
|
-
this.res.on('close', () => {
|
|
171
|
+
this.res.on('close', () => {
|
|
172
|
+
this.res = null
|
|
173
|
+
})
|
|
151
174
|
|
|
152
175
|
this.server.emit('request', this.req, this.res)
|
|
153
176
|
|
|
@@ -168,14 +191,14 @@ module.exports = class HTTPServerConnection {
|
|
|
168
191
|
}
|
|
169
192
|
}
|
|
170
193
|
|
|
171
|
-
_onchunklength
|
|
194
|
+
_onchunklength(data) {
|
|
172
195
|
this._length = parseInt(data.toString(), 16)
|
|
173
196
|
|
|
174
197
|
if (this._length === 0) this._onfinished()
|
|
175
198
|
else this._state = constants.state.IN_CHUNK
|
|
176
199
|
}
|
|
177
200
|
|
|
178
|
-
_onchunk
|
|
201
|
+
_onchunk(data) {
|
|
179
202
|
this._read += data.byteLength
|
|
180
203
|
|
|
181
204
|
this.req.push(data)
|
|
@@ -183,7 +206,7 @@ module.exports = class HTTPServerConnection {
|
|
|
183
206
|
this._state = constants.state.BEFORE_CHUNK
|
|
184
207
|
}
|
|
185
208
|
|
|
186
|
-
_onbody
|
|
209
|
+
_onbody(data) {
|
|
187
210
|
this._read += data.byteLength
|
|
188
211
|
|
|
189
212
|
this.req.push(data)
|
|
@@ -191,7 +214,7 @@ module.exports = class HTTPServerConnection {
|
|
|
191
214
|
if (this._read === this._length) this._onfinished()
|
|
192
215
|
}
|
|
193
216
|
|
|
194
|
-
_onupgrade
|
|
217
|
+
_onupgrade(head) {
|
|
195
218
|
this._ondetach()
|
|
196
219
|
|
|
197
220
|
const req = this.req
|
|
@@ -202,7 +225,7 @@ module.exports = class HTTPServerConnection {
|
|
|
202
225
|
this.server.emit('upgrade', req, this.socket, head || empty)
|
|
203
226
|
}
|
|
204
227
|
|
|
205
|
-
_ontimeout
|
|
228
|
+
_ontimeout() {
|
|
206
229
|
const reqTimeout = this.req && this.req.emit('timeout')
|
|
207
230
|
const resTimeout = this.res && this.res.emit('timeout')
|
|
208
231
|
const serverTimeout = this.server.emit('timeout', this.socket)
|
|
@@ -210,11 +233,11 @@ module.exports = class HTTPServerConnection {
|
|
|
210
233
|
if (!reqTimeout && !resTimeout && !serverTimeout) this.socket.destroy()
|
|
211
234
|
}
|
|
212
235
|
|
|
213
|
-
_onfinished
|
|
236
|
+
_onfinished() {
|
|
214
237
|
if (this.req) this.req.push(null)
|
|
215
238
|
}
|
|
216
239
|
|
|
217
|
-
_onreset
|
|
240
|
+
_onreset() {
|
|
218
241
|
this._state = constants.state.BEFORE_HEAD
|
|
219
242
|
this._length = -1
|
|
220
243
|
this._read = 0
|
|
@@ -226,13 +249,14 @@ module.exports = class HTTPServerConnection {
|
|
|
226
249
|
}
|
|
227
250
|
}
|
|
228
251
|
|
|
229
|
-
_ondrain
|
|
252
|
+
_ondrain() {
|
|
230
253
|
if (this.res) this.res._continueWrite()
|
|
231
254
|
}
|
|
232
255
|
|
|
233
|
-
_ondetach
|
|
256
|
+
_ondetach() {
|
|
234
257
|
this.socket
|
|
235
|
-
.off('error',
|
|
258
|
+
.off('error', noop)
|
|
259
|
+
.off('close', this._onclose)
|
|
236
260
|
.off('data', this._ondata)
|
|
237
261
|
.off('drain', this._ondrain)
|
|
238
262
|
.off('timeout', this._ontimeout)
|
|
@@ -240,3 +264,5 @@ module.exports = class HTTPServerConnection {
|
|
|
240
264
|
HTTPServerConnection._connections.delete(this.socket)
|
|
241
265
|
}
|
|
242
266
|
}
|
|
267
|
+
|
|
268
|
+
function noop() {}
|
package/lib/server-response.js
CHANGED
|
@@ -2,7 +2,7 @@ const HTTPOutgoingMessage = require('./outgoing-message')
|
|
|
2
2
|
const constants = require('./constants')
|
|
3
3
|
|
|
4
4
|
module.exports = class HTTPServerResponse extends HTTPOutgoingMessage {
|
|
5
|
-
constructor
|
|
5
|
+
constructor(socket, req, close) {
|
|
6
6
|
super(socket)
|
|
7
7
|
|
|
8
8
|
this.req = req
|
|
@@ -18,12 +18,12 @@ module.exports = class HTTPServerResponse extends HTTPOutgoingMessage {
|
|
|
18
18
|
this._pendingWrite = null
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
end
|
|
21
|
+
end(data) {
|
|
22
22
|
this._finishing = true
|
|
23
23
|
return super.end(data)
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
writeHead
|
|
26
|
+
writeHead(statusCode, statusMessage = null, headers = {}) {
|
|
27
27
|
if (typeof statusMessage === 'object' && statusMessage !== null) {
|
|
28
28
|
headers = statusMessage
|
|
29
29
|
statusMessage = null
|
|
@@ -34,15 +34,23 @@ module.exports = class HTTPServerResponse extends HTTPOutgoingMessage {
|
|
|
34
34
|
this.headers = headers || {}
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
_header
|
|
38
|
-
let h =
|
|
37
|
+
_header() {
|
|
38
|
+
let h =
|
|
39
|
+
'HTTP/1.1 ' +
|
|
40
|
+
this.statusCode +
|
|
41
|
+
' ' +
|
|
42
|
+
(this.statusMessage === null
|
|
43
|
+
? constants.status[this.statusCode]
|
|
44
|
+
: this.statusMessage) +
|
|
45
|
+
'\r\n'
|
|
39
46
|
|
|
40
47
|
for (const name of Object.keys(this.headers)) {
|
|
41
48
|
const n = name.toLowerCase()
|
|
42
49
|
const v = this.headers[name]
|
|
43
50
|
|
|
44
51
|
if (n === 'content-length') this._chunked = false
|
|
45
|
-
if (n === 'connection' && v && v.toLowerCase() === 'close')
|
|
52
|
+
if (n === 'connection' && v && v.toLowerCase() === 'close')
|
|
53
|
+
this._close = true
|
|
46
54
|
|
|
47
55
|
h += httpCase(n) + ': ' + v + '\r\n'
|
|
48
56
|
}
|
|
@@ -54,10 +62,13 @@ module.exports = class HTTPServerResponse extends HTTPOutgoingMessage {
|
|
|
54
62
|
return h
|
|
55
63
|
}
|
|
56
64
|
|
|
57
|
-
_write
|
|
65
|
+
_write(data, encoding, cb) {
|
|
58
66
|
if (this.headersSent === false) {
|
|
59
67
|
if (this._finishing) {
|
|
60
|
-
this.setHeader(
|
|
68
|
+
this.setHeader(
|
|
69
|
+
'Content-Length',
|
|
70
|
+
(data.byteLength + this._writableState.buffered).toString()
|
|
71
|
+
)
|
|
61
72
|
}
|
|
62
73
|
|
|
63
74
|
this.flushHeaders()
|
|
@@ -77,25 +88,26 @@ module.exports = class HTTPServerResponse extends HTTPOutgoingMessage {
|
|
|
77
88
|
else this._pendingWrite = cb
|
|
78
89
|
}
|
|
79
90
|
|
|
80
|
-
_final
|
|
91
|
+
_final(cb) {
|
|
81
92
|
if (this.headersSent === false) {
|
|
82
93
|
this.setHeader('Content-Length', '0')
|
|
83
94
|
this.flushHeaders()
|
|
84
95
|
}
|
|
85
96
|
|
|
86
|
-
if (this._chunked && this._onlyHeaders === false)
|
|
97
|
+
if (this._chunked && this._onlyHeaders === false)
|
|
98
|
+
this.socket.write(Buffer.from('0\r\n\r\n'))
|
|
87
99
|
if (this._close) this.socket.end()
|
|
88
100
|
|
|
89
101
|
cb(null)
|
|
90
102
|
}
|
|
91
103
|
|
|
92
|
-
_predestroy
|
|
104
|
+
_predestroy() {
|
|
93
105
|
super._predestroy()
|
|
94
106
|
this.req.destroy()
|
|
95
107
|
this._continueWrite()
|
|
96
108
|
}
|
|
97
109
|
|
|
98
|
-
_continueWrite
|
|
110
|
+
_continueWrite() {
|
|
99
111
|
if (this._pendingWrite === null) return
|
|
100
112
|
const cb = this._pendingWrite
|
|
101
113
|
this._pendingWrite = null
|
|
@@ -103,7 +115,7 @@ module.exports = class HTTPServerResponse extends HTTPOutgoingMessage {
|
|
|
103
115
|
}
|
|
104
116
|
}
|
|
105
117
|
|
|
106
|
-
function httpCase
|
|
118
|
+
function httpCase(n) {
|
|
107
119
|
let s = ''
|
|
108
120
|
for (const part of n.split('-')) {
|
|
109
121
|
s += (s ? '-' : '') + part.slice(0, 1).toUpperCase() + part.slice(1)
|
package/lib/server.js
CHANGED
|
@@ -2,7 +2,7 @@ const TCPServer = require('bare-tcp').Server
|
|
|
2
2
|
const HTTPServerConnection = require('./server-connection')
|
|
3
3
|
|
|
4
4
|
module.exports = class HTTPServer extends TCPServer {
|
|
5
|
-
constructor
|
|
5
|
+
constructor(opts = {}, onrequest) {
|
|
6
6
|
if (typeof opts === 'function') {
|
|
7
7
|
onrequest = opts
|
|
8
8
|
opts = {}
|
|
@@ -12,16 +12,19 @@ module.exports = class HTTPServer extends TCPServer {
|
|
|
12
12
|
|
|
13
13
|
this._timeout = 0
|
|
14
14
|
|
|
15
|
-
this.on(
|
|
15
|
+
this.on(
|
|
16
|
+
'connection',
|
|
17
|
+
(socket) => new HTTPServerConnection(this, socket, opts)
|
|
18
|
+
)
|
|
16
19
|
|
|
17
20
|
if (onrequest) this.on('request', onrequest)
|
|
18
21
|
}
|
|
19
22
|
|
|
20
|
-
get timeout
|
|
23
|
+
get timeout() {
|
|
21
24
|
return this._timeout || undefined // For Node.js compatibility
|
|
22
25
|
}
|
|
23
26
|
|
|
24
|
-
setTimeout
|
|
27
|
+
setTimeout(ms = 0, ontimeout) {
|
|
25
28
|
if (ontimeout) this.on('timeout', ontimeout)
|
|
26
29
|
|
|
27
30
|
this._timeout = ms
|
|
@@ -29,7 +32,7 @@ module.exports = class HTTPServer extends TCPServer {
|
|
|
29
32
|
return this
|
|
30
33
|
}
|
|
31
34
|
|
|
32
|
-
close
|
|
35
|
+
close(onclose) {
|
|
33
36
|
super.close(onclose)
|
|
34
37
|
|
|
35
38
|
for (const socket of this._connections) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bare-http1",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0",
|
|
4
4
|
"description": "Native HTTP/1 library for JavaScript",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./index.js",
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"lib"
|
|
13
13
|
],
|
|
14
14
|
"scripts": {
|
|
15
|
-
"test": "
|
|
15
|
+
"test": "prettier . --check && bare test.js"
|
|
16
16
|
},
|
|
17
17
|
"repository": {
|
|
18
18
|
"type": "git",
|
|
@@ -26,11 +26,12 @@
|
|
|
26
26
|
"homepage": "https://github.com/holepunchto/bare-http1#readme",
|
|
27
27
|
"dependencies": {
|
|
28
28
|
"bare-events": "^2.0.0",
|
|
29
|
-
"bare-stream": "^2.
|
|
30
|
-
"bare-tcp": "^
|
|
29
|
+
"bare-stream": "^2.3.0",
|
|
30
|
+
"bare-tcp": "^2.0.0"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
33
|
"brittle": "^3.3.0",
|
|
34
|
-
"
|
|
34
|
+
"prettier": "^3.4.1",
|
|
35
|
+
"prettier-config-standard": "^7.0.0"
|
|
35
36
|
}
|
|
36
37
|
}
|