HTTP / 1 does not use the same request semantics as HTTP / 2, so HTTP / 1 clients must be discovered and processed on the HTTP / 2 server. To support both, you need to use the HTTP2 compatibility API .
A hang occurs when an HTTP1 client connects to an HTTP / 2 server using allowHTTP1: true , but does not process an HTTP / 1 request.
Examples are based on Node sample documentation code .
HTTP / 1 and / 2 mixed server
const http2 = require('http2') const fs = require('fs') var options = { key: fs.readFileSync('server-key.pem'), cert: fs.readFileSync('server-crt.pem'), //ca: fs.readFileSync('ca-crt.pem'), allowHTTP1: true, } var server = http2.createSecureServer(options, (req, res) => { // detects if it is a HTTPS request or HTTP/2 const { socket: { alpnProtocol } } = (req.httpVersion === '2.0') ? req.stream.session : req res.writeHead(200, { 'content-type': 'application/json' }) res.end(JSON.stringify({ alpnProtocol, httpVersion: req.httpVersion })) }) server.listen(8443)
HTTP / 2 Client
const http2 = require('http2') const fs = require('fs') const client = http2.connect('https://localhost:8443', { ca: fs.readFileSync('ca-crt.pem'), rejectUnauthorized: true, }) client.on('socketError', (err) => console.error(err)) client.on('error', (err) => console.error(err)) const req = client.request({ ':path': '/' }) req.on('response', (headers, flags) => { for (const name in headers) { console.log('Header: "%s" "%s"', name, headers[name]) } }) req.setEncoding('utf8') let data = '' req.on('data', chunk => data += chunk) req.on('end', () => { console.log('Data:', data) client.destroy() }) req.end()
Then runs:
โ node http2_client.js (node:34542) ExperimentalWarning: The http2 module is an experimental API. Header: ":status" "200" Header: "content-type" "application/json" Header: "date" "Sat, 02 Dec 2017 23:27:21 GMT" Data: {"alpnProtocol":"h2","httpVersion":"2.0"}
HTTP / 1 Client
const https = require('https') const fs = require('fs') var options = { method: 'GET', hostname: 'localhost', port: '8443', path: '/', protocol: 'https:', ca: fs.readFileSync('ca-crt.pem'), rejectUnauthorized: true, //agent: false } var req = https.request(options, function(res){ var body = '' res.setEncoding('utf8') res.on('data', data => body += data) res.on('end', ()=> console.log('Body:', body)) }) req.on('response', response => { for (const name in response.headers) { console.log('Header: "%s" "%s"', name, response.headers[name]) } }) req.end()
Then run
โ node http1_client.js Header: "content-type" "application/json" Header: "date" "Sat, 02 Dec 2017 23:27:08 GMT" Header: "connection" "close" Header: "transfer-encoding" "chunked" Body: {"alpnProtocol":false,"httpVersion":"1.1"}
HTTP / 2 server
Using a simple HTTP / 2 server will work with http2_client , but it will hang for http1_client . The TLS connection to the HTTP / 1 client will close when allowHTTP1: true removed.
const http2 = require('http2') const fs = require('fs') var options = { key: fs.readFileSync('server-key.pem'), cert: fs.readFileSync('server-crt.pem'), ca: fs.readFileSync('ca-crt.pem'), allowHTTP1: true, } var server = http2.createSecureServer(options) server.on('error', error => console.log(error)) server.on('connect', conn => console.log('connect', conn)) server.on('socketError', error => console.log('socketError', error)) server.on('frameError', error => console.log('frameError', error)) server.on('remoteSettings', settings => console.log('remote settings', settings)) server.on('stream', (stream, headers) => { console.log('stream', headers) stream.respond({ 'content-type': 'application/html', ':status': 200 }) console.log(stream.session) stream.end(JSON.stringify({ alpnProtocol: stream.session.socket.alpnProtocol, httpVersion: "2" })) }) server.listen(8443)
Certs
With the advanced configuration of the intermediate certificate, which is described in detail in essence, a complete certificate chain for the CA must be provided to clients.
cat ca/x/certs/x.public.pem > caxy.pem cat ca/y/certs/y.public.pem >> caxy.pem
Then in clients use this ca in parameters.
{ ca: fs.readFileSync('caxy.pem'), }
These examples were performed using the following simple CA installation from this circle.com article :
To simplify the configuration, let's capture the following CA configuration file.
wget https:
Next, create a new certificate authority using this configuration.
openssl req -new -x509 \ -days 9999 \ -config ca.cnf \ -keyout ca-key.pem \ -out ca-crt.pem
Now that we have our certificate authority in ca-key.pem and ca-crt.pem, it allows us to generate a private key for the server.
openssl genrsa \ -out server-key.pem \ 4096
The next step is to create a certificate signing request. Again to simplify the configuration, allows you to use server.cnf as a configuration shortcut.
wget https:
Now generate the certificate signing request well.
openssl req -new \ -config server.cnf \ -key server-key.pem \ -out server-csr.pem
Now let's sign the request.
openssl x509 -req -extfile server.cnf \ -days 999 \ -passin "pass:password" \ -in server-csr.pem \ -CA ca-crt.pem \ -CAkey ca-key.pem \ -CAcreateserial \ -out server-crt.pem