Пример #1
0
        public async Task ClosedStream_FrameReceived_ResetsStream()
        {
            HttpClientHandler handler = CreateHttpClientHandler();

            handler.ServerCertificateCustomValidationCallback = TestHelper.AllowAllCertificates;

            using (var server = Http2LoopbackServer.CreateServer())
                using (var client = new HttpClient(handler))
                {
                    Task sendTask = client.GetAsync(server.Address);

                    await server.EstablishConnectionAsync();

                    await server.ReadRequestHeaderAsync();

                    // Send a reset stream frame so that stream 1 moves to a terminal state.
                    RstStreamFrame resetStream = new RstStreamFrame(FrameFlags.Padded, 0x1, 1);
                    await server.WriteFrameAsync(resetStream);

                    // Send a frame on the now-closed stream.
                    DataFrame invalidFrame = new DataFrame(new byte[10], FrameFlags.Padded, 10, 1);
                    await server.WriteFrameAsync(invalidFrame);

                    // Receive a RST_STREAM frame.
                    Frame receivedFrame = await server.ReadFrameAsync(TimeSpan.FromSeconds(30));

                    Assert.Equal(FrameType.RstStream, receivedFrame.Type);

                    await Assert.ThrowsAsync <HttpRequestException>(async() => await sendTask);
                }
        }
Пример #2
0
        public async Task Http2_StreamResetByServerAfterPartialBodySent_RequestFails()
        {
            HttpClientHandler handler = CreateHttpClientHandler();

            handler.ServerCertificateCustomValidationCallback = TestHelper.AllowAllCertificates;

            using (var server = Http2LoopbackServer.CreateServer())
                using (var client = new HttpClient(handler))
                {
                    Task <HttpResponseMessage> sendTask = client.GetAsync(server.Address);

                    await server.EstablishConnectionAsync();

                    int streamId = await server.ReadRequestHeaderAsync();

                    // Send response headers and partial response body
                    await server.SendDefaultResponseHeadersAsync(streamId);

                    DataFrame dataFrame = new DataFrame(new byte[10], FrameFlags.None, 0, streamId);
                    await server.WriteFrameAsync(dataFrame);

                    // Send a reset stream frame so that the stream moves to a terminal state.
                    RstStreamFrame resetStream = new RstStreamFrame(FrameFlags.None, 0x2, streamId);
                    await server.WriteFrameAsync(resetStream);

                    await Assert.ThrowsAsync <HttpRequestException>(async() => await sendTask);
                }
        }
Пример #3
0
        public async Task ResetResponseStream_FrameReceived_ConnectionError()
        {
            HttpClientHandler handler = CreateHttpClientHandler();

            handler.ServerCertificateCustomValidationCallback = TestHelper.AllowAllCertificates;

            using (var server = Http2LoopbackServer.CreateServer())
                using (var client = new HttpClient(handler))
                {
                    Task <HttpResponseMessage> sendTask = client.GetAsync(server.Address);

                    await server.EstablishConnectionAsync();

                    int streamId = await server.ReadRequestHeaderAsync();

                    await server.SendDefaultResponseHeadersAsync(streamId);

                    // Send a reset stream frame so that stream 1 moves to a terminal state.
                    RstStreamFrame resetStream = new RstStreamFrame(FrameFlags.None, 0x1, streamId);
                    await server.WriteFrameAsync(resetStream);

                    await Assert.ThrowsAsync <HttpRequestException>(async() => await sendTask);

                    // Send a frame on the now-closed stream.
                    DataFrame invalidFrame = new DataFrame(new byte[10], FrameFlags.None, 0, streamId);
                    await server.WriteFrameAsync(invalidFrame);

                    if (!IsWinHttpHandler)
                    {
                        // The client should close the connection as this is a fatal connection level error.
                        Assert.Null(await server.ReadFrameAsync(TimeSpan.FromSeconds(30)));
                    }
                }
        }
Пример #4
0
        private void HandleRstFrame(RstStreamFrame resetFrame, out Http2Stream stream)
        {
            Http2Logger.LogDebug("RST_STREAM frame: stream id={0}, status code={1}",
                                 resetFrame.StreamId, resetFrame.StatusCode);

            /* 12 -> 6.4
             * RST_STREAM frames MUST be associated with a stream.  If a RST_STREAM
             * frame is received with a stream identifier of 0x0, the recipient MUST
             * treat this as a connection error of type PROTOCOL_ERROR. */
            if (resetFrame.StreamId == 0)
            {
                throw new ProtocolError(ResetStatusCode.ProtocolError, "Rst frame with stream id=0");
            }

            stream = GetStream(resetFrame.StreamId);

            if (stream.Closed)
            {
                /* 12 -> 5.4.2
                 * An endpoint MUST NOT send a RST_STREAM in response to an RST_STREAM
                 * frame, to avoid looping. */
                if (!stream.WasRstSent)
                {
                    throw new Http2StreamNotFoundException(resetFrame.StreamId);
                }
                return;
            }

            if (!(stream.ReservedRemote || stream.Opened || stream.HalfClosedLocal))
            {
                throw new ProtocolError(ResetStatusCode.ProtocolError, "Rst for non opened or reserved stream");
            }

            stream.Close(ResetStatusCode.None);
        }
