Skip to content
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

Kestrel response header encoding #33776

Merged
merged 26 commits into from
Jul 6, 2021
Merged

Conversation

Tratcher
Copy link
Member

@Tratcher Tratcher commented Jun 22, 2021

Fixes #4727

I have all of the plumbing in place with tests written and passing, but there's plenty of room to clean it up.

  • Added Http3HeadersEncoder for consistency with HTTP/2.
  • Response.Headers validation has been relaxed if you're using a non-default encoder. Control characters are still blocked, but non-ascii characters will be allowed.
    • If the custom encoder decides to throw an exception when serializing the response rather than using a default replacement character, that will cause the connection to be aborted (HTTP/1 & 2, not sure about 3) to avoid inconsistent states like out of sync dynamic encoding. Applications can filter response headers in middleware/OnResponseStarting to avoid this.
  • Fixed a few spots where the request header encoding wasn't null checked.

TODO:

  • Sync HPack changes back to runtime

cc: @benaadams

ResponseHeadersWritingBenchmark (HTTP/1)

Before:

Method Type Mean Error StdDev Median Op/s Gen 0 Gen 1 Gen 2 Allocated
Output TechEmpowerPlaintext 1.403 us 0.0321 us 0.0874 us 1.400 us 712,510.4 - - - 144 B
Output PlaintextChunked 1.241 us 0.0287 us 0.0786 us 1.200 us 805,555.6 - - - 144 B
Output PlaintextWithCookie 1.617 us 0.0609 us 0.1748 us 1.600 us 618,489.6 - - - 144 B
Output Plain(...)ookie [26] 1.635 us 0.0367 us 0.0906 us 1.600 us 611,724.7 - - - 144 B
Output LiveAspNet 3.493 us 0.0687 us 0.1257 us 3.500 us 286,298.6 - - - 144 B

After, default encoder:

Method Type Mean Error StdDev Median Op/s Gen 0 Gen 1 Gen 2 Allocated
Output TechEmpowerPlaintext 1.563 us 0.0352 us 0.0720 us 1.600 us 639,899.6 - - - 144 B
Output PlaintextChunked 1.468 us 0.0332 us 0.0476 us 1.500 us 681,265.2 - - - 144 B
Output PlaintextWithCookie 1.934 us 0.0467 us 0.1332 us 1.900 us 517,051.7 - - - 144 B
Output Plain(...)ookie [26] 2.051 us 0.0397 us 0.0736 us 2.100 us 487,528.3 - - - 144 B
Output LiveAspNet 3.661 us 0.0780 us 0.2162 us 3.700 us 273,173.7 - - - 144 B

After, utf8 encoder:

Method Type Mean Error StdDev Op/s Gen 0 Gen 1 Gen 2 Allocated
Output TechEmpowerPlaintext 1.806 us 0.0482 us 0.1344 us 553,846.2 - - - 144 B
Output PlaintextChunked 1.810 us 0.0545 us 0.1573 us 552,359.0 - - - 144 B
Output PlaintextWithCookie 2.118 us 0.0493 us 0.1390 us 472,036.9 - - - 144 B
Output Plain(...)ookie [26] 2.103 us 0.0578 us 0.1660 us 475,475.5 - - - 144 B
Output LiveAspNet 3.877 us 0.0992 us 0.2781 us 257,936.5 - - - 144 B

HPackHeaderWriterBenchmark (Http/2)

Before:

Method Mean Error StdDev Op/s Gen 0 Gen 1 Gen 2 Allocated
BeginEncodeHeaders_KnownHeaders 636.2 ns 1.89 ns 1.68 ns 1,571,833.8 - - - -
BeginEncodeHeaders_UnknownHeaders 724.9 ns 4.26 ns 3.78 ns 1,379,445.1 - - - -

After:

Method Mean Error StdDev Op/s Gen 0 Gen 1 Gen 2 Allocated
BeginEncodeHeaders_KnownHeaders 682.1 ns 2.02 ns 1.89 ns 1,465,996.9 - - - -
BeginEncodeHeaders_KnownHeaders_CustomEncoding 816.1 ns 2.87 ns 2.69 ns 1,225,306.0 - - - -
BeginEncodeHeaders_UnknownHeaders 773.0 ns 2.75 ns 2.29 ns 1,293,619.5 - - - -
BeginEncodeHeaders_UnknownHeaders_CustomEncoding 750.2 ns 3.03 ns 2.83 ns 1,333,002.7 - - - -

Crank Plaintext default encoding:

