#108 Move compression providers back to options

This commit is contained in:
Chris R 2016-10-13 15:50:45 -07:00
Родитель 88b78e7233
Коммит e7b41c4f53
8 изменённых файлов: 204 добавлений и 21 удалений

Просмотреть файл

@ -2,7 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
using System.IO; using System.IO.Compression;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
@ -18,9 +18,13 @@ namespace ResponseCompressionSample
{ {
public void ConfigureServices(IServiceCollection services) public void ConfigureServices(IServiceCollection services)
{ {
services.AddSingleton<ICompressionProvider, GzipCompressionProvider>(); services.Configure<GzipCompressionProviderOptions>(options => options.Level = CompressionLevel.Fastest);
services.AddSingleton<ICompressionProvider, CustomCompressionProvider>(); services.AddResponseCompression(options =>
services.AddResponseCompression("text/plain", "text/html"); {
options.Providers.Add<GzipCompressionProvider>();
options.Providers.Add<CustomCompressionProvider>();
options.MimeTypes = new[] { "text/plain", "text/html" };
});
} }
public void Configure(IApplicationBuilder app) public void Configure(IApplicationBuilder app)
@ -63,10 +67,7 @@ namespace ResponseCompressionSample
public static void Main(string[] args) public static void Main(string[] args)
{ {
var host = new WebHostBuilder() var host = new WebHostBuilder()
.UseKestrel(options => .UseKestrel()
{
options.UseConnectionLogging();
})
// .UseWebListener() // .UseWebListener()
.ConfigureLogging(factory => .ConfigureLogging(factory =>
{ {

Просмотреть файл

@ -0,0 +1,51 @@
// 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.ObjectModel;
#if NETSTANDARD1_3
using System.Reflection;
#endif
namespace Microsoft.AspNetCore.ResponseCompression
{
/// <summary>
/// A Collection of ICompressionProvider's that also allows them to be instantiated from an <see cref="IServiceProvider" />.
/// </summary>
public class CompressionProviderCollection : Collection<ICompressionProvider>
{
/// <summary>
/// Adds a type representing an <see cref="ICompressionProvider"/>.
/// </summary>
/// <remarks>
/// Provider instances will be created using an <see cref="IServiceProvider" />.
/// </remarks>
public void Add<TCompressionProvider>() where TCompressionProvider : ICompressionProvider
{
Add(typeof(TCompressionProvider));
}
/// <summary>
/// Adds a type representing an <see cref="ICompressionProvider"/>.
/// </summary>
/// <param name="providerType">Type representing an <see cref="ICompressionProvider"/>.</param>
/// <remarks>
/// Provider instances will be created using an <see cref="IServiceProvider" />.
/// </remarks>
public void Add(Type providerType)
{
if (providerType == null)
{
throw new ArgumentNullException(nameof(providerType));
}
if (!typeof(ICompressionProvider).IsAssignableFrom(providerType))
{
throw new ArgumentException($"The provider must implement {nameof(ICompressionProvider)}", nameof(providerType));
}
var factory = new CompressionProviderFactory(providerType);
Add(factory);
}
}
}

Просмотреть файл

@ -0,0 +1,48 @@
// 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.IO;
using Microsoft.Extensions.DependencyInjection;
namespace Microsoft.AspNetCore.ResponseCompression
{
/// <summary>
/// This is a placeholder for the CompressionProviderCollection that allows creating the given type via
/// an <see cref="IServiceProvider" />.
/// </summary>
internal class CompressionProviderFactory : ICompressionProvider
{
internal CompressionProviderFactory(Type providerType)
{
ProviderType = providerType;
}
internal Type ProviderType { get; }
internal ICompressionProvider CreateInstance(IServiceProvider serviceProvider)
{
if (serviceProvider == null)
{
throw new ArgumentNullException(nameof(serviceProvider));
}
return (ICompressionProvider)ActivatorUtilities.CreateInstance(serviceProvider, ProviderType, Type.EmptyTypes);
}
string ICompressionProvider.EncodingName
{
get { throw new NotSupportedException(); }
}
bool ICompressionProvider.SupportsFlush
{
get { throw new NotSupportedException(); }
}
Stream ICompressionProvider.CreateStream(Stream outputStream)
{
throw new NotSupportedException();
}
}
}

Просмотреть файл

@ -1,8 +1,10 @@
// Copyright (c) .NET Foundation. All rights reserved. // 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. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.IO; using System.IO;
using System.IO.Compression; using System.IO.Compression;
using Microsoft.Extensions.Options;
namespace Microsoft.AspNetCore.ResponseCompression namespace Microsoft.AspNetCore.ResponseCompression
{ {
@ -11,6 +13,22 @@ namespace Microsoft.AspNetCore.ResponseCompression
/// </summary> /// </summary>
public class GzipCompressionProvider : ICompressionProvider public class GzipCompressionProvider : ICompressionProvider
{ {
/// <summary>
/// Creates a new instance of GzipCompressionProvider with options.
/// </summary>
/// <param name="options"></param>
public GzipCompressionProvider(IOptions<GzipCompressionProviderOptions> options)
{
if (options == null)
{
throw new ArgumentNullException(nameof(options));
}
Options = options.Value;
}
private GzipCompressionProviderOptions Options { get; }
/// <inheritdoc /> /// <inheritdoc />
public string EncodingName => "gzip"; public string EncodingName => "gzip";
@ -29,15 +47,10 @@ namespace Microsoft.AspNetCore.ResponseCompression
} }
} }
/// <summary>
/// What level of compression to use for the stream.
/// </summary>
public CompressionLevel Level { get; set; } = CompressionLevel.Fastest;
/// <inheritdoc /> /// <inheritdoc />
public Stream CreateStream(Stream outputStream) public Stream CreateStream(Stream outputStream)
{ {
return new GZipStream(outputStream, Level, leaveOpen: true); return new GZipStream(outputStream, Options.Level, leaveOpen: true);
} }
} }
} }

Просмотреть файл

@ -0,0 +1,23 @@
// 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.IO.Compression;
using Microsoft.Extensions.Options;
namespace Microsoft.AspNetCore.ResponseCompression
{
/// <summary>
/// Options for the GzipCompressionProvider
/// </summary>
public class GzipCompressionProviderOptions : IOptions<GzipCompressionProviderOptions>
{
/// <summary>
/// What level of compression to use for the stream. The default is Fastest.
/// </summary>
public CompressionLevel Level { get; set; } = CompressionLevel.Fastest;
/// <inheritdoc />
GzipCompressionProviderOptions IOptions<GzipCompressionProviderOptions>.Value => this;
}
}

Просмотреть файл

@ -20,5 +20,10 @@ namespace Microsoft.AspNetCore.ResponseCompression
/// Enable compression on HTTPS connections may expose security problems. /// Enable compression on HTTPS connections may expose security problems.
/// </summary> /// </summary>
public bool EnableForHttps { get; set; } = false; public bool EnableForHttps { get; set; } = false;
/// <summary>
/// The ICompressionProviders to use for responses.
/// </summary>
public CompressionProviderCollection Providers { get; } = new CompressionProviderCollection();
} }
} }

Просмотреть файл

@ -20,28 +20,37 @@ namespace Microsoft.AspNetCore.ResponseCompression
/// <summary> /// <summary>
/// If no compression providers are specified then GZip is used by default. /// If no compression providers are specified then GZip is used by default.
/// </summary> /// </summary>
/// <param name="providers">Compression providers to use, if any.</param> /// <param name="services">Services to use when instantiating compression providers.</param>
/// <param name="options"></param> /// <param name="options"></param>
public ResponseCompressionProvider(IEnumerable<ICompressionProvider> providers, IOptions<ResponseCompressionOptions> options) public ResponseCompressionProvider(IServiceProvider services, IOptions<ResponseCompressionOptions> options)
{ {
if (providers == null) if (services == null)
{ {
throw new ArgumentNullException(nameof(providers)); throw new ArgumentNullException(nameof(services));
} }
if (options == null) if (options == null)
{ {
throw new ArgumentNullException(nameof(options)); throw new ArgumentNullException(nameof(options));
} }
_providers = providers.ToArray(); _providers = options.Value.Providers.ToArray();
if (_providers.Length == 0) if (_providers.Length == 0)
{ {
_providers = new [] { new GzipCompressionProvider() }; // Use the factory so it can resolve IOptions<GzipCompressionProviderOptions> from DI.
_providers = new ICompressionProvider[] { new CompressionProviderFactory(typeof(GzipCompressionProvider)) };
}
for (var i = 0; i < _providers.Length; i++)
{
var factory = _providers[i] as CompressionProviderFactory;
if (factory != null)
{
_providers[i] = factory.CreateInstance(services);
}
} }
if (options.Value.MimeTypes == null || !options.Value.MimeTypes.Any()) if (options.Value.MimeTypes == null || !options.Value.MimeTypes.Any())
{ {
throw new InvalidOperationException("No MIME types specified"); throw new InvalidOperationException("No MIME types specified.");
} }
_mimeTypes = new HashSet<string>(options.Value.MimeTypes, StringComparer.OrdinalIgnoreCase); _mimeTypes = new HashSet<string>(options.Value.MimeTypes, StringComparer.OrdinalIgnoreCase);
} }

Просмотреть файл

@ -4,6 +4,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.IO.Compression;
using System.Net.Http; using System.Net.Http;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -12,6 +13,7 @@ using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.TestHost; using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Net.Http.Headers; using Microsoft.Net.Http.Headers;
using Xunit; using Xunit;
@ -108,6 +110,37 @@ namespace Microsoft.AspNetCore.ResponseCompression.Tests
CheckResponseCompressed(response, expectedBodyLength: 24); CheckResponseCompressed(response, expectedBodyLength: 24);
} }
[Fact]
public async Task GZipCompressionProvider_OptionsSetInDI_Compress()
{
var builder = new WebHostBuilder()
.ConfigureServices(services =>
{
services.Configure<GzipCompressionProviderOptions>(options => options.Level = CompressionLevel.NoCompression);
services.AddResponseCompression(TextPlain);
})
.Configure(app =>
{
app.UseResponseCompression();
app.Run(context =>
{
context.Response.Headers[HeaderNames.ContentMD5] = "MD5";
context.Response.ContentType = TextPlain;
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);
CheckResponseCompressed(response, expectedBodyLength: 123);
}
[Theory] [Theory]
[InlineData("")] [InlineData("")]
[InlineData("text/plain2")] [InlineData("text/plain2")]