* [RFC 7540] 6.10 CONTINUATION * * NOTE: Logically, the CONTINUATION frames are part of the HEADERS frame. ([RFC *7540] 6.2 HEADERS) * */
| 1009 | * |
| 1010 | */ |
| 1011 | Http2Error |
| 1012 | Http2ConnectionState::rcv_continuation_frame(const Http2Frame &frame) |
| 1013 | { |
| 1014 | const Http2StreamId stream_id = frame.header().streamid; |
| 1015 | const uint32_t payload_length = frame.header().length; |
| 1016 | |
| 1017 | Http2StreamDebug(this->session, stream_id, "Received CONTINUATION frame"); |
| 1018 | |
| 1019 | if (!http2_is_client_streamid(stream_id)) { |
| 1020 | return Http2Error(Http2ErrorClass::HTTP2_ERROR_CLASS_CONNECTION, Http2ErrorCode::HTTP2_ERROR_PROTOCOL_ERROR, |
| 1021 | "continuation bad client id"); |
| 1022 | } |
| 1023 | |
| 1024 | if (payload_length == 0 && (frame.header().flags & HTTP2_FLAGS_HEADERS_END_HEADERS) == 0x0) { |
| 1025 | this->increment_received_empty_frame_count(); |
| 1026 | if (configured_max_empty_frames_per_minute >= 0 && |
| 1027 | this->get_received_empty_frame_count() > static_cast<uint32_t>(configured_max_empty_frames_per_minute)) { |
| 1028 | Metrics::Counter::increment(http2_rsb.max_empty_frames_per_minute_exceeded); |
| 1029 | Http2StreamDebug(this->session, stream_id, "Observed too many empty CONTINUATION frames: %u within the last minute", |
| 1030 | this->get_received_empty_frame_count()); |
| 1031 | return Http2Error(Http2ErrorClass::HTTP2_ERROR_CLASS_CONNECTION, Http2ErrorCode::HTTP2_ERROR_ENHANCE_YOUR_CALM, |
| 1032 | "recv continuation too frequent empty frame"); |
| 1033 | } |
| 1034 | } |
| 1035 | |
| 1036 | // Find opened stream |
| 1037 | // CONTINUATION frames MUST be associated with a stream. If a |
| 1038 | // CONTINUATION frame is received whose stream identifier field is 0x0, |
| 1039 | // the recipient MUST respond with a connection error ([RFC 7540] Section |
| 1040 | // 5.4.1) of type PROTOCOL_ERROR. |
| 1041 | Http2Stream *stream = this->find_stream(stream_id); |
| 1042 | if (stream == nullptr) { |
| 1043 | if (this->is_valid_streamid(stream_id)) { |
| 1044 | return Http2Error(Http2ErrorClass::HTTP2_ERROR_CLASS_CONNECTION, Http2ErrorCode::HTTP2_ERROR_STREAM_CLOSED, |
| 1045 | "continuation stream freed with valid id"); |
| 1046 | } else { |
| 1047 | return Http2Error(Http2ErrorClass::HTTP2_ERROR_CLASS_CONNECTION, Http2ErrorCode::HTTP2_ERROR_PROTOCOL_ERROR, |
| 1048 | "continuation stream freed with invalid id"); |
| 1049 | } |
| 1050 | } else { |
| 1051 | switch (stream->get_state()) { |
| 1052 | case Http2StreamState::HTTP2_STREAM_STATE_HALF_CLOSED_REMOTE: |
| 1053 | return Http2Error(Http2ErrorClass::HTTP2_ERROR_CLASS_CONNECTION, Http2ErrorCode::HTTP2_ERROR_STREAM_CLOSED, |
| 1054 | "continuation half close remote"); |
| 1055 | case Http2StreamState::HTTP2_STREAM_STATE_IDLE: |
| 1056 | break; |
| 1057 | default: |
| 1058 | return Http2Error(Http2ErrorClass::HTTP2_ERROR_CLASS_CONNECTION, Http2ErrorCode::HTTP2_ERROR_PROTOCOL_ERROR, |
| 1059 | "continuation bad state"); |
| 1060 | } |
| 1061 | } |
| 1062 | |
| 1063 | // Update CONTINUATION frame count per minute. |
| 1064 | this->increment_received_continuation_frame_count(); |
| 1065 | // Close this connection if its CONTINUATION frame count exceeds a limit. |
| 1066 | if (configured_max_continuation_frames_per_minute != 0 && |
| 1067 | this->get_received_continuation_frame_count() > static_cast<uint32_t>(configured_max_continuation_frames_per_minute)) { |
| 1068 | Metrics::Counter::increment(http2_rsb.max_continuation_frames_per_minute_exceeded); |
nothing calls this directly
no test coverage detected