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