application old new
CPU Usage (%) 100 99 -1.00%
Cores usage (%) 1,194 1,193 -0.08%
Working Set (MB) 72 71 -1.39%
Private Memory (MB) 395 370 -6.33%
Build Time (ms) 2,951 2,993 +1.42%
Start Time (ms) 162 202 +24.69%
Published Size (KB) 106,170 106,170 0.00%
.NET Core SDK Version 6.0.100-preview.6.21276.12 6.0.100-preview.6.21276.12
load old new
CPU Usage (%) 42 43 +2.38%
Cores usage (%) 503 513 +1.99%
Working Set (MB) 38 38 0.00%
Private Memory (MB) 358 358 0.00%
Start Time (ms) 0 0
First Request (ms) 97 149 +53.61%
Requests/sec 2,410,464 2,342,838 -2.81%
Requests 36,397,228 35,375,101 -2.81%
Mean latency (ms) 1.17 1.14 -2.56%
Max latency (ms) 49.73 41.52 -16.51%
Bad responses 0 0
Socket errors 0 0
Read throughput (MB/s) 303.44 294.93 -2.80%
Latency 50th (ms) 0.95 0.97 +2.11%
Latency 75th (ms) 1.39 1.41 +1.44%
Latency 90th (ms) 1.79 1.80 +0.56%
Latency 99th (ms) 0.00 0.00

@Tratcher Tratcher added this to the 6.0-preview7 milestone Jun 22, 2021
@Tratcher Tratcher self-assigned this Jun 22, 2021
@Tratcher
Copy link
Member Author

/AzurePipelines run AspNetCore-benchmarking-pr

@azure-pipelines
Copy link

No pipelines are associated with this pull request.

@Tratcher Tratcher marked this pull request as ready for review June 25, 2021 23:15
@Tratcher Tratcher requested a review from a team June 25, 2021 23:15
Copy link
Contributor

@JunTaoLuo JunTaoLuo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First batch


internal static int GetResponseHeaderStaticTableId(KnownHeaderType responseHeaderType)
{
// Not Implemented
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When will we need to do this? Do we need to file a followup issue?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

@JunTaoLuo JunTaoLuo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will circle back on reviewing tests. But src changes LGTM


public Func<string, Encoding?> EncodingSelector { get; set; } = KestrelServerOptions.DefaultHeaderEncodingSelector;

public int QPackStaticTableId => GetResponseHeaderStaticTableId(_knownHeaderType);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where/how is this used?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The pattern is copied from Http2HeadersEnumerator and will be used when implementing #33980.

@Tratcher Tratcher force-pushed the tratcher/non-ascii branch from 06634ad to b518b31 Compare June 30, 2021 23:11
Comment on lines 151 to 152
[InlineData(2, 2)]
[InlineData(3, 2)]
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're now using GetMaxByteCount(1) to ensure we have enough space to write at least one character into the buffer, but for ASCII this returns 2 for some reason. I don't know how one char could become two ASCII bytes.

@Tratcher
Copy link
Member Author

Rebased to resolve merge conflicts. I fixed a bug in WriteEncodedMultiWrite uncovered by the new benchmarks where it didn't ensure buffer space the first time through the loop.

@Tratcher
Copy link
Member Author

Tratcher commented Jul 1, 2021

/azp run

@azure-pipelines
Copy link

Azure Pipelines successfully started running 2 pipeline(s).

{
_log.QPackEncodingError(_connectionId, streamId, ex);
_connectionContext.Abort(new ConnectionAbortedException(ex.Message, ex));
_http3Stream.Abort(new ConnectionAbortedException(ex.Message, ex), Http3ErrorCode.InternalError);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't aborting the _connectionContext abort the stream implicitly?

Copy link
Member Author

@Tratcher Tratcher Jul 2, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JamesNK? I wasn't sure about that part, I know state is tracked much differently from HTTP/2.

@halter73 halter73 added the api-ready-for-review API is ready for formal API review - https://github.com/dotnet/apireviews label Jul 2, 2021
@ghost
Copy link

ghost commented Jul 2, 2021

Thank you for submitting this for API review. This will be reviewed by @dotnet/aspnet-api-review at the next meeting of the ASP.NET Core API Review group. Please ensure you take a look at the API review process documentation and ensure that:

  • The PR contains changes to the reference-assembly that describe the API change. Or, you have included a snippet of reference-assembly-style code that illustrates the API change.
  • The PR describes the impact to users, both positive (useful new APIs) and negative (breaking changes).
  • Someone is assigned to "champion" this change in the meeting, and they understand the impact and design of the change.

@Tratcher Tratcher merged commit 051aa95 into dotnet:main Jul 6, 2021
@Tratcher Tratcher deleted the tratcher/non-ascii branch July 6, 2021 16:44
@davidfowl
Copy link
Member

Please watch the benchmarks after this change makes it all the way in.

@pranavkm pranavkm added api-approved API was approved in API review, it can be implemented and removed api-ready-for-review API is ready for formal API review - https://github.com/dotnet/apireviews labels Jul 13, 2021
@amcasey amcasey added area-networking Includes servers, yarp, json patch, bedrock, websockets, http client factory, and http abstractions and removed area-runtime labels Jun 6, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
api-approved API was approved in API review, it can be implemented area-networking Includes servers, yarp, json patch, bedrock, websockets, http client factory, and http abstractions
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Allow non-ASCII characters in response header values
10 participants