Reduce simple HTTP/2 post app allocation by ~40% (#32557)
* Remove cancellation-related allocations in Http2Stream
We don't need to allocate a linked token source in SendRequestBodyAsync if the caller's token is the default
* Reduce size of SendDataAsync state machine
We're carrying around an extra 24-bytes for a `ReadOnlyMemory<byte>`, when we could instead just use the argument.
* Tweak HeaderField's ctor to use ROS.ToArray
If `value` happens to be empty, this will avoid an allocation. But what actually led me to do this was just tightening up the code.
* Make HPackDecoder.State enum 1 instead of 4 bytes
* Remove spilled CancellationTokenSource field from SendDataAsync
* Add known-header values for access-control-* headers
* Reduce allocation in SslStream.ReadAsync
The current structure is that ReadAsync makes two calls to FillBufferAsync, one to ensure the frame header is read and another to ensure any additional payload is read. This has two issues:
1. It ensures that in addition to allocating a state machine for FillBufferAsync (or, rather, a helper it uses) when it needs to yield, it'll also end up allocating for ReadAsync.
2. It complicates error handling, which needs to differentiate whether the first read can't get any bytes or whether a subsequent read can't, which necessitates storing state like how many bytes we initially had buffered so we can compare to that to see if we need to throw.
We can instead:
- Make FillBufferAsync into a simple "read until we get the requested number of bytes" loop and throw if it fails to do so.
- Do the initial read in ReadAsync, thereby allowing us to special-case the first read for both error handling and to minimize the chances that the helper call needs to yield.
This eliminates a bunch of FillBufferAsync state machines and also decreases the size of the state machines when they are needed.
* Replace CreditManager's waiter queue with a circular singly-linked list
This has a variety of benefits:
- We no longer need to allocate a `Queue<Waiter>` and its underlying `Waiter[]`.
- We no longer need to allocate a `TaskCompletionSource<int>` and its `Task<int>`, instead creating a single `IValueTaskSource<T>` implementation.
- For non-cancelable waiters, we can specialize to not need to carry around a meaningless CancellationToken field.
- For cancelable waiters (the common case), we can avoid an entire async method and its state machine by just storing the relevant state onto the waiter itself.
* Fix comment from previous change
* Manually inline and specialize EnsureIncomingBytesAsync
It's not that much more code to just manually inline EnsureIncomingBytesAsync into the three places it's used, and doing so has multiple benefits, both for size and for error messages.
* Update src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Connection.cs
Co-Authored-By: Cory Nelson <phrosty@gmail.com>
* Fix typo in online feedback
Co-authored-by: Cory Nelson <phrosty@gmail.com>