diff --git a/services/web/app/src/Features/Compile/CompileController.js b/services/web/app/src/Features/Compile/CompileController.js index cff4c23543..32daff9554 100644 --- a/services/web/app/src/Features/Compile/CompileController.js +++ b/services/web/app/src/Features/Compile/CompileController.js @@ -58,9 +58,21 @@ const getSplitTestOptions = callbackify(async function (req, res) { ? Settings.compilesUserContentDomain : Settings.pdfDownloadDomain + const { variant: hybridDomainVariant } = + await SplitTestHandler.promises.getAssignment( + editorReq, + res, + 'pdf-download-domain-hybrid' + ) + const enableHybridPdfDownload = hybridDomainVariant === 'enabled' + if (!req.query.enable_pdf_caching) { // The frontend does not want to do pdf caching. - return { pdfDownloadDomain, enablePdfCaching: false } + return { + pdfDownloadDomain, + enableHybridPdfDownload, + enablePdfCaching: false, + } } // Double check with the latest split test assignment. @@ -74,10 +86,19 @@ const getSplitTestOptions = callbackify(async function (req, res) { const enablePdfCaching = variant === 'enabled' if (!enablePdfCaching) { // Skip the lookup of the chunk size when caching is not enabled. - return { pdfDownloadDomain, enablePdfCaching: false } + return { + pdfDownloadDomain, + enableHybridPdfDownload, + enablePdfCaching: false, + } } const pdfCachingMinChunkSize = await getPdfCachingMinChunkSize(editorReq, res) - return { pdfDownloadDomain, enablePdfCaching, pdfCachingMinChunkSize } + return { + pdfDownloadDomain, + enableHybridPdfDownload, + enablePdfCaching, + pdfCachingMinChunkSize, + } }) module.exports = CompileController = { @@ -118,8 +139,12 @@ module.exports = CompileController = { getSplitTestOptions(req, res, (err, splitTestOptions) => { if (err) return next(err) - let { enablePdfCaching, pdfCachingMinChunkSize, pdfDownloadDomain } = - splitTestOptions + let { + enablePdfCaching, + pdfCachingMinChunkSize, + pdfDownloadDomain, + enableHybridPdfDownload, + } = splitTestOptions options.enablePdfCaching = enablePdfCaching if (enablePdfCaching) { options.pdfCachingMinChunkSize = pdfCachingMinChunkSize @@ -186,6 +211,7 @@ module.exports = CompileController = { timings, pdfDownloadDomain, pdfCachingMinChunkSize, + enableHybridPdfDownload, }) } ) diff --git a/services/web/frontend/js/features/pdf-preview/util/fetchFromCompileDomain.ts b/services/web/frontend/js/features/pdf-preview/util/fetchFromCompileDomain.ts index 3a4120b993..c5b1d6f317 100644 --- a/services/web/frontend/js/features/pdf-preview/util/fetchFromCompileDomain.ts +++ b/services/web/frontend/js/features/pdf-preview/util/fetchFromCompileDomain.ts @@ -38,12 +38,16 @@ export async function fetchFromCompileDomain(url: string, init: RequestInit) { } } -function withFallbackCompileDomain(url: string) { +export function swapDomain(url: string, domain: string) { const u = new URL(url) - u.hostname = new URL(getMeta('ol-fallbackCompileDomain')).hostname + u.hostname = new URL(domain).hostname return u.href } +function withFallbackCompileDomain(url: string) { + return swapDomain(url, getMeta('ol-fallbackCompileDomain')) +} + function recordFallbackUsage() { setTimeout(() => { postJSON('/record-user-content-domain-fallback-usage').catch(() => {}) diff --git a/services/web/frontend/js/features/pdf-preview/util/output-files.js b/services/web/frontend/js/features/pdf-preview/util/output-files.js index 2488ce8379..d01749eb5c 100644 --- a/services/web/frontend/js/features/pdf-preview/util/output-files.js +++ b/services/web/frontend/js/features/pdf-preview/util/output-files.js @@ -3,7 +3,8 @@ import HumanReadableLogs from '../../../ide/human-readable-logs/HumanReadableLog import BibLogParser from '../../../ide/log-parser/bib-log-parser' import { v4 as uuid } from 'uuid' import { enablePdfCaching } from './pdf-caching-flags' -import { fetchFromCompileDomain } from './fetchFromCompileDomain' +import { fetchFromCompileDomain, swapDomain } from './fetchFromCompileDomain' +import { userContentDomainAccessCheckPassed } from '../../user-content-domain-access-check' // Warnings that may disappear after a second LaTeX pass const TRANSIENT_WARNING_REGEX = /^(Reference|Citation).+undefined on input line/ @@ -28,7 +29,8 @@ export function handleOutputFiles(outputFiles, projectId, data) { outputFile.pdfUrl = `${buildURL( outputFile, - data.pdfDownloadDomain + data.pdfDownloadDomain, + data.enableHybridPdfDownload )}?${params}` // build the URL for downloading the PDF @@ -71,7 +73,7 @@ export const handleLogFiles = async (outputFiles, data, signal) => { if (logFile) { try { const response = await fetchFromCompileDomain( - buildURL(logFile, data.pdfDownloadDomain), + buildURL(logFile, data.pdfDownloadDomain, data.enableHybridPdfDownload), { signal } ) @@ -102,7 +104,7 @@ export const handleLogFiles = async (outputFiles, data, signal) => { if (blgFile) { try { const response = await fetchFromCompileDomain( - buildURL(blgFile, data.pdfDownloadDomain), + buildURL(blgFile, data.pdfDownloadDomain, data.enableHybridPdfDownload), { signal } ) @@ -159,7 +161,20 @@ export function buildLogEntryAnnotations(entries, fileTreeManager) { return logEntryAnnotations } -function buildURL(file, pdfDownloadDomain) { +function buildURL(file, pdfDownloadDomain, enableHybridPdfDownload) { + const userContentDomain = getMeta('ol-compilesUserContentDomain') + if ( + enableHybridPdfDownload && + userContentDomainAccessCheckPassed() && + file.build && + userContentDomain + ) { + // This user is enrolled in the hybrid download of compile output. + // The access check passed, so try to use the new user content domain. + // Downloads from the compiles domains must include a build id. + // The build id is used implicitly for access control. + return swapDomain(`${pdfDownloadDomain}${file.url}`, userContentDomain) + } if (file.build && pdfDownloadDomain) { // Downloads from the compiles domain must include a build id. // The build id is used implicitly for access control. diff --git a/services/web/frontend/js/features/user-content-domain-access-check/index.ts b/services/web/frontend/js/features/user-content-domain-access-check/index.ts index 8f991ec9d2..1496578506 100644 --- a/services/web/frontend/js/features/user-content-domain-access-check/index.ts +++ b/services/web/frontend/js/features/user-content-domain-access-check/index.ts @@ -204,12 +204,24 @@ export async function checkUserContentDomainAccess() { body: { failed, succeeded: cases.length - failed }, }) } catch (e) {} + + return failed === 0 +} + +let accessCheckPassed = false + +export function userContentDomainAccessCheckPassed() { + return accessCheckPassed } export function scheduleUserContentDomainAccessCheck() { sleep(INITIAL_DELAY_MS).then(() => { - checkUserContentDomainAccess().catch(err => { - captureException(err) - }) + checkUserContentDomainAccess() + .then(ok => { + accessCheckPassed = ok + }) + .catch(err => { + captureException(err) + }) }) } diff --git a/services/web/test/unit/src/Compile/CompileControllerTests.js b/services/web/test/unit/src/Compile/CompileControllerTests.js index eb1f6e7835..bf331cda32 100644 --- a/services/web/test/unit/src/Compile/CompileControllerTests.js +++ b/services/web/test/unit/src/Compile/CompileControllerTests.js @@ -121,6 +121,7 @@ describe('CompileController', function () { }, ], pdfDownloadDomain: 'https://compiles.overleaf.test', + enableHybridPdfDownload: false, }) ) }) @@ -163,6 +164,7 @@ describe('CompileController', function () { }, ], pdfDownloadDomain: 'https://compiles.overleaf.test/zone/b', + enableHybridPdfDownload: false, }) ) }) @@ -204,6 +206,7 @@ describe('CompileController', function () { JSON.stringify({ status: this.status, outputFiles: this.outputFiles, + enableHybridPdfDownload: false, }) ) })