From c8974b7fa229ff4f1e026e1ebff50d1081cc5f8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karol=20S=C3=B3jko?= Date: Mon, 12 Jun 2023 11:55:18 +0200 Subject: [PATCH] fix(home-server): accept application/octet-stream requests for files (#625) * fix(home-server): accept application/octet-stream requests for files * fix(files): check for empty chunks --- .../UploadFileChunk/UploadFileChunk.spec.ts | 18 ++++++++++++++++++ .../UseCase/UploadFileChunk/UploadFileChunk.ts | 11 +++++++++++ packages/files/src/Infra/FS/FSFileUploader.ts | 9 ++++++--- packages/home-server/src/Server/HomeServer.ts | 9 +++++++-- 4 files changed, 42 insertions(+), 5 deletions(-) diff --git a/packages/files/src/Domain/UseCase/UploadFileChunk/UploadFileChunk.spec.ts b/packages/files/src/Domain/UseCase/UploadFileChunk/UploadFileChunk.spec.ts index 3c6641243..56ac0db77 100644 --- a/packages/files/src/Domain/UseCase/UploadFileChunk/UploadFileChunk.spec.ts +++ b/packages/files/src/Domain/UseCase/UploadFileChunk/UploadFileChunk.spec.ts @@ -26,6 +26,24 @@ describe('UploadFileChunk', () => { logger.warn = jest.fn() }) + it('should not upload a data chunk with 0 bytes', async () => { + expect( + await createUseCase().execute({ + chunkId: 2, + data: new Uint8Array([]), + resourceRemoteIdentifier: '2-3-4', + resourceUnencryptedFileSize: 123, + userUuid: '1-2-3', + }), + ).toEqual({ + success: false, + message: 'Empty file chunk', + }) + + expect(fileUploader.uploadFileChunk).not.toHaveBeenCalled() + expect(uploadRepository.storeUploadChunkResult).not.toHaveBeenCalled() + }) + it('should not upload a data chunk to a non existing file upload session', async () => { uploadRepository.retrieveUploadSessionId = jest.fn().mockReturnValue(undefined) diff --git a/packages/files/src/Domain/UseCase/UploadFileChunk/UploadFileChunk.ts b/packages/files/src/Domain/UseCase/UploadFileChunk/UploadFileChunk.ts index 54e840c9c..0e50e0534 100644 --- a/packages/files/src/Domain/UseCase/UploadFileChunk/UploadFileChunk.ts +++ b/packages/files/src/Domain/UseCase/UploadFileChunk/UploadFileChunk.ts @@ -18,6 +18,17 @@ export class UploadFileChunk implements UseCaseInterface { async execute(dto: UploadFileChunkDTO): Promise { try { + if (!dto.data.byteLength || dto.data.byteLength === 0) { + this.logger.debug( + `Skipping upload file chunk ${dto.chunkId} with 0 bytes for resource: ${dto.resourceRemoteIdentifier}`, + ) + + return { + success: false, + message: 'Empty file chunk', + } + } + this.logger.debug( `Starting upload file chunk ${dto.chunkId} with ${dto.data.byteLength} bytes for resource: ${dto.resourceRemoteIdentifier}`, ) diff --git a/packages/files/src/Infra/FS/FSFileUploader.ts b/packages/files/src/Infra/FS/FSFileUploader.ts index 478fb4d02..245b8dc26 100644 --- a/packages/files/src/Infra/FS/FSFileUploader.ts +++ b/packages/files/src/Infra/FS/FSFileUploader.ts @@ -39,7 +39,9 @@ export class FSFileUploader implements FileUploaderInterface { ) } - this.logger.debug(`FS storing file chunk ${dto.chunkId} in memory for ${dto.uploadId}`) + this.logger.debug( + `FS storing file chunk ${dto.chunkId} in memory for ${dto.uploadId}: ${dto.data}, ${dto.data.byteLength}`, + ) fileChunks.set(dto.chunkId, dto.data) @@ -61,11 +63,12 @@ export class FSFileUploader implements FileUploaderInterface { const orderedKeys = [...fileChunks.keys()].sort((a, b) => a - b) for (const orderedKey of orderedKeys) { const chunk = fileChunks.get(orderedKey) - if (!chunk) { + if (!chunk || chunk.byteLength === 0) { throw new Error(`Could not find chunk ${orderedKey} for upload ${uploadId}`) } - this.logger.debug(`FS writing chunk ${orderedKey} for ${uploadId}: ${JSON.stringify(chunk)}`) + this.logger.debug(`FS writing chunk ${orderedKey} for ${uploadId}: ${chunk.toString()} ${chunk.byteLength}}`) + await promises.appendFile(`${this.fileUploadPath}/${filePath}`, chunk) } diff --git a/packages/home-server/src/Server/HomeServer.ts b/packages/home-server/src/Server/HomeServer.ts index 97c714d7f..9fc81f1f9 100644 --- a/packages/home-server/src/Server/HomeServer.ts +++ b/packages/home-server/src/Server/HomeServer.ts @@ -12,7 +12,7 @@ import { InversifyExpressServer } from 'inversify-express-utils' import helmet from 'helmet' import * as cors from 'cors' import * as http from 'http' -import { text, json, Request, Response, NextFunction } from 'express' +import { text, json, Request, Response, NextFunction, raw } from 'express' import * as winston from 'winston' import { PassThrough } from 'stream' // eslint-disable-next-line @typescript-eslint/no-var-requires @@ -104,12 +104,17 @@ export class HomeServer implements HomeServerInterface { })) /* eslint-enable */ app.use(json({ limit: '50mb' })) + app.use(raw({ limit: '50mb', type: 'application/octet-stream' })) app.use( text({ type: ['text/plain', 'application/x-www-form-urlencoded', 'application/x-www-form-urlencoded; charset=utf-8'], }), ) - app.use(cors()) + app.use( + cors({ + exposedHeaders: ['Content-Range', 'Accept-Ranges'], + }), + ) app.use( robots({ UserAgent: '*',