From da635cb4b472a03c8fb597e621adbe7acc9752a3 Mon Sep 17 00:00:00 2001 From: Zac Spitzer Date: Wed, 26 Jun 2024 14:14:21 +0200 Subject: [PATCH] LDEV-4949 serve extensions directly for older versions https://luceeserver.atlassian.net/browse/LDEV-4949 --- apps/updateserver/Application.cfc | 3 ++ apps/updateserver/rest/ExtensionProvider.cfc | 23 ++++++++++- apps/updateserver/services/ExtensionCache.cfc | 36 ++++++++++++++++++ tests/testExtensionDownload.cfc | 38 +++++++++++++++++++ 4 files changed, 98 insertions(+), 2 deletions(-) create mode 100644 apps/updateserver/services/ExtensionCache.cfc create mode 100644 tests/testExtensionDownload.cfc diff --git a/apps/updateserver/Application.cfc b/apps/updateserver/Application.cfc index 44e8be2..4467a30 100644 --- a/apps/updateserver/Application.cfc +++ b/apps/updateserver/Application.cfc @@ -72,6 +72,8 @@ component { , mavenMatcher = new services.legacy.MavenMatcher() ); + var extensionCache = new services.ExtensionCache(); + extMetaReader.setBundleDownloadservice( bundleDownloadService ); extMetaReader.loadMeta(); @@ -83,6 +85,7 @@ component { application.extensionsCdnUrl = extCdnUrl; application.extensionsS3Root = extS3Root; application.extMetaReader = extMetaReader; + application.extensionCache = extensionCache; application.bundleDownloadService = bundleDownloadService; application.jiraChangelogService = jiraChangelogService; } diff --git a/apps/updateserver/rest/ExtensionProvider.cfc b/apps/updateserver/rest/ExtensionProvider.cfc index e740036..fc87160 100644 --- a/apps/updateserver/rest/ExtensionProvider.cfc +++ b/apps/updateserver/rest/ExtensionProvider.cfc @@ -4,8 +4,9 @@ */ component { - variables.metaReader = application.extMetaReader; - variables.cdnURL = application.extensionsCdnUrl; + variables.metaReader = application.extMetaReader; + variables.cdnURL = application.extensionsCdnUrl; + variables.extensionCache = application.extensionCache /** * @httpmethod GET @@ -91,7 +92,25 @@ component { , withLogo = false ); + param name="url.allowRedirect" default=""; + if ( isEmpty( url.allowRedirect ) && left( cgi.request_url, 5 ) == "http:" ){ + url.allowRedirect = false; // fall back to serving directly for older versions + } else { + url.allowRedirect = true; + } + if ( StructCount( ext ) ) { + if ( !url.allowRedirect ){ + var path = extensionCache.getExtensionLex( variables.cdnURL & ext.filename ); + var filename = listLast( path, "/" ); + header name="cache-control" value="public, max-age=#DateDiff( "s", Now(), expires )#"; + header name="Content-Disposition" value="attachment; filename=""#fileName#"""; + content + reset = true + file = path + type = "application/x-zip-compressed" + deletefile = false; + } header statuscode="302" statustext="Found"; header name="Location" value=variables.cdnURL & ext.filename; return; diff --git a/apps/updateserver/services/ExtensionCache.cfc b/apps/updateserver/services/ExtensionCache.cfc new file mode 100644 index 0000000..66798d6 --- /dev/null +++ b/apps/updateserver/services/ExtensionCache.cfc @@ -0,0 +1,36 @@ +component accessors=true { + + property name="extensionCache" type="any" default="cache/extensions/"; + + function getExtensionLex( extensionUrl ){ + var filename = listLast( arguments.extensionUrl, "/"); + var cachedir = expandPath( variables.extensionCache ); + + if ( !directoryExists( cachedir ) ){ + directoryCreate( cachedir ); + } else if ( fileExists ( cachedir & filename )) { + return cachedir & filename; + } + lock name="ext-#filename#" type="exclusive" timeout=10 { + return _fetchExtensionLex( cachedir, arguments.extensionUrl ) + } + + if ( fileExists ( cachedir & filename ) ) { + return cachedir & filename; + } + throw "unable to find extension [#filename#]"; + } + + private function _fetchExtensionLex( string cachedir, string extensionUrl ){ + var filename = listLast( arguments.extensionUrl, "/"); + if ( fileExists ( cachedir & filename )) { + return cachedir & filename; + } + systemOutput( "Downloading #arguments.extensionUrl# (cache miss)", true); + http url=arguments.extensionUrl method="get" result="local.core" path=cachedir; + var file = directoryList(path=cachedir, filter="#filename#"); + // systemOutput(file, true); + return file[ 1 ]; + } + +} \ No newline at end of file diff --git a/tests/testExtensionDownload.cfc b/tests/testExtensionDownload.cfc new file mode 100644 index 0000000..fdc40af --- /dev/null +++ b/tests/testExtensionDownload.cfc @@ -0,0 +1,38 @@ +component extends="org.lucee.cfml.test.LuceeTestCase" labels="data-provider-integration" { + + function beforeAll (){ + variables.dir = getDirectoryFromPath(getCurrentTemplatePath()); + application action="update" mappings={ + "/services" : expandPath( dir & "../apps/updateserver/services" ) + }; + variables.artifacts = dir & "/artifacts"; + if ( !DirectoryExists( variables.artifacts )) + directoryCreate( variables.artifacts ); + + variables.buildDir = getTempDirectory() & "/build-test-#createUniqueId()#/"; + if ( DirectoryExists( variables.buildDir ) ) + directoryDelete( variables.buildDir, true ); + directoryCreate( variables.buildDir, true ); + + } + + function run( testResults , testBox ) { + describe( "extensions need to be served directly for older lucee versions", function() { + it(title="check local extension cache works", body=function(){ + var extensionCache = new services.extensionCache(); + var path = extensionCache.getExtensionLex( "https://ext.lucee.org/compress-extension-1.0.0.15.lex" ); + systemOutput( path, true ) + expect( fileExists( path ) ).toBeTrue(); + }); + + it(title="check local extension cache works (from cache)", body=function(){ + var extensionCache = new services.extensionCache(); + var path = extensionCache.getExtensionLex( "https://ext.lucee.org/compress-extension-1.0.0.15.lex" ); + // systemOutput( path, true ) + expect( fileExists( path ) ).toBeTrue(); + }); + + }); + } + +}