#108 Change ResponseCompression to be DI centric

This commit is contained in:
Chris R 2016-09-30 10:15:46 -07:00
parent 3e119a87be
commit 9bc8a83a39
14 changed files with 374 additions and 311 deletions

View File

@ -0,0 +1,16 @@
using System.IO;
using Microsoft.AspNetCore.ResponseCompression;
namespace ResponseCompressionSample
{
public class CustomCompressionProvider : ICompressionProvider
{
public string EncodingName => "custom";
public Stream CreateStream(Stream outputStream)
{
// Create a custom compression stream wrapper here
return outputStream;
}
}
}

View File

@ -1,22 +1,9 @@
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:49658/",
"sslPort": 0
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"ResponseCompressionSample": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"web": {
"commandName": "web",
"launchUrl": "http://localhost:5000/",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}

View File

@ -5,21 +5,26 @@ using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.ResponseCompression;
using Microsoft.Extensions.DependencyInjection;
namespace ResponseCompressionSample
{
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<ICompressionProvider, GzipCompressionProvider>();
services.AddTransient<ICompressionProvider, CustomCompressionProvider>();
services.AddResponseCompression("text/plain", "text/html");
}
public void Configure(IApplicationBuilder app)
{
app.UseResponseCompression(new ResponseCompressionOptions()
{
ShouldCompressResponse = ResponseCompressionUtils.CreateShouldCompressResponseDelegate(new string[] { "text/plain" })
});
app.UseResponseCompression();
app.Run(async context =>
{
context.Response.Headers["Content-Type"] = "text/plain";
context.Response.ContentType = "text/plain";
await context.Response.WriteAsync(LoremIpsum.Text);
});
}

View File

@ -2,12 +2,10 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Primitives;
using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNetCore.ResponseCompression
@ -21,19 +19,19 @@ namespace Microsoft.AspNetCore.ResponseCompression
private readonly Stream _bodyOriginalStream;
private readonly Func<HttpContext, bool> _shouldCompressResponse;
private readonly IResponseCompressionProvider _provider;
private readonly IResponseCompressionProvider _compressionProvider;
private readonly ICompressionProvider _compressionProvider;
private bool _compressionChecked = false;
private Stream _compressionStream = null;
internal BodyWrapperStream(HttpResponse response, Stream bodyOriginalStream, Func<HttpContext, bool> shouldCompressResponse, IResponseCompressionProvider compressionProvider)
internal BodyWrapperStream(HttpResponse response, Stream bodyOriginalStream, IResponseCompressionProvider provider, ICompressionProvider compressionProvider)
{
_response = response;
_bodyOriginalStream = bodyOriginalStream;
_shouldCompressResponse = shouldCompressResponse;
_provider = provider;
_compressionProvider = compressionProvider;
}
@ -174,9 +172,8 @@ namespace Microsoft.AspNetCore.ResponseCompression
private bool IsCompressable()
{
return _response.Headers[HeaderNames.ContentRange] == StringValues.Empty && // The response is not partial
_response.Headers[HeaderNames.ContentEncoding] == StringValues.Empty && // Not specific encoding already set
_shouldCompressResponse(_response.HttpContext);
return !_response.Headers.ContainsKey(HeaderNames.ContentRange) && // The response is not partial
_provider.ShouldCompressResponse(_response.HttpContext);
}
}
}

View File

@ -9,26 +9,27 @@ namespace Microsoft.AspNetCore.ResponseCompression
/// <summary>
/// GZIP compression provider.
/// </summary>
public class GzipResponseCompressionProvider : IResponseCompressionProvider
public class GzipCompressionProvider : ICompressionProvider
{
private readonly CompressionLevel _level;
/// <summary>
/// Initialize a new <see cref="GzipResponseCompressionProvider"/>.
/// Initialize a new <see cref="GzipCompressionProvider"/>.
/// </summary>
/// <param name="level">The compression level.</param>
public GzipResponseCompressionProvider(CompressionLevel level)
public GzipCompressionProvider()
{
_level = level;
}
/// <inheritdoc />
public string EncodingName { get; } = "gzip";
public string EncodingName => "gzip";
/// <summary>
/// What level of compression to use for the stream.
/// </summary>
public CompressionLevel Level { get; set; } = CompressionLevel.Fastest;
/// <inheritdoc />
public Stream CreateStream(Stream outputStream)
{
return new GZipStream(outputStream, _level, true);
return new GZipStream(outputStream, Level, leaveOpen: true);
}
}
}

