Skip to content

Commit c1b73f2

Browse files
ronagtargos
authored andcommitted
http: outgoing cork
PR-URL: #29053 Reviewed-By: Anna Henningsen <[email protected]>
1 parent 63e3a62 commit c1b73f2

File tree

4 files changed

+101
-7
lines changed

4 files changed

+101
-7
lines changed

doc/api/http.md

+16
Original file line numberDiff line numberDiff line change
@@ -1223,6 +1223,13 @@ added: v0.3.0
12231223

12241224
See [`response.socket`][].
12251225

1226+
### `response.cork()`
1227+
<!-- YAML
1228+
added: REPLACEME
1229+
-->
1230+
1231+
See [`writable.cork()`][].
1232+
12261233
### `response.end([data[, encoding]][, callback])`
12271234
<!-- YAML
12281235
added: v0.1.90
@@ -1508,6 +1515,13 @@ response.statusMessage = 'Not found';
15081515
After response header was sent to the client, this property indicates the
15091516
status message which was sent out.
15101517

1518+
### `response.uncork()`
1519+
<!-- YAML
1520+
added: REPLACEME
1521+
-->
1522+
1523+
See [`writable.uncork()`][].
1524+
15111525
### `response.writableEnded`
15121526
<!-- YAML
15131527
added: v12.9.0
@@ -2333,3 +2347,5 @@ not abort the request or do anything besides add a `'timeout'` event.
23332347
[`socket.unref()`]: net.html#net_socket_unref
23342348
[`url.parse()`]: url.html#url_url_parse_urlstring_parsequerystring_slashesdenotehost
23352349
[`HPE_HEADER_OVERFLOW`]: errors.html#errors_hpe_header_overflow
2350+
[`writable.cork()`]: stream.html#stream_writable_cork
2351+
[`writable.uncork()`]: stream.html#stream_writable_uncork

lib/_http_outgoing.js

+40-7
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ const { validateString } = require('internal/validators');
5555
const HIGH_WATER_MARK = getDefaultHighWaterMark();
5656
const { CRLF, debug } = common;
5757

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

@@ -98,6 +100,7 @@ function OutgoingMessage() {
98100

99101
this.finished = false;
100102
this._headerSent = false;
103+
this[kCorked] = 0;
101104

102105
this.socket = null;
103106
this.connection = 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();
@@ -204,6 +214,21 @@ OutgoingMessage.prototype._renderHeaders = function _renderHeaders() {
204214
return headers;
205215
};
206216

217+
OutgoingMessage.prototype.cork = function() {
218+
if (this.socket) {
219+
this.socket.cork();
220+
} else {
221+
this[kCorked]++;
222+
}
223+
};
224+
225+
OutgoingMessage.prototype.uncork = function() {
226+
if (this.socket) {
227+
this.socket.uncork();
228+
} else if (this[kCorked]) {
229+
this[kCorked]--;
230+
}
231+
};
207232

208233
OutgoingMessage.prototype.setTimeout = function setTimeout(msecs, callback) {
209234

@@ -694,7 +719,10 @@ OutgoingMessage.prototype.end = function end(chunk, encoding, callback) {
694719
return this;
695720
}
696721

697-
var uncork;
722+
if (this.socket) {
723+
this.socket.cork();
724+
}
725+
698726
if (chunk) {
699727
if (typeof chunk !== 'string' && !(chunk instanceof Buffer)) {
700728
throw new ERR_INVALID_ARG_TYPE('chunk', ['string', 'Buffer'], chunk);
@@ -705,10 +733,6 @@ OutgoingMessage.prototype.end = function end(chunk, encoding, callback) {
705733
else
706734
this._contentLength = chunk.length;
707735
}
708-
if (this.connection) {
709-
this.connection.cork();
710-
uncork = true;
711-
}
712736
write_(this, chunk, encoding, null, true);
713737
} else if (!this._header) {
714738
this._contentLength = 0;
@@ -727,8 +751,12 @@ OutgoingMessage.prototype.end = function end(chunk, encoding, callback) {
727751
this._send('', 'latin1', finish);
728752
}
729753

730-
if (uncork)
731-
this.connection.uncork();
754+
if (this.socket) {
755+
// Fully uncork connection on end().
756+
this.socket._writableState.corked = 1;
757+
this.socket.uncork();
758+
}
759+
this[kCorked] = 0;
732760

733761
this.finished = true;
734762

@@ -789,6 +817,11 @@ OutgoingMessage.prototype._flush = function _flush() {
789817
};
790818

791819
OutgoingMessage.prototype._flushOutput = function _flushOutput(socket) {
820+
while (this[kCorked]) {
821+
this[kCorked]--;
822+
socket.cork();
823+
}
824+
792825
const outputLength = this.outputData.length;
793826
if (outputLength <= 0)
794827
return undefined;

lib/internal/http2/compat.js

+12
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;
+33
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)