1 // Copyright 2016 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/goaway_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,GoAwayPayloadDecoder::PayloadState v)19 std::ostream& operator<<(std::ostream& out,
20 GoAwayPayloadDecoder::PayloadState v) {
21 switch (v) {
22 case GoAwayPayloadDecoder::PayloadState::kStartDecodingFixedFields:
23 return out << "kStartDecodingFixedFields";
24 case GoAwayPayloadDecoder::PayloadState::kHandleFixedFieldsStatus:
25 return out << "kHandleFixedFieldsStatus";
26 case GoAwayPayloadDecoder::PayloadState::kReadOpaqueData:
27 return out << "kReadOpaqueData";
28 case GoAwayPayloadDecoder::PayloadState::kResumeDecodingFixedFields:
29 return out << "kResumeDecodingFixedFields";
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_167_1)
35 << "Invalid GoAwayPayloadDecoder::PayloadState: " << unknown;
36 return out << "GoAwayPayloadDecoder::PayloadState(" << unknown << ")";
37 }
38
StartDecodingPayload(FrameDecoderState * state,DecodeBuffer * db)39 DecodeStatus GoAwayPayloadDecoder::StartDecodingPayload(
40 FrameDecoderState* state, DecodeBuffer* db) {
41 QUICHE_DVLOG(2) << "GoAwayPayloadDecoder::StartDecodingPayload: "
42 << state->frame_header();
43 QUICHE_DCHECK_EQ(Http2FrameType::GOAWAY, 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 GoAwayPayloadDecoder::ResumeDecodingPayload(
53 FrameDecoderState* state, DecodeBuffer* db) {
54 QUICHE_DVLOG(2)
55 << "GoAwayPayloadDecoder::ResumeDecodingPayload: remaining_payload="
56 << state->remaining_payload() << ", db->Remaining=" << db->Remaining();
57
58 const Http2FrameHeader& frame_header = state->frame_header();
59 QUICHE_DCHECK_EQ(Http2FrameType::GOAWAY, frame_header.type);
60 QUICHE_DCHECK_LE(db->Remaining(), frame_header.payload_length);
61 QUICHE_DCHECK_NE(PayloadState::kHandleFixedFieldsStatus, payload_state_);
62
63 // |status| has to be initialized to some value to avoid compiler error in
64 // case PayloadState::kHandleFixedFieldsStatus below, but value does not
65 // matter, see QUICHE_DCHECK_NE above.
66 DecodeStatus status = DecodeStatus::kDecodeError;
67 size_t avail;
68 while (true) {
69 QUICHE_DVLOG(2)
70 << "GoAwayPayloadDecoder::ResumeDecodingPayload payload_state_="
71 << payload_state_;
72 switch (payload_state_) {
73 case PayloadState::kStartDecodingFixedFields:
74 status = state->StartDecodingStructureInPayload(&goaway_fields_, db);
75 ABSL_FALLTHROUGH_INTENDED;
76
77 case PayloadState::kHandleFixedFieldsStatus:
78 if (status == DecodeStatus::kDecodeDone) {
79 state->listener()->OnGoAwayStart(frame_header, goaway_fields_);
80 } else {
81 // Not done decoding the structure. Either we've got more payload
82 // to decode, or we've run out because the payload is too short,
83 // in which case OnFrameSizeError will have already been called.
84 QUICHE_DCHECK((status == DecodeStatus::kDecodeInProgress &&
85 state->remaining_payload() > 0) ||
86 (status == DecodeStatus::kDecodeError &&
87 state->remaining_payload() == 0))
88 << "\n status=" << status
89 << "; remaining_payload=" << state->remaining_payload();
90 payload_state_ = PayloadState::kResumeDecodingFixedFields;
91 return status;
92 }
93 ABSL_FALLTHROUGH_INTENDED;
94
95 case PayloadState::kReadOpaqueData:
96 // The opaque data is all the remains to be decoded, so anything left
97 // in the decode buffer is opaque data.
98 avail = db->Remaining();
99 if (avail > 0) {
100 state->listener()->OnGoAwayOpaqueData(db->cursor(), avail);
101 db->AdvanceCursor(avail);
102 state->ConsumePayload(avail);
103 }
104 if (state->remaining_payload() > 0) {
105 payload_state_ = PayloadState::kReadOpaqueData;
106 return DecodeStatus::kDecodeInProgress;
107 }
108 state->listener()->OnGoAwayEnd();
109 return DecodeStatus::kDecodeDone;
110
111 case PayloadState::kResumeDecodingFixedFields:
112 status = state->ResumeDecodingStructureInPayload(&goaway_fields_, db);
113 payload_state_ = PayloadState::kHandleFixedFieldsStatus;
114 continue;
115 }
116 QUICHE_BUG(http2_bug_167_2) << "PayloadState: " << payload_state_;
117 }
118 }
119
120 } // namespace http2
121