View File

@ -0,0 +1,25 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.IO;
namespace Microsoft.AspNetCore.ResponseCompression
{
/// <summary>
/// Provides a specific compression implementation to compress HTTP responses.
/// </summary>
public interface ICompressionProvider
{
/// <summary>
/// The encoding name used in the 'Accept-Encoding' request header and 'Content-Encoding' response header.
/// </summary>
string EncodingName { get; }
/// <summary>
/// Create a new compression stream.
/// </summary>
/// <param name="outputStream">The stream where the compressed data have to be written.</param>
/// <returns>The compression stream.</returns>
Stream CreateStream(Stream outputStream);
}
}

View File

@ -1,25 +1,27 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.IO;
using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.ResponseCompression
{
/// <summary>
/// Provides methods to be able to compress HTTP responses.
/// Used to examine requests and responses to see if compression should be enabled.
/// </summary>
public interface IResponseCompressionProvider
{
/// <summary>
/// The name that will be searched in the 'Accept-Encoding' request header.
/// Examines the request and selects an acceptable compression provider, if any.
/// </summary>
string EncodingName { get; }
/// <param name="context"></param>
/// <returns>A compression provider or null if compression should not be used.</returns>
ICompressionProvider GetCompressionProvider(HttpContext context);
/// <summary>
/// Create a new compression stream.
/// Examines the response on first write to see if compression should be used.
/// </summary>
/// <param name="outputStream">The stream where the compressed data have to be written.</param>
/// <returns>The new stream.</returns>
Stream CreateStream(Stream outputStream);
/// <param name="context"></param>
/// <returns></returns>
bool ShouldCompressResponse(HttpContext context);
}
}

View File

@ -3,7 +3,8 @@
using System;
using Microsoft.AspNetCore.ResponseCompression;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
namespace Microsoft.AspNetCore.Builder
{
@ -13,22 +14,53 @@ namespace Microsoft.AspNetCore.Builder
public static class ResponseCompressionExtensions
{
/// <summary>
/// Allows to compress HTTP Responses.
/// Add response compression services and enable compression for responses with the given MIME types.
/// </summary>
/// <param name="services">The <see cref="IServiceCollection"/> for adding services.</param>
/// <param name="mimeTypes">Response Content-Type MIME types to enable compression for.</param>
/// <returns></returns>
public static IServiceCollection AddResponseCompression(this IServiceCollection services, params string[] mimeTypes)
{
return services.AddResponseCompression(options =>
{
options.MimeTypes = mimeTypes;
});
}
/// <summary>
/// Add response compression services and configure the related options.
/// </summary>
/// <param name="services">The <see cref="IServiceCollection"/> for adding services.</param>
/// <param name="configureOptions">A delegate to configure the <see cref="ResponseCompressionOptions"/>.</param>
/// <returns></returns>
public static IServiceCollection AddResponseCompression(this IServiceCollection services, Action<ResponseCompressionOptions> configureOptions)
{
if (services == null)
{
throw new ArgumentNullException(nameof(services));
}
if (configureOptions == null)
{
throw new ArgumentNullException(nameof(configureOptions));
}
services.Configure(configureOptions);
services.TryAddTransient<IResponseCompressionProvider, ResponseCompressionProvider>();
return services;
}
/// <summary>
/// Adds middleware for dynamically compressing HTTP Responses.
/// </summary>
/// <param name="builder">The <see cref="IApplicationBuilder"/> instance this method extends.</param>
/// <param name="options">The <see cref="ResponseCompressionOptions"/>.</param>
public static IApplicationBuilder UseResponseCompression(this IApplicationBuilder builder, ResponseCompressionOptions options)
public static IApplicationBuilder UseResponseCompression(this IApplicationBuilder builder)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (options == null)
{
throw new ArgumentNullException(nameof(options));
}
return builder.UseMiddleware<ResponseCompressionMiddleware>(Options.Create(options));
return builder.UseMiddleware<ResponseCompressionMiddleware>();
}
}
}

View File

