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

http2 throws ERR_HTTP2_INVALID_STREAM with minor amount of concurrency #22135

Closed
buu700 opened this issue Aug 5, 2018 · 2 comments
Closed

http2 throws ERR_HTTP2_INVALID_STREAM with minor amount of concurrency #22135

buu700 opened this issue Aug 5, 2018 · 2 comments

Comments

@buu700
Copy link

buu700 commented Aug 5, 2018

  • Version: v10.8.0
  • Platform: Linux 86f664ae731c 4.9.93-linuxkit-aufs #1 SMP Wed Jun 6 16:55:56 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux (ubuntu:18.04 Docker image)
  • Subsystem: http2

After a small handful of concurrent requests, http2 fails with the following error:

internal/http2/core.js:1791
      throw new ERR_HTTP2_INVALID_STREAM();
      ^

Error [ERR_HTTP2_INVALID_STREAM]: The stream has been destroyed
    at ServerHttp2Stream.sendTrailers (internal/http2/core.js:1791:13)
    at ServerHttp2Stream.onStreamTrailersReady (internal/http2/compat.js:377:8)
    at ServerHttp2Stream.emit (events.js:182:13)
    at Http2Stream.onStreamTrailers [as ontrailers] (internal/http2/core.js:318:15)
    at ServerHttp2Stream.submitRstStream (internal/http2/core.js:328:19)
    at ServerHttp2Stream.finishCloseStream (internal/http2/core.js:1533:3)
    at closeStream (internal/http2/core.js:1517:7)
    at ServerHttp2Stream.close (internal/http2/core.js:1846:5)
    at state.streams.forEach (internal/http2/core.js:2691:46)
    at Map.forEach (<anonymous>)

Test case:

cat > server.js << EOM
	const app = new (require('koa'))();
	const crypto = require('crypto');
	const fs = require('fs');
	const http2 = require('http2');


	app.use(async ctx => {
		await new Promise(resolve => setTimeout(resolve, 1000));

		ctx.body = 'balls';
		ctx.status = 200;
	});

	http2.createSecureServer(
		{
			allowHTTP1: true,
			cert: fs.readFileSync('cert.pem'),
			key: fs.readFileSync('key.pem'),
			dhparam: fs.readFileSync('dhparams.pem'),
			secureOptions:
				crypto.constants.SSL_OP_NO_SSLv3 |
				crypto.constants.SSL_OP_NO_TLSv1
		},
		app.callback()
	).listen(
		31337
	);
EOM

killall node
node server.js &
echo -n > count
while ps ux | grep server.js | grep -v grep 2> /dev/null ; do
	echo -e '1\n2\n3\n4\n5\n6\n7\n8\n9\n10'
done | xargs -P10 bash -c '
	echo >> count
	curl -sk https://localhost:31337 > /dev/null
'

When the error occurs, you can manually kill the command with ctrl+C and wc -l count to view how many requests were initiated before the failure. For me it's been anywhere between 10 and 70.

This doesn't seem to be reproducible when using either https or spdy instead of http2.

@mscdex
Copy link
Contributor

mscdex commented Aug 5, 2018

Can you reproduce the issue without koa?

@buu700
Copy link
Author

buu700 commented Aug 5, 2018

Ah right, good point. After giving it a few minutes, the koa-free alternative below doesn't seem affected. I'll close this and open a ticket there.

cat > server.js << EOM
	const crypto = require('crypto');
	const fs = require('fs');
	const http2 = require('http2');


	const server = http2.createSecureServer({
		allowHTTP1: true,
		cert: fs.readFileSync('cert.pem'),
		key: fs.readFileSync('key.pem'),
		dhparam: fs.readFileSync('dhparams.pem'),
		secureOptions:
			crypto.constants.SSL_OP_NO_SSLv3 |
			crypto.constants.SSL_OP_NO_TLSv1
	});

	server.on('stream', stream => {
		setTimeout(
			() => {
				stream.respond({'content-type': 'text/plain', ':status': 200});
				stream.end('balls');
			},
			1000
		);
	});

	server.listen(31337);
EOM

Edit: compatibility API version:

cat > server.js << EOM
	const crypto = require('crypto');
	const fs = require('fs');
	const http2 = require('http2');


	const server = http2.createSecureServer(
		{
			allowHTTP1: true,
			cert: fs.readFileSync('cert.pem'),
			key: fs.readFileSync('key.pem'),
			dhparam: fs.readFileSync('dhparams.pem'),
			secureOptions:
				crypto.constants.SSL_OP_NO_SSLv3 |
				crypto.constants.SSL_OP_NO_TLSv1
		},
		(req, res) => {
			setTimeout(
				() => {
					res.writeHead(200, {'content-type': 'text/plain'});
					res.end('balls');
				},
				1000
			);
		}
	);

	server.listen(31337);
EOM

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants