Be more defensive in HtmlDirectoryFormatter (#9755)

Fixes aspnet/AspNetCore-Internal#2245

Our tests were flaking out due to this possible race where the directory browser middleware enumerates the directory but a file is deleted while we're formatting the response. This change just catches the FNF/DNF exceptions and suppresses the file from the directory listing
This commit is contained in:
Andrew Stanton-Nurse 2019-04-29 11:25:26 -07:00 committed by GitHub
parent 49ae712294
commit fe3dfe627b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 50 additions and 10 deletions

View File

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
using System.IO;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Linq; using System.Linq;
@ -131,6 +132,11 @@ namespace Microsoft.AspNetCore.StaticFiles
HtmlEncode(Resources.HtmlDir_LastModified)); HtmlEncode(Resources.HtmlDir_LastModified));
foreach (var subdir in contents.Where(info => info.IsDirectory)) foreach (var subdir in contents.Where(info => info.IsDirectory))
{
// Collect directory metadata in a try...catch in case the file is deleted while we're getting the data.
// The metadata is retrieved prior to calling AppendFormat so if it throws, we won't have written a row
// to the table.
try
{ {
builder.AppendFormat(@" builder.AppendFormat(@"
<tr class=""directory""> <tr class=""directory"">
@ -141,8 +147,30 @@ namespace Microsoft.AspNetCore.StaticFiles
HtmlEncode(subdir.Name), HtmlEncode(subdir.Name),
HtmlEncode(subdir.LastModified.ToString(CultureInfo.CurrentCulture))); HtmlEncode(subdir.LastModified.ToString(CultureInfo.CurrentCulture)));
} }
catch (DirectoryNotFoundException)
{
// The physical DirectoryInfo class doesn't appear to throw for either
// of Name or LastWriteTimeUtc (which backs LastModified in the physical provider)
// if the directory doesn't exist. However, we don't know what other providers might do.
// Just skip this directory. It was deleted while we were enumerating.
}
catch (FileNotFoundException)
{
// The physical DirectoryInfo class doesn't appear to throw for either
// of Name or LastWriteTimeUtc (which backs LastModified in the physical provider)
// if the directory doesn't exist. However, we don't know what other providers might do.
// Just skip this directory. It was deleted while we were enumerating.
}
}
foreach (var file in contents.Where(info => !info.IsDirectory)) foreach (var file in contents.Where(info => !info.IsDirectory))
{
// Collect file metadata in a try...catch in case the file is deleted while we're getting the data.
// The metadata is retrieved prior to calling AppendFormat so if it throws, we won't have written a row
// to the table.
try
{ {
builder.AppendFormat(@" builder.AppendFormat(@"
<tr class=""file""> <tr class=""file"">
@ -154,6 +182,18 @@ namespace Microsoft.AspNetCore.StaticFiles
HtmlEncode(file.Length.ToString("n0", CultureInfo.CurrentCulture)), HtmlEncode(file.Length.ToString("n0", CultureInfo.CurrentCulture)),
HtmlEncode(file.LastModified.ToString(CultureInfo.CurrentCulture))); HtmlEncode(file.LastModified.ToString(CultureInfo.CurrentCulture)));
} }
catch (DirectoryNotFoundException)
{
// There doesn't appear to be a case where DirectoryNotFound is thrown in the physical provider,
// but we don't know what other providers might do.
// Just skip this file. It was deleted while we were enumerating.
}
catch (FileNotFoundException)
{
// Just skip this file. It was deleted while we were enumerating.
}
}
builder.Append(@" builder.Append(@"
</tbody> </tbody>