Skip to content

Commit

Permalink
Make URI Unc path recognition more consistent across platforms (#35050)
Browse files Browse the repository at this point in the history
* Recognize '//' Uris as UNC on Unix

* Add test for Unc equality

* Add test for short Unc paths

* Add test for UNC implicit-explicit roundtrip

* Merge two if statements into one
  • Loading branch information
MihaZupan authored Jun 1, 2020
1 parent 7372934 commit 7493302
Show file tree
Hide file tree
Showing 3 changed files with 145 additions and 1 deletion.
4 changes: 3 additions & 1 deletion src/libraries/System.Private.Uri/src/System/Uri.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3518,7 +3518,9 @@ private static unsafe int ParseSchemeCheckImplicitFile(char* uriString, int leng
}

// Unix: Unix path?
if (!IsWindowsSystem && idx < length && uriString[idx] == '/')
// A path starting with 2 / or \ (including mixed) is treated as UNC and will be matched below
if (!IsWindowsSystem && idx < length && uriString[idx] == '/' &&
(idx + 1 == length || (uriString[idx + 1] != '/' && uriString[idx + 1] != '\\')))
{
flags |= (Flags.UnixPath | Flags.ImplicitFile | Flags.AuthorityFound);
syntax = UriParser.UnixFileUri;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
<Compile Include="IriEncodingDecodingTests.cs" />
<Compile Include="IriRelativeFileResolutionTest.cs" />
<Compile Include="IriTest.cs" />
<Compile Include="UncTest.cs" />
<Compile Include="UriBuilderParameterTest.cs" />
<Compile Include="UriBuilderRefreshTest.cs" />
<Compile Include="UriBuilderTests.cs" />
Expand Down
141 changes: 141 additions & 0 deletions src/libraries/System.Private.Uri/tests/FunctionalTests/UncTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using Xunit;

namespace System.PrivateUri.Tests
{
public static class UncTest
{
[Fact]
public static void Uri_WithTwoForwardSlashes_IsUnc()
{
// Will be recognized as UNC - even on Unix
Assert.True(new Uri("//foo").IsUnc);
}

[Theory]
// / and \ can be mixed and will be recognized the same on all platforms
[InlineData(@"\\host")]
[InlineData(@"//host")]
[InlineData(@"\/host")]
[InlineData(@"/\host")]
public static void Uri_RecognizesUncPaths(string uriString)
{
var uri = new Uri(uriString);
Assert.True(uri.IsUnc);
Assert.Equal("host", uri.Host);
Assert.Equal("file://host/", uri.AbsoluteUri);
Assert.Equal(@"\\host", uri.LocalPath);

// Same for explicit file form
uri = new Uri("file://" + uriString);
Assert.True(uri.IsUnc);
Assert.Equal("host", uri.Host);
Assert.Equal("file://host/", uri.AbsoluteUri);
Assert.Equal(@"\\host", uri.LocalPath);
}

[Theory]
[InlineData(@"\\host/share")]
[InlineData(@"\\host\share")]
[InlineData(@"//host/share")]
[InlineData(@"//host\share")]
[InlineData(@"\/host/share")]
[InlineData(@"\/host\share")]
[InlineData(@"/\host/share")]
[InlineData(@"/\host\share")]
public static void Uri_UncLocalPath_ConvertsForwardSlashes(string uriString)
{
var uri = new Uri(uriString);
Assert.True(uri.IsUnc);
Assert.Equal("host", uri.Host);
Assert.Equal("file://host/share", uri.AbsoluteUri);
Assert.Equal(@"\\host\share", uri.LocalPath);
Assert.Equal(@"/share", uri.AbsolutePath);

// Same for explicit file form
uri = new Uri("file://" + uriString);
Assert.True(uri.IsUnc);
Assert.Equal("host", uri.Host);
Assert.Equal("file://host/share", uri.AbsoluteUri);
Assert.Equal(@"\\host\share", uri.LocalPath);
Assert.Equal(@"/share", uri.AbsolutePath);
}

[Theory]
[InlineData(@"\\host\..")]
[InlineData(@"\\host/..")]
[InlineData(@"//host\..")]
[InlineData(@"//host/..")]
[InlineData(@"\\host\foo\..\..")]
[InlineData(@"//host\foo\..\..")]
[InlineData(@"\/host\foo\..\..")]
[InlineData(@"/\host\foo\..\..")]
[InlineData(@"\\host/foo/../..")]
[InlineData(@"\\host/foo/../../")]
public static void Uri_UncPathEscaping_FixesOnHost(string uriString)
{
Uri uri = new Uri(uriString);
Assert.True(uri.IsUnc);
Assert.Equal("host", uri.Host);
Assert.Equal(@"\\host\", uri.LocalPath);
Assert.Equal("/", uri.AbsolutePath);

// Same for explicit file form
uri = new Uri("file://" + uriString);
Assert.True(uri.IsUnc);
Assert.Equal("host", uri.Host);
Assert.Equal(@"\\host\", uri.LocalPath);
Assert.Equal("/", uri.AbsolutePath);
}

[Theory]
[InlineData(@"\\host")]
[InlineData(@"//host")]
[InlineData(@"\/host")]
[InlineData(@"/\host")]
public static void Uri_UncPathEquality_IgnoresCase(string uncPath)
{
Assert.Equal(new Uri(uncPath), new Uri(uncPath.ToUpper()));
}

[Theory]
[InlineData(@"\\")]
[InlineData(@"//")]
[InlineData(@"\/")]
[InlineData(@"/\")]
public static void Uri_ShortUnc_NotRecognizedAsAbsolute(string uriString)
{
Assert.False(Uri.TryCreate(uriString, UriKind.Absolute, out _));

Assert.Throws<UriFormatException>(() => new Uri(uriString));

Assert.True(Uri.TryCreate(uriString, UriKind.Relative, out Uri uri));

// Invalid as it's a relative Uri
Assert.Throws<InvalidOperationException>(() => uri.IsUnc);
}

[Theory]
[InlineData(@"\\host/share")]
[InlineData(@"\\host\share")]
[InlineData(@"//host/share")]
[InlineData(@"//host\share")]
[InlineData(@"\/host/share")]
[InlineData(@"\/host\share")]
[InlineData(@"/\host/share")]
[InlineData(@"/\host\share")]
public static void Uri_ImplicitToExplicitForm_StillRecognizedAsUnc(string uriString)
{
var uri = new Uri(uriString);
Assert.True(uri.IsUnc);
Assert.Equal("file://host/share", uri.AbsoluteUri);

uri = new Uri(uri.AbsoluteUri);
Assert.True(uri.IsUnc);
Assert.Equal("file://host/share", uri.AbsoluteUri);
}
}
}

0 comments on commit 7493302

Please sign in to comment.