// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.IO; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Features; using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Primitives; using Xunit; namespace Microsoft.AspNetCore.StaticFiles { public class StaticFileContextTest { [Fact] public void LookupFileInfo_ReturnsFalse_IfFileDoesNotExist() { // Arrange var options = new StaticFileOptions(); 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 lookupResult = context.LookupFileInfo(); // Assert Assert.True(validateResult); Assert.False(contentTypeResult); Assert.False(lookupResult); } [Fact] public void LookupFileInfo_ReturnsTrue_IfFileExists() { // Arrange 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(); httpContext.Request.Path = new PathString("/test/foo.txt"); 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 var result = context.LookupFileInfo(); // Assert Assert.True(validateResult); Assert.True(contentTypeResult); 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(httpsCompressionFeature); httpContext.Request.Path = new PathString("/test/foo.txt"); 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); var result = context.LookupFileInfo(); Assert.True(validateResult); Assert.True(contentTypeResult); 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(httpsCompressionFeature); httpContext.Request.Path = new PathString("/test/bar.txt"); 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); var result = context.LookupFileInfo(); Assert.True(validateResult); Assert.True(contentTypeResult); Assert.False(result); Assert.Equal(HttpsCompressionMode.Default, httpsCompressionFeature.Mode); } [Fact] public async Task RequestAborted_DoesntThrow() { 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(); httpContext.Request.Path = new PathString("/test/foo.txt"); httpContext.RequestAborted = new CancellationToken(canceled: true); var body = new MemoryStream(); httpContext.Response.Body = body; 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); var result = context.LookupFileInfo(); Assert.True(validateResult); Assert.True(contentTypeResult); Assert.True(result); await context.SendAsync(); Assert.Equal(0, body.Length); } private sealed class TestFileProvider : IFileProvider { private readonly Dictionary _files = new Dictionary(StringComparer.Ordinal); public void AddFile(string path, IFileInfo fileInfo) { _files[path] = fileInfo; } public IDirectoryContents GetDirectoryContents(string subpath) { throw new NotImplementedException(); } public IFileInfo GetFileInfo(string subpath) { if (_files.TryGetValue(subpath, out var result)) { return result; } return new NotFoundFileInfo(); } public IChangeToken Watch(string filter) { throw new NotSupportedException(); } private class NotFoundFileInfo : IFileInfo { public bool Exists { get { return false; } } public bool IsDirectory { get { throw new NotImplementedException(); } } public DateTimeOffset LastModified { get { throw new NotImplementedException(); } } public long Length { get { throw new NotImplementedException(); } } public string Name { get { throw new NotImplementedException(); } } public string PhysicalPath { get { throw new NotImplementedException(); } } public Stream CreateReadStream() { throw new NotImplementedException(); } } } private sealed class TestFileInfo : IFileInfo { public bool Exists { get { return true; } } public bool IsDirectory { get { return false; } } public DateTimeOffset LastModified { get; set; } public long Length { get; set; } public string Name { get; set; } public string PhysicalPath { get; set; } public Stream CreateReadStream() { return new MemoryStream(); } } private class TestHttpsCompressionFeature : IHttpsCompressionFeature { public HttpsCompressionMode Mode { get; set; } } } }