Servidor Node.js HTTP2 Error: socket colgar

Dada la última versión de Node.js con soporte experimental HTTP2:

$ node -v v9.2.0 

Un servidor HTTP2:

 var options = { key: getKey(), cert: getCert(), allowHTTP1: true } var server = http2.createSecureServer(options) server.on('stream', onstream) server.on('error', onerror) server.on('connect', onconnect) server.on('socketError', onsocketerror) server.on('frameError', onframeerror) server.on('remoteSettings', onremotesettings) server.listen(8443) function onconnect() { console.log('connect') } function onremotesettings(settings) { console.log('remote settings', settings) } function onframeerror(error) { console.log('frame error', error) } function onsocketerror(error) { console.log('socket error', error) } function onerror(error) { console.log(error) } function onstream(stream, headers) { console.log('stream') } 

Y una petición hecha a ella:

 var https = require('https') var options = { method: 'GET', hostname: 'localhost', port: '8443', path: '/', protocol: 'https:', rejectUnauthorized: false, agent: false } var req = https.request(options, function(res){ var body = '' res.setEncoding('utf8') res.on('data', function(data){ body += data; }); res.on('end', function(){ callback(null, body) }) }) req.end() 

Simplemente cuelga y eventualmente dice:

 Error: socket hang up at createHangUpError (_http_client.js:330:15) at TLSSocket.socketOnEnd (_http_client.js:423:23) at TLSSocket.emit (events.js:164:20) at endReadableNT (_stream_readable.js:1054:12) at _combinedTickCallback (internal/process/next_tick.js:138:11) at process._tickCallback (internal/process/next_tick.js:180:9) 

Si se establece rejectUnauthorized: true produce un error:

 Error: self signed certificate at TLSSocket.onConnectSecure (_tls_wrap.js:1036:34) at TLSSocket.emit (events.js:159:13) at TLSSocket._finishInit (_tls_wrap.js:637:8) 

No estoy seguro de qué está mal y por qué no llegará al punto de la stream de registro.

Si entro en el navegador y visito https: // localhost: 8443 , y hago clic en los mensajes de advertencia, en realidad registra la stream y realiza la solicitud correctamente. Pero no he podido obtener nodo para realizar la solicitud.

Me gustaría tratar esto como un servidor HTTP1, así que no quiero usar el cliente HTTP2 para hacer la solicitud. Pero intenté usar eso y lo mismo.

HTTP / 1 no comparte la misma semántica de solicitud que HTTP / 2, por lo que los clientes HTTP / 1 deben detectarse y manejarse en un servidor HTTP / 2. Para dar soporte a ambos necesitas usar la API de compatibilidad HTTP2 .

El “locking” se produce cuando un cliente HTTP1 se conecta a un servidor HTTP / 2 con allowHTTP1: true set pero no maneja la solicitud HTTP / 1.

Los ejemplos se basan en el código de ejemplo de la documentación del nodo .

HTTP / 1 y / 2 Servidor Mixto

 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) 

Cliente HTTP / 2

 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() 

Entonces corriendo:

 → 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 Cliente

 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() 

Entonces corriendo

 → 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"} 

Servidor HTTP / 2

El uso del servidor HTTP / 2 simple funcionará con http2_client pero se “bloqueará” para un http1_client . La conexión TLS de un cliente HTTP / 1 se cerrará cuando elimine allowHTTP1: true .

 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

Con la configuración del certificado intermedio extendido detallada en la lista, se debe proporcionar a los clientes la cadena completa de certificados para la CA.

 cat ca/x/certs/x.public.pem > caxy.pem cat ca/y/certs/y.public.pem >> caxy.pem 

Luego en los clientes usa esta ca en las opciones.

 { ca: fs.readFileSync('caxy.pem'), } 

Estos ejemplos se ejecutaron con la siguiente configuración simple de CA de este artículo de circle.com :

Para simplificar la configuración, tomemos el siguiente archivo de configuración de CA.

 wget https://raw.githubusercontent.com/anders94/https-authorized-clients/master/keys/ca.cnf 

A continuación, crearemos una nueva autoridad de certificación utilizando esta configuración.

 openssl req -new -x509 \ -days 9999 \ -config ca.cnf \ -keyout ca-key.pem \ -out ca-crt.pem 

Ahora que tenemos nuestra autoridad de certificación en ca-key.pem y ca-crt.pem, generemos una clave privada para el servidor.

 openssl genrsa \ -out server-key.pem \ 4096 

Nuestro siguiente paso es generar una solicitud de firma de certificado. Nuevamente para simplificar la configuración, usemos server.cnf como acceso directo de configuración.

 wget https://raw.githubusercontent.com/anders94/https-authorized-clients/master/keys/server.cnf 

Ahora generaremos la solicitud de firma de certificado.

 openssl req -new \ -config server.cnf \ -key server-key.pem \ -out server-csr.pem 

Ahora firmemos la solicitud.

 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