@ -2,14 +2,9 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.IO.Compression;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Primitives;
using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNetCore.ResponseCompression
{
@ -20,9 +15,7 @@ namespace Microsoft.AspNetCore.ResponseCompression
{
private readonly RequestDelegate _next;
private readonly Dictionary<string, IResponseCompressionProvider> _compressionProviders;
private readonly Func<HttpContext, bool> _shouldCompressResponse;
private readonly IResponseCompressionProvider _provider;
private readonly bool _enableHttps;
@ -30,35 +23,24 @@ namespace Microsoft.AspNetCore.ResponseCompression
/// Initialize the Response Compression middleware.
/// </summary>
/// <param name="next"></param>
/// <param name="provider"></param>
/// <param name="options"></param>
public ResponseCompressionMiddleware(RequestDelegate next, IOptions<ResponseCompressionOptions> options)
public ResponseCompressionMiddleware(RequestDelegate next, IResponseCompressionProvider provider, IOptions<ResponseCompressionOptions> options)
{
if (options.Value.ShouldCompressResponse == null)
if (next == null)
{
throw new ArgumentException($"{nameof(options.Value.ShouldCompressResponse)} is not provided in argument {nameof(options)}");
throw new ArgumentNullException(nameof(next));
}
if (provider == null)
{
throw new ArgumentNullException(nameof(provider));
}
if (options == null)
{
throw new ArgumentNullException(nameof(options));
}
_shouldCompressResponse = options.Value.ShouldCompressResponse;
_next = next;
var providers = options.Value.Providers;
if (providers == null)
{
providers = new IResponseCompressionProvider[]
{
new GzipResponseCompressionProvider(CompressionLevel.Fastest)
};
}
else if (!providers.Any())
{
throw new ArgumentException($"{nameof(options.Value.Providers)} cannot be empty in argument {nameof(options)}");
}
_compressionProviders = providers.ToDictionary(p => p.EncodingName, StringComparer.OrdinalIgnoreCase);
_compressionProviders.Add("*", providers.First());
_compressionProviders.Add("identity", null);
_provider = provider;
_enableHttps = options.Value.EnableHttps;
}
@ -69,11 +51,11 @@ namespace Microsoft.AspNetCore.ResponseCompression
/// <returns></returns>
public async Task Invoke(HttpContext context)
{
IResponseCompressionProvider compressionProvider = null;
ICompressionProvider compressionProvider = null;
if (!context.Request.IsHttps || _enableHttps)
{
compressionProvider = SelectProvider(context.Request.Headers[HeaderNames.AcceptEncoding]);
compressionProvider = _provider.GetCompressionProvider(context);
}
if (compressionProvider == null)
@ -84,7 +66,7 @@ namespace Microsoft.AspNetCore.ResponseCompression
var bodyStream = context.Response.Body;
using (var bodyWrapperStream = new BodyWrapperStream(context.Response, bodyStream, _shouldCompressResponse, compressionProvider))
using (var bodyWrapperStream = new BodyWrapperStream(context.Response, bodyStream, _provider, compressionProvider))
{
context.Response.Body = bodyWrapperStream;
@ -98,29 +80,5 @@ namespace Microsoft.AspNetCore.ResponseCompression
}
}
}
private IResponseCompressionProvider SelectProvider(StringValues acceptEncoding)
{
IList<StringWithQualityHeaderValue> unsorted;
if (StringWithQualityHeaderValue.TryParseList(acceptEncoding, out unsorted) && unsorted != null)
{
var sorted = unsorted
.Where(s => s.Quality.GetValueOrDefault(1) > 0)
.OrderByDescending(s => s.Quality.GetValueOrDefault(1));
foreach (var encoding in sorted)
{
IResponseCompressionProvider provider;
if (_compressionProviders.TryGetValue(encoding.Value, out provider))
{
return provider;
}
}
}
return null;
}
}
}

View File

