简体   繁体   English

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

[英]Node.js HTTP2 server Error: socket hang up

Given the latest version of Node.js with experimental HTTP2 support: 鉴于最新版本的Node.js具有实验性HTTP2支持:

$ node -v
v9.2.0

An HTTP2 server: 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')
}

And a request made to it: 并提出要求:

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

It just hangs and eventually says: 它只是挂起并最终说:

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)

If rejectUnauthorized: true is set, then it errors: 如果设置了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)

Not sure what is going wrong and why it won't get to the point of logging stream . 不确定出了什么问题以及为什么它不会达到记录stream

If I go in the browser and visit https://localhost:8443 , and click through the warning messages, it does actually log stream and successfully make the request. 如果我进入浏览器并访问https:// localhost:8443 ,并单击警告消息,它实际上会记录stream并成功发出请求。 But haven't been able to get node to make the request. 但是还没有得到节点来发出请求。

I would like to treat this as an HTTP1 server, so don't want to use the HTTP2 client to make the request. 我想将此视为HTTP1服务器,因此不要使用HTTP2客户端来发出请求。 But tried using that and same thing. 但尝试使用同样的东西。

HTTP/1 doesn't share the same request semantics as HTTP/2 so HTTP/1 clients need to be detected and handled in a HTTP/2 server. HTTP / 1不与HTTP / 2共享相同的请求语义,因此需要在HTTP / 2服务器中检测和处理HTTP / 1客户端。 To support both you need to use the HTTP2 Compatibility API . 要同时支持,您需要使用HTTP2兼容性API

The "hang" occurs when a HTTP1 client connects to a HTTP/2 server with allowHTTP1: true set but doesn't handle the HTTP/1 request. 当HTTP1客户端使用allowHTTP1: true设置连接到HTTP / 2服务器但不处理HTTP / 1请求时,会发生“挂起”。

The examples are based on the Node documentation example code . 这些示例基于Node文档示例代码

HTTP/1 and /2 Mixed Server 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 Client 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()

Then running: 然后运行:

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

Then running 然后跑

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

Using the plain HTTP/2 Server will work with the http2_client but will "hang" for a http1_client . 使用普通的HTTP / 2服务器将与合作http2_client但会“挂”了http1_client The TLS connection from a HTTP/1 client will be closed when you remove allowHTTP1: true . 删除allowHTTP1: true时,将关闭来自HTTP / 1客户端的TLS连接。

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 extended intermediate certificate setup detailed in the gist, the complete certificate chain for the CA needs to be supplied to the clients. 通过要点中详述的扩展中间证书设置,需要将完整的CA证书链提供给客户端。

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

Then in the clients use this ca in the options. 然后在客户端使用该ca的选项。

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

These examples were run withe the following simple CA setup from this circle.com article : 这些示例是使用此circle.com文章中的以下简单CA设置运行的:

To simplify the configuration, let's grab the following CA configuration file. 为了简化配置,让我们获取以下CA配置文件。

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

Next, we'll 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, let's generate a private key for the server. 现在我们在ca-key.pem和ca-crt.pem中拥有了我们的证书权限,让我们为服务器生成一个私钥。

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

Our next move is to generate a certificate signing request. 我们的下一步是生成证书签名请求。 Again to simplify configuration, let's use server.cnf as a configuration shortcut. 再次简化配置,让我们使用server.cnf作为配置快捷方式。

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

Now we'll generate the certificate signing request. 现在我们将生成证书签名请求。

 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 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM