-
Notifications
You must be signed in to change notification settings - Fork 4.9k
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
Implement UriCreationOptions #59173
Implement UriCreationOptions #59173
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
namespace System | ||
{ | ||
/// <summary> | ||
/// Options that control how a <seealso cref="Uri"/> is created and behaves. | ||
/// </summary> | ||
public struct UriCreationOptions | ||
{ | ||
private bool _disablePathAndQueryCanonicalization; | ||
|
||
/// <summary> | ||
/// Disables validation and normalization of the Path and Query. | ||
/// No transformations of the URI past the Authority will take place. | ||
/// <see cref="Uri"/> instances created with this option do not support <see cref="Uri.Fragment"/>s. | ||
/// <see cref="Uri.GetComponents(UriComponents, UriFormat)"/> may not be used for <see cref="UriComponents.Path"/> or <see cref="UriComponents.Query"/>. | ||
/// Be aware that disabling canonicalization also means that reserved characters will not be escaped, | ||
/// which may corrupt the HTTP request and makes the application subject to request smuggling. | ||
/// Only set this option if you have ensured that the URI string is already sanitized. | ||
/// </summary> | ||
public bool DangerousDisablePathAndQueryCanonicalization | ||
{ | ||
readonly get => _disablePathAndQueryCanonicalization; | ||
set => _disablePathAndQueryCanonicalization = value; | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,7 +13,7 @@ public partial class Uri | |
// | ||
// All public ctors go through here | ||
// | ||
private void CreateThis(string? uri, bool dontEscape, UriKind uriKind) | ||
private void CreateThis(string? uri, bool dontEscape, UriKind uriKind, in UriCreationOptions creationOptions = default) | ||
{ | ||
DebugAssertInCtor(); | ||
|
||
|
@@ -31,6 +31,9 @@ private void CreateThis(string? uri, bool dontEscape, UriKind uriKind) | |
if (dontEscape) | ||
_flags |= Flags.UserEscaped; | ||
|
||
if (creationOptions.DangerousDisablePathAndQueryCanonicalization) | ||
_flags |= Flags.DisablePathAndQueryCanonicalization; | ||
|
||
ParsingError err = ParseScheme(_string, ref _flags, ref _syntax!); | ||
|
||
InitializeUri(err, uriKind, out UriFormatException? e); | ||
|
@@ -259,6 +262,26 @@ public static bool TryCreate([NotNullWhen(true)] string? uriString, UriKind uriK | |
return e is null && result != null; | ||
} | ||
|
||
/// <summary> | ||
/// Creates a new <see cref="Uri"/> using the specified <see cref="string"/> instance and <see cref="UriCreationOptions"/>. | ||
/// </summary> | ||
/// <param name="uriString">The string representation of the <see cref="Uri"/>.</param> | ||
/// <param name="creationOptions">Options that control how the <seealso cref="Uri"/> is created and behaves.</param> | ||
/// <param name="result">The constructed <see cref="Uri"/>.</param> | ||
/// <returns><see langword="true"/> if the <see cref="Uri"/> was successfully created; otherwise, <see langword="false"/>.</returns> | ||
public static bool TryCreate([NotNullWhen(true)] string? uriString, in UriCreationOptions creationOptions, [NotNullWhen(true)] out Uri? result) | ||
{ | ||
if (uriString is null) | ||
{ | ||
result = null; | ||
return false; | ||
} | ||
UriFormatException? e = null; | ||
result = CreateHelper(uriString, false, UriKind.Absolute, ref e, in creationOptions); | ||
result?.DebugSetLeftCtor(); | ||
return e is null && result != null; | ||
} | ||
|
||
public static bool TryCreate(Uri? baseUri, string? relativeUri, [NotNullWhen(true)] out Uri? result) | ||
{ | ||
if (TryCreate(relativeUri, UriKind.RelativeOrAbsolute, out Uri? relativeLink)) | ||
|
@@ -309,6 +332,16 @@ public static bool TryCreate(Uri? baseUri, Uri? relativeUri, [NotNullWhen(true)] | |
} | ||
|
||
public string GetComponents(UriComponents components, UriFormat format) | ||
{ | ||
if (DisablePathAndQueryCanonicalization && (components & (UriComponents.Path | UriComponents.Query)) != 0) | ||
{ | ||
throw new InvalidOperationException(SR.net_uri_GetComponentsCalledWhenCanonicalizationDisabled); | ||
} | ||
Comment on lines
+336
to
+339
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Offline discussion: I'm concerned that there's no way for the consumer to know if DisablePathAndQueryCanonicalization is set and avoid this exception. This method can also throw IOE for relative Uris, but there you can check IsAbsoluteUri first. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
In the current version this is unreachable for relative Uris (since There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we should consider adding a way to expose this, something like IsPathAndQueryCanonicalizationDisabled. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just to be super-clear: We can track that for 7.0, we do not consider it blocking attempt for 6.0 backport. |
||
|
||
return InternalGetComponents(components, format); | ||
} | ||
|
||
private string InternalGetComponents(UriComponents components, UriFormat format) | ||
{ | ||
if (((components & UriComponents.SerializationInfoString) != 0) && components != UriComponents.SerializationInfoString) | ||
throw new ArgumentOutOfRangeException(nameof(components), components, SR.net_uri_NotJustSerialization); | ||
|
@@ -590,7 +623,7 @@ private Uri(Flags flags, UriParser? uriParser, string uri) | |
// | ||
// a Uri.TryCreate() method goes through here. | ||
// | ||
internal static Uri? CreateHelper(string uriString, bool dontEscape, UriKind uriKind, ref UriFormatException? e) | ||
internal static Uri? CreateHelper(string uriString, bool dontEscape, UriKind uriKind, ref UriFormatException? e, in UriCreationOptions creationOptions = default) | ||
{ | ||
// if (!Enum.IsDefined(typeof(UriKind), uriKind)) -- We currently believe that Enum.IsDefined() is too slow | ||
// to be used here. | ||
|
@@ -606,6 +639,9 @@ private Uri(Flags flags, UriParser? uriParser, string uri) | |
if (dontEscape) | ||
flags |= Flags.UserEscaped; | ||
|
||
if (creationOptions.DangerousDisablePathAndQueryCanonicalization) | ||
flags |= Flags.DisablePathAndQueryCanonicalization; | ||
|
||
// We won't use User factory for these errors | ||
if (err != ParsingError.None) | ||
{ | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you please explain what
readonly get
does, and link me to associated docs? Thank youThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/struct#readonly-instance-members