Skip to content

Instantly share code, notes, and snippets.

@LanceMcCarthy
Last active August 28, 2018 17:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save LanceMcCarthy/60f28dadce533b2623f0eed1ffc0ccf9 to your computer and use it in GitHub Desktop.
Save LanceMcCarthy/60f28dadce533b2623f0eed1ffc0ccf9 to your computer and use it in GitHub Desktop.
Brotli Compression Test
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