/// <summary>
        /// Provides translation between HTTP/2 and HTTP header objects while ensuring the stream
        /// is in a valid state for additional headers.
        /// </summary>
        /// <param name="ctx">The context for which this message has been received.
        /// Used to send informational header if detected.</param>
        /// <param name="stream">The stream the <paramref name="headers"/> apply to</param>
        /// <param name="headers">The headers to process</param>
        /// <param name="endOfStream"><c>true</c> if the <paramref name="stream"/> has received the end of stream flag</param>
        /// <param name="allowAppend"><c>true</c> if headers will be appended if the stream already exists.
        /// <para>if <c>false</c> and the stream already exists this method returns <c>null</c>.</para></param>
        /// <param name="appendToTrailer"><c>true</c> if a message <paramref name="stream"/> already exists then the headers
        /// should be added to the trailing headers.
        /// <para><c>false</c> then appends will be done to the initial headers.</para></param>
        /// <returns>The object used to track the stream corresponding to <paramref name="stream"/>. <c>null</c> if
        /// <paramref name="allowAppend"/> is <c>false</c> and the stream already exists.</returns>
        /// <exception cref="Http2Exception">If the stream id is not in the correct state to process the headers request</exception>
        protected virtual IFullHttpMessage ProcessHeadersBegin(IChannelHandlerContext ctx, IHttp2Stream stream, IHttp2Headers headers,
                                                               bool endOfStream, bool allowAppend, bool appendToTrailer)
        {
            IFullHttpMessage msg = GetMessage(stream);
            var release          = true;

            if (msg is null)
            {
                msg = NewMessage(stream, headers, _validateHttpHeaders, ctx.Allocator);
            }
            else if (allowAppend)
            {
                release = false;
                HttpConversionUtil.AddHttp2ToHttpHeaders(stream.Id, headers, msg, appendToTrailer);
            }
            else
            {
                release = false;
                msg     = null;
            }

            if (_sendDetector.MustSendImmediately(msg))
            {
                // Copy the message (if necessary) before sending. The content is not expected to be copied (or used) in
                // this operation but just in case it is used do the copy before sending and the resource may be released
                IFullHttpMessage copy = endOfStream ? null : _sendDetector.CopyIfNeeded(ctx.Allocator, msg);
                FireChannelRead(ctx, msg, release, stream);
                return(copy);
            }

            return(msg);
        }
Beispiel #2
0
 // 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>
 /// Create a new <see cref="IFullHttpMessage"/> based upon the current connection parameters
 /// </summary>
 /// <param name="stream">The stream to create a message for</param>
 /// <param name="headers">The headers associated with <paramref name="stream"/>.</param>
 /// <param name="validateHttpHeaders"><c>true</c> to validate HTTP headers in the http-codec
 /// <para><c>false</c> not to validate HTTP headers in the http-codec</para></param>
 /// <param name="alloc">The <see cref="IByteBufferAllocator"/> to use to generate the content of the message</param>
 /// <returns></returns>
 protected virtual IFullHttpMessage NewMessage(IHttp2Stream stream, IHttp2Headers headers, bool validateHttpHeaders,
                                               IByteBufferAllocator alloc)
 {
     if (_connection.IsServer)
     {
         return(HttpConversionUtil.ToFullHttpRequest(stream.Id, headers, alloc, validateHttpHeaders));
     }
     return(HttpConversionUtil.ToFullHttpResponse(stream.Id, headers, alloc, validateHttpHeaders));
 }
        /// <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();
            }
        }