Exemplo n.º 1
0
 /// <summary>
 /// Translate and add HTTP/2 headers to HTTP/1.x headers.
 /// </summary>
 /// <param name="streamId">The stream associated with <paramref name="sourceHeaders"/>.</param>
 /// <param name="sourceHeaders">The HTTP/2 headers to convert.</param>
 /// <param name="destinationMessage">The object which will contain the resulting HTTP/1.x headers.</param>
 /// <param name="addToTrailer"><c>true</c> to add to trailing headers. <c>false</c> to add to initial headers.</param>
 /// <exception cref="Http2Exception">If not all HTTP/2 headers can be translated to HTTP/1.x.</exception>
 public static void AddHttp2ToHttpHeaders(int streamId, IHttp2Headers sourceHeaders,
                                          IFullHttpMessage destinationMessage, bool addToTrailer)
 {
     AddHttp2ToHttpHeaders(streamId, sourceHeaders,
                           addToTrailer ? destinationMessage.TrailingHeaders : destinationMessage.Headers,
                           destinationMessage.ProtocolVersion, addToTrailer, destinationMessage is IHttpRequest);
 }
        public override int OnDataRead(IChannelHandlerContext ctx, int streamId, IByteBuffer data, int padding, bool endOfStream)
        {
            IHttp2Stream     stream = _connection.Stream(streamId);
            IFullHttpMessage msg    = GetMessage(stream);

            if (msg is null)
            {
                ThrowHelper.ThrowConnectionError_DataFrameReceivedForUnknownStream(streamId);
            }

            var content           = msg.Content;
            int dataReadableBytes = data.ReadableBytes;

            if (content.ReadableBytes > _maxContentLength - dataReadableBytes)
            {
                ThrowHelper.ThrowConnectionError_ContentLengthExceededMax(_maxContentLength, streamId);
            }

            _ = content.WriteBytes(data, data.ReaderIndex, dataReadableBytes);

            if (endOfStream)
            {
                FireChannelRead(ctx, msg, false, stream);
            }

            // All bytes have been processed.
            return(dataReadableBytes + padding);
        }
        /// <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);
        }
        public override void OnPushPromiseRead(IChannelHandlerContext ctx, int streamId, int promisedStreamId, IHttp2Headers headers, int padding)
        {
            // A push promise should not be allowed to add headers to an existing stream
            IHttp2Stream promisedStream = _connection.Stream(promisedStreamId);

            if (headers.Status is null)
            {
                // A PUSH_PROMISE frame has no Http response status.
                // https://tools.ietf.org/html/rfc7540#section-8.2.1
                // Server push is semantically equivalent to a server responding to a
                // request; however, in this case, that request is also sent by the
                // server, as a PUSH_PROMISE frame.
                headers.Status = HttpResponseStatus.OK.CodeAsText;
            }
            IFullHttpMessage msg = ProcessHeadersBegin(ctx, promisedStream, headers, false, false, false);

            if (msg is null)
            {
                ThrowHelper.ThrowConnectionError_PushPromiseFrameReceivedForPreExistingStreamId(promisedStreamId);
            }

            _ = msg.Headers.SetInt(HttpConversionUtil.ExtensionHeaderNames.StreamPromiseId, streamId);
            _ = msg.Headers.SetShort(HttpConversionUtil.ExtensionHeaderNames.StreamWeight, Http2CodecUtil.DefaultPriorityWeight);

            ProcessHeadersEnd(ctx, promisedStream, msg, false);
        }
Exemplo n.º 5
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>
        /// The stream is out of scope for the HTTP message flow and will no longer be tracked
        /// </summary>
        /// <param name="stream">The stream to remove associated state with</param>
        /// <param name="release"><c>true</c> to call release on the value if it is present. <c>false</c> to not call release.</param>
        protected void RemoveMessage(IHttp2Stream stream, bool release)
        {
            IFullHttpMessage msg = stream.RemoveProperty <IFullHttpMessage>(_messageKey);

            if (release && msg is object)
            {
                _ = msg.Release();
            }
        }
        /// <summary>
        /// Make <paramref name="message"/> be the state associated with <paramref name="stream"/>.
        /// </summary>
        /// <param name="stream">The stream which <paramref name="message"/> is associated with.</param>
        /// <param name="message">The message which contains the HTTP semantics.</param>
        protected void PutMessage(IHttp2Stream stream, IFullHttpMessage message)
        {
            IFullHttpMessage previous = stream.SetProperty <IFullHttpMessage>(_messageKey, message);

            if (previous != message && previous is object)
            {
                _ = previous.Release();
            }
        }
        public override void OnHeadersRead(IChannelHandlerContext ctx, int streamId, IHttp2Headers headers, int padding, bool endOfStream)
        {
            IHttp2Stream     stream = _connection.Stream(streamId);
            IFullHttpMessage msg    = ProcessHeadersBegin(ctx, stream, headers, endOfStream, true, true);

            if (msg is object)
            {
                ProcessHeadersEnd(ctx, stream, msg, endOfStream);
            }
        }
 public IFullHttpMessage CopyIfNeeded(IByteBufferAllocator allocator, IFullHttpMessage msg)
 {
     if (msg is IFullHttpRequest request)
     {
         var copy = (IFullHttpRequest)request.Replace(allocator.Buffer(0));
         _ = copy.Headers.Remove(HttpHeaderNames.Expect);
         return(copy);
     }
     return(null);
 }
        public override void OnRstStreamRead(IChannelHandlerContext ctx, int streamId, Http2Error errorCode)
        {
            IHttp2Stream     stream = _connection.Stream(streamId);
            IFullHttpMessage msg    = GetMessage(stream);

            if (msg is object)
            {
                OnRstStreamRead(stream, msg);
            }
            _ = ctx.FireExceptionCaught(ThrowHelper.GetStreamError_Http2ToHttpLayerCaughtStreamReset(streamId, errorCode));
        }