Пример #5
0
        public async Task Http2_StreamResetByServerBeforeHeadersSent_RequestFails()
        {
            if (IsWinHttpHandler)
            {
                // WinHTTP does not genenerate an exception here.
                // It seems to ignore a RST_STREAM sent before headers are sent, and continue to wait for HEADERS.
                return;
            }

            HttpClientHandler handler = CreateHttpClientHandler();

            handler.ServerCertificateCustomValidationCallback = TestHelper.AllowAllCertificates;

            using (var server = Http2LoopbackServer.CreateServer())
                using (var client = new HttpClient(handler))
                {
                    Task <HttpResponseMessage> sendTask = client.GetAsync(server.Address);

                    await server.EstablishConnectionAsync();

                    int streamId = await server.ReadRequestHeaderAsync();

                    // Send a reset stream frame so that the stream moves to a terminal state.
                    RstStreamFrame resetStream = new RstStreamFrame(FrameFlags.None, 0x2, streamId);
                    await server.WriteFrameAsync(resetStream);

                    await Assert.ThrowsAsync <HttpRequestException>(async() => await sendTask);
                }
        }
Пример #6
0
        public async Task Http2_StreamResetByServer_RequestFails()
        {
            HttpClientHandler handler = CreateHttpClientHandler();

            handler.ServerCertificateCustomValidationCallback = TestHelper.AllowAllCertificates;

            using (var server = Http2LoopbackServer.CreateServer())
                using (var client = new HttpClient(handler))
                {
                    Task sendTask = client.GetAsync(server.Address);

                    await server.AcceptConnectionAsync();

                    await server.SendConnectionPrefaceAsync();

                    // Receive the request header frame.
                    Frame receivedFrame = await server.ReadFrameAsync(TimeSpan.FromSeconds(30));

                    // Send a reset stream frame so that stream 1 moves to a terminal state.
                    RstStreamFrame resetStream = new RstStreamFrame(FrameFlags.Padded, 0x1, 1);
                    await server.WriteFrameAsync(resetStream);

                    // This currently throws an IOException.
                    await Assert.ThrowsAsync <HttpRequestException>(async() => await sendTask);
                }
        }
Пример #7
0
        /// <summary>
        /// Proess close rest stream frame.
        /// </summary>
        /// <param name="httpContext">The current http context.</param>
        /// <param name="errorCode">Error code registry.</param>
        /// <param name="streamID">The current stream id.</param>
        internal static void ProcessCloseRstStreamFrame(Nequeo.Net.Http2.HttpContext httpContext, ErrorCodeRegistry errorCode, int streamID)
        {
            // Create the reset stream frame response.
            RstStreamFrame frame = new RstStreamFrame(streamID, errorCode);

            // Write the frame.
            httpContext.ResponseWrite(frame.Buffer);
        }
Пример #8
0
        /// <summary>
        /// Process stream not found frame.
        /// </summary>
        /// <param name="httpContext">The current http context.</param>
        /// <param name="streamID">The current stream id.</param>
        private static void ProcessStreamNotFoundFrame(Nequeo.Net.Http2.HttpContext httpContext, int streamID)
        {
            // Create the reset stream frame response.
            RstStreamFrame frame = new RstStreamFrame(streamID, ErrorCodeRegistry.Stream_Closed);

            // Write the frame.
            httpContext.ResponseWrite(frame.Buffer);
        }
Пример #9
0
        public void WriteRst(ResetStatusCode code)
        {
            var frame = new RstStreamFrame(_id, code);

            _writeQueue.WriteFrame(frame);
            ResetSent = true;

            if (OnFrameSent != null)
            {
                OnFrameSent(this, new FrameSentArgs(frame));
            }
        }
Пример #10
0
        public void WriteRst(ResetStatusCode code)
        {
            if (Closed)
            {
                return;
            }

            var frame = new RstStreamFrame(_id, code);

            _writeQueue.WriteFrame(frame);

            if (OnFrameSent != null)
            {
                OnFrameSent(this, new FrameSentEventArgs(frame));
            }
        }
