Beispiel #1
0
        /// <summary>
        /// Proess close goaway frame.
        /// </summary>
        /// <param name="httpContext">The current http context.</param>
        /// <param name="errorCode">Error code registry.</param>
        /// <param name="lastStreamID">The last good stream id.</param>
        private static void ProcessCloseGoAwayFrame(Nequeo.Net.Http2.HttpContext httpContext, ErrorCodeRegistry errorCode, int lastStreamID)
        {
            // Create the goaway frame response.
            GoAwayFrame frame = new GoAwayFrame(lastStreamID, errorCode);

            // Write the frame.
            httpContext.ResponseWrite(frame.Buffer);
        }
Beispiel #2
0
        /// <summary>
        /// Process Protocol control frame.
        /// </summary>
        /// <param name="frame">The control frame.</param>
        private void ProcessControlFrame(ControlFrame frame)
        {
            if (frame.Version != Version)
            {
                throw new ProtocolExeption(StatusCode.UnsupportedVersion);
            }

            if (frame.Type == FrameType.GoAway)
            {
                GoAwayFrame goAway = (GoAwayFrame)frame;
                CloseInternal(goAway.Status, goAway.LastSeenGoodStreamId, false);
            }
            else
            {
                Http2Stream stream = this.streamsStore.GetStreamById(frame.StreamId);

                // if this is rst frame - don't send error or it will go in rst loop
                if (stream == null && frame.Type != FrameType.RTS && frame.Type != FrameType.Settings && frame.Type != FrameType.SynStream)
                {
                    this.SendRST(frame.StreamId, StatusCode.InvalidStream);
                    return;
                }

                switch (frame.Type)
                {
                case FrameType.SynStream:
                    //TODO validate syn_stream and send syn_reply or rst
                    this.OnSessionFrame(this, new ControlFrameEventArgs(frame));
                    break;

                case FrameType.SynReply:
                    this.OnSessionFrame(this, new ControlFrameEventArgs(frame));
                    break;

                case FrameType.Headers:
                    this.OnStreamFrame(this, new HeadersEventArgs(this.streamsStore.GetStreamById(frame.StreamId), frame.Headers));
                    break;

                case FrameType.RTS:
                    this.OnStreamFrame(this, new RSTEventArgs(this.streamsStore.GetStreamById(frame.StreamId), frame.StatusCode));
                    break;

                case FrameType.Ping:
                    this.OnSocketPing(this, new PingEventArgs(frame.StreamId));
                    break;

                case FrameType.Settings:
                    OnSessionFrame(this, new SettingsEventArgs(frame));
                    break;

                case FrameType.WindowUpdate:
                    Http2Stream http2Stream = this.streamsStore.GetStreamById(frame.StreamId);
                    http2Stream.UpdateWindowBalance(frame.DeltaWindowSize);
                    break;
                }
            }
        }
Beispiel #3
0
        /// <summary>
        /// Writes the go away frame.
        /// </summary>
        /// <param name="code">The code.</param>
        public void WriteGoAway(ResetStatusCode code)
        {
            //if there were no streams opened
            if (_lastId == -1)
            {
                _lastId = 0; //then set lastId to 0 as spec tells. (See GoAway chapter)
            }

            var frame = new GoAwayFrame(_lastId, code);

            _writeQueue.WriteFrame(frame);
        }
Beispiel #4
0
        private void HandleGoAwayFrame(GoAwayFrame goAwayFrame)
        {
            Http2Logger.LogDebug("GOAWAY frame: stream id={0}, status code={1}", goAwayFrame.StreamId,
                                 goAwayFrame.StatusCode);

            if (goAwayFrame.StreamId != 0)
            {
                throw new ProtocolError(ResetStatusCode.ProtocolError, "GoAway Stream id should always be null");
            }

            _goAwayReceived = true;

            Http2Logger.LogDebug("last successful id = {0}", goAwayFrame.LastGoodStreamId);
            Dispose();
        }
Beispiel #5
0
        /// <summary>
        /// Writes the GOAWAY frame.
        /// </summary>
        /// <param name="code">The Reset Status code.</param>
        public void WriteGoAway(ResetStatusCode code)
        {
            //if there were no streams opened
            if (_lastId == -1)
            {
                _lastId = 0; //then set lastId to 0 as spec tells. (See GoAway chapter)
            }

            var frame = new GoAwayFrame(_lastId, code);

            Http2Logger.LogDebug("Sending GOAWAY frame: stream id={0}, code={1}, last good id={2}",
                                 frame.StreamId, frame.StatusCode, frame.LastGoodStreamId);

            _writeQueue.WriteFrame(frame);
        }
