bare-http1 3.8.2 → 4.0.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 -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 +8 -8
- package/lib/outgoing-message.js +9 -9
- package/lib/server-connection.js +40 -22
- package/lib/server-response.js +25 -13
- package/lib/server.js +8 -5
- package/package.json +5 -4
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,8 +1,8 @@
|
|
|
1
1
|
const { Readable } = require('bare-stream')
|
|
2
2
|
|
|
3
3
|
module.exports = class HTTPIncomingMessage extends Readable {
|
|
4
|
-
constructor
|
|
5
|
-
super()
|
|
4
|
+
constructor(socket = null, headers = {}, opts = {}) {
|
|
5
|
+
super({ eagerOpen: true })
|
|
6
6
|
|
|
7
7
|
this.socket = socket
|
|
8
8
|
this.headers = headers
|
|
@@ -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
|
@@ -9,11 +9,11 @@ const empty = Buffer.alloc(0)
|
|
|
9
9
|
module.exports = class HTTPServerConnection {
|
|
10
10
|
static _connections = new WeakMap()
|
|
11
11
|
|
|
12
|
-
static for
|
|
12
|
+
static for(socket) {
|
|
13
13
|
return this._connections.get(socket) || null
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
constructor
|
|
16
|
+
constructor(server, socket, opts = {}) {
|
|
17
17
|
const {
|
|
18
18
|
IncomingMessage = HTTPIncomingMessage,
|
|
19
19
|
ServerResponse = HTTPServerResponse
|
|
@@ -51,18 +51,18 @@ module.exports = class HTTPServerConnection {
|
|
|
51
51
|
if (this.server.timeout) socket.setTimeout(this.server.timeout)
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
get idle
|
|
54
|
+
get idle() {
|
|
55
55
|
return this._idle
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
_onclose
|
|
58
|
+
_onclose() {
|
|
59
59
|
if (this.req && !isEnded(this.req)) this.req.destroy()
|
|
60
60
|
if (this.res && !isFinished(this.res)) this.res.destroy()
|
|
61
61
|
const err = getStreamError(this.socket)
|
|
62
62
|
if (err) this.socket.destroy(err)
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
_ondata
|
|
65
|
+
_ondata(data) {
|
|
66
66
|
this._idle = false
|
|
67
67
|
|
|
68
68
|
if (this._state === constants.state.IN_BODY) return this._onbody(data)
|
|
@@ -85,7 +85,10 @@ module.exports = class HTTPServerConnection {
|
|
|
85
85
|
|
|
86
86
|
if (this._state === constants.state.BEFORE_CHUNK) {
|
|
87
87
|
const head = this._buffer.subarray(0, i - 1)
|
|
88
|
-
this._buffer =
|
|
88
|
+
this._buffer =
|
|
89
|
+
i + 1 === this._buffer.byteLength
|
|
90
|
+
? null
|
|
91
|
+
: this._buffer.subarray(i + 1)
|
|
89
92
|
i = 0
|
|
90
93
|
hits = 0
|
|
91
94
|
this._onchunklength(head)
|
|
@@ -99,7 +102,10 @@ module.exports = class HTTPServerConnection {
|
|
|
99
102
|
continue
|
|
100
103
|
}
|
|
101
104
|
|
|
102
|
-
this._buffer =
|
|
105
|
+
this._buffer =
|
|
106
|
+
i + 1 === this._buffer.byteLength
|
|
107
|
+
? null
|
|
108
|
+
: this._buffer.subarray(i + 1)
|
|
103
109
|
i = 0
|
|
104
110
|
hits = 0
|
|
105
111
|
this._onchunk(chunk)
|
|
@@ -111,7 +117,10 @@ module.exports = class HTTPServerConnection {
|
|
|
111
117
|
} else if (hits === 3 && b === 10) {
|
|
112
118
|
if (this._state === constants.state.BEFORE_HEAD) {
|
|
113
119
|
const head = this._buffer.subarray(0, i - 3)
|
|
114
|
-
this._buffer =
|
|
120
|
+
this._buffer =
|
|
121
|
+
i + 1 === this._buffer.byteLength
|
|
122
|
+
? null
|
|
123
|
+
: this._buffer.subarray(i + 1)
|
|
115
124
|
i = 0
|
|
116
125
|
hits = 0
|
|
117
126
|
this._onhead(head)
|
|
@@ -124,7 +133,7 @@ module.exports = class HTTPServerConnection {
|
|
|
124
133
|
}
|
|
125
134
|
}
|
|
126
135
|
|
|
127
|
-
_onhead
|
|
136
|
+
_onhead(data) {
|
|
128
137
|
this._state = constants.state.IN_HEAD
|
|
129
138
|
|
|
130
139
|
const r = data.toString().split('\r\n')
|
|
@@ -142,7 +151,10 @@ module.exports = class HTTPServerConnection {
|
|
|
142
151
|
|
|
143
152
|
this.req = new this._IncomingMessage(this.socket, headers, { method, url })
|
|
144
153
|
|
|
145
|
-
this.req.on('close', () => {
|
|
154
|
+
this.req.on('close', () => {
|
|
155
|
+
this.req = null
|
|
156
|
+
this._onreset()
|
|
157
|
+
})
|
|
146
158
|
|
|
147
159
|
if (headers.connection && headers.connection.toLowerCase() === 'upgrade') {
|
|
148
160
|
const head = this._buffer
|
|
@@ -150,9 +162,15 @@ module.exports = class HTTPServerConnection {
|
|
|
150
162
|
return this._onupgrade(head)
|
|
151
163
|
}
|
|
152
164
|
|
|
153
|
-
this.res = new this._ServerResponse(
|
|
165
|
+
this.res = new this._ServerResponse(
|
|
166
|
+
this.socket,
|
|
167
|
+
this.req,
|
|
168
|
+
headers.connection === 'close'
|
|
169
|
+
)
|
|
154
170
|
|
|
155
|
-
this.res.on('close', () => {
|
|
171
|
+
this.res.on('close', () => {
|
|
172
|
+
this.res = null
|
|
173
|
+
})
|
|
156
174
|
|
|
157
175
|
this.server.emit('request', this.req, this.res)
|
|
158
176
|
|
|
@@ -173,14 +191,14 @@ module.exports = class HTTPServerConnection {
|
|
|
173
191
|
}
|
|
174
192
|
}
|
|
175
193
|
|
|
176
|
-
_onchunklength
|
|
194
|
+
_onchunklength(data) {
|
|
177
195
|
this._length = parseInt(data.toString(), 16)
|
|
178
196
|
|
|
179
197
|
if (this._length === 0) this._onfinished()
|
|
180
198
|
else this._state = constants.state.IN_CHUNK
|
|
181
199
|
}
|
|
182
200
|
|
|
183
|
-
_onchunk
|
|
201
|
+
_onchunk(data) {
|
|
184
202
|
this._read += data.byteLength
|
|
185
203
|
|
|
186
204
|
this.req.push(data)
|
|
@@ -188,7 +206,7 @@ module.exports = class HTTPServerConnection {
|
|
|
188
206
|
this._state = constants.state.BEFORE_CHUNK
|
|
189
207
|
}
|
|
190
208
|
|
|
191
|
-
_onbody
|
|
209
|
+
_onbody(data) {
|
|
192
210
|
this._read += data.byteLength
|
|
193
211
|
|
|
194
212
|
this.req.push(data)
|
|
@@ -196,7 +214,7 @@ module.exports = class HTTPServerConnection {
|
|
|
196
214
|
if (this._read === this._length) this._onfinished()
|
|
197
215
|
}
|
|
198
216
|
|
|
199
|
-
_onupgrade
|
|
217
|
+
_onupgrade(head) {
|
|
200
218
|
this._ondetach()
|
|
201
219
|
|
|
202
220
|
const req = this.req
|
|
@@ -207,7 +225,7 @@ module.exports = class HTTPServerConnection {
|
|
|
207
225
|
this.server.emit('upgrade', req, this.socket, head || empty)
|
|
208
226
|
}
|
|
209
227
|
|
|
210
|
-
_ontimeout
|
|
228
|
+
_ontimeout() {
|
|
211
229
|
const reqTimeout = this.req && this.req.emit('timeout')
|
|
212
230
|
const resTimeout = this.res && this.res.emit('timeout')
|
|
213
231
|
const serverTimeout = this.server.emit('timeout', this.socket)
|
|
@@ -215,11 +233,11 @@ module.exports = class HTTPServerConnection {
|
|
|
215
233
|
if (!reqTimeout && !resTimeout && !serverTimeout) this.socket.destroy()
|
|
216
234
|
}
|
|
217
235
|
|
|
218
|
-
_onfinished
|
|
236
|
+
_onfinished() {
|
|
219
237
|
if (this.req) this.req.push(null)
|
|
220
238
|
}
|
|
221
239
|
|
|
222
|
-
_onreset
|
|
240
|
+
_onreset() {
|
|
223
241
|
this._state = constants.state.BEFORE_HEAD
|
|
224
242
|
this._length = -1
|
|
225
243
|
this._read = 0
|
|
@@ -231,11 +249,11 @@ module.exports = class HTTPServerConnection {
|
|
|
231
249
|
}
|
|
232
250
|
}
|
|
233
251
|
|
|
234
|
-
_ondrain
|
|
252
|
+
_ondrain() {
|
|
235
253
|
if (this.res) this.res._continueWrite()
|
|
236
254
|
}
|
|
237
255
|
|
|
238
|
-
_ondetach
|
|
256
|
+
_ondetach() {
|
|
239
257
|
this.socket
|
|
240
258
|
.off('error', noop)
|
|
241
259
|
.off('close', this._onclose)
|
|
@@ -247,4 +265,4 @@ module.exports = class HTTPServerConnection {
|
|
|
247
265
|
}
|
|
248
266
|
}
|
|
249
267
|
|
|
250
|
-
function noop
|
|
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.1",
|
|
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",
|
|
@@ -27,10 +27,11 @@
|
|
|
27
27
|
"dependencies": {
|
|
28
28
|
"bare-events": "^2.0.0",
|
|
29
29
|
"bare-stream": "^2.3.0",
|
|
30
|
-
"bare-tcp": "^
|
|
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
|
}
|