Skip to content

Commit

Permalink
refactor: simplify busy implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
ronag committed Aug 28, 2020
1 parent fb4ae3b commit 7688716
Show file tree
Hide file tree
Showing 5 changed files with 50 additions and 85 deletions.
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ Machine: 2.8GHz AMD EPYC 7402P<br/>
Configuration: Node v14.4, HTTP/1.1 without TLS, 100 connections, Linux 5.4.12-1-lts

```
http - keepalive x 5,634 ops/sec ±2.53% (274 runs sampled)
undici - pipeline x 8,642 ops/sec ±3.08% (276 runs sampled)
undici - request x 12,681 ops/sec ±0.51% (279 runs sampled)
undici - stream x 14,006 ops/sec ±0.53% (280 runs sampled)
undici - dispatch x 15,002 ops/sec ±0.39% (278 runs sampled)
http - keepalive x 5,882 ops/sec ±1.87% (274 runs sampled)
undici - pipeline x 9,189 ops/sec ±2.02% (272 runs sampled)
undici - request x 12,623 ops/sec ±0.89% (277 runs sampled)
undici - stream x 14,136 ops/sec ±0.61% (280 runs sampled)
undici - dispatch x 14,883 ops/sec ±0.44% (281 runs sampled)
```

The benchmark is a simple `hello world` [example](benchmarks/index.js).
Expand Down
73 changes: 40 additions & 33 deletions lib/core/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -213,35 +213,7 @@ class Client extends EventEmitter {
}

get busy () {
// TODO: ignore aborted requests.

if (this.size >= this[kPipelining]) {
return true
}

if (this.size && !this.connected) {
return true
}

if (this[kReset] || this[kWriting]) {
return true
}

if (this[kResuming]) {
for (let n = this[kPendingIdx]; n < this[kQueue].length; n++) {
const { idempotent, body, reset } = this[kQueue][n]
if (!idempotent || reset) {
return true
}
if (util.isStream(body) && util.bodyLength(body) !== 0) {
return true
}
}
} else if (this.pending > 0) {
return true
}

return false
return this[kReset] || this[kWriting] || this.pending > 0
}

get destroyed () {
Expand Down Expand Up @@ -938,7 +910,22 @@ function _resume (client) {
}

function write (client, request) {
const { body, header } = request
const { body, header, method, upgrade } = request

// https://tools.ietf.org/html/rfc7231#section-4.3.1
// https://tools.ietf.org/html/rfc7231#section-4.3.2
// https://tools.ietf.org/html/rfc7231#section-4.3.5

// Sending a payload body on a request that does not
// expect it can cause undefined behavior on some
// servers and corrupt connection state. Do not
// re-use the connection for further requests.

const expectsPayload = (
method === 'PUT' ||
method === 'POST' ||
method === 'PATCH'
)

if (body && typeof body.read === 'function') {
// Try to read EOF in order to get length.
Expand All @@ -951,7 +938,7 @@ function write (client, request) {
contentLength = request.contentLength
}

if (contentLength === 0 && !request.expectsPayload) {
if (contentLength === 0 && !expectsPayload) {
// https://tools.ietf.org/html/rfc7230#section-3.3.2
// A user agent SHOULD NOT send a Content-Length header field when
// the request message does not contain a payload body and the method
Expand All @@ -976,7 +963,19 @@ function write (client, request) {
return false
}

if (request.reset) {
if (method === 'HEAD') {
// https://github.com/mcollina/undici/issues/258

// Close after a HEAD request to interop with misbehaving servers
// that may send a body in the response.

client[kReset] = true
}

if (method === 'CONNECT' || upgrade) {
// On CONNECT or upgrade, block pipeline from dispatching further
// requests on this connection.

client[kReset] = true
}

Expand Down Expand Up @@ -1004,6 +1003,10 @@ function write (client, request) {
socket.write('\r\n', 'ascii')
socket.uncork()

if (!expectsPayload) {
client[kReset] = true
}

request.body = null
} else {
client[kWriting] = true
Expand All @@ -1030,6 +1033,10 @@ function write (client, request) {
}

if (bytesWritten === 0) {
if (!expectsPayload) {
client[kReset] = true
}

if (contentLength === null) {
socket.write(`${header}transfer-encoding: chunked\r\n`, 'ascii')
} else {
Expand Down Expand Up @@ -1094,7 +1101,7 @@ function write (client, request) {
}

if (bytesWritten === 0) {
if (request.expectsPayload) {
if (expectsPayload) {
// https://tools.ietf.org/html/rfc7230#section-3.3.2
// A user agent SHOULD send a Content-Length in a request message when
// no Transfer-Encoding is sent and the request method defines a meaning
Expand Down
42 changes: 0 additions & 42 deletions lib/core/request.js
Original file line number Diff line number Diff line change
Expand Up @@ -191,48 +191,6 @@ class Request {
this[kResume] = resume
}

get expectsPayload () {
const { method } = this
return (
method === 'PUT' ||
method === 'POST' ||
method === 'PATCH'
)
}

get reset () {
const { method, upgrade, body } = this

if (method === 'HEAD') {
// https://github.com/mcollina/undici/issues/258

// Close after a HEAD request to interop with misbehaving servers
// that may send a body in the response.

return true
}

if (method === 'CONNECT' || upgrade) {
// On CONNECT or upgrade, block pipeline from dispatching further
// requests on this connection.
return true
}

if (body && !this.expectsPayload && util.bodyLength(body) !== 0) {
// https://tools.ietf.org/html/rfc7231#section-4.3.1
// https://tools.ietf.org/html/rfc7231#section-4.3.2
// https://tools.ietf.org/html/rfc7231#section-4.3.5

// Sending a payload body on a request that does not
// expect it can cause undefined behavior on some
// servers and corrupt connection state. Do not
// re-use the connection for further requests.
return true
}

return false
}

onConnect () {
assert(!this.aborted)

Expand Down
4 changes: 2 additions & 2 deletions test/client-pipelining.js
Original file line number Diff line number Diff line change
Expand Up @@ -423,7 +423,7 @@ test('pipelining HEAD busy', (t) => {
})
})
body.push(null)
t.strictEqual(client.busy, false)
t.strictEqual(client.busy, true)
}

{
Expand Down Expand Up @@ -568,7 +568,7 @@ test('pipelining idempotent busy', (t) => {
})
})
body.push(null)
t.strictEqual(client.busy, false)
t.strictEqual(client.busy, true)
}

{
Expand Down
6 changes: 3 additions & 3 deletions test/pipeline-pipelining.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ test('pipeline pipelining', (t) => {
method: 'GET',
path: '/'
}, ({ body }) => body).end().resume()
t.strictEqual(client.busy, false)
t.strictEqual(client.busy, true)
t.strictDeepEqual(client.running, 0)
t.strictDeepEqual(client.pending, 1)

Expand Down Expand Up @@ -82,15 +82,15 @@ test('pipeline pipelining retry', (t) => {
.on('error', (err) => {
t.ok(err)
})
t.strictEqual(client.busy, false)
t.strictEqual(client.busy, true)
t.strictDeepEqual(client.running, 0)
t.strictDeepEqual(client.pending, 1)

client.pipeline({
method: 'GET',
path: '/'
}, ({ body }) => body).end().resume()
t.strictEqual(client.busy, false)
t.strictEqual(client.busy, true)
t.strictDeepEqual(client.running, 0)
t.strictDeepEqual(client.pending, 2)

Expand Down

0 comments on commit 7688716

Please sign in to comment.