Skip to content

Commit 62e15a7

Browse files
ronagMylesBorins
authored andcommitted
http: outgoing cork
PR-URL: #29053 Reviewed-By: Anna Henningsen <[email protected]>
1 parent 41720d7 commit 62e15a7

File tree

4 files changed

+100
-6
lines changed

4 files changed

+100
-6
lines changed

doc/api/http.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1231,6 +1231,13 @@ deprecated: v13.0.0
12311231

12321232
See [`response.socket`][].
12331233

1234+
### response.cork()
1235+
<!-- YAML
1236+
added: REPLACEME
1237+
-->
1238+
1239+
See [`writable.cork()`][].
1240+
12341241
### response.end(\[data\[, encoding\]\]\[, callback\])
12351242
<!-- YAML
12361243
added: v0.1.90
@@ -1516,6 +1523,13 @@ response.statusMessage = 'Not found';
15161523
After response header was sent to the client, this property indicates the
15171524
status message which was sent out.
15181525

1526+
### response.uncork()
1527+
<!-- YAML
1528+
added: REPLACEME
1529+
-->
1530+
1531+
See [`writable.uncork()`][].
1532+
15191533
### response.writableEnded
15201534
<!-- YAML
15211535
added: v12.9.0
@@ -2358,3 +2372,5 @@ not abort the request or do anything besides add a `'timeout'` event.
23582372
[`socket.unref()`]: net.html#net_socket_unref
23592373
[`url.parse()`]: url.html#url_url_parse_urlstring_parsequerystring_slashesdenotehost
23602374
[`HPE_HEADER_OVERFLOW`]: errors.html#errors_hpe_header_overflow
2375+
[`writable.cork()`]: stream.html#stream_writable_cork
2376+
[`writable.uncork()`]: stream.html#stream_writable_uncork

lib/_http_outgoing.js

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ const { validateString } = require('internal/validators');
5656
const HIGH_WATER_MARK = getDefaultHighWaterMark();
5757
const { CRLF, debug } = common;
5858

59+
const kCorked = Symbol('corked');
60+
5961
const RE_CONN_CLOSE = /(?:^|\W)close(?:$|\W)/i;
6062
const RE_TE_CHUNKED = common.chunkExpression;
6163

@@ -99,6 +101,7 @@ function OutgoingMessage() {
99101

100102
this.finished = false;
101103
this._headerSent = false;
104+
this[kCorked] = 0;
102105

103106
this.socket = null;
104107
this._header = null;
@@ -137,6 +140,13 @@ Object.defineProperty(OutgoingMessage.prototype, 'writableHighWaterMark', {
137140
}
138141
});
139142

143+
Object.defineProperty(OutgoingMessage.prototype, 'writableCorked', {
144+
get() {
145+
const corked = this.socket ? this.socket.writableCorked : 0;
146+
return corked + this[kCorked];
147+
}
148+
});
149+
140150
Object.defineProperty(OutgoingMessage.prototype, '_headers', {
141151
get: internalUtil.deprecate(function() {
142152
return this.getHeaders();
@@ -213,6 +223,21 @@ OutgoingMessage.prototype._renderHeaders = function _renderHeaders() {
213223
return headers;
214224
};
215225

226+
OutgoingMessage.prototype.cork = function() {
227+
if (this.socket) {
228+
this.socket.cork();
229+
} else {
230+
this[kCorked]++;
231+
}
232+
};
233+
234+
OutgoingMessage.prototype.uncork = function() {
235+
if (this.socket) {
236+
this.socket.uncork();
237+
} else if (this[kCorked]) {
238+
this[kCorked]--;
239+
}
240+
};
216241

217242
OutgoingMessage.prototype.setTimeout = function setTimeout(msecs, callback) {
218243

@@ -710,7 +735,10 @@ OutgoingMessage.prototype.end = function end(chunk, encoding, callback) {
710735
return this;
711736
}
712737

713-
var uncork;
738+
if (this.socket) {
739+
this.socket.cork();
740+
}
741+
714742
if (chunk) {
715743
if (typeof chunk !== 'string' && !(chunk instanceof Buffer)) {
716744
throw new ERR_INVALID_ARG_TYPE('chunk', ['string', 'Buffer'], chunk);
@@ -721,10 +749,6 @@ OutgoingMessage.prototype.end = function end(chunk, encoding, callback) {
721749
else
722750
this._contentLength = chunk.length;
723751
}
724-
if (this.socket) {
725-
this.socket.cork();
726-
uncork = true;
727-
}
728752
write_(this, chunk, encoding, null, true);
729753
} else if (!this._header) {
730754
this._contentLength = 0;
@@ -743,8 +767,12 @@ OutgoingMessage.prototype.end = function end(chunk, encoding, callback) {
743767
this._send('', 'latin1', finish);
744768
}
745769

746-
if (uncork)
770+
if (this.socket) {
771+
// Fully uncork connection on end().
772+
this.socket._writableState.corked = 1;
747773
this.socket.uncork();
774+
}
775+
this[kCorked] = 0;
748776

749777
this.finished = true;
750778

@@ -805,6 +833,11 @@ OutgoingMessage.prototype._flush = function _flush() {
805833
};
806834

807835
OutgoingMessage.prototype._flushOutput = function _flushOutput(socket) {
836+
while (this[kCorked]) {
837+
this[kCorked]--;
838+
socket.cork();
839+
}
840+
808841
const outputLength = this.outputData.length;
809842
if (outputLength <= 0)
810843
return undefined;

lib/internal/http2/compat.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -503,6 +503,10 @@ class Http2ServerResponse extends Stream {
503503
return this[kState].statusCode;
504504
}
505505

506+
get writableCorked() {
507+
return this[kStream].writableCorked;
508+
}
509+
506510
set statusCode(code) {
507511
code |= 0;
508512
if (code >= 100 && code < 200)
@@ -627,6 +631,14 @@ class Http2ServerResponse extends Stream {
627631
return this;
628632
}
629633

634+
cork() {
635+
this[kStream].cork();
636+
}
637+
638+
uncork() {
639+
this[kStream].uncork();
640+
}
641+
630642
write(chunk, encoding, cb) {
631643
if (typeof encoding === 'function') {
632644
cb = encoding;
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
'use strict';
2+
const common = require('../common');
3+
const http = require('http');
4+
const assert = require('assert');
5+
6+
const server = http.createServer((req, res) => {
7+
let corked = false;
8+
const originalWrite = res.socket.write;
9+
res.socket.write = common.mustCall((...args) => {
10+
assert.strictEqual(corked, false);
11+
return originalWrite.call(res.socket, ...args);
12+
}, 5);
13+
corked = true;
14+
res.cork();
15+
assert.strictEqual(res.writableCorked, res.socket.writableCorked);
16+
res.cork();
17+
assert.strictEqual(res.writableCorked, res.socket.writableCorked);
18+
res.writeHead(200, { 'a-header': 'a-header-value' });
19+
res.uncork();
20+
assert.strictEqual(res.writableCorked, res.socket.writableCorked);
21+
corked = false;
22+
res.end('asd');
23+
assert.strictEqual(res.writableCorked, res.socket.writableCorked);
24+
});
25+
26+
server.listen(0, () => {
27+
http.get({ port: server.address().port }, (res) => {
28+
res.on('data', common.mustCall());
29+
res.on('end', common.mustCall(() => {
30+
server.close();
31+
}));
32+
});
33+
});

0 commit comments

Comments
 (0)