@ -1,9 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.ResponseCompression
{
@ -13,19 +11,13 @@ namespace Microsoft.AspNetCore.ResponseCompression
public class ResponseCompressionOptions
{
/// <summary>
/// Called when an HTTP request accepts a compatible compression algorithm, and returns True
/// if the response should be compressed.
/// Response Content-Type MIME types to compress.
/// </summary>
public Func<HttpContext, bool> ShouldCompressResponse { get; set; }
public IEnumerable<string> MimeTypes { get; set; }
/// <summary>
/// The compression providers. If 'null', the GZIP provider is set as default.
/// </summary>
public IEnumerable<IResponseCompressionProvider> Providers { get; set; }
/// <summary>
/// 'False' to enable compression only on HTTP requests. Enable compression on HTTPS requests
/// may lead to security problems.
/// Indicates if responses over HTTPS connections should be compressed. The default is 'false'.
/// Enable compression on HTTPS connections may expose security problems.
/// </summary>
public bool EnableHttps { get; set; } = false;
}

View File

@ -0,0 +1,114 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Primitives;
using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNetCore.ResponseCompression
{
/// <inheritdoc />
public class ResponseCompressionProvider : IResponseCompressionProvider
{
private readonly ICompressionProvider[] _providers;
private readonly HashSet<string> _mimeTypes;
/// <summary>
/// If no compression providers are specified then GZip is used by default.
/// </summary>
/// <param name="providers">Compression providers to use, if any.</param>
/// <param name="options"></param>
public ResponseCompressionProvider(IEnumerable<ICompressionProvider> providers, IOptions<ResponseCompressionOptions> options)
{
if (providers == null)
{
throw new ArgumentNullException(nameof(providers));
}
if (options == null)
{
throw new ArgumentNullException(nameof(options));
}
_providers = providers.ToArray();
if (_providers.Length == 0)
{
_providers = new [] { new GzipCompressionProvider() };
}
if (options.Value.MimeTypes == null || !options.Value.MimeTypes.Any())
{
throw new InvalidOperationException("No mime types specified");
}
_mimeTypes = new HashSet<string>(options.Value.MimeTypes, StringComparer.OrdinalIgnoreCase);
}
/// <inheritdoc />
public virtual ICompressionProvider GetCompressionProvider(HttpContext context)
{
IList<StringWithQualityHeaderValue> unsorted;
// e.g. Accept-Encoding: gzip, deflate, sdch
var accept = context.Request.Headers[HeaderNames.AcceptEncoding];
if (!StringValues.IsNullOrEmpty(accept)
&& StringWithQualityHeaderValue.TryParseList(accept, out unsorted)
&& unsorted != null && unsorted.Count > 0)
{
// TODO PERF: clients don't usually include quality values so this sort will not have any effect. Fast-path?
var sorted = unsorted
.Where(s => s.Quality.GetValueOrDefault(1) > 0)
.OrderByDescending(s => s.Quality.GetValueOrDefault(1));
foreach (var encoding in sorted)
{
// There will rarely be more than three providers, and there's only one by default
foreach (var provider in _providers)
{
if (string.Equals(provider.EncodingName, encoding.Value, StringComparison.OrdinalIgnoreCase))
{
return provider;
}
}
// Uncommon but valid options
if (string.Equals("*", encoding.Value, StringComparison.Ordinal))
{
// Any
return _providers[0];
}
if (string.Equals("identity", encoding.Value, StringComparison.OrdinalIgnoreCase))
{
// No compression
return null;
}
}
}
return null;
}
/// <inheritdoc />
public virtual bool ShouldCompressResponse(HttpContext context)
{
var mimeType = context.Response.ContentType;
if (string.IsNullOrEmpty(mimeType))
{
return false;
}
var separator = mimeType.IndexOf(';');
if (separator >= 0)
{
// Remove the content-type optional parameters
mimeType = mimeType.Substring(0, separator);
mimeType = mimeType.Trim();
}
return _mimeTypes.Contains(mimeType);
}
}
}

View File

@ -1,49 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.ResponseCompression
{
/// <summary>
/// Response Compression middleware utility methods.
/// </summary>
public static class ResponseCompressionUtils
{
/// <summary>
/// Create a delegate that propose to compress response, depending on a list of authorized
/// MIME types for the HTTP response.
/// </summary>
public static Func<HttpContext, bool> CreateShouldCompressResponseDelegate(IEnumerable<string> mimeTypes)
{
if (mimeTypes == null)
{
throw new ArgumentNullException(nameof(mimeTypes));
}
var mimeTypeSet = new HashSet<string>(mimeTypes, StringComparer.OrdinalIgnoreCase);
return (httpContext) =>
{
var mimeType = httpContext.Response.ContentType;
if (string.IsNullOrEmpty(mimeType))
{
return false;
}
var separator = mimeType.IndexOf(';');
if (separator >= 0)
{
// Remove the content-type optional parameters
mimeType = mimeType.Substring(0, separator);
mimeType = mimeType.Trim();
}
return mimeTypeSet.Contains(mimeType);
};
}
}
}

View File

@ -3,15 +3,12 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.Options;
using Microsoft.Net.Http.Headers;
using Xunit;
@ -21,18 +18,6 @@ namespace Microsoft.AspNetCore.ResponseCompression.Tests
{
private const string TextPlain = "text/plain";
[Fact]
public void Options_NullShouldCompressResponse_Throws()
{
Assert.Throws<ArgumentException>(() =>
{
new ResponseCompressionMiddleware(null, Options.Create(new ResponseCompressionOptions()
{
ShouldCompressResponse = null
}));
});
}
[Fact]
public void Options_HttpsDisabledByDefault()
{
@ -41,19 +26,6 @@ namespace Microsoft.AspNetCore.ResponseCompression.Tests
Assert.False(options.EnableHttps);
}
[Fact]
public void Options_EmptyProviderList_Throws()
{
Assert.Throws<ArgumentException>(() =>
{
new ResponseCompressionMiddleware(null, Options.Create(new ResponseCompressionOptions()
{
ShouldCompressResponse = _ => true,
Providers = new IResponseCompressionProvider[0]
}));
});
}
[Fact]
public async Task Request_NoAcceptEncoding_Uncompressed()
{
@ -78,6 +50,91 @@ namespace Microsoft.AspNetCore.ResponseCompression.Tests
CheckResponseNotCompressed(response, expectedBodyLength: 100);
}
[Fact]
public void NoMimeTypes_Throws()
{
var builder = new WebHostBuilder()
.ConfigureServices(services =>
{
services.AddResponseCompression();
})
.Configure(app =>
{
app.UseResponseCompression();
app.Run(context =>
{
context.Response.ContentType = TextPlain;
return context.Response.WriteAsync(new string('a', 100));
});
});
Assert.Throws<InvalidOperationException>(() => new TestServer(builder));
}
[Theory]
[InlineData("text/plain")]
[InlineData("text/PLAIN")]
[InlineData("text/plain; charset=ISO-8859-4")]
[InlineData("text/plain ; charset=ISO-8859-4")]
public async Task ContentType_WithCharset_Compress(string contentType)
{
var builder = new WebHostBuilder()
.ConfigureServices(services =>
{
services.AddResponseCompression(TextPlain);
})
.Configure(app =>
{
app.UseResponseCompression();
app.Run(context =>
{
context.Response.ContentType = contentType;
return context.Response.WriteAsync(new string('a', 100));
});
});
var server = new TestServer(builder);
var client = server.CreateClient();
var request = new HttpRequestMessage(HttpMethod.Get, "");
request.Headers.AcceptEncoding.ParseAdd("gzip");
var response = await client.SendAsync(request);
Assert.Equal(24, response.Content.ReadAsByteArrayAsync().Result.Length);
}
[Theory]
[InlineData("")]
[InlineData("text/plain2")]
public async Task MimeTypes_OtherContentTypes_NoMatch(string contentType)
{
var builder = new WebHostBuilder()
.ConfigureServices(services =>
{
services.AddResponseCompression(TextPlain);
})
.Configure(app =>
{
app.UseResponseCompression();
app.Run(context =>
{
context.Response.ContentType = contentType;
return context.Response.WriteAsync(new string('a', 100));
});
});
var server = new TestServer(builder);
var client = server.CreateClient();
var request = new HttpRequestMessage(HttpMethod.Get, "");
request.Headers.AcceptEncoding.ParseAdd("gzip");
var response = await client.SendAsync(request);
Assert.Equal(100, response.Content.ReadAsByteArrayAsync().Result.Length);
}
[Fact]
public async Task Request_AcceptStar_Compressed()
{
@ -134,7 +191,7 @@ namespace Microsoft.AspNetCore.ResponseCompression.Tests
}
[Fact]
public async Task Response_WithContentEncodingAlreadySet_NotCompressed()
public async Task Response_WithContentEncodingAlreadySet_Stacked()
{
var otherContentEncoding = "something";
@ -143,9 +200,9 @@ namespace Microsoft.AspNetCore.ResponseCompression.Tests
r.Headers[HeaderNames.ContentEncoding] = otherContentEncoding;
});
Assert.NotNull(response.Headers.GetValues(HeaderNames.ContentMD5));
Assert.Single(response.Content.Headers.ContentEncoding, otherContentEncoding);
Assert.Equal(50, response.Content.Headers.ContentLength);
Assert.True(response.Content.Headers.ContentEncoding.Contains(otherContentEncoding));
Assert.True(response.Content.Headers.ContentEncoding.Contains("gzip"));
Assert.Equal(24, response.Content.Headers.ContentLength);
}
[Theory]
@ -153,52 +210,47 @@ namespace Microsoft.AspNetCore.ResponseCompression.Tests
[InlineData(true, 24)]
public async Task Request_Https_CompressedIfEnabled(bool enableHttps, int expectedLength)
{
var options = new ResponseCompressionOptions()
{
ShouldCompressResponse = _ => true,
Providers = new IResponseCompressionProvider[]
var builder = new WebHostBuilder()
.ConfigureServices(services =>
{
new GzipResponseCompressionProvider(CompressionLevel.Optimal)
},
EnableHttps = enableHttps
};
services.AddResponseCompression(options =>
{
options.EnableHttps = enableHttps;
options.MimeTypes = new[] { TextPlain };
});
})
.Configure(app =>
{
app.UseResponseCompression();
app.Run(context =>
{
context.Response.ContentType = TextPlain;
return context.Response.WriteAsync(new string('a', 100));
});
});
var middleware = new ResponseCompressionMiddleware(async context =>
{
context.Response.ContentType = TextPlain;
await context.Response.WriteAsync(new string('a', 100));
}, Options.Create(options));
var server = new TestServer(builder);
server.BaseAddress = new Uri("https://localhost/");
var client = server.CreateClient();
var httpContext = new DefaultHttpContext();
httpContext.Request.Headers[HeaderNames.AcceptEncoding] = "gzip";
httpContext.Request.IsHttps = true;
var request = new HttpRequestMessage(HttpMethod.Get, "");
request.Headers.AcceptEncoding.ParseAdd("gzip");
httpContext.Response.Body = new MemoryStream();
var response = await client.SendAsync(request);
await middleware.Invoke(httpContext);
Assert.Equal(expectedLength, httpContext.Response.Body.Length);
Assert.Equal(expectedLength, response.Content.ReadAsByteArrayAsync().Result.Length);
}
private Task<HttpResponseMessage> InvokeMiddleware(int uncompressedBodyLength, string[] requestAcceptEncodings, string responseType, Action<HttpResponse> addResponseAction = null)
{
var options = new ResponseCompressionOptions()
{
ShouldCompressResponse = ctx =>
{
var contentType = ctx.Response.Headers[HeaderNames.ContentType];
return contentType.ToString().IndexOf(TextPlain) >= 0;
},
Providers = new IResponseCompressionProvider[]
{
new GzipResponseCompressionProvider(CompressionLevel.Optimal)
}
};
var builder = new WebHostBuilder()
.ConfigureServices(services =>
{
services.AddResponseCompression(TextPlain);
})
.Configure(app =>
{
app.UseResponseCompression(options);
app.UseResponseCompression();
app.Run(context =>
{
context.Response.Headers[HeaderNames.ContentMD5] = "MD5";

View File

@ -1,69 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Linq;
using Microsoft.AspNetCore.Http;
using Xunit;
namespace Microsoft.AspNetCore.ResponseCompression.Tests
{
public class ResponseCompressionUtilsTest
{
private const string TextPlain = "text/plain";
[Fact]
public void CreateShouldCompressResponseDelegate_NullMimeTypes_Throws()
{
Assert.Throws<ArgumentNullException>(() =>
{
ResponseCompressionUtils.CreateShouldCompressResponseDelegate(null);
});
}
[Fact]
public void CreateShouldCompressResponseDelegate_Empty_DontCompress()
{
var httpContext = new DefaultHttpContext();
httpContext.Response.ContentType = TextPlain;
var func = ResponseCompressionUtils.CreateShouldCompressResponseDelegate(Enumerable.Empty<string>());
var result = func(httpContext);
Assert.False(result);
}
[Theory]
[InlineData("text/plain")]
[InlineData("text/PLAIN")]
[InlineData("text/plain; charset=ISO-8859-4")]
[InlineData("text/plain ; charset=ISO-8859-4")]
public void CreateShouldCompressResponseDelegate_WithCharset_Compress(string contentType)
{
var httpContext = new DefaultHttpContext();
httpContext.Response.ContentType = contentType;
var func = ResponseCompressionUtils.CreateShouldCompressResponseDelegate(new string[] { TextPlain });
var result = func(httpContext);
Assert.True(result);
}
[Theory]
[InlineData("")]
[InlineData("text/plain2")]
public void CreateShouldCompressResponseDelegate_OtherContentTypes_NoMatch(string contentType)
{
var httpContext = new DefaultHttpContext();
httpContext.Response.ContentType = contentType;
var func = ResponseCompressionUtils.CreateShouldCompressResponseDelegate(new string[] { TextPlain });
var result = func(httpContext);
Assert.False(result);
}
}
}