如何从 NodeJS 中的缓冲区流式传输范围 http 请求
How to stream range http requests from buffers in NodeJS
当我们从
流式传输时,在节点中测试 http 范围请求
fs.createReadStream('index.html').pipe(res)
到http服务器的httpres
,来自节点的http客户端接受它。
但是,当我通过管道传输缓冲区流时:
const content = fs.readFileSync("src/index.html");
const stream = new Readable();
stream.push(
opts.start && opts.end
? content.slice(opts.start, opts.end + 1)
: content
);
stream.push(null);
stream.pipe(res);
Curl 和浏览器接受它,但 NodeJS http
客户端除外,它抛出:
events.js:180
throw er; // Unhandled 'error' event
^
Error: Parse Error
at Socket.socketOnData (_http_client.js:452:22)
at Socket.emit (events.js:203:13)
at addChunk (_stream_readable.js:294:12)
at readableAddChunk (_stream_readable.js:275:11)
at Socket.Readable.push (_stream_readable.js:210:10)
at TCP.onStreamRead (internal/stream_base_commons.js:166:17)
Emitted 'error' event at:
at Socket.socketOnData (_http_client.js:458:9)
at Socket.emit (events.js:203:13)
[... lines matching original stack trace ...]
at TCP.onStreamRead (internal/stream_base_commons.js:166:17) {
bytesParsed: 234,
code: 'HPE_INVALID_CONSTANT',
reason: 'Expected HTTP/'
}
要测试它,只需将第 56 行从 'read'
更改为 'buffer'
:
index.js
const http = require("http");
const fs = require("fs");
const Readable = require("stream").Readable;
const stats = fs.statSync("index.html");
const handler = function(read_or_buffer) {
return function(req, res) {
let code = 200;
const opts = {};
const headers = {
"Content-Length": stats.size,
"Content-Type": "text/html",
"Last-Modified": stats.mtime.toUTCString()
};
if (req.headers.range) {
code = 206;
let [x, y] = req.headers.range.replace("bytes=", "").split("-");
let end = (opts.end = parseInt(y, 10) || stats.size - 1);
let start = (opts.start = parseInt(x, 10) || 0);
if (start >= stats.size || end >= stats.size) {
res.setHeader("Content-Range", `bytes */${stats.size}`);
res.statusCode = 416;
return res.end();
}
headers["Content-Range"] = `bytes ${start}-${end}/${stats.size}`;
headers["Content-Length"] = end - start + 1;
headers["Accept-Ranges"] = "bytes";
}
res.writeHead(code, headers);
if (read_or_buffer == "read")
fs.createReadStream("index.html", opts).pipe(res);
if (read_or_buffer == "buffer") {
const content = fs.readFileSync("index.html");
const stream = new Readable();
stream.push(
opts.start && opts.end
? content.slice(opts.start, opts.end + 1)
: content
);
stream.push(null);
stream.pipe(res);
}
};
};
http.createServer(handler("read")).listen(8080);
// TESTS
const options = { headers: { Range: "bytes=0-4" } };
http.get("http://127.0.0.1:8080/", options, response => {
let data = "";
response.on("data", chunk => (data += chunk));
response.on("end", () => {
console.log(data);
process.exit();
});
});
index.html
Hello world!
当服务器收到从0
开始的http范围请求时:
opts.start && opts.end
计算结果为 false,因为 opts.start
是 0
,所以代码发送的是整个缓冲区,而不是预期的切片。
由于 NodeJS 严格符合 HTTP 规范,因此未接受请求。
解决方案是验证,当 opts.start
存在时,它是否是 zero
:
opts.start || opts.start === 0 && opts.end
当我们从
流式传输时,在节点中测试 http 范围请求fs.createReadStream('index.html').pipe(res)
到http服务器的httpres
,来自节点的http客户端接受它。
但是,当我通过管道传输缓冲区流时:
const content = fs.readFileSync("src/index.html");
const stream = new Readable();
stream.push(
opts.start && opts.end
? content.slice(opts.start, opts.end + 1)
: content
);
stream.push(null);
stream.pipe(res);
Curl 和浏览器接受它,但 NodeJS http
客户端除外,它抛出:
events.js:180
throw er; // Unhandled 'error' event
^
Error: Parse Error
at Socket.socketOnData (_http_client.js:452:22)
at Socket.emit (events.js:203:13)
at addChunk (_stream_readable.js:294:12)
at readableAddChunk (_stream_readable.js:275:11)
at Socket.Readable.push (_stream_readable.js:210:10)
at TCP.onStreamRead (internal/stream_base_commons.js:166:17)
Emitted 'error' event at:
at Socket.socketOnData (_http_client.js:458:9)
at Socket.emit (events.js:203:13)
[... lines matching original stack trace ...]
at TCP.onStreamRead (internal/stream_base_commons.js:166:17) {
bytesParsed: 234,
code: 'HPE_INVALID_CONSTANT',
reason: 'Expected HTTP/'
}
要测试它,只需将第 56 行从 'read'
更改为 'buffer'
:
index.js
const http = require("http");
const fs = require("fs");
const Readable = require("stream").Readable;
const stats = fs.statSync("index.html");
const handler = function(read_or_buffer) {
return function(req, res) {
let code = 200;
const opts = {};
const headers = {
"Content-Length": stats.size,
"Content-Type": "text/html",
"Last-Modified": stats.mtime.toUTCString()
};
if (req.headers.range) {
code = 206;
let [x, y] = req.headers.range.replace("bytes=", "").split("-");
let end = (opts.end = parseInt(y, 10) || stats.size - 1);
let start = (opts.start = parseInt(x, 10) || 0);
if (start >= stats.size || end >= stats.size) {
res.setHeader("Content-Range", `bytes */${stats.size}`);
res.statusCode = 416;
return res.end();
}
headers["Content-Range"] = `bytes ${start}-${end}/${stats.size}`;
headers["Content-Length"] = end - start + 1;
headers["Accept-Ranges"] = "bytes";
}
res.writeHead(code, headers);
if (read_or_buffer == "read")
fs.createReadStream("index.html", opts).pipe(res);
if (read_or_buffer == "buffer") {
const content = fs.readFileSync("index.html");
const stream = new Readable();
stream.push(
opts.start && opts.end
? content.slice(opts.start, opts.end + 1)
: content
);
stream.push(null);
stream.pipe(res);
}
};
};
http.createServer(handler("read")).listen(8080);
// TESTS
const options = { headers: { Range: "bytes=0-4" } };
http.get("http://127.0.0.1:8080/", options, response => {
let data = "";
response.on("data", chunk => (data += chunk));
response.on("end", () => {
console.log(data);
process.exit();
});
});
index.html
Hello world!
当服务器收到从0
开始的http范围请求时:
opts.start && opts.end
计算结果为 false,因为 opts.start
是 0
,所以代码发送的是整个缓冲区,而不是预期的切片。
由于 NodeJS 严格符合 HTTP 规范,因此未接受请求。
解决方案是验证,当 opts.start
存在时,它是否是 zero
:
opts.start || opts.start === 0 && opts.end