private void WriteHeadersFrame(IChannelHandlerContext ctx, IHttp2HeadersFrame headersFrame, IPromise promise) { if (Http2CodecUtil.IsStreamIdValid(headersFrame.Stream.Id)) { _ = Encoder.WriteHeadersAsync(ctx, headersFrame.Stream.Id, headersFrame.Headers, headersFrame.Padding, headersFrame.IsEndStream, promise); } else { var stream = (DefaultHttp2FrameStream)headersFrame.Stream; var connection = Connection; var streamId = connection.Local.IncrementAndGetNextStreamId; if (streamId < 0) { promise.SetException(new Http2NoMoreStreamIdsException()); // Simulate a GOAWAY being received due to stream exhaustion on this connection. We use the maximum // valid stream ID for the current peer. OnHttp2Frame(ctx, new DefaultHttp2GoAwayFrame(connection.IsServer ? int.MaxValue : int.MaxValue - 1, Http2Error.NoError, ByteBufferUtil.WriteAscii(ctx.Allocator, "Stream IDs exhausted on local stream creation"))); return; } stream.Id = streamId; // Use a Map to store all pending streams as we may have multiple. This is needed as if we would store the // stream in a field directly we may override the stored field before onStreamAdded(...) was called // and so not correctly set the property for the buffered stream. // // See https://github.com/netty/netty/issues/8692 var result = _frameStreamToInitializeMap.TryAdd(streamId, stream); // We should not re-use ids. Debug.Assert(result); _ = Encoder.WriteHeadersAsync(ctx, streamId, headersFrame.Headers, headersFrame.Padding, headersFrame.IsEndStream, promise); if (!promise.IsCompleted) { _ = Interlocked.Increment(ref v_numBufferedStreams); // Clean up the stream being initialized if writing the headers fails and also // decrement the number of buffered streams. _ = promise.Task.ContinueWith(ResetNufferedStreamsAction, (this, streamId), TaskContinuationOptions.ExecuteSynchronously); } else { HandleHeaderFuture(promise.Task, streamId); } } }