Skip to content

Commit b916035

Browse files
ronagBridgeAR
authored andcommitted
http2: make HTTP2ServerResponse more streams compliant
HTTP2ServerResponse.write would behave differently than both http1 and streams. This PR makes it more compliant with stream.Writable behaviour. PR-URL: #30964 Refs: #29529 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Rich Trott <rtrott@gmail.com>
1 parent b193142 commit b916035

2 files changed

Lines changed: 74 additions & 39 deletions

File tree

‎lib/internal/http2/compat.js‎

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ const {
4040
ERR_HTTP2_STATUS_INVALID,
4141
ERR_INVALID_ARG_VALUE,
4242
ERR_INVALID_CALLBACK,
43-
ERR_INVALID_HTTP_TOKEN
43+
ERR_INVALID_HTTP_TOKEN,
44+
ERR_STREAM_WRITE_AFTER_END
4445
},
4546
hideStackFrames
4647
}=require('internal/errors');
@@ -439,6 +440,7 @@ class Http2ServerResponse extends Stream {
439440
this[kState]={
440441
closed: false,
441442
ending: false,
443+
destroyed: false,
442444
headRequest: false,
443445
sendDate: true,
444446
statusCode: HTTP_STATUS_OK,
@@ -649,23 +651,32 @@ class Http2ServerResponse extends Stream {
649651
}
650652

651653
write(chunk,encoding,cb){
654+
conststate=this[kState];
655+
652656
if(typeofencoding==='function'){
653657
cb=encoding;
654658
encoding='utf8';
655659
}
656660

657-
if(this[kState].closed){
658-
consterr=newERR_HTTP2_INVALID_STREAM();
661+
leterr;
662+
if(state.ending){
663+
err=newERR_STREAM_WRITE_AFTER_END();
664+
}elseif(state.closed){
665+
err=newERR_HTTP2_INVALID_STREAM();
666+
}elseif(state.destroyed){
667+
returnfalse;
668+
}
669+
670+
if(err){
659671
if(typeofcb==='function')
660672
process.nextTick(cb,err);
661-
else
662-
throwerr;
663-
return;
673+
this.destroy(err);
674+
returnfalse;
664675
}
665676

666677
conststream=this[kStream];
667678
if(!stream.headersSent)
668-
this.writeHead(this[kState].statusCode);
679+
this.writeHead(state.statusCode);
669680
returnstream.write(chunk,encoding,cb);
670681
}
671682

@@ -712,8 +723,10 @@ class Http2ServerResponse extends Stream {
712723
}
713724

714725
destroy(err){
715-
if(this[kState].closed)
726+
if(this[kState].destroyed)
716727
return;
728+
729+
this[kState].destroyed=true;
717730
this[kStream].destroy(err);
718731
}
719732

‎test/parallel/test-http2-compat-serverresponse-write.js‎

Lines changed: 53 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -10,42 +10,64 @@ if (!hasCrypto)
1010
skip('missing crypto');
1111
const{ createServer, connect }=require('http2');
1212
constassert=require('assert');
13-
14-
constserver=createServer();
15-
server.listen(0,mustCall(()=>{
16-
constport=server.address().port;
17-
consturl=`http://localhost:${port}`;
18-
constclient=connect(url,mustCall(()=>{
19-
constrequest=client.request();
20-
request.resume();
21-
request.on('end',mustCall());
22-
request.on('close',mustCall(()=>{
23-
client.close();
13+
{
14+
constserver=createServer();
15+
server.listen(0,mustCall(()=>{
16+
constport=server.address().port;
17+
consturl=`http://localhost:${port}`;
18+
constclient=connect(url,mustCall(()=>{
19+
constrequest=client.request();
20+
request.resume();
21+
request.on('end',mustCall());
22+
request.on('close',mustCall(()=>{
23+
client.close();
24+
}));
2425
}));
25-
}));
2626

27-
server.once('request',mustCall((request,response)=>{
28-
// response.write() returns true
29-
assert(response.write('muahaha','utf8',mustCall()));
27+
server.once('request',mustCall((request,response)=>{
28+
// response.write() returns true
29+
assert(response.write('muahaha','utf8',mustCall()));
3030

31-
response.stream.close(0,mustCall(()=>{
32-
response.on('error',mustNotCall());
31+
response.stream.close(0,mustCall(()=>{
32+
response.on('error',mustNotCall());
3333

34-
// response.write() without cb returns error
35-
assert.throws(
36-
()=>{response.write('muahaha');},
37-
{
38-
name: 'Error',
39-
code: 'ERR_HTTP2_INVALID_STREAM',
40-
message: 'The stream has been destroyed'
41-
}
42-
);
34+
// response.write() without cb returns error
35+
response.write('muahaha',mustCall((err)=>{
36+
assert.strictEqual(err.code,'ERR_HTTP2_INVALID_STREAM');
4337

44-
// response.write() with cb returns falsy value
45-
assert(!response.write('muahaha',mustCall()));
38+
// response.write() with cb returns falsy value
39+
assert(!response.write('muahaha',mustCall()));
40+
41+
client.destroy();
42+
server.close();
43+
}));
44+
}));
45+
}));
46+
}));
47+
}
48+
49+
{
50+
// Http2ServerResponse.write ERR_STREAM_WRITE_AFTER_END
51+
constserver=createServer();
52+
server.listen(0,mustCall(()=>{
53+
constport=server.address().port;
54+
consturl=`http://localhost:${port}`;
55+
constclient=connect(url,mustCall(()=>{
56+
constrequest=client.request();
57+
request.resume();
58+
request.on('end',mustCall());
59+
request.on('close',mustCall(()=>{
60+
client.close();
61+
}));
62+
}));
4663

47-
client.destroy();
48-
server.close();
64+
server.once('request',mustCall((request,response)=>{
65+
response.end();
66+
response.write('asd',mustCall((err)=>{
67+
assert.strictEqual(err.code,'ERR_STREAM_WRITE_AFTER_END');
68+
client.destroy();
69+
server.close();
70+
}));
4971
}));
5072
}));
51-
}));
73+
}

0 commit comments

Comments
 (0)