diff --git a/services/web/frontend/js/serviceWorker.js b/services/web/frontend/js/serviceWorker.js index b2f9de9550..ad799fe3ea 100644 --- a/services/web/frontend/js/serviceWorker.js +++ b/services/web/frontend/js/serviceWorker.js @@ -1,7 +1,8 @@ import { v4 as uuid } from 'uuid' const COMPILE_REQUEST_MATCHER = /^\/project\/[0-9a-f]{24}\/compile$/ const PDF_JS_CHUNK_SIZE = 128 * 1024 - +const MAX_SUBREQUEST_COUNT = 8 +const MAX_SUBREQUEST_BYTES = 4 * PDF_JS_CHUNK_SIZE const PDF_FILES = new Map() const METRICS = { @@ -44,6 +45,15 @@ function trackChunkVerify({ sizeDiffers, mismatch, success }) { } } +/** + * @param {Array} chunks + */ +function countBytes(chunks) { + return chunks.reduce((totalBytes, chunk) => { + return totalBytes + (chunk.end - chunk.start) + }, 0) +} + /** * @param {FetchEvent} event */ @@ -140,9 +150,26 @@ function processPdfRequest( .map(i => parseInt(i, 10)) const end = last + 1 + // Check that handling the range request won't trigger excessive subrequests, + // (to avoid unwanted latency compared to the original request). const chunks = getMatchingChunks(file.ranges, start, end) - const dynamicChunks = getInterleavingDynamicChunks(chunks, start, end) + const chunksSize = countBytes(chunks) + + if (chunks.length + dynamicChunks.length > MAX_SUBREQUEST_COUNT) { + // fall back to the original range request when splitting the range creates + // too many subrequests. + return + } + if ( + chunksSize > MAX_SUBREQUEST_BYTES && + !(dynamicChunks.length === 0 && chunks.length === 1) + ) { + // fall back to the original range request when a very large amount of + // object data would be requested, unless it is the only object in the + // request. + return + } // URL prefix is /project/:id/user/:id/build/... or /project/:id/build/... // for authenticated and unauthenticated users respectively.