56 lines
No EOL
1.5 KiB
C#
56 lines
No EOL
1.5 KiB
C#
using System.IO.Compression;
|
|
using Microsoft.AspNetCore.StaticAssets;
|
|
|
|
namespace Iceshrimp.Backend.Core.Extensions;
|
|
|
|
public static class WebApplicationStaticAssetsExtensions
|
|
{
|
|
public static void MapStaticAssetsWithTransparentDecompression(this WebApplication app)
|
|
{
|
|
app.MapStaticAssets()
|
|
.Finally(builder =>
|
|
{
|
|
var @delegate = builder.RequestDelegate;
|
|
builder.RequestDelegate = async ctx =>
|
|
{
|
|
HookTransparentDecompression(ctx);
|
|
if (@delegate?.Invoke(ctx) is { } task)
|
|
await task;
|
|
};
|
|
});
|
|
}
|
|
|
|
private static void HookTransparentDecompression(HttpContext ctx)
|
|
{
|
|
if (ctx.GetEndpoint()?.Metadata.GetMetadata<StaticAssetDescriptor>() is not { } descriptor) return;
|
|
if (descriptor.AssetPath == descriptor.Route) return;
|
|
if (!descriptor.AssetPath.EndsWith(".br")) return;
|
|
if (descriptor.Selectors is not []) return;
|
|
|
|
var body = ctx.Response.Body;
|
|
var compressed = new MemoryStream();
|
|
ctx.Response.Body = compressed;
|
|
|
|
ctx.Response.OnStarting(async () =>
|
|
{
|
|
int? length = null;
|
|
var desc = descriptor.Properties.FirstOrDefault(p => p.Name == "Uncompressed-Length")?.Value;
|
|
if (int.TryParse(desc, out var parsed))
|
|
length = parsed;
|
|
|
|
try
|
|
{
|
|
ctx.Response.Headers.ContentLength = length;
|
|
ctx.Response.Headers.ContentEncoding = "plain";
|
|
|
|
await using var brotli = new BrotliStream(compressed, CompressionMode.Decompress);
|
|
compressed.Seek(0, SeekOrigin.Begin);
|
|
await brotli.CopyToAsync(body);
|
|
}
|
|
finally
|
|
{
|
|
await compressed.DisposeAsync();
|
|
}
|
|
});
|
|
}
|
|
} |