Пример #11
0
        public async Task ResetResponseStream_FrameReceived_ResetsStream()
        {
            HttpClientHandler handler = CreateHttpClientHandler();

            handler.ServerCertificateCustomValidationCallback = TestHelper.AllowAllCertificates;

            using (var server = Http2LoopbackServer.CreateServer())
                using (var client = new HttpClient(handler))
                {
                    Task <HttpResponseMessage> sendTask = client.GetAsync(server.Address);

                    await server.EstablishConnectionAsync();

                    int streamId = await server.ReadRequestHeaderAsync();

                    // Send a reset stream frame so that stream 1 moves to a terminal state.
                    RstStreamFrame resetStream = new RstStreamFrame(FrameFlags.None, 0x1, streamId);
                    await server.WriteFrameAsync(resetStream);

                    await Assert.ThrowsAsync <HttpRequestException>(async() => await sendTask);

                    // Send a frame on the now-closed stream.
                    DataFrame invalidFrame = new DataFrame(new byte[10], FrameFlags.None, 0, streamId);
                    await server.WriteFrameAsync(invalidFrame);

                    // Receive a RST_STREAM frame.
                    Frame receivedFrame = await server.ReadFrameAsync(TimeSpan.FromSeconds(30));

                    Assert.Equal(FrameType.RstStream, receivedFrame.Type);
                    Assert.Equal(streamId, receivedFrame.StreamId);

                    // Connection should still be usable.
                    sendTask = client.GetAsync(server.Address);
                    streamId = await server.ReadRequestHeaderAsync();

                    await server.SendDefaultResponseAsync(streamId);

                    HttpResponseMessage response = await sendTask;
                    Assert.Equal(HttpStatusCode.OK, response.StatusCode);
                }
        }
Пример #12
0
        public async Task Http2_StreamResetByServer_RequestFails()
        {
            HttpClientHandler handler = CreateHttpClientHandler();

            handler.ServerCertificateCustomValidationCallback = TestHelper.AllowAllCertificates;

            using (var server = Http2LoopbackServer.CreateServer())
                using (var client = new HttpClient(handler))
                {
                    Task sendTask = client.GetAsync(server.Address);

                    await server.EstablishConnectionAsync();

                    await server.ReadRequestHeaderAsync();

                    // Send a reset stream frame so that stream 1 moves to a terminal state.
                    RstStreamFrame resetStream = new RstStreamFrame(FrameFlags.Padded, 0x1, 1);
                    await server.WriteFrameAsync(resetStream);

                    await Assert.ThrowsAsync <HttpRequestException>(async() => await sendTask);
                }
        }
Пример #13
0
        /// <summary>
        /// Process a reset stream frame request.
        /// </summary>
        /// <param name="httpContext">The current http context.</param>
        /// <param name="resetFrame">The reset stream frame.</param>
        /// <param name="stream">The selected stream context.</param>
        private static void ProcessResetStreamFrameRequest(Nequeo.Net.Http2.HttpContext httpContext, RstStreamFrame resetFrame, out ContextStream stream)
        {
            /*  RST_STREAM frames MUST be associated with a stream.  If a RST_STREAM
             *  frame is received with a stream identifier of 0x0, the recipient MUST
             *  treat this as a connection error of type PROTOCOL_ERROR. */
            if (resetFrame.StreamId == 0)
            {
                throw new ProtocolError(ErrorCodeRegistry.Protocol_Error, "Reset stream frame with stream id is equal to 0.");
            }

            // Attempt to get the sepcific stream.
            stream = httpContext.GetStream(resetFrame.StreamId);
            if (stream == null)
            {
                throw new MaxConcurrentStreamsLimitException();
            }

            // If the stream is closed.
            if (stream.Closed)
            {
                /* 12 -> 5.4.2
                 * An endpoint MUST NOT send a RST_STREAM in response to an RST_STREAM
                 * frame, to avoid looping. */
                if (!stream.WasResetSent)
                {
                    throw new StreamNotFoundException(resetFrame.StreamId);
                }
                return;
            }

            if (!(stream.ReservedRemote || stream.Opened || stream.HalfClosedLocal))
            {
                throw new ProtocolError(ErrorCodeRegistry.Protocol_Error, "Reset stream for non opened or reserved stream.");
            }

            // Close the current stream.
            ProcessClose(httpContext, ErrorCodeRegistry.No_Error, stream);
        }
