using System.IO; | |
using System.IO.Compression; | |
using System.Threading.Tasks; | |
using Brotli; // Install the Brotli.NET NuGet package. | |
namespace WebApiDemo.WebApi | |
{ | |
public class BrotliCompressor : ICompressor | |
{ | |
public string EncodingType => "br"; | |
public async Task Compress(Stream source, Stream destination) | |
{ | |
using (Stream compressionStream = new BrotliStream(destination, CompressionMode.Compress)) | |
{ | |
await source.CopyToAsync(compressionStream); | |
} | |
} | |
} | |
} |
using System; | |
using System.IO; | |
using System.Net; | |
using System.Net.Http; | |
using System.Threading.Tasks; | |
namespace WebApiDemo.WebApi | |
{ | |
public class CompressedContent : HttpContent | |
{ | |
private readonly HttpContent _content; | |
private readonly ICompressor _compressor; | |
public CompressedContent(HttpContent content, ICompressor compressor) | |
{ | |
_content = content; | |
_compressor = compressor ?? throw new ArgumentNullException(nameof(compressor)); | |
foreach (var header in content.Headers) | |
{ | |
Headers.TryAddWithoutValidation(header.Key, header.Value); | |
} | |
} | |
protected override bool TryComputeLength(out long length) | |
{ | |
length = -1; | |
return false; | |
} | |
protected override async Task SerializeToStreamAsync(Stream stream, TransportContext context) | |
{ | |
if (stream == null) | |
{ | |
throw new ArgumentNullException(nameof(stream)); | |
} | |
if (_content == null) | |
{ | |
return; | |
} | |
using (Stream contentStream = await _content.ReadAsStreamAsync()) | |
{ | |
await _compressor.Compress(contentStream, stream); | |
} | |
} | |
protected override void Dispose(bool disposing) | |
{ | |
if (!disposing) | |
{ | |
return; | |
} | |
_content?.Dispose(); | |
} | |
} | |
} |
using System.Collections.Generic; | |
using System.Net.Http; | |
using System.Threading; | |
using System.Threading.Tasks; | |
namespace WebApiDemo.WebApi | |
{ | |
public class CompressionHandler : DelegatingHandler | |
{ | |
private readonly IReadOnlyDictionary<string, ICompressor> _compressors; | |
public CompressionHandler() | |
{ | |
_compressors = new Dictionary<string, ICompressor> | |
{ | |
{"br", new BrotliCompressor()}, | |
{"gzip", new GZipCompressor()}, | |
{"deflate", new DeflateCompressor()}, | |
{"identity", null} | |
}; | |
} | |
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) | |
{ | |
var response = await base.SendAsync(request, cancellationToken); | |
if (!ShouldCompressResponse(response.Content)) | |
{ | |
return response; | |
} | |
var encodings = request.Headers.AcceptEncoding; | |
if (encodings == null || | |
encodings.Count == 0) | |
{ | |
return response; | |
} | |
foreach (var encoding in encodings) | |
{ | |
if (!_compressors.TryGetValue(encoding.Value, out var compressor)) | |
{ | |
continue; | |
} | |
if (compressor == null) | |
{ | |
break; | |
} | |
response.Content = new CompressedContent(response.Content, compressor); | |
response.Content.Headers.ContentMD5 = null; | |
response.Content.Headers.ContentLength = null; | |
response.Content.Headers.ContentEncoding.Add(compressor.EncodingType); | |
response.Headers.Vary.ParseAdd("Accept-Encoding"); | |
break; | |
} | |
return response; | |
} | |
private static bool ShouldCompressResponse(HttpContent content) | |
{ | |
if (content.Headers.ContentRange != null || | |
content.Headers.ContentEncoding.Count > 0) | |
{ | |
return false; | |
} | |
switch (content.Headers.ContentType.MediaType) | |
{ | |
case "application/json": | |
case "application/xml": | |
case "text/html": | |
case "text/json": | |
case "text/plain": | |
case "text/xml": | |
return true; | |
default: | |
return false; | |
} | |
} | |
} | |
} |
using System.IO; | |
using System.IO.Compression; | |
using System.Threading.Tasks; | |
namespace WebApiDemo.WebApi | |
{ | |
public class DeflateCompressor : ICompressor | |
{ | |
public string EncodingType => "deflate"; | |
public async Task Compress(Stream source, Stream destination) | |
{ | |
using (Stream compressionStream = new DeflateStream(destination, CompressionMode.Compress)) | |
{ | |
await source.CopyToAsync(compressionStream); | |
} | |
} | |
} | |
} |
using System.IO; | |
using System.IO.Compression; | |
using System.Threading.Tasks; | |
namespace WebApiDemo.WebApi | |
{ | |
public class GZipCompressor : ICompressor | |
{ | |
public string EncodingType => "gzip"; | |
public async Task Compress(Stream source, Stream destination) | |
{ | |
using (Stream compressionStream = new GZipStream(destination, CompressionMode.Compress)) | |
{ | |
await source.CopyToAsync(compressionStream); | |
} | |
} | |
} | |
} |
using System.IO; | |
using System.Threading.Tasks; | |
namespace WebApiDemo.WebApi | |
{ | |
public interface ICompressor | |
{ | |
string EncodingType { get; } | |
Task Compress(Stream source, Stream destination); | |
} | |
} |
using EPiServer.Framework; | |
using EPiServer.Framework.Initialization; | |
using System.Web.Http; | |
namespace WebApiDemo.WebApi | |
{ | |
public class InitializeSite : IInitializableModule | |
{ | |
public void Initialize(InitializationEngine context) | |
{ | |
GlobalConfiguration.Configuration.MessageHandlers.Insert(0, new CompressionHandler()); | |
} | |
public void Uninitialize(InitializationEngine context) | |
{ | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment