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.
using System;
using System.IO;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
@ -33,7 +34,7 @@ namespace Microsoft.AspNetCore.StaticFiles
throw new ArgumentNullException(nameof(encoder));
}
_htmlEncoder = encoder;
}
}
/// <summary>
/// Generates an HTML view for a directory.
@ -81,8 +82,8 @@ namespace Microsoft.AspNetCore.StaticFiles
margin-top: 5px;
margin-bottom: 0px;}
#index {
border-collapse: separate;
border-spacing: 0;
border-collapse: separate;
border-spacing: 0;
margin: 0 0 20px; }
#index th {
vertical-align: bottom;
@ -132,27 +133,66 @@ namespace Microsoft.AspNetCore.StaticFiles
foreach (var subdir in contents.Where(info => info.IsDirectory))
{
builder.AppendFormat(@"
// 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(@"
<tr class=""directory"">
<td class=""name""><a href=""./{0}/"">{0}/</a></td>
<td></td>
<td class=""modified"">{1}</td>
</tr>",
HtmlEncode(subdir.Name),
HtmlEncode(subdir.LastModified.ToString(CultureInfo.CurrentCulture)));
HtmlEncode(subdir.Name),
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))
{
builder.AppendFormat(@"
// 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(@"
<tr class=""file"">
<td class=""name""><a href=""./{0}"">{0}</a></td>
<td class=""length"">{1}</td>
<td class=""modified"">{2}</td>
</tr>",
HtmlEncode(file.Name),
HtmlEncode(file.Length.ToString("n0", CultureInfo.CurrentCulture)),
HtmlEncode(file.LastModified.ToString(CultureInfo.CurrentCulture)));
HtmlEncode(file.Name),
HtmlEncode(file.Length.ToString("n0", 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(@"