Пример #14
0
        /// <summary>
        /// Dispatches the incoming frame.
        /// </summary>
        /// <param name="frame">The frame.</param>
        /// <exception cref="System.NotImplementedException"></exception>
        private void DispatchIncomingFrame(Frame frame)
        {
            Http2Stream stream = null;

            try
            {
                if (frame.PayloadLength > Constants.MaxFramePayloadSize)
                {
                    throw new ProtocolError(ResetStatusCode.FrameSizeError,
                                            String.Format("Frame too large: Type: {0} {1}", frame.FrameType,
                                                          frame.PayloadLength));
                }

                /* 12 -> 6.5
                 * A SETTINGS frame MUST be sent by both endpoints at the start of a
                 * connection, and MAY be sent at any other time by either endpoint over
                 * the lifetime of the connection.*/
                if (frame.FrameType != FrameType.Settings && !_wasSettingsReceived)
                {
                    throw new ProtocolError(ResetStatusCode.ProtocolError,
                                            "Settings frame was not the first frame in the session");
                }

                Http2Logger.LogDebug("Incoming frame: " + frame.FrameType);

                switch (frame.FrameType)
                {
                case FrameType.Headers:
                    HandleHeaders(frame as HeadersFrame, out stream);
                    break;

                case FrameType.Continuation:
                    HandleContinuation(frame as ContinuationFrame, out stream);
                    break;

                case FrameType.Priority:
                    HandlePriority(frame as PriorityFrame, out stream);
                    break;

                case FrameType.RstStream:
                    HandleRstFrame(frame as RstStreamFrame, out stream);
                    break;

                case FrameType.Data:
                    HandleDataFrame(frame as DataFrame, out stream);
                    break;

                case FrameType.Ping:
                    HandlePingFrame(frame as PingFrame);
                    break;

                case FrameType.Settings:
                    HandleSettingsFrame(frame as SettingsFrame);
                    if (!(frame as SettingsFrame).IsAck)
                    {
                        // sending ACK settings
                        WriteSettings(new SettingsPair[0], true);
                    }
                    break;

                case FrameType.WindowUpdate:
                    HandleWindowUpdateFrame(frame as WindowUpdateFrame, out stream);
                    break;

                case FrameType.GoAway:
                    HandleGoAwayFrame(frame as GoAwayFrame);
                    break;

                case FrameType.PushPromise:
                    HandlePushPromiseFrame(frame as PushPromiseFrame, out stream);
                    if (stream != null)     //This means that sequence is complete
                    {
                        _promisedResources.Add(stream.Id, stream.Headers.GetValue(CommonHeaders.Path));
                    }
                    break;

                case FrameType.AltSvc:
                    HandleAltSvcFrame(frame);
                    break;

                case FrameType.Blocked:
                    HandleBlockedFrame(frame);
                    break;

                default:
                    /* 12 -> 4.1
                     * Implementations MUST treat the receipt of an unknown frame type
                     * (any frame types not defined in this document) as a connection
                     * error of type PROTOCOL_ERROR. */
                    throw new ProtocolError(ResetStatusCode.ProtocolError, "Unknown frame type detected");
                }

                _lastFrame = frame;

                if (frame is IEndStreamFrame && ((IEndStreamFrame)frame).IsEndStream)
                {
                    //Tell the stream that it was the last frame
                    Http2Logger.LogDebug("Final frame received for stream id=" + stream.Id);
                    stream.HalfClosedRemote = true;

                    //Promised resource has been pushed
                    if (_promisedResources.ContainsKey(stream.Id))
                    {
                        _promisedResources.Remove(stream.Id);
                    }
                }

                if (stream == null || OnFrameReceived == null)
                {
                    return;
                }

                OnFrameReceived(this, new FrameReceivedEventArgs(stream, frame));
                stream.FramesReceived++;
            }

            //09
            //5.1.  Stream States
            //An endpoint MUST NOT send frames on a closed stream.  An endpoint
            //that receives a frame after receiving a RST_STREAM [RST_STREAM] or
            //a frame containing a END_STREAM flag on that stream MUST treat
            //that as a stream error (Section 5.4.2) of type STREAM_CLOSED
            //[STREAM_CLOSED].
            catch (Http2StreamNotFoundException ex)
            {
                Http2Logger.LogDebug("Frame for already Closed stream with stream id={0}", ex.Id);
                var rstFrame = new RstStreamFrame(ex.Id, ResetStatusCode.StreamClosed);

                Http2Logger.LogDebug("Sending RST_STREAM frame: stream id={0}, status code={1}",
                                     rstFrame.StreamId, rstFrame.StatusCode);

                _writeQueue.WriteFrame(rstFrame);
                stream.WasRstSent = true;
            }
            catch (CompressionError ex)
            {
                //The endpoint is unable to maintain the compression context for the connection.
                Http2Logger.LogError("Compression error occurred: " + ex.Message);
                Close(ResetStatusCode.CompressionError);
            }
            catch (ProtocolError pEx)
            {
                Http2Logger.LogError("Protocol error occurred: " + pEx.Message);
                Close(pEx.Code);
            }
            catch (MaxConcurrentStreamsLimitException)
            {
                //Remote side tries to open more streams than allowed
                Dispose();
            }
            catch (Exception ex)
            {
                Http2Logger.LogError("Unknown error occurred: " + ex.Message);
                Close(ResetStatusCode.InternalError);
            }
        }