@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
@@ -2,7 +2,7 @@
2
2
  "author": "Vladimir Barbarosh <vladimir.barbarosh@gmail.com>",
3
3
  "license": "MIT",
4
4
  "name": "@vbarbarosh/node-helpers",
5
- "version": "3.53.0",
5
+ "version": "3.54.0",
6
6
  "description": "A set of helpers for JavaScript/Node.js",
7
7
  "files": [
8
8
  "src"
@@ -4,7 +4,11 @@
4
4
  */
5
5
  function escape_content_disposition(s)
6
6
  {
7
- const out = s.replaceAll('\\', '\\\\').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;