@vbarbarosh/node-helpers 3.53.0 → 3.54.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -4,7 +4,11 @@
|
|
|
4
4
|
*/
|
|
5
5
|
function escape_content_disposition(s)
|
|
6
6
|
{
|
|
7
|
-
const out = s.replaceAll('\\', '\\\\')
|
|
7
|
+
const out = s.replaceAll('\\', '\\\\')
|
|
8
|
+
.replaceAll('"', '\\"')
|
|
9
|
+
.replaceAll('%', '\\%')
|
|
10
|
+
.replaceAll('\n', '%x0A')
|
|
11
|
+
.replaceAll('\0', '%x00');
|
|
8
12
|
if (out.match(/\s|;/)) {
|
|
9
13
|
return `"${out}"`;
|
|
10
14
|
}
|
|
@@ -5,7 +5,10 @@ const items = [
|
|
|
5
5
|
['foo.bar', 'foo.bar'],
|
|
6
6
|
['foo"bar', 'foo\\"bar'],
|
|
7
7
|
['foo;bar', '"foo;bar"'],
|
|
8
|
+
['foo%bar', 'foo\\%bar'],
|
|
8
9
|
['foo\\bar', 'foo\\\\bar'],
|
|
10
|
+
['foo\nbar', 'foo%x0Abar'],
|
|
11
|
+
['foo\0bar', 'foo%x00bar'],
|
|
9
12
|
['foo bar', '"foo bar"'],
|
|
10
13
|
['foo "bar".txt', '"foo \\"bar\\".txt"'],
|
|
11
14
|
];
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
const escape_content_disposition = require('./escape_content_disposition');
|
|
2
|
+
const fs_fclose = require('./fs_fclose');
|
|
3
|
+
const fs_fopen = require('./fs_fopen');
|
|
4
|
+
const fs_fread = require('./fs_fread');
|
|
5
|
+
const fs_path_basename = require('./fs_path_basename');
|
|
6
|
+
const fs_size = require('./fs_size');
|
|
7
|
+
const http_range_parse = require('./http_range_parse');
|
|
8
|
+
|
|
9
|
+
// https://stackoverflow.com/questions/63649387/get-vs-head-methods-in-express-example#comment120040338_63649698
|
|
10
|
+
// > Note: the app.get() function is automatically called for the HTTP HEAD
|
|
11
|
+
// > method in addition to the GET method if app.head() was not called for the
|
|
12
|
+
// > path before app.get().
|
|
13
|
+
//
|
|
14
|
+
// https://expressjs.com/en/api.html#router.METHOD
|
|
15
|
+
// > The router.get() function is automatically called for the HTTP HEAD method
|
|
16
|
+
// > in addition to the GET method if router.head() was not called for the path
|
|
17
|
+
// > before router.get().
|
|
18
|
+
//
|
|
19
|
+
// https://www.npmjs.com/package/send
|
|
20
|
+
|
|
21
|
+
async function http_stream_range(req, res, file)
|
|
22
|
+
{
|
|
23
|
+
// req.log(`[http_stream_range_begin] ${JSON.stringify(req.headers)}`);
|
|
24
|
+
|
|
25
|
+
const total = await fs_size(file);
|
|
26
|
+
|
|
27
|
+
if (req.method === 'HEAD') {
|
|
28
|
+
res.header('Content-Length', total);
|
|
29
|
+
res.header('Content-Disposition', `inline; filename=${escape_content_disposition(fs_path_basename(file))};`);
|
|
30
|
+
res.status(200);
|
|
31
|
+
res.end();
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (req.method !== 'GET') {
|
|
36
|
+
res.status(405).end();
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const fp = await fs_fopen(file);
|
|
41
|
+
const buf = Buffer.alloc(2*1024*1024);
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
let req_close = false;
|
|
45
|
+
req.once('close', () => req_close = true);
|
|
46
|
+
|
|
47
|
+
let first, last;
|
|
48
|
+
|
|
49
|
+
// bytes=0-
|
|
50
|
+
// bytes=-100
|
|
51
|
+
if (req.headers.range) {
|
|
52
|
+
const range = http_range_parse(req.headers.range, total);
|
|
53
|
+
first = range.first;
|
|
54
|
+
last = range.last;
|
|
55
|
+
// req.log(`[http_stream_range_range] ${JSON.stringify({range: req.headers.range, first, last, total, orig: req.headers.range})}`);
|
|
56
|
+
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Range
|
|
57
|
+
res.status(206);
|
|
58
|
+
res.header('Content-Range', `bytes ${first}-${last}/${total}`);
|
|
59
|
+
res.header('Content-Length', last - first + 1);
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
first = 0;
|
|
63
|
+
last = total - 1;
|
|
64
|
+
res.header('Content-Length', last - first + 1);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
let total_read = 0;
|
|
68
|
+
for (let offset = first; offset <= last && !req_close; ) {
|
|
69
|
+
const chunk = await fs_fread(fp, buf, offset, Math.min(buf.length, last - offset + 1));
|
|
70
|
+
offset += chunk.length;
|
|
71
|
+
total_read += chunk.length;
|
|
72
|
+
if (res.write(chunk)) {
|
|
73
|
+
// req.log(`[http_stream_range_write] ${chunk.length}`);
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
await new Promise(function (resolve) {
|
|
77
|
+
res.on('drain', drain);
|
|
78
|
+
res.on('close', close);
|
|
79
|
+
function drain() {
|
|
80
|
+
// req.log('[http_stream_range_drain]');
|
|
81
|
+
res.off('drain', drain);
|
|
82
|
+
res.off('close', close);
|
|
83
|
+
resolve();
|
|
84
|
+
}
|
|
85
|
+
function close() {
|
|
86
|
+
// req.log('[http_stream_range_close]');
|
|
87
|
+
res.off('drain', drain);
|
|
88
|
+
res.off('close', close);
|
|
89
|
+
resolve();
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// req.log(`[http_stream_range_end_ok] ${JSON.stringify({req_close, total_read})}`);
|
|
95
|
+
res.end();
|
|
96
|
+
}
|
|
97
|
+
finally {
|
|
98
|
+
await fs_fclose(fp);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
module.exports = http_stream_range;
|