├── src ├── Tests │ ├── Global.asax │ ├── Content │ │ └── Images │ │ │ └── app-icon-1024.png │ ├── Models │ │ ├── TestModel.cs │ │ ├── LoginModel.cs │ │ └── PdfLayout.cs │ ├── Tests.csproj.DotSettings │ ├── Global.asax.cs │ ├── App_Start │ │ └── WebApiConfig.cs │ ├── Extensions │ │ ├── HttpContentExtensions.cs │ │ ├── ByteArrayExtensions.cs │ │ ├── TimeSpanExtensions.cs │ │ ├── LongExtensions.cs │ │ └── HttpResponseMessageExtensions.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── app.config │ ├── Tests.v2.ncrunchproject │ ├── packages.config │ ├── web.config │ ├── Tests │ │ ├── Integration │ │ │ ├── CompressionHandlerWithoutThresholdTests.cs │ │ │ ├── AcceptEncodingStarCompressionHandlerWithThresholdTests.cs │ │ │ └── CompressionHandlerWithThresholdTests.cs │ │ ├── Facade │ │ │ └── OwinHostTests.cs │ │ └── Common │ │ │ └── TestFixture.cs │ ├── Handlers │ │ └── TraceMessageHandler.cs │ ├── Controllers │ │ ├── TestController.cs │ │ └── FileController.cs │ ├── Tests.csproj │ └── Tests.StrongName.csproj ├── StrongKey.snk ├── Microsoft.AspNet.WebApi.MessageHandlers.Compression.jmconfig ├── Core │ ├── Interfaces │ │ ├── IStreamManager.cs │ │ └── ICompressor.cs │ ├── StreamManager.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── HttpContentOperations.cs │ ├── Compressors │ │ ├── GZipCompressor.cs │ │ ├── DeflateCompressor.cs │ │ └── BaseCompressor.cs │ ├── Extensions │ │ └── HttpContentHeaderExtensions.cs │ ├── Core.csproj │ ├── Models │ │ └── CompressedContent.cs │ └── Core.StrongName.csproj ├── Server │ ├── packages.config │ ├── app.config │ ├── RecyclableStreamManager.cs │ ├── Attributes │ │ └── CompressionAttribute.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── Server.csproj │ ├── Server.StrongName.csproj │ └── ServerCompressionHandler.cs ├── Server.Owin │ ├── app.config │ ├── packages.config │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── Server.Owin.csproj │ ├── Server.Owin.StrongName.csproj │ └── OwinServerCompressionHandler.cs ├── Microsoft.AspNet.WebApi.MessageHandlers.Compression.v2.ncrunchsolution ├── Client │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── Client.csproj │ ├── Client.StrongName.csproj │ └── ClientCompressionHandler.cs └── Microsoft.AspNet.WebApi.MessageHandlers.Compression.sln ├── .gitattributes ├── appveyor.yml ├── dist └── nuget │ ├── Microsoft.AspNet.WebApi.MessageHandlers.Compression.nuspec │ ├── Microsoft.AspNet.WebApi.Extensions.Compression.Server.Owin.nuspec │ ├── Microsoft.AspNet.WebApi.Extensions.Compression.Server.Owin.StrongName.nuspec │ ├── Microsoft.AspNet.WebApi.Extensions.Compression.Server.nuspec │ ├── Microsoft.AspNet.WebApi.Extensions.Compression.Server.StrongName.nuspec │ ├── System.Net.Http.Extensions.Compression.Client.nuspec │ └── System.Net.Http.Extensions.Compression.Client.StrongName.nuspec ├── .gitignore ├── README.md └── LICENSE /src/Tests/Global.asax: -------------------------------------------------------------------------------- 1 | <%@ Application Codebehind="Global.asax.cs" Inherits="Tests.WebApiApplication" Language="C#" %> 2 | -------------------------------------------------------------------------------- /src/StrongKey.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azzlack/Microsoft.AspNet.WebApi.MessageHandlers.Compression/HEAD/src/StrongKey.snk -------------------------------------------------------------------------------- /src/Tests/Content/Images/app-icon-1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azzlack/Microsoft.AspNet.WebApi.MessageHandlers.Compression/HEAD/src/Tests/Content/Images/app-icon-1024.png -------------------------------------------------------------------------------- /src/Microsoft.AspNet.WebApi.MessageHandlers.Compression.jmconfig: -------------------------------------------------------------------------------- 1 | false -------------------------------------------------------------------------------- /src/Tests/Models/TestModel.cs: -------------------------------------------------------------------------------- 1 | namespace Tests.Models 2 | { 3 | public class TestModel 4 | { 5 | public TestModel() 6 | { 7 | } 8 | 9 | public TestModel(string data) 10 | { 11 | this.Data = data; 12 | } 13 | 14 | public string Data { get; set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Core/Interfaces/IStreamManager.cs: -------------------------------------------------------------------------------- 1 | namespace System.Net.Http.Extensions.Compression.Core.Interfaces 2 | { 3 | using System.IO; 4 | 5 | public interface IStreamManager 6 | { 7 | /// Gets a stream. 8 | /// (Optional) the stream name. 9 | /// The stream. 10 | Stream GetStream(string tag = null); 11 | } 12 | } -------------------------------------------------------------------------------- /src/Server/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/Tests/Tests.csproj.DotSettings: -------------------------------------------------------------------------------- 1 | 2 | True -------------------------------------------------------------------------------- /src/Server.Owin/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/Tests/Global.asax.cs: -------------------------------------------------------------------------------- 1 | namespace Tests 2 | { 3 | using System.Web; 4 | using System.Web.Http; 5 | 6 | // Note: For instructions on enabling IIS6 or IIS7 classic mode, 7 | // visit http://go.microsoft.com/?LinkId=9394801 8 | 9 | public class WebApiApplication : HttpApplication 10 | { 11 | protected void Application_Start() 12 | { 13 | WebApiConfig.Register(GlobalConfiguration.Configuration); 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /src/Core/StreamManager.cs: -------------------------------------------------------------------------------- 1 | namespace System.Net.Http.Extensions.Compression.Core 2 | { 3 | using System.IO; 4 | using System.Net.Http.Extensions.Compression.Core.Interfaces; 5 | 6 | /// Manager for streams. 7 | public class StreamManager : IStreamManager 8 | { 9 | public static IStreamManager Instance { get; } = new StreamManager(); 10 | 11 | public Stream GetStream(string tag = null) 12 | { 13 | return new MemoryStream(); 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /src/Server/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/Server.Owin/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | -------------------------------------------------------------------------------- /src/Tests/Models/LoginModel.cs: -------------------------------------------------------------------------------- 1 | namespace Tests.Models 2 | { 3 | public class LoginModel 4 | { 5 | public LoginModel() 6 | { 7 | } 8 | 9 | public LoginModel(string username, string password, string returnUrl) 10 | { 11 | this.Username = username; 12 | this.Password = password; 13 | this.ReturnUrl = returnUrl; 14 | } 15 | 16 | public string Username { get; set; } 17 | 18 | public string Password { get; set; } 19 | 20 | public string ReturnUrl { get; set; } 21 | } 22 | } -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: $(Major).$(Minor).0.{build} 2 | branches: 3 | only: 4 | - develop 5 | configuration: Release 6 | platform: Any CPU 7 | assembly_info: 8 | patch: true 9 | file: '**\AssemblyInfo.*' 10 | assembly_version: $(Major).$(Minor).0 11 | assembly_file_version: $(Major).$(Minor).0.{build} 12 | assembly_informational_version: $(Major).$(Minor).0.{build}-{branch} 13 | environment: 14 | Major: 2 15 | Minor: 1 16 | before_build: 17 | - cmd: nuget restore src\Microsoft.AspNet.WebApi.MessageHandlers.Compression.sln 18 | build: 19 | project: src\Microsoft.AspNet.WebApi.MessageHandlers.Compression.sln 20 | verbosity: minimal 21 | cache: 22 | - packages -> **\packages.config -------------------------------------------------------------------------------- /src/Tests/Models/PdfLayout.cs: -------------------------------------------------------------------------------- 1 | namespace Tests.Models 2 | { 3 | using iTextSharp.text; 4 | using iTextSharp.text.pdf; 5 | 6 | public class PdfLayout : PdfPageEventHelper 7 | { 8 | public override void OnStartPage(PdfWriter writer, Document doc) 9 | { 10 | } 11 | 12 | public override void OnEndPage(PdfWriter writer, Document doc) 13 | { 14 | var cb = writer.DirectContent; 15 | cb.MoveTo(doc.LeftMargin, doc.BottomMargin - 10f); 16 | cb.LineTo(doc.PageSize.Width - doc.RightMargin, doc.BottomMargin - 10f); 17 | cb.SetRGBColorStroke(255, 85, 0); 18 | cb.Stroke(); 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /src/Microsoft.AspNet.WebApi.MessageHandlers.Compression.v2.ncrunchsolution: -------------------------------------------------------------------------------- 1 | 2 | 1 3 | false 4 | true 5 | true 6 | UseDynamicAnalysis 7 | UseStaticAnalysis 8 | UseStaticAnalysis 9 | UseStaticAnalysis 10 | UseStaticAnalysis 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/Tests/App_Start/WebApiConfig.cs: -------------------------------------------------------------------------------- 1 | namespace Tests 2 | { 3 | using System.Net.Http.Extensions.Compression.Core.Compressors; 4 | using System.Web.Http; 5 | 6 | using Microsoft.AspNet.WebApi.Extensions.Compression.Server; 7 | 8 | public static class WebApiConfig 9 | { 10 | public static void Register(HttpConfiguration config) 11 | { 12 | config.MapHttpAttributeRoutes(); 13 | 14 | config.Routes.MapHttpRoute( 15 | name: "DefaultApi", 16 | routeTemplate: "api/{controller}/{id}", 17 | defaults: new { id = RouteParameter.Optional }); 18 | 19 | // Add compression message handler 20 | config.MessageHandlers.Insert(0, new ServerCompressionHandler(0, new GZipCompressor(RecyclableStreamManager.Instance), new DeflateCompressor(RecyclableStreamManager.Instance))); 21 | 22 | // Configure error details policy 23 | config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always; 24 | 25 | config.EnsureInitialized(); 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /src/Server/RecyclableStreamManager.cs: -------------------------------------------------------------------------------- 1 | namespace Microsoft.AspNet.WebApi.Extensions.Compression.Server 2 | { 3 | using System.IO; 4 | using System.Net.Http.Extensions.Compression.Core.Interfaces; 5 | 6 | using Microsoft.IO; 7 | 8 | /// Manager for streams. 9 | public class RecyclableStreamManager : IStreamManager 10 | { 11 | private readonly RecyclableMemoryStreamManager recyclableMemoryStreamManager; 12 | 13 | /// Initializes a new instance of the class. 14 | public RecyclableStreamManager() 15 | { 16 | this.recyclableMemoryStreamManager = new RecyclableMemoryStreamManager(); 17 | } 18 | 19 | public static IStreamManager Instance { get; } = new RecyclableStreamManager(); 20 | 21 | public Stream GetStream(string tag = null) 22 | { 23 | return !string.IsNullOrEmpty(tag) 24 | ? this.recyclableMemoryStreamManager.GetStream(tag) 25 | : this.recyclableMemoryStreamManager.GetStream(); 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /src/Tests/Extensions/HttpContentExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Tests.Extensions 2 | { 3 | using System.Net.Http; 4 | using System.Threading.Tasks; 5 | 6 | public static class HttpContentExtensions 7 | { 8 | /// 9 | /// Gets the payload size as a human readable string. 10 | /// 11 | /// The content. 12 | /// The payload size as a human readable string. 13 | public static string SizeAsHumanReadableString(this HttpContent content) 14 | { 15 | if (content.Headers.ContentLength.HasValue) 16 | { 17 | return content.Headers.ContentLength.Value.SizeAsHumanReadableString(LongExtensions.SIType.Byte); 18 | } 19 | 20 | var tcs = new TaskCompletionSource(); 21 | Task.Run(async () => 22 | { 23 | await content.LoadIntoBufferAsync(); 24 | 25 | tcs.SetResult(await content.ReadAsByteArrayAsync()); 26 | }).ConfigureAwait(false); 27 | 28 | return tcs.Task.Result.SizeAsHumanReadableString(); 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /src/Core/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Resources; 2 | using System.Reflection; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | 6 | // General Information about an assembly is controlled through the following 7 | // set of attributes. Change these attribute values to modify the information 8 | // associated with an assembly. 9 | [assembly: AssemblyTitle("Core")] 10 | [assembly: AssemblyDescription("")] 11 | [assembly: AssemblyConfiguration("")] 12 | [assembly: AssemblyCompany("")] 13 | [assembly: AssemblyProduct("Core")] 14 | [assembly: AssemblyCopyright("Copyright © 2016")] 15 | [assembly: AssemblyTrademark("")] 16 | [assembly: AssemblyCulture("")] 17 | [assembly: NeutralResourcesLanguage("en")] 18 | 19 | // Version information for an assembly consists of the following four values: 20 | // 21 | // Major Version 22 | // Minor Version 23 | // Build Number 24 | // Revision 25 | // 26 | // You can specify all the values or you can default the Build and Revision Numbers 27 | // by using the '*' as shown below: 28 | // [assembly: AssemblyVersion("1.0.*")] 29 | [assembly: AssemblyVersion("1.0.0.0")] 30 | [assembly: AssemblyFileVersion("1.0.0.0")] 31 | -------------------------------------------------------------------------------- /src/Tests/Extensions/ByteArrayExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Tests.Extensions 2 | { 3 | internal static class ByteArrayExtensions 4 | { 5 | /// 6 | /// Gets the size as a human readable string. 7 | /// 8 | /// The byte array. 9 | /// The size as a human readable string.. 10 | public static string SizeAsHumanReadableString(this byte[] s) 11 | { 12 | // Larger or equal to 1KB but lesser than 1MB 13 | if (s.Length >= 1024 && s.Length < 1048576) 14 | { 15 | return string.Format("{0}KB", s.Length / 1024); 16 | } 17 | 18 | // Larger or equal to 1MB but lesser than 1GB 19 | if (s.Length >= 1048576 && s.Length < 1073741824) 20 | { 21 | return string.Format("{0}MB", s.Length / 1024 / 1024); 22 | } 23 | 24 | // Larger or equal to 1Gt but lesser than 1TB 25 | if (s.Length >= 1073741824) 26 | { 27 | return string.Format("{0}GB", s.Length / 1024 / 1024 / 1024); 28 | } 29 | 30 | return string.Format("{0}bytes", s.Length); 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /src/Core/Interfaces/ICompressor.cs: -------------------------------------------------------------------------------- 1 | namespace System.Net.Http.Extensions.Compression.Core.Interfaces 2 | { 3 | using System.IO; 4 | using System.Threading.Tasks; 5 | 6 | /// 7 | /// Interface for stream compressors. 8 | /// 9 | public interface ICompressor 10 | { 11 | /// 12 | /// Gets the encoding type. 13 | /// 14 | /// The encoding type. 15 | string EncodingType { get; } 16 | 17 | /// 18 | /// Compresses the specified source stream onto the destination stream. 19 | /// 20 | /// The source. 21 | /// The destination. 22 | /// The compressed content length. 23 | Task Compress(Stream source, Stream destination); 24 | 25 | /// 26 | /// Decompresses the specified source stream onto the destination stream. 27 | /// 28 | /// The source. 29 | /// The destination. 30 | /// An async void. 31 | Task Decompress(Stream source, Stream destination); 32 | } 33 | } -------------------------------------------------------------------------------- /src/Client/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Resources; 3 | 4 | // General Information about an assembly is controlled through the following 5 | // set of attributes. Change these attribute values to modify the information 6 | // associated with an assembly. 7 | [assembly: AssemblyTitle("System.Net.Http.Extensions.Compression.Client")] 8 | [assembly: AssemblyDescription("Module for Microsoft HTTP Client that enables GZip and Deflate support for incoming and outgoing requests")] 9 | [assembly: AssemblyConfiguration("")] 10 | [assembly: AssemblyCompany("EyeCatch")] 11 | [assembly: AssemblyProduct("System.Net.Http.Extensions.Compression.Client")] 12 | [assembly: AssemblyCopyright("Copyright © EyeCatch 2016")] 13 | [assembly: AssemblyTrademark("")] 14 | [assembly: AssemblyCulture("")] 15 | [assembly: NeutralResourcesLanguage("en")] 16 | 17 | // Version information for an assembly consists of the following four values: 18 | // 19 | // Major Version 20 | // Minor Version 21 | // Build Number 22 | // Revision 23 | // 24 | // You can specify all the values or you can default the Build and Revision Numbers 25 | // by using the '*' as shown below: 26 | // [assembly: AssemblyVersion("1.0.*")] 27 | [assembly: AssemblyVersion("1.0.0.0")] 28 | [assembly: AssemblyFileVersion("1.0.0.0")] 29 | -------------------------------------------------------------------------------- /src/Tests/Extensions/TimeSpanExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Tests.Extensions 2 | { 3 | using System; 4 | 5 | public static class TimeSpanExtensions 6 | { 7 | /// 8 | /// Returns the timespan as a human readable string. 9 | /// 10 | /// The timespan. 11 | /// The timespan as a human readable string. 12 | public static string AsHumanReadableString(this TimeSpan ts) 13 | { 14 | // Larger or equal to 1 second but lesser than 1 minute 15 | if (ts.TotalMilliseconds >= 1000 && ts.TotalMilliseconds < 60000) 16 | { 17 | return string.Format("{0}s", ts.TotalMilliseconds / 1000); 18 | } 19 | 20 | // Larger or equal to 1 minute but lesser than 1 hour 21 | if (ts.TotalMilliseconds >= 60000 && ts.TotalMilliseconds < 3600000) 22 | { 23 | return string.Format("{0}m", ts.TotalMilliseconds / 1000 / 60); 24 | } 25 | 26 | // Larger or equal to 1 hour but lesser than 1 day 27 | if (ts.TotalMilliseconds >= 3600000 && ts.TotalMilliseconds < 86400000) 28 | { 29 | return string.Format("{0}h", ts.TotalMilliseconds / 1000 / 60 / 60); 30 | } 31 | 32 | return string.Format("{0}ms", ts.TotalMilliseconds); 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /src/Server/Attributes/CompressionAttribute.cs: -------------------------------------------------------------------------------- 1 | namespace Microsoft.AspNet.WebApi.Extensions.Compression.Server.Attributes 2 | { 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using System.Web.Http.Controllers; 6 | using System.Web.Http.Filters; 7 | 8 | public class CompressionAttribute : ActionFilterAttribute 9 | { 10 | /// Initializes a new instance of the class. 11 | public CompressionAttribute() 12 | { 13 | this.Enabled = true; 14 | } 15 | 16 | /// Gets or sets a value indicating whether to enable compression for this request. 17 | /// true if enabled, false if not. The default value is false. 18 | public bool Enabled { get; set; } 19 | 20 | /// Executes the action executing asynchronous action. 21 | /// The action context. 22 | /// The cancellation token. 23 | /// A Task. 24 | public override Task OnActionExecutingAsync(HttpActionContext actionContext, CancellationToken cancellationToken) 25 | { 26 | actionContext.Request.Properties["compression:Enable"] = this.Enabled; 27 | 28 | return base.OnActionExecutingAsync(actionContext, cancellationToken); 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /src/Tests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Tests")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("Tests")] 13 | [assembly: AssemblyCopyright("Copyright © 2014")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("45ecb0d8-a4c6-4e4a-866e-7de3bcf4f1a8")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /src/Tests/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /dist/nuget/Microsoft.AspNet.WebApi.MessageHandlers.Compression.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Microsoft.AspNet.WebApi.MessageHandlers.Compression 5 | 2.0.0 6 | Microsoft ASP.NET Web API Compression Support 7 | Ove Andersen 8 | EyeCatch 9 | 10 | NOTE!!! 11 | 12 | This is an obsolete package, it only installs the Microsoft.AspNet.WebApi.Extensions.Compression.Server and System.Net.Http.Extensions.Compression.Client packages. 13 | 14 | NOTE!!! This is an obsolete package, it only installs the Microsoft.AspNet.WebApi.Extensions.Compression.Server and System.Net.Http.Extensions.Compression.Client packages. 15 | en-US 16 | https://github.com/azzlack/Microsoft.AspNet.WebApi.MessageHandlers.Compression 17 | http://www.eyecatch.no/favicon.ico 18 | false 19 | http://www.eyecatch.no/licenses/apache-20/ 20 | Copyright EyeCatch 2016 21 | 22 | 23 | 24 | 25 | 26 | aspnet webapi aspnetwebapi compression gzip deflate eyecatch 27 | 28 | -------------------------------------------------------------------------------- /src/Tests/Tests.v2.ncrunchproject: -------------------------------------------------------------------------------- 1 | 2 | 1000 3 | false 4 | false 5 | false 6 | true 7 | false 8 | false 9 | false 10 | false 11 | false 12 | true 13 | true 14 | false 15 | true 16 | true 17 | true 18 | 60000 19 | 20 | 21 | 22 | AutoDetect 23 | STA 24 | x86 25 | 26 | 27 | Tests.Tests.Facade.WebHostTests 28 | 29 | 30 | -------------------------------------------------------------------------------- /src/Server.Owin/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | // General Information about an assembly is controlled through the following 5 | // set of attributes. Change these attribute values to modify the information 6 | // associated with an assembly. 7 | [assembly: AssemblyTitle("Microsoft.AspNet.WebApi.Extensions.Compression.Server.Owin")] 8 | [assembly: AssemblyDescription("OWIN support for Microsoft.AspNet.WebApi.Extensions.Compression.Server")] 9 | [assembly: AssemblyConfiguration("")] 10 | [assembly: AssemblyCompany("EyeCatch")] 11 | [assembly: AssemblyProduct("Microsoft.AspNet.WebApi.Extensions.Compression.Server.Owin")] 12 | [assembly: AssemblyCopyright("Copyright © EyeCatch 2016")] 13 | [assembly: AssemblyTrademark("")] 14 | [assembly: AssemblyCulture("")] 15 | 16 | // Setting ComVisible to false makes the types in this assembly not visible 17 | // to COM components. If you need to access a type in this assembly from 18 | // COM, set the ComVisible attribute to true on that type. 19 | [assembly: ComVisible(false)] 20 | 21 | // The following GUID is for the ID of the typelib if this project is exposed to COM 22 | [assembly: Guid("daa0a504-7ed7-4906-9d6b-fb566ae47084")] 23 | 24 | // Version information for an assembly consists of the following four values: 25 | // 26 | // Major Version 27 | // Minor Version 28 | // Build Number 29 | // Revision 30 | // 31 | // You can specify all the values or you can default the Build and Revision Numbers 32 | // by using the '*' as shown below: 33 | // [assembly: AssemblyVersion("1.0.*")] 34 | [assembly: AssemblyVersion("1.0.0.0")] 35 | [assembly: AssemblyFileVersion("1.0.0.0")] 36 | -------------------------------------------------------------------------------- /src/Server/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | // General Information about an assembly is controlled through the following 5 | // set of attributes. Change these attribute values to modify the information 6 | // associated with an assembly. 7 | [assembly: AssemblyTitle("Microsoft.AspNet.WebApi.Extensions.Compression.Server")] 8 | [assembly: AssemblyDescription("Module for ASP.NET WebApi that enables GZip and Deflate support for incoming and outgoing requests")] 9 | [assembly: AssemblyConfiguration("")] 10 | [assembly: AssemblyCompany("EyeCatch")] 11 | [assembly: AssemblyProduct("Microsoft.AspNet.WebApi.Extensions.Compression.Server")] 12 | [assembly: AssemblyCopyright("Copyright © EyeCatch 2016")] 13 | [assembly: AssemblyTrademark("")] 14 | [assembly: AssemblyCulture("")] 15 | 16 | // Setting ComVisible to false makes the types in this assembly not visible 17 | // to COM components. If you need to access a type in this assembly from 18 | // COM, set the ComVisible attribute to true on that type. 19 | [assembly: ComVisible(false)] 20 | 21 | // The following GUID is for the ID of the typelib if this project is exposed to COM 22 | [assembly: Guid("ee709c23-849d-43c5-9f47-0b8d8ddb43d8")] 23 | 24 | // Version information for an assembly consists of the following four values: 25 | // 26 | // Major Version 27 | // Minor Version 28 | // Build Number 29 | // Revision 30 | // 31 | // You can specify all the values or you can default the Build and Revision Numbers 32 | // by using the '*' as shown below: 33 | // [assembly: AssemblyVersion("1.0.*")] 34 | [assembly: AssemblyVersion("1.0.0.0")] 35 | [assembly: AssemblyFileVersion("1.0.0.0")] 36 | -------------------------------------------------------------------------------- /src/Tests/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/Core/HttpContentOperations.cs: -------------------------------------------------------------------------------- 1 | namespace System.Net.Http.Extensions.Compression.Core 2 | { 3 | using System.IO; 4 | using System.Net.Http; 5 | using System.Net.Http.Extensions.Compression.Core.Extensions; 6 | using System.Net.Http.Extensions.Compression.Core.Interfaces; 7 | using System.Threading.Tasks; 8 | 9 | /// 10 | /// Helper methods for operating on instances. 11 | /// 12 | public class HttpContentOperations 13 | { 14 | /// 15 | /// Decompresses the compressed HTTP content. 16 | /// 17 | /// The compressed HTTP content. 18 | /// The compressor. 19 | /// The decompressed content. 20 | public async Task DecompressContent(HttpContent compressedContent, ICompressor compressor) 21 | { 22 | var decompressedContentStream = new MemoryStream(); 23 | 24 | // Decompress buffered content 25 | using (var ms = new MemoryStream(await compressedContent.ReadAsByteArrayAsync())) 26 | { 27 | await compressor.Decompress(ms, decompressedContentStream).ConfigureAwait(false); 28 | } 29 | 30 | // Set position back to 0 so it can be read again 31 | decompressedContentStream.Position = 0; 32 | 33 | var decompressedContent = new StreamContent(decompressedContentStream); 34 | 35 | // Copy content headers so we know what got sent back 36 | compressedContent.Headers.CopyTo(decompressedContent.Headers); 37 | 38 | return decompressedContent; 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /dist/nuget/Microsoft.AspNet.WebApi.Extensions.Compression.Server.Owin.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Microsoft.AspNet.WebApi.Extensions.Compression.Server.Owin 5 | 2.0.0 6 | OWIN support for Microsoft.AspNet.WebApi.Extensions.Compression.Server 7 | Ove Andersen 8 | EyeCatch 9 | Package for supporting OWIN when using Microsoft ASP.NET Web API Compression Support 10 | Package for supporting OWIN when using Microsoft ASP.NET Web API Compression Support. 11 | en-US 12 | https://github.com/azzlack/Microsoft.AspNet.WebApi.MessageHandlers.Compression 13 | http://www.eyecatch.no/favicon.ico 14 | false 15 | http://www.eyecatch.no/licenses/apache-20/ 16 | Copyright EyeCatch 2016 17 | 18 | 19 | 20 | 21 | 22 | aspnet webapi aspnetwebapi http compression gzip deflate eyecatch 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /src/Tests/Extensions/LongExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Tests.Extensions 2 | { 3 | using System; 4 | 5 | internal static class LongExtensions 6 | { 7 | /// 8 | /// Gets the size as a human readable string. 9 | /// 10 | /// The byte array. 11 | /// The size as a human readable string. 12 | public static string SizeAsHumanReadableString(this long input, SIType siType) 13 | { 14 | if (siType == SIType.Byte) 15 | { 16 | // Larger or equal to 1KB but lesser than 1MB 17 | if (input >= 1024 && input < 1048576) 18 | { 19 | return string.Format("{0}KB", input / 1024); 20 | } 21 | 22 | // Larger or equal to 1MB but lesser than 1GB 23 | if (input >= 1048576 && input < 1073741824) 24 | { 25 | return string.Format("{0}MB", input / 1024 / 1024); 26 | } 27 | 28 | // Larger or equal to 1Gt but lesser than 1TB 29 | if (input >= 1073741824 && input < 1099511627776) 30 | { 31 | return string.Format("{0}GB", input / 1024 / 1024 / 1024); 32 | } 33 | 34 | return string.Format("{0}bytes", input); 35 | } 36 | 37 | throw new NotImplementedException(); 38 | } 39 | 40 | /// 41 | /// Enumeration for the Internation System of Units (SI). 42 | /// 43 | public enum SIType 44 | { 45 | /// 46 | /// The byte unit type (B, kB, MB, GB, TB, etc.) 47 | /// 48 | Byte 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /dist/nuget/Microsoft.AspNet.WebApi.Extensions.Compression.Server.Owin.StrongName.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Microsoft.AspNet.WebApi.Extensions.Compression.Server.Owin.StrongName 5 | 2.0.0 6 | OWIN support for Microsoft.AspNet.WebApi.Extensions.Compression.Server.StrongName 7 | Ove Andersen 8 | EyeCatch 9 | Package for supporting OWIN when using Microsoft ASP.NET Web API Compression Support 10 | Package for supporting OWIN when using Microsoft ASP.NET Web API Compression Support. 11 | en-US 12 | https://github.com/azzlack/Microsoft.AspNet.WebApi.MessageHandlers.Compression 13 | http://www.eyecatch.no/favicon.ico 14 | false 15 | http://www.eyecatch.no/licenses/apache-20/ 16 | Copyright EyeCatch 2016 17 | 18 | 19 | 20 | 21 | 22 | aspnet webapi aspnetwebapi http compression gzip deflate eyecatch 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /src/Tests/Extensions/HttpResponseMessageExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Tests.Extensions 2 | { 3 | using Newtonsoft.Json; 4 | using System.Net.Http; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | public static class HttpResponseMessageExtensions 9 | { 10 | public static async Task ToTestString(this HttpResponseMessage response) 11 | { 12 | var sb = new StringBuilder(); 13 | 14 | sb.AppendLine(); 15 | sb.AppendLine($"Response Code: {(int)response.StatusCode} {response.StatusCode}"); 16 | sb.AppendLine(); 17 | sb.AppendLine("Headers:"); 18 | 19 | foreach (var header in response.Headers) 20 | { 21 | sb.AppendLine($" {header.Key}: {string.Join(", ", header.Value)}"); 22 | } 23 | 24 | if (response.Content != null) 25 | { 26 | foreach (var header in response.Content.Headers) 27 | { 28 | sb.AppendLine($" {header.Key}: {string.Join(", ", header.Value)}"); 29 | } 30 | 31 | sb.AppendLine(); 32 | sb.AppendLine("Content:"); 33 | 34 | if (response.Content.Headers.ContentType?.MediaType == "application/json") 35 | { 36 | sb.AppendLine( 37 | JsonConvert.SerializeObject( 38 | JsonConvert.DeserializeObject(await response.Content.ReadAsStringAsync()), 39 | Formatting.Indented)); 40 | } 41 | else 42 | { 43 | sb.AppendLine(await response.Content.ReadAsStringAsync()); 44 | } 45 | 46 | sb.AppendLine(); 47 | } 48 | 49 | return sb.ToString(); 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /src/Tests/web.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /src/Core/Compressors/GZipCompressor.cs: -------------------------------------------------------------------------------- 1 | namespace System.Net.Http.Extensions.Compression.Core.Compressors 2 | { 3 | using System.IO; 4 | using System.IO.Compression; 5 | using System.Net.Http.Extensions.Compression.Core.Interfaces; 6 | 7 | /// 8 | /// Compressor for handling gzip encodings. 9 | /// 10 | public class GZipCompressor : BaseCompressor 11 | { 12 | /// Initializes a new instance of the class. 13 | public GZipCompressor() 14 | { 15 | } 16 | 17 | /// Initializes a new instance of the class. 18 | /// Manager for stream. 19 | public GZipCompressor(IStreamManager streamManager) 20 | : base(streamManager) 21 | { 22 | } 23 | 24 | /// 25 | /// Gets the encoding type. 26 | /// 27 | /// The encoding type. 28 | public override string EncodingType 29 | { 30 | get { return "gzip"; } 31 | } 32 | 33 | /// 34 | /// Creates the compression stream. 35 | /// 36 | /// The output stream. 37 | /// The compressed stream. 38 | public override Stream CreateCompressionStream(Stream output) 39 | { 40 | return new GZipStream(output, CompressionMode.Compress, true); 41 | } 42 | 43 | /// 44 | /// Creates the decompression stream. 45 | /// 46 | /// The input stream. 47 | /// The decompressed stream. 48 | public override Stream CreateDecompressionStream(Stream input) 49 | { 50 | return new GZipStream(input, CompressionMode.Decompress, true); 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /src/Core/Compressors/DeflateCompressor.cs: -------------------------------------------------------------------------------- 1 | namespace System.Net.Http.Extensions.Compression.Core.Compressors 2 | { 3 | using System.IO; 4 | using System.IO.Compression; 5 | using System.Net.Http.Extensions.Compression.Core.Interfaces; 6 | 7 | /// 8 | /// Compressor for handling deflate encodings. 9 | /// 10 | public class DeflateCompressor : BaseCompressor 11 | { 12 | /// 13 | /// Initializes a new instance of the class. 14 | /// 15 | public DeflateCompressor() 16 | { 17 | } 18 | 19 | /// 20 | /// Initializes a new instance of the class. 21 | /// 22 | /// Manager for stream. 23 | public DeflateCompressor(IStreamManager streamManager) 24 | : base(streamManager) 25 | { 26 | } 27 | 28 | /// 29 | /// Gets the encoding type. 30 | /// 31 | /// The encoding type. 32 | public override string EncodingType 33 | { 34 | get { return "deflate"; } 35 | } 36 | 37 | /// 38 | /// Creates the compression stream. 39 | /// 40 | /// The output stream. 41 | /// The compressed stream. 42 | public override Stream CreateCompressionStream(Stream output) 43 | { 44 | return new DeflateStream(output, CompressionMode.Compress, true); 45 | } 46 | 47 | /// 48 | /// Creates the decompression stream. 49 | /// 50 | /// The input stream. 51 | /// The decompressed stream. 52 | public override Stream CreateDecompressionStream(Stream input) 53 | { 54 | return new DeflateStream(input, CompressionMode.Decompress, true); 55 | } 56 | } 57 | } -------------------------------------------------------------------------------- /src/Core/Extensions/HttpContentHeaderExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace System.Net.Http.Extensions.Compression.Core.Extensions 2 | { 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using System.Net.Http.Headers; 6 | 7 | public static class HttpContentHeaderExtensions 8 | { 9 | /// Copies all headers from the source to the target. 10 | /// The source. 11 | /// The target. 12 | /// true to handle content length. 13 | /// true to handle changed values. 14 | public static void CopyTo( 15 | this HttpContentHeaders source, 16 | HttpContentHeaders target, 17 | bool handleContentLength = true, 18 | bool handleChangedValues = false) 19 | { 20 | // Copy all other headers unless they have been modified and we want to preserve that 21 | foreach (var header in source) 22 | { 23 | try 24 | { 25 | if (!handleContentLength && header.Key.Equals("Content-Length", StringComparison.OrdinalIgnoreCase)) 26 | { 27 | continue; 28 | } 29 | 30 | if (!handleChangedValues) 31 | { 32 | // If values have changed, dont update it 33 | if (target.Contains(header.Key)) 34 | { 35 | if (target.GetValues(header.Key).Any(targetValue => header.Value.Any(originalValue => originalValue != targetValue))) 36 | { 37 | continue; 38 | } 39 | } 40 | } 41 | 42 | target.Add(header.Key, header.Value); 43 | } 44 | catch (Exception ex) 45 | { 46 | Debug.WriteLine(ex); 47 | } 48 | } 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /src/Tests/Tests/Integration/CompressionHandlerWithoutThresholdTests.cs: -------------------------------------------------------------------------------- 1 | namespace Tests.Tests.Integration 2 | { 3 | using global::Tests.Tests.Common; 4 | using Microsoft.AspNet.WebApi.Extensions.Compression.Server; 5 | using NUnit.Framework; 6 | using System.Net.Http; 7 | using System.Net.Http.Extensions.Compression.Client; 8 | using System.Net.Http.Extensions.Compression.Core; 9 | using System.Net.Http.Extensions.Compression.Core.Compressors; 10 | using System.Net.Http.Headers; 11 | using System.Web.Http; 12 | 13 | [TestFixture] 14 | public class CompressionHandlerWithoutThresholdTests : TestFixture 15 | { 16 | private HttpServer server; 17 | 18 | [TestFixtureSetUp] 19 | public void TestFixtureSetUp() 20 | { 21 | var config = new HttpConfiguration(); 22 | 23 | config.MapHttpAttributeRoutes(); 24 | 25 | config.Routes.MapHttpRoute( 26 | name: "DefaultApi", 27 | routeTemplate: "api/{controller}/{id}", 28 | defaults: new { id = RouteParameter.Optional }); 29 | 30 | config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always; 31 | 32 | config.MessageHandlers.Insert(0, new ServerCompressionHandler(0, new GZipCompressor(StreamManager.Instance), new DeflateCompressor(StreamManager.Instance))); 33 | 34 | this.server = new HttpServer(config); 35 | } 36 | 37 | [SetUp] 38 | public void SetUp() 39 | { 40 | var client = new HttpClient(new ClientCompressionHandler(this.server, 0, new GZipCompressor(), new DeflateCompressor())); 41 | client.DefaultRequestHeaders.AcceptEncoding.Add(new StringWithQualityHeaderValue("gzip")); 42 | client.DefaultRequestHeaders.AcceptEncoding.Add(new StringWithQualityHeaderValue("deflate")); 43 | 44 | this.Client = client; 45 | } 46 | 47 | [TestFixtureTearDown] 48 | public void TestFixtureTearDown() 49 | { 50 | if (this.server != null) 51 | { 52 | this.server.Dispose(); 53 | } 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /dist/nuget/Microsoft.AspNet.WebApi.Extensions.Compression.Server.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Microsoft.AspNet.WebApi.Extensions.Compression.Server 5 | 2.0.0 6 | Microsoft ASP.NET Web API Compression Support 7 | Ove Andersen 8 | EyeCatch 9 | 10 | Module for ASP.NET Web API that enables GZip and Deflate support for incoming and outgoing requests. 11 | Please note that this is not an official Microsoft package. 12 | 13 | 14 | Module for ASP.NET Web API that enables GZip and Deflate support for incoming and outgoing requests. Please note that this is not an official Microsoft package. 15 | 16 | en-US 17 | https://github.com/azzlack/Microsoft.AspNet.WebApi.MessageHandlers.Compression 18 | http://www.eyecatch.no/favicon.ico 19 | false 20 | http://www.eyecatch.no/licenses/apache-20/ 21 | Copyright EyeCatch 2016 22 | 23 | 24 | 25 | 26 | 27 | aspnet webapi aspnetwebapi http compression gzip deflate eyecatch 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /dist/nuget/Microsoft.AspNet.WebApi.Extensions.Compression.Server.StrongName.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Microsoft.AspNet.WebApi.Extensions.Compression.Server.StrongName 5 | 2.0.0 6 | Microsoft ASP.NET Web API Compression Support 7 | Ove Andersen 8 | EyeCatch 9 | 10 | Module for ASP.NET Web API that enables GZip and Deflate support for incoming and outgoing requests. 11 | Please note that this is not an official Microsoft package. 12 | 13 | 14 | Module for ASP.NET Web API that enables GZip and Deflate support for incoming and outgoing requests. Please note that this is not an official Microsoft package. 15 | 16 | en-US 17 | https://github.com/azzlack/Microsoft.AspNet.WebApi.MessageHandlers.Compression 18 | http://www.eyecatch.no/favicon.ico 19 | false 20 | http://www.eyecatch.no/licenses/apache-20/ 21 | Copyright EyeCatch 2016 22 | 23 | 24 | 25 | 26 | 27 | aspnet webapi aspnetwebapi http compression gzip deflate eyecatch 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /dist/nuget/System.Net.Http.Extensions.Compression.Client.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | System.Net.Http.Extensions.Compression.Client 5 | 2.0.0 6 | Microsoft HTTP Client Compression Support 7 | Ove Andersen 8 | EyeCatch 9 | 10 | Module for Microsoft HTTP Client that enables GZip and Deflate support for incoming and outgoing requests. 11 | Please note that this is not an official Microsoft package. 12 | 13 | 14 | Module for Microsoft HTTP Client that enables GZip and Deflate support for incoming and outgoing requests. Please note that this is not an official Microsoft package. 15 | 16 | en-US 17 | https://github.com/azzlack/Microsoft.AspNet.WebApi.MessageHandlers.Compression 18 | http://www.eyecatch.no/favicon.ico 19 | false 20 | http://www.eyecatch.no/licenses/apache-20/ 21 | Copyright EyeCatch 2016 22 | 23 | 24 | aspnet webapi aspnetwebapi http compression gzip deflate eyecatch 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /dist/nuget/System.Net.Http.Extensions.Compression.Client.StrongName.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | System.Net.Http.Extensions.Compression.Client.StrongName 5 | 2.0.0 6 | Microsoft HTTP Client Compression Support 7 | Ove Andersen 8 | EyeCatch 9 | 10 | Module for Microsoft HTTP Client that enables GZip and Deflate support for incoming and outgoing requests. 11 | Please note that this is not an official Microsoft package. 12 | 13 | 14 | Module for Microsoft HTTP Client that enables GZip and Deflate support for incoming and outgoing requests. Please note that this is not an official Microsoft package. 15 | 16 | en-US 17 | https://github.com/azzlack/Microsoft.AspNet.WebApi.MessageHandlers.Compression 18 | http://www.eyecatch.no/favicon.ico 19 | false 20 | http://www.eyecatch.no/licenses/apache-20/ 21 | Copyright EyeCatch 2016 22 | 23 | 24 | aspnet webapi aspnetwebapi http compression gzip deflate eyecatch 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /src/Tests/Tests/Integration/AcceptEncodingStarCompressionHandlerWithThresholdTests.cs: -------------------------------------------------------------------------------- 1 | namespace Tests.Tests.Integration 2 | { 3 | using global::Tests.Extensions; 4 | using global::Tests.Tests.Common; 5 | 6 | using Microsoft.AspNet.WebApi.Extensions.Compression.Server; 7 | using NUnit.Framework; 8 | using System; 9 | using System.Net.Http; 10 | using System.Net.Http.Extensions.Compression.Client; 11 | using System.Net.Http.Extensions.Compression.Core; 12 | using System.Net.Http.Extensions.Compression.Core.Compressors; 13 | using System.Net.Http.Headers; 14 | using System.Web.Http; 15 | 16 | [TestFixture] 17 | public class AcceptEncodingStarCompressionHandlerWithThresholdTests 18 | { 19 | private HttpServer server; 20 | 21 | public HttpClient Client { get; set; } 22 | 23 | [TestFixtureSetUp] 24 | public void TestFixtureSetUp() 25 | { 26 | var config = new HttpConfiguration(); 27 | 28 | config.MapHttpAttributeRoutes(); 29 | 30 | config.Routes.MapHttpRoute( 31 | name: "DefaultApi", 32 | routeTemplate: "api/{controller}/{id}", 33 | defaults: new { id = RouteParameter.Optional }); 34 | 35 | config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always; 36 | 37 | config.MessageHandlers.Insert(0, new ServerCompressionHandler(4096, new GZipCompressor(StreamManager.Instance), new DeflateCompressor(StreamManager.Instance))); 38 | 39 | this.server = new HttpServer(config); 40 | } 41 | 42 | [SetUp] 43 | public void SetUp() 44 | { 45 | var client = new HttpClient(new ClientCompressionHandler(this.server, 0, new GZipCompressor(), new DeflateCompressor())); 46 | client.DefaultRequestHeaders.AcceptEncoding.Add(new StringWithQualityHeaderValue("*")); 47 | 48 | this.Client = client; 49 | } 50 | 51 | [TestFixtureTearDown] 52 | public void TestFixtureTearDown() 53 | { 54 | if (this.server != null) 55 | { 56 | this.server.Dispose(); 57 | } 58 | } 59 | 60 | [Test] 61 | public async void Get_WhenResponseSizeIsLessThanTreshold_ShouldReturnUncompressedContent() 62 | { 63 | var response = await this.Client.GetAsync("http://localhost:55399/api/test"); 64 | 65 | var content = await response.Content.ReadAsStringAsync(); 66 | 67 | Console.Write(await response.ToTestString()); 68 | 69 | Assert.IsTrue(response.IsSuccessStatusCode); 70 | Assert.AreEqual(response.Content.Headers.ContentLength, content.Length); 71 | Assert.IsFalse(response.Content.Headers.ContentEncoding.Contains("gzip")); 72 | } 73 | 74 | [Test] 75 | public async void Get_WhenResponseSizeIsLargerThanTreshold_ShouldReturnCompressedContent() 76 | { 77 | var response = await this.Client.GetAsync("http://localhost:55399/api/file/image"); 78 | 79 | var content = await response.Content.ReadAsStringAsync(); 80 | 81 | Console.Write(await response.ToTestString()); 82 | 83 | Assert.IsTrue(response.IsSuccessStatusCode); 84 | Assert.AreNotEqual(response.Content.Headers.ContentLength, content.Length); 85 | Assert.IsTrue(response.Content.Headers.ContentEncoding.Contains("gzip")); 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/Tests/Tests/Integration/CompressionHandlerWithThresholdTests.cs: -------------------------------------------------------------------------------- 1 | namespace Tests.Tests.Integration 2 | { 3 | using global::Tests.Extensions; 4 | using global::Tests.Tests.Common; 5 | 6 | using Microsoft.AspNet.WebApi.Extensions.Compression.Server; 7 | using NUnit.Framework; 8 | using System; 9 | using System.Net.Http; 10 | using System.Net.Http.Extensions.Compression.Client; 11 | using System.Net.Http.Extensions.Compression.Core; 12 | using System.Net.Http.Extensions.Compression.Core.Compressors; 13 | using System.Net.Http.Headers; 14 | using System.Web.Http; 15 | 16 | [TestFixture] 17 | public class CompressionHandlerWithThresholdTests 18 | { 19 | private HttpServer server; 20 | 21 | public HttpClient Client { get; set; } 22 | 23 | [TestFixtureSetUp] 24 | public void TestFixtureSetUp() 25 | { 26 | var config = new HttpConfiguration(); 27 | 28 | config.MapHttpAttributeRoutes(); 29 | 30 | config.Routes.MapHttpRoute( 31 | name: "DefaultApi", 32 | routeTemplate: "api/{controller}/{id}", 33 | defaults: new { id = RouteParameter.Optional }); 34 | 35 | config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always; 36 | 37 | config.MessageHandlers.Insert(0, new ServerCompressionHandler(4096, new GZipCompressor(StreamManager.Instance), new DeflateCompressor(StreamManager.Instance))); 38 | 39 | this.server = new HttpServer(config); 40 | } 41 | 42 | [SetUp] 43 | public void SetUp() 44 | { 45 | var client = new HttpClient(new ClientCompressionHandler(this.server, 0, new GZipCompressor(), new DeflateCompressor())); 46 | client.DefaultRequestHeaders.AcceptEncoding.Add(new StringWithQualityHeaderValue("gzip")); 47 | client.DefaultRequestHeaders.AcceptEncoding.Add(new StringWithQualityHeaderValue("deflate")); 48 | 49 | this.Client = client; 50 | } 51 | 52 | [TestFixtureTearDown] 53 | public void TestFixtureTearDown() 54 | { 55 | if (this.server != null) 56 | { 57 | this.server.Dispose(); 58 | } 59 | } 60 | 61 | [Test] 62 | public async void Get_WhenResponseSizeIsLessThanTreshold_ShouldReturnUncompressedContent() 63 | { 64 | var response = await this.Client.GetAsync("http://localhost:55399/api/test"); 65 | 66 | var content = await response.Content.ReadAsStringAsync(); 67 | 68 | Console.Write(await response.ToTestString()); 69 | 70 | Assert.IsTrue(response.IsSuccessStatusCode); 71 | Assert.AreEqual(response.Content.Headers.ContentLength, content.Length); 72 | Assert.IsFalse(response.Content.Headers.ContentEncoding.Contains("gzip")); 73 | } 74 | 75 | [Test] 76 | public async void Get_WhenResponseSizeIsLargerThanTreshold_ShouldReturnCompressedContent() 77 | { 78 | var response = await this.Client.GetAsync("http://localhost:55399/api/file/image"); 79 | 80 | var content = await response.Content.ReadAsStringAsync(); 81 | 82 | Console.Write(await response.ToTestString()); 83 | 84 | Assert.IsTrue(response.IsSuccessStatusCode); 85 | Assert.AreNotEqual(response.Content.Headers.ContentLength, content.Length); 86 | Assert.IsTrue(response.Content.Headers.ContentEncoding.Contains("gzip")); 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.sln.docstates 8 | 9 | # Build results 10 | [Dd]ebug/ 11 | [Dd]ebugPublic/ 12 | [Rr]elease/ 13 | x64/ 14 | build/ 15 | bld/ 16 | [Bb]in/ 17 | [Oo]bj/ 18 | 19 | # MSTest test Results 20 | [Tt]est[Rr]esult*/ 21 | [Bb]uild[Ll]og.* 22 | 23 | #NUNIT 24 | *.VisualState.xml 25 | TestResult.xml 26 | 27 | # Build Results of an ATL Project 28 | [Dd]ebugPS/ 29 | [Rr]eleasePS/ 30 | dlldata.c 31 | 32 | *_i.c 33 | *_p.c 34 | *_i.h 35 | *.ilk 36 | *.meta 37 | *.obj 38 | *.pch 39 | *.pdb 40 | *.pgc 41 | *.pgd 42 | *.rsp 43 | *.sbr 44 | *.tlb 45 | *.tli 46 | *.tlh 47 | *.tmp 48 | *.tmp_proj 49 | *.log 50 | *.vspscc 51 | *.vssscc 52 | .builds 53 | *.pidb 54 | *.svclog 55 | *.scc 56 | 57 | # Chutzpah Test files 58 | _Chutzpah* 59 | 60 | # Visual C++ cache files 61 | ipch/ 62 | *.aps 63 | *.ncb 64 | *.opensdf 65 | *.sdf 66 | *.cachefile 67 | 68 | # Visual Studio profiler 69 | *.psess 70 | *.vsp 71 | *.vspx 72 | 73 | # TFS 2012 Local Workspace 74 | $tf/ 75 | 76 | # Guidance Automation Toolkit 77 | *.gpState 78 | 79 | # ReSharper is a .NET coding add-in 80 | _ReSharper*/ 81 | *.[Rr]e[Ss]harper 82 | *.DotSettings.user 83 | 84 | # JustCode is a .NET coding addin-in 85 | .JustCode 86 | 87 | # TeamCity is a build add-in 88 | _TeamCity* 89 | 90 | # DotCover is a Code Coverage Tool 91 | *.dotCover 92 | 93 | # NCrunch 94 | *.ncrunch* 95 | _NCrunch_* 96 | .*crunch*.local.xml 97 | 98 | # MightyMoose 99 | *.mm.* 100 | AutoTest.Net/ 101 | 102 | # Web workbench (sass) 103 | .sass-cache/ 104 | 105 | # Installshield output folder 106 | [Ee]xpress/ 107 | 108 | # DocProject is a documentation generator add-in 109 | DocProject/buildhelp/ 110 | DocProject/Help/*.HxT 111 | DocProject/Help/*.HxC 112 | DocProject/Help/*.hhc 113 | DocProject/Help/*.hhk 114 | DocProject/Help/*.hhp 115 | DocProject/Help/Html2 116 | DocProject/Help/html 117 | 118 | # Click-Once directory 119 | publish/ 120 | 121 | # Publish Web Output 122 | *.[Pp]ublish.xml 123 | *.azurePubxml 124 | 125 | # NuGet Packages Directory 126 | packages/ 127 | ## TODO: If the tool you use requires repositories.config uncomment the next line 128 | #!packages/repositories.config 129 | 130 | # Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets 131 | # This line needs to be after the ignore of the build folder (and the packages folder if the line above has been uncommented) 132 | !packages/build/ 133 | 134 | # Windows Azure Build Output 135 | csx/ 136 | *.build.csdef 137 | 138 | # Windows Store app package directory 139 | AppPackages/ 140 | 141 | # Others 142 | sql/ 143 | *.Cache 144 | ClientBin/ 145 | [Ss]tyle[Cc]op.* 146 | ~$* 147 | *~ 148 | *.dbmdl 149 | *.dbproj.schemaview 150 | *.pfx 151 | *.publishsettings 152 | node_modules/ 153 | 154 | # RIA/Silverlight projects 155 | Generated_Code/ 156 | 157 | # Backup & report files from converting an old project file to a newer 158 | # Visual Studio version. Backup files are not needed, because we have git ;-) 159 | _UpgradeReport_Files/ 160 | Backup*/ 161 | UpgradeLog*.XML 162 | UpgradeLog*.htm 163 | 164 | # SQL Server files 165 | *.mdf 166 | *.ldf 167 | 168 | # Business Intelligence projects 169 | *.rdl.data 170 | *.bim.layout 171 | *.bim_*.settings 172 | 173 | # Microsoft Fakes 174 | FakesAssemblies/ 175 | src/NuGetPackage/*.nupkg 176 | src/NuGetPackage/lib/ 177 | src/NuGetPackage/src/ 178 | *.nupkg 179 | -------------------------------------------------------------------------------- /src/Tests/Tests/Facade/OwinHostTests.cs: -------------------------------------------------------------------------------- 1 | namespace Tests.Tests.Facade 2 | { 3 | using System; 4 | using System.Net.Http; 5 | using System.Net.Http.Extensions.Compression.Client; 6 | using System.Net.Http.Extensions.Compression.Core.Compressors; 7 | using System.Net.Http.Headers; 8 | using System.Text; 9 | using System.Web.Http; 10 | 11 | using global::Tests.Extensions; 12 | using global::Tests.Handlers; 13 | using global::Tests.Models; 14 | using global::Tests.Tests.Common; 15 | 16 | using Microsoft.AspNet.Identity; 17 | using Microsoft.AspNet.WebApi.Extensions.Compression.Server; 18 | using Microsoft.AspNet.WebApi.Extensions.Compression.Server.Owin; 19 | using Microsoft.Owin; 20 | using Microsoft.Owin.Security.Cookies; 21 | using Microsoft.Owin.Testing; 22 | 23 | using Newtonsoft.Json; 24 | 25 | using NUnit.Framework; 26 | 27 | using Owin; 28 | 29 | [TestFixture] 30 | public class OwinHostTests : TestFixture 31 | { 32 | private TestServer server; 33 | 34 | [TestFixtureSetUp] 35 | public void TestFixtureSetup() 36 | { 37 | this.server = TestServer.Create(); 38 | } 39 | 40 | [SetUp] 41 | public void SetUp() 42 | { 43 | var client = new HttpClient(new TraceMessageHandler(new ClientCompressionHandler(this.server.Handler, new GZipCompressor(), new DeflateCompressor()))) 44 | { 45 | BaseAddress = new Uri("http://localhost:55399") 46 | }; 47 | 48 | client.DefaultRequestHeaders.AcceptEncoding.Add(new StringWithQualityHeaderValue("gzip")); 49 | client.DefaultRequestHeaders.AcceptEncoding.Add(new StringWithQualityHeaderValue("deflate")); 50 | 51 | this.Client = client; 52 | } 53 | 54 | [TestFixtureTearDown] 55 | public void TestFixtureTearDown() 56 | { 57 | this.server.Dispose(); 58 | } 59 | 60 | [Test] 61 | public async void Post_WhenHeaderIsModifiedOnServer_ShouldReturnModifiedHeader() 62 | { 63 | var loginModel = new LoginModel("Test", "Test", "http://localhost:55399/api/test"); 64 | var response = await this.Client.PostAsync("http://localhost:55399/api/test/login", new StringContent(JsonConvert.SerializeObject(loginModel), Encoding.UTF8, "application/json")); 65 | 66 | Console.Write(await response.ToTestString()); 67 | 68 | Assert.AreEqual(loginModel.ReturnUrl, response.Headers.Location.ToString()); 69 | } 70 | 71 | public class OwinStartup 72 | { 73 | public void Configuration(IAppBuilder appBuilder) 74 | { 75 | var config = new HttpConfiguration(); 76 | config.MapHttpAttributeRoutes(); 77 | config.Routes.MapHttpRoute("DefaultApi", "api/{controller}/{id}", new { id = RouteParameter.Optional }); 78 | 79 | // Add compression message handler 80 | config.MessageHandlers.Insert(0, new OwinServerCompressionHandler(0, new GZipCompressor(), new DeflateCompressor())); 81 | 82 | appBuilder.UseCookieAuthentication( 83 | new CookieAuthenticationOptions() 84 | { 85 | AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie, 86 | LoginPath = new PathString("/api/test/login") 87 | }); 88 | 89 | appBuilder.UseWebApi(config); 90 | } 91 | } 92 | } 93 | } -------------------------------------------------------------------------------- /src/Tests/Handlers/TraceMessageHandler.cs: -------------------------------------------------------------------------------- 1 | namespace Tests.Handlers 2 | { 3 | using System; 4 | using System.Diagnostics; 5 | using System.Net.Http; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | 9 | using global::Tests.Extensions; 10 | 11 | public class TraceMessageHandler : DelegatingHandler 12 | { 13 | /// 14 | /// Initializes a new instance of the class. 15 | /// 16 | public TraceMessageHandler() 17 | : this(new HttpClientHandler()) 18 | { 19 | } 20 | 21 | /// 22 | /// Initializes a new instance of the class. 23 | /// 24 | /// The inner handler. 25 | public TraceMessageHandler(HttpMessageHandler innerHandler) 26 | { 27 | this.InnerHandler = innerHandler; 28 | } 29 | 30 | /// 31 | /// Sends an HTTP request to the inner handler to send to the server as an asynchronous operation. 32 | /// 33 | /// The HTTP request message to send to the server. 34 | /// A cancellation token to cancel operation. 35 | /// Returns . The task object representing the asynchronous operation. 36 | protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) 37 | { 38 | var startTime = DateTime.Now; 39 | 40 | var requestMessage = string.Format("Request: {0} {1}", request.Method, request.RequestUri); 41 | Trace.TraceInformation(requestMessage); 42 | 43 | // Try to read out the request content 44 | if (request.Content != null) 45 | { 46 | await request.Content.LoadIntoBufferAsync(); 47 | 48 | Trace.TraceInformation("Request Body: {0}", await request.Content.ReadAsStringAsync()); 49 | } 50 | 51 | var response = await base.SendAsync(request, cancellationToken); 52 | 53 | var responseMessage = string.Format( 54 | "Response: {0} {1} - {{ Time: {2}, Size: {3} }}", 55 | (int)response.StatusCode, 56 | response.ReasonPhrase, 57 | DateTime.Now.Subtract(startTime).AsHumanReadableString(), 58 | response.Content != null ? response.Content.SizeAsHumanReadableString() : "0"); 59 | 60 | // Try to read out the response content 61 | if (response.Content != null) 62 | { 63 | await response.Content.LoadIntoBufferAsync(); 64 | } 65 | 66 | if (!response.IsSuccessStatusCode) 67 | { 68 | Trace.TraceError(responseMessage); 69 | 70 | if (response.Content != null) 71 | { 72 | Trace.TraceError(string.Format("Response Body: {0}", await response.Content.ReadAsStringAsync())); 73 | } 74 | } 75 | else 76 | { 77 | Trace.TraceInformation(responseMessage); 78 | 79 | if (response.Content != null) 80 | { 81 | Trace.TraceInformation("Response Body: {0}", await response.Content.ReadAsStringAsync()); 82 | } 83 | } 84 | 85 | return response; 86 | } 87 | } 88 | } -------------------------------------------------------------------------------- /src/Client/Client.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 10.0 6 | Debug 7 | AnyCPU 8 | {56E19FF7-9AC7-48C5-9AC2-105F55AA16E1} 9 | Library 10 | Properties 11 | System.Net.Http.Extensions.Compression.Client 12 | System.Net.Http.Extensions.Compression.Client 13 | en-US 14 | 512 15 | {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 16 | Profile111 17 | v4.5 18 | 19 | 20 | 21 | 22 | true 23 | full 24 | false 25 | bin\Debug\ 26 | DEBUG;TRACE 27 | prompt 28 | 4 29 | 30 | 31 | pdbonly 32 | true 33 | bin\Release\ 34 | TRACE 35 | prompt 36 | 4 37 | 38 | 39 | true 40 | bin\Test\ 41 | DEBUG;TRACE 42 | full 43 | AnyCPU 44 | prompt 45 | MinimumRecommendedRules.ruleset 46 | 47 | 48 | true 49 | bin\Staging\ 50 | DEBUG;TRACE 51 | full 52 | AnyCPU 53 | prompt 54 | MinimumRecommendedRules.ruleset 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | {b7534554-ab6c-44ac-bf34-424dbe596216} 63 | Core 64 | 65 | 66 | 67 | 74 | -------------------------------------------------------------------------------- /src/Tests/Controllers/TestController.cs: -------------------------------------------------------------------------------- 1 | namespace Tests.Controllers 2 | { 3 | using global::Tests.Models; 4 | using Microsoft.AspNet.Identity; 5 | using Microsoft.Owin.Security; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Net; 9 | using System.Net.Http; 10 | using System.Security.Claims; 11 | using System.Threading.Tasks; 12 | using System.Web.Http; 13 | 14 | public class TestController : ApiController 15 | { 16 | private IAuthenticationManager Authentication => this.Request.GetOwinContext().Authentication; 17 | 18 | public async Task Get() 19 | { 20 | return this.Request.CreateResponse(new TestModel("Get()")); 21 | } 22 | 23 | [Route("api/test/customheader")] 24 | public async Task GetCustomHeader() 25 | { 26 | var response = this.Request.CreateResponse(new TestModel("Get()")); 27 | response.Headers.Add("DataServiceVersion", "3.0"); 28 | 29 | return response; 30 | } 31 | 32 | [Route("api/test/customcontentencoding")] 33 | public async Task GetCustomContentEncoding() 34 | { 35 | var response = this.Request.CreateResponse(new TestModel("Get()")); 36 | response.Content.Headers.ContentEncoding.Add("lol"); 37 | 38 | return response; 39 | } 40 | 41 | [Route("api/test/redirect")] 42 | public async Task GetRedirect() 43 | { 44 | var response = this.Request.CreateResponse(HttpStatusCode.Redirect); 45 | response.Content = new ByteArrayContent(new byte[1024]); 46 | response.Content.Headers.ContentLength = 1024; 47 | response.Headers.Location = new Uri($"{this.Request.RequestUri.Scheme}://{this.Request.RequestUri.Authority}/api/test"); 48 | 49 | return response; 50 | } 51 | 52 | public async Task Get(string id) 53 | { 54 | return this.Request.CreateResponse(new TestModel("Get(" + id + ")")); 55 | } 56 | 57 | [HttpPost] 58 | public async Task Post(TestModel m) 59 | { 60 | return this.Request.CreateResponse(m); 61 | } 62 | 63 | [HttpPost] 64 | [Route("api/test/login")] 65 | public async Task Login(LoginModel m) 66 | { 67 | if (m.Username == m.Password) 68 | { 69 | this.Authentication.SignOut(DefaultAuthenticationTypes.ApplicationCookie); 70 | this.Authentication.SignIn( 71 | new AuthenticationProperties 72 | { 73 | IsPersistent = false, 74 | ExpiresUtc = DateTimeOffset.UtcNow.AddMinutes(1), 75 | RedirectUri = m.ReturnUrl 76 | }, 77 | new ClaimsIdentity(new List() { new Claim(ClaimTypes.NameIdentifier, m.Username) }, DefaultAuthenticationTypes.ApplicationCookie)); 78 | 79 | var response = this.Request.CreateResponse(HttpStatusCode.Redirect); 80 | response.Headers.Location = new Uri(m.ReturnUrl); 81 | 82 | return response; 83 | } 84 | 85 | return this.Request.CreateResponse(HttpStatusCode.Unauthorized); 86 | } 87 | 88 | [HttpPut] 89 | public async Task Put(string id, TestModel m) 90 | { 91 | return this.Request.CreateResponse(m); 92 | } 93 | 94 | [HttpDelete] 95 | public async Task Delete(string id) 96 | { 97 | return this.Request.CreateResponse(); 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/Core/Compressors/BaseCompressor.cs: -------------------------------------------------------------------------------- 1 | namespace System.Net.Http.Extensions.Compression.Core.Compressors 2 | { 3 | using System.IO; 4 | using System.Net.Http.Extensions.Compression.Core.Interfaces; 5 | using System.Threading.Tasks; 6 | 7 | /// 8 | /// Base compressor for compressing streams. 9 | /// 10 | /// 11 | /// Based on the work by: 12 | /// Ben Foster (http://benfoster.io/blog/aspnet-web-api-compression) 13 | /// Kiran Challa (http://blogs.msdn.com/b/kiranchalla/archive/2012/09/04/handling-compression-accept-encoding-sample.aspx) 14 | /// 15 | public abstract class BaseCompressor : ICompressor 16 | { 17 | /// Manager for stream. 18 | private readonly IStreamManager streamManager; 19 | 20 | /// Initializes a new instance of the class. 21 | protected BaseCompressor() 22 | { 23 | this.streamManager = StreamManager.Instance; 24 | } 25 | 26 | /// Initializes a new instance of the class. 27 | /// The stream manager. 28 | protected BaseCompressor(IStreamManager streamManager) 29 | { 30 | this.streamManager = streamManager; 31 | } 32 | 33 | /// 34 | /// Gets the encoding type. 35 | /// 36 | /// The encoding type. 37 | public abstract string EncodingType { get; } 38 | 39 | /// 40 | /// Creates the compression stream. 41 | /// 42 | /// The output stream. 43 | /// The compressed stream. 44 | public abstract Stream CreateCompressionStream(Stream output); 45 | 46 | /// 47 | /// Creates the decompression stream. 48 | /// 49 | /// The input stream. 50 | /// The decompressed stream. 51 | public abstract Stream CreateDecompressionStream(Stream input); 52 | 53 | /// 54 | /// Compresses the specified source stream onto the destination stream. 55 | /// 56 | /// The source. 57 | /// The destination. 58 | /// An async void. 59 | public virtual async Task Compress(Stream source, Stream destination) 60 | { 61 | using (var mem = this.streamManager.GetStream()) 62 | { 63 | using (var gzip = this.CreateCompressionStream(mem)) 64 | { 65 | await source.CopyToAsync(gzip); 66 | } 67 | 68 | mem.Position = 0; 69 | 70 | await mem.CopyToAsync(destination); 71 | 72 | return mem.Length; 73 | } 74 | } 75 | 76 | /// 77 | /// Decompresses the specified source stream onto the destination stream. 78 | /// 79 | /// The source. 80 | /// The destination. 81 | /// An async void. 82 | public virtual async Task Decompress(Stream source, Stream destination) 83 | { 84 | var decompressed = this.CreateDecompressionStream(source); 85 | 86 | await this.Pump(decompressed, destination); 87 | 88 | decompressed.Dispose(); 89 | } 90 | 91 | /// 92 | /// Copies the specified input stream onto the output stream. 93 | /// 94 | /// The input. 95 | /// The output. 96 | /// An async void. 97 | protected virtual async Task Pump(Stream input, Stream output) 98 | { 99 | await input.CopyToAsync(output); 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/Core/Core.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 10.0 6 | Debug 7 | AnyCPU 8 | {B7534554-AB6C-44AC-BF34-424DBE596216} 9 | Library 10 | Properties 11 | System.Net.Http.Extensions.Compression.Core 12 | System.Net.Http.Extensions.Compression.Core 13 | en-US 14 | 512 15 | {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 16 | Profile111 17 | v4.5 18 | 19 | 20 | true 21 | full 22 | false 23 | bin\Debug\ 24 | DEBUG;TRACE 25 | prompt 26 | 4 27 | 28 | 29 | pdbonly 30 | true 31 | bin\Release\ 32 | TRACE 33 | prompt 34 | 4 35 | 36 | 37 | true 38 | bin\Test\ 39 | DEBUG;TRACE 40 | full 41 | AnyCPU 42 | prompt 43 | MinimumRecommendedRules.ruleset 44 | 45 | 46 | true 47 | bin\Staging\ 48 | DEBUG;TRACE 49 | full 50 | AnyCPU 51 | prompt 52 | MinimumRecommendedRules.ruleset 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETPortable\v4.5\System.Net.Http.dll 69 | 70 | 71 | 72 | 79 | -------------------------------------------------------------------------------- /src/Client/Client.StrongName.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 10.0 6 | Debug 7 | AnyCPU 8 | {FF00D5E5-9581-4047-A5EE-CB968A567EE2} 9 | Library 10 | Properties 11 | System.Net.Http.Extensions.Compression.Client 12 | System.Net.Http.Extensions.Compression.Client 13 | en-US 14 | 512 15 | {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 16 | Profile111 17 | v4.5 18 | 19 | 20 | 21 | 22 | true 23 | full 24 | false 25 | bin\Debug.StrongName\ 26 | DEBUG;TRACE 27 | prompt 28 | 4 29 | 30 | 31 | pdbonly 32 | true 33 | bin\Release.StrongName\ 34 | TRACE 35 | prompt 36 | 4 37 | 38 | 39 | true 40 | bin\Test.StrongName\ 41 | DEBUG;TRACE 42 | full 43 | AnyCPU 44 | prompt 45 | MinimumRecommendedRules.ruleset 46 | 47 | 48 | true 49 | bin\Staging.StrongName\ 50 | DEBUG;TRACE 51 | full 52 | AnyCPU 53 | prompt 54 | MinimumRecommendedRules.ruleset 55 | 56 | 57 | true 58 | 59 | 60 | ..\StrongKey.snk 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | StrongKey.snk 69 | 70 | 71 | 72 | 73 | {2ef04e20-6cd8-420d-bac4-192ecd3f9ad5} 74 | Core.StrongName 75 | 76 | 77 | 78 | 85 | -------------------------------------------------------------------------------- /src/Core/Models/CompressedContent.cs: -------------------------------------------------------------------------------- 1 | namespace System.Net.Http.Extensions.Compression.Core.Models 2 | { 3 | using System; 4 | using System.IO; 5 | using System.Net; 6 | using System.Net.Http; 7 | using System.Net.Http.Extensions.Compression.Core.Extensions; 8 | using System.Net.Http.Extensions.Compression.Core.Interfaces; 9 | using System.Threading.Tasks; 10 | 11 | /// 12 | /// Represents compressed HTTP content. 13 | /// 14 | public class CompressedContent : HttpContent 15 | { 16 | /// 17 | /// The original content 18 | /// 19 | private readonly HttpContent originalContent; 20 | 21 | /// 22 | /// The compressor 23 | /// 24 | private readonly ICompressor compressor; 25 | 26 | /// 27 | /// Initializes a new instance of the class. 28 | /// 29 | /// The content. 30 | /// The compressor. 31 | public CompressedContent(HttpContent content, ICompressor compressor) 32 | { 33 | if (content == null) 34 | { 35 | throw new ArgumentNullException(nameof(content)); 36 | } 37 | 38 | if (compressor == null) 39 | { 40 | throw new ArgumentNullException(nameof(compressor)); 41 | } 42 | 43 | this.originalContent = content; 44 | this.compressor = compressor; 45 | 46 | this.CopyHeaders(); 47 | } 48 | 49 | /// 50 | /// Determines whether the HTTP content has a valid length in bytes. 51 | /// 52 | /// The length in bytes of the HTTP content. 53 | /// Returns .true if is a valid length; otherwise, false. 54 | protected override bool TryComputeLength(out long length) 55 | { 56 | length = -1; 57 | 58 | return false; 59 | } 60 | 61 | /// 62 | /// serialize to stream as an asynchronous operation. 63 | /// 64 | /// The target stream. 65 | /// Information about the transport (channel binding token, for example). This parameter may be null. 66 | /// Returns .The task object representing the asynchronous operation. 67 | protected override async Task SerializeToStreamAsync(Stream stream, TransportContext context) 68 | { 69 | if (stream == null) 70 | { 71 | throw new ArgumentNullException(nameof(stream)); 72 | } 73 | 74 | // Read and compress stream 75 | using (var ms = new MemoryStream(await this.originalContent.ReadAsByteArrayAsync())) 76 | { 77 | var compressedLength = await this.compressor.Compress(ms, stream).ConfigureAwait(false); 78 | 79 | // Content-Length: {size} 80 | this.Headers.ContentLength = compressedLength; 81 | } 82 | } 83 | 84 | /// 85 | /// Adds the headers. 86 | /// 87 | private void CopyHeaders() 88 | { 89 | this.originalContent.Headers.CopyTo(this.Headers, false); 90 | 91 | // Content-Encoding: {content-encodings} 92 | this.Headers.ContentEncoding.Add(this.compressor.EncodingType); 93 | } 94 | 95 | /// 96 | /// Releases the unmanaged resources used by the and optionally disposes of the managed resources. 97 | /// 98 | /// true to release both managed and unmanaged resources; false to releases only unmanaged resources. 99 | protected override void Dispose(bool disposing) 100 | { 101 | // Dispose original stream 102 | this.originalContent.Dispose(); 103 | 104 | base.Dispose(disposing); 105 | } 106 | } 107 | } -------------------------------------------------------------------------------- /src/Tests/Controllers/FileController.cs: -------------------------------------------------------------------------------- 1 | namespace Tests.Controllers 2 | { 3 | using global::Tests.Models; 4 | using iTextSharp.text; 5 | using iTextSharp.text.pdf; 6 | using Microsoft.AspNet.WebApi.Extensions.Compression.Server.Attributes; 7 | using System; 8 | using System.IO; 9 | using System.Net; 10 | using System.Net.Http; 11 | using System.Net.Http.Headers; 12 | using System.Threading.Tasks; 13 | using System.Web.Http; 14 | 15 | public class FileController : ApiController 16 | { 17 | [Route("api/file/image")] 18 | public async Task GetImage() 19 | { 20 | using (var ms = new MemoryStream()) 21 | { 22 | var baseDir = Directory.GetParent(AppDomain.CurrentDomain.BaseDirectory).Parent; 23 | var filePath = baseDir.FullName + "/Content/Images/app-icon-1024.png"; 24 | 25 | using (var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read)) 26 | { 27 | fs.Position = 0; 28 | await fs.CopyToAsync(ms); 29 | } 30 | 31 | var result = new HttpResponseMessage(HttpStatusCode.OK) 32 | { 33 | Content = new ByteArrayContent(ms.ToArray()) 34 | }; 35 | result.Content.Headers.ContentType = new MediaTypeHeaderValue("image/png"); 36 | 37 | return result; 38 | } 39 | } 40 | 41 | [Compression(Enabled = false)] 42 | [Route("api/file/uncompressedimage")] 43 | public async Task GetUncompressedImage() 44 | { 45 | using (var ms = new MemoryStream()) 46 | { 47 | var baseDir = Directory.GetParent(AppDomain.CurrentDomain.BaseDirectory).Parent; 48 | var filePath = baseDir.FullName + "/Content/Images/app-icon-1024.png"; 49 | 50 | using (var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read)) 51 | { 52 | fs.Position = 0; 53 | await fs.CopyToAsync(ms); 54 | } 55 | 56 | var result = new HttpResponseMessage(HttpStatusCode.OK) 57 | { 58 | Content = new ByteArrayContent(ms.ToArray()) 59 | }; 60 | result.Content.Headers.ContentType = new MediaTypeHeaderValue("image/png"); 61 | 62 | return result; 63 | } 64 | } 65 | 66 | [Route("api/file/pdf")] 67 | public async Task GetPdf() 68 | { 69 | var document = new Document(PageSize.A4); 70 | var pdfstream = new MemoryStream(); 71 | 72 | document.SetMargins(document.LeftMargin, document.RightMargin, document.TopMargin, document.BottomMargin + 10f); 73 | 74 | var writer = PdfWriter.GetInstance(document, pdfstream); 75 | writer.PageEvent = new PdfLayout(); 76 | 77 | // Write PDF document 78 | document.Open(); 79 | 80 | var baseDir = Directory.GetParent(AppDomain.CurrentDomain.BaseDirectory).Parent; 81 | 82 | var logoHeader = Image.GetInstance(baseDir.FullName + "/Content/Images/app-icon-1024.png"); 83 | logoHeader.ScalePercent(10); 84 | logoHeader.SetAbsolutePosition(document.PageSize.Width - logoHeader.ScaledWidth - document.RightMargin, document.PageSize.Height - document.TopMargin - logoHeader.ScaledHeight); 85 | document.Add(logoHeader); 86 | 87 | var headline = new Paragraph("Unicorns!"); 88 | 89 | document.Add(headline); 90 | 91 | document.Add(new Paragraph(" ")); 92 | 93 | document.Close(); 94 | 95 | var result = new HttpResponseMessage(HttpStatusCode.OK) { Content = new StreamContent(new MemoryStream(pdfstream.ToArray())) }; 96 | 97 | result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf"); 98 | result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment") 99 | { 100 | FileName = "unicorns.pdf" 101 | }; 102 | 103 | return result; 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/Core/Core.StrongName.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 10.0 6 | Debug 7 | AnyCPU 8 | {2EF04E20-6CD8-420D-BAC4-192ECD3F9AD5} 9 | Library 10 | Properties 11 | System.Net.Http.Extensions.Compression.Core 12 | System.Net.Http.Extensions.Compression.Core 13 | en-US 14 | 512 15 | {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 16 | Profile111 17 | v4.5 18 | 19 | 20 | true 21 | full 22 | false 23 | bin\Debug.StrongName\ 24 | DEBUG;TRACE 25 | prompt 26 | 4 27 | 28 | 29 | pdbonly 30 | true 31 | bin\Release.StrongName\ 32 | TRACE 33 | prompt 34 | 4 35 | 36 | 37 | true 38 | bin\Test.StrongName\ 39 | DEBUG;TRACE 40 | full 41 | AnyCPU 42 | prompt 43 | MinimumRecommendedRules.ruleset 44 | 45 | 46 | true 47 | bin\Staging.StrongName\ 48 | DEBUG;TRACE 49 | full 50 | AnyCPU 51 | prompt 52 | MinimumRecommendedRules.ruleset 53 | 54 | 55 | true 56 | 57 | 58 | ..\StrongKey.snk 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETPortable\v4.5\System.Net.Http.dll 75 | 76 | 77 | 78 | 79 | StrongKey.snk 80 | 81 | 82 | 83 | 90 | -------------------------------------------------------------------------------- /src/Server/Server.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {EE709C23-849D-43C5-9F47-0B8D8DDB43D8} 8 | Library 9 | Properties 10 | Microsoft.AspNet.WebApi.Extensions.Compression.Server 11 | Microsoft.AspNet.WebApi.Extensions.Compression.Server 12 | v4.5 13 | 512 14 | 15 | 16 | 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | pdbonly 27 | true 28 | bin\Release\ 29 | TRACE 30 | prompt 31 | 4 32 | 33 | 34 | true 35 | bin\Test\ 36 | DEBUG;TRACE 37 | full 38 | AnyCPU 39 | prompt 40 | MinimumRecommendedRules.ruleset 41 | 42 | 43 | true 44 | bin\Staging\ 45 | DEBUG;TRACE 46 | full 47 | AnyCPU 48 | prompt 49 | MinimumRecommendedRules.ruleset 50 | 51 | 52 | 53 | ..\packages\Microsoft.IO.RecyclableMemoryStream.1.1.0.0\lib\net45\Microsoft.IO.RecyclableMemoryStream.dll 54 | True 55 | 56 | 57 | ..\packages\Newtonsoft.Json.6.0.4\lib\net45\Newtonsoft.Json.dll 58 | True 59 | 60 | 61 | 62 | 63 | ..\packages\Microsoft.AspNet.WebApi.Client.5.2.3\lib\net45\System.Net.Http.Formatting.dll 64 | True 65 | 66 | 67 | False 68 | ..\packages\Microsoft.AspNet.WebApi.Core.5.2.3\lib\net45\System.Web.Http.dll 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | {B7534554-AB6C-44AC-BF34-424DBE596216} 91 | Core 92 | 93 | 94 | 95 | 102 | -------------------------------------------------------------------------------- /src/Server/Server.StrongName.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {297AC86E-3B54-4E76-A4EA-A5BF1E730D6F} 8 | Library 9 | Properties 10 | Microsoft.AspNet.WebApi.Extensions.Compression.Server 11 | Microsoft.AspNet.WebApi.Extensions.Compression.Server 12 | v4.5 13 | 512 14 | 15 | 16 | 17 | true 18 | full 19 | false 20 | bin\Debug.StrongName\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | pdbonly 27 | true 28 | bin\Release.StrongName\ 29 | TRACE 30 | prompt 31 | 4 32 | 33 | 34 | true 35 | bin\Test.StrongName\ 36 | DEBUG;TRACE 37 | full 38 | AnyCPU 39 | prompt 40 | MinimumRecommendedRules.ruleset 41 | 42 | 43 | true 44 | bin\Staging.StrongName\ 45 | DEBUG;TRACE 46 | full 47 | AnyCPU 48 | prompt 49 | MinimumRecommendedRules.ruleset 50 | 51 | 52 | true 53 | 54 | 55 | ..\StrongKey.snk 56 | 57 | 58 | 59 | ..\packages\Microsoft.IO.RecyclableMemoryStream.1.1.0.0\lib\net45\Microsoft.IO.RecyclableMemoryStream.dll 60 | True 61 | 62 | 63 | ..\packages\Newtonsoft.Json.6.0.4\lib\net45\Newtonsoft.Json.dll 64 | True 65 | 66 | 67 | 68 | 69 | ..\packages\Microsoft.AspNet.WebApi.Client.5.2.3\lib\net45\System.Net.Http.Formatting.dll 70 | True 71 | 72 | 73 | False 74 | ..\packages\Microsoft.AspNet.WebApi.Core.5.2.3\lib\net45\System.Web.Http.dll 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | StrongKey.snk 93 | 94 | 95 | 96 | 97 | 98 | 99 | {2ef04e20-6cd8-420d-bac4-192ecd3f9ad5} 100 | Core.StrongName 101 | 102 | 103 | 104 | 111 | -------------------------------------------------------------------------------- /src/Server.Owin/Server.Owin.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {DAA0A504-7ED7-4906-9D6B-FB566AE47084} 8 | Library 9 | Properties 10 | Microsoft.AspNet.WebApi.Extensions.Compression.Server.Owin 11 | Microsoft.AspNet.WebApi.Extensions.Compression.Server.Owin 12 | v4.5 13 | 512 14 | 15 | 16 | true 17 | full 18 | false 19 | bin\Debug\ 20 | DEBUG;TRACE 21 | prompt 22 | 4 23 | 24 | 25 | pdbonly 26 | true 27 | bin\Release\ 28 | TRACE 29 | prompt 30 | 4 31 | 32 | 33 | true 34 | bin\Test\ 35 | DEBUG;TRACE 36 | full 37 | AnyCPU 38 | prompt 39 | MinimumRecommendedRules.ruleset 40 | 41 | 42 | true 43 | bin\Staging\ 44 | DEBUG;TRACE 45 | full 46 | AnyCPU 47 | prompt 48 | MinimumRecommendedRules.ruleset 49 | 50 | 51 | 52 | ..\packages\Microsoft.Owin.3.0.1\lib\net45\Microsoft.Owin.dll 53 | True 54 | 55 | 56 | ..\packages\Newtonsoft.Json.6.0.4\lib\net45\Newtonsoft.Json.dll 57 | True 58 | 59 | 60 | ..\packages\Owin.1.0\lib\net40\Owin.dll 61 | True 62 | 63 | 64 | 65 | 66 | ..\packages\Microsoft.AspNet.WebApi.Client.5.2.3\lib\net45\System.Net.Http.Formatting.dll 67 | True 68 | 69 | 70 | ..\packages\Microsoft.AspNet.WebApi.Core.5.2.3\lib\net45\System.Web.Http.dll 71 | True 72 | 73 | 74 | ..\packages\Microsoft.AspNet.WebApi.Owin.5.2.3\lib\net45\System.Web.Http.Owin.dll 75 | True 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | {b7534554-ab6c-44ac-bf34-424dbe596216} 91 | Core 92 | 93 | 94 | {ee709c23-849d-43c5-9f47-0b8d8ddb43d8} 95 | Server 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 110 | -------------------------------------------------------------------------------- /src/Server.Owin/Server.Owin.StrongName.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {AA0CD548-E262-44AB-9AA2-A7ADA6998C99} 8 | Library 9 | Properties 10 | Microsoft.AspNet.WebApi.Extensions.Compression.Server.Owin 11 | Microsoft.AspNet.WebApi.Extensions.Compression.Server.Owin 12 | v4.5 13 | 512 14 | 15 | 16 | true 17 | full 18 | false 19 | bin\Debug.StrongName\ 20 | DEBUG;TRACE 21 | prompt 22 | 4 23 | 24 | 25 | pdbonly 26 | true 27 | bin\Release.StrongName\ 28 | TRACE 29 | prompt 30 | 4 31 | 32 | 33 | true 34 | bin\Test.StrongName\ 35 | DEBUG;TRACE 36 | full 37 | AnyCPU 38 | prompt 39 | MinimumRecommendedRules.ruleset 40 | 41 | 42 | true 43 | bin\Staging.StrongName\ 44 | DEBUG;TRACE 45 | full 46 | AnyCPU 47 | prompt 48 | MinimumRecommendedRules.ruleset 49 | 50 | 51 | true 52 | 53 | 54 | ..\StrongKey.snk 55 | 56 | 57 | 58 | ..\packages\Microsoft.Owin.3.0.1\lib\net45\Microsoft.Owin.dll 59 | True 60 | 61 | 62 | ..\packages\Newtonsoft.Json.6.0.4\lib\net45\Newtonsoft.Json.dll 63 | True 64 | 65 | 66 | ..\packages\Owin.1.0\lib\net40\Owin.dll 67 | True 68 | 69 | 70 | 71 | 72 | ..\packages\Microsoft.AspNet.WebApi.Client.5.2.3\lib\net45\System.Net.Http.Formatting.dll 73 | True 74 | 75 | 76 | ..\packages\Microsoft.AspNet.WebApi.Core.5.2.3\lib\net45\System.Web.Http.dll 77 | True 78 | 79 | 80 | ..\packages\Microsoft.AspNet.WebApi.Owin.5.2.3\lib\net45\System.Web.Http.Owin.dll 81 | True 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | StrongKey.snk 97 | 98 | 99 | 100 | 101 | 102 | 103 | {2ef04e20-6cd8-420d-bac4-192ecd3f9ad5} 104 | Core.StrongName 105 | 106 | 107 | {297ac86e-3b54-4e76-a4ea-a5bf1e730d6f} 108 | Server.StrongName 109 | 110 | 111 | 112 | 119 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Compression support for ASP.NET WebAPI and HttpClient 2 | =================================================== 3 | [![Build status](https://ci.appveyor.com/api/projects/status/a4u9vjftr5cf5hja?svg=true)](https://ci.appveyor.com/project/azzlack/microsoft-aspnet-webapi-messagehandlers-compressio) 4 | 5 | 6 | Drop-in module for ASP.Net WebAPI that enables `GZip` and `Deflate` support. 7 | This module is based on [this blog post by Ben Foster](http://benfoster.io/blog/aspnet-web-api-compression) which in turn is based on this blog post by [Kiran Challa](http://blogs.msdn.com/b/kiranchalla/archive/2012/09/04/handling-compression-accept-encoding-sample.aspx). 8 | This code improves on their work by adding several new options, as well as fixing some issues with the original code. 9 | 10 | | Package | Version | Downloads | 11 | | ------- | ------- | --------- | 12 | | Microsoft.AspNet.WebApi.Extensions.Compression.Server | [![NuGet Package Version](http://img.shields.io/nuget/v/Microsoft.AspNet.WebApi.Extensions.Compression.Server.svg?style=flat-square)](https://www.nuget.org/packages/Microsoft.AspNet.WebApi.Extensions.Compression.Server/) | [![NuGet Package Downloads](http://img.shields.io/nuget/dt/Microsoft.AspNet.WebApi.Extensions.Compression.Server.svg?style=flat-square)](https://www.nuget.org/packages/Microsoft.AspNet.WebApi.Extensions.Compression.Server/) | 13 | | System.Net.Http.Extensions.Compression.Client | [![NuGet Package Version](http://img.shields.io/nuget/v/System.Net.Http.Extensions.Compression.Client.svg?style=flat-square)](https://www.nuget.org/packages/System.Net.Http.Extensions.Compression.Client/) | [![NuGet Package Downloads](http://img.shields.io/nuget/dt/System.Net.Http.Extensions.Compression.Client.svg?style=flat-square)](https://www.nuget.org/packages/System.Net.Http.Extensions.Compression.Client/) | 14 | 15 | ## How to use 16 | ### Server side 17 | You need to add the compression handler as the last applied message handler on outgoing requests, and the first one on incoming requests. 18 | To do that, just add the following line to your `App_Start\WebApiConfig.cs` file after adding all your other message handlers: 19 | ```csharp 20 | GlobalConfiguration.Configuration.MessageHandlers.Insert(0, new ServerCompressionHandler(new GZipCompressor(), new DeflateCompressor())); 21 | ``` 22 | This will insert the `ServerCompressionHandler` to the request pipeline as the first on incoming requests, and the last on outgoing requests. 23 | 24 | ### Client side 25 | 26 | #### JavaScript 27 | If you are doing your requests with `JavaScript` you probably don't have to do do anything. 28 | Just make sure the `gzip` and `deflate` values are included in the `Accept-Encoding` header. (Most browsers do this by default) 29 | 30 | #### C\# 31 | You need to apply the following code when creating your `HttpClient`. 32 | ```csharp 33 | var client = new HttpClient(new ClientCompressionHandler(new GZipCompressor(), new DeflateCompressor())); 34 | 35 | client.DefaultRequestHeaders.AcceptEncoding.Add(new StringWithQualityHeaderValue("gzip")); 36 | client.DefaultRequestHeaders.AcceptEncoding.Add(new StringWithQualityHeaderValue("deflate")); 37 | ``` 38 | 39 | Thats it! You should now immediately start experiencing smaller payloads when doing GET, POST, PUT, etc. 40 | 41 | ## Advanced use 42 | #### Skip compression of requests/responses that are smaller than a specified value 43 | By default, both `ServerCompressionHandler` and `ClientCompressionHandler` compress everything larger than `860 bytes`. 44 | However, this can be overriden by inserting a threshold as the first parameter like this: 45 | ```csharp 46 | var serverCompressionHandler = new ServerCompressionHandler(4096, new GZipCompressor(), new DeflateCompressor()); 47 | var clientCompressionHandler = new ClientCompressionHandler(4096, new GZipCompressor(), new DeflateCompressor()); 48 | ``` 49 | The above code will skip compression for any request/response that is smaller than `4096 bytes` / `4 kB`. 50 | 51 | #### Disable compression for endpoint 52 | It is possible to disable compression for a specific endpoint. Just add the `[Compression(Enabled = false)]` attribute to your endpoint method. (Or the whole controller if you want to disable for all endpoints in it) 53 | 54 | #### OWIN Authentication 55 | When using the OWIN Authentication pipeline, you might encounter errors saying that `Server cannot append header after http headers have been sent`. This is a [bug with OWIN](http://katanaproject.codeplex.com/discussions/540202), but as of this moment it has not been fixed. 56 | 57 | The workaround is to install the [Microsoft.AspNet.WebApi.Extensions.Compression.Server.Owin](https://www.nuget.org/packages/Microsoft.AspNet.WebApi.Extensions.Compression.Server.Owin/) package and use the included `OwinServerCompressionHandler` instead of the default `ServerCompressionHandler`. This class contains code to detect whether the headers have been sent already and prevent any attempts at compression. 58 | 59 | ## Version history 60 | #### 2.0.3 (current) 61 | * [Fixed unnecessary dependency on .NET 4.5.2](https://github.com/azzlack/Microsoft.AspNet.WebApi.MessageHandlers.Compression/issues/28) 62 | * [Fixed System.ArgumentException in CompressionAttribute](https://github.com/azzlack/Microsoft.AspNet.WebApi.MessageHandlers.Compression/issues/25) (Thanks to [SneakyMax](https://github.com/SneakyMax)) 63 | * Various readme fixes (Thanks to [wiltodelta](https://github.com/wiltodelta), [IainCole](https://github.com/IainCole), [coni2k](https://github.com/coni2k) and [altumano](https://github.com/altumano)) 64 | 65 | #### 2.0.0 66 | * Fixed [UWP projects referencing Microsoft.AspNet.WebApi.MessageHandlers.Compression does not compile](https://github.com/azzlack/Microsoft.AspNet.WebApi.MessageHandlers.Compression/issues/17) 67 | * Fixed [Remove Microsoft.Bcl dependency in .NET 4.5](https://github.com/azzlack/Microsoft.AspNet.WebApi.MessageHandlers.Compression/issues/23) 68 | * Fixed [compressing even when accept encoding is null](https://github.com/azzlack/Microsoft.AspNet.WebApi.MessageHandlers.Compression/issues/21) 69 | * Fixed [Possible issue while redirecting after de-compression](https://github.com/azzlack/Microsoft.AspNet.WebApi.MessageHandlers.Compression/issues/11) and [Owin UseCookieAuthentication does not work anymore after we insert ServerCompressionHandler](https://github.com/azzlack/Microsoft.AspNet.WebApi.MessageHandlers.Compression/issues/22) 70 | * Fixed [Async throttled actions with disabled compression](https://github.com/azzlack/Microsoft.AspNet.WebApi.MessageHandlers.Compression/issues/20) 71 | * Improved performance a little by not always buffering the response. See #6 72 | * Fixed [Server cannot append header after HTTP headers have been sent](https://github.com/azzlack/Microsoft.AspNet.WebApi.MessageHandlers.Compression/issues/13). NOTE: The fix is in a [separate package](https://www.nuget.org/packages/Microsoft.AspNet.WebApi.Extensions.Compression.Server.Owin/) 73 | 74 | #### [1.3.0](https://www.nuget.org/packages/Microsoft.AspNet.WebApi.MessageHandlers.Compression/1.3.0) 75 | * Added attribute for disable compression for certain routes 76 | * Fixed clearing of non-standard properties when compressing and decompressing 77 | 78 | #### [1.2.2](https://www.nuget.org/packages/Microsoft.AspNet.WebApi.MessageHandlers.Compression/1.2.2) 79 | * Stop trying to compress requests/responses with no content 80 | 81 | #### [1.2.1](https://www.nuget.org/packages/Microsoft.AspNet.WebApi.MessageHandlers.Compression/1.2.1) 82 | * Properly copy HTTP headers from the content that is going to be compressed 83 | 84 | #### [1.2.0](https://www.nuget.org/packages/Microsoft.AspNet.WebApi.MessageHandlers.Compression/1.2.0) 85 | * Fixed 504 timeout error when returning `ByteArrayContent` from an `ApiController` 86 | * Fixed bug with content stream sometimes being disposed before returning 87 | * Added better test coverage 88 | 89 | #### [1.1.2](https://www.nuget.org/packages/Microsoft.AspNet.WebApi.MessageHandlers.Compression/1.1.2) 90 | * Changed default threshold for compression to 860 bytes (what Akamai uses) 91 | * Now reports proper Content-Length 92 | 93 | #### [1.1.0](https://www.nuget.org/packages/Microsoft.AspNet.WebApi.MessageHandlers.Compression/1.1.0) 94 | * Simplified usage 95 | * Added support for setting a minimum content size for compressing 96 | 97 | #### [1.0.3](https://www.nuget.org/packages/Microsoft.AspNet.WebApi.MessageHandlers.Compression/1.0.3) 98 | * First release, basic compression of server responses and client requests 99 | * Did not support compressing POSTs and PUTs 100 | -------------------------------------------------------------------------------- /src/Server/ServerCompressionHandler.cs: -------------------------------------------------------------------------------- 1 | namespace Microsoft.AspNet.WebApi.Extensions.Compression.Server 2 | { 3 | using System; 4 | using System.Net.Http; 5 | using System.Net.Http.Extensions.Compression.Core.Compressors; 6 | using System.Net.Http.Extensions.Compression.Core.Interfaces; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | 10 | /// 11 | /// Message handler for handling gzip/deflate requests/responses. 12 | /// 13 | public class ServerCompressionHandler : BaseServerCompressionHandler 14 | { 15 | /// 16 | /// Initializes a new instance of the class. 17 | /// 18 | public ServerCompressionHandler() 19 | : base(null, 860, new GZipCompressor(RecyclableStreamManager.Instance), new DeflateCompressor(RecyclableStreamManager.Instance)) 20 | { 21 | } 22 | 23 | /// 24 | /// Initializes a new instance of the class. 25 | /// 26 | /// The compressors. 27 | public ServerCompressionHandler(params ICompressor[] compressors) 28 | : base(null, 860, null, false, compressors) 29 | { 30 | } 31 | 32 | /// 33 | /// Initializes a new instance of the class. 34 | /// 35 | /// ConfigureAwait value. 36 | /// The compressors. 37 | public ServerCompressionHandler(bool continueOnCapturedContext, params ICompressor[] compressors) 38 | : base(null, 860, null, continueOnCapturedContext, compressors) 39 | { 40 | } 41 | 42 | /// 43 | /// Initializes a new instance of the class. 44 | /// 45 | /// The content size threshold before compressing. 46 | /// The compressors. 47 | public ServerCompressionHandler(int contentSizeThreshold, params ICompressor[] compressors) 48 | : base(null, contentSizeThreshold, null, false, compressors) 49 | { 50 | } 51 | 52 | /// 53 | /// Initializes a new instance of the class. 54 | /// 55 | /// The content size threshold before compressing. 56 | /// ConfigureAwait value. 57 | /// The compressors. 58 | public ServerCompressionHandler(int contentSizeThreshold, bool continueOnCapturedContext, params ICompressor[] compressors) 59 | : base(null, contentSizeThreshold, null, continueOnCapturedContext, compressors) 60 | { 61 | } 62 | 63 | /// 64 | /// Initializes a new instance of the class. 65 | /// 66 | /// The inner handler. 67 | /// The compressors. 68 | public ServerCompressionHandler(HttpMessageHandler innerHandler, params ICompressor[] compressors) 69 | : base(innerHandler, 860, null, false, compressors) 70 | { 71 | } 72 | 73 | /// 74 | /// Initializes a new instance of the class. 75 | /// 76 | /// The inner handler. 77 | /// ConfigureAwait value. 78 | /// The compressors. 79 | public ServerCompressionHandler(HttpMessageHandler innerHandler, bool continueOnCapturedContext, params ICompressor[] compressors) 80 | : base(innerHandler, 860, null, continueOnCapturedContext, compressors) 81 | { 82 | } 83 | 84 | /// 85 | /// Initializes a new instance of the class. 86 | /// 87 | /// The inner handler. 88 | /// The content size threshold before compressing. 89 | /// The compressors. 90 | public ServerCompressionHandler(HttpMessageHandler innerHandler, int contentSizeThreshold, params ICompressor[] compressors) 91 | : base(innerHandler, contentSizeThreshold, null, false, compressors) 92 | { 93 | } 94 | 95 | /// 96 | /// Initializes a new instance of the class. 97 | /// 98 | /// The inner handler. 99 | /// The content size threshold before compressing. 100 | /// ConfigureAwait value. 101 | /// The compressors. 102 | public ServerCompressionHandler(HttpMessageHandler innerHandler, int contentSizeThreshold, bool continueOnCapturedContext, params ICompressor[] compressors) 103 | : base(innerHandler, contentSizeThreshold, null, continueOnCapturedContext, compressors) 104 | { 105 | } 106 | 107 | /// 108 | /// Initializes a new instance of the class. 109 | /// 110 | /// The inner handler. 111 | /// The content size threshold before compressing. 112 | /// Custom delegate to enable or disable compression. 113 | /// The compressors. 114 | public ServerCompressionHandler(HttpMessageHandler innerHandler, int contentSizeThreshold, Predicate enableCompression, params ICompressor[] compressors) 115 | : base(innerHandler, contentSizeThreshold, enableCompression, false, compressors) 116 | { 117 | } 118 | 119 | /// 120 | /// Initializes a new instance of the class. 121 | /// 122 | /// The inner handler. 123 | /// The content size threshold before compressing. 124 | /// Custom delegate to enable or disable compression. 125 | /// ConfigureAwait value. 126 | /// The compressors. 127 | public ServerCompressionHandler(HttpMessageHandler innerHandler, int contentSizeThreshold, 128 | Predicate enableCompression, bool continueOnCapturedContext, 129 | params ICompressor[] compressors) 130 | : base(innerHandler, contentSizeThreshold, enableCompression, continueOnCapturedContext, compressors) 131 | { 132 | } 133 | 134 | /// Handles the request. 135 | /// The request. 136 | /// A cancellation token to cancel operation. 137 | /// A Task<HttpResponseMessage> 138 | public override Task HandleRequest(HttpRequestMessage request, CancellationToken cancellationToken) 139 | { 140 | return this.HandleDecompression(request, cancellationToken); 141 | } 142 | 143 | /// Handles the response. 144 | /// The request. 145 | /// The response. 146 | /// A cancellation token to cancel operation. 147 | /// The handled response. 148 | public override Task HandleResponse(HttpRequestMessage request, HttpResponseMessage response, CancellationToken cancellationToken) 149 | { 150 | return this.HandleCompression(request, response, cancellationToken); 151 | } 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /src/Server.Owin/OwinServerCompressionHandler.cs: -------------------------------------------------------------------------------- 1 | namespace Microsoft.AspNet.WebApi.Extensions.Compression.Server.Owin 2 | { 3 | using Microsoft.AspNet.WebApi.Extensions.Compression.Server; 4 | using Microsoft.Owin; 5 | using System; 6 | using System.Net.Http; 7 | using System.Net.Http.Extensions.Compression.Core.Interfaces; 8 | using System.Threading; 9 | using System.Threading.Tasks; 10 | 11 | public class OwinServerCompressionHandler : ServerCompressionHandler 12 | { 13 | /// 14 | /// Initializes a new instance of the class. 15 | /// 16 | /// The compressors. 17 | public OwinServerCompressionHandler(params ICompressor[] compressors) 18 | : base(null, 860, null, false, compressors) 19 | { 20 | } 21 | 22 | /// 23 | /// Initializes a new instance of the class. 24 | /// 25 | /// ConfigureAwait value. 26 | /// The compressors. 27 | public OwinServerCompressionHandler(bool continueOnCapturedContext, params ICompressor[] compressors) 28 | : base(null, 860, null, continueOnCapturedContext, compressors) 29 | { 30 | } 31 | 32 | /// 33 | /// Initializes a new instance of the class. 34 | /// 35 | /// The content size threshold before compressing. 36 | /// The compressors. 37 | public OwinServerCompressionHandler(int contentSizeThreshold, params ICompressor[] compressors) 38 | : base(null, contentSizeThreshold, null, false, compressors) 39 | { 40 | } 41 | 42 | /// 43 | /// Initializes a new instance of the class. 44 | /// 45 | /// The content size threshold before compressing. 46 | /// ConfigureAwait value. 47 | /// The compressors. 48 | public OwinServerCompressionHandler(int contentSizeThreshold, bool continueOnCapturedContext, params ICompressor[] compressors) 49 | : base(null, contentSizeThreshold, null, continueOnCapturedContext, compressors) 50 | { 51 | } 52 | 53 | /// 54 | /// Initializes a new instance of the class. 55 | /// 56 | /// The inner handler. 57 | /// The compressors. 58 | public OwinServerCompressionHandler(HttpMessageHandler innerHandler, params ICompressor[] compressors) 59 | : base(innerHandler, 860, null, false, compressors) 60 | { 61 | } 62 | 63 | /// 64 | /// Initializes a new instance of the class. 65 | /// 66 | /// The inner handler. 67 | /// ConfigureAwait value. 68 | /// The compressors. 69 | public OwinServerCompressionHandler(HttpMessageHandler innerHandler, bool continueOnCapturedContext, params ICompressor[] compressors) 70 | : base(innerHandler, 860, null, continueOnCapturedContext, compressors) 71 | { 72 | } 73 | 74 | /// 75 | /// Initializes a new instance of the class. 76 | /// 77 | /// The inner handler. 78 | /// The content size threshold before compressing. 79 | /// The compressors. 80 | public OwinServerCompressionHandler(HttpMessageHandler innerHandler, int contentSizeThreshold, params ICompressor[] compressors) 81 | : base(innerHandler, contentSizeThreshold, null, false, compressors) 82 | { 83 | } 84 | 85 | /// 86 | /// Initializes a new instance of the class. 87 | /// 88 | /// The inner handler. 89 | /// The content size threshold before compressing. 90 | /// ConfigureAwait value. 91 | /// The compressors. 92 | public OwinServerCompressionHandler(HttpMessageHandler innerHandler, int contentSizeThreshold, bool continueOnCapturedContext, params ICompressor[] compressors) 93 | : base(innerHandler, contentSizeThreshold, null, continueOnCapturedContext, compressors) 94 | { 95 | } 96 | 97 | /// 98 | /// Initializes a new instance of the class. 99 | /// 100 | /// The inner handler. 101 | /// The content size threshold before compressing. 102 | /// Custom delegate to enable or disable compression. 103 | /// The compressors. 104 | public OwinServerCompressionHandler(HttpMessageHandler innerHandler, int contentSizeThreshold, Predicate enableCompression, params ICompressor[] compressors) 105 | : base(innerHandler, contentSizeThreshold, enableCompression, false, compressors) 106 | { 107 | } 108 | 109 | /// 110 | /// Initializes a new instance of the class. 111 | /// 112 | /// The inner handler. 113 | /// The content size threshold before compressing. 114 | /// Custom delegate to enable or disable compression. 115 | /// ConfigureAwait value. 116 | /// The compressors. 117 | public OwinServerCompressionHandler(HttpMessageHandler innerHandler, int contentSizeThreshold, Predicate enableCompression, bool continueOnCapturedContext, params ICompressor[] compressors) 118 | : base(innerHandler, contentSizeThreshold, enableCompression, continueOnCapturedContext, compressors) 119 | { 120 | } 121 | 122 | /// Handles the request. 123 | /// The request. 124 | /// A cancellation token to cancel operation. 125 | /// A Task<HttpResponseMessage> 126 | public override Task HandleRequest(HttpRequestMessage request, CancellationToken cancellationToken) 127 | { 128 | request.GetOwinContext().Response.OnSendingHeaders( 129 | response => 130 | { 131 | ((IOwinResponse)response).Environment["HeadersWritten"] = true; 132 | }, 133 | request.GetOwinContext().Response); 134 | 135 | return base.HandleRequest(request, cancellationToken); 136 | } 137 | 138 | /// Handles the response. 139 | /// The request. 140 | /// The response. 141 | /// A cancellation token to cancel operation. 142 | /// The handled response. 143 | public override async Task HandleResponse(HttpRequestMessage request, HttpResponseMessage response, CancellationToken cancellationToken) 144 | { 145 | // Check if headers are already written, and skip processing if they are 146 | if (request.GetOwinContext().Response.Environment.ContainsKey("HeadersWritten")) 147 | { 148 | bool written; 149 | bool.TryParse(request.GetOwinContext().Response.Environment["HeadersWritten"].ToString(), out written); 150 | 151 | if (written) 152 | { 153 | return response; 154 | } 155 | } 156 | 157 | return await base.HandleResponse(request, response, cancellationToken); 158 | } 159 | } 160 | } -------------------------------------------------------------------------------- /src/Client/ClientCompressionHandler.cs: -------------------------------------------------------------------------------- 1 | namespace System.Net.Http.Extensions.Compression.Client 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Diagnostics; 6 | using System.Linq; 7 | using System.Net.Http; 8 | using System.Net.Http.Extensions.Compression.Core; 9 | using System.Net.Http.Extensions.Compression.Core.Interfaces; 10 | using System.Net.Http.Extensions.Compression.Core.Models; 11 | using System.Threading; 12 | using System.Threading.Tasks; 13 | 14 | /// 15 | /// Message handler for handling gzip/deflate requests/responses on a . 16 | /// 17 | public class ClientCompressionHandler : DelegatingHandler 18 | { 19 | /// 20 | /// The content size threshold before compressing. 21 | /// 22 | private readonly int contentSizeThreshold; 23 | 24 | /// 25 | /// The HTTP content operations 26 | /// 27 | private readonly HttpContentOperations httpContentOperations; 28 | 29 | /// 30 | /// Initializes a new instance of the class. 31 | /// 32 | /// The compressors. 33 | public ClientCompressionHandler(params ICompressor[] compressors) 34 | : this(new HttpClientHandler(), 860, compressors) 35 | { 36 | } 37 | 38 | /// 39 | /// Initializes a new instance of the class. 40 | /// 41 | /// The content size threshold before compressing. 42 | /// The compressors. 43 | public ClientCompressionHandler(int contentSizeThreshold, params ICompressor[] compressors) 44 | : this(new HttpClientHandler(), contentSizeThreshold, compressors) 45 | { 46 | } 47 | 48 | /// 49 | /// Initializes a new instance of the class. 50 | /// 51 | /// The inner handler. 52 | /// The compressors. 53 | public ClientCompressionHandler(HttpMessageHandler innerHandler, params ICompressor[] compressors) 54 | : this(innerHandler, 860, compressors) 55 | { 56 | } 57 | 58 | /// 59 | /// Initializes a new instance of the class. 60 | /// 61 | /// The inner handler. 62 | /// The content size threshold before compressing. 63 | /// The compressors. 64 | public ClientCompressionHandler(HttpMessageHandler innerHandler, int contentSizeThreshold, params ICompressor[] compressors) 65 | { 66 | if (innerHandler != null) 67 | { 68 | this.InnerHandler = innerHandler; 69 | } 70 | 71 | this.Compressors = compressors; 72 | this.contentSizeThreshold = contentSizeThreshold; 73 | 74 | this.httpContentOperations = new HttpContentOperations(); 75 | } 76 | 77 | /// 78 | /// Gets the compressors. 79 | /// 80 | /// The compressors. 81 | public ICollection Compressors { get; private set; } 82 | 83 | /// 84 | /// send as an asynchronous operation. 85 | /// 86 | /// The HTTP request message to send to the server. 87 | /// A cancellation token to cancel operation. 88 | /// Returns . The task object representing the asynchronous operation. 89 | protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) 90 | { 91 | // Compress uncompressed requests from the client to the server 92 | if (request.Content != null && request.Headers.AcceptEncoding.Any()) 93 | { 94 | await this.CompressRequest(request); 95 | } 96 | 97 | var response = await base.SendAsync(request, cancellationToken).ConfigureAwait(false); 98 | 99 | var process = true; 100 | 101 | try 102 | { 103 | if (response.Content != null) 104 | { 105 | // Buffer content for further processing 106 | await response.Content.LoadIntoBufferAsync(); 107 | } 108 | else 109 | { 110 | process = false; 111 | } 112 | } 113 | catch (Exception ex) 114 | { 115 | process = false; 116 | 117 | Debug.WriteLine(ex.Message); 118 | } 119 | 120 | // Decompress compressed responses to the client from the server 121 | if (process && response.Content != null && response.Content.Headers.ContentEncoding.Any()) 122 | { 123 | await this.DecompressResponse(response); 124 | } 125 | 126 | return response; 127 | } 128 | 129 | /// 130 | /// Compresses the content. 131 | /// 132 | /// The request. 133 | /// An async void. 134 | private async Task CompressRequest(HttpRequestMessage request) 135 | { 136 | // As per RFC2616.14.3: 137 | // Ignores encodings with quality == 0 138 | // If multiple content-codings are acceptable, then the acceptable content-coding with the highest non-zero qvalue is preferred. 139 | var compressor = (from encoding in request.Headers.AcceptEncoding 140 | let quality = encoding.Quality ?? 1.0 141 | where quality > 0 142 | join c in this.Compressors on encoding.Value.ToLowerInvariant() equals 143 | c.EncodingType.ToLowerInvariant() 144 | orderby quality descending 145 | select c).FirstOrDefault(); 146 | 147 | if (compressor != null) 148 | { 149 | // Try to set content-length by loading the content into memory if the content-length is not already set 150 | if (request.Content.Headers.ContentLength == null) 151 | { 152 | await request.Content.LoadIntoBufferAsync(); 153 | } 154 | 155 | try 156 | { 157 | // Only compress request if size is larger than threshold (if set) 158 | if (this.contentSizeThreshold == 0) 159 | { 160 | request.Content = new CompressedContent(request.Content, compressor); 161 | } 162 | else if (this.contentSizeThreshold > 0 && request.Content.Headers.ContentLength >= this.contentSizeThreshold) 163 | { 164 | request.Content = new CompressedContent(request.Content, compressor); 165 | } 166 | } 167 | catch (Exception ex) 168 | { 169 | throw new Exception(string.Format("Unable to compress request using compressor '{0}'", compressor.GetType()), ex); 170 | } 171 | } 172 | } 173 | 174 | /// 175 | /// Decompresses the response. 176 | /// 177 | /// The response. 178 | /// An async void. 179 | private async Task DecompressResponse(HttpResponseMessage response) 180 | { 181 | var encoding = response.Content.Headers.ContentEncoding.FirstOrDefault(); 182 | 183 | if (encoding != null) 184 | { 185 | var compressor = this.Compressors.FirstOrDefault(c => c.EncodingType.Equals(encoding, StringComparison.OrdinalIgnoreCase)); 186 | 187 | if (compressor != null) 188 | { 189 | try 190 | { 191 | response.Content = await this.httpContentOperations.DecompressContent(response.Content, compressor).ConfigureAwait(false); 192 | } 193 | catch (Exception ex) 194 | { 195 | throw new Exception(string.Format("Unable to decompress response using compressor '{0}'", compressor.GetType()), ex); 196 | } 197 | } 198 | } 199 | } 200 | } 201 | } 202 | -------------------------------------------------------------------------------- /src/Tests/Tests/Common/TestFixture.cs: -------------------------------------------------------------------------------- 1 | namespace Tests.Tests.Common 2 | { 3 | using global::Tests.Extensions; 4 | using global::Tests.Models; 5 | using Newtonsoft.Json; 6 | using NUnit.Framework; 7 | using System; 8 | using System.Net.Http; 9 | using System.Text; 10 | 11 | public abstract class TestFixture 12 | { 13 | public HttpClient Client { get; set; } 14 | 15 | [Test] 16 | public async void Get_WhenMessageHandlerIsConfigured_ShouldReturnCompressedContent() 17 | { 18 | var response = await this.Client.GetAsync("http://localhost:55399/api/test"); 19 | 20 | Console.Write(await response.ToTestString()); 21 | 22 | Assert.IsTrue(response.Content.Headers.ContentEncoding.Contains("gzip")); 23 | 24 | var result = JsonConvert.DeserializeObject(await response.Content.ReadAsStringAsync()); 25 | 26 | Assert.AreEqual("Get()", result.Data); 27 | } 28 | 29 | [Test] 30 | public async void Get_WhenGivenCustomHeader_ShouldReturnCompressedContentWithCustomHeader() 31 | { 32 | var response = await this.Client.GetAsync("http://localhost:55399/api/test/customheader"); 33 | 34 | Console.Write(await response.ToTestString()); 35 | 36 | Assert.IsTrue(response.Content.Headers.ContentEncoding.Contains("gzip")); 37 | Assert.IsTrue(response.Headers.Contains("DataServiceVersion"), "The response did not contain the DataServiceVersion header"); 38 | } 39 | 40 | [Test] 41 | public async void Get_WhenGivenCustomContentEncoding_ShouldReturnCompressedContentWithCustomContentEncoding() 42 | { 43 | var response = await this.Client.GetAsync("http://localhost:55399/api/test/customcontentencoding"); 44 | 45 | Console.WriteLine($"Content-Encoding: {string.Join(", ", response.Content.Headers.ContentEncoding)}"); 46 | 47 | Assert.IsTrue(response.Content.Headers.ContentEncoding.Contains("gzip")); 48 | Assert.IsTrue(response.Content.Headers.ContentEncoding.Contains("lol")); 49 | } 50 | 51 | [Test] 52 | public async void Get_WhenGivenAcceptEncodingNull_ShouldReturnUncompressedContent() 53 | { 54 | var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost:55399/api/test"); 55 | this.Client.DefaultRequestHeaders.AcceptEncoding.Clear(); 56 | 57 | Console.WriteLine("Accept-Encoding: {0}", string.Join(", ", request.Headers.AcceptEncoding)); 58 | 59 | var response = await this.Client.SendAsync(request); 60 | 61 | Console.Write(await response.ToTestString()); 62 | 63 | Assert.IsFalse(response.Content.Headers.ContentEncoding.Contains("gzip"), "The server returned compressed content"); 64 | } 65 | 66 | [Test] 67 | public async void Get_WhenResponseHeaderIsModified_ShouldReturnModifiedResponse() 68 | { 69 | var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost:55399/api/test/redirect"); 70 | 71 | var response = await this.Client.SendAsync(request); 72 | 73 | Console.Write(await response.ToTestString()); 74 | 75 | Assert.AreEqual(29, response.Content.Headers.ContentLength); 76 | Assert.AreEqual("http://localhost:55399/api/test", response.Headers.Location.ToString()); 77 | } 78 | 79 | [Test] 80 | public async void GetImage_WhenMessageHandlerIsConfigured_ShouldReturnCompressedContent() 81 | { 82 | var response = await this.Client.GetAsync("http://localhost:55399/api/file/image"); 83 | 84 | Console.Write(await response.ToTestString()); 85 | 86 | Assert.IsTrue(response.Content.Headers.ContentType.MediaType == "image/png"); 87 | Assert.IsTrue(response.Content.Headers.ContentEncoding.Contains("gzip")); 88 | 89 | var content = await response.Content.ReadAsByteArrayAsync(); 90 | 91 | Assert.AreEqual(749, response.Content.Headers.ContentLength); 92 | Assert.AreEqual(4596, content.Length); 93 | } 94 | 95 | [Test] 96 | public async void GetImage_WhenAttributeIsConfigured_ShouldReturnUncompressedContent() 97 | { 98 | 99 | var response = await this.Client.GetAsync("http://localhost:55399/api/file/uncompressedimage"); 100 | 101 | Console.Write(await response.ToTestString()); 102 | 103 | Assert.IsTrue(response.Content.Headers.ContentType.MediaType == "image/png"); 104 | Assert.IsFalse(response.Content.Headers.ContentEncoding.Contains("gzip")); 105 | 106 | var content = await response.Content.ReadAsByteArrayAsync(); 107 | 108 | Assert.AreEqual(4596, content.Length); 109 | } 110 | 111 | [Test] 112 | public async void GetPdf_WhenMessageHandlerIsConfigured_ShouldReturnCompressedContent() 113 | { 114 | var response = await this.Client.GetAsync("http://localhost:55399/api/file/pdf"); 115 | 116 | Console.Write(await response.ToTestString()); 117 | 118 | Assert.IsTrue(response.Content.Headers.ContentType.MediaType == "application/pdf"); 119 | Assert.IsTrue(response.Content.Headers.ContentEncoding.Contains("gzip")); 120 | 121 | var content = await response.Content.ReadAsByteArrayAsync(); 122 | 123 | Assert.AreEqual(16538, content.Length); 124 | } 125 | 126 | [TestCase("1")] 127 | [TestCase("10")] 128 | public async void GetSpecific_WhenMessageHandlerIsConfigured_ShouldReturnCompressedContent(string id) 129 | { 130 | var response = await this.Client.GetAsync("http://localhost:55399/api/test/" + id); 131 | 132 | Console.Write(await response.ToTestString()); 133 | 134 | Assert.IsTrue(response.Content.Headers.ContentEncoding.Contains("gzip")); 135 | 136 | var content = await response.Content.ReadAsStringAsync(); 137 | 138 | var result = JsonConvert.DeserializeObject(content); 139 | 140 | Assert.AreEqual("Get(" + id + ")", result.Data); 141 | } 142 | 143 | [TestCase("Content1")] 144 | [TestCase("Content10")] 145 | [TestCase("Content10Content10Content10Content10Content10Content10Content10Content10Content10Content10Content10Content10")] 146 | public async void Post_WhenMessageHandlerIsConfigured_ShouldReturnCompressedContent(string body) 147 | { 148 | var response = await this.Client.PostAsync("http://localhost:55399/api/test", new StringContent(JsonConvert.SerializeObject(new TestModel(body)), Encoding.UTF8, "application/json")); 149 | 150 | Console.Write(await response.ToTestString()); 151 | 152 | Assert.IsTrue(response.Content.Headers.ContentEncoding.Contains("gzip")); 153 | 154 | var content = await response.Content.ReadAsStringAsync(); 155 | 156 | var result = JsonConvert.DeserializeObject(content); 157 | 158 | Assert.AreEqual(body, result.Data); 159 | } 160 | 161 | [TestCase("Content1")] 162 | [TestCase("Content10")] 163 | [TestCase("Content10Content10Content10Content10Content10Content10Content10Content10Content10Content10Content10Content10")] 164 | public async void Post_WhenNestedMessageHandlerIsConfigured_ShouldReturnCompressedContent(string body) 165 | { 166 | var response = await this.Client.PostAsync("http://localhost:55399/api/test", new StringContent(JsonConvert.SerializeObject(new TestModel(body)), Encoding.UTF8, "application/json")); 167 | 168 | Console.Write(await response.ToTestString()); 169 | 170 | Assert.IsTrue(response.Content.Headers.ContentEncoding.Contains("gzip")); 171 | 172 | var content = await response.Content.ReadAsStringAsync(); 173 | 174 | var result = JsonConvert.DeserializeObject(content); 175 | 176 | Assert.AreEqual(body, result.Data); 177 | } 178 | 179 | [TestCase("1", "Content1")] 180 | [TestCase("2", "Content10")] 181 | public async void Put_WhenMessageHandlerIsConfigured_ShouldReturnCompressedContent(string id, string body) 182 | { 183 | var response = await this.Client.PutAsync("http://localhost:55399/api/test/" + id, new StringContent(JsonConvert.SerializeObject(new TestModel(body)), Encoding.UTF8, "application/json")); 184 | 185 | Console.Write(await response.ToTestString()); 186 | 187 | Assert.IsTrue(response.Content.Headers.ContentEncoding.Contains("gzip")); 188 | 189 | var content = await response.Content.ReadAsStringAsync(); 190 | 191 | var result = JsonConvert.DeserializeObject(content); 192 | 193 | Assert.AreEqual(body, result.Data); 194 | } 195 | 196 | [TestCase("1")] 197 | [TestCase("2")] 198 | public async void Delete_WhenMessageHandlerIsConfigured_ShouldReturnCompressedContent(string id) 199 | { 200 | var response = await this.Client.DeleteAsync("http://localhost:55399/api/test/" + id); 201 | 202 | Assert.That(response.Content == null || string.IsNullOrEmpty(await response.Content.ReadAsStringAsync())); 203 | Assert.IsTrue(response.IsSuccessStatusCode); 204 | } 205 | } 206 | } 207 | -------------------------------------------------------------------------------- /src/Microsoft.AspNet.WebApi.MessageHandlers.Compression.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25420.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Client", "Client\Client.csproj", "{56E19FF7-9AC7-48C5-9AC2-105F55AA16E1}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Tests\Tests.csproj", "{7952E97C-2C7E-424B-A912-ED875576CA92}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Server", "Server\Server.csproj", "{EE709C23-849D-43C5-9F47-0B8D8DDB43D8}" 11 | EndProject 12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core", "Core\Core.csproj", "{B7534554-AB6C-44AC-BF34-424DBE596216}" 13 | EndProject 14 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Server.Owin", "Server.Owin\Server.Owin.csproj", "{DAA0A504-7ED7-4906-9D6B-FB566AE47084}" 15 | EndProject 16 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{977C3F06-2918-4747-8658-8B7C9B31E8C2}" 17 | ProjectSection(SolutionItems) = preProject 18 | StrongKey.snk = StrongKey.snk 19 | EndProjectSection 20 | EndProject 21 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Client.StrongName", "Client\Client.StrongName.csproj", "{FF00D5E5-9581-4047-A5EE-CB968A567EE2}" 22 | EndProject 23 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "StrongNamed", "StrongNamed", "{33FDEBCE-A3CE-473E-B246-D561355046ED}" 24 | EndProject 25 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core.StrongName", "Core\Core.StrongName.csproj", "{2EF04E20-6CD8-420D-BAC4-192ECD3F9AD5}" 26 | EndProject 27 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Server.StrongName", "Server\Server.StrongName.csproj", "{297AC86E-3B54-4E76-A4EA-A5BF1E730D6F}" 28 | EndProject 29 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Server.Owin.StrongName", "Server.Owin\Server.Owin.StrongName.csproj", "{AA0CD548-E262-44AB-9AA2-A7ADA6998C99}" 30 | EndProject 31 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.StrongName", "Tests\Tests.StrongName.csproj", "{F390EA4F-93FD-4D14-9D24-DC3152537DD1}" 32 | EndProject 33 | Global 34 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 35 | Debug|Any CPU = Debug|Any CPU 36 | Release|Any CPU = Release|Any CPU 37 | Staging|Any CPU = Staging|Any CPU 38 | Test|Any CPU = Test|Any CPU 39 | EndGlobalSection 40 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 41 | {56E19FF7-9AC7-48C5-9AC2-105F55AA16E1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 42 | {56E19FF7-9AC7-48C5-9AC2-105F55AA16E1}.Debug|Any CPU.Build.0 = Debug|Any CPU 43 | {56E19FF7-9AC7-48C5-9AC2-105F55AA16E1}.Release|Any CPU.ActiveCfg = Release|Any CPU 44 | {56E19FF7-9AC7-48C5-9AC2-105F55AA16E1}.Release|Any CPU.Build.0 = Release|Any CPU 45 | {56E19FF7-9AC7-48C5-9AC2-105F55AA16E1}.Staging|Any CPU.ActiveCfg = Staging|Any CPU 46 | {56E19FF7-9AC7-48C5-9AC2-105F55AA16E1}.Staging|Any CPU.Build.0 = Staging|Any CPU 47 | {56E19FF7-9AC7-48C5-9AC2-105F55AA16E1}.Test|Any CPU.ActiveCfg = Test|Any CPU 48 | {56E19FF7-9AC7-48C5-9AC2-105F55AA16E1}.Test|Any CPU.Build.0 = Test|Any CPU 49 | {7952E97C-2C7E-424B-A912-ED875576CA92}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 50 | {7952E97C-2C7E-424B-A912-ED875576CA92}.Debug|Any CPU.Build.0 = Debug|Any CPU 51 | {7952E97C-2C7E-424B-A912-ED875576CA92}.Release|Any CPU.ActiveCfg = Release|Any CPU 52 | {7952E97C-2C7E-424B-A912-ED875576CA92}.Release|Any CPU.Build.0 = Release|Any CPU 53 | {7952E97C-2C7E-424B-A912-ED875576CA92}.Staging|Any CPU.ActiveCfg = Staging|Any CPU 54 | {7952E97C-2C7E-424B-A912-ED875576CA92}.Staging|Any CPU.Build.0 = Staging|Any CPU 55 | {7952E97C-2C7E-424B-A912-ED875576CA92}.Test|Any CPU.ActiveCfg = Test|Any CPU 56 | {7952E97C-2C7E-424B-A912-ED875576CA92}.Test|Any CPU.Build.0 = Test|Any CPU 57 | {EE709C23-849D-43C5-9F47-0B8D8DDB43D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 58 | {EE709C23-849D-43C5-9F47-0B8D8DDB43D8}.Debug|Any CPU.Build.0 = Debug|Any CPU 59 | {EE709C23-849D-43C5-9F47-0B8D8DDB43D8}.Release|Any CPU.ActiveCfg = Release|Any CPU 60 | {EE709C23-849D-43C5-9F47-0B8D8DDB43D8}.Release|Any CPU.Build.0 = Release|Any CPU 61 | {EE709C23-849D-43C5-9F47-0B8D8DDB43D8}.Staging|Any CPU.ActiveCfg = Staging|Any CPU 62 | {EE709C23-849D-43C5-9F47-0B8D8DDB43D8}.Staging|Any CPU.Build.0 = Staging|Any CPU 63 | {EE709C23-849D-43C5-9F47-0B8D8DDB43D8}.Test|Any CPU.ActiveCfg = Test|Any CPU 64 | {EE709C23-849D-43C5-9F47-0B8D8DDB43D8}.Test|Any CPU.Build.0 = Test|Any CPU 65 | {B7534554-AB6C-44AC-BF34-424DBE596216}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 66 | {B7534554-AB6C-44AC-BF34-424DBE596216}.Debug|Any CPU.Build.0 = Debug|Any CPU 67 | {B7534554-AB6C-44AC-BF34-424DBE596216}.Release|Any CPU.ActiveCfg = Release|Any CPU 68 | {B7534554-AB6C-44AC-BF34-424DBE596216}.Release|Any CPU.Build.0 = Release|Any CPU 69 | {B7534554-AB6C-44AC-BF34-424DBE596216}.Staging|Any CPU.ActiveCfg = Staging|Any CPU 70 | {B7534554-AB6C-44AC-BF34-424DBE596216}.Staging|Any CPU.Build.0 = Staging|Any CPU 71 | {B7534554-AB6C-44AC-BF34-424DBE596216}.Test|Any CPU.ActiveCfg = Test|Any CPU 72 | {B7534554-AB6C-44AC-BF34-424DBE596216}.Test|Any CPU.Build.0 = Test|Any CPU 73 | {DAA0A504-7ED7-4906-9D6B-FB566AE47084}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 74 | {DAA0A504-7ED7-4906-9D6B-FB566AE47084}.Debug|Any CPU.Build.0 = Debug|Any CPU 75 | {DAA0A504-7ED7-4906-9D6B-FB566AE47084}.Release|Any CPU.ActiveCfg = Release|Any CPU 76 | {DAA0A504-7ED7-4906-9D6B-FB566AE47084}.Release|Any CPU.Build.0 = Release|Any CPU 77 | {DAA0A504-7ED7-4906-9D6B-FB566AE47084}.Staging|Any CPU.ActiveCfg = Staging|Any CPU 78 | {DAA0A504-7ED7-4906-9D6B-FB566AE47084}.Staging|Any CPU.Build.0 = Staging|Any CPU 79 | {DAA0A504-7ED7-4906-9D6B-FB566AE47084}.Test|Any CPU.ActiveCfg = Test|Any CPU 80 | {DAA0A504-7ED7-4906-9D6B-FB566AE47084}.Test|Any CPU.Build.0 = Test|Any CPU 81 | {FF00D5E5-9581-4047-A5EE-CB968A567EE2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 82 | {FF00D5E5-9581-4047-A5EE-CB968A567EE2}.Debug|Any CPU.Build.0 = Debug|Any CPU 83 | {FF00D5E5-9581-4047-A5EE-CB968A567EE2}.Release|Any CPU.ActiveCfg = Release|Any CPU 84 | {FF00D5E5-9581-4047-A5EE-CB968A567EE2}.Release|Any CPU.Build.0 = Release|Any CPU 85 | {FF00D5E5-9581-4047-A5EE-CB968A567EE2}.Staging|Any CPU.ActiveCfg = Staging|Any CPU 86 | {FF00D5E5-9581-4047-A5EE-CB968A567EE2}.Staging|Any CPU.Build.0 = Staging|Any CPU 87 | {FF00D5E5-9581-4047-A5EE-CB968A567EE2}.Test|Any CPU.ActiveCfg = Test|Any CPU 88 | {FF00D5E5-9581-4047-A5EE-CB968A567EE2}.Test|Any CPU.Build.0 = Test|Any CPU 89 | {2EF04E20-6CD8-420D-BAC4-192ECD3F9AD5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 90 | {2EF04E20-6CD8-420D-BAC4-192ECD3F9AD5}.Debug|Any CPU.Build.0 = Debug|Any CPU 91 | {2EF04E20-6CD8-420D-BAC4-192ECD3F9AD5}.Release|Any CPU.ActiveCfg = Release|Any CPU 92 | {2EF04E20-6CD8-420D-BAC4-192ECD3F9AD5}.Release|Any CPU.Build.0 = Release|Any CPU 93 | {2EF04E20-6CD8-420D-BAC4-192ECD3F9AD5}.Staging|Any CPU.ActiveCfg = Staging|Any CPU 94 | {2EF04E20-6CD8-420D-BAC4-192ECD3F9AD5}.Staging|Any CPU.Build.0 = Staging|Any CPU 95 | {2EF04E20-6CD8-420D-BAC4-192ECD3F9AD5}.Test|Any CPU.ActiveCfg = Test|Any CPU 96 | {2EF04E20-6CD8-420D-BAC4-192ECD3F9AD5}.Test|Any CPU.Build.0 = Test|Any CPU 97 | {297AC86E-3B54-4E76-A4EA-A5BF1E730D6F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 98 | {297AC86E-3B54-4E76-A4EA-A5BF1E730D6F}.Debug|Any CPU.Build.0 = Debug|Any CPU 99 | {297AC86E-3B54-4E76-A4EA-A5BF1E730D6F}.Release|Any CPU.ActiveCfg = Release|Any CPU 100 | {297AC86E-3B54-4E76-A4EA-A5BF1E730D6F}.Release|Any CPU.Build.0 = Release|Any CPU 101 | {297AC86E-3B54-4E76-A4EA-A5BF1E730D6F}.Staging|Any CPU.ActiveCfg = Staging|Any CPU 102 | {297AC86E-3B54-4E76-A4EA-A5BF1E730D6F}.Staging|Any CPU.Build.0 = Staging|Any CPU 103 | {297AC86E-3B54-4E76-A4EA-A5BF1E730D6F}.Test|Any CPU.ActiveCfg = Test|Any CPU 104 | {297AC86E-3B54-4E76-A4EA-A5BF1E730D6F}.Test|Any CPU.Build.0 = Test|Any CPU 105 | {AA0CD548-E262-44AB-9AA2-A7ADA6998C99}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 106 | {AA0CD548-E262-44AB-9AA2-A7ADA6998C99}.Debug|Any CPU.Build.0 = Debug|Any CPU 107 | {AA0CD548-E262-44AB-9AA2-A7ADA6998C99}.Release|Any CPU.ActiveCfg = Release|Any CPU 108 | {AA0CD548-E262-44AB-9AA2-A7ADA6998C99}.Release|Any CPU.Build.0 = Release|Any CPU 109 | {AA0CD548-E262-44AB-9AA2-A7ADA6998C99}.Staging|Any CPU.ActiveCfg = Staging|Any CPU 110 | {AA0CD548-E262-44AB-9AA2-A7ADA6998C99}.Staging|Any CPU.Build.0 = Staging|Any CPU 111 | {AA0CD548-E262-44AB-9AA2-A7ADA6998C99}.Test|Any CPU.ActiveCfg = Test|Any CPU 112 | {AA0CD548-E262-44AB-9AA2-A7ADA6998C99}.Test|Any CPU.Build.0 = Test|Any CPU 113 | {F390EA4F-93FD-4D14-9D24-DC3152537DD1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 114 | {F390EA4F-93FD-4D14-9D24-DC3152537DD1}.Debug|Any CPU.Build.0 = Debug|Any CPU 115 | {F390EA4F-93FD-4D14-9D24-DC3152537DD1}.Release|Any CPU.ActiveCfg = Release|Any CPU 116 | {F390EA4F-93FD-4D14-9D24-DC3152537DD1}.Release|Any CPU.Build.0 = Release|Any CPU 117 | {F390EA4F-93FD-4D14-9D24-DC3152537DD1}.Staging|Any CPU.ActiveCfg = Staging|Any CPU 118 | {F390EA4F-93FD-4D14-9D24-DC3152537DD1}.Staging|Any CPU.Build.0 = Staging|Any CPU 119 | {F390EA4F-93FD-4D14-9D24-DC3152537DD1}.Test|Any CPU.ActiveCfg = Test|Any CPU 120 | {F390EA4F-93FD-4D14-9D24-DC3152537DD1}.Test|Any CPU.Build.0 = Test|Any CPU 121 | EndGlobalSection 122 | GlobalSection(SolutionProperties) = preSolution 123 | HideSolutionNode = FALSE 124 | EndGlobalSection 125 | GlobalSection(NestedProjects) = preSolution 126 | {977C3F06-2918-4747-8658-8B7C9B31E8C2} = {33FDEBCE-A3CE-473E-B246-D561355046ED} 127 | {FF00D5E5-9581-4047-A5EE-CB968A567EE2} = {33FDEBCE-A3CE-473E-B246-D561355046ED} 128 | {2EF04E20-6CD8-420D-BAC4-192ECD3F9AD5} = {33FDEBCE-A3CE-473E-B246-D561355046ED} 129 | {297AC86E-3B54-4E76-A4EA-A5BF1E730D6F} = {33FDEBCE-A3CE-473E-B246-D561355046ED} 130 | {AA0CD548-E262-44AB-9AA2-A7ADA6998C99} = {33FDEBCE-A3CE-473E-B246-D561355046ED} 131 | {F390EA4F-93FD-4D14-9D24-DC3152537DD1} = {33FDEBCE-A3CE-473E-B246-D561355046ED} 132 | EndGlobalSection 133 | EndGlobal 134 | -------------------------------------------------------------------------------- /src/Tests/Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {7952E97C-2C7E-424B-A912-ED875576CA92} 8 | Library 9 | Properties 10 | Tests 11 | Tests 12 | v4.5.2 13 | 512 14 | 15 | 16 | 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | pdbonly 27 | true 28 | bin\Release\ 29 | TRACE 30 | prompt 31 | 4 32 | 33 | 34 | true 35 | bin\Test\ 36 | DEBUG;TRACE 37 | full 38 | AnyCPU 39 | prompt 40 | MinimumRecommendedRules.ruleset 41 | 42 | 43 | true 44 | bin\Staging\ 45 | DEBUG;TRACE 46 | full 47 | AnyCPU 48 | prompt 49 | MinimumRecommendedRules.ruleset 50 | 51 | 52 | 53 | ..\packages\iTextSharp-LGPL.4.1.6\lib\iTextSharp.dll 54 | 55 | 56 | ..\packages\Microsoft.AspNet.Identity.Core.2.2.1\lib\net45\Microsoft.AspNet.Identity.Core.dll 57 | True 58 | 59 | 60 | ..\packages\Microsoft.Owin.3.0.1\lib\net45\Microsoft.Owin.dll 61 | True 62 | 63 | 64 | ..\packages\Microsoft.Owin.Host.HttpListener.3.0.1\lib\net45\Microsoft.Owin.Host.HttpListener.dll 65 | True 66 | 67 | 68 | ..\packages\Microsoft.Owin.Host.SystemWeb.3.0.1\lib\net45\Microsoft.Owin.Host.SystemWeb.dll 69 | True 70 | 71 | 72 | ..\packages\Microsoft.Owin.Hosting.3.0.1\lib\net45\Microsoft.Owin.Hosting.dll 73 | True 74 | 75 | 76 | ..\packages\Microsoft.Owin.Security.3.0.1\lib\net45\Microsoft.Owin.Security.dll 77 | True 78 | 79 | 80 | ..\packages\Microsoft.Owin.Security.Cookies.3.0.1\lib\net45\Microsoft.Owin.Security.Cookies.dll 81 | True 82 | 83 | 84 | ..\packages\Microsoft.Owin.Testing.3.0.1\lib\net45\Microsoft.Owin.Testing.dll 85 | True 86 | 87 | 88 | False 89 | ..\packages\Newtonsoft.Json.6.0.4\lib\net45\Newtonsoft.Json.dll 90 | 91 | 92 | ..\packages\NUnit.2.6.3\lib\nunit.framework.dll 93 | 94 | 95 | ..\packages\Owin.1.0\lib\net40\Owin.dll 96 | 97 | 98 | 99 | 100 | 101 | 102 | ..\packages\Microsoft.AspNet.WebApi.Client.5.2.3\lib\net45\System.Net.Http.Formatting.dll 103 | True 104 | 105 | 106 | 107 | 108 | ..\packages\Microsoft.AspNet.WebApi.Core.5.2.3\lib\net45\System.Web.Http.dll 109 | True 110 | 111 | 112 | ..\packages\Microsoft.AspNet.WebApi.Owin.5.2.3\lib\net45\System.Web.Http.Owin.dll 113 | True 114 | 115 | 116 | ..\packages\Microsoft.AspNet.WebApi.SelfHost.5.2.3\lib\net45\System.Web.Http.SelfHost.dll 117 | True 118 | 119 | 120 | ..\packages\Microsoft.AspNet.WebApi.WebHost.5.2.3\lib\net45\System.Web.Http.WebHost.dll 121 | True 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | Global.asax 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | Designer 156 | 157 | 158 | 159 | 160 | 161 | {56e19ff7-9ac7-48c5-9ac2-105f55aa16e1} 162 | Client 163 | 164 | 165 | {b7534554-ab6c-44ac-bf34-424dbe596216} 166 | Core 167 | 168 | 169 | {daa0a504-7ed7-4906-9d6b-fb566ae47084} 170 | Server.Owin 171 | 172 | 173 | {ee709c23-849d-43c5-9f47-0b8d8ddb43d8} 174 | Server 175 | 176 | 177 | 178 | 179 | PreserveNewest 180 | 181 | 182 | 183 | 184 | 191 | -------------------------------------------------------------------------------- /src/Tests/Tests.StrongName.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {F390EA4F-93FD-4D14-9D24-DC3152537DD1} 8 | Library 9 | Properties 10 | Tests 11 | Tests 12 | v4.5.2 13 | 512 14 | 15 | 16 | 17 | true 18 | full 19 | false 20 | bin\Debug.StrongName\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | pdbonly 27 | true 28 | bin\Release.StrongName\ 29 | TRACE 30 | prompt 31 | 4 32 | 33 | 34 | true 35 | bin\Test.StrongName\ 36 | DEBUG;TRACE 37 | full 38 | AnyCPU 39 | prompt 40 | MinimumRecommendedRules.ruleset 41 | 42 | 43 | true 44 | bin\Staging.StrongName\ 45 | DEBUG;TRACE 46 | full 47 | AnyCPU 48 | prompt 49 | MinimumRecommendedRules.ruleset 50 | 51 | 52 | true 53 | 54 | 55 | ..\StrongKey.snk 56 | 57 | 58 | 59 | ..\packages\iTextSharp-LGPL.4.1.6\lib\iTextSharp.dll 60 | 61 | 62 | ..\packages\Microsoft.AspNet.Identity.Core.2.2.1\lib\net45\Microsoft.AspNet.Identity.Core.dll 63 | True 64 | 65 | 66 | ..\packages\Microsoft.Owin.3.0.1\lib\net45\Microsoft.Owin.dll 67 | True 68 | 69 | 70 | ..\packages\Microsoft.Owin.Host.HttpListener.3.0.1\lib\net45\Microsoft.Owin.Host.HttpListener.dll 71 | True 72 | 73 | 74 | ..\packages\Microsoft.Owin.Host.SystemWeb.3.0.1\lib\net45\Microsoft.Owin.Host.SystemWeb.dll 75 | True 76 | 77 | 78 | ..\packages\Microsoft.Owin.Hosting.3.0.1\lib\net45\Microsoft.Owin.Hosting.dll 79 | True 80 | 81 | 82 | ..\packages\Microsoft.Owin.Security.3.0.1\lib\net45\Microsoft.Owin.Security.dll 83 | True 84 | 85 | 86 | ..\packages\Microsoft.Owin.Security.Cookies.3.0.1\lib\net45\Microsoft.Owin.Security.Cookies.dll 87 | True 88 | 89 | 90 | ..\packages\Microsoft.Owin.Testing.3.0.1\lib\net45\Microsoft.Owin.Testing.dll 91 | True 92 | 93 | 94 | False 95 | ..\packages\Newtonsoft.Json.6.0.4\lib\net45\Newtonsoft.Json.dll 96 | 97 | 98 | ..\packages\NUnit.2.6.3\lib\nunit.framework.dll 99 | 100 | 101 | ..\packages\Owin.1.0\lib\net40\Owin.dll 102 | 103 | 104 | 105 | 106 | 107 | 108 | ..\packages\Microsoft.AspNet.WebApi.Client.5.2.3\lib\net45\System.Net.Http.Formatting.dll 109 | True 110 | 111 | 112 | 113 | 114 | ..\packages\Microsoft.AspNet.WebApi.Core.5.2.3\lib\net45\System.Web.Http.dll 115 | True 116 | 117 | 118 | ..\packages\Microsoft.AspNet.WebApi.Owin.5.2.3\lib\net45\System.Web.Http.Owin.dll 119 | True 120 | 121 | 122 | ..\packages\Microsoft.AspNet.WebApi.SelfHost.5.2.3\lib\net45\System.Web.Http.SelfHost.dll 123 | True 124 | 125 | 126 | ..\packages\Microsoft.AspNet.WebApi.WebHost.5.2.3\lib\net45\System.Web.Http.WebHost.dll 127 | True 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | Global.asax 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | StrongKey.snk 160 | 161 | 162 | 163 | Designer 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | {ff00d5e5-9581-4047-a5ee-cb968a567ee2} 174 | Client.StrongName 175 | 176 | 177 | {2ef04e20-6cd8-420d-bac4-192ecd3f9ad5} 178 | Core.StrongName 179 | 180 | 181 | {aa0cd548-e262-44ab-9aa2-a7ada6998c99} 182 | Server.Owin.StrongName 183 | 184 | 185 | {297ac86e-3b54-4e76-a4ea-a5bf1e730d6f} 186 | Server.StrongName 187 | 188 | 189 | 190 | 197 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. --------------------------------------------------------------------------------