Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix non-ascii absolute file path handling in Uri #36429

Merged
merged 4 commits into from
May 21, 2020
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
55 changes: 12 additions & 43 deletions src/libraries/System.Private.Uri/src/System/Uri.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3262,16 +3262,17 @@ private unsafe void ParseRemaining()
// For iri parsing if we found unicode the idx has offset into _originalUnicodeString..
// so restart parsing from there and make _info.Offset.Path as _string.Length

idx = _info.Offset.Path;
origIdx = _info.Offset.Path;
idx = origIdx = _info.Offset.Path;
MihaZupan marked this conversation as resolved.
Show resolved Hide resolved

//Some uris do not have a query
// When '?' is passed as delimiter, then it's special case
// so both '?' and '#' will work as delimiters
if (buildIriStringFromPath)
{
// Dos paths have no host. Other schemes cleared/set _string with host information in PrivateParseMinimal.
if (IsDosPath)
DebugAssertInCtor();
ManickaP marked this conversation as resolved.
Show resolved Hide resolved

// Dos/Unix paths have no host. Other schemes cleared/set _string with host information in PrivateParseMinimal.
if (IsFile && !IsUncPath)
{
if (IsImplicitFile)
{
Expand Down Expand Up @@ -3314,19 +3315,7 @@ private unsafe void ParseRemaining()
origIdx = index == -1 ? _originalUnicodeString.Length : (index + origIdx);
}

// Correctly escape unescape
string escapedPath = EscapeUnescapeIri(_originalUnicodeString, offset, origIdx, UriComponents.Path);

// Normalize path
try
{
_string += escapedPath;
}
catch (ArgumentException)
{
UriFormatException e = GetException(ParsingError.BadFormat)!;
throw e;
}
_string += EscapeUnescapeIri(_originalUnicodeString, offset, origIdx, UriComponents.Path);
ManickaP marked this conversation as resolved.
Show resolved Hide resolved

if (_string.Length > ushort.MaxValue)
{
Expand Down Expand Up @@ -3445,6 +3434,8 @@ private unsafe void ParseRemaining()
//
if (buildIriStringFromPath)
{
DebugAssertInCtor();

int offset = origIdx;

if (origIdx < _originalUnicodeString.Length && _originalUnicodeString[origIdx] == '?')
Expand All @@ -3460,19 +3451,7 @@ private unsafe void ParseRemaining()
origIdx = _originalUnicodeString.Length;
}

// Correctly escape unescape
string escapedPath = EscapeUnescapeIri(_originalUnicodeString, offset, origIdx, UriComponents.Query);

// Normalize path
try
{
_string += escapedPath;
}
catch (ArgumentException)
{
UriFormatException e = GetException(ParsingError.BadFormat)!;
throw e;
}
_string += EscapeUnescapeIri(_originalUnicodeString, offset, origIdx, UriComponents.Query);

if (_string.Length > ushort.MaxValue)
{
Expand Down Expand Up @@ -3521,25 +3500,15 @@ private unsafe void ParseRemaining()
//
if (buildIriStringFromPath)
{
DebugAssertInCtor();

int offset = origIdx;

if (origIdx < _originalUnicodeString.Length && _originalUnicodeString[origIdx] == '#')
{
origIdx = _originalUnicodeString.Length;

// Correctly escape unescape
string escapedPath = EscapeUnescapeIri(_originalUnicodeString, offset, origIdx, UriComponents.Fragment);

// Normalize path
try
{
_string += escapedPath;
}
catch (ArgumentException)
{
UriFormatException e = GetException(ParsingError.BadFormat)!;
throw e;
}
_string += EscapeUnescapeIri(_originalUnicodeString, offset, origIdx, UriComponents.Fragment);

if (_string.Length > ushort.MaxValue)
{
Expand Down
53 changes: 53 additions & 0 deletions src/libraries/System.Private.Uri/tests/FunctionalTests/UriTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -738,5 +738,58 @@ public static void Uri_DoesNotLockOnString()
Assert.True(Monitor.TryEnter(uriString, TimeSpan.FromSeconds(10)));
Assert.False(timedOut);
}

public static IEnumerable<object[]> FilePathHandlesNonAscii_TestData()
{
if (PlatformDetection.IsNotWindows)
ManickaP marked this conversation as resolved.
Show resolved Hide resolved
{
// Unix absolute file path
yield return new object[] { "/\u00FCri/", "file:///\u00FCri/", "/%C3%BCri/", "file:///%C3%BCri/", "/\u00FCri/" };
yield return new object[] { "/a/b\uD83D\uDE1F/Foo.cs", "file:///a/b\uD83D\uDE1F/Foo.cs", "/a/b%F0%9F%98%9F/Foo.cs", "file:///a/b%F0%9F%98%9F/Foo.cs", "/a/b\uD83D\uDE1F/Foo.cs" };
}

// Absolute fie path
yield return new object[] { "file:///\u00FCri/", "file:///\u00FCri/", "/%C3%BCri/", "file:///%C3%BCri/", "/\u00FCri/" };
yield return new object[] { "file:///a/b\uD83D\uDE1F/Foo.cs", "file:///a/b\uD83D\uDE1F/Foo.cs", "/a/b%F0%9F%98%9F/Foo.cs", "file:///a/b%F0%9F%98%9F/Foo.cs", "/a/b\uD83D\uDE1F/Foo.cs" };

// DOS
yield return new object[] { "file://C:/\u00FCri/", "file:///C:/\u00FCri/", "C:/%C3%BCri/", "file:///C:/%C3%BCri/", "C:\\\u00FCri\\" };
yield return new object[] { "file:///C:/\u00FCri/", "file:///C:/\u00FCri/", "C:/%C3%BCri/", "file:///C:/%C3%BCri/", "C:\\\u00FCri\\" };
yield return new object[] { "C:/\u00FCri/", "file:///C:/\u00FCri/", "C:/%C3%BCri/", "file:///C:/%C3%BCri/", "C:\\\u00FCri\\" };

// UNC
yield return new object[] { "\\\\\u00FCri/", "file://\u00FCri/", "/", "file://\u00FCri/", "\\\\\u00FCri\\" };
yield return new object[] { "file://\u00FCri/", "file://\u00FCri/", "/", "file://\u00FCri/", "\\\\\u00FCri\\" };

// ? and # handling
if (PlatformDetection.IsWindows)
{
yield return new object[] { "file:///a/?b/c\u00FC/", "file:///a/?b/c\u00FC/", "/a/", "file:///a/?b/c%C3%BC/", "/a/" };
yield return new object[] { "file:///a/#b/c\u00FC/", "file:///a/#b/c\u00FC/", "/a/", "file:///a/#b/c%C3%BC/", "/a/" };
yield return new object[] { "file:///a/?b/#c/d\u00FC/", "file:///a/?b/#c/d\u00FC/", "/a/", "file:///a/?b/#c/d%C3%BC/", "/a/" };
}
else
{
yield return new object[] { "/a/?b/c\u00FC/", "file:///a/%3Fb/c\u00FC/", "/a/%3Fb/c%C3%BC/", "file:///a/%3Fb/c%C3%BC/", "/a/?b/c\u00FC/" };
yield return new object[] { "/a/#b/c\u00FC/", "file:///a/%23b/c\u00FC/", "/a/%23b/c%C3%BC/", "file:///a/%23b/c%C3%BC/", "/a/#b/c\u00FC/" };
yield return new object[] { "/a/?b/#c/d\u00FC/", "file:///a/%3Fb/%23c/d\u00FC/", "/a/%3Fb/%23c/d%C3%BC/", "file:///a/%3Fb/%23c/d%C3%BC/", "/a/?b/#c/d\u00FC/" };
MihaZupan marked this conversation as resolved.
Show resolved Hide resolved

yield return new object[] { "file:///a/?b/c\u00FC/", "file:///a/?b/c\u00FC/", "/a/", "file:///a/?b/c%C3%BC/", "/a/" };
yield return new object[] { "file:///a/#b/c\u00FC/", "file:///a/#b/c\u00FC/", "/a/", "file:///a/#b/c%C3%BC/", "/a/" };
yield return new object[] { "file:///a/?b/#c/d\u00FC/", "file:///a/?b/#c/d\u00FC/", "/a/", "file:///a/?b/#c/d%C3%BC/", "/a/" };
}
}

[Theory]
[MemberData(nameof(FilePathHandlesNonAscii_TestData))]
public static void FilePathHandlesNonAscii(string uriString, string toString, string absolutePath, string absoluteUri, string localPath)
{
var uri = new Uri(uriString);

Assert.Equal(toString, uri.ToString());
Assert.Equal(absolutePath, uri.AbsolutePath);
Assert.Equal(absoluteUri, uri.AbsoluteUri);
Assert.Equal(localPath, uri.LocalPath);
}
}
}