// note that this may behave strangely when used for the initial upgrade // message when using h2c, since that message is ineligible for flow // control, but there is not yet an API for signaling that. internal static void Handle(IChannelHandlerContext ctx, IHttp2Connection connection, IHttp2FrameListener listener, IFullHttpMessage message) { try { int streamId = GetStreamId(connection, message.Headers); IHttp2Stream stream = connection.Stream(streamId); if (stream is null) { stream = connection.Remote.CreateStream(streamId, false); } _ = message.Headers.Set(HttpConversionUtil.ExtensionHeaderNames.Scheme, HttpScheme.Http.Name); IHttp2Headers messageHeaders = HttpConversionUtil.ToHttp2Headers(message, true); var hasContent = message.Content.IsReadable(); var hasTrailers = !message.TrailingHeaders.IsEmpty; listener.OnHeadersRead(ctx, streamId, messageHeaders, 0, !(hasContent || hasTrailers)); if (hasContent) { _ = listener.OnDataRead(ctx, streamId, message.Content, 0, !hasTrailers); } if (hasTrailers) { IHttp2Headers headers = HttpConversionUtil.ToHttp2Headers(message.TrailingHeaders, true); listener.OnHeadersRead(ctx, streamId, headers, 0, true); } _ = stream.CloseRemoteSide(); } finally { _ = message.Release(); } }
/// <summary> /// Handles conversion of <see cref="IHttpMessage"/> and <see cref="IHttpContent"/> to HTTP/2 frames. /// </summary> public override void Write(IChannelHandlerContext ctx, object msg, IPromise promise) { var httpMsg = msg as IHttpMessage; var contentMsg = msg as IHttpContent; if (httpMsg is null && contentMsg is null) { _ = ctx.WriteAsync(msg, promise); return; } var release = true; var promiseAggregator = new SimplePromiseAggregator(promise); try { var encoder = Encoder; var endStream = false; if (httpMsg is object) { // Provide the user the opportunity to specify the streamId _currentStreamId = GetStreamId(httpMsg.Headers); // Convert and write the headers. var http2Headers = HttpConversionUtil.ToHttp2Headers(httpMsg, _validateHeaders); endStream = msg is IFullHttpMessage fullHttpMsg && !fullHttpMsg.Content.IsReadable(); WriteHeaders(ctx, encoder, _currentStreamId, httpMsg.Headers, http2Headers, endStream, promiseAggregator); } if (!endStream && contentMsg is object) { var isLastContent = false; HttpHeaders trailers = EmptyHttpHeaders.Default; IHttp2Headers http2Trailers = EmptyHttp2Headers.Instance; if (msg is ILastHttpContent lastContentMsg) { isLastContent = true; // Convert any trailing headers. trailers = lastContentMsg.TrailingHeaders; http2Trailers = HttpConversionUtil.ToHttp2Headers(trailers, _validateHeaders); } // Write the data var content = contentMsg.Content; endStream = isLastContent && trailers.IsEmpty; _ = encoder.WriteDataAsync(ctx, _currentStreamId, content, 0, endStream, promiseAggregator.NewPromise()); release = false; if (!trailers.IsEmpty) { // Write trailing headers. WriteHeaders(ctx, encoder, _currentStreamId, trailers, http2Trailers, true, promiseAggregator); } } } catch (Exception t) { OnError(ctx, true, t); promiseAggregator.SetException(t); } finally { if (release) { _ = ReferenceCountUtil.Release(msg); } _ = promiseAggregator.DoneAllocatingPromises(); } }