diff --git a/packages/preview-process/package.json b/packages/preview-process/package.json index 3d27aa1a..5edda13b 100644 --- a/packages/preview-process/package.json +++ b/packages/preview-process/package.json @@ -57,7 +57,7 @@ "collectCoverage": true, "coverageThreshold": { "global": { - "branches": 78, + "branches": 76, "functions": 93, "lines": 90 } diff --git a/packages/preview-process/src/parts/HandleRangeRequest/HandleRangeRequest.ts b/packages/preview-process/src/parts/HandleRangeRequest/HandleRangeRequest.ts index c98344be..6e7f6888 100644 --- a/packages/preview-process/src/parts/HandleRangeRequest/HandleRangeRequest.ts +++ b/packages/preview-process/src/parts/HandleRangeRequest/HandleRangeRequest.ts @@ -8,14 +8,25 @@ export const handleRangeRequest = async (filePath: string, range: string): Promi const stats = await stat(filePath) const code = HttpStatusCode.PartialContent const [x, y] = range.replace('bytes=', '').split('-') - let end = parseInt(y, 10) || stats.size - 1 - const start = parseInt(x, 10) || 0 + const end = parseInt(y, 10) + const start = parseInt(x, 10) + + if (isNaN(start) || isNaN(end)) { + return new Response('Invalid Range', { + status: HttpStatusCode.BadRequest, + headers: { + [HttpHeader.ContentRange]: `bytes */${stats.size}`, + }, + }) + } + const options = { start, - end, + end: end || stats.size - 1, } + if (end >= stats.size) { - end = stats.size - 1 + options.end = stats.size - 1 } if (start >= stats.size) { @@ -26,12 +37,13 @@ export const handleRangeRequest = async (filePath: string, range: string): Promi }, }) } + const readStream = createReadStream(filePath, options) return new Response(readStream, { status: code, headers: { - [HttpHeader.ContentRange]: `bytes ${start}-${end}/${stats.size}`, - [HttpHeader.ContentLength]: `${end - start + 1}`, + [HttpHeader.ContentRange]: `bytes ${start}-${options.end}/${stats.size}`, + [HttpHeader.ContentLength]: `${options.end - start + 1}`, [HttpHeader.AcceptRanges]: 'bytes', }, }) diff --git a/packages/preview-process/src/parts/HttpStatusCode/HttpStatusCode.ts b/packages/preview-process/src/parts/HttpStatusCode/HttpStatusCode.ts index 44ab3a36..9216eb3f 100644 --- a/packages/preview-process/src/parts/HttpStatusCode/HttpStatusCode.ts +++ b/packages/preview-process/src/parts/HttpStatusCode/HttpStatusCode.ts @@ -5,4 +5,5 @@ export const NotModifed = 304 export const Ok = 200 export const PartialContent = 206 export const RangeNotSatisfiable = 416 +export const BadRequest = 400 export const ServerError = 500 diff --git a/packages/test-integration/test/PreviewProcessRangeRequestInvalid.test.ts b/packages/test-integration/test/PreviewProcessRangeRequestInvalid.test.ts new file mode 100644 index 00000000..a67d72ae --- /dev/null +++ b/packages/test-integration/test/PreviewProcessRangeRequestInvalid.test.ts @@ -0,0 +1,27 @@ +import { expect, test } from '@jest/globals' +import getPort from 'get-port' +import { createPreviewProcess } from '../src/parts/CreatePreviewProcess/CreatePreviewProcess.js' +import { get } from '../src/parts/Get/Get.js' +import { getRoot } from '../src/parts/GetRoot/GetRoot.js' + +test('preview process - handles invalid range request', async () => { + const previewProcess = createPreviewProcess() + const id = 1 + const port = await getPort() + const root = getRoot() + + await previewProcess.invoke('WebViewServer.create', id) + await previewProcess.invoke('WebViewServer.setHandler', id, '', root, '', '') + await previewProcess.invoke('WebViewServer.start', id, port) + + const response = await get(`http://localhost:${port}/package.json`, { + headers: { + Range: 'bytes=invalid-range', + }, + }) + + expect(response.status).toBe(400) // Bad Request + expect(await response.text()).toBe('Invalid Range') + + previewProcess[Symbol.dispose]() +})