Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.
/ corefx Public archive

Commit

Permalink
Support XmlKnownDtds in XmlPreloadedResolver (#36971)
Browse files Browse the repository at this point in the history
* Support XmlKnownDtds in XmlPreloadedResolver

The XmlPreloadedResolver class purports to provide certain well-known DTDs - its constructor accepts a value from the XmlKnownDtds enumeration, and if you specify either of the supported DTD sets (sXHTML 1.0, and RSS 0.91) it is meant to make the relevant DTDs available.

In desktop .NET, this works as advertised. In .NET Core, it does not, for the reasons described in https://github.com/dotnet/corefx/issues/36929

This was missed because there are no tests to verify that the XmlPreloadedResolver makes the relevant DTDs available when asked to. (In fact, the tests that existed seemed to be based on a misunderstanding of how this class works.) This adds tests that verify that the known DTDs are provided when requested. And it reinstates the relevant EmbeddedResource entries required to enable the functionality. (The code for this has been in .NET Core's XmlPreloadedResolver all along. It was only the absence of the necessary embedded resources preventing it from working.)

* Completed refactoring that was half-complete

In the previous commit I introduced the NormalizeContent method to avoid duplication of various calls to string.Replace but I left in one such call. This replaces it with a call to the new NormalizeContent, as intended.

* Replace \ with / in test paths

Tests failed on Linux with this:

Could not find a part of the path '/root/helix/work/workitem/Utils\\DTDs\\/XHTML10\

I'm hoping that replacing the backslashes with forward slashes will get the tests passing.

* Replaced fixed / and \ with Path.Combine

Although using / everywhere did seem to work, the preferred way to generate paths is Path.Combine.

Note that this still uses string concatenation for the system ID URI because that's not a path, it's just an identifier.
  • Loading branch information
idg10 authored and krwq committed Apr 19, 2019
1 parent 069f540 commit eb63183
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 11 deletions.
5 changes: 2 additions & 3 deletions src/System.Private.Xml/src/System.Private.Xml.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -535,8 +535,7 @@
</ItemGroup>
<!-- Embedded DTD files -->
<ItemGroup>
<!-- TODO: check why was this embedded on desktop - no test failures observed after commenting this out -->
<!--<EmbeddedResource Include="Utils\DTDs\XHTML10\no_comments\xhtml1-strict.dtd">
<EmbeddedResource Include="Utils\DTDs\XHTML10\no_comments\xhtml1-strict.dtd">
<LogicalName>xhtml1-strict.dtd</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Utils\DTDs\XHTML10\no_comments\xhtml1-transitional.dtd">
Expand All @@ -556,7 +555,7 @@
</EmbeddedResource>
<EmbeddedResource Include="Utils\DTDs\RSS091\no_comments\rss-0.91.dtd">
<LogicalName>rss-0.91.dtd</LogicalName>
</EmbeddedResource>-->
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="System\Xml\XmlCharType.bin">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,34 @@
<Configurations>netstandard-Debug;netstandard-Release</Configurations>
</PropertyGroup>
<ItemGroup>
<None Include="$(SourceDir)\System.Private.Xml\src\Utils\DTDs\XHTML10\no_comments\xhtml1-frameset.dtd">
<Link>Utils\DTDs\XHTML10\no_comments\xhtml1-frameset.dtd</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="$(SourceDir)\System.Private.Xml\src\Utils\DTDs\XHTML10\no_comments\xhtml1-strict.dtd">
<Link>Utils\DTDs\XHTML10\no_comments\xhtml1-strict.dtd</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="$(SourceDir)\System.Private.Xml\src\Utils\DTDs\XHTML10\no_comments\xhtml1-transitional.dtd">
<Link>Utils\DTDs\XHTML10\no_comments\xhtml1-transitional.dtd</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="$(SourceDir)\System.Private.Xml\src\Utils\DTDs\XHTML10\no_comments\xhtml-lat1.ent">
<Link>Utils\DTDs\XHTML10\no_comments\xhtml-lat1.ent</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="$(SourceDir)\System.Private.Xml\src\Utils\DTDs\XHTML10\no_comments\xhtml-special.ent">
<Link>Utils\DTDs\XHTML10\no_comments\xhtml-special.ent</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="$(SourceDir)\System.Private.Xml\src\Utils\DTDs\XHTML10\no_comments\xhtml-symbol.ent">
<Link>Utils\DTDs\XHTML10\no_comments\xhtml-symbol.ent</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="$(SourceDir)\System.Private.Xml\src\Utils\DTDs\RSS091\no_comments\rss-0.91.dtd">
<Link>Utils\DTDs\RSS091\no_comments\rss-0.91.dtd</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<Compile Include="DummyStream.cs" />
<Compile Include="XmlPreloadedResolverAddRemoveTests.cs" />
<Compile Include="XmlPreloadedResolverConstructionTests.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
// See the LICENSE file in the project root for more information.

using System.Xml.Resolvers;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using Xunit;
using System.Threading.Tasks;
Expand Down Expand Up @@ -45,24 +47,48 @@ public void XmlResolverGetEntityWithInvalidData()
}

[Fact]
public void XmlResolverGetEntityWithValidData()
public void XmlResolverGetEntityWithValidUserSuppliedData()
{
var uri = new Uri("-//W3C//DTD XHTML 1.0 Transitional//EN", UriKind.RelativeOrAbsolute);
var uri = new Uri("-//W3C//DTD FAKE 1.0 Not Real//EN", UriKind.RelativeOrAbsolute);
XmlPreloadedResolver xmlResolver =
GetResolverWithStringData(XmlKnownDtds.Xhtml10, "Sample String Data", uri);
Stream streamResult = xmlResolver.GetEntity(uri, null, null) as Stream;
Assert.NotNull(streamResult);
byte[] data = new byte[streamResult.Length];
streamResult.Read(data, 0, Convert.ToInt32(streamResult.Length));
Assert.Equal("Sample String Data", Encoding.ASCII.GetString(data).Replace("\0", ""));
Assert.Equal("Sample String Data", NormalizeContent(Encoding.ASCII.GetString(data)));

uri = new Uri("-//W3C//DTD XHTML 1.0 Transitional//EN", UriKind.RelativeOrAbsolute);
uri = new Uri("-//W3C//DTD FAKE 1.0 Not Real//EN", UriKind.RelativeOrAbsolute);
xmlResolver = GetResolverWithStringData(XmlKnownDtds.Xhtml10, "Sample String Data", uri);
TextReader textResult = xmlResolver.GetEntity(uri, null, typeof(TextReader)) as TextReader;
Assert.NotNull(textResult);
Assert.Equal("Sample String Data", textResult.ReadLine());
}

[Fact]
public void XmlResolverGetKnownEntity()
{
var xmlResolver = new XmlPreloadedResolver(XmlKnownDtds.All);
foreach (var dtdInfo in GetKnownDtds())
{
string expectedContent = NormalizeContent(File.ReadAllText(dtdInfo.SourcePath));

var uri = new Uri(dtdInfo.PublicId, UriKind.RelativeOrAbsolute);
Stream streamResult = xmlResolver.GetEntity(uri, null, null) as Stream;
Assert.NotNull(streamResult);
byte[] data = new byte[streamResult.Length];
streamResult.Read(data, 0, Convert.ToInt32(streamResult.Length));
Assert.Equal(expectedContent, NormalizeContent(Encoding.ASCII.GetString(data)));

uri = new Uri(dtdInfo.SystemId, UriKind.RelativeOrAbsolute);
streamResult = xmlResolver.GetEntity(uri, null, null) as Stream;
Assert.NotNull(streamResult);
data = new byte[streamResult.Length];
streamResult.Read(data, 0, Convert.ToInt32(streamResult.Length));
Assert.Equal(expectedContent, NormalizeContent(Encoding.ASCII.GetString(data)));
}
}

[Fact]
public void XmlResolverGetEntityAsyncWithInvalidData()
{
Expand All @@ -81,17 +107,71 @@ public void XmlResolverGetEntityAsyncWithInvalidData()
}

[Fact]
public void XmlResolverGetEntityAsyncWithValidData()
public void XmlResolverGetEntityAsyncWithValidUserSuppliedData()
{
byte[] inpData = Encoding.ASCII.GetBytes("hello world");
var xmlResolver = new XmlPreloadedResolver(XmlKnownDtds.Xhtml10);
xmlResolver.Add(new Uri("-//W3C//ENTITIES Latin 1 for XHTML//EN", UriKind.RelativeOrAbsolute), inpData);
Task<object> output = xmlResolver.GetEntityAsync(new Uri("-//W3C//ENTITIES Latin 1 for XHTML//EN",
xmlResolver.Add(new Uri("-//W3C//DTD FAKE 1.0 Not Real//EN", UriKind.RelativeOrAbsolute), inpData);
Task<object> output = xmlResolver.GetEntityAsync(new Uri("-//W3C//DTD FAKE 1.0 Not Real//EN",
UriKind.RelativeOrAbsolute), null, typeof(Stream));
var result = new byte[inpData.Length];
(output.Result as Stream).Read(result, 0, result.Length);
Assert.Equal(inpData, result);
}


[Fact]
public async Task XmlResolverGetKnownEntityAsync()
{
var xmlResolver = new XmlPreloadedResolver(XmlKnownDtds.All);
foreach (var dtdInfo in GetKnownDtds())
{
string expectedContent = NormalizeContent(File.ReadAllText(dtdInfo.SourcePath));

var uri = new Uri(dtdInfo.PublicId, UriKind.RelativeOrAbsolute);
Stream streamResult = await xmlResolver.GetEntityAsync(uri, null, null) as Stream;
Assert.NotNull(streamResult);
byte[] data = new byte[streamResult.Length];
streamResult.Read(data, 0, Convert.ToInt32(streamResult.Length));
Assert.Equal(expectedContent, NormalizeContent(Encoding.ASCII.GetString(data)));

uri = new Uri(dtdInfo.SystemId, UriKind.RelativeOrAbsolute);
streamResult = await xmlResolver.GetEntityAsync(uri, null, null) as Stream;
Assert.NotNull(streamResult);
data = new byte[streamResult.Length];
streamResult.Read(data, 0, Convert.ToInt32(streamResult.Length));
Assert.Equal(expectedContent, NormalizeContent(Encoding.ASCII.GetString(data)));
}
}

private static IEnumerable<(string PublicId, string SystemId, string SourcePath)> GetKnownDtds()
{
var xhtmlDtds = new (string PublicId, string RelativeId)[]
{
("-//W3C//DTD XHTML 1.0 Strict//EN", "xhtml1-strict.dtd"),
("-//W3C//DTD XHTML 1.0 Transitional//EN", "xhtml1-transitional.dtd"),
("-//W3C//DTD XHTML 1.0 Frameset//EN", "xhtml1-frameset.dtd"),
("-//W3C//ENTITIES Latin 1 for XHTML//EN", "xhtml-lat1.ent"),
("-//W3C//ENTITIES Symbols for XHTML//EN", "xhtml-symbol.ent"),
("-//W3C//ENTITIES Special for XHTML//EN", "xhtml-special.ent"),
};
var rssDtds = new (string PublicId, string RelativeId)[]
{
("-//Netscape Communications//DTD RSS 0.91//EN", "rss-0.91.dtd"),
};

string dtdFolderRoot = Path.Combine("Utils", "DTDs");

return Enumerable.Concat(
GetKnownDtds(xhtmlDtds, "http://www.w3.org/TR/xhtml1/DTD/", Path.Combine(dtdFolderRoot, "XHTML10", "no_comments")),
GetKnownDtds(rssDtds, "http://my.netscape.com/publish/formats/", Path.Combine(dtdFolderRoot, "RSS091", "no_comments")));

IEnumerable<(string PublicId, string SystemId, string SourcePath)> GetKnownDtds(
IEnumerable<(string PublicId, string RelativeId)> ids,
string systemUrlPrefix,
string pathPrefix) =>
ids.Select(x => (x.PublicId, systemUrlPrefix + x.RelativeId, Path.Combine(pathPrefix, x.RelativeId)));
}

private static string NormalizeContent(string content) => content.Replace("\0", "").Replace("\r\n", "\n");
}
}

0 comments on commit eb63183

Please sign in to comment.