{"id":13824741,"url":"https://github.com/Matthias247/http2dotnet","last_synced_at":"2025-07-08T19:32:51.150Z","repository":{"id":65414220,"uuid":"79266429","full_name":"Matthias247/http2dotnet","owner":"Matthias247","description":"HTTP/2 support for .NET standard","archived":false,"fork":false,"pushed_at":"2023-01-19T16:22:27.000Z","size":358,"stargazers_count":101,"open_issues_count":3,"forks_count":24,"subscribers_count":11,"default_branch":"master","last_synced_at":"2025-05-26T03:11:04.801Z","etag":null,"topics":["csharp","dotnet","hpack","http","http2","network","rfc-7540","rfc-7541"],"latest_commit_sha":null,"homepage":null,"language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Matthias247.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-01-17T20:01:27.000Z","updated_at":"2025-03-11T14:44:53.000Z","dependencies_parsed_at":"2023-02-11T09:20:22.639Z","dependency_job_id":null,"html_url":"https://github.com/Matthias247/http2dotnet","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/Matthias247/http2dotnet","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Matthias247%2Fhttp2dotnet","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Matthias247%2Fhttp2dotnet/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Matthias247%2Fhttp2dotnet/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Matthias247%2Fhttp2dotnet/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Matthias247","download_url":"https://codeload.github.com/Matthias247/http2dotnet/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Matthias247%2Fhttp2dotnet/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":259559689,"owners_count":22876499,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["csharp","dotnet","hpack","http","http2","network","rfc-7540","rfc-7541"],"created_at":"2024-08-04T09:01:08.468Z","updated_at":"2025-07-08T19:32:50.786Z","avatar_url":"https://github.com/Matthias247.png","language":"C#","readme":"http2dotnet\n===========\n\n**Nuget package:** [http2dotnet](http://www.nuget.org/packages/http2dotnet/)\n\nThis library implements the HTTP/2 and HPACK protocol for .NET standard.\nThe goal of the library is to cover all protocol handling parts of the HTTP/2\n([RFC7540](http://httpwg.org/specs/rfc7540.html)) and HPACK\n([RFC7541](http://httpwg.org/specs/rfc7541.html)) specifications.\nThe goal of this library is NOT to provide a ready-to-use HTTP/2 server or\nclient framework with concrete Request/Response abstractions. Instead it should\nenable other .NET libraries and applications to easily integrate HTTP/2 support\nby encapsulating the protocol handling in easy to use and flexible .NET classes.\nExamples for building simple applications on top of the library are provided\nwithin the repository.\n\n## Current state\n\nThis library is currently in an experimental state. The majority of HTTP/2\nfeatures have been implemented and there is already a pretty good test coverage.\nIt was tested in a h2c with prior knowledge configuration against **nghttp2**\nand **h2load**. It was however not yet tested with an encrypted connection and\na browser based client due a missing SSL connection with ALPN negotiation.\nVarious features have not yet been implemented (see current limitations).\n\n## Design goals\n\n- Enable an easy integration of HTTP/2 into different frameworks and\n  applications (ASP.NET core, other webservers, gRPC, etc.)\n- Provide an abstract interface for HTTP/2 Streams, on which custom Request\n  and Response classes can be built. The Stream abstraction supports all\n  features of the HTTP/2 specification, including sending HEADERS in both\n  directions, flow-controlled DATA and trailing HEADERS. The Request and\n  Response are handled fully independet, which enables features like pure server\n  side data streaming.\n- IO layer independence:  \n  This library uses an abstract IO interface, which is defined in the\n  `ByteStreams.cs` source file.  \n  The IO layer can be implemented by .NET System.IO.Streams, Pipelines,\n  TCP sockets, TLS sockets and other IO sources.  \n  The abstract IO interface is inspired by the Go IO interfaces. It was chosen\n  for simplicity, high performance (async reads/writes can be avoided) and \n  because it can easily provide the necessary flow control behavior.\n- Correctness and reliability:  \n  All HTTP/2 frames are validated according to the HTTP/2 specification.  \n  Full backpressure behavior is provided for HTTP/2 DATA\n  frames (through the specified flow control mechanism) as well as for other\n  HTTP/2 frames (by suspending the reception of further frames until a suitable\n  response for a frame could be sent).\n- High performance:  \n  To achieve a high performance and low overhead the library tries to minimize\n  dynamic allocations whereever possible, e.g. through the use of `ValueTask`\n  structures and reusing of buffers and data structures. If possible the library\n  also avoids async operations by trying nonblocking operations first to avoid\n  scheduling and continuation overhead.\n- Full support of all HTTP/2 features (however not yet everything is implemented)\n\n## Usage\n\nThe library exposes 2 important classes:\n- `Connection` represents a HTTP/2 connection between a client and the server.  \n  HTTP/2 connections are created on top of bidirectional streams. For encrypted\n  HTTP/2 (the only variant support by browser - also called h2) the Connection\n  must be created on top of SSL connection. For unencrypted connections (h2c)\n  the Connection has to be created on top of a TCP stream.  \n  On top of the connection multiple bidirectional streams can be established.\n- `IStream` represents a single bidirectional HTTP/2 stream.  \n  Each Request/Response pair in HTTP/2 is represented by a single Stream.\n\nIn the most common lifecycle a HTTP/2 client will create a new Stream on top\nof the Connection, send all required headers for the Request through the Stream,\nand then send optional data through the stream. The server will receive a\nnotification about a new create stream and invoke a request handler. The request\nhandler can then read the received headers, read all incoming data from the\nstream and respond with headers and data.\n\nBesides that there are some advanced features, e.g. both clients and servers\ncan also Cancel/Reset the stream during processing and both are able to send\ntrailing headers at the end of a Stream.\n\nThis library does not create the underlying TCP or SSL connections on server or\nclient side. This is the responsibility of the library user. However this\npattern allows to use the library on top of arbitrary IO stream types.\n\nTo create a `Connection` on top of IO streams the constructor of `Connection`\ncan be used:\n\n```cs\nConnectionConfiguration config =\n    new ConnectionConfigurationBuilder(isServer: true)\n    .UseStreamListener(AcceptIncomingStream)\n    .Build();\n\nConnection http2Connection = new Connection(\n    config: config,\n    inputStream: inputStream,\n    outputStream: outputStream);\n```\n\nConfiguration settings which are shared between multiple connections are\nconfigured through the `ConnectionConfigurationBuilder` which stores them in an\n`ConnectionConfiguration` instance. The remaining parameters which are unique\nfor each `Connection` instance are configured directly thorugh the `Connection`\nconstructor parameters.\n\nThe most important arguments for the `Connection` are:\n- `inputStream`/`outputStream`: The `Connection` will will use the IO streams\n  to read data from the remote side and send data to the remote side. The\n  connection will take ownership of these streams and close the `OutputStream`\n  when done.\n- `config`: The configuration for the connection, which is possibly also shared\n  with other `Connection` instances.\n\nThe most important arguments for the `ConnectionConfiguration` are:\n- `isServer`: Specifies whether the local side of the `Connection` represents a\n  HTTP/2 server (`true`) or client (`false`).\n- `UseStreamListener`: This sets up the callback function that will be invoked\n  every time a new Stream is initiated from the remote side. This means it is\n  currently only required for HTTP/2 server applications.\n  An `IStream` instance which represents the initiated stream is passed to the\n  callback function as an argument. The callback function should either return\n  `true` if it wants to process the new stream or `false` otherwise.\n  If the application wants to process the new stream it must use it in another\n  `Task` and must not block the callback.\n- `UseSettings`: The HTTP/2 settings that should be used. `Settings.Default`\n  represents the default values from the HTTP/2 specification and should usually\n  be a good choice. These are also utilized if not explicitely specified.\n\nBesides that there are various optional arguments which allow further control\nabout the desired behavior.\n\nAfter the `Connection` was set up it will handle all HTTP/2 protocol related\nconcerns up to the point where the underlying connection gets closed.\n\nThe `IStream` class allows to read and write headers and bytes for a single\nHTTP/2 stream. The methods `ReadHeadersAsync()` and `WriteHeadersAsync()`\nallow to read and write headers from a connection.\nThe returned `ValueTask` from `ReadHeadersAsync` will only be fulfilled once\nheaders from the remote side where received.\nAccording to the HTTP/2 request/reponse lifecycle applications have to send\nheaders at the start of each Stream - which means it is not allowed to send\ndata before any headers have been sent. The received headers will contain the\npseudo-headers which are used in HTTP/2 for transmission of the HTTP method\n(`:method`), status code (`:status`), etc. These are not automatically extracted\ninto seperated fields because `IStream` is used for requests and responses -\ndepending on whether the library is used for a client or server. However higher\nlevel frameworks can easily extract the fields from the returned header lists.\nThis library will always validate received and sent headers for correctness.\nThis means a user of the library on the server side can rely on the fact that\nthe `:method`, `:scheme` and `:path` pseudo headers are preset and that after\nthe first real header field no pseudo header field follows.\n\nAfter headers were sent for a Stream data can be written to the stream. As\n`IStream` implements the ByteStream abstractions in this library, the data can\nbe written through the provided `WriteAsync` function. The returned `Task` will\nonly be fulfilled after the data could be sent through the underlying\nconnection. This will take the available flow control windows into account. The\nstream can be closed from the by calling the `CloseAsync` function. Alternativly\nthe `endOfStream` parameter of `WriteHeadersAsync` can be used for closing the\nStream without sending any data (e.g. for HTTP GET requests). The read and write\ndirections of HTTP/2 are fully independet. This means closing the local side of\nthe stream will still allow reading headers and data from the remote side. A\nstream is only fully closed after all headers and data were consumed from the\nremote side in addition to the local side having been closed. To read data from\nthe stream the `ReadAsync` function can be used. This function will signal the\nend of stream if the stream was closed from the remote side. After a stream was\nclosed from the remote side trailers can be read through the `ReadTrailersAsync`\nfunction. To send trailers data must be transferred and instead of closing the\nstream with `CloseAsync` or setting `endOfStream` to `true` the\n`WriteTrailersAsync` function must be used.\n\nThe `Cancel` function on the `IStream` can be used to reset the stream. This\nwill move the Stream into the Closed state if it wasn't Closed before. In order\nto notify the remote side a RST_STREAM frame will be sent. All streams must be\neither fully processed (read side fully consumed - write side closed) or\ncancelled/reset by the application. Otherwise the Stream will be leaked. The\n`Dispose` method on stream will also cancel the stream.\n\nThe following example shows the handling of a stream on the server side:\n\n```cs\nstatic async void HandleIncomingStream(IStream stream)\n{\n    try\n    {\n        // Read the headers\n        var headers = await stream.ReadHeadersAsync();\n        var method = headers.First(h =\u003e h.Name == \":method\").Value;\n        var path = headers.First(h =\u003e h.Name == \":path\").Value;\n        // Print the request method and path\n        Console.WriteLine(\"Method: {0}, Path: {1}\", method, path);\n\n        // Read the request body and write it to console\n        var buf = new byte[2048];\n        while (true)\n        {\n            var readResult = await stream.ReadAsync(new ArraySegment\u003cbyte\u003e(buf));\n            if (readResult.EndOfStream) break;\n            // Print the received bytes\n            Console.WriteLine(Encoding.ASCII.GetString(buf, 0, readResult.BytesRead));\n        }\n\n        // Send a response which consists of headers and a payload\n        var responseHeaders = new HeaderField[] {\n            new HeaderField { Name = \":status\", Value = \"200\" },\n            new HeaderField { Name = \"content-type\", Value = \"text/html\" },\n        };\n        await stream.WriteHeadersAsync(responseHeaders, false);\n        await stream.WriteAsync(new ArraySegment\u003cbyte\u003e(\n            Encoding.ASCII.GetBytes(\"Hello World!\")), true);\n\n        // Request is fully handled here\n    }\n    catch (Exception)\n    {\n        stream.Cancel();\n    }\n}\n```\n\nAll `IStream` APIs are completely thread-safe, which means they can be used from\nan arbitrary thread. However the user application must still follow the basic\nHTTP/2 contracts, which e.g. means that now trailers may be sent before data was\nsent, and that neither data nor trailers might be sent if the end of the stream\nwas already indicated.\n\n## Client stream creation\n\nThe library can be used to build HTTP/2 clients. For this usecase outgoing\n`IStream`s need to be created, which represent the sent request. This can be\nachieved through the `Connection.CreateStreamAsync()` method, which creates a\nnew outgoing stream. The user must pass all the required headers (including the\npseudo-headers `:method`, `:path`, `:scheme` and optionally `:authority`) as an\nargument, as calling the method will directly lead to sending a HTTP/2 headers\nframe which the requested header fields. The method will return the newly\ncreated stream.\n\nExample for creating and using client stream, in case a HTTP/2 connection has\nalready been established and configured for client-side use:\n\n```cs\n\nHeaderField[] headers = new HeaderField[]\n{\n    new HeaderField { Name = \":method\", Value = \"GET\" },\n    new HeaderField { Name = \":scheme\", Value = \"http\" },\n    new HeaderField { Name = \":path\", Value = \"/\" },\n};\n\nvar stream = await http2Connection.CreateStreamAsync(\n    headers, endOfStream: true);\n\n// Wait for response headers\nvar reponseHeaders = await stream.ReadHeadersAsync();\n\n// Read response data\nvar readDataResult = await stream.ReadAsync(buffer);\n```\n\n### Checking for stream ID exhaustion\n\nThe HTTP/2 specification only allows 2^30 streams to be created on a single\n`Connection`. If a client requires to make more requests after some time it needs\nto create a new `Connection`. This library allows to check whether a new\n`Connection needs to be created` through the `Connection.IsExhausted` property,\nwhich will return `true` if no additional outgoing stream can be created.\n\n### Checking for the maximum concurrent streams limit of the remote side\n\nThe library currently does not take the maximum concurrent streams setting into\naccount, which is indicated by a remote through a HTTP/2 settings frame. This\nmeans it will try to create any amount of streams that the user requests, with\nthe risk of the remote side resetting those streams. A future update might\nprovide an additional possibility to check the current maximum concurrent stream\nlimit.\n\n## Server side connection upgrades\n\n### Upgrading over HTTPS\nOver HTTPS, the server and client negotiate a protocol using Application Layer \nProtocol Negotiation. This is done before data is sent, so both the client and \nserver know what protocol to use before handling the connection, so if we \ninstruct the client to use HTTP/2, the connection is already HTTP/2 by the\ntime we get it.\n\nExample code for how an upgrade can be performed over HTTPS can be found in \nthe `HttpsExampleServer`.\n\n### Upgrading from HTTP/1.1\n\nThe library supports HTTP connection upgrade requests on server side.\nExample code for how an upgrade can be performed can be found in the\n`UpgradeExampleServer`.\n\nIn order to upgrade from HTTP/1.1 to HTTP/2, an external HTTP/1.1 parser is\nneeded which reads the complete upgrade request into memory (including an\noptional body).\n\nIf the headers of the received HTTP/1.1 request contain the related upgrade\nheaders (`connection: Upgrade, HTTP2-Settings`), they payload can be checked for\nwhether it is a valid upgrade request or not. In order to perform this step a\n`ServerUpgradeRequestBuilder` instance must be created, which gets fed all the\ninformation (headers, payload, request method, etc.) from the HTTP/1.1 request.\nWith the `ServerUpgradeRequestBuilder.build()` method a `ServerUpgradeRequest`\ncan be created, which exposes an `IsValid` property This property reflects\nwhether the incoming request is a fully valid upgrade request.\n\nOnly if the upgrade request is valid a HTTP/2 `Connection` object may be\ncreated, which get's the inital upgrade request injected into the constructor\nas part of the `Connection.Options` struct. In this case the `Connection` will\ncreate a HTTP/2 `Stream` from the content of the initial upgrade request,\nwhich can be handled like any other HTTP/2 Stream/Request.\n\nIn case the upgrade request is valid, it is the applications responsibility to\nsend `HTTP/1.1 101 Switching Protocols` response before creating the HTTP/2\nconnection object - which won't perform this step, and instead directly speak\nHTTP/2.\n\nIn case the upgrade request is not valid, the application may choose to either\ntreat the request as a normal HTTP/1.1 request (by filtering all ugprade related)\nheaders, or by responding with an HTTP error.\n\nEven if the upgrade headers are available and valid in the initial HTTP/1.1\nrequest, applications may choose not to perform the upgrade but instead handle\nthe request as a normal HTTP/1.1 request. An example scenario for this is when\nthe upgrade request indicates a large body (through `content-length` header),\nwhich would needed to be completely read into memory before the upgrade can be\nperformed: If the application doesn't perform the upgrade and just handles the\nrequest in traditional fashion, the request body can be streamed in normal\nHTTP/1.1 fashion. It is generelly not recommended to accept upgrade requests\nwhich contain a body payload. However most client side libraries that perform\nupgrades (e.g. nghttp), will perform the upgrade only via `GET` and `HEAD`\nrequests anyway.\n\n### Handling HTTP/2 (with and without upgrade) and HTTP/1 in parallel\n\nIn some scenarios it might be required to implement a HTTP server that\nsupports all of the following scenarios:\n- Support for standard HTTP/1.1 requests\n- Support for HTTP/2 requests with prior knowledge (The client knows that the\n  server speaks HTTP/2 and no upgrade is required).\n- Support for upgrading from HTTP/1.1 to HTTP/2\n\nThis library supports this scenario, and a suitable example implementation can\nbe found in the `UpgradeExampleServer` example.\n\nThe idea is that the application (or web-server which is built around the\nlibrary) starts off by reading and parsing an initial HTTP/1.1 request header\nthrough it's preferred HTTP parser.\n\n- If the request header exactly equals `PRI * HTTP/2.0\\r\\n\\r\\n` it's the start\n  of a HTTP/2 connection with prior knowledge. In this case the application can\n  directly create a HTTP/2 `Connection` object on top of the incoming stream\n  and delegate all further processing to it. In this special case, the\n  application must make sure that the stream that is passed to the `Connection`\n  object still emits the `PRI * HTTP/2.0\\r\\n\\r\\n` header when the `Connection`\n  starts reading from it. Otherwise the `Connection` establishment will fail.\n  This means the application must inspect, but not consume this header.\n- If the request is another HTTP/1.1 request which contains ugprade headers, the\n  application can handle it as described in the [Upgrading from HTTP/1.1 on server side](#upgrading-from-http-1-1-on-server-side)\n  section. In this case it's the applications responsibility to consume the\n  ugprade request from the underlying stream and send the necessary upgrade\n  status response.\n- If the request header is an HTTP/1.1 request without upgrade headers, the\n  application can directly handle it in it's preferred way.\n- If the incoming data resembles no HTTP request at all, the application can\n  also handle it in it's preferred way, e.g. by optionally sending an HTTP error\n  code and closing the connection.\n\nIn the first two cases the actual HTTP requests from the connection will further\nflow to the application through by the means of this libraries `IStream`\nabstraction. In the third case it depends on the approach for HTTP/1.1 parrsing\nand processing. It's the applications responsibility to transform all request\nkinds into a common `Request`/`Response` representation if needed.\n\n## Client side connection upgrades\n\n### Upgrading from HTTP/1.1\n\nThe library supports HTTP connection upgrade requests on client side in a\nsimilar fashion as they are supported on the server side. Example code for how\nan upgrade can be performed can be found in the `CliExample`.\n\nIn order to upgrade from HTTP/1.1 to HTTP/2, the user has to send a normal\nHTTP/1.1 request which contains the required `Upgrade` headers to the server.\nSending this request over the underyling TCP connection is outside of the scope\nof this library.\nWhen the HTTP/1.1 response status line and headers are received the user code\nmust check whether the upgrade was successful or not. In the success case a\nHTTP/2 `Connection` object can be constructed on top of the `Connection`. This\n`Connection` object obtain the ownership to the underlying streams (from which\nthe HTTP/1.1 data already has been consumed) and an information about that the\nfact that the `Connection` was created as part of an `Upgrade`, because in this\ncase one Stream will already be existing at startup.\n\nFor performing client side upgrades, the first step is to create a\n`ClientUpgradeRequestBuilder`, which allows to configure the HTTP/2 settings\nwhich will be later on used. Through the `ClientUpgradeRequestBuilder.build()`\nmethod a `ClientUpgradeRequest` can be created. This `ClientUpgradeRequest` also\nexposes an `IsValid` property which reflects whether the upgrade request is valid.\nOnly if the upgrade request is valid a HTTP/2 `Connection` object may be created.\n\nThe `ClientUpgradeRequest` contains a `Base64EncodedSettings` property, which\nreturns the base64 encoded settings which the user must send to the server\ninside the `Http2-Settings` header during the upgrade attempt.\n\nIn case the user received a `HTTP/1.1 101 Switching Protocols` status a\n`Connection` object can be created on top of the underlying streams. The\n`Connection` must be informed about the pending upgrade attempt through the\n`Connection.Options.ClientUpgradeRequest` constructor option.\n\nThe response for the HTTP/1.1 request which triggered the upgrade will in this\ncase be delivered through a HTTP/2 stream, which will always utilize the\nStream ID 1. As the stream was created implicitly through the upgrade and not\ndue to calling `CreateStreamAsync()` the created `IStream` must be returned to\nthe user in another fashion: The user can retrieve a reference to this first\nstream through the `ClientUpgradeRequest.UpgradeRequestStream` property, which\nreturns a `Task\u003cIStream\u003e` which will be fulfilled once a `Connection` has been\ncreated which used the `UpgradeRequest`. Due to this fact a\n`ClientUpgradeRequest` instance may be not be reused for performing multiple\nconnection upgrade requests. It will be bound to the first `Connection` to which\nit gets handed over.\n\n### Handling HTTP/2 (with and without upgrade) and HTTP/1 in parallel\n\nIn order to handle HTTP/2 on client side with a possible fallback to HTTP/1.1\nfor servers which do not support HTTP/1.1 the upgrade mechanism can be used:\nThe first request form the client can be started as a HTTP/1.1 request, which\ncontains the connection upgrade request in it's headers. If the server responds\nwith `101 Switching Protocols` then a HTTP/2 connection can be established on\ntop of this connection and the response of this request as well as further\nrequests can be performed in HTTP/2 fashion. If the server ignores the upgrade\nthe response must be normally processed with a HTTP/1.1 reader.\n\n## Ping handling\n\nThe library will automatically respond to received PING frames\nby sending the associated PING ACKs.\n\nThe user can also issue PINGs from the local side of the connection through the\n`PingAsync` method of the `Connection` class. Calling this method will issue\nsending a PING frame to the remote side of the connection. It returns a task\nthat will be completed once either the remote side acknowledges the PING or the\nconnection was closed. If the connection closes before an ACK was received the\nreturned `Task` will fail with a `ConnectionClosedException`.\n\nExample for measuring the connection latency through the ping mechanism:\n\n```cs\ntry\n{\n    var stopWatch = new Stopwatch();\n    stopWatch.Start();\n    await http2Connection.PingAsync();\n    // Ping request was sent and acknowledge has been received\n    stopWatch.Stop();\n    var latency = stopWatch.Elapsed;\n}\ncatch (ConnectionClosedException)\n{\n    // The connection was closed before an acknowledge to the PING\n    // was received\n}\n```\n\n## Informational headers\n\nThe library supports sending informational headers (HTTP status code in the 1xy\nrange) on server side and receiving them on client side.\n\nTo send informational headers on server side the user can simply call\n`stream.WriteHeadersAsync` multiple times. It is possible to send multiple\nsets of informational headers before the set of headers with the final status\ncode is written. Only the final header set may set `endOfStream` to `true`.\nUsers should not write data before a header with a final `:status` code (outside\nof the 100 range) was sent - because this would be detected as a protocol error\non the client side.\n\nTo receive informational headers on the client side the `stream.ReadHeadersAsync()`\nmay be called multiple times on the client side in the special case where one call\nreturns headers with a `status: 1xy` field. In these cases the next call to\n`ReadHeadersAsync()` will block until the next set of headers (either informational\nor of the final response) will be received.\n\nExample:\n\n```cs\nvar headers = await stream.ReadHeadersAsync();\nif (headers.First(h =\u003e h.Name == \":status\").Value == \"100\")\n{\n    // Received only informational headers\n    headers = await stream.ReadHeadersAsync();\n    // headers now contains the next set of received headers.\n}\n```\n\n**Remark:**\n\nIf the server sends multiple sets of informational headers and possibly the\nfinal headers before the user calls `stream.ReadHeadersAsync()`, this call will\nonly deliver the last delivered set of headers to the user. The headers are not\nqueued internally but overwritten. However for known use-cases of informational\nheaders (the `100 Continue` header) this causes no problem, since the server\nwill only send more headers after user interaction (sending data).\n\n## Performance considerations\n\nFor achieving the best possible performance the following things should be\nconsidered:\n\n- The IO stream abstractions which are handed over to the `Connection` should be\n  optimized for handling small writes and reads in a fast way. E.g. the library\n  might often try to read or write only a few bytes, like the header of a HTTP/2\n  frame. In this case the IO operation should operate synchronously if possible.\n  The TCP socket wrappers which are provided inside this library (see\n  `SocketExtensions.CreateStreams()`) e.g. provide this behavior by trying to\n  perform synchronous nonblocking read and write operations before falling back\n  into async behavior.\n- The library will try to allocate all send and receive buffers from a custom\n  `ArrayPool\u003cbyte\u003e` allocator which can be provided by the user through the\n  `ConnectionConfigurationBuilder.UseBufferPool()` setting. If no allocator is\n  specified the `ArrayPool\u003cbyte\u003e.Shared` is used, which might not be optimal for\n  HTTP/2 operations. The library will use the allocator to allocate buffers of\n  up to `Settings.MaxFrameSize` bytes in normal operation. Therefore the\n  configured allocator can be optimized for these kind of buffer sizes.\n\n## Current limitations\n\nThe library currently faces the following limitations:\n- Missing support for push promises.\n- Missing support for reading the remote SETTINGS from application side.\n- The scheduling of outgoing DATA frames is very basic and relies mostly on flow\n  control windows and the maximum supported frame size. It is currently not\n  guaranteed that concurrent streams with equal flow control windows will get\n  the same amount of bandwith.\n  HTTP/2 priorization features are also not supported - however these are optional\n  according to the HTTP/2 specification and may not be required for lots of\n  applications.","funding_links":[],"categories":["C# #"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FMatthias247%2Fhttp2dotnet","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FMatthias247%2Fhttp2dotnet","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FMatthias247%2Fhttp2dotnet/lists"}