Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 11 additions & 41 deletions src/libraries/System.Private.Uri/src/System/Uri.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3270,8 +3270,10 @@ private unsafe void ParseRemaining()
// 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();
Comment thread
ManickaP marked this conversation as 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 +3316,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);
Comment thread
ManickaP marked this conversation as resolved.

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

int offset = origIdx;

if (origIdx < _originalUnicodeString.Length && _originalUnicodeString[origIdx] == '?')
Expand All @@ -3460,19 +3452,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 +3501,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
60 changes: 60 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,65 @@ 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)
Comment thread
ManickaP marked this conversation as 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/" };
Comment thread
MihaZupan marked this conversation as 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);

var uri2 = new Uri(uri.AbsoluteUri);

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