Exemplo n.º 11
0
 static void AppendFullCommon(StringBuilder buf, IFullHttpMessage msg)
 {
     _ = buf.Append(StringUtil.SimpleClassName(msg));
     _ = buf.Append("(decodeResult: ");
     _ = buf.Append(msg.Result);
     _ = buf.Append(", version: ");
     _ = buf.Append(msg.ProtocolVersion);
     _ = buf.Append(", content: ");
     _ = buf.Append(msg.Content);
     _ = buf.Append(')');
     _ = buf.Append(StringUtil.Newline);
 }
            public bool MustSendImmediately(IFullHttpMessage msg)
            {
                switch (msg)
                {
                case IFullHttpResponse response:
                    return(response.Status.CodeClass == HttpStatusClass.Informational);

                case IFullHttpRequest _:
                    return(msg.Headers.Contains(HttpHeaderNames.Expect));

                default:
                    return(false);
                }
            }
        public override void ChannelRead(IChannelHandlerContext context, object message)
        {
            IFullHttpMessage fm = message as IFullHttpMessage;

            try
            {
                Task.Run(async() =>
                {
                    HandleProxy.OnEnd(context, message);
                }
                         );
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
        public override void OnHeadersRead(IChannelHandlerContext ctx, int streamId, IHttp2Headers headers, int streamDependency, short weight, bool exclusive, int padding, bool endOfStream)
        {
            IHttp2Stream     stream = _connection.Stream(streamId);
            IFullHttpMessage msg    = ProcessHeadersBegin(ctx, stream, headers, endOfStream, true, true);

            if (msg is object)
            {
                // Add headers for dependency and weight.
                // See https://github.com/netty/netty/issues/5866
                if (streamDependency != Http2CodecUtil.ConnectionStreamId)
                {
                    _ = msg.Headers.SetInt(HttpConversionUtil.ExtensionHeaderNames.StreamDependencyId, streamDependency);
                }
                _ = msg.Headers.SetShort(HttpConversionUtil.ExtensionHeaderNames.StreamWeight, weight);

                ProcessHeadersEnd(ctx, stream, msg, endOfStream);
            }
        }
        public void ServerResponseHeaderInformational()
        {
            this.BootstrapEnv(1, 2, 1, 2, 1);
            IFullHttpMessage request     = new DefaultFullHttpRequest(DotNetty.Codecs.Http.HttpVersion.Http11, HttpMethod.Put, "/info/test", true);
            HttpHeaders      httpHeaders = request.Headers;

            httpHeaders.SetInt(HttpConversionUtil.ExtensionHeaderNames.StreamId, 3);
            httpHeaders.Set(HttpHeaderNames.Expect, HttpHeaderValues.Continue);
            httpHeaders.SetInt(HttpHeaderNames.ContentLength, 0);
            httpHeaders.SetShort(HttpConversionUtil.ExtensionHeaderNames.StreamWeight, 16);

            IHttp2Headers http2Headers = new DefaultHttp2Headers()
            {
                Method = new AsciiString("PUT"),
                Path   = new AsciiString("/info/test")
            };

            http2Headers.Set(new AsciiString(HttpHeaderNames.Expect.ToString()), new AsciiString(HttpHeaderValues.Continue.ToString()));
            IFullHttpMessage response  = new DefaultFullHttpResponse(DotNetty.Codecs.Http.HttpVersion.Http11, HttpResponseStatus.Continue);
            string           text      = "a big payload";
            IByteBuffer      payload   = Unpooled.CopiedBuffer(Encoding.UTF8.GetBytes(text));
            IFullHttpMessage request2  = (IFullHttpMessage)request.Replace(payload);
            IFullHttpMessage response2 = new DefaultFullHttpResponse(DotNetty.Codecs.Http.HttpVersion.Http11, HttpResponseStatus.OK);

            try
            {
                Http2TestUtil.RunInChannel(this.clientChannel, () =>
                {
                    this.clientHandler.Encoder.WriteHeadersAsync(this.CtxClient(), 3, http2Headers, 0, false, this.NewPromiseClient());
                    this.clientChannel.Flush();
                });

                this.AwaitRequests();
                httpHeaders = response.Headers;
                httpHeaders.SetInt(HttpConversionUtil.ExtensionHeaderNames.StreamId, 3);
                httpHeaders.SetInt(HttpHeaderNames.ContentLength, 0);
                IHttp2Headers http2HeadersResponse = new DefaultHttp2Headers()
                {
                    Status = new AsciiString("100")
                };
                Http2TestUtil.RunInChannel(this.serverConnectedChannel, () =>
                {
                    this.serverHandler.Encoder.WriteHeadersAsync(this.CtxServer(), 3, http2HeadersResponse, 0, false, this.NewPromiseServer());
                    this.serverConnectedChannel.Flush();
                });

                this.AwaitResponses();
                httpHeaders = request2.Headers;
                httpHeaders.SetInt(HttpHeaderNames.ContentLength, text.Length);
                httpHeaders.Remove(HttpHeaderNames.Expect);
                Http2TestUtil.RunInChannel(this.clientChannel, () =>
                {
                    this.clientHandler.Encoder.WriteDataAsync(this.CtxClient(), 3, payload.RetainedDuplicate(), 0, true, this.NewPromiseClient());
                    this.clientChannel.Flush();
                });

                this.AwaitRequests2();
                httpHeaders = response2.Headers;
                httpHeaders.SetInt(HttpConversionUtil.ExtensionHeaderNames.StreamId, 3);
                httpHeaders.SetInt(HttpHeaderNames.ContentLength, 0);
                httpHeaders.SetShort(HttpConversionUtil.ExtensionHeaderNames.StreamWeight, 16);

                IHttp2Headers http2HeadersResponse2 = new DefaultHttp2Headers()
                {
                    Status = new AsciiString("200")
                };
                Http2TestUtil.RunInChannel(this.serverConnectedChannel, () =>
                {
                    this.serverHandler.Encoder.WriteHeadersAsync(this.CtxServer(), 3, http2HeadersResponse2, 0, true, this.NewPromiseServer());
                    this.serverConnectedChannel.Flush();
                });

                this.AwaitResponses2();
                var requestCaptor = new ArgumentCaptor <IFullHttpMessage>();
                this.serverListener.Verify(x => x.MessageReceived(It.Is <IHttpObject>(v => requestCaptor.Capture((IFullHttpMessage)v))), Times.Exactly(2));
                this.capturedRequests = requestCaptor.GetAllValues();
                Assert.Equal(2, this.capturedRequests.Count);
                // We do not expect to have this header in the captured request so remove it now.
                Assert.NotNull(request.Headers.Remove((AsciiString)"x-http2-stream-weight"));

                Assert.Equal(request, (IFullHttpRequest)this.capturedRequests[0]);
                Assert.Equal(request2, this.capturedRequests[1]);

                var responseCaptor = new ArgumentCaptor <IFullHttpMessage>();
                this.clientListener.Verify(x => x.MessageReceived(It.Is <IHttpObject>(v => responseCaptor.Capture((IFullHttpMessage)v))), Times.Exactly(2));
                this.capturedResponses = responseCaptor.GetAllValues();
                Assert.Equal(2, this.capturedResponses.Count);
                Assert.Equal(response, this.capturedResponses[0]);
                Assert.Equal(response2, this.capturedResponses[1]);
            }
            finally
            {
                request.Release();
                request2.Release();
                response.Release();
                response2.Release();
            }
        }
 /// <summary>
 /// Set final headers and fire a channel read event
 /// </summary>
 /// <param name="ctx">The context to fire the event on</param>
 /// <param name="msg">The message to send</param>
 /// <param name="release"><c>true</c> to call release on the value if it is present. <c>false</c> to not call release.</param>
 /// <param name="stream">the stream of the message which is being fired</param>
 protected virtual void FireChannelRead(IChannelHandlerContext ctx, IFullHttpMessage msg, bool release, IHttp2Stream stream)
 {
     RemoveMessage(stream, release);
     HttpUtil.SetContentLength(msg, msg.Content.ReadableBytes);
     _ = ctx.FireChannelRead(msg);
 }
 /// <summary>
 /// Called if a <c>RST_STREAM</c> is received but we have some data for that stream.
 /// </summary>
 /// <param name="stream"></param>
 /// <param name="msg"></param>
 protected virtual void OnRstStreamRead(IHttp2Stream stream, IFullHttpMessage msg)
 {
     RemoveMessage(stream, true);
 }
 /// <summary>
 /// After HTTP/2 headers have been processed by <see cref="ProcessHeadersBegin"/> this method either
 /// sends the result up the pipeline or retains the message for future processing.
 /// </summary>
 /// <param name="ctx">The context for which this message has been received</param>
 /// <param name="stream">The stream the <paramref name="msg"/> corresponds to</param>
 /// <param name="msg">The object which represents all headers/data for corresponding to <paramref name="stream"/>.</param>
 /// <param name="endOfStream"><c>true</c> if this is the last event for the stream</param>
 private void ProcessHeadersEnd(IChannelHandlerContext ctx, IHttp2Stream stream, IFullHttpMessage msg, bool endOfStream)
 {
     if (endOfStream)
     {
         // Release if the msg from the map is different from the object being forwarded up the pipeline.
         FireChannelRead(ctx, msg, GetMessage(stream) != msg, stream);
     }
     else
     {
         PutMessage(stream, msg);
     }
 }