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