Skip to content

Commit 63b7330

Browse files
mitjapMurderlon
andauthoredOct 3, 2022
Fix validation of upload-length header in patch handler (#303)
Co-authored-by: Merlijn Vos <merlijn@soverin.net>
1 parent 2e89200 commit 63b7330

8 files changed

+35
-38
lines changed
 

‎lib/constants.js

+4
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,10 @@ const ERRORS = {
6464
status_code: 501,
6565
body: 'Concatenation extension is not (yet) supported. Disable parallel uploads in the tus client.\n',
6666
},
67+
UNSUPPORTED_CREATION_DEFER_LENGTH_EXTENSION: {
68+
status_code: 501,
69+
body: 'creation-defer-length extension is not (yet) supported.\n',
70+
},
6771
};
6872

6973
const EVENT_ENDPOINT_CREATED = 'EVENT_ENDPOINT_CREATED';

‎lib/handlers/PatchHandler.js

+18-19
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,6 @@ class PatchHandler extends BaseHandler {
3232
throw ERRORS.INVALID_CONTENT_TYPE;
3333
}
3434

35-
// The request MUST validate upload-length related headers
36-
const upload_length = req.headers['upload-length'];
37-
const upload_defer_length = req.headers['upload-defer-length'];
38-
39-
if ((upload_length === undefined) === (upload_defer_length === undefined)) {
40-
throw ERRORS.INVALID_LENGTH;
41-
}
42-
4335
const file = await this.store.getOffset(file_id);
4436

