Skip to content

Commit f85f263

Browse files
fix(vfs): defer stream creation in vfs requests (#80)
Defers the creation of streams in the VFS process to avoid any cleanups that ends up in exceptions because they are handled outside the scope/context. Fixes #79
1 parent f1c684a commit f85f263

File tree

1 file changed

+31
-32
lines changed

1 file changed

+31
-32
lines changed

src/vfs.js

Lines changed: 31 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,10 @@ const {
4343

4444
const respondNumber = result => typeof result === 'number' ? result : -1;
4545
const respondBoolean = result => typeof result === 'boolean' ? result : !!result;
46-
const requestPath = req => ([sanitize(req.fields.path)]);
47-
const requestSearch = req => ([sanitize(req.fields.root), req.fields.pattern]);
48-
const requestCross = req => ([sanitize(req.fields.from), sanitize(req.fields.to)]);
49-
const requestFile = req => ([sanitize(req.fields.path), streamFromRequest(req)]);
46+
const requestPath = req => [sanitize(req.fields.path)];
47+
const requestSearch = req => [sanitize(req.fields.root), req.fields.pattern];
48+
const requestFile = req => [sanitize(req.fields.path), () => streamFromRequest(req)];
49+
const requestCross = req => [sanitize(req.fields.from), sanitize(req.fields.to)];
5050

5151
/*
5252
* Parses the range request headers
@@ -65,7 +65,10 @@ const onDone = (req, res) => {
6565
if (req.files) {
6666
for (let fieldname in req.files) {
6767
try {
68-
fs.removeSync(req.files[fieldname].path);
68+
const n = req.files[fieldname].path;
69+
if (fs.existsSync(n)) {
70+
fs.removeSync(n);
71+
}
6972
} catch (e) {
7073
console.warn('Failed to unlink temporary file', e);
7174
}
@@ -77,26 +80,18 @@ const onDone = (req, res) => {
7780
* Wraps a vfs adapter request
7881
*/
7982
const wrapper = fn => (req, res, next) => fn(req, res)
80-
.then(result => {
83+
.then(result => new Promise((resolve, reject) => {
8184
if (result instanceof Stream) {
82-
result.on('error', error => {
83-
next(error);
84-
});
85-
86-
result.on('end', () => {
87-
onDone(req, res);
88-
});
89-
85+
result.once('error', reject);
86+
result.once('end', resolve);
9087
result.pipe(res);
9188
} else {
9289
res.json(result);
93-
onDone(req, res);
90+
resolve();
9491
}
95-
})
96-
.catch(error => {
97-
next(error);
98-
onDone(req, res);
99-
});
92+
}))
93+
.catch(error => next(error))
94+
.finally(() => onDone(req, res));
10095

10196
/**
10297
* Creates the middleware
@@ -147,27 +142,31 @@ const createOptions = req => {
147142
// Standard request with only a target
148143
const createRequestFactory = findMountpoint => (getter, method, readOnly, respond) => async (req, res) => {
149144
const options = createOptions(req);
150-
const args = [...getter(req, res), options];
145+
const [target, ...rest] = getter(req, res);
146+
147+
const found = await findMountpoint(target);
148+
const attributes = found.mount.attributes || {};
149+
const strict = attributes.strictGroups !== false;
151150

152-
const found = await findMountpoint(args[0]);
153151
if (method === 'search') {
154-
if (found.mount.attributes && found.mount.attributes.searchable === false) {
152+
if (attributes.searchable === false) {
155153
return [];
156154
}
157155
}
158156

159-
const {attributes} = found.mount;
160-
const strict = attributes.strictGroups !== false;
161-
const ranges = (!attributes.adapter || attributes.adapter === 'system') || attributes.ranges === true;
162-
const vfsMethodWrapper = m => found.adapter[m]
163-
? found.adapter[m](found)(...args)
164-
: Promise.reject(new Error(`Adapter does not support ${m}`));
165-
const readstat = () => vfsMethodWrapper('stat').catch(() => ({}));
166157
await checkMountpointPermission(req, res, method, readOnly, strict)(found);
167158

159+
const vfsMethodWrapper = m => {
160+
const args = [target, ...rest.map(r => typeof r === 'function' ? r() : r), options];
161+
return found.adapter[m]
162+
? found.adapter[m](found)(...args)
163+
: Promise.reject(new Error(`Adapter does not support ${m}`));
164+
};
165+
168166
const result = await vfsMethodWrapper(method);
169167
if (method === 'readfile') {
170-
const stat = await readstat();
168+
const ranges = (!attributes.adapter || attributes.adapter === 'system') || attributes.ranges === true;
169+
const stat = await vfsMethodWrapper('stat').catch(() => ({}));
171170

172171
if (ranges && options.range) {
173172
try {
@@ -192,7 +191,7 @@ const createRequestFactory = findMountpoint => (getter, method, readOnly, respon
192191
}
193192

194193
if (options.download) {
195-
const filename = encodeURIComponent(path.basename(args[0]));
194+
const filename = encodeURIComponent(path.basename(target));
196195
res.append('Content-Disposition', `attachment; filename*=utf-8''${filename}`);
197196
}
198197
}

0 commit comments

Comments
 (0)