1 // Copyright 2020 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "quiche/http2/decoder/payload_decoders/priority_update_payload_decoder.h"
6
7 #include <stddef.h>
8
9 #include "absl/base/macros.h"
10 #include "quiche/http2/decoder/decode_buffer.h"
11 #include "quiche/http2/decoder/http2_frame_decoder_listener.h"
12 #include "quiche/http2/http2_constants.h"
13 #include "quiche/http2/http2_structures.h"
14 #include "quiche/common/platform/api/quiche_bug_tracker.h"
15 #include "quiche/common/platform/api/quiche_logging.h"
16
17 namespace http2 {
18
operator <<(std::ostream & out,PriorityUpdatePayloadDecoder::PayloadState v)19 std::ostream& operator<<(std::ostream& out,
20 PriorityUpdatePayloadDecoder::PayloadState v) {
21 switch (v) {
22 case PriorityUpdatePayloadDecoder::PayloadState::kStartDecodingFixedFields:
23 return out << "kStartDecodingFixedFields";
24 case PriorityUpdatePayloadDecoder::PayloadState::kResumeDecodingFixedFields:
25 return out << "kResumeDecodingFixedFields";
26 case PriorityUpdatePayloadDecoder::PayloadState::kHandleFixedFieldsStatus:
27 return out << "kHandleFixedFieldsStatus";
28 case PriorityUpdatePayloadDecoder::PayloadState::kReadPriorityFieldValue:
29 return out << "kReadPriorityFieldValue";
30 }
31 // Since the value doesn't come over the wire, only a programming bug should
32 // result in reaching this point.
33 int unknown = static_cast<int>(v);
34 QUICHE_BUG(http2_bug_173_1)
35 << "Invalid PriorityUpdatePayloadDecoder::PayloadState: " << unknown;
36 return out << "PriorityUpdatePayloadDecoder::PayloadState(" << unknown << ")";
37 }
38
StartDecodingPayload(FrameDecoderState * state,DecodeBuffer * db)39 DecodeStatus PriorityUpdatePayloadDecoder::StartDecodingPayload(
40 FrameDecoderState* state, DecodeBuffer* db) {
41 QUICHE_DVLOG(2) << "PriorityUpdatePayloadDecoder::StartDecodingPayload: "
42 << state->frame_header();
43 QUICHE_DCHECK_EQ(Http2FrameType::PRIORITY_UPDATE, state->frame_header().type);
44 QUICHE_DCHECK_LE(db->Remaining(), state->frame_header().payload_length);
45 QUICHE_DCHECK_EQ(0, state->frame_header().flags);
46
47 state->InitializeRemainders();
48 payload_state_ = PayloadState::kStartDecodingFixedFields;
49 return ResumeDecodingPayload(state, db);
50 }
51
ResumeDecodingPayload(FrameDecoderState * state,DecodeBuffer * db)52 DecodeStatus PriorityUpdatePayloadDecoder::ResumeDecodingPayload(
53 FrameDecoderState* state, DecodeBuffer* db) {
54 QUICHE_DVLOG(2) << "PriorityUpdatePayloadDecoder::ResumeDecodingPayload: "
55 "remaining_payload="
56 << state->remaining_payload()
57 << ", db->Remaining=" << db->Remaining();
58
59 const Http2FrameHeader& frame_header = state->frame_header();
60 QUICHE_DCHECK_EQ(Http2FrameType::PRIORITY_UPDATE, frame_header.type);
61 QUICHE_DCHECK_LE(db->Remaining(), frame_header.payload_length);
62 QUICHE_DCHECK_NE(PayloadState::kHandleFixedFieldsStatus, payload_state_);
63
64 // |status| has to be initialized to some value to avoid compiler error in
65 // case PayloadState::kHandleFixedFieldsStatus below, but value does not
66 // matter, see QUICHE_DCHECK_NE above.
67 DecodeStatus status = DecodeStatus::kDecodeError;
68 size_t avail;
69 while (true) {
70 QUICHE_DVLOG(2)
71 << "PriorityUpdatePayloadDecoder::ResumeDecodingPayload payload_state_="
72 << payload_state_;
73 switch (payload_state_) {
74 case PayloadState::kStartDecodingFixedFields:
75 status = state->StartDecodingStructureInPayload(
76 &priority_update_fields_, db);
77 ABSL_FALLTHROUGH_INTENDED;
78
79 case PayloadState::kHandleFixedFieldsStatus:
80 if (status == DecodeStatus::kDecodeDone) {
81 state->listener()->OnPriorityUpdateStart(frame_header,
82 priority_update_fields_);
83 } else {
84 // Not done decoding the structure. Either we've got more payload
85 // to decode, or we've run out because the payload is too short,
86 // in which case OnFrameSizeError will have already been called.
87 QUICHE_DCHECK((status == DecodeStatus::kDecodeInProgress &&
88 state->remaining_payload() > 0) ||
89 (status == DecodeStatus::kDecodeError &&
90 state->remaining_payload() == 0))
91 << "\n status=" << status
92 << "; remaining_payload=" << state->remaining_payload();
93 payload_state_ = PayloadState::kResumeDecodingFixedFields;
94 return status;
95 }
96 ABSL_FALLTHROUGH_INTENDED;
97
98 case PayloadState::kReadPriorityFieldValue:
99 // Anything left in the decode buffer is the Priority Field Value.
100 avail = db->Remaining();
101 if (avail > 0) {
102 state->listener()->OnPriorityUpdatePayload(db->cursor(), avail);
103 db->AdvanceCursor(avail);
104 state->ConsumePayload(avail);
105 }
106 if (state->remaining_payload() > 0) {
107 payload_state_ = PayloadState::kReadPriorityFieldValue;
108 return DecodeStatus::kDecodeInProgress;
109 }
110 state->listener()->OnPriorityUpdateEnd();
111 return DecodeStatus::kDecodeDone;
112
113 case PayloadState::kResumeDecodingFixedFields:
114 status = state->ResumeDecodingStructureInPayload(
115 &priority_update_fields_, db);
116 payload_state_ = PayloadState::kHandleFixedFieldsStatus;
117 continue;
118 }
119 QUICHE_BUG(http2_bug_173_2) << "PayloadState: " << payload_state_;
120 }
121 }
122
123 } // namespace http2
124