diff --git a/src/libraries/System.Private.Uri/src/System/UriExt.cs b/src/libraries/System.Private.Uri/src/System/UriExt.cs index ee35d01e96232e..5afdef39335386 100644 --- a/src/libraries/System.Private.Uri/src/System/UriExt.cs +++ b/src/libraries/System.Private.Uri/src/System/UriExt.cs @@ -1053,30 +1053,42 @@ private void CreateThisFromUri(Uri otherUri) { DebugAssertInCtor(); - // Clone the other URI but develop own UriInfo member - _info = null!; - _flags = otherUri._flags; - if (InFact(Flags.MinimalUriInfoSet)) + + if (InFact(Flags.AllUriInfoSet)) + { + // We can share it now without mutation concern, for since AllUriInfoSet it is immutable. + _info = otherUri._info; + } + else { - _flags &= ~(Flags.MinimalUriInfoSet | Flags.AllUriInfoSet | Flags.IndexMask); - // Port / Path offset - int portIndex = otherUri._info.Offset.Path; - if (InFact(Flags.NotDefaultPort)) + Debug.Assert(!InFact(Flags.HasUnicode) || otherUri.IsNotAbsoluteUri); + // Clone the other URI but develop own UriInfo member + // We cannot just reference otherUri._info as this UriInfo will be mutated later + // which could be happening concurrently and in a not thread safe manner. + _info = null!; + + if (InFact(Flags.MinimalUriInfoSet)) { - // Find the start of the port. Account for non-canonical ports like :00123 - while (otherUri._string[portIndex] != ':' && portIndex > otherUri._info.Offset.Host) + _flags &= ~(Flags.MinimalUriInfoSet | Flags.AllUriInfoSet | Flags.IndexMask); + // Port / Path offset + int portIndex = otherUri._info.Offset.Path; + if (InFact(Flags.NotDefaultPort)) { - portIndex--; - } - if (otherUri._string[portIndex] != ':') - { - // Something wrong with the NotDefaultPort flag. Reset to path index - Debug.Fail("Uri failed to locate custom port at index: " + portIndex); - portIndex = otherUri._info.Offset.Path; + // Find the start of the port. Account for non-canonical ports like :00123 + while (otherUri._string[portIndex] != ':' && portIndex > otherUri._info.Offset.Host) + { + portIndex--; + } + if (otherUri._string[portIndex] != ':') + { + // Something wrong with the NotDefaultPort flag. Reset to path index + Debug.Fail("Uri failed to locate custom port at index: " + portIndex); + portIndex = otherUri._info.Offset.Path; + } } + _flags |= (Flags)portIndex; // Port or path } - _flags |= (Flags)portIndex; // Port or path } _syntax = otherUri._syntax; diff --git a/src/libraries/System.Private.Uri/tests/FunctionalTests/UriTests.cs b/src/libraries/System.Private.Uri/tests/FunctionalTests/UriTests.cs index 7b9bf9d1d4ec6f..77127e35d0b3d4 100644 --- a/src/libraries/System.Private.Uri/tests/FunctionalTests/UriTests.cs +++ b/src/libraries/System.Private.Uri/tests/FunctionalTests/UriTests.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Threading; using System.Threading.Tasks; @@ -729,6 +730,25 @@ public static void Uri_CombineUsesNewUriString() Assert.Equal(Combined, new Uri(baseUri, RelativeUriString).AbsoluteUri); } + [Theory] + [InlineData("http://bar/Testue/testImage.jpg", "http://bar/Testue/testImage.jpg", "http://bar/Testue/testImage.jpg", "bar")] + [InlineData(@"\\nas\Testue\testImage.jpg", "file://nas/Testue/testImage.jpg", "file://nas/Testue/testImage.jpg", "nas")] + // Tests that internal Uri info were properly applied during a Combine operation when URI contains non-ascii character. + [InlineData("http://bar/Test\u00fc/testImage.jpg", "http://bar/Test\u00fc/testImage.jpg", "http://bar/Test%C3%BC/testImage.jpg", "bar")] + [InlineData("\\\\nas\\Test\u00fc\\testImage.jpg", "file://nas/Test\u00fc/testImage.jpg", "file://nas/Test%C3%BC/testImage.jpg", "nas")] + public static void Uri_CombineWithAbsoluteUriResultInAbsoluteSchemaIgnoringOriginalBase(string relativeUri, string expectedUri, string expectedAbsoluteUri, string expectedHost) + { + string baseUriString = "combine-scheme://foo"; + + var baseUri = new Uri(baseUriString, UriKind.Absolute); + var uri = new Uri(relativeUri); + var resultUri = new Uri(baseUri, uri); + + Assert.Equal(expectedUri, resultUri.ToString()); + Assert.Equal(expectedAbsoluteUri, resultUri.AbsoluteUri); + Assert.Equal(expectedHost, resultUri.Host); + } + [Fact] public static void Uri_CachesIdnHost() {