Beispiel #6
0
        public async Task GoAwayFrame_NoPendingStreams_ConnectionClosed()
        {
            HttpClientHandler handler = CreateHttpClientHandler();

            handler.ServerCertificateCustomValidationCallback = TestHelper.AllowAllCertificates;

            using (var server = Http2LoopbackServer.CreateServer())
                using (var client = new HttpClient(handler))
                {
                    int streamId = await EstablishConnectionAndProcessOneRequestAsync(client, server);

                    // Send GOAWAY.
                    GoAwayFrame goAwayFrame = new GoAwayFrame(streamId, 0, new byte[0], 0);
                    await server.WriteFrameAsync(goAwayFrame);

                    // The client should close the connection.
                    await server.WaitForConnectionShutdownAsync();

                    // New request should cause a new connection
                    await EstablishConnectionAndProcessOneRequestAsync(client, server);
                }
        }
Beispiel #7
0
        public async Task GoAwayFrame_NonzeroStream_ConnectionError()
        {
            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 GoAway frame on stream 1.
                    GoAwayFrame invalidFrame = new GoAwayFrame(0, 0, new byte[0], 1);
                    await server.WriteFrameAsync(invalidFrame);

                    // As this is a connection level error, the client should see the request fail.
                    await Assert.ThrowsAsync <HttpRequestException>(async() => await sendTask);
                }
        }
Beispiel #8
0
        public async Task GoAwayFrame_AllPendingStreamsValid_RequestsSucceedAndConnectionClosed()
        {
            HttpClientHandler handler = CreateHttpClientHandler();

            handler.ServerCertificateCustomValidationCallback = TestHelper.AllowAllCertificates;

            using (var server = Http2LoopbackServer.CreateServer())
                using (var client = new HttpClient(handler))
                {
                    await EstablishConnectionAndProcessOneRequestAsync(client, server);

                    // Issue three requests
                    Task <HttpResponseMessage> sendTask1 = client.GetAsync(server.Address);
                    Task <HttpResponseMessage> sendTask2 = client.GetAsync(server.Address);
                    Task <HttpResponseMessage> sendTask3 = client.GetAsync(server.Address);

                    // Receive three requests
                    int streamId1 = await server.ReadRequestHeaderAsync();

                    int streamId2 = await server.ReadRequestHeaderAsync();

                    int streamId3 = await server.ReadRequestHeaderAsync();

                    Assert.True(streamId1 < streamId2);
                    Assert.True(streamId2 < streamId3);

                    // Send various partial responses

                    // First response: Don't send anything yet

                    // Second response: Send headers, no body yet
                    await server.SendDefaultResponseHeadersAsync(streamId2);

                    // Third response: Send headers, partial body
                    await server.SendDefaultResponseHeadersAsync(streamId3);

                    await server.SendResponseDataAsync(streamId3, new byte[5], endStream : false);

                    // Send a GOAWAY frame that indicates that we will process all three streams
                    GoAwayFrame goAwayFrame = new GoAwayFrame(streamId3, 0, new byte[0], 0);
                    await server.WriteFrameAsync(goAwayFrame);

                    // Finish sending responses
                    await server.SendDefaultResponseHeadersAsync(streamId1);

                    await server.SendResponseDataAsync(streamId1, new byte[10], endStream : true);

                    await server.SendResponseDataAsync(streamId2, new byte[10], endStream : true);

                    await server.SendResponseDataAsync(streamId3, new byte[5], endStream : true);

                    // We will not send any more frames, so send EOF now, and ensure the client handles this properly.
                    server.ShutdownSend();

                    // Receive all responses
                    HttpResponseMessage response1 = await sendTask1;
                    Assert.Equal(HttpStatusCode.OK, response1.StatusCode);
                    Assert.Equal(10, (await response1.Content.ReadAsByteArrayAsync()).Length);
                    HttpResponseMessage response2 = await sendTask2;
                    Assert.Equal(HttpStatusCode.OK, response2.StatusCode);
                    Assert.Equal(10, (await response2.Content.ReadAsByteArrayAsync()).Length);
                    HttpResponseMessage response3 = await sendTask3;
                    Assert.Equal(HttpStatusCode.OK, response3.StatusCode);
                    Assert.Equal(10, (await response3.Content.ReadAsByteArrayAsync()).Length);

                    // Now that all pending responses have been sent, the client should close the connection.
                    await server.WaitForConnectionShutdownAsync();

                    // New request should cause a new connection
                    await EstablishConnectionAndProcessOneRequestAsync(client, server);
                }
        }
        /// <summary>
        /// Deserializes the data into control frame.
        /// </summary>
        /// <param name="data">The data.</param>
        /// <returns>Deserialized frame.</returns>
        private BaseFrame DeserializeControlFrame(byte[] data)
        {
            FrameType    type  = GetFrameType(data);
            ControlFrame frame = new ControlFrame();

            switch (type)
            {
            case FrameType.RTS:
                frame.StreamId   = BinaryHelper.Int32FromBytes(new ArraySegment <byte>(data, 8, 4));
                frame.StatusCode = (StatusCode)BinaryHelper.Int32FromBytes(new ArraySegment <byte>(data, 12, 4));
                break;

            case FrameType.Headers:
            case FrameType.SynReply:
                ParseControlFrameHeader(ref frame, data);

                frame.StreamId = BinaryHelper.Int32FromBytes(new ArraySegment <byte>(data, 8, 4));

                ParseControlFrameHeaders(ref frame, data, 12);
                break;

            case FrameType.SynStream:
                ParseControlFrameHeader(ref frame, data);

                frame.StreamId             = BinaryHelper.Int32FromBytes(new ArraySegment <byte>(data, 8, 4));
                frame.AssociatedToStreamId = BinaryHelper.Int32FromBytes(new ArraySegment <byte>(data, 12, 4));
                frame.Priority             = (byte)(data[16] >> 5);
                frame.Slot = data[17];

                ParseControlFrameHeaders(ref frame, data, 18);
                break;

            case FrameType.Settings:
                int numberOfEntries = BinaryHelper.Int32FromBytes(new ArraySegment <byte>(data, 8, 4));
                frame = new SettingsFrame(numberOfEntries);
                int headersOffset = 12;

                for (int i = 0; i < numberOfEntries; i++)
                {
                    int key = BinaryHelper.Int32FromBytes(new ArraySegment <byte>(data, headersOffset, 4));
                    headersOffset += 4;
                    int value = BinaryHelper.Int32FromBytes(new ArraySegment <byte>(data, headersOffset, 4));
                    headersOffset += 4;

                    frame.SettingsHeaders.Add(key, value);
                }

                ParseControlFrameHeader(ref frame, data);
                break;

            case FrameType.GoAway:
                int        lastSeenGoodStreamId = BinaryHelper.Int32FromBytes(new ArraySegment <byte>(data, 8, 4));
                StatusCode status = StatusCode.Success;

                if (data.Length > 12)
                {
                    status = (StatusCode)BinaryHelper.Int32FromBytes(new ArraySegment <byte>(data, 12, 4));
                }

                frame = new GoAwayFrame(lastSeenGoodStreamId, status);
                ParseControlFrameHeader(ref frame, data);
                break;

            case FrameType.Ping:
                int streamID = BinaryHelper.Int32FromBytes(new ArraySegment <byte>(data, 4, 4));
                frame = new ControlFrame {
                    StreamId = streamID
                };

                ParseControlFrameHeader(ref frame, data);
                break;

            case FrameType.WindowUpdate:
                int streamId        = BinaryHelper.Int32FromBytes(new ArraySegment <byte>(data, 4, 4));
                int deltaWindowSize = BinaryHelper.Int32FromBytes(new ArraySegment <byte>(data, 8, 4));
                frame = new WindowUpdateFrame(streamId, deltaWindowSize);

                ParseControlFrameHeader(ref frame, data);
                break;
            }
            frame.Type = type;

            return(frame);
        }
Beispiel #10
0
        /// <summary>
        /// Process a goaway frame request.
        /// </summary>
        /// <param name="httpContext">The current http context.</param>
        /// <param name="goAwayFrame">The goaway frame.</param>
        private static void ProcessGoAwayFrameRequest(Nequeo.Net.Http2.HttpContext httpContext, GoAwayFrame goAwayFrame)
        {
            if (goAwayFrame.StreamId != 0)
            {
                throw new ProtocolError(ErrorCodeRegistry.Protocol_Error, "GoAway frame stream id is not equal to 0.");
            }

            // Close the connection.
            throw new ProtocolError(ErrorCodeRegistry.No_Error, "Close the session and connection.");
        }