Open KalleOlaviNiemitalo opened 11 months ago
Tagging subscribers to this area: @dotnet/ncl See info in area-owners.md if you want to be subscribed.
Author: | KalleOlaviNiemitalo |
---|---|
Assignees: | - |
Labels: | `area-System.Net` |
Milestone: | - |
It boils down to the fact, that empty relative Uri will keep query and fragment part of the base Uri when combining. (@MihaZupan will take a look at the spec if this is correct behavior)
While query and fragment of the base are ignored in MakeRelativeUri
call, which makes sense.
In isolation both cases make sense to me, which would make it By Design. Let's see what @MihaZupan finds in the RFC.
Here's C# code:
static void Main()
{
Uri baseUri = new Uri("http://localhost/alpha?beta#gamma");
Uri targetUri = new Uri("http://localhost/alpha");
string relative1 = "";
string relative2 = "#hello";
string relative3 = "?world";
PrintRelative(baseUri, targetUri);
PrintCombined(baseUri, relative1);
PrintCombined(baseUri, relative2);
PrintCombined(baseUri, relative3);
}
static void PrintRelative(Uri baseUri, Uri targetUri)
{
Console.WriteLine($"Base: {baseUri}");
Console.WriteLine($"Target: {targetUri}");
Uri relativeUri = baseUri.MakeRelativeUri(targetUri);
Console.WriteLine($"Relative: {relativeUri}");
Console.WriteLine();
}
static void PrintCombined(Uri baseUri, string relativeUri)
{
Console.WriteLine($"Base: {baseUri}");
Console.WriteLine($"Relative: {relativeUri}");
Uri combinedUri = new Uri(baseUri, relativeUri);
Console.WriteLine($"Combined: {combinedUri}");
Console.WriteLine();
}
Output:
Base: http://localhost/alpha?beta#gamma
Target: http://localhost/alpha
Relative:
Base: http://localhost/alpha?beta#gamma
Relative:
Combined: http://localhost/alpha?beta#gamma
Base: http://localhost/alpha?beta#gamma
Relative: #hello
Combined: http://localhost/alpha?beta#hello
Base: http://localhost/alpha?beta#gamma
Relative: ?world
Combined: http://localhost/alpha?world
AFAIK, the Uri(Uri, Uri) constructor complies with IETF RFC 3986 section 5.2 Relative Resolution. That RFC does not define an algorithm for Uri.MakeRelativeUri to use. Anyway, Uri.MakeRelativeUri is documented as returning "a relative Uri that, when appended to the current URI instance, yields uri
". If "appended" means the Uri(Uri, Uri) constructor, then Uri.MakeRelativeUri does not work as documented.
Anyway, Uri.MakeRelativeUri is documented as returning "a relative Uri that, when appended to the current URI instance, yields
uri
"
Which it does, except the case of empty relative path and base Uri having query or fragment. I am not surprised such corner case was overlooked in the documentation.
The way I read something like
baseUri.MakeRelativeUri(targetUri);
is along the lines of "what should I put into an href so that a browser currently at {baseUri} will navigate to {targetUri}". That would also imply that the following holds:
targetUri == new Uri(baseUri, baseUri.MakeRelativeUri(targetUri)))
It looks like this breaks down if paths are the same
and base has a query
and target doesn't have a query
.
With that, I would expect that MakeRelativeUri
would take the merging behavior into account, such that the following expected behavior would make sense to me
Base Target Relative
---- ------ --------
Expected:
http://localhost/alpha?beta#gamma http://localhost/alpha#delta alpha#delta
Actual:
http://localhost/alpha?beta#gamma http://localhost/alpha#delta #delta
The other case that was pointed out is fine IMO, given that both will produce the same result when merged.
Expected:
http://localhost/alpha?beta#gamma http://localhost/alpha?beta#delta #delta
Actual (but still fine):
http://localhost/alpha?beta#gamma http://localhost/alpha?beta#delta ?beta#delta
I'm inclined to say that this is a bug, though I don't have a good sense of how impactful of a breaking change fixing it would be.
@MihaZupan I don't understand why in your first case you expect relative to be "alpha#delta". I would expect it to be "#delta", which is actual behavior. What am I missing?
Triage: Even if we decide something is a bug, we should keep in mind that diverging from .NET Framework might not be desirable for some SW. Unless it is really problematic, we should likely Won't Fix it. Moving to 9.0 to make a decision there.
Description
Uri.MakeRelativeUri returns the wrong relative URI if the base URI has a query and the argument URI has the same scheme, authority, and path but no query. When the base URI and the relative URI are combined using the Uri(Uri, Uri) constructor, the result is not equal to the original argument URI.
Reproduction Steps
Expected behavior
Actual behavior
Regression?
No, I get the same incorrect result on .NET Framework.
Known Workarounds
No response
Configuration
PowerShell 7.3.5 using .NET 7.0.8 on Windows 10 version 22H2 x64.
Other information
No response