Last active
August 28, 2018 17:37
-
-
Save LanceMcCarthy/60f28dadce533b2623f0eed1ffc0ccf9 to your computer and use it in GitHub Desktop.
Brotli Compression Test
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.ObjectModel; | |
using System.IO; | |
using System.IO.Compression; | |
using System.Linq; | |
using System.Net.Http; | |
using System.Threading.Tasks; | |
using static System.Console; | |
namespace BrotliTest | |
{ | |
// Uses .NET Core 2.1 (no need for Brotli Prerelease NuGet Package) | |
class Program | |
{ | |
static void Main(string[] args) | |
{ | |
WriteLine("Brotli Decompression Test - Hit Enter to begin Brotli Test..."); | |
ReadLine(); | |
RunTest().Wait(); | |
WriteLine("Done - Hit Enter to exit..."); | |
ReadLine(); | |
} | |
private static async Task RunTest() | |
{ | |
//Known Brotli supported endpoint. Use this site to test yours https://nixcp.com/tools/brotli-test/ | |
var url = "https://kittyhacker101.tk"; | |
using (var client = new HttpClient(new DecompressionHandler {InnerHandler = new HttpClientHandler()})) | |
using (var response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead)) | |
{ | |
string result = await response.Content.ReadAsStringAsync(); | |
if (result != null && result.Length >= 200) | |
{ | |
WriteLine(result.Substring(0, 200) + "..."); | |
} | |
} | |
} | |
} | |
public class DecompressionHandler : DelegatingHandler | |
{ | |
public Collection<ICompressor> Compressors; | |
public DecompressionHandler() | |
{ | |
Compressors = new Collection<ICompressor> { new GZipCompressor(), new DeflateCompressor(), new BrotliCompressor() }; | |
} | |
protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) | |
{ | |
if (request.Content != null) | |
{ | |
// NOTE - Hardcoding Brotli, normally this would be determined from the request headers | |
var encoding = "br"; | |
var compressor = Compressors.FirstOrDefault(c => c.EncodingType.Equals(encoding, StringComparison.InvariantCultureIgnoreCase)); | |
if (compressor != null) | |
{ | |
request.Content = await DecompressContentAsync(request.Content, compressor).ConfigureAwait(true); | |
} | |
} | |
var response = await base.SendAsync(request, cancellationToken).ConfigureAwait(true); | |
return response; | |
} | |
private static async Task<HttpContent> DecompressContentAsync(HttpContent compressedContent, ICompressor compressor) | |
{ | |
using (compressedContent) | |
{ | |
var decompressed = new MemoryStream(); | |
var compressedStream = await compressedContent.ReadAsStreamAsync(); | |
await compressor.Decompress(compressedStream, decompressed).ConfigureAwait(true); | |
decompressed.Position = 0; | |
var newContent = new StreamContent(decompressed); | |
newContent.Headers.ContentType = compressedContent.Headers.ContentType; | |
return newContent; | |
} | |
} | |
} | |
public class DeflateCompressor : Compressor | |
{ | |
private const string DeflateEncoding = "deflate"; | |
public override string EncodingType | |
{ | |
get { return DeflateEncoding; } | |
} | |
public override Stream CreateCompressionStream(Stream output) | |
{ | |
return new DeflateStream(output, CompressionMode.Compress, leaveOpen: true); | |
} | |
public override Stream CreateDecompressionStream(Stream input) | |
{ | |
Console.WriteLine($"DeflateCompressor CreateDeompressionStream - {input.Length}"); | |
return new DeflateStream(input, CompressionMode.Decompress, leaveOpen: true); | |
} | |
} | |
public class GZipCompressor : Compressor | |
{ | |
private const string GZipEncoding = "gzip"; | |
public override string EncodingType | |
{ | |
get { return GZipEncoding; } | |
} | |
public override Stream CreateCompressionStream(Stream output) | |
{ | |
return new GZipStream(output, CompressionMode.Compress, leaveOpen: true); | |
} | |
public override Stream CreateDecompressionStream(Stream input) | |
{ | |
Console.WriteLine($"GZipCompressor CreateDeompressionStream - {input.Length}"); | |
return new GZipStream(input, CompressionMode.Decompress, leaveOpen: true); | |
} | |
} | |
public class BrotliCompressor : Compressor | |
{ | |
private const string BrotliEncoding = "br"; | |
public override string EncodingType | |
{ | |
get { return BrotliEncoding; } | |
} | |
public override Stream CreateCompressionStream(Stream output) | |
{ | |
return new BrotliStream(output, CompressionMode.Compress, leaveOpen: true); | |
} | |
public override Stream CreateDecompressionStream(Stream input) | |
{ | |
return new BrotliStream(input, CompressionMode.Decompress, leaveOpen: true); | |
} | |
} | |
public abstract class Compressor : ICompressor | |
{ | |
public abstract string EncodingType { get; } | |
public abstract Stream CreateCompressionStream(Stream output); | |
public abstract Stream CreateDecompressionStream(Stream input); | |
public virtual Task Compress(Stream source, Stream destination) | |
{ | |
var compressed = CreateCompressionStream(destination); | |
return Pump(source, compressed) | |
.ContinueWith(task => compressed.Dispose()); | |
} | |
public virtual Task Decompress(Stream source, Stream destination) | |
{ | |
var decompressed = CreateDecompressionStream(source); | |
return Pump(decompressed, destination) | |
.ContinueWith(task => decompressed.Dispose()); | |
} | |
protected virtual Task Pump(Stream input, Stream output) | |
{ | |
return input.CopyToAsync(output); | |
} | |
} | |
public interface ICompressor | |
{ | |
string EncodingType { get; } | |
Task Compress(Stream source, Stream destination); | |
Task Decompress(Stream source, Stream destination); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment