如何管道从标准输入到一个http请求在NodeJS?



我有一个http服务器监听端口9090 -管道请求标准输出,像这样:

let server  = http.createServer((req, res) => {req.pipe(process.stdout)})
server.listen(9090)

当我发送一些带curl的东西,像这样:

curl -XGET -T - 'http://localhost:9090' < /tmp/text-input

它工作了,我在服务器的终端上看到了输出

,但当我尝试以下节点:

const http = require('http')
const nurl = new URL("http://localhost:9090")
let request = http.request(nurl)
request.on('response', (res) => {
process.stdin.pipe(request)
})
request.end() // If I emit this, nothing happens. If I keep this, I get the below error

并尝试像这样运行它:node request.js < /tmp/text-input,我得到以下错误:

node:events:368
throw er; // Unhandled 'error' event
^
Error [ERR_STREAM_WRITE_AFTER_END]: write after end
at new NodeError (node:internal/errors:371:5)
at write_ (node:_http_outgoing:748:11)
at ClientRequest.write (node:_http_outgoing:707:15)
at ClientRequest.<anonymous> (/home/tomk/workspace/js-playground/http.js:17:7)
at ClientRequest.emit (node:events:390:28)
at HTTPParser.parserOnIncomingClient (node:_http_client:623:27)
at HTTPParser.parserOnHeadersComplete (node:_http_common:128:17)
at Socket.socketOnData (node:_http_client:487:22)
at Socket.emit (node:events:390:28)
at addChunk (node:internal/streams/readable:324:12)
Emitted 'error' event on ClientRequest instance at:
at emitErrorNt (node:_http_outgoing:726:9)
at processTicksAndRejections (node:internal/process/task_queues:84:21) {
code: 'ERR_STREAM_WRITE_AFTER_END'
}

我想管道我的stdin到http服务器相同的方式,我可以与curl -T -。我的请求代码有什么问题?

简短回答

在node中发送分块编码消息,使用POST方法:

let request = http.request(url, { method: 'POST' })
process.stdin.pipe(request)

编辑:更直接的方法

或者,发送任何带有分块编码的请求方法:

let request = http.request(url)
request.setHeader("transfer-encoding", "chunked")
request.flushHeaders()
process.stdin.pipe(request)

稍长(但不完整)的答案

我打开了一个监听netcat(监听普通tcp),如nc -l 9090,以查看curl的请求如何与我的代码不同,并在头中发现了一些关键的差异。

在curl中,出现了标题Transfer-Encoding: chunked,但在我的代码发出的请求中缺少。此外,我的代码有一个标题Connection: closed

我记录了请求对象,发现useChunkedEncodingByDefault被设置为false,这是令人困惑的从nodejs http文档的报价:

发送'Content-Length'报头将禁用默认的分块编码。

暗示它应该是默认值

但是后来我在节点

的源代码中发现了这个
if (method === 'GET' ||
method === 'HEAD' ||
method === 'DELETE' ||
method === 'OPTIONS' ||
method === 'TRACE' ||
method === 'CONNECT') {
this.useChunkedEncodingByDefault = false;
} else {
this.useChunkedEncodingByDefault = true;
}

编辑

无论如何要发送分块编码,我(最终)发现我需要显式地添加Transfer-Encoding: Chunked头:

request.setHeader("transfer-encoding", "chunked")
# and then
request.flushHeaders()

所以,综上所述,节点不允许发送发送默认GET请求与分块编码,但curl。奇怪的是,不幸的是没有文档(据我所知),但重要的是我让它工作