parent
7fb3d57f54
commit
6db129a588
|
|
@ -131,6 +131,12 @@ namespace Microsoft.AspNetCore.Http.Features
|
||||||
public T Fetch(Microsoft.AspNetCore.Http.Features.IFeatureCollection features) { throw null; }
|
public T Fetch(Microsoft.AspNetCore.Http.Features.IFeatureCollection features) { throw null; }
|
||||||
public T Update(Microsoft.AspNetCore.Http.Features.IFeatureCollection features, T feature) { throw null; }
|
public T Update(Microsoft.AspNetCore.Http.Features.IFeatureCollection features, T feature) { throw null; }
|
||||||
}
|
}
|
||||||
|
public enum HttpsCompressionMode
|
||||||
|
{
|
||||||
|
Compress = 2,
|
||||||
|
Default = 0,
|
||||||
|
DoNotCompress = 1,
|
||||||
|
}
|
||||||
public partial interface IFeatureCollection : System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<System.Type, object>>, System.Collections.IEnumerable
|
public partial interface IFeatureCollection : System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<System.Type, object>>, System.Collections.IEnumerable
|
||||||
{
|
{
|
||||||
bool IsReadOnly { get; }
|
bool IsReadOnly { get; }
|
||||||
|
|
@ -207,6 +213,10 @@ namespace Microsoft.AspNetCore.Http.Features
|
||||||
{
|
{
|
||||||
Microsoft.AspNetCore.Http.IHeaderDictionary Trailers { get; set; }
|
Microsoft.AspNetCore.Http.IHeaderDictionary Trailers { get; set; }
|
||||||
}
|
}
|
||||||
|
public partial interface IHttpsCompressionFeature
|
||||||
|
{
|
||||||
|
Microsoft.AspNetCore.Http.Features.HttpsCompressionMode Mode { get; set; }
|
||||||
|
}
|
||||||
public partial interface IHttpSendFileFeature
|
public partial interface IHttpSendFileFeature
|
||||||
{
|
{
|
||||||
System.Threading.Tasks.Task SendFileAsync(string path, long offset, long? count, System.Threading.CancellationToken cancellation);
|
System.Threading.Tasks.Task SendFileAsync(string path, long offset, long? count, System.Threading.CancellationToken cancellation);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Http.Features
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Use to dynamically control response compression for HTTPS requests.
|
||||||
|
/// </summary>
|
||||||
|
public enum HttpsCompressionMode
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// No value has been specified, use the configured defaults.
|
||||||
|
/// </summary>
|
||||||
|
Default = 0,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Opts out of compression over HTTPS. Enabling compression on HTTPS requests for remotely manipulable content
|
||||||
|
/// may expose security problems.
|
||||||
|
/// </summary>
|
||||||
|
DoNotCompress,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Opts into compression over HTTPS. Enabling compression on HTTPS requests for remotely manipulable content
|
||||||
|
/// may expose security problems.
|
||||||
|
/// </summary>
|
||||||
|
Compress,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Http.Features
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Configures response compression behavior for HTTPS on a per-request basis.
|
||||||
|
/// </summary>
|
||||||
|
public interface IHttpsCompressionFeature
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The <see cref="HttpsCompressionMode"/> to use.
|
||||||
|
/// </summary>
|
||||||
|
HttpsCompressionMode Mode { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -15,7 +15,7 @@ namespace Microsoft.AspNetCore.ResponseCompression
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Stream wrapper that create specific compression stream only if necessary.
|
/// Stream wrapper that create specific compression stream only if necessary.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal class BodyWrapperStream : Stream, IHttpBufferingFeature, IHttpSendFileFeature, IHttpResponseStartFeature
|
internal class BodyWrapperStream : Stream, IHttpBufferingFeature, IHttpSendFileFeature, IHttpResponseStartFeature, IHttpsCompressionFeature
|
||||||
{
|
{
|
||||||
private readonly HttpContext _context;
|
private readonly HttpContext _context;
|
||||||
private readonly Stream _bodyOriginalStream;
|
private readonly Stream _bodyOriginalStream;
|
||||||
|
|
@ -46,6 +46,8 @@ namespace Microsoft.AspNetCore.ResponseCompression
|
||||||
return _compressionStream?.DisposeAsync() ?? new ValueTask();
|
return _compressionStream?.DisposeAsync() ?? new ValueTask();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HttpsCompressionMode IHttpsCompressionFeature.Mode { get; set; } = HttpsCompressionMode.Default;
|
||||||
|
|
||||||
public override bool CanRead => false;
|
public override bool CanRead => false;
|
||||||
|
|
||||||
public override bool CanSeek => false;
|
public override bool CanSeek => false;
|
||||||
|
|
|
||||||
|
|
@ -55,11 +55,13 @@ namespace Microsoft.AspNetCore.ResponseCompression
|
||||||
var originalBufferFeature = context.Features.Get<IHttpBufferingFeature>();
|
var originalBufferFeature = context.Features.Get<IHttpBufferingFeature>();
|
||||||
var originalSendFileFeature = context.Features.Get<IHttpSendFileFeature>();
|
var originalSendFileFeature = context.Features.Get<IHttpSendFileFeature>();
|
||||||
var originalStartFeature = context.Features.Get<IHttpResponseStartFeature>();
|
var originalStartFeature = context.Features.Get<IHttpResponseStartFeature>();
|
||||||
|
var originalCompressionFeature = context.Features.Get<IHttpsCompressionFeature>();
|
||||||
|
|
||||||
var bodyWrapperStream = new BodyWrapperStream(context, bodyStream, _provider,
|
var bodyWrapperStream = new BodyWrapperStream(context, bodyStream, _provider,
|
||||||
originalBufferFeature, originalSendFileFeature, originalStartFeature);
|
originalBufferFeature, originalSendFileFeature, originalStartFeature);
|
||||||
context.Response.Body = bodyWrapperStream;
|
context.Response.Body = bodyWrapperStream;
|
||||||
context.Features.Set<IHttpBufferingFeature>(bodyWrapperStream);
|
context.Features.Set<IHttpBufferingFeature>(bodyWrapperStream);
|
||||||
|
context.Features.Set<IHttpsCompressionFeature>(bodyWrapperStream);
|
||||||
if (originalSendFileFeature != null)
|
if (originalSendFileFeature != null)
|
||||||
{
|
{
|
||||||
context.Features.Set<IHttpSendFileFeature>(bodyWrapperStream);
|
context.Features.Set<IHttpSendFileFeature>(bodyWrapperStream);
|
||||||
|
|
@ -79,6 +81,7 @@ namespace Microsoft.AspNetCore.ResponseCompression
|
||||||
{
|
{
|
||||||
context.Response.Body = bodyStream;
|
context.Response.Body = bodyStream;
|
||||||
context.Features.Set(originalBufferFeature);
|
context.Features.Set(originalBufferFeature);
|
||||||
|
context.Features.Set(originalCompressionFeature);
|
||||||
if (originalSendFileFeature != null)
|
if (originalSendFileFeature != null)
|
||||||
{
|
{
|
||||||
context.Features.Set(originalSendFileFeature);
|
context.Features.Set(originalSendFileFeature);
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
// 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.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.AspNetCore.Http.Features;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.ResponseCompression
|
namespace Microsoft.AspNetCore.ResponseCompression
|
||||||
{
|
{
|
||||||
|
|
@ -22,8 +23,11 @@ namespace Microsoft.AspNetCore.ResponseCompression
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Indicates if responses over HTTPS connections should be compressed. The default is 'false'.
|
/// Indicates if responses over HTTPS connections should be compressed. The default is 'false'.
|
||||||
/// Enabling compression on HTTPS connections may expose security problems.
|
/// Enabling compression on HTTPS requests for remotely manipulable content may expose security problems.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This can be overridden per request using <see cref="IHttpsCompressionFeature"/>.
|
||||||
|
/// </remarks>
|
||||||
public bool EnableForHttps { get; set; } = false;
|
public bool EnableForHttps { get; set; } = false;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Http.Features;
|
||||||
using Microsoft.AspNetCore.ResponseCompression.Internal;
|
using Microsoft.AspNetCore.ResponseCompression.Internal;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
@ -170,6 +171,17 @@ namespace Microsoft.AspNetCore.ResponseCompression
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public virtual bool ShouldCompressResponse(HttpContext context)
|
public virtual bool ShouldCompressResponse(HttpContext context)
|
||||||
{
|
{
|
||||||
|
var httpsMode = context.Features.Get<IHttpsCompressionFeature>()?.Mode ?? HttpsCompressionMode.Default;
|
||||||
|
|
||||||
|
// Check if the app has opted into or out of compression over HTTPS
|
||||||
|
if (context.Request.IsHttps
|
||||||
|
&& (httpsMode == HttpsCompressionMode.DoNotCompress
|
||||||
|
|| !(_enableForHttps || httpsMode == HttpsCompressionMode.Compress)))
|
||||||
|
{
|
||||||
|
_logger.NoCompressionForHttps();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (context.Response.Headers.ContainsKey(HeaderNames.ContentRange))
|
if (context.Response.Headers.ContainsKey(HeaderNames.ContentRange))
|
||||||
{
|
{
|
||||||
_logger.NoCompressionDueToHeader(HeaderNames.ContentRange);
|
_logger.NoCompressionDueToHeader(HeaderNames.ContentRange);
|
||||||
|
|
@ -215,12 +227,6 @@ namespace Microsoft.AspNetCore.ResponseCompression
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public bool CheckRequestAcceptsCompression(HttpContext context)
|
public bool CheckRequestAcceptsCompression(HttpContext context)
|
||||||
{
|
{
|
||||||
if (context.Request.IsHttps && !_enableForHttps)
|
|
||||||
{
|
|
||||||
_logger.NoCompressionForHttps();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(context.Request.Headers[HeaderNames.AcceptEncoding]))
|
if (string.IsNullOrEmpty(context.Request.Headers[HeaderNames.AcceptEncoding]))
|
||||||
{
|
{
|
||||||
_logger.NoAcceptEncoding();
|
_logger.NoAcceptEncoding();
|
||||||
|
|
|
||||||
|
|
@ -453,7 +453,123 @@ namespace Microsoft.AspNetCore.ResponseCompression.Tests
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
AssertLog(logMessages.Single(), LogLevel.Debug, "No response compression available for HTTPS requests. See ResponseCompressionOptions.EnableForHttps.");
|
AssertLog(logMessages.Skip(1).Single(), LogLevel.Debug, "No response compression available for HTTPS requests. See ResponseCompressionOptions.EnableForHttps.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(HttpsCompressionMode.Default, 100)]
|
||||||
|
[InlineData(HttpsCompressionMode.DoNotCompress, 100)]
|
||||||
|
[InlineData(HttpsCompressionMode.Compress, 30)]
|
||||||
|
public async Task Request_Https_CompressedIfOptIn(HttpsCompressionMode mode, int expectedLength)
|
||||||
|
{
|
||||||
|
var sink = new TestSink(
|
||||||
|
TestSink.EnableWithTypeName<ResponseCompressionProvider>,
|
||||||
|
TestSink.EnableWithTypeName<ResponseCompressionProvider>);
|
||||||
|
var loggerFactory = new TestLoggerFactory(sink, enabled: true);
|
||||||
|
|
||||||
|
var builder = new WebHostBuilder()
|
||||||
|
.ConfigureServices(services =>
|
||||||
|
{
|
||||||
|
services.AddSingleton<ILoggerFactory>(loggerFactory);
|
||||||
|
services.AddResponseCompression(options =>
|
||||||
|
{
|
||||||
|
options.EnableForHttps = false;
|
||||||
|
options.MimeTypes = new[] { TextPlain };
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.Configure(app =>
|
||||||
|
{
|
||||||
|
app.UseResponseCompression();
|
||||||
|
app.Run(context =>
|
||||||
|
{
|
||||||
|
var feature = context.Features.Get<IHttpsCompressionFeature>();
|
||||||
|
feature.Mode = mode;
|
||||||
|
context.Response.ContentType = TextPlain;
|
||||||
|
return context.Response.WriteAsync(new string('a', 100));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
var server = new TestServer(builder)
|
||||||
|
{
|
||||||
|
BaseAddress = new Uri("https://localhost/")
|
||||||
|
};
|
||||||
|
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
var request = new HttpRequestMessage(HttpMethod.Get, "");
|
||||||
|
request.Headers.AcceptEncoding.ParseAdd("gzip");
|
||||||
|
|
||||||
|
var response = await client.SendAsync(request);
|
||||||
|
|
||||||
|
Assert.Equal(expectedLength, response.Content.ReadAsByteArrayAsync().Result.Length);
|
||||||
|
|
||||||
|
var logMessages = sink.Writes.ToList();
|
||||||
|
if (mode == HttpsCompressionMode.Compress)
|
||||||
|
{
|
||||||
|
AssertCompressedWithLog(logMessages, "gzip");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AssertLog(logMessages.Skip(1).Single(), LogLevel.Debug, "No response compression available for HTTPS requests. See ResponseCompressionOptions.EnableForHttps.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(HttpsCompressionMode.Default, 30)]
|
||||||
|
[InlineData(HttpsCompressionMode.Compress, 30)]
|
||||||
|
[InlineData(HttpsCompressionMode.DoNotCompress, 100)]
|
||||||
|
public async Task Request_Https_NotCompressedIfOptOut(HttpsCompressionMode mode, int expectedLength)
|
||||||
|
{
|
||||||
|
var sink = new TestSink(
|
||||||
|
TestSink.EnableWithTypeName<ResponseCompressionProvider>,
|
||||||
|
TestSink.EnableWithTypeName<ResponseCompressionProvider>);
|
||||||
|
var loggerFactory = new TestLoggerFactory(sink, enabled: true);
|
||||||
|
|
||||||
|
var builder = new WebHostBuilder()
|
||||||
|
.ConfigureServices(services =>
|
||||||
|
{
|
||||||
|
services.AddSingleton<ILoggerFactory>(loggerFactory);
|
||||||
|
services.AddResponseCompression(options =>
|
||||||
|
{
|
||||||
|
options.EnableForHttps = true;
|
||||||
|
options.MimeTypes = new[] { TextPlain };
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.Configure(app =>
|
||||||
|
{
|
||||||
|
app.UseResponseCompression();
|
||||||
|
app.Run(context =>
|
||||||
|
{
|
||||||
|
var feature = context.Features.Get<IHttpsCompressionFeature>();
|
||||||
|
feature.Mode = mode;
|
||||||
|
context.Response.ContentType = TextPlain;
|
||||||
|
return context.Response.WriteAsync(new string('a', 100));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
var server = new TestServer(builder)
|
||||||
|
{
|
||||||
|
BaseAddress = new Uri("https://localhost/")
|
||||||
|
};
|
||||||
|
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
var request = new HttpRequestMessage(HttpMethod.Get, "");
|
||||||
|
request.Headers.AcceptEncoding.ParseAdd("gzip");
|
||||||
|
|
||||||
|
var response = await client.SendAsync(request);
|
||||||
|
|
||||||
|
Assert.Equal(expectedLength, response.Content.ReadAsByteArrayAsync().Result.Length);
|
||||||
|
|
||||||
|
var logMessages = sink.Writes.ToList();
|
||||||
|
if (mode == HttpsCompressionMode.DoNotCompress)
|
||||||
|
{
|
||||||
|
AssertLog(logMessages.Skip(1).Single(), LogLevel.Debug, "No response compression available for HTTPS requests. See ResponseCompressionOptions.EnableForHttps.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AssertCompressedWithLog(logMessages, "gzip");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,7 @@ namespace Microsoft.AspNetCore.Builder
|
||||||
public StaticFileOptions(Microsoft.AspNetCore.StaticFiles.Infrastructure.SharedOptions sharedOptions) : base (default(Microsoft.AspNetCore.StaticFiles.Infrastructure.SharedOptions)) { }
|
public StaticFileOptions(Microsoft.AspNetCore.StaticFiles.Infrastructure.SharedOptions sharedOptions) : base (default(Microsoft.AspNetCore.StaticFiles.Infrastructure.SharedOptions)) { }
|
||||||
public Microsoft.AspNetCore.StaticFiles.IContentTypeProvider ContentTypeProvider { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
|
public Microsoft.AspNetCore.StaticFiles.IContentTypeProvider ContentTypeProvider { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
|
||||||
public string DefaultContentType { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
|
public string DefaultContentType { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
|
||||||
|
public Microsoft.AspNetCore.Http.Features.HttpsCompressionMode HttpsCompression { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
|
||||||
public System.Action<Microsoft.AspNetCore.StaticFiles.StaticFileResponseContext> OnPrepareResponse { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
|
public System.Action<Microsoft.AspNetCore.StaticFiles.StaticFileResponseContext> OnPrepareResponse { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
|
||||||
public bool ServeUnknownFileTypes { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
|
public bool ServeUnknownFileTypes { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,12 +12,15 @@ namespace StaticFilesSample
|
||||||
public void ConfigureServices(IServiceCollection services)
|
public void ConfigureServices(IServiceCollection services)
|
||||||
{
|
{
|
||||||
services.AddDirectoryBrowser();
|
services.AddDirectoryBrowser();
|
||||||
|
services.AddResponseCompression();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Configure(IApplicationBuilder app, IWebHostEnvironment host)
|
public void Configure(IApplicationBuilder app, IWebHostEnvironment host)
|
||||||
{
|
{
|
||||||
Console.WriteLine("webroot: " + host.WebRootPath);
|
Console.WriteLine("webroot: " + host.WebRootPath);
|
||||||
|
|
||||||
|
app.UseResponseCompression();
|
||||||
|
|
||||||
app.UseFileServer(new FileServerOptions
|
app.UseFileServer(new FileServerOptions
|
||||||
{
|
{
|
||||||
EnableDirectoryBrowsing = true
|
EnableDirectoryBrowsing = true
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netcoreapp3.0</TargetFramework>
|
<TargetFramework>netcoreapp3.0</TargetFramework>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Reference Include="Microsoft.AspNetCore.ResponseCompression" />
|
||||||
<Reference Include="Microsoft.AspNetCore.StaticFiles" />
|
<Reference Include="Microsoft.AspNetCore.StaticFiles" />
|
||||||
<Reference Include="Microsoft.AspNetCore.Server.IISIntegration" />
|
<Reference Include="Microsoft.AspNetCore.Server.IISIntegration" />
|
||||||
<Reference Include="Microsoft.AspNetCore.Server.Kestrel" />
|
<Reference Include="Microsoft.AspNetCore.Server.Kestrel" />
|
||||||
|
|
|
||||||
|
|
@ -322,6 +322,7 @@ namespace Microsoft.AspNetCore.StaticFiles
|
||||||
|
|
||||||
public async Task SendAsync()
|
public async Task SendAsync()
|
||||||
{
|
{
|
||||||
|
SetCompressionMode();
|
||||||
ApplyResponseHeaders(Constants.Status200Ok);
|
ApplyResponseHeaders(Constants.Status200Ok);
|
||||||
string physicalPath = _fileInfo.PhysicalPath;
|
string physicalPath = _fileInfo.PhysicalPath;
|
||||||
var sendFile = _context.Features.Get<IHttpSendFileFeature>();
|
var sendFile = _context.Features.Get<IHttpSendFileFeature>();
|
||||||
|
|
@ -366,6 +367,7 @@ namespace Microsoft.AspNetCore.StaticFiles
|
||||||
|
|
||||||
_responseHeaders.ContentRange = ComputeContentRange(_range, out var start, out var length);
|
_responseHeaders.ContentRange = ComputeContentRange(_range, out var start, out var length);
|
||||||
_response.ContentLength = length;
|
_response.ContentLength = length;
|
||||||
|
SetCompressionMode();
|
||||||
ApplyResponseHeaders(Constants.Status206PartialContent);
|
ApplyResponseHeaders(Constants.Status206PartialContent);
|
||||||
|
|
||||||
string physicalPath = _fileInfo.PhysicalPath;
|
string physicalPath = _fileInfo.PhysicalPath;
|
||||||
|
|
@ -404,5 +406,15 @@ namespace Microsoft.AspNetCore.StaticFiles
|
||||||
length = end - start + 1;
|
length = end - start + 1;
|
||||||
return new ContentRangeHeaderValue(start, end, _length);
|
return new ContentRangeHeaderValue(start, end, _length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Only called when we expect to serve the body.
|
||||||
|
private void SetCompressionMode()
|
||||||
|
{
|
||||||
|
var responseCompressionFeature = _context.Features.Get<IHttpsCompressionFeature>();
|
||||||
|
if (responseCompressionFeature != null)
|
||||||
|
{
|
||||||
|
responseCompressionFeature.Mode = _options.HttpsCompression;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +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 Microsoft.AspNetCore.Http.Features;
|
||||||
using Microsoft.AspNetCore.StaticFiles;
|
using Microsoft.AspNetCore.StaticFiles;
|
||||||
using Microsoft.AspNetCore.StaticFiles.Infrastructure;
|
using Microsoft.AspNetCore.StaticFiles.Infrastructure;
|
||||||
|
|
||||||
|
|
@ -46,6 +47,15 @@ namespace Microsoft.AspNetCore.Builder
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool ServeUnknownFileTypes { get; set; }
|
public bool ServeUnknownFileTypes { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates if files should be compressed for HTTPS requests when the Response Compression middleware is available.
|
||||||
|
/// The default value is <see cref="HttpsCompressionMode.Compress"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Enabling compression on HTTPS requests for remotely manipulable content may expose security problems.
|
||||||
|
/// </remarks>
|
||||||
|
public HttpsCompressionMode HttpsCompression { get; set; } = HttpsCompressionMode.Compress;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Called after the status code and headers have been set, but before the body has been written.
|
/// Called after the status code and headers have been set, but before the body has been written.
|
||||||
/// This can be used to add or change the response headers.
|
/// This can be used to add or change the response headers.
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,10 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Http.Features;
|
||||||
using Microsoft.Extensions.FileProviders;
|
using Microsoft.Extensions.FileProviders;
|
||||||
using Microsoft.Extensions.Logging.Abstractions;
|
using Microsoft.Extensions.Logging.Abstractions;
|
||||||
using Microsoft.Extensions.Primitives;
|
using Microsoft.Extensions.Primitives;
|
||||||
|
|
@ -54,6 +56,54 @@ namespace Microsoft.AspNetCore.StaticFiles
|
||||||
Assert.True(result);
|
Assert.True(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task EnablesHttpsCompression_IfMatched()
|
||||||
|
{
|
||||||
|
var options = new StaticFileOptions();
|
||||||
|
var fileProvider = new TestFileProvider();
|
||||||
|
fileProvider.AddFile("/foo.txt", new TestFileInfo
|
||||||
|
{
|
||||||
|
LastModified = new DateTimeOffset(2014, 1, 2, 3, 4, 5, TimeSpan.Zero)
|
||||||
|
});
|
||||||
|
var pathString = new PathString("/test");
|
||||||
|
var httpContext = new DefaultHttpContext();
|
||||||
|
var httpsCompressionFeature = new TestHttpsCompressionFeature();
|
||||||
|
httpContext.Features.Set<IHttpsCompressionFeature>(httpsCompressionFeature);
|
||||||
|
httpContext.Request.Path = new PathString("/test/foo.txt");
|
||||||
|
var context = new StaticFileContext(httpContext, options, pathString, NullLogger.Instance, fileProvider, new FileExtensionContentTypeProvider());
|
||||||
|
|
||||||
|
context.ValidatePath();
|
||||||
|
var result = context.LookupFileInfo();
|
||||||
|
Assert.True(result);
|
||||||
|
|
||||||
|
await context.SendAsync();
|
||||||
|
|
||||||
|
Assert.Equal(HttpsCompressionMode.Compress, httpsCompressionFeature.Mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void SkipsHttpsCompression_IfNotMatched()
|
||||||
|
{
|
||||||
|
var options = new StaticFileOptions();
|
||||||
|
var fileProvider = new TestFileProvider();
|
||||||
|
fileProvider.AddFile("/foo.txt", new TestFileInfo
|
||||||
|
{
|
||||||
|
LastModified = new DateTimeOffset(2014, 1, 2, 3, 4, 5, TimeSpan.Zero)
|
||||||
|
});
|
||||||
|
var pathString = new PathString("/test");
|
||||||
|
var httpContext = new DefaultHttpContext();
|
||||||
|
var httpsCompressionFeature = new TestHttpsCompressionFeature();
|
||||||
|
httpContext.Features.Set<IHttpsCompressionFeature>(httpsCompressionFeature);
|
||||||
|
httpContext.Request.Path = new PathString("/test/bar.txt");
|
||||||
|
var context = new StaticFileContext(httpContext, options, pathString, NullLogger.Instance, fileProvider, new FileExtensionContentTypeProvider());
|
||||||
|
|
||||||
|
context.ValidatePath();
|
||||||
|
var result = context.LookupFileInfo();
|
||||||
|
Assert.False(result);
|
||||||
|
|
||||||
|
Assert.Equal(HttpsCompressionMode.Default, httpsCompressionFeature.Mode);
|
||||||
|
}
|
||||||
|
|
||||||
private sealed class TestFileProvider : IFileProvider
|
private sealed class TestFileProvider : IFileProvider
|
||||||
{
|
{
|
||||||
private readonly Dictionary<string, IFileInfo> _files = new Dictionary<string, IFileInfo>(StringComparer.Ordinal);
|
private readonly Dictionary<string, IFileInfo> _files = new Dictionary<string, IFileInfo>(StringComparer.Ordinal);
|
||||||
|
|
@ -162,8 +212,13 @@ namespace Microsoft.AspNetCore.StaticFiles
|
||||||
|
|
||||||
public Stream CreateReadStream()
|
public Stream CreateReadStream()
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
return new MemoryStream();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class TestHttpsCompressionFeature : IHttpsCompressionFeature
|
||||||
|
{
|
||||||
|
public HttpsCompressionMode Mode { get; set; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue