#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.
using System;
using System.IO;
using System.IO.Compression;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
@ -18,9 +18,13 @@ namespace ResponseCompressionSample
{
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<ICompressionProvider, GzipCompressionProvider>();
services.AddSingleton<ICompressionProvider, CustomCompressionProvider>();
services.AddResponseCompression("text/plain", "text/html");
services.Configure<GzipCompressionProviderOptions>(options => options.Level = CompressionLevel.Fastest);
services.AddResponseCompression(options =>
{
options.Providers.Add<GzipCompressionProvider>();
options.Providers.Add<CustomCompressionProvider>();
options.MimeTypes = new[] { "text/plain", "text/html" };
});
}
public void Configure(IApplicationBuilder app)
@ -63,10 +67,7 @@ namespace ResponseCompressionSample
public static void Main(string[] args)
{
var host = new WebHostBuilder()
.UseKestrel(options =>
{
options.UseConnectionLogging();
})
.UseKestrel()
// .UseWebListener()
.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.
// 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.Compression;
using Microsoft.Extensions.Options;
namespace Microsoft.AspNetCore.ResponseCompression
{
@ -11,6 +13,22 @@ namespace Microsoft.AspNetCore.ResponseCompression
/// </summary>
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 />
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 />
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.
/// </summary>
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>
/// 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="services">Services to use when instantiating compression providers.</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)
{
throw new ArgumentNullException(nameof(options));
}
_providers = providers.ToArray();
_providers = options.Value.Providers.ToArray();
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())
{
throw new InvalidOperationException("No MIME types specified");
throw new InvalidOperationException("No MIME types specified.");
}
_mimeTypes = new HashSet<string>(options.Value.MimeTypes, StringComparer.OrdinalIgnoreCase);
}

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

@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
@ -12,6 +13,7 @@ using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Net.Http.Headers;
using Xunit;
@ -108,6 +110,37 @@ namespace Microsoft.AspNetCore.ResponseCompression.Tests
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]
[InlineData("")]
[InlineData("text/plain2")]