4537
if (file.size !== offset) {
@@ -48,22 +40,29 @@ class PatchHandler extends BaseHandler {
4840
throw ERRORS.INVALID_OFFSET;
4941
}
5042

51-
// Throw error is upload-length header does not match current upload-length if it is set.
52-
if (upload_length !== undefined && file.upload_length !== undefined && upload_length !== file.upload_length) {
53-
throw ERRORS.INVALID_LENGTH;
54-
}
55-
// Throw error is upload-defer-length header is present while upload-length is already known
56-
else if (upload_defer_length !== undefined && file.upload_length !== undefined) {
57-
throw ERRORS.INVALID_LENGTH;
58-
}
43+
// The request MUST validate upload-length related headers
44+
const upload_length = req.headers['upload-length'];
45+
if (upload_length !== undefined) {
46+
// Throw error if extension is not supported
47+
if (!this.store.hasExtension('creation-defer-length')) {
48+
throw ERRORS.UNSUPPORTED_CREATION_DEFER_LENGTH_EXTENSION;
49+
}
50+
51+
// Throw error if upload-length is already set.
52+
if (file.upload_length !== undefined) {
53+
throw ERRORS.INVALID_LENGTH;
54+
}
55+
56+
if (parseInt(upload_length, 10) < file.size) {
57+
throw ERRORS.INVALID_LENGTH;
58+
}
5959

60-
// Declare upload-length if it is not already known
61-
if (upload_length !== undefined && file.upload_length === undefined) {
6260
await this.store.declareUploadLength(file_id, upload_length);
61+
file.upload_length = upload_length;
6362
}
6463

6564
const new_offset = await this.store.write(req, file_id, offset);
66-
if (new_offset === parseInt(upload_length, 10)) {
65+
if (new_offset === parseInt(file.upload_length, 10)) {
6766
this.emit(EVENTS.EVENT_UPLOAD_COMPLETE, { file: new File(file_id, file.upload_length, file.upload_defer_length, file.upload_metadata) });
6867
}
6968

‎lib/handlers/PostHandler.js

+7
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,13 @@ class PostHandler extends BaseHandler {
3737
const upload_defer_length = req.headers['upload-defer-length'];
3838
const upload_metadata = req.headers['upload-metadata'];
3939

40+
if (upload_defer_length !== undefined) {
41+
// Throw error if extension is not supported
42+
if (!this.store.hasExtension('creation-defer-length')) {
43+
throw ERRORS.UNSUPPORTED_CREATION_DEFER_LENGTH_EXTENSION;
44+
}
45+
}
46+
4047
if ((upload_length === undefined) === (upload_defer_length === undefined)) {
4148
throw ERRORS.INVALID_LENGTH;
4249
}

‎lib/stores/DataStore.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ class DataStore extends EventEmitter {
8585
* Called in PATCH requests when upload length is known after being defered.
8686
*
8787
* @param {string} file_id Name of file
88-
* @param {number} upload_length Declared upload length
88+
* @param {string} upload_length Declared upload length
8989
* @return {Promise}
9090
*/
9191
async declareUploadLength(file_id, upload_length) {

‎test/Test-EndToEnd.js

-2
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,6 @@ describe('EndToEnd', () => {
219219
const write_stream = agent.patch(`${STORE_PATH}/${file_id}`)
220220
.set('Tus-Resumable', TUS_RESUMABLE)
221221
.set('Upload-Offset', 0)
222-
.set('Upload-Length', TEST_FILE_SIZE)
223222
.set('Content-Type', 'application/offset+octet-stream');
224223

225224
write_stream.on('response', (res) => {
@@ -471,7 +470,6 @@ describe('EndToEnd', () => {
471470
const write_stream = agent.patch(`${STORE_PATH}/${file_id}`)
472471
.set('Tus-Resumable', TUS_RESUMABLE)
473472
.set('Upload-Offset', 0)
474-
.set('Upload-Length', `${TEST_FILE_SIZE}`)
475473
.set('Content-Type', 'application/offset+octet-stream');
476474

477475
write_stream.on('response', (res) => {

‎test/Test-PatchHandler.js

+3-15
Original file line numberDiff line numberDiff line change
@@ -65,19 +65,6 @@ describe('PatchHandler', () => {
6565
return assert.rejects(() => handler.send(req, res), { status_code: 403 });
6666
});
6767

68-
it('should 400 if upload-defer-length is present when it upload-length is known', () => {
69-
req.headers = {
70-
'upload-offset': '0',
71-
'upload-defer-length': '1',
72-
'content-type': 'application/offset+octet-stream',
73-
};
74-
req.url = `${path}/file`;
75-
76-
store.getOffset.resolves({ size: 0, upload_length: '512' });
77-
78-
return assert.rejects(() => handler.send(req, res), { status_code: 400 });
79-
});
80-
8168
it('should declare upload-length once it is send', async () => {
8269
req.headers = {
8370
'upload-offset': '0',
@@ -86,6 +73,7 @@ describe('PatchHandler', () => {
8673
};
8774
req.url = `${path}/file`;
8875

76+
store.hasExtension.withArgs('creation-defer-length').returns(true);
8977
store.getOffset.resolves({ size: 0, upload_defer_length: '1' });
9078
store.write.resolves(5);
9179
store.declareUploadLength.resolves();
@@ -95,7 +83,7 @@ describe('PatchHandler', () => {
9583
assert.equal(store.declareUploadLength.calledOnceWith('file', '10'), true);
9684
});
9785

98-
it('should 400 if upload-length does not match', () => {
86+
it('should 400 if upload-length is already set', () => {
9987
req.headers = {
10088
'upload-offset': '0',
10189
'upload-length': '10',
@@ -104,6 +92,7 @@ describe('PatchHandler', () => {
10492
req.url = `${path}/file`;
10593

10694
store.getOffset.resolves({ size: 0, upload_length: '20' });
95+
store.hasExtension.withArgs('creation-defer-length').returns(true);
10796

10897
return assert.rejects(() => handler.send(req, res), { status_code: 400 });
10998
});
@@ -134,7 +123,6 @@ describe('PatchHandler', () => {
134123
it('must acknowledge successful PATCH requests with the 204', async () => {
135124
req.headers = {
136125
'upload-offset': '0',
137-
'upload-length': '1024',
138126
'content-type': 'application/offset+octet-stream',
139127
};
140128
req.url = `${path}/1234`;

‎test/Test-PostHandler.js

+2
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ describe('PostHandler', () => {
2121
let res = null;
2222

2323
const fake_store = sinon.createStubInstance(DataStore);
24+
fake_store.hasExtension.withArgs('creation-defer-length').returns(true);
2425

2526
beforeEach((done) => {
2627
req = { headers: {}, url: '/files', host: 'localhost:3000' };
@@ -169,6 +170,7 @@ describe('PostHandler', () => {
169170

170171
fake_store.create.resolvesArg(0);
171172
fake_store.write.resolves(upload_length);
173+
fake_store.hasExtension.withArgs('creation-defer-length').returns(true);
172174

173175
const handler = new PostHandler(fake_store, { path: '/test/output' });
174176
handler.on(EVENTS.EVENT_UPLOAD_COMPLETE, (obj) => {

‎test/Test-Server.js

-1
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,6 @@ describe('Server', () => {
305305
.send('test')
306306
.set('Tus-Resumable', TUS_RESUMABLE)
307307
.set('Upload-Offset', 0)
308-
.set('Upload-Length', Buffer.byteLength('test', 'utf8'))
309308
.set('Content-Type', 'application/offset+octet-stream')
310309
.end((err) => { if (err) done(err) });
311310
})

0 commit comments

Comments
 (0)
Please sign in to comment.