Handle FileNotFoundException when it is thrown while attempting to send a file (#233)
This commit is contained in:
parent
3cd6add095
commit
0c7ff81a9f
|
|
@ -21,6 +21,7 @@
|
||||||
<MicrosoftNETCoreApp20PackageVersion>2.0.0</MicrosoftNETCoreApp20PackageVersion>
|
<MicrosoftNETCoreApp20PackageVersion>2.0.0</MicrosoftNETCoreApp20PackageVersion>
|
||||||
<MicrosoftNETCoreApp21PackageVersion>2.1.0-preview2-26130-04</MicrosoftNETCoreApp21PackageVersion>
|
<MicrosoftNETCoreApp21PackageVersion>2.1.0-preview2-26130-04</MicrosoftNETCoreApp21PackageVersion>
|
||||||
<MicrosoftNETTestSdkPackageVersion>15.6.0</MicrosoftNETTestSdkPackageVersion>
|
<MicrosoftNETTestSdkPackageVersion>15.6.0</MicrosoftNETTestSdkPackageVersion>
|
||||||
|
<MoqPackageVersion>4.7.49</MoqPackageVersion>
|
||||||
<XunitAnalyzersPackageVersion>0.8.0</XunitAnalyzersPackageVersion>
|
<XunitAnalyzersPackageVersion>0.8.0</XunitAnalyzersPackageVersion>
|
||||||
<XunitPackageVersion>2.3.1</XunitPackageVersion>
|
<XunitPackageVersion>2.3.1</XunitPackageVersion>
|
||||||
<XunitRunnerVisualStudioPackageVersion>2.4.0-beta.1.build3945</XunitRunnerVisualStudioPackageVersion>
|
<XunitRunnerVisualStudioPackageVersion>2.4.0-beta.1.build3945</XunitRunnerVisualStudioPackageVersion>
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
|
@ -67,7 +68,7 @@ namespace Microsoft.AspNetCore.StaticFiles
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context"></param>
|
/// <param name="context"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public Task Invoke(HttpContext context)
|
public async Task Invoke(HttpContext context)
|
||||||
{
|
{
|
||||||
var fileContext = new StaticFileContext(context, _options, _matchUrl, _logger, _fileProvider, _contentTypeProvider);
|
var fileContext = new StaticFileContext(context, _options, _matchUrl, _logger, _fileProvider, _contentTypeProvider);
|
||||||
|
|
||||||
|
|
@ -97,21 +98,36 @@ namespace Microsoft.AspNetCore.StaticFiles
|
||||||
case StaticFileContext.PreconditionState.ShouldProcess:
|
case StaticFileContext.PreconditionState.ShouldProcess:
|
||||||
if (fileContext.IsHeadMethod)
|
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);
|
catch (FileNotFoundException)
|
||||||
return fileContext.SendAsync();
|
{
|
||||||
|
context.Response.Clear();
|
||||||
|
}
|
||||||
|
break;
|
||||||
case StaticFileContext.PreconditionState.NotModified:
|
case StaticFileContext.PreconditionState.NotModified:
|
||||||
_logger.LogPathNotModified(fileContext.SubPath);
|
_logger.LogPathNotModified(fileContext.SubPath);
|
||||||
return fileContext.SendStatusAsync(Constants.Status304NotModified);
|
await fileContext.SendStatusAsync(Constants.Status304NotModified);
|
||||||
|
return;
|
||||||
|
|
||||||
case StaticFileContext.PreconditionState.PreconditionFailed:
|
case StaticFileContext.PreconditionState.PreconditionFailed:
|
||||||
_logger.LogPreconditionFailed(fileContext.SubPath);
|
_logger.LogPreconditionFailed(fileContext.SubPath);
|
||||||
return fileContext.SendStatusAsync(Constants.Status412PreconditionFailed);
|
await fileContext.SendStatusAsync(Constants.Status412PreconditionFailed);
|
||||||
|
return;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
var exception = new NotImplementedException(fileContext.GetPreconditionState().ToString());
|
var exception = new NotImplementedException(fileContext.GetPreconditionState().ToString());
|
||||||
|
|
@ -120,7 +136,7 @@ namespace Microsoft.AspNetCore.StaticFiles
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return _next(context);
|
await _next(context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="$(MicrosoftAspNetCoreTestHostPackageVersion)" />
|
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="$(MicrosoftAspNetCoreTestHostPackageVersion)" />
|
||||||
|
<PackageReference Include="Moq" Version="$(MoqPackageVersion)" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
||||||
|
|
@ -3,16 +3,20 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Http.Features;
|
||||||
using Microsoft.AspNetCore.TestHost;
|
using Microsoft.AspNetCore.TestHost;
|
||||||
using Microsoft.AspNetCore.Testing.xunit;
|
using Microsoft.AspNetCore.Testing.xunit;
|
||||||
using Microsoft.Extensions.FileProviders;
|
using Microsoft.Extensions.FileProviders;
|
||||||
|
using Moq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.StaticFiles
|
namespace Microsoft.AspNetCore.StaticFiles
|
||||||
|
|
@ -29,6 +33,59 @@ namespace Microsoft.AspNetCore.StaticFiles
|
||||||
var response = await server.CreateClient().GetAsync("/ranges.txt");
|
var response = await server.CreateClient().GetAsync("/ranges.txt");
|
||||||
|
|
||||||
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
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<IHttpSendFileFeature>();
|
||||||
|
mockSendFile.Setup(m => m.SendFileAsync(It.IsAny<string>(), It.IsAny<long>(), It.IsAny<long?>(), It.IsAny<CancellationToken>()))
|
||||||
|
.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]
|
[Fact]
|
||||||
|
|
@ -101,6 +158,7 @@ namespace Microsoft.AspNetCore.StaticFiles
|
||||||
Assert.Equal("text/plain", response.Content.Headers.ContentType.ToString());
|
Assert.Equal("text/plain", response.Content.Headers.ContentType.ToString());
|
||||||
Assert.True(response.Content.Headers.ContentLength == fileInfo.Length);
|
Assert.True(response.Content.Headers.ContentLength == fileInfo.Length);
|
||||||
Assert.Equal(response.Content.Headers.ContentLength, responseContent.Length);
|
Assert.Equal(response.Content.Headers.ContentLength, responseContent.Length);
|
||||||
|
Assert.NotNull(response.Headers.ETag);
|
||||||
|
|
||||||
using (var stream = fileInfo.CreateReadStream())
|
using (var stream = fileInfo.CreateReadStream())
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue