diff --git a/build/dependencies.props b/build/dependencies.props
index f8d54b4212..53fad8ff02 100644
--- a/build/dependencies.props
+++ b/build/dependencies.props
@@ -21,6 +21,7 @@
2.0.0
2.1.0-preview2-26130-04
15.6.0
+ 4.7.49
0.8.0
2.3.1
2.4.0-beta.1.build3945
diff --git a/src/Microsoft.AspNetCore.StaticFiles/StaticFileMiddleware.cs b/src/Microsoft.AspNetCore.StaticFiles/StaticFileMiddleware.cs
index d8910bcdbf..46594fc35d 100644
--- a/src/Microsoft.AspNetCore.StaticFiles/StaticFileMiddleware.cs
+++ b/src/Microsoft.AspNetCore.StaticFiles/StaticFileMiddleware.cs
@@ -3,6 +3,7 @@
using System;
using System.Diagnostics;
+using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
@@ -67,7 +68,7 @@ namespace Microsoft.AspNetCore.StaticFiles
///
///
///
- public Task Invoke(HttpContext context)
+ public async Task Invoke(HttpContext context)
{
var fileContext = new StaticFileContext(context, _options, _matchUrl, _logger, _fileProvider, _contentTypeProvider);
@@ -97,21 +98,36 @@ namespace Microsoft.AspNetCore.StaticFiles
case StaticFileContext.PreconditionState.ShouldProcess:
if (fileContext.IsHeadMethod)
{
- return fileContext.SendStatusAsync(Constants.Status200Ok);
+ await fileContext.SendStatusAsync(Constants.Status200Ok);
+ return;
}
- if (fileContext.IsRangeRequest)
+
+ try
{
- return fileContext.SendRangeAsync();
+ if (fileContext.IsRangeRequest)
+ {
+ await fileContext.SendRangeAsync();
+ return;
+ }
+
+ await fileContext.SendAsync();
+ _logger.LogFileServed(fileContext.SubPath, fileContext.PhysicalPath);
+ return;
}
- _logger.LogFileServed(fileContext.SubPath, fileContext.PhysicalPath);
- return fileContext.SendAsync();
+ catch (FileNotFoundException)
+ {
+ context.Response.Clear();
+ }
+ break;
case StaticFileContext.PreconditionState.NotModified:
_logger.LogPathNotModified(fileContext.SubPath);
- return fileContext.SendStatusAsync(Constants.Status304NotModified);
+ await fileContext.SendStatusAsync(Constants.Status304NotModified);
+ return;
case StaticFileContext.PreconditionState.PreconditionFailed:
_logger.LogPreconditionFailed(fileContext.SubPath);
- return fileContext.SendStatusAsync(Constants.Status412PreconditionFailed);
+ await fileContext.SendStatusAsync(Constants.Status412PreconditionFailed);
+ return;
default:
var exception = new NotImplementedException(fileContext.GetPreconditionState().ToString());
@@ -120,7 +136,7 @@ namespace Microsoft.AspNetCore.StaticFiles
}
}
- return _next(context);
+ await _next(context);
}
}
}
diff --git a/test/Microsoft.AspNetCore.StaticFiles.Tests/Microsoft.AspNetCore.StaticFiles.Tests.csproj b/test/Microsoft.AspNetCore.StaticFiles.Tests/Microsoft.AspNetCore.StaticFiles.Tests.csproj
index 9a59dfca29..5826c54b9c 100644
--- a/test/Microsoft.AspNetCore.StaticFiles.Tests/Microsoft.AspNetCore.StaticFiles.Tests.csproj
+++ b/test/Microsoft.AspNetCore.StaticFiles.Tests/Microsoft.AspNetCore.StaticFiles.Tests.csproj
@@ -18,6 +18,7 @@
+
diff --git a/test/Microsoft.AspNetCore.StaticFiles.Tests/StaticFileMiddlewareTests.cs b/test/Microsoft.AspNetCore.StaticFiles.Tests/StaticFileMiddlewareTests.cs
index 7517782e51..31c6894d2b 100644
--- a/test/Microsoft.AspNetCore.StaticFiles.Tests/StaticFileMiddlewareTests.cs
+++ b/test/Microsoft.AspNetCore.StaticFiles.Tests/StaticFileMiddlewareTests.cs
@@ -3,16 +3,20 @@
using System;
using System.Collections.Generic;
+using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
+using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.TestHost;
using Microsoft.AspNetCore.Testing.xunit;
using Microsoft.Extensions.FileProviders;
+using Moq;
using Xunit;
namespace Microsoft.AspNetCore.StaticFiles
@@ -29,6 +33,59 @@ namespace Microsoft.AspNetCore.StaticFiles
var response = await server.CreateClient().GetAsync("/ranges.txt");
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
+ Assert.Null(response.Headers.ETag);
+ }
+
+ [ConditionalFact]
+ [OSSkipCondition(OperatingSystems.Windows, SkipReason = "Symlinks not supported on Windows")]
+ public async Task ReturnsNotFoundForBrokenSymlink()
+ {
+ var badLink = Path.Combine(AppContext.BaseDirectory, Path.GetRandomFileName() + ".txt");
+
+ Process.Start("ln", $"-s \"/tmp/{Path.GetRandomFileName()}\" \"{badLink}\"").WaitForExit();
+ Assert.True(File.Exists(badLink), "Should have created a symlink");
+
+ try
+ {
+ var builder = new WebHostBuilder()
+ .Configure(app => app.UseStaticFiles(new StaticFileOptions { ServeUnknownFileTypes = true }))
+ .UseWebRoot(AppContext.BaseDirectory);
+ var server = new TestServer(builder);
+
+ var response = await server.CreateClient().GetAsync(Path.GetFileName(badLink));
+
+ Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
+ Assert.Null(response.Headers.ETag);
+ }
+ finally
+ {
+ File.Delete(badLink);
+ }
+ }
+
+ [Fact]
+ public async Task ReturnsNotFoundIfSendFileThrows()
+ {
+ var mockSendFile = new Mock();
+ mockSendFile.Setup(m => m.SendFileAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()))
+ .ThrowsAsync(new FileNotFoundException());
+ var builder = new WebHostBuilder()
+ .Configure(app =>
+ {
+ app.Use(async (ctx, next) =>
+ {
+ ctx.Features.Set(mockSendFile.Object);
+ await next();
+ });
+ app.UseStaticFiles(new StaticFileOptions { ServeUnknownFileTypes = true });
+ })
+ .UseWebRoot(AppContext.BaseDirectory);
+ var server = new TestServer(builder);
+
+ var response = await server.CreateClient().GetAsync("TestDocument.txt");
+
+ Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
+ Assert.Null(response.Headers.ETag);
}
[Fact]
@@ -101,6 +158,7 @@ namespace Microsoft.AspNetCore.StaticFiles
Assert.Equal("text/plain", response.Content.Headers.ContentType.ToString());
Assert.True(response.Content.Headers.ContentLength == fileInfo.Length);
Assert.Equal(response.Content.Headers.ContentLength, responseContent.Length);
+ Assert.NotNull(response.Headers.ETag);
using (var stream = fileInfo.CreateReadStream())
{