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)); } }
private void HandleRstFrame(RstStreamFrame resetFrame, out Http2Stream stream) { stream = GetStream(resetFrame.StreamId); //Spec 06 tells that impl MUST not answer with rst on rst to avoid loop. if (stream != null) { Http2Logger.LogDebug("RST frame with code {0} for id {1}", resetFrame.StatusCode, resetFrame.StreamId); stream.Dispose(ResetStatusCode.None); } //Do not signal an error because (06) //WINDOW_UPDATE [WINDOW_UPDATE], PRIORITY [PRIORITY], or RST_STREAM //[RST_STREAM] frames can be received in this state for a short //period after a frame containing an END_STREAM flag is sent. }
/// <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); } }
private void HandleRstFrame(RstStreamFrame resetFrame, out Http2Stream stream) { //spec 09 //6.4. RST_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 (Section 5.4.1) of type //PROTOCOL_ERROR. if (resetFrame.StreamId == 0) { throw new ProtocolError(ResetStatusCode.ProtocolError, "resetFrame with id = 0"); } stream = GetStream(resetFrame.StreamId); //09 -> 5.4.2. Stream Error Handling //An endpoint MUST NOT send a RST_STREAM in response to an RST_STREAM //frame, to avoid looping. if (stream == null) return; Http2Logger.LogDebug("RST frame with code {0} for id {1}", resetFrame.StatusCode, resetFrame.StreamId); stream.Dispose(ResetStatusCode.None); //09 -> 5.1. Stream States //WINDOW_UPDATE, PRIORITY, or RST_STREAM frames can be received in //this state for a short period after a DATA or HEADERS frame //containing an END_STREAM flag is sent. Until the remote peer //receives and processes the frame bearing the END_STREAM flag, it //might send frame of any of these types. Endpoints MUST ignore //WINDOW_UPDATE, PRIORITY, or RST_STREAM frames received in this //state, though endpoints MAY choose to treat frames that arrive a //significant time after sending END_STREAM as a connection error //(Section 5.4.1) of type PROTOCOL_ERROR. }
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); }