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); } }
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); } }
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))); } } }
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); }
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); } }
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); } }
/// <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); }
/// <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); }
public void WriteRst(ResetStatusCode code) { var frame = new RstStreamFrame(_id, code); _writeQueue.WriteFrame(frame); ResetSent = true; if (OnFrameSent != null) { OnFrameSent(this, new FrameSentArgs(frame)); } }
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)); } }
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); } }
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); } }
/// <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); }
/// <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); } }