Node.js HTTP2服务器错误:套接字挂断

鉴于具有实验性HTTP2支持的最新版本的Node.js:

$ node -v v9.2.0 

一个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') } 

并提出要求:

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

它只是挂起,最终说:

 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) 

如果rejectUnauthorized: true被设置,那么错误:

 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) 

不知道什么是错的,为什么它不会到达日志stream的点。

如果我在浏览器中访问并访问https:// localhost:8443 ,并单击警告消息,它实际上会loggingstream并成功请求。 但是还没有能够得到节点发出的请求。

我想把它当作一个HTTP1服务器,所以不想用HTTP2客户端发出请求。 但尝试使用同样的事情。

HTTP / 1不共享与HTTP / 2相同的请求语义,因此需要在HTTP / 2服务器中检测和处理HTTP / 1客户端。 要同时支持您需要使用HTTP2兼容性API 。

HTTP1客户端通过allowHTTP1: true设置连接到HTTP / 2服务器,但不处理HTTP / 1请求时,会发生“挂起”。

这些示例基于Node文档示例代码 。

HTTP / 1和/ 2混合服务器

 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客户端

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

然后运行:

 → 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客户端

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

然后运行

 → 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服务器

使用纯HTTP / 2服务器将与http2_client一起工作,但会挂起http1_client 。 当您删除allowHTTP1: true时,来自HTTP / 1客户端的TLS连接将被closures。

 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) 

证书

在要点详述的扩展中间证书设置的情况下,CA的完整证书链需要被提供给客户端。

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

然后在客户端使用这个ca的选项。

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

这些示例是通过以下circle.com文章中简单的CA设置运行的:

为了简化configuration,我们抓住以下CAconfiguration文件。

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

接下来,我们将使用此configuration创build一个新的证书颁发机构。

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

现在我们在ca-key.pem和ca-crt.pem中拥有了我们的证书权限,让我们为服务器生成一个私钥。

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

我们的下一步是生成一个证书签名请求。 再次为了简化configuration,我们使用server.cnf作为configuration快捷方式。

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

现在我们将生成证书签名请求。

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

现在让我们签署请求。

 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