Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HTTP/2 ServerResponse.destroy() has the same effect as ServerResponse.end() #35302

Open
szmarczak opened this issue Sep 22, 2020 · 4 comments
Open
Labels
http2 Issues or PRs related to the http2 subsystem.

Comments

@szmarczak
Copy link
Member

szmarczak commented Sep 22, 2020

  • Version: 14.11.0
  • Platform: Linux solus 5.6.19-158.current #1 SMP PREEMPT Sun Jul 26 14:17:01 UTC 2020 x86_64 GNU/Linux
  • Subsystem: http2

What steps will reproduce the bug?

const http = require('http2');

const server = http.createServer((request, response) => {
    response.write('a');
    
    setTimeout(() => {
		// Does the same as response.end()
        response.destroy();
    }, 100);
});

// HTTP/2
server.listen(() => {
    const session = http.connect(`http://localhost:${server.address().port}`);
    const stream = session.request();
    stream.resume();
    stream.end();

    stream.once('aborted', () => {
        console.log('stream aborted');
    });
    
    stream.once('end', () => {
        console.log('stream end');
        
        session.close();
        server.close();
    });
    
    stream.once('error', () => {
        console.log('stream error');
    });
});

// HTTP/1.1:
// server.listen(() => {
    // const request = http.get(`http://localhost:${server.address().port}`, response => {
	// 	console.log('got response');
	// 	response.resume();

    //     response.once('aborted', () => {
    //         console.log('aborted');
    //         server.close();
	// 	});

	// 	response.once('end', () => {
	// 		console.log('response end');
	// 	});

	// 	response.once('close', () => {
	// 		console.log('response close');
	// 	});
    // });
// });

Note: it works as expected when the method is e.g. POST and stream.end() is not called.

How often does it reproduce? Is there a required condition?

Always.

What is the expected behavior?

aborted or an error event.

What do you see instead?

end event is emitted.

@kanongil
Copy link
Contributor

I just encountered this issue as well. You can workaround this issue by adding any error to the destroy() call.

Once you do that, you will trigger another bug in the client, where end is emitted before error:

stream end
stream error

@kanongil
Copy link
Contributor

Calling destroy() without an error on an active stream should probably end up sending a RST_STREAM frame with code 8. to signal that it was aborted.

@watilde watilde added the http2 Issues or PRs related to the http2 subsystem. label Sep 26, 2020
@kanongil
Copy link
Contributor

Hmm, this is actually already tested against here (introduced in #15074):

{
const req = client.request();
req.on('response', common.mustNotCall());
req.on('error', common.mustNotCall());
req.on('end', common.mustCall());
req.on('close', common.mustCall(() => countdown.dec()));
req.resume();
}

Except, the test is wrong! – and doesn't match the equivalent HTTP1 behaviour (which will emit an 'aborted' event in the client)

I tried messing with the _destroy() implementation in core.js, but when I add a NGHTTP2_CANCEL code to the close, the client will just ignore the code and treat it as a normal stream end! So I guess we are up to 3 bugs now...

Actually, the aborted comment is plain wrong, since the aborted event is only emitted when the writable side is still open (which it won't be). So this logic just loses all such aborts:

// RST code 8 not emitted as an error as its used by clients to signify
// abort and is already covered by aborted event, also allows more
// seamless compatibility with http1
if (err == null && code !== NGHTTP2_NO_ERROR && code !== NGHTTP2_CANCEL)
err = new ERR_HTTP2_STREAM_ERROR(nameForErrorCode[code] || code);

@kanongil
Copy link
Contributor

kanongil commented Sep 28, 2020

FYI, I made an attempt at fixing all 3 bugs in #35209.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
http2 Issues or PRs related to the http2 subsystem.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants