Http2dotnet
HTTP/2 support for .NET standard
Install / Use
/learn @Matthias247/Http2dotnetREADME
http2dotnet
Nuget package: http2dotnet
This library implements the HTTP/2 and HPACK protocol for .NET standard. The goal of the library is to cover all protocol handling parts of the HTTP/2 (RFC7540) and HPACK (RFC7541) specifications. The goal of this library is NOT to provide a ready-to-use HTTP/2 server or client framework with concrete Request/Response abstractions. Instead it should enable other .NET libraries and applications to easily integrate HTTP/2 support by encapsulating the protocol handling in easy to use and flexible .NET classes. Examples for building simple applications on top of the library are provided within the repository.
Current state
This library is currently in an experimental state. The majority of HTTP/2 features have been implemented and there is already a pretty good test coverage. It was tested in a h2c with prior knowledge configuration against nghttp2 and h2load. It was however not yet tested with an encrypted connection and a browser based client due a missing SSL connection with ALPN negotiation. Various features have not yet been implemented (see current limitations).
Design goals
- Enable an easy integration of HTTP/2 into different frameworks and applications (ASP.NET core, other webservers, gRPC, etc.)
- Provide an abstract interface for HTTP/2 Streams, on which custom Request and Response classes can be built. The Stream abstraction supports all features of the HTTP/2 specification, including sending HEADERS in both directions, flow-controlled DATA and trailing HEADERS. The Request and Response are handled fully independet, which enables features like pure server side data streaming.
- IO layer independence:
This library uses an abstract IO interface, which is defined in theByteStreams.cssource file.
The IO layer can be implemented by .NET System.IO.Streams, Pipelines, TCP sockets, TLS sockets and other IO sources.
The abstract IO interface is inspired by the Go IO interfaces. It was chosen for simplicity, high performance (async reads/writes can be avoided) and because it can easily provide the necessary flow control behavior. - Correctness and reliability:
All HTTP/2 frames are validated according to the HTTP/2 specification.
Full backpressure behavior is provided for HTTP/2 DATA frames (through the specified flow control mechanism) as well as for other HTTP/2 frames (by suspending the reception of further frames until a suitable response for a frame could be sent). - High performance:
To achieve a high performance and low overhead the library tries to minimize dynamic allocations whereever possible, e.g. through the use ofValueTaskstructures and reusing of buffers and data structures. If possible the library also avoids async operations by trying nonblocking operations first to avoid scheduling and continuation overhead. - Full support of all HTTP/2 features (however not yet everything is implemented)
Usage
The library exposes 2 important classes:
Connectionrepresents a HTTP/2 connection between a client and the server.
HTTP/2 connections are created on top of bidirectional streams. For encrypted HTTP/2 (the only variant support by browser - also called h2) the Connection must be created on top of SSL connection. For unencrypted connections (h2c) the Connection has to be created on top of a TCP stream.
On top of the connection multiple bidirectional streams can be established.IStreamrepresents a single bidirectional HTTP/2 stream.
Each Request/Response pair in HTTP/2 is represented by a single Stream.
In the most common lifecycle a HTTP/2 client will create a new Stream on top of the Connection, send all required headers for the Request through the Stream, and then send optional data through the stream. The server will receive a notification about a new create stream and invoke a request handler. The request handler can then read the received headers, read all incoming data from the stream and respond with headers and data.
Besides that there are some advanced features, e.g. both clients and servers can also Cancel/Reset the stream during processing and both are able to send trailing headers at the end of a Stream.
This library does not create the underlying TCP or SSL connections on server or client side. This is the responsibility of the library user. However this pattern allows to use the library on top of arbitrary IO stream types.
To create a Connection on top of IO streams the constructor of Connection
can be used:
ConnectionConfiguration config =
new ConnectionConfigurationBuilder(isServer: true)
.UseStreamListener(AcceptIncomingStream)
.Build();
Connection http2Connection = new Connection(
config: config,
inputStream: inputStream,
outputStream: outputStream);
Configuration settings which are shared between multiple connections are
configured through the ConnectionConfigurationBuilder which stores them in an
ConnectionConfiguration instance. The remaining parameters which are unique
for each Connection instance are configured directly thorugh the Connection
constructor parameters.
The most important arguments for the Connection are:
inputStream/outputStream: TheConnectionwill will use the IO streams to read data from the remote side and send data to the remote side. The connection will take ownership of these streams and close theOutputStreamwhen done.config: The configuration for the connection, which is possibly also shared with otherConnectioninstances.
The most important arguments for the ConnectionConfiguration are:
isServer: Specifies whether the local side of theConnectionrepresents a HTTP/2 server (true) or client (false).UseStreamListener: This sets up the callback function that will be invoked every time a new Stream is initiated from the remote side. This means it is currently only required for HTTP/2 server applications. AnIStreaminstance which represents the initiated stream is passed to the callback function as an argument. The callback function should either returntrueif it wants to process the new stream orfalseotherwise. If the application wants to process the new stream it must use it in anotherTaskand must not block the callback.UseSettings: The HTTP/2 settings that should be used.Settings.Defaultrepresents the default values from the HTTP/2 specification and should usually be a good choice. These are also utilized if not explicitely specified.
Besides that there are various optional arguments which allow further control about the desired behavior.
After the Connection was set up it will handle all HTTP/2 protocol related
concerns up to the point where the underlying connection gets closed.
The IStream class allows to read and write headers and bytes for a single
HTTP/2 stream. The methods ReadHeadersAsync() and WriteHeadersAsync()
allow to read and write headers from a connection.
The returned ValueTask from ReadHeadersAsync will only be fulfilled once
headers from the remote side where received.
According to the HTTP/2 request/reponse lifecycle applications have to send
headers at the start of each Stream - which means it is not allowed to send
data before any headers have been sent. The received headers will contain the
pseudo-headers which are used in HTTP/2 for transmission of the HTTP method
(:method), status code (:status), etc. These are not automatically extracted
into seperated fields because IStream is used for requests and responses -
depending on whether the library is used for a client or server. However higher
level frameworks can easily extract the fields from the returned header lists.
This library will always validate received and sent headers for correctness.
This means a user of the library on the server side can rely on the fact that
the :method, :scheme and :path pseudo headers are preset and that after
the first real header field no pseudo header field follows.
After headers were sent for a Stream data can be written to the stream. As
IStream implements the ByteStream abstractions in this library, the data can
be written through the provided WriteAsync function. The returned Task will
only be fulfilled after the data could be sent through the underlying
connection. This will take the available flow control windows into account. The
stream can be closed from the by calling the CloseAsync function. Alternativly
the endOfStream parameter of WriteHeadersAsync can be used for closing the
Stream without sending any data (e.g. for HTTP GET requests). The read and write
directions of HTTP/2 are fully independet. This means closing the local side of
the stream will still allow reading headers and data from the remote side. A
stream is only fully closed after all headers and data were consumed from the
remote side in addition to the local side having been closed. To read data from
the stream the ReadAsync function can be used. This function will signal the
end of stream if the stream was closed from the remote side. After a stream was
closed from the remote side trailers can be read through the ReadTrailersAsync
function. To send trailers data must be transferred and instead of closing the
stream with CloseAsync or setting endOfStream to true the
WriteTrailersAsync function must be used.
The Cancel function on the IStream can be used to reset the stream. This
will move the Stream into the Closed state if it wasn't Closed before. In order
to notify the remote side a RST_STREAM frame will be sent. All streams must be
either fully processed (read side fully consumed - write side closed) or
cancelled/reset by the application. Otherwise the Stream will be leaked. The
`Dis
Related Skills
openhue
343.3kControl Philips Hue lights and scenes via the OpenHue CLI.
sag
343.3kElevenLabs text-to-speech with mac-style say UX.
weather
343.3kGet current weather and forecasts via wttr.in or Open-Meteo
tweakcc
1.5kCustomize Claude Code's system prompts, create custom toolsets, input pattern highlighters, themes/thinking verbs/spinners, customize input box & user message styling, support AGENTS.md, unlock private/unreleased features, and much more. Supports both native/npm installs on all platforms.
