From 2bab2c3d1d33b3d0e3ed2f10da0678ccc3e5ea66 Mon Sep 17 00:00:00 2001 From: Tommy Parnell Date: Fri, 24 Jun 2016 09:35:52 -0400 Subject: [PATCH] default to optimal compression, clean up code (#4) --- CompressR.sln | 1 + gulpfile.js | 2 +- src/CompressR.MVC/BaseCompressAttribute.cs | 20 +++++++ src/CompressR.MVC/CompressAttribute.cs | 15 +++--- src/CompressR.MVC/CompressFactory.cs | 28 +++++----- src/CompressR.MVC/CompressR.MVC.projitems | 1 + src/CompressR.MVC/DeflateAttribute.cs | 8 ++- src/CompressR.MVC/GzipAttribute.cs | 9 ++-- .../Controllers/ValuesController.cs | 7 +-- src/CompressR.WebApi/BaseCompressAttribute.cs | 51 ++++++++++++++++++ src/CompressR.WebApi/CompressAttribute.cs | 52 +++---------------- src/CompressR.WebApi/CompressR.WebApi.csproj | 1 + src/CompressR.WebApi/CompressedContent.cs | 16 +++--- src/CompressR.WebApi/DeflateAttribute.cs | 41 +++------------ src/CompressR.WebApi/GzipAttribute.cs | 42 +++------------ 15 files changed, 134 insertions(+), 160 deletions(-) create mode 100644 src/CompressR.MVC/BaseCompressAttribute.cs create mode 100644 src/CompressR.WebApi/BaseCompressAttribute.cs diff --git a/CompressR.sln b/CompressR.sln index 820237f..3763cc9 100644 --- a/CompressR.sln +++ b/CompressR.sln @@ -20,6 +20,7 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{84E10868-C75C-4FDF-8AE1-465F1593A211}" ProjectSection(SolutionItems) = preProject appveyor.yml = appveyor.yml + gulpfile.js = gulpfile.js EndProjectSection EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CompressR.WebApiUnitTests", "src\CompressR.WebApiUnitTests\CompressR.WebApiUnitTests.csproj", "{61D97564-ACF6-4396-AB96-2D29CDCC3814}" diff --git a/gulpfile.js b/gulpfile.js index 6acd405..ff3d97a 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -5,7 +5,7 @@ var download = require("gulp-download"); var del = require('del'); var assemblyInfo = require('gulp-dotnet-assembly-info'); var xunit = xunit = require('gulp-xunit-runner'); -var version = '1.2.2'; +var version = '1.3.2'; gulp.task('clean', ()=>{ return del(['src/**/obj/', 'src/**/bin/Release', 'nuget.exe', 'nupkgs']) diff --git a/src/CompressR.MVC/BaseCompressAttribute.cs b/src/CompressR.MVC/BaseCompressAttribute.cs new file mode 100644 index 0000000..7d7afad --- /dev/null +++ b/src/CompressR.MVC/BaseCompressAttribute.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.IO.Compression; +using System.Text; + +namespace CompressR.MVC +{ + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)] + public abstract class BaseCompressAttribute : System.Web.Mvc.ActionFilterAttribute + { + protected bool RequireCompression { get; set; } + + public CompressionLevel CompressionLevel { get; set; } = CompressionLevel.Optimal; + + protected BaseCompressAttribute(bool requireCompression = false) + { + RequireCompression = requireCompression; + } + } +} \ No newline at end of file diff --git a/src/CompressR.MVC/CompressAttribute.cs b/src/CompressR.MVC/CompressAttribute.cs index 1f7dde9..edb68b6 100644 --- a/src/CompressR.MVC/CompressAttribute.cs +++ b/src/CompressR.MVC/CompressAttribute.cs @@ -7,18 +7,21 @@ using System.Web.Mvc; namespace CompressR.MVC { [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = false)] - public sealed class CompressAttribute : System.Web.Mvc.ActionFilterAttribute + public sealed class CompressAttribute : BaseCompressAttribute { - private bool RequireCompression { get; set; } - public CompressAttribute(bool requireCompression = false) + : base(requireCompression) { - RequireCompression = requireCompression; } - public override void OnActionExecuting(ActionExecutingContext filterContext) + /// + /// Override to compress the content that is generated by + /// an action method. + /// + /// + public override void OnActionExecuting(System.Web.Mvc.ActionExecutingContext filterContext) { - CompressFactory.Compress(filterContext, RequireCompression); + CompressFactory.Compress(filterContext, RequireCompression, CompressionLevel); } } } \ No newline at end of file diff --git a/src/CompressR.MVC/CompressFactory.cs b/src/CompressR.MVC/CompressFactory.cs index 4e4c399..d652867 100644 --- a/src/CompressR.MVC/CompressFactory.cs +++ b/src/CompressR.MVC/CompressFactory.cs @@ -1,21 +1,21 @@ -using CompressR.Exceptions; -using System; +using System; using System.Collections.Generic; using System.IO.Compression; using System.Linq; using System.Text; +using CompressR.Exceptions; namespace CompressR.MVC { public static class CompressFactory { - public static void Compress(string compression, System.Web.Mvc.ActionExecutingContext filterContext, bool requireCompression) + public static void Compress(System.Web.Mvc.ActionExecutingContext filterContext, bool requireCompression, string compression, CompressionLevel compressLevel = CompressionLevel.Optimal) { var context = filterContext.RequestContext.HttpContext; var compressionAccepted = context.Request.Headers.Get(Constants.AcceptEncoding)?.Split(',').Trim().Any(a => string.Equals(a, compression, StringComparison.OrdinalIgnoreCase)) ?? false; - if (!compressionAccepted) + if(!compressionAccepted) { - if (requireCompression) + if(requireCompression) { throw new CompressRException("Compression required but client did not send accept header"); } @@ -25,18 +25,16 @@ namespace CompressR.MVC } } - - - HandleCompression(compression, filterContext); + HandleCompression(compression, filterContext, compressLevel); } - public static void Compress(System.Web.Mvc.ActionExecutingContext filterContext, bool requireCompression) + public static void Compress(System.Web.Mvc.ActionExecutingContext filterContext, bool requireCompression, CompressionLevel compressLevel = CompressionLevel.Optimal) { var context = filterContext.RequestContext.HttpContext; var compressionAlgorithm = context.Request.Headers.Get(Constants.AcceptEncoding)?.Split(',').Trim().Intersect(Constants.Compressors, StringComparer.OrdinalIgnoreCase)?.FirstOrDefault(); - if (!string.IsNullOrWhiteSpace(compressionAlgorithm)) + if(!string.IsNullOrWhiteSpace(compressionAlgorithm)) { - HandleCompression(compressionAlgorithm, filterContext); + HandleCompression(compressionAlgorithm, filterContext, compressLevel); } else if(requireCompression) { @@ -44,19 +42,19 @@ namespace CompressR.MVC } } - private static void HandleCompression(string compression, System.Web.Mvc.ActionExecutingContext filterContext) + private static void HandleCompression(string compression, System.Web.Mvc.ActionExecutingContext filterContext, CompressionLevel compressLevel = CompressionLevel.Optimal) { var context = filterContext.RequestContext.HttpContext; - switch (compression) + switch(compression) { case Constants.Gzip: - context.Response.Filter = new GZipStream(context.Response.Filter, CompressionMode.Compress); + context.Response.Filter = new GZipStream(context.Response.Filter, compressLevel); context.Response.AppendHeader(Constants.ContentEncoding, Constants.Gzip); context.Response.Cache.VaryByHeaders[Constants.AcceptEncoding] = true; break; case Constants.Deflate: - context.Response.Filter = new DeflateStream(context.Response.Filter, CompressionMode.Compress); + context.Response.Filter = new DeflateStream(context.Response.Filter, compressLevel); context.Response.AppendHeader(Constants.ContentEncoding, Constants.Deflate); context.Response.Cache.VaryByHeaders[Constants.AcceptEncoding] = true; break; diff --git a/src/CompressR.MVC/CompressR.MVC.projitems b/src/CompressR.MVC/CompressR.MVC.projitems index eb002e0..0c75c82 100644 --- a/src/CompressR.MVC/CompressR.MVC.projitems +++ b/src/CompressR.MVC/CompressR.MVC.projitems @@ -9,6 +9,7 @@ CompressR.MVC + diff --git a/src/CompressR.MVC/DeflateAttribute.cs b/src/CompressR.MVC/DeflateAttribute.cs index c280353..b78fce0 100644 --- a/src/CompressR.MVC/DeflateAttribute.cs +++ b/src/CompressR.MVC/DeflateAttribute.cs @@ -5,13 +5,11 @@ using System.Linq; namespace CompressR.MVC { [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = false)] - public sealed class DeflateAttribute : System.Web.Mvc.ActionFilterAttribute + public sealed class DeflateAttribute : BaseCompressAttribute { - private bool RequireCompression { get; set; } - public DeflateAttribute(bool requireCompression = false) + : base(requireCompression) { - RequireCompression = requireCompression; } /// @@ -21,7 +19,7 @@ namespace CompressR.MVC /// public override void OnActionExecuting(System.Web.Mvc.ActionExecutingContext filterContext) { - CompressFactory.Compress(Constants.Deflate, filterContext, RequireCompression); + CompressFactory.Compress(filterContext, RequireCompression, Constants.Deflate, CompressionLevel); } } } \ No newline at end of file diff --git a/src/CompressR.MVC/GzipAttribute.cs b/src/CompressR.MVC/GzipAttribute.cs index c270ff4..a1e3ae2 100644 --- a/src/CompressR.MVC/GzipAttribute.cs +++ b/src/CompressR.MVC/GzipAttribute.cs @@ -4,14 +4,11 @@ using System.Linq; namespace CompressR.MVC { - [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = false)] - public sealed class GzipAttribute : System.Web.Mvc.ActionFilterAttribute + public sealed class GzipAttribute : BaseCompressAttribute { - private bool RequireCompression { get; set; } - public GzipAttribute(bool requireCompression = false) + : base(requireCompression) { - RequireCompression = requireCompression; } /// @@ -21,7 +18,7 @@ namespace CompressR.MVC /// public override void OnActionExecuting(System.Web.Mvc.ActionExecutingContext filterContext) { - CompressFactory.Compress(Constants.Gzip, filterContext, RequireCompression); + CompressFactory.Compress(filterContext, RequireCompression, Constants.Gzip, CompressionLevel); } } } \ No newline at end of file diff --git a/src/CompressR.Sample/Controllers/ValuesController.cs b/src/CompressR.Sample/Controllers/ValuesController.cs index b5a893f..ce33bf0 100644 --- a/src/CompressR.Sample/Controllers/ValuesController.cs +++ b/src/CompressR.Sample/Controllers/ValuesController.cs @@ -1,11 +1,11 @@ -using CompressR.WebApi; -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Http; using System.Threading.Tasks; using System.Web.Http; +using CompressR.WebApi; namespace CompressR.Sample.Controllers { @@ -32,7 +32,6 @@ namespace CompressR.Sample.Controllers { A = 1, B = new string[] { "1", "A", "B" } - }); } @@ -44,9 +43,7 @@ namespace CompressR.Sample.Controllers { A = 1, B = new string[] { "1", "A", "B" } - }); } - } } \ No newline at end of file diff --git a/src/CompressR.WebApi/BaseCompressAttribute.cs b/src/CompressR.WebApi/BaseCompressAttribute.cs new file mode 100644 index 0000000..e0c22e1 --- /dev/null +++ b/src/CompressR.WebApi/BaseCompressAttribute.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.IO.Compression; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Web.Http.Filters; +using CompressR.Exceptions; + +namespace CompressR.WebApi +{ + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)] + public abstract class BaseCompressAttribute : System.Web.Http.Filters.ActionFilterAttribute + { + protected bool RequireCompression { get; set; } + public CompressionLevel CompressionLevel { get; set; } = CompressionLevel.Optimal; + + protected BaseCompressAttribute(bool requireCompression = false) + { + RequireCompression = requireCompression; + } + + protected async Task CompressAction(HttpActionExecutedContext actionExecutedContext, params string[] compressors) + { + if(actionExecutedContext.Response.Content == null) + { + return; + } + var acceptedEncoding = actionExecutedContext + .Response + .RequestMessage + .Headers + .AcceptEncoding + .Select(a => a.Value) + .Intersect(compressors, StringComparer.OrdinalIgnoreCase) + .FirstOrDefault(); + + if(string.IsNullOrWhiteSpace(acceptedEncoding)) + { + if(RequireCompression) + { + throw new CompressRException("Compression required but client did not send accept header"); + } + return; + } + + actionExecutedContext.Response.Content = new CompressedContent(actionExecutedContext.Response.Content, acceptedEncoding, CompressionLevel); + } + } +} \ No newline at end of file diff --git a/src/CompressR.WebApi/CompressAttribute.cs b/src/CompressR.WebApi/CompressAttribute.cs index 120cd7d..41f1970 100644 --- a/src/CompressR.WebApi/CompressAttribute.cs +++ b/src/CompressR.WebApi/CompressAttribute.cs @@ -1,62 +1,22 @@ -using CompressR.Exceptions; -using System; -using System.Collections.Generic; -using System.IO.Compression; -using System.Linq; -using System.Net.Http; -using System.Text; -using System.Threading; +using System.Threading; using System.Threading.Tasks; -using System.Web.Http.Controllers; using System.Web.Http.Filters; namespace CompressR.WebApi { - [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = false)] - public sealed class CompressAttribute : System.Web.Http.Filters.ActionFilterAttribute + public sealed class CompressAttribute : BaseCompressAttribute { - private bool RequireCompression { get; set; } - public CompressAttribute(bool requireCompression = false) - { - RequireCompression = requireCompression; - } + : base(requireCompression) { } public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext) { - OnActionExecutedAsync(actionExecutedContext, CancellationToken.None).Wait(); - + base.CompressAction(actionExecutedContext, Constants.Compressors).Wait(); } - public override async Task OnActionExecutedAsync(HttpActionExecutedContext actionExecutedContext, CancellationToken cancellationToken) + public override Task OnActionExecutedAsync(HttpActionExecutedContext actionExecutedContext, CancellationToken cancellationToken) { - if(actionExecutedContext.Response.Content == null) - { - return; - } - var acceptedEncoding = actionExecutedContext - .Response - .RequestMessage - .Headers - .AcceptEncoding - .Select(a => a.Value) - .Intersect(Constants.Compressors, StringComparer.OrdinalIgnoreCase) - .FirstOrDefault(); - - if (string.IsNullOrWhiteSpace(acceptedEncoding)) - { - if (RequireCompression) - { - throw new CompressRException("Compression required but client did not send accept header"); - } - else - { - return; - } - - } - - actionExecutedContext.Response.Content = new CompressedContent(actionExecutedContext.Response.Content, acceptedEncoding); + return base.CompressAction(actionExecutedContext, Constants.Compressors); } } } \ No newline at end of file diff --git a/src/CompressR.WebApi/CompressR.WebApi.csproj b/src/CompressR.WebApi/CompressR.WebApi.csproj index 932dc0b..38ad9a7 100644 --- a/src/CompressR.WebApi/CompressR.WebApi.csproj +++ b/src/CompressR.WebApi/CompressR.WebApi.csproj @@ -53,6 +53,7 @@ + diff --git a/src/CompressR.WebApi/CompressedContent.cs b/src/CompressR.WebApi/CompressedContent.cs index dc47b68..cdc67dd 100644 --- a/src/CompressR.WebApi/CompressedContent.cs +++ b/src/CompressR.WebApi/CompressedContent.cs @@ -9,12 +9,13 @@ namespace CompressR.WebApi { public class CompressedContent : HttpContent { + private readonly CompressionLevel compressionLevel; private readonly string _encodingType; private readonly HttpContent _originalContent; - public CompressedContent(HttpContent content, string encodingType = "gzip") + public CompressedContent(HttpContent content, string encodingType = "gzip", CompressionLevel compressionLevel = CompressionLevel.Optimal) { - if (content == null) + if(content == null) { throw new ArgumentNullException("content"); } @@ -22,11 +23,12 @@ namespace CompressR.WebApi _originalContent = content; _encodingType = encodingType.ToLowerInvariant(); - foreach (var header in _originalContent.Headers) + foreach(var header in _originalContent.Headers) { Headers.TryAddWithoutValidation(header.Key, header.Value); } Headers.ContentEncoding.Add(encodingType); + this.compressionLevel = compressionLevel; } protected override bool TryComputeLength(out long length) @@ -38,14 +40,14 @@ namespace CompressR.WebApi protected override Task SerializeToStreamAsync(Stream stream, TransportContext context) { Stream compressedStream = null; - switch (_encodingType) + switch(_encodingType) { case Constants.Gzip: - compressedStream = new GZipStream(stream, CompressionMode.Compress, true); + compressedStream = new GZipStream(stream, compressionLevel, true); break; case Constants.Deflate: - compressedStream = new DeflateStream(stream, CompressionMode.Compress, true); + compressedStream = new DeflateStream(stream, compressionLevel, true); break; default: @@ -55,7 +57,7 @@ namespace CompressR.WebApi return _originalContent.CopyToAsync(compressedStream).ContinueWith(tsk => { - if (compressedStream != null) + if(compressedStream != null) { compressedStream.Dispose(); } diff --git a/src/CompressR.WebApi/DeflateAttribute.cs b/src/CompressR.WebApi/DeflateAttribute.cs index e90fc4b..2b4949e 100644 --- a/src/CompressR.WebApi/DeflateAttribute.cs +++ b/src/CompressR.WebApi/DeflateAttribute.cs @@ -1,5 +1,4 @@ -using CompressR.Exceptions; -using System; +using System; using System.Collections.Generic; using System.IO.Compression; using System.Linq; @@ -9,49 +8,23 @@ using System.Threading; using System.Threading.Tasks; using System.Web.Http.Controllers; using System.Web.Http.Filters; +using CompressR.Exceptions; namespace CompressR.WebApi { - [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = false)] - public sealed class DeflateAttribute : System.Web.Http.Filters.ActionFilterAttribute + public sealed class DeflateAttribute : BaseCompressAttribute { - private bool RequireCompression { get; set; } - public DeflateAttribute(bool requireCompression = false) - { - RequireCompression = requireCompression; - } + : base(requireCompression) { } public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext) { - OnActionExecutedAsync(actionExecutedContext, CancellationToken.None).Wait(); + base.CompressAction(actionExecutedContext, Constants.Deflate).Wait(); } - public override async Task OnActionExecutedAsync(HttpActionExecutedContext actionExecutedContext, CancellationToken cancellationToken) + public override Task OnActionExecutedAsync(HttpActionExecutedContext actionExecutedContext, CancellationToken cancellationToken) { - if(actionExecutedContext.Response.Content == null) - { - return; - } - var acceptedEncoding = actionExecutedContext - .Response - .RequestMessage - .Headers - .AcceptEncoding - .Select(a => a.Value) - .Any(a => a.Equals(Constants.Deflate, StringComparison.OrdinalIgnoreCase)); - - if (!acceptedEncoding && RequireCompression) - { - throw new CompressRException("Compression required but client did not send accept header"); - } - - if (!acceptedEncoding) - { - return; - } - - actionExecutedContext.Response.Content = new CompressedContent(actionExecutedContext.Response.Content, Constants.Deflate); + return base.CompressAction(actionExecutedContext, Constants.Deflate); } } } \ No newline at end of file diff --git a/src/CompressR.WebApi/GzipAttribute.cs b/src/CompressR.WebApi/GzipAttribute.cs index f263f50..b79457e 100644 --- a/src/CompressR.WebApi/GzipAttribute.cs +++ b/src/CompressR.WebApi/GzipAttribute.cs @@ -1,5 +1,4 @@ -using CompressR.Exceptions; -using System; +using System; using System.IO.Compression; using System.Linq; using System.Net.Http; @@ -8,50 +7,23 @@ using System.Threading; using System.Threading.Tasks; using System.Web.Http.Controllers; using System.Web.Http.Filters; +using CompressR.Exceptions; namespace CompressR.WebApi { - [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = false)] - public sealed class GzipAttribute : System.Web.Http.Filters.ActionFilterAttribute + public sealed class GzipAttribute : BaseCompressAttribute { - private bool RequireCompression { get; set; } - public GzipAttribute(bool requireCompression = false) - { - RequireCompression = requireCompression; - } + : base(requireCompression) { } public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext) { - OnActionExecutedAsync(actionExecutedContext, CancellationToken.None).Wait(); + base.CompressAction(actionExecutedContext, Constants.Gzip).Wait(); } - public override async Task OnActionExecutedAsync(HttpActionExecutedContext actionExecutedContext, CancellationToken cancellationToken) + public override Task OnActionExecutedAsync(HttpActionExecutedContext actionExecutedContext, CancellationToken cancellationToken) { - if(actionExecutedContext.Response.Content == null) - { - return; - } - var acceptedEncoding = actionExecutedContext - .Response - .RequestMessage - .Headers - .AcceptEncoding - .Select(a => a.Value) - .Any(a => a.Equals(Constants.Gzip, StringComparison.OrdinalIgnoreCase)); - - - if (!acceptedEncoding && RequireCompression) - { - throw new CompressRException("Compression required but client did not send accept header"); - } - - if (!acceptedEncoding) - { - return; - } - - actionExecutedContext.Response.Content = new CompressedContent(actionExecutedContext.Response.Content, Constants.Gzip); + return base.CompressAction(actionExecutedContext, Constants.Gzip); } } } \ No newline at end of file