From d1bc6f0780c272cfea520c5df66fc4edb7122841 Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Sun, 25 Feb 2018 21:54:18 +0100 Subject: [PATCH] http2: send error text in case of ALPN mismatch Send a human-readable HTTP/1 response in case of an unexpected ALPN protocol. This helps with debugging this condition, since previously the only result of it would be a closed socket. PR-URL: https://github.com/nodejs/node/pull/18986 Reviewed-By: James M Snell Reviewed-By: Gus Caplan Reviewed-By: Colin Ihrig Reviewed-By: Matteo Collina Reviewed-By: Luigi Pinca Reviewed-By: Ruben Bridgewater --- lib/internal/http2/core.js | 13 +++++++++++-- test/parallel/test-http2-https-fallback.js | 13 ++++++++++--- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/lib/internal/http2/core.js b/lib/internal/http2/core.js index e18f581955fec0..42a95903107307 100644 --- a/lib/internal/http2/core.js +++ b/lib/internal/http2/core.js @@ -2499,8 +2499,17 @@ function connectionListener(socket) { return httpConnectionListener.call(this, socket); } // Let event handler deal with the socket - if (!this.emit('unknownProtocol', socket)) - socket.destroy(); + debug(`Unknown protocol from ${socket.remoteAddress}:${socket.remotePort}`); + if (!this.emit('unknownProtocol', socket)) { + // We don't know what to do, so let's just tell the other side what's + // going on in a format that they *might* understand. + socket.end('HTTP/1.0 403 Forbidden\r\n' + + 'Content-Type: text/plain\r\n\r\n' + + 'Unknown ALPN Protocol, expected `h2` to be available.\n' + + 'If this is a HTTP request: The server was not ' + + 'configured with the `allowHTTP1` option or a ' + + 'listener for the `unknownProtocol` event.\n'); + } return; } diff --git a/test/parallel/test-http2-https-fallback.js b/test/parallel/test-http2-https-fallback.js index 01b694e586dd49..5d9a7e17103e56 100644 --- a/test/parallel/test-http2-https-fallback.js +++ b/test/parallel/test-http2-https-fallback.js @@ -6,7 +6,7 @@ const fixtures = require('../common/fixtures'); if (!common.hasCrypto) common.skip('missing crypto'); -const { strictEqual } = require('assert'); +const { strictEqual, ok } = require('assert'); const { createSecureContext } = require('tls'); const { createSecureServer, connect } = require('http2'); const { get } = require('https'); @@ -131,10 +131,17 @@ function onSession(session) { // HTTP/1.1 client get(Object.assign(parse(origin), clientOptions), common.mustNotCall()) - .on('error', common.mustCall(cleanup)); + .on('error', common.mustCall(cleanup)) + .end(); // Incompatible ALPN TLS client + let text = ''; tls(Object.assign({ port, ALPNProtocols: ['fake'] }, clientOptions)) - .on('error', common.mustCall(cleanup)); + .setEncoding('utf8') + .on('data', (chunk) => text += chunk) + .on('end', common.mustCall(() => { + ok(/Unknown ALPN Protocol, expected `h2` to be available/.test(text)); + cleanup(); + })); })); }