Reduce allocation in HTTP/2 requests by ~30% (#32406)
* Avoid forcing ExpectContinue / TransferEncodingChunked headers into existence
* Reduce cookie-related allocations
- Make CookieParser/CookieTokenizer into structs. CookieParser is just created as a helper with some state to help a loop do parsing; it's not passed around in any way. And CookieTokenizer is just the implementation of CookieParser separated out for some reason. They can both be structs.
- Remove enumerator allocations related to cookies. Also reduces interface method invocations.
* Remove often-unnecessary allocation in Uri.IdnHost
This is happening for every request. But if the host is already all ASCII, we don't need to create a new string, and if it's already all lowercase, we don't need to create yet another new string. (This logic could stand to be cleaned up further; I just removed the allocations and some unnecessarily complex unsafe code along the way.)
* Combine ReadAtLeastAsync into EnsureIncomingBytesAsync
There's no need for them to be separate, and separating them leads to an extra async stack frame / state machine.
* Reduce calls to ReadFrameAsync that need to yield
Many frames either won't have a payload, or the act of waiting for the header will also end up waiting for the payload (in the same packet). By pulling out the wait in the hot path, we significantly reduce the number of times ReadFrameAsync will need to yield.
* Override Http2ReadStream.CopyToAsync
Reduces allocation / improves throughput when using responseStream.CopyToAsync.
* Shrink EnsureIncomingBytesAsync state machine by 8 bytes
Remove two lifted Int32s.
* Shrink size of Http2Connection.SendAsync state machine
The order of a comparison operation is, based on C# required order of operations, forcing a temporary to be spilled and lifted to the state machine.
* Add additional values to known headers
Avoids string allocations when these common response values are used.
* Remove dead Uri code