Reduce StaticFileContext allocation/struct copy (#9815)
This commit is contained in:
parent
3c40df452d
commit
617bc1ac02
|
|
@ -2,13 +2,13 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.Endpoints;
|
||||
using Microsoft.AspNetCore.Http.Extensions;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using Microsoft.AspNetCore.Http.Headers;
|
||||
|
|
@ -22,129 +22,99 @@ namespace Microsoft.AspNetCore.StaticFiles
|
|||
internal struct StaticFileContext
|
||||
{
|
||||
private const int StreamCopyBufferSize = 64 * 1024;
|
||||
|
||||
private readonly HttpContext _context;
|
||||
private readonly StaticFileOptions _options;
|
||||
private readonly PathString _matchUrl;
|
||||
private readonly HttpRequest _request;
|
||||
private readonly HttpResponse _response;
|
||||
private readonly ILogger _logger;
|
||||
private readonly IFileProvider _fileProvider;
|
||||
private readonly IContentTypeProvider _contentTypeProvider;
|
||||
private string _method;
|
||||
private bool _isGet;
|
||||
private bool _isHead;
|
||||
private PathString _subPath;
|
||||
private string _contentType;
|
||||
private IFileInfo _fileInfo;
|
||||
private long _length;
|
||||
private DateTimeOffset _lastModified;
|
||||
private EntityTagHeaderValue _etag;
|
||||
private readonly string _method;
|
||||
private readonly string _contentType;
|
||||
|
||||
private IFileInfo _fileInfo;
|
||||
private EntityTagHeaderValue _etag;
|
||||
private RequestHeaders _requestHeaders;
|
||||
private ResponseHeaders _responseHeaders;
|
||||
private RangeItemHeaderValue _range;
|
||||
|
||||
private long _length;
|
||||
private readonly PathString _subPath;
|
||||
private DateTimeOffset _lastModified;
|
||||
|
||||
private PreconditionState _ifMatchState;
|
||||
private PreconditionState _ifNoneMatchState;
|
||||
private PreconditionState _ifModifiedSinceState;
|
||||
private PreconditionState _ifUnmodifiedSinceState;
|
||||
|
||||
private RangeItemHeaderValue _range;
|
||||
private bool _isRangeRequest;
|
||||
private RequestType _requestType;
|
||||
|
||||
public StaticFileContext(HttpContext context, StaticFileOptions options, PathString matchUrl, ILogger logger, IFileProvider fileProvider, IContentTypeProvider contentTypeProvider)
|
||||
public StaticFileContext(HttpContext context, StaticFileOptions options, ILogger logger, IFileProvider fileProvider, string contentType, PathString subPath)
|
||||
{
|
||||
_context = context;
|
||||
_options = options;
|
||||
_matchUrl = matchUrl;
|
||||
_request = context.Request;
|
||||
_response = context.Response;
|
||||
_logger = logger;
|
||||
_requestHeaders = _request.GetTypedHeaders();
|
||||
_responseHeaders = _response.GetTypedHeaders();
|
||||
_fileProvider = fileProvider;
|
||||
_contentTypeProvider = contentTypeProvider;
|
||||
|
||||
_method = null;
|
||||
_isGet = false;
|
||||
_isHead = false;
|
||||
_subPath = PathString.Empty;
|
||||
_contentType = null;
|
||||
_method = _request.Method;
|
||||
_contentType = contentType;
|
||||
_fileInfo = null;
|
||||
_length = 0;
|
||||
_lastModified = new DateTimeOffset();
|
||||
_etag = null;
|
||||
_requestHeaders = null;
|
||||
_responseHeaders = null;
|
||||
_range = null;
|
||||
|
||||
_length = 0;
|
||||
_subPath = subPath;
|
||||
_lastModified = new DateTimeOffset();
|
||||
_ifMatchState = PreconditionState.Unspecified;
|
||||
_ifNoneMatchState = PreconditionState.Unspecified;
|
||||
_ifModifiedSinceState = PreconditionState.Unspecified;
|
||||
_ifUnmodifiedSinceState = PreconditionState.Unspecified;
|
||||
_range = null;
|
||||
_isRangeRequest = false;
|
||||
|
||||
if (HttpMethods.IsGet(_method))
|
||||
{
|
||||
_requestType = RequestType.IsGet;
|
||||
}
|
||||
else if (HttpMethods.IsHead(_method))
|
||||
{
|
||||
_requestType = RequestType.IsHead;
|
||||
}
|
||||
else
|
||||
{
|
||||
_requestType = RequestType.Unspecified;
|
||||
}
|
||||
}
|
||||
|
||||
internal enum PreconditionState
|
||||
{
|
||||
Unspecified,
|
||||
NotModified,
|
||||
ShouldProcess,
|
||||
PreconditionFailed
|
||||
}
|
||||
private RequestHeaders RequestHeaders => (_requestHeaders ??= _request.GetTypedHeaders());
|
||||
|
||||
public bool IsHeadMethod
|
||||
{
|
||||
get { return _isHead; }
|
||||
}
|
||||
private ResponseHeaders ResponseHeaders => (_responseHeaders ??= _response.GetTypedHeaders());
|
||||
|
||||
public bool IsHeadMethod => _requestType.HasFlag(RequestType.IsHead);
|
||||
|
||||
public bool IsGetMethod => _requestType.HasFlag(RequestType.IsGet);
|
||||
|
||||
public bool IsRangeRequest
|
||||
{
|
||||
get { return _isRangeRequest; }
|
||||
}
|
||||
|
||||
public string SubPath
|
||||
{
|
||||
get { return _subPath.Value; }
|
||||
}
|
||||
|
||||
public string PhysicalPath
|
||||
{
|
||||
get { return _fileInfo?.PhysicalPath; }
|
||||
}
|
||||
|
||||
public bool ValidateNoEndpoint()
|
||||
{
|
||||
// Return true because we only want to run if there is no endpoint.
|
||||
return _context.GetEndpoint() == null;
|
||||
}
|
||||
|
||||
public bool ValidateMethod()
|
||||
{
|
||||
_method = _request.Method;
|
||||
_isGet = HttpMethods.IsGet(_method);
|
||||
_isHead = HttpMethods.IsHead(_method);
|
||||
return _isGet || _isHead;
|
||||
}
|
||||
|
||||
// Check if the URL matches any expected paths
|
||||
public bool ValidatePath()
|
||||
{
|
||||
return Helpers.TryMatchPath(_context, _matchUrl, forDirectory: false, subpath: out _subPath);
|
||||
}
|
||||
|
||||
public bool LookupContentType()
|
||||
{
|
||||
if (_contentTypeProvider.TryGetContentType(_subPath.Value, out _contentType))
|
||||
get => _requestType.HasFlag(RequestType.IsRange);
|
||||
private set
|
||||
{
|
||||
return true;
|
||||
if (value)
|
||||
{
|
||||
_requestType |= RequestType.IsRange;
|
||||
}
|
||||
else
|
||||
{
|
||||
_requestType &= ~RequestType.IsRange;
|
||||
}
|
||||
}
|
||||
|
||||
if (_options.ServeUnknownFileTypes)
|
||||
{
|
||||
_contentType = _options.DefaultContentType;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public string SubPath => _subPath.Value;
|
||||
|
||||
public string PhysicalPath => _fileInfo?.PhysicalPath;
|
||||
|
||||
public bool LookupFileInfo()
|
||||
{
|
||||
_fileInfo = _fileProvider.GetFileInfo(_subPath.Value);
|
||||
|
|
@ -175,8 +145,10 @@ namespace Microsoft.AspNetCore.StaticFiles
|
|||
|
||||
private void ComputeIfMatch()
|
||||
{
|
||||
var requestHeaders = RequestHeaders;
|
||||
|
||||
// 14.24 If-Match
|
||||
var ifMatch = _requestHeaders.IfMatch;
|
||||
var ifMatch = requestHeaders.IfMatch;
|
||||
if (ifMatch != null && ifMatch.Any())
|
||||
{
|
||||
_ifMatchState = PreconditionState.PreconditionFailed;
|
||||
|
|
@ -191,7 +163,7 @@ namespace Microsoft.AspNetCore.StaticFiles
|
|||
}
|
||||
|
||||
// 14.26 If-None-Match
|
||||
var ifNoneMatch = _requestHeaders.IfNoneMatch;
|
||||
var ifNoneMatch = requestHeaders.IfNoneMatch;
|
||||
if (ifNoneMatch != null && ifNoneMatch.Any())
|
||||
{
|
||||
_ifNoneMatchState = PreconditionState.ShouldProcess;
|
||||
|
|
@ -208,10 +180,11 @@ namespace Microsoft.AspNetCore.StaticFiles
|
|||
|
||||
private void ComputeIfModifiedSince()
|
||||
{
|
||||
var requestHeaders = RequestHeaders;
|
||||
var now = DateTimeOffset.UtcNow;
|
||||
|
||||
// 14.25 If-Modified-Since
|
||||
var ifModifiedSince = _requestHeaders.IfModifiedSince;
|
||||
var ifModifiedSince = requestHeaders.IfModifiedSince;
|
||||
if (ifModifiedSince.HasValue && ifModifiedSince <= now)
|
||||
{
|
||||
bool modified = ifModifiedSince < _lastModified;
|
||||
|
|
@ -219,7 +192,7 @@ namespace Microsoft.AspNetCore.StaticFiles
|
|||
}
|
||||
|
||||
// 14.28 If-Unmodified-Since
|
||||
var ifUnmodifiedSince = _requestHeaders.IfUnmodifiedSince;
|
||||
var ifUnmodifiedSince = requestHeaders.IfUnmodifiedSince;
|
||||
if (ifUnmodifiedSince.HasValue && ifUnmodifiedSince <= now)
|
||||
{
|
||||
bool unmodified = ifUnmodifiedSince >= _lastModified;
|
||||
|
|
@ -230,7 +203,7 @@ namespace Microsoft.AspNetCore.StaticFiles
|
|||
private void ComputeIfRange()
|
||||
{
|
||||
// 14.27 If-Range
|
||||
var ifRangeHeader = _requestHeaders.IfRange;
|
||||
var ifRangeHeader = RequestHeaders.IfRange;
|
||||
if (ifRangeHeader != null)
|
||||
{
|
||||
// If the validator given in the If-Range header field matches the
|
||||
|
|
@ -242,12 +215,12 @@ namespace Microsoft.AspNetCore.StaticFiles
|
|||
{
|
||||
if (_lastModified > ifRangeHeader.LastModified)
|
||||
{
|
||||
_isRangeRequest = false;
|
||||
IsRangeRequest = false;
|
||||
}
|
||||
}
|
||||
else if (_etag != null && ifRangeHeader.EntityTag != null && !ifRangeHeader.EntityTag.Compare(_etag, useStrongComparison: true))
|
||||
{
|
||||
_isRangeRequest = false;
|
||||
IsRangeRequest = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -259,12 +232,15 @@ namespace Microsoft.AspNetCore.StaticFiles
|
|||
|
||||
// A server MUST ignore a Range header field received with a request method other
|
||||
// than GET.
|
||||
if (!_isGet)
|
||||
if (!IsGetMethod)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
(_isRangeRequest, _range) = RangeHelper.ParseRange(_context, _requestHeaders, _length, _logger);
|
||||
(var isRangeRequest, var range) = RangeHelper.ParseRange(_context, RequestHeaders, _length, _logger);
|
||||
|
||||
_range = range;
|
||||
IsRangeRequest = isRangeRequest;
|
||||
}
|
||||
|
||||
public void ApplyResponseHeaders(int statusCode)
|
||||
|
|
@ -278,9 +254,11 @@ namespace Microsoft.AspNetCore.StaticFiles
|
|||
{
|
||||
_response.ContentType = _contentType;
|
||||
}
|
||||
_responseHeaders.LastModified = _lastModified;
|
||||
_responseHeaders.ETag = _etag;
|
||||
_responseHeaders.Headers[HeaderNames.AcceptRanges] = "bytes";
|
||||
|
||||
var responseHeaders = ResponseHeaders;
|
||||
responseHeaders.LastModified = _lastModified;
|
||||
responseHeaders.ETag = _etag;
|
||||
responseHeaders.Headers[HeaderNames.AcceptRanges] = "bytes";
|
||||
}
|
||||
if (statusCode == Constants.Status200Ok)
|
||||
{
|
||||
|
|
@ -294,10 +272,7 @@ namespace Microsoft.AspNetCore.StaticFiles
|
|||
}
|
||||
|
||||
public PreconditionState GetPreconditionState()
|
||||
{
|
||||
return GetMaxPreconditionState(_ifMatchState, _ifNoneMatchState,
|
||||
_ifModifiedSinceState, _ifUnmodifiedSinceState);
|
||||
}
|
||||
=> GetMaxPreconditionState(_ifMatchState, _ifNoneMatchState, _ifModifiedSinceState, _ifUnmodifiedSinceState);
|
||||
|
||||
private static PreconditionState GetMaxPreconditionState(params PreconditionState[] states)
|
||||
{
|
||||
|
|
@ -320,6 +295,52 @@ namespace Microsoft.AspNetCore.StaticFiles
|
|||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public async Task ServeStaticFile(HttpContext context, RequestDelegate next)
|
||||
{
|
||||
ComprehendRequestHeaders();
|
||||
switch (GetPreconditionState())
|
||||
{
|
||||
case PreconditionState.Unspecified:
|
||||
case PreconditionState.ShouldProcess:
|
||||
if (IsHeadMethod)
|
||||
{
|
||||
await SendStatusAsync(Constants.Status200Ok);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (IsRangeRequest)
|
||||
{
|
||||
await SendRangeAsync();
|
||||
return;
|
||||
}
|
||||
|
||||
await SendAsync();
|
||||
_logger.FileServed(SubPath, PhysicalPath);
|
||||
return;
|
||||
}
|
||||
catch (FileNotFoundException)
|
||||
{
|
||||
context.Response.Clear();
|
||||
}
|
||||
await next(context);
|
||||
return;
|
||||
case PreconditionState.NotModified:
|
||||
_logger.FileNotModified(SubPath);
|
||||
await SendStatusAsync(Constants.Status304NotModified);
|
||||
return;
|
||||
case PreconditionState.PreconditionFailed:
|
||||
_logger.PreconditionFailed(SubPath);
|
||||
await SendStatusAsync(Constants.Status412PreconditionFailed);
|
||||
return;
|
||||
default:
|
||||
var exception = new NotImplementedException(GetPreconditionState().ToString());
|
||||
Debug.Fail(exception.ToString());
|
||||
throw exception;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task SendAsync()
|
||||
{
|
||||
SetCompressionMode();
|
||||
|
|
@ -358,14 +379,14 @@ namespace Microsoft.AspNetCore.StaticFiles
|
|||
// 14.16 Content-Range - A server sending a response with status code 416 (Requested range not satisfiable)
|
||||
// SHOULD include a Content-Range field with a byte-range-resp-spec of "*". The instance-length specifies
|
||||
// the current length of the selected resource. e.g. */length
|
||||
_responseHeaders.ContentRange = new ContentRangeHeaderValue(_length);
|
||||
ResponseHeaders.ContentRange = new ContentRangeHeaderValue(_length);
|
||||
ApplyResponseHeaders(Constants.Status416RangeNotSatisfiable);
|
||||
|
||||
_logger.RangeNotSatisfiable(SubPath);
|
||||
return;
|
||||
}
|
||||
|
||||
_responseHeaders.ContentRange = ComputeContentRange(_range, out var start, out var length);
|
||||
ResponseHeaders.ContentRange = ComputeContentRange(_range, out var start, out var length);
|
||||
_response.ContentLength = length;
|
||||
SetCompressionMode();
|
||||
ApplyResponseHeaders(Constants.Status206PartialContent);
|
||||
|
|
@ -416,5 +437,22 @@ namespace Microsoft.AspNetCore.StaticFiles
|
|||
responseCompressionFeature.Mode = _options.HttpsCompression;
|
||||
}
|
||||
}
|
||||
|
||||
internal enum PreconditionState : byte
|
||||
{
|
||||
Unspecified,
|
||||
NotModified,
|
||||
ShouldProcess,
|
||||
PreconditionFailed
|
||||
}
|
||||
|
||||
[Flags]
|
||||
private enum RequestType : byte
|
||||
{
|
||||
Unspecified = 0b_000,
|
||||
IsHead = 0b_001,
|
||||
IsGet = 0b_010,
|
||||
IsRange = 0b_100,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,12 +2,11 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.Endpoints;
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
|
@ -70,83 +69,83 @@ namespace Microsoft.AspNetCore.StaticFiles
|
|||
/// <returns></returns>
|
||||
public Task Invoke(HttpContext context)
|
||||
{
|
||||
var fileContext = new StaticFileContext(context, _options, _matchUrl, _logger, _fileProvider, _contentTypeProvider);
|
||||
|
||||
if (!fileContext.ValidateNoEndpoint())
|
||||
if (!ValidateNoEndpoint(context))
|
||||
{
|
||||
_logger.EndpointMatched();
|
||||
}
|
||||
else if (!fileContext.ValidateMethod())
|
||||
else if (!ValidateMethod(context))
|
||||
{
|
||||
_logger.RequestMethodNotSupported(context.Request.Method);
|
||||
}
|
||||
else if (!fileContext.ValidatePath())
|
||||
else if (!ValidatePath(context, _matchUrl, out var subPath))
|
||||
{
|
||||
_logger.PathMismatch(fileContext.SubPath);
|
||||
_logger.PathMismatch(subPath);
|
||||
}
|
||||
else if (!fileContext.LookupContentType())
|
||||
else if (!LookupContentType(_contentTypeProvider, _options, subPath, out var contentType))
|
||||
{
|
||||
_logger.FileTypeNotSupported(fileContext.SubPath);
|
||||
_logger.FileTypeNotSupported(subPath);
|
||||
}
|
||||
else if (!fileContext.LookupFileInfo())
|
||||
else
|
||||
{
|
||||
// If we get here, we can try to serve the file
|
||||
return TryServeStaticFile(context, contentType, subPath);
|
||||
}
|
||||
|
||||
return _next(context);
|
||||
}
|
||||
|
||||
// Return true because we only want to run if there is no endpoint.
|
||||
private static bool ValidateNoEndpoint(HttpContext context) => context.GetEndpoint() == null;
|
||||
|
||||
private static bool ValidateMethod(HttpContext context)
|
||||
{
|
||||
var method = context.Request.Method;
|
||||
var isValid = false;
|
||||
if (HttpMethods.IsGet(method))
|
||||
{
|
||||
isValid = true;
|
||||
}
|
||||
else if (HttpMethods.IsHead(method))
|
||||
{
|
||||
isValid = true;
|
||||
}
|
||||
|
||||
return isValid;
|
||||
}
|
||||
|
||||
internal static bool ValidatePath(HttpContext context, PathString matchUrl, out PathString subPath) => Helpers.TryMatchPath(context, matchUrl, forDirectory: false, out subPath);
|
||||
|
||||
internal static bool LookupContentType(IContentTypeProvider contentTypeProvider, StaticFileOptions options, PathString subPath, out string contentType)
|
||||
{
|
||||
if (contentTypeProvider.TryGetContentType(subPath.Value, out contentType))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (options.ServeUnknownFileTypes)
|
||||
{
|
||||
contentType = options.DefaultContentType;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private Task TryServeStaticFile(HttpContext context, string contentType, PathString subPath)
|
||||
{
|
||||
var fileContext = new StaticFileContext(context, _options, _logger, _fileProvider, contentType, subPath);
|
||||
|
||||
if (!fileContext.LookupFileInfo())
|
||||
{
|
||||
_logger.FileNotFound(fileContext.SubPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we get here, we can try to serve the file
|
||||
return ServeStaticFile(context, fileContext);
|
||||
return fileContext.ServeStaticFile(context, _next);
|
||||
}
|
||||
|
||||
return _next(context);
|
||||
}
|
||||
|
||||
private async Task ServeStaticFile(HttpContext context, StaticFileContext fileContext)
|
||||
{
|
||||
fileContext.ComprehendRequestHeaders();
|
||||
switch (fileContext.GetPreconditionState())
|
||||
{
|
||||
case StaticFileContext.PreconditionState.Unspecified:
|
||||
case StaticFileContext.PreconditionState.ShouldProcess:
|
||||
if (fileContext.IsHeadMethod)
|
||||
{
|
||||
await fileContext.SendStatusAsync(Constants.Status200Ok);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (fileContext.IsRangeRequest)
|
||||
{
|
||||
await fileContext.SendRangeAsync();
|
||||
return;
|
||||
}
|
||||
|
||||
await fileContext.SendAsync();
|
||||
_logger.FileServed(fileContext.SubPath, fileContext.PhysicalPath);
|
||||
return;
|
||||
}
|
||||
catch (FileNotFoundException)
|
||||
{
|
||||
context.Response.Clear();
|
||||
}
|
||||
await _next(context);
|
||||
return;
|
||||
case StaticFileContext.PreconditionState.NotModified:
|
||||
_logger.FileNotModified(fileContext.SubPath);
|
||||
await fileContext.SendStatusAsync(Constants.Status304NotModified);
|
||||
return;
|
||||
|
||||
case StaticFileContext.PreconditionState.PreconditionFailed:
|
||||
_logger.PreconditionFailed(fileContext.SubPath);
|
||||
await fileContext.SendStatusAsync(Constants.Status412PreconditionFailed);
|
||||
return;
|
||||
|
||||
default:
|
||||
var exception = new NotImplementedException(fileContext.GetPreconditionState().ToString());
|
||||
Debug.Fail(exception.ToString());
|
||||
throw exception;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,14 +22,18 @@ namespace Microsoft.AspNetCore.StaticFiles
|
|||
{
|
||||
// Arrange
|
||||
var options = new StaticFileOptions();
|
||||
var context = new StaticFileContext(new DefaultHttpContext(), options, PathString.Empty, NullLogger.Instance, new TestFileProvider(), new FileExtensionContentTypeProvider());
|
||||
var httpContext = new DefaultHttpContext();
|
||||
var pathString = PathString.Empty;
|
||||
var validateResult = StaticFileMiddleware.ValidatePath(httpContext, pathString, out var subPath);
|
||||
var contentTypeResult = StaticFileMiddleware.LookupContentType(new FileExtensionContentTypeProvider(), options, subPath, out var contentType);
|
||||
var context = new StaticFileContext(httpContext, options, NullLogger.Instance, new TestFileProvider(), contentType, subPath);
|
||||
|
||||
// Act
|
||||
var validateResult = context.ValidatePath();
|
||||
var lookupResult = context.LookupFileInfo();
|
||||
|
||||
// Assert
|
||||
Assert.True(validateResult);
|
||||
Assert.False(contentTypeResult);
|
||||
Assert.False(lookupResult);
|
||||
}
|
||||
|
||||
|
|
@ -46,13 +50,17 @@ namespace Microsoft.AspNetCore.StaticFiles
|
|||
var pathString = new PathString("/test");
|
||||
var httpContext = new DefaultHttpContext();
|
||||
httpContext.Request.Path = new PathString("/test/foo.txt");
|
||||
var context = new StaticFileContext(httpContext, options, pathString, NullLogger.Instance, fileProvider, new FileExtensionContentTypeProvider());
|
||||
var validateResult = StaticFileMiddleware.ValidatePath(httpContext, pathString, out var subPath);
|
||||
var contentTypeResult = StaticFileMiddleware.LookupContentType(new FileExtensionContentTypeProvider(), options, subPath, out var contentType);
|
||||
|
||||
var context = new StaticFileContext(httpContext, options, NullLogger.Instance, fileProvider, contentType, subPath);
|
||||
|
||||
// Act
|
||||
context.ValidatePath();
|
||||
var result = context.LookupFileInfo();
|
||||
|
||||
// Assert
|
||||
Assert.True(validateResult);
|
||||
Assert.True(contentTypeResult);
|
||||
Assert.True(result);
|
||||
}
|
||||
|
||||
|
|
@ -70,10 +78,14 @@ namespace Microsoft.AspNetCore.StaticFiles
|
|||
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());
|
||||
var validateResult = StaticFileMiddleware.ValidatePath(httpContext, pathString, out var subPath);
|
||||
var contentTypeResult = StaticFileMiddleware.LookupContentType(new FileExtensionContentTypeProvider(), options, subPath, out var contentType);
|
||||
|
||||
var context = new StaticFileContext(httpContext, options, NullLogger.Instance, fileProvider, contentType, subPath);
|
||||
|
||||
context.ValidatePath();
|
||||
var result = context.LookupFileInfo();
|
||||
Assert.True(validateResult);
|
||||
Assert.True(contentTypeResult);
|
||||
Assert.True(result);
|
||||
|
||||
await context.SendAsync();
|
||||
|
|
@ -95,10 +107,14 @@ namespace Microsoft.AspNetCore.StaticFiles
|
|||
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());
|
||||
var validateResult = StaticFileMiddleware.ValidatePath(httpContext, pathString, out var subPath);
|
||||
var contentTypeResult = StaticFileMiddleware.LookupContentType(new FileExtensionContentTypeProvider(), options, subPath, out var contentType);
|
||||
|
||||
var context = new StaticFileContext(httpContext, options, NullLogger.Instance, fileProvider, contentType, subPath);
|
||||
|
||||
context.ValidatePath();
|
||||
var result = context.LookupFileInfo();
|
||||
Assert.True(validateResult);
|
||||
Assert.True(contentTypeResult);
|
||||
Assert.False(result);
|
||||
|
||||
Assert.Equal(HttpsCompressionMode.Default, httpsCompressionFeature.Mode);
|
||||
|
|
|
|||
Loading…
Reference in New Issue