using System.Globalization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Headers;
using Microsoft.Extensions.Options;
using Microsoft.Net.Http.Headers;
using SixLabors.ImageSharp.Formats.Webp;
using SixLabors.ImageSharp.Web.Commands;
using SixLabors.ImageSharp.Web.Middleware;
using SixLabors.ImageSharp.Web.Processors;
using Umbraco.Cms.Core.Configuration.Models;
namespace Umbraco.Cms.Imaging.ImageSharp;
///
/// Configures the ImageSharp middleware options.
///
///
public sealed class ConfigureImageSharpMiddlewareOptions : IConfigureOptions
{
private readonly Configuration _configuration;
private readonly ImagingSettings _imagingSettings;
///
/// Initializes a new instance of the class.
///
/// The ImageSharp configuration.
/// The Umbraco imaging settings.
public ConfigureImageSharpMiddlewareOptions(Configuration configuration, IOptions imagingSettings)
{
_configuration = configuration;
_imagingSettings = imagingSettings.Value;
}
///
public void Configure(ImageSharpMiddlewareOptions options)
{
options.Configuration = _configuration;
options.HMACSecretKey = _imagingSettings.HMACSecretKey;
options.BrowserMaxAge = _imagingSettings.Cache.BrowserMaxAge;
options.CacheMaxAge = _imagingSettings.Cache.CacheMaxAge;
options.CacheHashLength = _imagingSettings.Cache.CacheHashLength;
// Use configurable maximum width and height
options.OnParseCommandsAsync = context =>
{
if (context.Commands.Count == 0 || _imagingSettings.HMACSecretKey.Length > 0)
{
// Nothing to parse or using HMAC authentication
return Task.CompletedTask;
}
if (context.Commands.Contains(ResizeWebProcessor.Width))
{
if (!int.TryParse(
context.Commands.GetValueOrDefault(ResizeWebProcessor.Width),
NumberStyles.Integer,
CultureInfo.InvariantCulture,
out var width)
|| width < 0
|| width >= _imagingSettings.Resize.MaxWidth)
{
context.Commands.Remove(ResizeWebProcessor.Width);
}
}
if (context.Commands.Contains(ResizeWebProcessor.Height))
{
if (!int.TryParse(
context.Commands.GetValueOrDefault(ResizeWebProcessor.Height),
NumberStyles.Integer,
CultureInfo.InvariantCulture,
out var height)
|| height < 0
|| height >= _imagingSettings.Resize.MaxHeight)
{
context.Commands.Remove(ResizeWebProcessor.Height);
}
}
return Task.CompletedTask;
};
// Change Cache-Control header when cache buster value is present
options.OnPrepareResponseAsync = context =>
{
if (context.Request.Query.ContainsKey("rnd") || context.Request.Query.ContainsKey("v"))
{
ResponseHeaders headers = context.Response.GetTypedHeaders();
CacheControlHeaderValue cacheControl = headers.CacheControl ?? new CacheControlHeaderValue()
{
Public = true
};
// ImageSharp enables cache revalidation by default, so disable and add immutable directive
cacheControl.MustRevalidate = false;
cacheControl.Extensions.Add(new NameValueHeaderValue("immutable"));
// Set updated value
headers.CacheControl = cacheControl;
}
return Task.CompletedTask;
};
options.Configuration.ImageFormatsManager.SetEncoder(
WebpFormat.Instance,
new WebpEncoder
{
// Default to Lossy encoding like in ImageSharp 2.x.
// ImageSharp 3.x seems to use Lossless for PNGs which creates files 10x larger than Lossy.
FileFormat = WebpFileFormatType.Lossy,
});
}
}