Merging 2.2 into master
This commit is contained in:
commit
b0ebdba897
|
|
@ -76,6 +76,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.HostFi
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.HostFiltering", "src\Microsoft.AspNetCore.HostFiltering\Microsoft.AspNetCore.HostFiltering.csproj", "{762F7276-C916-4111-A6C0-41668ABB3823}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.HostFiltering", "src\Microsoft.AspNetCore.HostFiltering\Microsoft.AspNetCore.HostFiltering.csproj", "{762F7276-C916-4111-A6C0-41668ABB3823}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "benchmarks", "benchmarks", "{C6DA6317-30FC-42FE-891C-64E75D88FF12}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.ResponseCompression.Benchmarks", "benchmarks\Microsoft.AspNetCore.ResponseCompression.Benchmarks\Microsoft.AspNetCore.ResponseCompression.Benchmarks.csproj", "{5AF10E85-5076-40B9-84CF-9830B585ABE5}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
|
@ -154,6 +158,10 @@ Global
|
||||||
{762F7276-C916-4111-A6C0-41668ABB3823}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{762F7276-C916-4111-A6C0-41668ABB3823}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{762F7276-C916-4111-A6C0-41668ABB3823}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{762F7276-C916-4111-A6C0-41668ABB3823}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{762F7276-C916-4111-A6C0-41668ABB3823}.Release|Any CPU.Build.0 = Release|Any CPU
|
{762F7276-C916-4111-A6C0-41668ABB3823}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{5AF10E85-5076-40B9-84CF-9830B585ABE5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{5AF10E85-5076-40B9-84CF-9830B585ABE5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{5AF10E85-5076-40B9-84CF-9830B585ABE5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{5AF10E85-5076-40B9-84CF-9830B585ABE5}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|
@ -178,6 +186,7 @@ Global
|
||||||
{5CEA6F31-A829-4A02-8CD5-EC3DDD4CC1EA} = {59A9B64C-E9BE-409E-89A2-58D72E2918F5}
|
{5CEA6F31-A829-4A02-8CD5-EC3DDD4CC1EA} = {59A9B64C-E9BE-409E-89A2-58D72E2918F5}
|
||||||
{4BC947ED-13B8-4BE6-82A4-96A48D86980B} = {8437B0F3-3894-4828-A945-A9187F37631D}
|
{4BC947ED-13B8-4BE6-82A4-96A48D86980B} = {8437B0F3-3894-4828-A945-A9187F37631D}
|
||||||
{762F7276-C916-4111-A6C0-41668ABB3823} = {A5076D28-FA7E-4606-9410-FEDD0D603527}
|
{762F7276-C916-4111-A6C0-41668ABB3823} = {A5076D28-FA7E-4606-9410-FEDD0D603527}
|
||||||
|
{5AF10E85-5076-40B9-84CF-9830B585ABE5} = {C6DA6317-30FC-42FE-891C-64E75D88FF12}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
SolutionGuid = {4518E9CE-3680-4E05-9259-B64EA7807158}
|
SolutionGuid = {4518E9CE-3680-4E05-9259-B64EA7807158}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
[assembly: BenchmarkDotNet.Attributes.AspNetCoreBenchmark]
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.ResponseCompression\Microsoft.AspNetCore.ResponseCompression.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="BenchmarkDotNet" Version="$(BenchmarkDotNetPackageVersion)" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Http" Version="$(MicrosoftAspNetCoreHttpPackageVersion)" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.BenchmarkRunner.Sources" Version="$(MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion)" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="$(MicrosoftExtensionsDependencyInjectionPackageVersion)" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
|
|
@ -0,0 +1,57 @@
|
||||||
|
// 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.Collections.Generic;
|
||||||
|
using BenchmarkDotNet.Attributes;
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using Microsoft.Net.Http.Headers;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.ResponseCompression.Benchmarks
|
||||||
|
{
|
||||||
|
public class ResponseCompressionProviderBenchmark
|
||||||
|
{
|
||||||
|
[GlobalSetup]
|
||||||
|
public void GlobalSetup()
|
||||||
|
{
|
||||||
|
var services = new ServiceCollection()
|
||||||
|
.AddOptions()
|
||||||
|
.AddResponseCompression()
|
||||||
|
.BuildServiceProvider();
|
||||||
|
|
||||||
|
var options = new ResponseCompressionOptions();
|
||||||
|
|
||||||
|
Provider = new ResponseCompressionProvider(services, Options.Create(options));
|
||||||
|
}
|
||||||
|
|
||||||
|
[ParamsSource(nameof(EncodingStrings))]
|
||||||
|
public string AcceptEncoding { get; set; }
|
||||||
|
|
||||||
|
public static IEnumerable<string> EncodingStrings()
|
||||||
|
{
|
||||||
|
return new[]
|
||||||
|
{
|
||||||
|
"gzip;q=0.8, compress;q=0.6, br;q=0.4",
|
||||||
|
"gzip, compress, br",
|
||||||
|
"br, compress, gzip",
|
||||||
|
"gzip, compress",
|
||||||
|
"identity",
|
||||||
|
"*"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResponseCompressionProvider Provider { get; set; }
|
||||||
|
|
||||||
|
[Benchmark]
|
||||||
|
public ICompressionProvider GetCompressionProvider()
|
||||||
|
{
|
||||||
|
var context = new DefaultHttpContext();
|
||||||
|
|
||||||
|
context.Request.Headers[HeaderNames.AcceptEncoding] = AcceptEncoding;
|
||||||
|
|
||||||
|
return Provider.GetCompressionProvider(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,7 +3,9 @@
|
||||||
<MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
|
<MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Label="Package Versions">
|
<PropertyGroup Label="Package Versions">
|
||||||
|
<BenchmarkDotNetPackageVersion>0.10.14</BenchmarkDotNetPackageVersion>
|
||||||
<InternalAspNetCoreSdkPackageVersion>3.0.0-alpha1-10005</InternalAspNetCoreSdkPackageVersion>
|
<InternalAspNetCoreSdkPackageVersion>3.0.0-alpha1-10005</InternalAspNetCoreSdkPackageVersion>
|
||||||
|
<MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>3.0.0-alpha1-10044</MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>
|
||||||
<MicrosoftAspNetCoreHostingAbstractionsPackageVersion>3.0.0-alpha1-10044</MicrosoftAspNetCoreHostingAbstractionsPackageVersion>
|
<MicrosoftAspNetCoreHostingAbstractionsPackageVersion>3.0.0-alpha1-10044</MicrosoftAspNetCoreHostingAbstractionsPackageVersion>
|
||||||
<MicrosoftAspNetCoreHttpAbstractionsPackageVersion>3.0.0-alpha1-10044</MicrosoftAspNetCoreHttpAbstractionsPackageVersion>
|
<MicrosoftAspNetCoreHttpAbstractionsPackageVersion>3.0.0-alpha1-10044</MicrosoftAspNetCoreHttpAbstractionsPackageVersion>
|
||||||
<MicrosoftAspNetCoreHttpExtensionsPackageVersion>3.0.0-alpha1-10044</MicrosoftAspNetCoreHttpExtensionsPackageVersion>
|
<MicrosoftAspNetCoreHttpExtensionsPackageVersion>3.0.0-alpha1-10044</MicrosoftAspNetCoreHttpExtensionsPackageVersion>
|
||||||
|
|
@ -16,6 +18,7 @@
|
||||||
<MicrosoftExtensionsConfigurationBinderPackageVersion>3.0.0-alpha1-10044</MicrosoftExtensionsConfigurationBinderPackageVersion>
|
<MicrosoftExtensionsConfigurationBinderPackageVersion>3.0.0-alpha1-10044</MicrosoftExtensionsConfigurationBinderPackageVersion>
|
||||||
<MicrosoftExtensionsConfigurationJsonPackageVersion>3.0.0-alpha1-10044</MicrosoftExtensionsConfigurationJsonPackageVersion>
|
<MicrosoftExtensionsConfigurationJsonPackageVersion>3.0.0-alpha1-10044</MicrosoftExtensionsConfigurationJsonPackageVersion>
|
||||||
<MicrosoftExtensionsFileProvidersAbstractionsPackageVersion>3.0.0-alpha1-10044</MicrosoftExtensionsFileProvidersAbstractionsPackageVersion>
|
<MicrosoftExtensionsFileProvidersAbstractionsPackageVersion>3.0.0-alpha1-10044</MicrosoftExtensionsFileProvidersAbstractionsPackageVersion>
|
||||||
|
<MicrosoftExtensionsDependencyInjectionPackageVersion>3.0.0-alpha1-10044</MicrosoftExtensionsDependencyInjectionPackageVersion>
|
||||||
<MicrosoftExtensionsLoggingAbstractionsPackageVersion>3.0.0-alpha1-10044</MicrosoftExtensionsLoggingAbstractionsPackageVersion>
|
<MicrosoftExtensionsLoggingAbstractionsPackageVersion>3.0.0-alpha1-10044</MicrosoftExtensionsLoggingAbstractionsPackageVersion>
|
||||||
<MicrosoftExtensionsLoggingConsolePackageVersion>3.0.0-alpha1-10044</MicrosoftExtensionsLoggingConsolePackageVersion>
|
<MicrosoftExtensionsLoggingConsolePackageVersion>3.0.0-alpha1-10044</MicrosoftExtensionsLoggingConsolePackageVersion>
|
||||||
<MicrosoftExtensionsLoggingTestingPackageVersion>3.0.0-alpha1-10044</MicrosoftExtensionsLoggingTestingPackageVersion>
|
<MicrosoftExtensionsLoggingTestingPackageVersion>3.0.0-alpha1-10044</MicrosoftExtensionsLoggingTestingPackageVersion>
|
||||||
|
|
|
||||||
|
|
@ -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.IO;
|
||||||
|
using System.IO.Compression;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.ResponseCompression
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Brotli compression provider.
|
||||||
|
/// </summary>
|
||||||
|
public class BrotliCompressionProvider : ICompressionProvider
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of <see cref="BrotliCompressionProvider"/> with options.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="options"></param>
|
||||||
|
public BrotliCompressionProvider(IOptions<BrotliCompressionProviderOptions> options)
|
||||||
|
{
|
||||||
|
if (options == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(options));
|
||||||
|
}
|
||||||
|
|
||||||
|
Options = options.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private BrotliCompressionProviderOptions Options { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public string EncodingName => "br";
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public bool SupportsFlush => true;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public Stream CreateStream(Stream outputStream)
|
||||||
|
{
|
||||||
|
#if NETCOREAPP2_1
|
||||||
|
return new BrotliStream(outputStream, Options.Level, leaveOpen: true);
|
||||||
|
#elif NET461 || NETSTANDARD2_0
|
||||||
|
// Brotli is only supported in .NET Core 2.1+
|
||||||
|
throw new PlatformNotSupportedException();
|
||||||
|
#else
|
||||||
|
#error Target frameworks need to be updated.
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
// 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.Compression;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.ResponseCompression
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Options for the <see cref="BrotliCompressionProvider"/>
|
||||||
|
/// </summary>
|
||||||
|
public class BrotliCompressionProviderOptions : IOptions<BrotliCompressionProviderOptions>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// What level of compression to use for the stream. The default is <see cref="CompressionLevel.Fastest"/>.
|
||||||
|
/// </summary>
|
||||||
|
public CompressionLevel Level { get; set; } = CompressionLevel.Fastest;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
BrotliCompressionProviderOptions IOptions<BrotliCompressionProviderOptions>.Value => this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -39,7 +39,7 @@ namespace Microsoft.AspNetCore.ResponseCompression
|
||||||
{
|
{
|
||||||
#if NET461
|
#if NET461
|
||||||
return false;
|
return false;
|
||||||
#elif NETSTANDARD2_0
|
#elif NETSTANDARD2_0 || NETCOREAPP2_1
|
||||||
return true;
|
return true;
|
||||||
#else
|
#else
|
||||||
#error target frameworks need to be updated
|
#error target frameworks need to be updated
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Description>ASP.NET Core middleware for HTTP Response compression.</Description>
|
<Description>ASP.NET Core middleware for HTTP Response compression.</Description>
|
||||||
<TargetFrameworks>net461;netstandard2.0</TargetFrameworks>
|
<TargetFrameworks>net461;netstandard2.0;netcoreapp2.1</TargetFrameworks>
|
||||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
<PackageTags>aspnetcore</PackageTags>
|
<PackageTags>aspnetcore</PackageTags>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,8 @@ namespace Microsoft.AspNetCore.ResponseCompression
|
||||||
public bool EnableForHttps { get; set; } = false;
|
public bool EnableForHttps { get; set; } = false;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The ICompressionProviders to use for responses.
|
/// The <see cref="ICompressionProvider"/> types to use for responses.
|
||||||
|
/// Providers are prioritized based on the order they are added.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public CompressionProviderCollection Providers { get; } = new CompressionProviderCollection();
|
public CompressionProviderCollection Providers { get; } = new CompressionProviderCollection();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,17 @@ namespace Microsoft.AspNetCore.ResponseCompression
|
||||||
if (_providers.Length == 0)
|
if (_providers.Length == 0)
|
||||||
{
|
{
|
||||||
// Use the factory so it can resolve IOptions<GzipCompressionProviderOptions> from DI.
|
// Use the factory so it can resolve IOptions<GzipCompressionProviderOptions> from DI.
|
||||||
_providers = new ICompressionProvider[] { new CompressionProviderFactory(typeof(GzipCompressionProvider)) };
|
_providers = new ICompressionProvider[]
|
||||||
|
{
|
||||||
|
#if NETCOREAPP2_1
|
||||||
|
new CompressionProviderFactory(typeof(BrotliCompressionProvider)),
|
||||||
|
#elif NET461 || NETSTANDARD2_0
|
||||||
|
// Brotli is only supported in .NET Core 2.1+
|
||||||
|
#else
|
||||||
|
#error Target frameworks need to be updated.
|
||||||
|
#endif
|
||||||
|
new CompressionProviderFactory(typeof(GzipCompressionProvider)),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
for (var i = 0; i < _providers.Length; i++)
|
for (var i = 0; i < _providers.Length; i++)
|
||||||
{
|
{
|
||||||
|
|
@ -62,42 +72,76 @@ namespace Microsoft.AspNetCore.ResponseCompression
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public virtual ICompressionProvider GetCompressionProvider(HttpContext context)
|
public virtual ICompressionProvider GetCompressionProvider(HttpContext context)
|
||||||
{
|
{
|
||||||
IList<StringWithQualityHeaderValue> unsorted;
|
|
||||||
|
|
||||||
// e.g. Accept-Encoding: gzip, deflate, sdch
|
// e.g. Accept-Encoding: gzip, deflate, sdch
|
||||||
var accept = context.Request.Headers[HeaderNames.AcceptEncoding];
|
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)
|
if (StringValues.IsNullOrEmpty(accept))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StringWithQualityHeaderValue.TryParseList(accept, out var encodings))
|
||||||
|
{
|
||||||
|
if (encodings.Count == 0)
|
||||||
{
|
{
|
||||||
// There will rarely be more than three providers, and there's only one by default
|
return null;
|
||||||
foreach (var provider in _providers)
|
}
|
||||||
|
|
||||||
|
var candidates = new HashSet<ProviderCandidate>();
|
||||||
|
|
||||||
|
foreach (var encoding in encodings)
|
||||||
|
{
|
||||||
|
var encodingName = encoding.Value;
|
||||||
|
var quality = encoding.Quality.GetValueOrDefault(1);
|
||||||
|
|
||||||
|
if (quality < double.Epsilon)
|
||||||
{
|
{
|
||||||
if (StringSegment.Equals(provider.EncodingName, encoding.Value, StringComparison.OrdinalIgnoreCase))
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < _providers.Length; i++)
|
||||||
|
{
|
||||||
|
var provider = _providers[i];
|
||||||
|
|
||||||
|
if (StringSegment.Equals(provider.EncodingName, encodingName, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
return provider;
|
candidates.Add(new ProviderCandidate(provider.EncodingName, quality, i, provider));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Uncommon but valid options
|
// Uncommon but valid options
|
||||||
if (StringSegment.Equals("*", encoding.Value, StringComparison.Ordinal))
|
if (StringSegment.Equals("*", encodingName, StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
// Any
|
for (int i = 0; i < _providers.Length; i++)
|
||||||
return _providers[0];
|
{
|
||||||
|
var provider = _providers[i];
|
||||||
|
|
||||||
|
// Any provider is a candidate.
|
||||||
|
candidates.Add(new ProviderCandidate(provider.EncodingName, quality, i, provider));
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if (StringSegment.Equals("identity", encoding.Value, StringComparison.OrdinalIgnoreCase))
|
|
||||||
|
if (StringSegment.Equals("identity", encodingName, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
// No compression
|
// We add 'identity' to the list of "candidates" with a very low priority and no provider.
|
||||||
return null;
|
// This will allow it to be ordered based on its quality (and priority) later in the method.
|
||||||
|
candidates.Add(new ProviderCandidate(encodingName.Value, quality, priority: int.MaxValue, provider: null));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (candidates.Count <= 1)
|
||||||
|
{
|
||||||
|
return candidates.ElementAtOrDefault(0).Provider;
|
||||||
|
}
|
||||||
|
|
||||||
|
var accepted = candidates
|
||||||
|
.OrderByDescending(x => x.Quality)
|
||||||
|
.ThenBy(x => x.Priority)
|
||||||
|
.First();
|
||||||
|
|
||||||
|
return accepted.Provider;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -139,5 +183,39 @@ namespace Microsoft.AspNetCore.ResponseCompression
|
||||||
}
|
}
|
||||||
return !string.IsNullOrEmpty(context.Request.Headers[HeaderNames.AcceptEncoding]);
|
return !string.IsNullOrEmpty(context.Request.Headers[HeaderNames.AcceptEncoding]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private readonly struct ProviderCandidate : IEquatable<ProviderCandidate>
|
||||||
|
{
|
||||||
|
public ProviderCandidate(string encodingName, double quality, int priority, ICompressionProvider provider)
|
||||||
|
{
|
||||||
|
EncodingName = encodingName;
|
||||||
|
Quality = quality;
|
||||||
|
Priority = priority;
|
||||||
|
Provider = provider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string EncodingName { get; }
|
||||||
|
|
||||||
|
public double Quality { get; }
|
||||||
|
|
||||||
|
public int Priority { get; }
|
||||||
|
|
||||||
|
public ICompressionProvider Provider { get; }
|
||||||
|
|
||||||
|
public bool Equals(ProviderCandidate other)
|
||||||
|
{
|
||||||
|
return string.Equals(EncodingName, other.EncodingName, StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Equals(object obj)
|
||||||
|
{
|
||||||
|
return obj is ProviderCandidate candidate && Equals(candidate);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
return StringComparer.OrdinalIgnoreCase.GetHashCode(EncodingName);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.IO.Compression;
|
using System.IO.Compression;
|
||||||
|
using System.Linq;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
@ -23,6 +24,26 @@ namespace Microsoft.AspNetCore.ResponseCompression.Tests
|
||||||
{
|
{
|
||||||
private const string TextPlain = "text/plain";
|
private const string TextPlain = "text/plain";
|
||||||
|
|
||||||
|
public static IEnumerable<object[]> SupportedEncodings =>
|
||||||
|
TestData.Select(x => new object[] { x.EncodingName });
|
||||||
|
|
||||||
|
public static IEnumerable<object[]> SupportedEncodingsWithBodyLength =>
|
||||||
|
TestData.Select(x => new object[] { x.EncodingName, x.ExpectedBodyLength });
|
||||||
|
|
||||||
|
private static IEnumerable<EncodingTestData> TestData
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
yield return new EncodingTestData("gzip", expectedBodyLength: 24);
|
||||||
|
#if NETCOREAPP2_2
|
||||||
|
yield return new EncodingTestData("br", expectedBodyLength: 20);
|
||||||
|
#elif NET461
|
||||||
|
#else
|
||||||
|
#error Target frameworks need to be updated.
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Options_HttpsDisabledByDefault()
|
public void Options_HttpsDisabledByDefault()
|
||||||
{
|
{
|
||||||
|
|
@ -42,15 +63,66 @@ namespace Microsoft.AspNetCore.ResponseCompression.Tests
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task Request_AcceptGzipDeflate_CompressedGzip()
|
public async Task Request_AcceptGzipDeflate_CompressedGzip()
|
||||||
{
|
{
|
||||||
var response = await InvokeMiddleware(100, requestAcceptEncodings: new string[] { "gzip", "deflate" }, responseType: TextPlain);
|
var response = await InvokeMiddleware(100, requestAcceptEncodings: new[] { "gzip", "deflate" }, responseType: TextPlain);
|
||||||
|
|
||||||
CheckResponseCompressed(response, expectedBodyLength: 24);
|
CheckResponseCompressed(response, expectedBodyLength: 24, expectedEncoding: "gzip");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Request_AcceptBrotli_CompressedBrotli()
|
||||||
|
{
|
||||||
|
var response = await InvokeMiddleware(100, requestAcceptEncodings: new[] { "br" }, responseType: TextPlain);
|
||||||
|
|
||||||
|
#if NET461
|
||||||
|
CheckResponseNotCompressed(response, expectedBodyLength: 100, sendVaryHeader: true);
|
||||||
|
#elif NETCOREAPP2_2
|
||||||
|
CheckResponseCompressed(response, expectedBodyLength: 20, expectedEncoding: "br");
|
||||||
|
#else
|
||||||
|
#error Target frameworks need to be updated.
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("gzip", "br")]
|
||||||
|
[InlineData("br", "gzip")]
|
||||||
|
public async Task Request_AcceptMixed_CompressedBrotli(string encoding1, string encoding2)
|
||||||
|
{
|
||||||
|
var response = await InvokeMiddleware(100, new[] { encoding1, encoding2 }, responseType: TextPlain);
|
||||||
|
|
||||||
|
#if NET461
|
||||||
|
CheckResponseCompressed(response, expectedBodyLength: 24, expectedEncoding: "gzip");
|
||||||
|
#elif NETCOREAPP2_2
|
||||||
|
CheckResponseCompressed(response, expectedBodyLength: 20, expectedEncoding: "br");
|
||||||
|
#else
|
||||||
|
#error Target frameworks need to be updated.
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#if NETCOREAPP2_2
|
||||||
|
[Theory]
|
||||||
|
[InlineData("gzip", "br")]
|
||||||
|
[InlineData("br", "gzip")]
|
||||||
|
public async Task Request_AcceptMixed_ConfiguredOrder_CompressedGzip(string encoding1, string encoding2)
|
||||||
|
{
|
||||||
|
void Configure(ResponseCompressionOptions options)
|
||||||
|
{
|
||||||
|
options.Providers.Add<GzipCompressionProvider>();
|
||||||
|
options.Providers.Add<BrotliCompressionProvider>();
|
||||||
|
}
|
||||||
|
|
||||||
|
var response = await InvokeMiddleware(100, new[] { encoding1, encoding2 }, responseType: TextPlain, configure: Configure);
|
||||||
|
|
||||||
|
CheckResponseCompressed(response, expectedBodyLength: 24, expectedEncoding: "gzip");
|
||||||
|
}
|
||||||
|
#elif NET461
|
||||||
|
#else
|
||||||
|
#error Target frameworks need to be updated.
|
||||||
|
#endif
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task Request_AcceptUnknown_NotCompressed()
|
public async Task Request_AcceptUnknown_NotCompressed()
|
||||||
{
|
{
|
||||||
var response = await InvokeMiddleware(100, requestAcceptEncodings: new string[] { "unknown" }, responseType: TextPlain);
|
var response = await InvokeMiddleware(100, requestAcceptEncodings: new[] { "unknown" }, responseType: TextPlain);
|
||||||
|
|
||||||
CheckResponseNotCompressed(response, expectedBodyLength: 100, sendVaryHeader: true);
|
CheckResponseNotCompressed(response, expectedBodyLength: 100, sendVaryHeader: true);
|
||||||
}
|
}
|
||||||
|
|
@ -86,7 +158,7 @@ namespace Microsoft.AspNetCore.ResponseCompression.Tests
|
||||||
|
|
||||||
var response = await client.SendAsync(request);
|
var response = await client.SendAsync(request);
|
||||||
|
|
||||||
CheckResponseCompressed(response, expectedBodyLength: 24);
|
CheckResponseCompressed(response, expectedBodyLength: 24, expectedEncoding: "gzip");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -117,7 +189,7 @@ namespace Microsoft.AspNetCore.ResponseCompression.Tests
|
||||||
|
|
||||||
var response = await client.SendAsync(request);
|
var response = await client.SendAsync(request);
|
||||||
|
|
||||||
CheckResponseCompressed(response, expectedBodyLength: 123);
|
CheckResponseCompressed(response, expectedBodyLength: 123, expectedEncoding: "gzip");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
|
|
@ -191,32 +263,38 @@ namespace Microsoft.AspNetCore.ResponseCompression.Tests
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task Request_AcceptStar_Compressed()
|
public async Task Request_AcceptStar_Compressed()
|
||||||
{
|
{
|
||||||
var response = await InvokeMiddleware(100, requestAcceptEncodings: new string[] { "*" }, responseType: TextPlain);
|
var response = await InvokeMiddleware(100, requestAcceptEncodings: new[] { "*" }, responseType: TextPlain);
|
||||||
|
|
||||||
CheckResponseCompressed(response, expectedBodyLength: 24);
|
#if NET461
|
||||||
|
CheckResponseCompressed(response, expectedBodyLength: 24, expectedEncoding: "gzip");
|
||||||
|
#elif NETCOREAPP2_2
|
||||||
|
CheckResponseCompressed(response, expectedBodyLength: 20, expectedEncoding: "br");
|
||||||
|
#else
|
||||||
|
#error Target frameworks need to be updated.
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task Request_AcceptIdentity_NotCompressed()
|
public async Task Request_AcceptIdentity_NotCompressed()
|
||||||
{
|
{
|
||||||
var response = await InvokeMiddleware(100, requestAcceptEncodings: new string[] { "identity" }, responseType: TextPlain);
|
var response = await InvokeMiddleware(100, requestAcceptEncodings: new[] { "identity" }, responseType: TextPlain);
|
||||||
|
|
||||||
CheckResponseNotCompressed(response, expectedBodyLength: 100, sendVaryHeader: true);
|
CheckResponseNotCompressed(response, expectedBodyLength: 100, sendVaryHeader: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData(new string[] { "identity;q=0.5", "gzip;q=1" }, 24)]
|
[InlineData(new[] { "identity;q=0.5", "gzip;q=1" }, 24)]
|
||||||
[InlineData(new string[] { "identity;q=0", "gzip;q=0.8" }, 24)]
|
[InlineData(new[] { "identity;q=0", "gzip;q=0.8" }, 24)]
|
||||||
[InlineData(new string[] { "identity;q=0.5", "gzip" }, 24)]
|
[InlineData(new[] { "identity;q=0.5", "gzip" }, 24)]
|
||||||
public async Task Request_AcceptWithHigherCompressionQuality_Compressed(string[] acceptEncodings, int expectedBodyLength)
|
public async Task Request_AcceptWithHigherCompressionQuality_Compressed(string[] acceptEncodings, int expectedBodyLength)
|
||||||
{
|
{
|
||||||
var response = await InvokeMiddleware(100, requestAcceptEncodings: acceptEncodings, responseType: TextPlain);
|
var response = await InvokeMiddleware(100, requestAcceptEncodings: acceptEncodings, responseType: TextPlain);
|
||||||
|
|
||||||
CheckResponseCompressed(response, expectedBodyLength: expectedBodyLength);
|
CheckResponseCompressed(response, expectedBodyLength: expectedBodyLength, expectedEncoding: "gzip");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData(new string[] { "gzip;q=0.5", "identity;q=0.8" }, 100)]
|
[InlineData(new[] { "gzip;q=0.5", "identity;q=0.8" }, 100)]
|
||||||
public async Task Request_AcceptWithhigherIdentityQuality_NotCompressed(string[] acceptEncodings, int expectedBodyLength)
|
public async Task Request_AcceptWithhigherIdentityQuality_NotCompressed(string[] acceptEncodings, int expectedBodyLength)
|
||||||
{
|
{
|
||||||
var response = await InvokeMiddleware(100, requestAcceptEncodings: acceptEncodings, responseType: TextPlain);
|
var response = await InvokeMiddleware(100, requestAcceptEncodings: acceptEncodings, responseType: TextPlain);
|
||||||
|
|
@ -227,7 +305,7 @@ namespace Microsoft.AspNetCore.ResponseCompression.Tests
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task Response_UnknownMimeType_NotCompressed()
|
public async Task Response_UnknownMimeType_NotCompressed()
|
||||||
{
|
{
|
||||||
var response = await InvokeMiddleware(100, requestAcceptEncodings: new string[] { "gzip" }, responseType: "text/custom");
|
var response = await InvokeMiddleware(100, requestAcceptEncodings: new[] { "gzip" }, responseType: "text/custom");
|
||||||
|
|
||||||
CheckResponseNotCompressed(response, expectedBodyLength: 100, sendVaryHeader: false);
|
CheckResponseNotCompressed(response, expectedBodyLength: 100, sendVaryHeader: false);
|
||||||
}
|
}
|
||||||
|
|
@ -235,7 +313,7 @@ namespace Microsoft.AspNetCore.ResponseCompression.Tests
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task Response_WithContentRange_NotCompressed()
|
public async Task Response_WithContentRange_NotCompressed()
|
||||||
{
|
{
|
||||||
var response = await InvokeMiddleware(50, requestAcceptEncodings: new string[] { "gzip" }, responseType: TextPlain, addResponseAction: (r) =>
|
var response = await InvokeMiddleware(50, requestAcceptEncodings: new[] { "gzip" }, responseType: TextPlain, addResponseAction: (r) =>
|
||||||
{
|
{
|
||||||
r.Headers[HeaderNames.ContentRange] = "1-2/*";
|
r.Headers[HeaderNames.ContentRange] = "1-2/*";
|
||||||
});
|
});
|
||||||
|
|
@ -248,7 +326,7 @@ namespace Microsoft.AspNetCore.ResponseCompression.Tests
|
||||||
{
|
{
|
||||||
var otherContentEncoding = "something";
|
var otherContentEncoding = "something";
|
||||||
|
|
||||||
var response = await InvokeMiddleware(50, requestAcceptEncodings: new string[] { "gzip" }, responseType: TextPlain, addResponseAction: (r) =>
|
var response = await InvokeMiddleware(50, requestAcceptEncodings: new[] { "gzip" }, responseType: TextPlain, addResponseAction: (r) =>
|
||||||
{
|
{
|
||||||
r.Headers[HeaderNames.ContentEncoding] = otherContentEncoding;
|
r.Headers[HeaderNames.ContentEncoding] = otherContentEncoding;
|
||||||
});
|
});
|
||||||
|
|
@ -282,8 +360,11 @@ namespace Microsoft.AspNetCore.ResponseCompression.Tests
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
var server = new TestServer(builder);
|
var server = new TestServer(builder)
|
||||||
server.BaseAddress = new Uri("https://localhost/");
|
{
|
||||||
|
BaseAddress = new Uri("https://localhost/")
|
||||||
|
};
|
||||||
|
|
||||||
var client = server.CreateClient();
|
var client = server.CreateClient();
|
||||||
|
|
||||||
var request = new HttpRequestMessage(HttpMethod.Get, "");
|
var request = new HttpRequestMessage(HttpMethod.Get, "");
|
||||||
|
|
@ -294,8 +375,9 @@ namespace Microsoft.AspNetCore.ResponseCompression.Tests
|
||||||
Assert.Equal(expectedLength, response.Content.ReadAsByteArrayAsync().Result.Length);
|
Assert.Equal(expectedLength, response.Content.ReadAsByteArrayAsync().Result.Length);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Theory]
|
||||||
public async Task FlushHeaders_SendsHeaders_Compresses()
|
[MemberData(nameof(SupportedEncodingsWithBodyLength))]
|
||||||
|
public async Task FlushHeaders_SendsHeaders_Compresses(string encoding, int expectedBodyLength)
|
||||||
{
|
{
|
||||||
var responseReceived = new ManualResetEvent(false);
|
var responseReceived = new ManualResetEvent(false);
|
||||||
|
|
||||||
|
|
@ -321,18 +403,19 @@ namespace Microsoft.AspNetCore.ResponseCompression.Tests
|
||||||
var client = server.CreateClient();
|
var client = server.CreateClient();
|
||||||
|
|
||||||
var request = new HttpRequestMessage(HttpMethod.Get, "");
|
var request = new HttpRequestMessage(HttpMethod.Get, "");
|
||||||
request.Headers.AcceptEncoding.ParseAdd("gzip");
|
request.Headers.AcceptEncoding.ParseAdd(encoding);
|
||||||
|
|
||||||
var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
|
var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
|
||||||
responseReceived.Set();
|
responseReceived.Set();
|
||||||
|
|
||||||
await response.Content.LoadIntoBufferAsync();
|
await response.Content.LoadIntoBufferAsync();
|
||||||
|
|
||||||
CheckResponseCompressed(response, expectedBodyLength: 24);
|
CheckResponseCompressed(response, expectedBodyLength, encoding);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Theory]
|
||||||
public async Task FlushAsyncHeaders_SendsHeaders_Compresses()
|
[MemberData(nameof(SupportedEncodingsWithBodyLength))]
|
||||||
|
public async Task FlushAsyncHeaders_SendsHeaders_Compresses(string encoding, int expectedBodyLength)
|
||||||
{
|
{
|
||||||
var responseReceived = new ManualResetEvent(false);
|
var responseReceived = new ManualResetEvent(false);
|
||||||
|
|
||||||
|
|
@ -358,18 +441,19 @@ namespace Microsoft.AspNetCore.ResponseCompression.Tests
|
||||||
var client = server.CreateClient();
|
var client = server.CreateClient();
|
||||||
|
|
||||||
var request = new HttpRequestMessage(HttpMethod.Get, "");
|
var request = new HttpRequestMessage(HttpMethod.Get, "");
|
||||||
request.Headers.AcceptEncoding.ParseAdd("gzip");
|
request.Headers.AcceptEncoding.ParseAdd(encoding);
|
||||||
|
|
||||||
var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
|
var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
|
||||||
responseReceived.Set();
|
responseReceived.Set();
|
||||||
|
|
||||||
await response.Content.LoadIntoBufferAsync();
|
await response.Content.LoadIntoBufferAsync();
|
||||||
|
|
||||||
CheckResponseCompressed(response, expectedBodyLength: 24);
|
CheckResponseCompressed(response, expectedBodyLength, encoding);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Theory]
|
||||||
public async Task FlushBody_CompressesAndFlushes()
|
[MemberData(nameof(SupportedEncodings))]
|
||||||
|
public async Task FlushBody_CompressesAndFlushes(string encoding)
|
||||||
{
|
{
|
||||||
var responseReceived = new ManualResetEvent(false);
|
var responseReceived = new ManualResetEvent(false);
|
||||||
|
|
||||||
|
|
@ -397,13 +481,12 @@ namespace Microsoft.AspNetCore.ResponseCompression.Tests
|
||||||
var client = server.CreateClient();
|
var client = server.CreateClient();
|
||||||
|
|
||||||
var request = new HttpRequestMessage(HttpMethod.Get, "");
|
var request = new HttpRequestMessage(HttpMethod.Get, "");
|
||||||
request.Headers.AcceptEncoding.ParseAdd("gzip");
|
request.Headers.AcceptEncoding.ParseAdd(encoding);
|
||||||
|
|
||||||
var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
|
var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
|
||||||
|
|
||||||
IEnumerable<string> contentMD5 = null;
|
Assert.False(response.Content.Headers.TryGetValues(HeaderNames.ContentMD5, out _));
|
||||||
Assert.False(response.Content.Headers.TryGetValues(HeaderNames.ContentMD5, out contentMD5));
|
Assert.Single(response.Content.Headers.ContentEncoding, encoding);
|
||||||
Assert.Single(response.Content.Headers.ContentEncoding, "gzip");
|
|
||||||
|
|
||||||
var body = await response.Content.ReadAsStreamAsync();
|
var body = await response.Content.ReadAsStreamAsync();
|
||||||
var read = await body.ReadAsync(new byte[100], 0, 100);
|
var read = await body.ReadAsync(new byte[100], 0, 100);
|
||||||
|
|
@ -415,8 +498,9 @@ namespace Microsoft.AspNetCore.ResponseCompression.Tests
|
||||||
Assert.True(read > 0);
|
Assert.True(read > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Theory]
|
||||||
public async Task FlushAsyncBody_CompressesAndFlushes()
|
[MemberData(nameof(SupportedEncodings))]
|
||||||
|
public async Task FlushAsyncBody_CompressesAndFlushes(string encoding)
|
||||||
{
|
{
|
||||||
var responseReceived = new ManualResetEvent(false);
|
var responseReceived = new ManualResetEvent(false);
|
||||||
|
|
||||||
|
|
@ -443,13 +527,12 @@ namespace Microsoft.AspNetCore.ResponseCompression.Tests
|
||||||
var client = server.CreateClient();
|
var client = server.CreateClient();
|
||||||
|
|
||||||
var request = new HttpRequestMessage(HttpMethod.Get, "");
|
var request = new HttpRequestMessage(HttpMethod.Get, "");
|
||||||
request.Headers.AcceptEncoding.ParseAdd("gzip");
|
request.Headers.AcceptEncoding.ParseAdd(encoding);
|
||||||
|
|
||||||
var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
|
var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
|
||||||
|
|
||||||
IEnumerable<string> contentMD5 = null;
|
Assert.False(response.Content.Headers.TryGetValues(HeaderNames.ContentMD5, out _));
|
||||||
Assert.False(response.Content.Headers.TryGetValues(HeaderNames.ContentMD5, out contentMD5));
|
Assert.Single(response.Content.Headers.ContentEncoding, encoding);
|
||||||
Assert.Single(response.Content.Headers.ContentEncoding, "gzip");
|
|
||||||
|
|
||||||
var body = await response.Content.ReadAsStreamAsync();
|
var body = await response.Content.ReadAsStreamAsync();
|
||||||
var read = await body.ReadAsync(new byte[100], 0, 100);
|
var read = await body.ReadAsync(new byte[100], 0, 100);
|
||||||
|
|
@ -461,8 +544,9 @@ namespace Microsoft.AspNetCore.ResponseCompression.Tests
|
||||||
Assert.True(read > 0);
|
Assert.True(read > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Theory]
|
||||||
public async Task TrickleWriteAndFlush_FlushesEachWrite()
|
[MemberData(nameof(SupportedEncodings))]
|
||||||
|
public async Task TrickleWriteAndFlush_FlushesEachWrite(string encoding)
|
||||||
{
|
{
|
||||||
var responseReceived = new[]
|
var responseReceived = new[]
|
||||||
{
|
{
|
||||||
|
|
@ -501,7 +585,7 @@ namespace Microsoft.AspNetCore.ResponseCompression.Tests
|
||||||
var client = server.CreateClient();
|
var client = server.CreateClient();
|
||||||
|
|
||||||
var request = new HttpRequestMessage(HttpMethod.Get, "");
|
var request = new HttpRequestMessage(HttpMethod.Get, "");
|
||||||
request.Headers.AcceptEncoding.ParseAdd("gzip");
|
request.Headers.AcceptEncoding.ParseAdd(encoding);
|
||||||
|
|
||||||
var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
|
var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
|
||||||
|
|
||||||
|
|
@ -509,9 +593,8 @@ namespace Microsoft.AspNetCore.ResponseCompression.Tests
|
||||||
Assert.NotNull(response.Content.Headers.GetValues(HeaderNames.ContentMD5));
|
Assert.NotNull(response.Content.Headers.GetValues(HeaderNames.ContentMD5));
|
||||||
Assert.Empty(response.Content.Headers.ContentEncoding);
|
Assert.Empty(response.Content.Headers.ContentEncoding);
|
||||||
#elif NETCOREAPP2_2 // Flush supported, compression enabled
|
#elif NETCOREAPP2_2 // Flush supported, compression enabled
|
||||||
IEnumerable<string> contentMD5 = null;
|
Assert.False(response.Content.Headers.TryGetValues(HeaderNames.ContentMD5, out _));
|
||||||
Assert.False(response.Content.Headers.TryGetValues(HeaderNames.ContentMD5, out contentMD5));
|
Assert.Single(response.Content.Headers.ContentEncoding, encoding);
|
||||||
Assert.Single(response.Content.Headers.ContentEncoding, "gzip");
|
|
||||||
#else
|
#else
|
||||||
#error Target frameworks need to be updated.
|
#error Target frameworks need to be updated.
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -527,8 +610,9 @@ namespace Microsoft.AspNetCore.ResponseCompression.Tests
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Theory]
|
||||||
public async Task TrickleWriteAndFlushAsync_FlushesEachWrite()
|
[MemberData(nameof(SupportedEncodings))]
|
||||||
|
public async Task TrickleWriteAndFlushAsync_FlushesEachWrite(string encoding)
|
||||||
{
|
{
|
||||||
var responseReceived = new[]
|
var responseReceived = new[]
|
||||||
{
|
{
|
||||||
|
|
@ -566,7 +650,7 @@ namespace Microsoft.AspNetCore.ResponseCompression.Tests
|
||||||
var client = server.CreateClient();
|
var client = server.CreateClient();
|
||||||
|
|
||||||
var request = new HttpRequestMessage(HttpMethod.Get, "");
|
var request = new HttpRequestMessage(HttpMethod.Get, "");
|
||||||
request.Headers.AcceptEncoding.ParseAdd("gzip");
|
request.Headers.AcceptEncoding.ParseAdd(encoding);
|
||||||
|
|
||||||
var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
|
var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
|
||||||
|
|
||||||
|
|
@ -574,9 +658,8 @@ namespace Microsoft.AspNetCore.ResponseCompression.Tests
|
||||||
Assert.NotNull(response.Content.Headers.GetValues(HeaderNames.ContentMD5));
|
Assert.NotNull(response.Content.Headers.GetValues(HeaderNames.ContentMD5));
|
||||||
Assert.Empty(response.Content.Headers.ContentEncoding);
|
Assert.Empty(response.Content.Headers.ContentEncoding);
|
||||||
#elif NETCOREAPP2_2 // Flush supported, compression enabled
|
#elif NETCOREAPP2_2 // Flush supported, compression enabled
|
||||||
IEnumerable<string> contentMD5 = null;
|
Assert.False(response.Content.Headers.TryGetValues(HeaderNames.ContentMD5, out _));
|
||||||
Assert.False(response.Content.Headers.TryGetValues(HeaderNames.ContentMD5, out contentMD5));
|
Assert.Single(response.Content.Headers.ContentEncoding, encoding);
|
||||||
Assert.Single(response.Content.Headers.ContentEncoding, "gzip");
|
|
||||||
#else
|
#else
|
||||||
#error Target framework needs to be updated
|
#error Target framework needs to be updated
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -705,7 +788,7 @@ namespace Microsoft.AspNetCore.ResponseCompression.Tests
|
||||||
|
|
||||||
var response = await client.SendAsync(request);
|
var response = await client.SendAsync(request);
|
||||||
|
|
||||||
CheckResponseCompressed(response, expectedBodyLength: 34);
|
CheckResponseCompressed(response, expectedBodyLength: 34, expectedEncoding: "gzip");
|
||||||
|
|
||||||
Assert.False(fakeSendFile.Invoked);
|
Assert.False(fakeSendFile.Invoked);
|
||||||
}
|
}
|
||||||
|
|
@ -749,17 +832,22 @@ namespace Microsoft.AspNetCore.ResponseCompression.Tests
|
||||||
|
|
||||||
var response = await client.SendAsync(request);
|
var response = await client.SendAsync(request);
|
||||||
|
|
||||||
CheckResponseCompressed(response, expectedBodyLength: 40);
|
CheckResponseCompressed(response, expectedBodyLength: 40, expectedEncoding: "gzip");
|
||||||
|
|
||||||
Assert.False(fakeSendFile.Invoked);
|
Assert.False(fakeSendFile.Invoked);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task<HttpResponseMessage> InvokeMiddleware(int uncompressedBodyLength, string[] requestAcceptEncodings, string responseType, Action<HttpResponse> addResponseAction = null)
|
private Task<HttpResponseMessage> InvokeMiddleware(
|
||||||
|
int uncompressedBodyLength,
|
||||||
|
string[] requestAcceptEncodings,
|
||||||
|
string responseType,
|
||||||
|
Action<HttpResponse> addResponseAction = null,
|
||||||
|
Action<ResponseCompressionOptions> configure = null)
|
||||||
{
|
{
|
||||||
var builder = new WebHostBuilder()
|
var builder = new WebHostBuilder()
|
||||||
.ConfigureServices(services =>
|
.ConfigureServices(services =>
|
||||||
{
|
{
|
||||||
services.AddResponseCompression();
|
services.AddResponseCompression(configure ?? (_ => { }));
|
||||||
})
|
})
|
||||||
.Configure(app =>
|
.Configure(app =>
|
||||||
{
|
{
|
||||||
|
|
@ -769,10 +857,7 @@ namespace Microsoft.AspNetCore.ResponseCompression.Tests
|
||||||
context.Response.Headers[HeaderNames.ContentMD5] = "MD5";
|
context.Response.Headers[HeaderNames.ContentMD5] = "MD5";
|
||||||
context.Response.ContentType = responseType;
|
context.Response.ContentType = responseType;
|
||||||
Assert.Null(context.Features.Get<IHttpSendFileFeature>());
|
Assert.Null(context.Features.Get<IHttpSendFileFeature>());
|
||||||
if (addResponseAction != null)
|
addResponseAction?.Invoke(context.Response);
|
||||||
{
|
|
||||||
addResponseAction(context.Response);
|
|
||||||
}
|
|
||||||
return context.Response.WriteAsync(new string('a', uncompressedBodyLength));
|
return context.Response.WriteAsync(new string('a', uncompressedBodyLength));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
@ -789,10 +874,8 @@ namespace Microsoft.AspNetCore.ResponseCompression.Tests
|
||||||
return client.SendAsync(request);
|
return client.SendAsync(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CheckResponseCompressed(HttpResponseMessage response, int expectedBodyLength)
|
private void CheckResponseCompressed(HttpResponseMessage response, int expectedBodyLength, string expectedEncoding)
|
||||||
{
|
{
|
||||||
IEnumerable<string> contentMD5 = null;
|
|
||||||
|
|
||||||
var containsVaryAcceptEncoding = false;
|
var containsVaryAcceptEncoding = false;
|
||||||
foreach (var value in response.Headers.GetValues(HeaderNames.Vary))
|
foreach (var value in response.Headers.GetValues(HeaderNames.Vary))
|
||||||
{
|
{
|
||||||
|
|
@ -803,8 +886,8 @@ namespace Microsoft.AspNetCore.ResponseCompression.Tests
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Assert.True(containsVaryAcceptEncoding);
|
Assert.True(containsVaryAcceptEncoding);
|
||||||
Assert.False(response.Content.Headers.TryGetValues(HeaderNames.ContentMD5, out contentMD5));
|
Assert.False(response.Content.Headers.TryGetValues(HeaderNames.ContentMD5, out _));
|
||||||
Assert.Single(response.Content.Headers.ContentEncoding, "gzip");
|
Assert.Single(response.Content.Headers.ContentEncoding, expectedEncoding);
|
||||||
Assert.Equal(expectedBodyLength, response.Content.Headers.ContentLength);
|
Assert.Equal(expectedBodyLength, response.Content.Headers.ContentLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -858,5 +941,18 @@ namespace Microsoft.AspNetCore.ResponseCompression.Tests
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private readonly struct EncodingTestData
|
||||||
|
{
|
||||||
|
public EncodingTestData(string encodingName, int expectedBodyLength)
|
||||||
|
{
|
||||||
|
EncodingName = encodingName;
|
||||||
|
ExpectedBodyLength = expectedBodyLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string EncodingName { get; }
|
||||||
|
|
||||||
|
public int ExpectedBodyLength { get; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue