-
Notifications
You must be signed in to change notification settings - Fork 527
Use enum for method rather than string compares #2294
Conversation
_httpVersion = version; | ||
|
||
Debug.Assert(RawTarget != null, "RawTarget was not set"); | ||
Debug.Assert(Method != null, "Method was not set"); | ||
Debug.Assert(((IHttpRequestFeature)this).Method != null, "Method was not set"); |
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.
Why this change?
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.
Ah, because method is an enum.
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.
To get the string version
OSX
|
3176df2
to
22fb267
Compare
Reworded commit to retrigger CI |
throw new ArgumentException(nameof(value)); | ||
} | ||
|
||
var firstChar = value[0]; |
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.
Why this implementation if its not performance sensitive? Just use the http abstractions methods
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.
Is used by http2 so may be perf sensitive :-/
Also are aspnet dlls ngen'd in the newest versions? Does mean none of the methods can be inlined if so.
Did you mean
if (HttpMethods.IsGet(value))
{
method = HttpMethod.Get;
return true;
}
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.
How is this used by HTTP/2? If this is only hit by applications setting the request method themselves, I too would prefer a more readable slightly slower version of this method.
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.
HTTP/2 goes via string header
((IHttpRequestFeature)this).Method = RequestHeaders[":method"];
Though it probably shouldn't use an interface cast for it https://github.com/dotnet/coreclr/issues/16198
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.
i.e. first line of Http2Stream.TryParseRequest(ReadResult result, out bool endConnection)
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.
Improved the TryParseRequest
not to cast via interface and use method directly
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.
Also changed to use the HttpMethods
strings
return _customMethod; | ||
} | ||
|
||
_customMethod = HttpUtilities.MethodToString(Method); |
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.
The old code did HttpUtilities.MethodToString(method) ?? string.Empty
. I'd either keep that or have MethodToString throw instead of return null neither of which should ever actually happen in practice.
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.
done
throw new ArgumentException(nameof(value)); | ||
} | ||
|
||
var firstChar = value[0]; |
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.
How is this used by HTTP/2? If this is only hit by applications setting the request method themselves, I too would prefer a more readable slightly slower version of this method.
@@ -324,7 +325,8 @@ public void Reset() | |||
MaxRequestBodySize = ServerOptions.Limits.MaxRequestBodySize; | |||
AllowSynchronousIO = ServerOptions.AllowSynchronousIO; | |||
TraceIdentifier = null; | |||
Method = null; | |||
Method = HttpMethod.Get; |
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.
HttpMethod.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.
Added
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.
Had to change None = byte.MaxValue
as so indexing and hashing uses the values
return _customMethod; | ||
} | ||
|
||
_customMethod = HttpUtilities.MethodToString(Method); |
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.
Why store this in _costomMethod? It's a static lookup. You store null for Set.
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.
GetKnownMethod
returns an HttpMethod
enum for known methods, but not the string so null.
The field is there anyway for a non-known method, so once you've looked it up once, might as well store it if its checked again rather than doing another lookup,
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.
So why didn't you store the value
in the Set code path?
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.
If you're going to use it for both then call it _methodText
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.
Ahh.. that's a very good point 😄
/// The Known Methods (CONNECT, DELETE, GET, HEAD, PATCH, POST, PUT, OPTIONS, TRACE) | ||
/// </remarks> | ||
/// <returns><c>true</c> if the input matches a known string, <c>false</c> otherwise.</returns> | ||
public static bool GetKnownMethod(string value, out HttpMethod method) |
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.
Should this be called TryGetKnownMethod
? It is following the try pattern of returning a bool with an out param.
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.
Changed
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.
Reverted, always returns value in out
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.
What's the bool for then?
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.
Ah good point can clean this up; return the string
K, done - I think |
return method != HttpMethod.Custom; | ||
} | ||
} | ||
|
||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
internal static unsafe HttpMethod GetKnownMethod(byte* data, int length, out int methodLength) | ||
internal static unsafe HttpMethod TryGetKnownMethod(byte* data, int length, out int methodLength) |
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.
This shouldn't be Try
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.
Because it always returns a value in out
?
Reverted
K, now done... hopefully :) |
/// The Known Methods (CONNECT, DELETE, GET, HEAD, PATCH, POST, PUT, OPTIONS, TRACE) | ||
/// </remarks> | ||
/// <returns><c>true</c> if the input matches a known string, <c>false</c> otherwise.</returns> | ||
public static string GetKnownMethod(string value, out HttpMethod method) |
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.
This method is taking string value
and then returning it back again. Why not just return HttpMethod instead of an out param?
Also XML docs need to be updated.
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.
Done
12bed8c
to
a009bc1
Compare
} | ||
set | ||
{ | ||
Method = HttpUtilities.GetKnownMethod(value); |
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.
Out of curiosity, what would happen if we treated every request method set by the app via IHttpRequestFeature.Method as a custom method whether or not it known in order to elide this call to GetKnownMethod?
I don't see how this could be a problem, because none of Kestrel's logic should be predicated on the request method set by the application.
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.
So the app couldn't change request behavior based on method? If you changed method from GET
-> HEAD
the would mean if the app produced no body, but a content length it would throw with an 500 error.
However that may be better than responding with a HEAD
response to a GET
request and then hanging the browser while waiting for data that would never come?
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.
While the app should be able to change all of the properties of HttpContext to lie to itself, the server must maintain an internal source of truth so it can properly interact with the client.
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.
Changed
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.
This change is really messing up the tests :(
a009bc1
to
1c6d82d
Compare
Changed and rebased |
35d5e16
to
7c528d4
Compare
Rebased |
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.
Thanks! LGTM.
@pakrym you want to take a look? |
@benaadams can you rebase? |
/// <returns><see cref="HttpMethod"/></returns> | ||
public static HttpMethod GetKnownMethod(string value) | ||
{ | ||
// Called by http/2 and if user code overrides and sets the Method on IHttpRequestFeature |
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.
This is only used for http2 now, not setting the method directly right?
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.
Yes, will change comment
7c528d4
to
b0e3650
Compare
Rebased, changed comment |
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.
LGTM
Thanks! |
😄 What does the |
Old benchmark to analyze the cost of connection adapters. Used here: https://github.com/aspnet/benchmarks/blob/dev/src/Benchmarks/Program.cs#L234 |
This really shows what a difference one extra string comparison per request makes. I think Thanks @benaadams for finding and fixing this. |
Don't think its just down to this one as there were 5 PRs merged at the same time; but glad they had an effect 😄 Use enum for method rather than string compares (#2294) |
Also none of them in the area of recent regression, so everything still to play for 😉 |
For GET method
IsHead
test is performed on the stringMethod
which sinceGET
isn't reference equal toHEAD
drops through toString.Equals
->OrdinalIgnoreCaseComparer.Equals
However the Parser starts with it as an Enum, which then gets converted to a string; so it can just be tested with the enum.
Rebased #2027
Resolves: #2020