/// <summary> /// 处理传入头的接收 /// </summary> public Http2Error?ProcessHeaders( CompleteHeadersFrameData headers) { var wakeupDataWaiter = false; var wakeupHeaderWaiter = false; var wakeupTrailerWaiter = false; var removeStream = false; lock (stateMutex) { switch (state) { case StreamState.ReservedLocal: case StreamState.ReservedRemote: return(new Http2Error { StreamId = Id, Code = ErrorCode.InternalError, Message = "接收到的头帧处于未覆盖的推送约定状态", }); case StreamState.Idle: case StreamState.Open: case StreamState.HalfClosedLocal: if (headersReceived != HeaderReceptionState.ReceivedAllHeaders) { HeaderValidationResult hvr; if (connection.IsServer) { hvr = HeaderValidator.ValidateRequestHeaders(headers.Headers); } else { hvr = HeaderValidator.ValidateResponseHeaders(headers.Headers); } if (hvr != HeaderValidationResult.Ok) { return(new Http2Error { StreamId = Id, Code = ErrorCode.ProtocolError, Message = "Received invalid headers", }); } if (!connection.config.IsServer && headers.Headers.IsInformationalHeaders()) { headersReceived = HeaderReceptionState.ReceivedInformationalHeaders; } else { headersReceived = HeaderReceptionState.ReceivedAllHeaders; } wakeupHeaderWaiter = true; declaredInContentLength = headers.Headers.GetContentLength(); inHeaders = headers.Headers; } else if (!dataReceived) { return(new Http2Error { StreamId = Id, Code = ErrorCode.ProtocolError, Message = "接收的无标题", }); } else { if (!headers.EndOfStream) { return(new Http2Error { StreamId = Id, Code = ErrorCode.ProtocolError, Message = "接收到没有endofstream标志", }); } var hvr = HeaderValidator.ValidateTrailingHeaders(headers.Headers); if (hvr != HeaderValidationResult.Ok) { return(new Http2Error { StreamId = Id, Code = ErrorCode.ProtocolError, Message = "接收到无效", }); } if (declaredInContentLength >= 0 && declaredInContentLength != totalInData) { return(new Http2Error { StreamId = Id, Code = ErrorCode.ProtocolError, Message = "数据帧的长度与内容长度不匹配", }); } wakeupTrailerWaiter = true; inTrailers = headers.Headers; } if (state == StreamState.Idle) { state = StreamState.Open; } if (headers.EndOfStream) { if (state == StreamState.HalfClosedLocal) { state = StreamState.Closed; removeStream = true; } else { state = StreamState.HalfClosedRemote; } wakeupTrailerWaiter = true; wakeupDataWaiter = true; } break; case StreamState.HalfClosedRemote: case StreamState.Closed: return(new Http2Error { Code = ErrorCode.StreamClosed, StreamId = Id, Message = "已接收封闭流的头", }); case StreamState.Reset: break; default: throw new Exception("未处理的流状态"); } } if (wakeupHeaderWaiter) { readHeadersPossible.Set(); } if (wakeupDataWaiter) { readDataPossible.Set(); } if (wakeupTrailerWaiter) { readTrailersPossible.Set(); } if (removeStream) { connection.UnregisterStream(this); } return(null); }
/// <summary> /// Processes the reception of incoming headers /// </summary> public Http2Error?ProcessHeaders( CompleteHeadersFrameData headers) { var wakeupDataWaiter = false; var wakeupHeaderWaiter = false; var wakeupTrailerWaiter = false; var removeStream = false; lock (stateMutex) { // Header frames are not valid in all states switch (state) { case StreamState.ReservedLocal: case StreamState.ReservedRemote: // Push promises are currently not implemented // So this needs to be reviewed later on // Currently we should never encounter this state return(new Http2Error { StreamId = Id, Code = ErrorCode.InternalError, Message = "Received header frame in uncovered push promise state", }); case StreamState.Idle: case StreamState.Open: case StreamState.HalfClosedLocal: // Open can mean we have already received headers // (in case we are a server) or not (in case we are // a client and only have sent headers) // If headers were already received before there must be // a data frame in between and these are trailers. // An exception is if we are client, where we can // receive informational headers and normal headers. // This requires no data in between. These header must // contain a 1xy status code. // Trailers must have the EndOfStream flag set and must // always follow after a data frame. if (headersReceived != HeaderReceptionState.ReceivedAllHeaders) { // We are receiving headers HeaderValidationResult hvr; if (connection.IsServer) { hvr = HeaderValidator.ValidateRequestHeaders(headers.Headers); } else { hvr = HeaderValidator.ValidateResponseHeaders(headers.Headers); } if (hvr != HeaderValidationResult.Ok) { return(new Http2Error { StreamId = Id, Code = ErrorCode.ProtocolError, Message = "Received invalid headers", }); } if (!connection.config.IsServer && headers.Headers.IsInformationalHeaders()) { // Clients support the reception of informational headers. // If this is only an informational header we might // receive additional headers later on. headersReceived = HeaderReceptionState.ReceivedInformationalHeaders; } else { // Servers don't support informational headers at all. // And if we are client and directly receive response // headers it's also fine. headersReceived = HeaderReceptionState.ReceivedAllHeaders; } wakeupHeaderWaiter = true; // TODO: Uncompress cookie headers here? declaredInContentLength = headers.Headers.GetContentLength(); inHeaders = headers.Headers; } else if (!dataReceived) { // We already have received headers, so this should // be trailers. However there was no DATA frame in // between, so this is simply invalid. return(new Http2Error { StreamId = Id, Code = ErrorCode.ProtocolError, Message = "Received trailers without headers", }); } else { // These are trailers // trailers must have end of stream set. It is not // valid to receive multiple trailers if (!headers.EndOfStream) { return(new Http2Error { StreamId = Id, Code = ErrorCode.ProtocolError, Message = "Received trailers without EndOfStream flag", }); } var hvr = HeaderValidator.ValidateTrailingHeaders(headers.Headers); if (hvr != HeaderValidationResult.Ok) { return(new Http2Error { StreamId = Id, Code = ErrorCode.ProtocolError, Message = "Received invalid trailers", }); } // If content-length was set we must also validate // it against the received dataamount here if (declaredInContentLength >= 0 && declaredInContentLength != totalInData) { return(new Http2Error { StreamId = Id, Code = ErrorCode.ProtocolError, Message = "Length of DATA frames does not match content-length", }); } wakeupTrailerWaiter = true; inTrailers = headers.Headers; } // Handle state changes that are caused by HEADERS frame if (state == StreamState.Idle) { state = StreamState.Open; } if (headers.EndOfStream) { if (state == StreamState.HalfClosedLocal) { state = StreamState.Closed; removeStream = true; } else // Must be Open, since Idle moves to Open { state = StreamState.HalfClosedRemote; } wakeupTrailerWaiter = true; wakeupDataWaiter = true; } break; case StreamState.HalfClosedRemote: case StreamState.Closed: // Received a header frame for a stream that was // already closed from remote side. // That's not valid return(new Http2Error { Code = ErrorCode.StreamClosed, StreamId = Id, Message = "Received headers for closed stream", }); case StreamState.Reset: // The stream was already reset // What we really should do here depends on the previous state, // which is not stored for efficiency. If we reset the // stream late headers are ok. If the remote resetted it // this is a protocol error for the stream. // As it does not really matter just ignore the frame. break; default: throw new Exception("Unhandled stream state"); } } // Wakeup any blocked calls that are waiting on headers or end of stream if (wakeupHeaderWaiter) { readHeadersPossible.Set(); } if (wakeupDataWaiter) { readDataPossible.Set(); } if (wakeupTrailerWaiter) { readTrailersPossible.Set(); } if (removeStream) { connection.UnregisterStream(this); } return(null); }