Return HTTP 404 if FileNotFoundException is thrown when attempting to send a file (#232)

This commit is contained in:
Nate McMaster 2018-02-21 13:22:31 -08:00 committed by GitHub
parent 3685035637
commit 6d7269dafb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 52 additions and 10 deletions

View File

@ -233,7 +233,7 @@ namespace Microsoft.AspNetCore.StaticFiles
// the Range header field.
if (ifRangeHeader.LastModified.HasValue)
{
if (_lastModified !=null && _lastModified > ifRangeHeader.LastModified)
if (_lastModified != null && _lastModified > ifRangeHeader.LastModified)
{
_isRangeRequest = false;
}
@ -318,11 +318,11 @@ namespace Microsoft.AspNetCore.StaticFiles
public async Task SendAsync()
{
ApplyResponseHeaders(Constants.Status200Ok);
string physicalPath = _fileInfo.PhysicalPath;
var sendFile = _context.Features.Get<IHttpSendFileFeature>();
if (sendFile != null && !string.IsNullOrEmpty(physicalPath))
{
ApplyResponseHeaders(Constants.Status200Ok);
// We don't need to directly cancel this, if the client disconnects it will fail silently.
await sendFile.SendFileAsync(physicalPath, 0, _length, CancellationToken.None);
return;
@ -332,6 +332,9 @@ namespace Microsoft.AspNetCore.StaticFiles
{
using (var readStream = _fileInfo.CreateReadStream())
{
// Don't apply headers until we are sure we can open this file.
ApplyResponseHeaders(Constants.Status200Ok);
// Larger StreamCopyBufferSize is required because in case of FileStream readStream isn't going to be buffering
await StreamCopyOperation.CopyToAsync(readStream, _response.Body, _length, StreamCopyBufferSize, _context.RequestAborted);
}

View File

@ -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
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public Task Invoke(HttpContext context)
public async Task Invoke(HttpContext context)
{
var fileContext = new StaticFileContext(context, _options, _matchUrl, _logger, _fileProvider, _contentTypeProvider);
@ -97,21 +98,35 @@ 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)
{
return fileContext.SendRangeAsync();
await fileContext.SendRangeAsync();
return;
}
try
{
await fileContext.SendAsync();
_logger.LogFileServed(fileContext.SubPath, fileContext.PhysicalPath);
return;
}
catch (FileNotFoundException)
{
_logger.LogFileNotFound(fileContext.SubPath);
await fileContext.SendStatusAsync(StatusCodes.Status404NotFound);
return;
}
_logger.LogFileServed(fileContext.SubPath, fileContext.PhysicalPath);
return fileContext.SendAsync();
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 +135,7 @@ namespace Microsoft.AspNetCore.StaticFiles
}
}
return _next(context);
await _next(context);
}
}
}

View File

@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
@ -13,6 +14,7 @@ using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.TestHost;
using Microsoft.AspNetCore.Testing.xunit;
using Microsoft.Extensions.FileProviders;
using Microsoft.Net.Http.Headers;
using Xunit;
namespace Microsoft.AspNetCore.StaticFiles
@ -29,6 +31,27 @@ 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");
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);
}
[Fact]
@ -101,6 +124,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())
{