Skip to content
代码片段 群组 项目
未验证 提交 3009e5c4 编辑于 作者: github-actions[bot]'s avatar github-actions[bot] 提交者: GitHub
浏览文件

[release/6.0] [Static web assets] Fix casing issues during parsing (#36228)


* Rename and relink ManifestStaticWebAssetFileProvider

* Merge manifest nodes on case insensitive OSes

* Add additional tests

* Update selenium versions

Co-authored-by: default avatarJavier Calvarro Nelson <jacalvar@microsoft.com>
上级 2e055d0d
无相关合并请求
......@@ -14,7 +14,7 @@
<Compile Include="$(SharedSourceRoot)RazorViews\*.cs" />
<Compile Include="$(SharedSourceRoot)StackTrace\**\*.cs" />
<Compile Include="$(SharedSourceRoot)ErrorPage\**\*.cs" />
<Compile Include="$(SharedSourceRoot)StaticWebAssets\**\*.cs" />
<Compile Include="$(SharedSourceRoot)StaticWebAssets\**\*.cs" LinkBase="StaticWebAssets" />
</ItemGroup>
<ItemGroup>
......
......@@ -57,6 +57,229 @@ namespace Microsoft.AspNetCore.Hosting.Tests.StaticWebAssets
Assert.Equal(expectedResult, file.Exists);
}
[Theory]
[InlineData("/img/icon.png", true)]
[InlineData("/Img/hero.gif", true)]
// Note that we've changed the casing of the first segment
[InlineData("/Img/icon.png", false)]
[InlineData("/img/hero.gif", false)]
public void ParseWorksWithNodesThatOnlyDifferOnCasing(string path, bool exists)
{
exists = exists | OperatingSystem.IsWindows();
// Arrange
using var memoryStream = new MemoryStream();
using var writer = new StreamWriter(memoryStream);
writer.Write(@"{
""ContentRoots"": [
""D:\\path\\"",
""D:\\other\\""
],
""Root"": {
""Children"": {
""img"": {
""Children"": {
""icon.png"": {
""Asset"": {
""ContentRootIndex"": 0,
""SubPath"": ""icon.png""
}
}
}
},
""Img"": {
""Children"": {
""hero.gif"": {
""Asset"": {
""ContentRootIndex"": 1,
""SubPath"": ""hero.gif""
}
}
}
}
}
}
}");
var first = new Mock<IFileProvider>();
first.Setup(s => s.GetFileInfo("icon.png")).Returns(new TestFileInfo() { Name = "icon.png", Exists = true });
var second = new Mock<IFileProvider>();
second.Setup(s => s.GetFileInfo("hero.gif")).Returns(new TestFileInfo() { Name = "hero.gif", Exists = true });
writer.Flush();
memoryStream.Seek(0, SeekOrigin.Begin);
var manifest = ManifestStaticWebAssetFileProvider.StaticWebAssetManifest.Parse(memoryStream);
var comparer = ManifestStaticWebAssetFileProvider.StaticWebAssetManifest.PathComparer;
var provider = new ManifestStaticWebAssetFileProvider(
manifest,
contentRoot => contentRoot switch
{
"D:\\path\\" => first.Object,
"D:\\other\\" => second.Object,
_ => throw new InvalidOperationException("Unknown provider")
});
// Act
var file = provider.GetFileInfo(path);
// Assert
Assert.Equal(exists, file.Exists);
}
[Theory]
[InlineData("/img/Subdir/icon.png", true)]
[InlineData("/Img/subdir/hero.gif", true)]
// Note that we've changed the casing of the second segment
[InlineData("/img/subdir/icon.png", false)]
[InlineData("/Img/Subdir/hero.gif", false)]
public void ParseWorksWithMergesNodesRecursively(string path, bool exists)
{
// Arrange
exists = exists | OperatingSystem.IsWindows();
var firstLevelCount = OperatingSystem.IsWindows() ? 1 : 2;
using var memoryStream = new MemoryStream();
using var writer = new StreamWriter(memoryStream);
writer.Write(@"{
""ContentRoots"": [
""D:\\path\\"",
""D:\\other\\""
],
""Root"": {
""Children"": {
""img"": {
""Children"": {
""Subdir"": {
""Children"": {
""icon.png"": {
""Asset"": {
""ContentRootIndex"": 0,
""SubPath"": ""icon.png""
}
}
}
}
}
},
""Img"": {
""Children"": {
""subdir"": {
""Children"": {
""hero.gif"": {
""Asset"": {
""ContentRootIndex"": 1,
""SubPath"": ""hero.gif""
}
}
}
}
}
}
}
}
}");
var first = new Mock<IFileProvider>();
first.Setup(s => s.GetFileInfo("icon.png")).Returns(new TestFileInfo() { Name = "icon.png", Exists = true });
var second = new Mock<IFileProvider>();
second.Setup(s => s.GetFileInfo("hero.gif")).Returns(new TestFileInfo() { Name = "hero.gif", Exists = true });
writer.Flush();
memoryStream.Seek(0, SeekOrigin.Begin);
var manifest = ManifestStaticWebAssetFileProvider.StaticWebAssetManifest.Parse(memoryStream);
var comparer = ManifestStaticWebAssetFileProvider.StaticWebAssetManifest.PathComparer;
var provider = new ManifestStaticWebAssetFileProvider(
manifest,
contentRoot => contentRoot switch
{
"D:\\path\\" => first.Object,
"D:\\other\\" => second.Object,
_ => throw new InvalidOperationException("Unknown provider")
});
// Act
var file = provider.GetFileInfo(path);
// Assert
Assert.Equal(exists, file.Exists);
Assert.Equal(firstLevelCount, manifest.Root.Children.Count);
Assert.All(manifest.Root.Children.Values, c => Assert.Single(c.Children));
}
[Theory]
[InlineData("/img/Subdir", true)]
[InlineData("/Img/subdir/hero.gif", true)]
// Note that we've changed the casing of the second segment
[InlineData("/img/subdir", false)]
[InlineData("/Img/Subdir/hero.gif", false)]
public void ParseWorksFolderAndFileWithDiferentCasing(string path, bool exists)
{
// Arrange
exists = exists | OperatingSystem.IsWindows();
var firstLevelCount = OperatingSystem.IsWindows() ? 1 : 2;
using var memoryStream = new MemoryStream();
using var writer = new StreamWriter(memoryStream);
// img/Subdir is a file without extension
writer.Write(@"{
""ContentRoots"": [
""D:\\path\\"",
""D:\\other\\""
],
""Root"": {
""Children"": {
""img"": {
""Children"": {
""Subdir"": {
""Asset"": {
""ContentRootIndex"": 0,
""SubPath"": ""Subdir""
}
}
}
},
""Img"": {
""Children"": {
""subdir"": {
""Children"": {
""hero.gif"": {
""Asset"": {
""ContentRootIndex"": 1,
""SubPath"": ""hero.gif""
}
}
}
}
}
}
}
}
}");
var first = new Mock<IFileProvider>();
first.Setup(s => s.GetFileInfo("Subdir")).Returns(new TestFileInfo() { Name = "Subdir", Exists = true });
var second = new Mock<IFileProvider>();
second.Setup(s => s.GetFileInfo("hero.gif")).Returns(new TestFileInfo() { Name = "hero.gif", Exists = true });
writer.Flush();
memoryStream.Seek(0, SeekOrigin.Begin);
var manifest = ManifestStaticWebAssetFileProvider.StaticWebAssetManifest.Parse(memoryStream);
var comparer = ManifestStaticWebAssetFileProvider.StaticWebAssetManifest.PathComparer;
var provider = new ManifestStaticWebAssetFileProvider(
manifest,
contentRoot => contentRoot switch
{
"D:\\path\\" => first.Object,
"D:\\other\\" => second.Object,
_ => throw new InvalidOperationException("Unknown provider")
});
// Act
var file = provider.GetFileInfo(path);
// Assert
Assert.Equal(exists, file.Exists);
Assert.Equal(firstLevelCount, manifest.Root.Children.Count);
Assert.All(manifest.Root.Children.Values, c => Assert.Single(c.Children));
}
[Fact]
public void CanFindFileListedOnTheManifest()
{
......
......@@ -3,7 +3,6 @@
using System.Collections;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.Extensions.FileProviders;
......@@ -367,9 +366,58 @@ namespace Microsoft.AspNetCore.StaticWebAssets
{
public override Dictionary<string, StaticWebAssetNode> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return new Dictionary<string, StaticWebAssetNode>(
JsonSerializer.Deserialize<IDictionary<string, StaticWebAssetNode>>(ref reader, options)!,
StaticWebAssetManifest.PathComparer);
var parsed = JsonSerializer.Deserialize<IDictionary<string, StaticWebAssetNode>>(ref reader, options)!;
var result = new Dictionary<string, StaticWebAssetNode>(StaticWebAssetManifest.PathComparer);
MergeChildren(parsed, result);
return result;
static void MergeChildren(
IDictionary<string, StaticWebAssetNode> newChildren,
IDictionary<string, StaticWebAssetNode> existing)
{
foreach (var (key, value) in newChildren)
{
if (!existing.TryGetValue(key, out var existingNode))
{
existing.Add(key, value);
}
else
{
if (value.Patterns != null)
{
if (existingNode.Patterns == null)
{
existingNode.Patterns = value.Patterns;
}
else
{
if (value.Patterns.Length > 0)
{
var newList = new StaticWebAssetPattern[existingNode.Patterns.Length + value.Patterns.Length];
existingNode.Patterns.CopyTo(newList, 0);
value.Patterns.CopyTo(newList, existingNode.Patterns.Length);
existingNode.Patterns = newList;
}
}
}
if (value.Children != null)
{
if (existingNode.Children == null)
{
existingNode.Children = value.Children;
}
else
{
if (value.Children.Count > 0)
{
MergeChildren(value.Children, existingNode.Children);
}
}
}
}
}
}
}
public override void Write(Utf8JsonWriter writer, Dictionary<string, StaticWebAssetNode> value, JsonSerializerOptions options)
......
0% 加载中 .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册