Skip to content

Commit 911eec0

Browse files
committed
http: add sendFile to OutgoingMessage
One of the most common usages of http servers are to response with files. This provides a faster alternative to: stream.pipeline(fs.createReadStream(path, { start, end }), res, callback) PR-URL: #50576
1 parent 2aaa21f commit 911eec0

File tree

1 file changed

+69
-0
lines changed

1 file changed

+69
-0
lines changed

lib/_http_outgoing.js

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1005,6 +1005,75 @@ function connectionCorkNT(conn) {
10051005
conn.uncork();
10061006
}
10071007

1008+
OutgoingMessage.prototype.sendFile = function sendFild (path, optsOrCallback, callback) {
1009+
let start = 0;
1010+
let end = null;
1011+
1012+
if (typeof optsOrCallback === 'function') {
1013+
callback = optsOrCallback;
1014+
opts = null;
1015+
} else if (opts != null) {
1016+
start = opts.start ?? 0;
1017+
end = opts.end;
1018+
}
1019+
1020+
fs.open(path, (err, fd) => {
1021+
if (err) {
1022+
callback(err);
1023+
} else {
1024+
let pos = start;
1025+
let drain = null;
1026+
1027+
const next = (err, bytesRead, buf) => {
1028+
pos += bytesRead;
1029+
1030+
if (err != null && err.code === 'EAGAIN') {
1031+
setImmediate(() => read(buf));
1032+
return;
1033+
} else if (err) {
1034+
// Fallthrough
1035+
} else if (bytesRead === 0) {
1036+
// Fallthrough
1037+
} else if (dst.write(bytesRead < buf.byteLength ? buf.subarray(0, bytesRead) : buf)) {
1038+
// TODO (perf): Use FastBuffer instead of subarray.
1039+
// TODO (perf): Is there a way we can re-use the buffer once we
1040+
// know for sure it's been flushed through the socket?
1041+
// TODO (perf): Avoid creating subarray and use offset parameter in fs.read
1042+
// instead.
1043+
read(bytesRead < buf.byteLength < buf.subarray(bytesRead), null);
1044+
return;
1045+
} else if (dst.destroyed) {
1046+
// Fallthrough
1047+
} else if (drain == null) {
1048+
drain = read;
1049+
dst.on('drain', drain);
1050+
return;
1051+
}
1052+
1053+
if (drain) {
1054+
dst.off('drain', drain);
1055+
}
1056+
1057+
fs.close(fd, nop);
1058+
1059+
callback(err, pos);
1060+
};
1061+
1062+
// TODO (perf): Avoid closure...
1063+
const read = (buf) => {
1064+
const n = end ? Math.min(end - pos, Buffer.poolSize) : Buffer.poolSize;
1065+
if (n <= 0) {
1066+
next(null, 0, null)
1067+
} else {
1068+
fs.read(fd, buf ?? Buffer.allocUnsafe(n), 0, n, pos, next);
1069+
}
1070+
};
1071+
1072+
read();
1073+
}
1074+
})
1075+
}
1076+
10081077
OutgoingMessage.prototype.addTrailers = function addTrailers(headers) {
10091078
this._trailer = '';
10101079
const keys = ObjectKeys(headers);

0 commit comments

Comments
 (0)