diff --git a/src/Simplify.Web.Tests/StaticFiles/Handlers/CachedFileHandlerTests.cs b/src/Simplify.Web.Tests/StaticFiles/Handlers/ClientCachedFileHandlerTests.cs similarity index 90% rename from src/Simplify.Web.Tests/StaticFiles/Handlers/CachedFileHandlerTests.cs rename to src/Simplify.Web.Tests/StaticFiles/Handlers/ClientCachedFileHandlerTests.cs index f045e13e..268c9a0e 100644 --- a/src/Simplify.Web.Tests/StaticFiles/Handlers/CachedFileHandlerTests.cs +++ b/src/Simplify.Web.Tests/StaticFiles/Handlers/ClientCachedFileHandlerTests.cs @@ -10,12 +10,12 @@ namespace Simplify.Web.Tests.StaticFiles.Handlers; [TestFixture] -public class CachedFileHandlerTests +public class ClientCachedFileHandlerTests { - private CachedFileHandler _handler = null!; + private ClientCachedFileHandler _handler = null!; [SetUp] - public void Initialize() => _handler = new CachedFileHandler(); + public void Initialize() => _handler = new ClientCachedFileHandler(); [Test] public void CanHandle_CanBeCached_True() diff --git a/src/Simplify.Web/Bootstrapper/Setup/BaseBootstrapperStaticFiles.cs b/src/Simplify.Web/Bootstrapper/Setup/BaseBootstrapperStaticFiles.cs index fc9783dd..156eabb7 100644 --- a/src/Simplify.Web/Bootstrapper/Setup/BaseBootstrapperStaticFiles.cs +++ b/src/Simplify.Web/Bootstrapper/Setup/BaseBootstrapperStaticFiles.cs @@ -64,10 +64,19 @@ public virtual void RegisterStaticFileRequestHandlingPipelineHandlers() if (TypesToExclude.Contains(typeof(IReadOnlyList))) return; - BootstrapperFactory.ContainerProvider.Register>(r => - [ - new CachedFileHandler(), - new NewFileHandler(r.Resolve(), r.Resolve()) - ], LifetimeType.Singleton); + BootstrapperFactory.ContainerProvider.Register(r => + { + IList handlers = + [ + new ClientCachedFileHandler() + ]; + + if (r.Resolve().StaticFilesMemoryCache) + handlers.Add(new InMemoryFilesCacheHandler(r.Resolve(), r.Resolve())); + else + handlers.Add(new NewFileHandler(r.Resolve(), r.Resolve())); + + return (IReadOnlyList)handlers; + }, LifetimeType.Singleton); } } \ No newline at end of file diff --git a/src/Simplify.Web/Settings/ISimplifyWebSettings.cs b/src/Simplify.Web/Settings/ISimplifyWebSettings.cs index 94a2efb1..6cc59944 100644 --- a/src/Simplify.Web/Settings/ISimplifyWebSettings.cs +++ b/src/Simplify.Web/Settings/ISimplifyWebSettings.cs @@ -151,6 +151,14 @@ public interface ISimplifyWebSettings /// bool StringTableMemoryCache { get; } + /// + /// Gets the value indicating whether static files memory cache enabled or disabled. + /// + /// + /// true if static files memory cache is enabled; otherwise, false. + /// + bool StaticFilesMemoryCache { get; } + /// /// Gets the value indicating whether file reader caching should be disabled. /// diff --git a/src/Simplify.Web/Settings/SimplifyWebSettings.cs b/src/Simplify.Web/Settings/SimplifyWebSettings.cs index 9c508be0..adfd4066 100644 --- a/src/Simplify.Web/Settings/SimplifyWebSettings.cs +++ b/src/Simplify.Web/Settings/SimplifyWebSettings.cs @@ -177,6 +177,14 @@ public SimplifyWebSettings(IConfiguration configuration) /// public bool StringTableMemoryCache { get; private set; } + /// + /// Gets the value indicating whether static files memory cache enabled or disabled. + /// + /// + /// true if static files memory cache is enabled; otherwise, false. + /// + public bool StaticFilesMemoryCache { get; private set; } + /// /// Gets the value indicating whether file reader caching should be disabled. /// @@ -263,6 +271,7 @@ private void LoadCacheSettings(IConfiguration config) { TemplatesMemoryCache = config.GetValue(nameof(TemplatesMemoryCache)); StringTableMemoryCache = config.GetValue(nameof(StringTableMemoryCache)); + StaticFilesMemoryCache = config.GetValue(nameof(StaticFilesMemoryCache)); DisableFileReaderCache = config.GetValue(nameof(DisableFileReaderCache)); } diff --git a/src/Simplify.Web/StaticFiles/Cache/FilesInMemoryCache.cs b/src/Simplify.Web/StaticFiles/Cache/FilesInMemoryCache.cs new file mode 100644 index 00000000..b29b3a7d --- /dev/null +++ b/src/Simplify.Web/StaticFiles/Cache/FilesInMemoryCache.cs @@ -0,0 +1,22 @@ +using System.Collections.Concurrent; + +namespace Simplify.Web.StaticFiles.Cache; + +/// +/// Provides static files in-memory cache. +/// +public static class FilesInMemoryCache +{ + // /// + // /// Gets the static files in-memory cache data. + // /// + // /// + // /// The static files in-memory cache data.. + // /// + // public static IDictionary Data { get; set; } = new Dictionary(); + + /// + /// Gets the static files in-memory cache data. + /// + public static readonly ConcurrentDictionary Items = new(); +} \ No newline at end of file diff --git a/src/Simplify.Web/StaticFiles/Handlers/CachedFileHandler.cs b/src/Simplify.Web/StaticFiles/Handlers/ClientCachedFileHandler.cs similarity index 90% rename from src/Simplify.Web/StaticFiles/Handlers/CachedFileHandler.cs rename to src/Simplify.Web/StaticFiles/Handlers/ClientCachedFileHandler.cs index 9415b520..fa580d2e 100644 --- a/src/Simplify.Web/StaticFiles/Handlers/CachedFileHandler.cs +++ b/src/Simplify.Web/StaticFiles/Handlers/ClientCachedFileHandler.cs @@ -8,10 +8,10 @@ namespace Simplify.Web.StaticFiles.Handlers; /// -/// Provides the cached file handler. +/// Provides the cached on client side files handler. /// /// -public class CachedFileHandler : IStaticFileRequestHandler +public class ClientCachedFileHandler : IStaticFileRequestHandler { /// /// Determines whether this handler can handle the file requested. diff --git a/src/Simplify.Web/StaticFiles/Handlers/InMemoryFilesCacheHandler.cs b/src/Simplify.Web/StaticFiles/Handlers/InMemoryFilesCacheHandler.cs new file mode 100644 index 00000000..4e7206ae --- /dev/null +++ b/src/Simplify.Web/StaticFiles/Handlers/InMemoryFilesCacheHandler.cs @@ -0,0 +1,36 @@ +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Simplify.Web.Http.ResponseWriting; +using Simplify.Web.StaticFiles.Cache; +using Simplify.Web.StaticFiles.Context; +using Simplify.Web.StaticFiles.IO; + +namespace Simplify.Web.StaticFiles.Handlers; + +/// +/// Provides the in-memory files cache. +/// +/// +public class InMemoryFilesCacheHandler(IResponseWriter responseWriter, IStaticFile staticFile) : IStaticFileRequestHandler +{ + /// + /// Determines whether this handler can handle the file requested. + /// + /// The context. + /// + /// true if this handler can handle the file requested; otherwise, false. + /// + public bool CanHandle(IStaticFileProcessingContext context) => !context.CanBeCached; + + /// + /// Executes the handler asynchronously. + /// + /// The context. + /// The response. + public async Task ExecuteAsync(IStaticFileProcessingContext context, HttpResponse response) + { + response.SetNewReturningFileAttributes(context); + + await responseWriter.WriteAsync(response, FilesInMemoryCache.Items.GetOrAdd(context.RelativeFilePath, staticFile.GetData)); + } +} \ No newline at end of file diff --git a/src/Simplify.Web/StaticFiles/Handlers/NewFileHandler.cs b/src/Simplify.Web/StaticFiles/Handlers/NewFileHandler.cs index 7ef7203c..bc3a2c4c 100644 --- a/src/Simplify.Web/StaticFiles/Handlers/NewFileHandler.cs +++ b/src/Simplify.Web/StaticFiles/Handlers/NewFileHandler.cs @@ -1,9 +1,5 @@ -using System; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; -using Simplify.System; -using Simplify.Web.Http.Mime; -using Simplify.Web.Http.ResponseTime; using Simplify.Web.Http.ResponseWriting; using Simplify.Web.StaticFiles.Context; using Simplify.Web.StaticFiles.IO; @@ -32,9 +28,7 @@ public class NewFileHandler(IResponseWriter responseWriter, IStaticFile staticFi /// The response. public async Task ExecuteAsync(IStaticFileProcessingContext context, HttpResponse response) { - response.SetContentMimeType(context.RelativeFilePath); - response.SetLastModifiedTime(context.LastModificationTime); - response.Headers["Expires"] = new DateTimeOffset(TimeProvider.Current.Now.AddYears(1)).ToString("R"); + response.SetNewReturningFileAttributes(context); await responseWriter.WriteAsync(response, await staticFile.GetDataAsync(context.RelativeFilePath)); } diff --git a/src/Simplify.Web/StaticFiles/IO/IStaticFile.cs b/src/Simplify.Web/StaticFiles/IO/IStaticFile.cs index f7a437b6..e639d72a 100644 --- a/src/Simplify.Web/StaticFiles/IO/IStaticFile.cs +++ b/src/Simplify.Web/StaticFiles/IO/IStaticFile.cs @@ -21,9 +21,14 @@ public interface IStaticFile DateTime GetLastModificationTime(string relativeFilePath); /// - /// Gets the file data. + /// Gets the file data asynchronously. /// /// The relative file path. - /// Task GetDataAsync(string relativeFilePath); + + /// + /// Gets the file data. + /// + /// The relative file path. + byte[] GetData(string relativeFilePath); } \ No newline at end of file diff --git a/src/Simplify.Web/StaticFiles/IO/StaticFile.cs b/src/Simplify.Web/StaticFiles/IO/StaticFile.cs index 1112fb0c..2498c96f 100644 --- a/src/Simplify.Web/StaticFiles/IO/StaticFile.cs +++ b/src/Simplify.Web/StaticFiles/IO/StaticFile.cs @@ -38,7 +38,7 @@ public bool IsValidPath(string relativeFilePath) public DateTime GetLastModificationTime(string relativeFilePath) => File.GetLastWriteTimeUtc(sitePhysicalPath + relativeFilePath).TrimMilliseconds(); /// - /// Gets the file data. + /// Gets the file data asynchronously. /// /// The relative file path. public async Task GetDataAsync(string relativeFilePath) @@ -59,4 +59,10 @@ public async Task GetDataAsync(string relativeFilePath) return result; } + + /// + /// Gets the file data. + /// + /// The relative file path. + public byte[] GetData(string relativeFilePath) => File.ReadAllBytes(relativeFilePath); } \ No newline at end of file diff --git a/src/Simplify.Web/StaticFiles/StaticFileResponseExtensions.cs b/src/Simplify.Web/StaticFiles/StaticFileResponseExtensions.cs new file mode 100644 index 00000000..40059b33 --- /dev/null +++ b/src/Simplify.Web/StaticFiles/StaticFileResponseExtensions.cs @@ -0,0 +1,26 @@ +using System; +using Microsoft.AspNetCore.Http; +using Simplify.System; +using Simplify.Web.Http.Mime; +using Simplify.Web.Http.ResponseTime; +using Simplify.Web.StaticFiles.Context; + +namespace Simplify.Web.StaticFiles; + +/// +/// Provides the static file response extensions. +/// +public static class StaticFileResponseExtensions +{ + /// + /// Sets the new returning file attributes. + /// + /// The HTTP response. + /// The static file processing context. + public static void SetNewReturningFileAttributes(this HttpResponse response, IStaticFileProcessingContext context) + { + response.SetContentMimeType(context.RelativeFilePath); + response.SetLastModifiedTime(context.LastModificationTime); + response.Headers["Expires"] = new DateTimeOffset(TimeProvider.Current.Now.AddYears(1)).ToString("R"); + } +} \ No newline at end of file