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/headers_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,HeadersPayloadDecoder::PayloadState v)19 std::ostream& operator<<(std::ostream& out,
20                          HeadersPayloadDecoder::PayloadState v) {
21   switch (v) {
22     case HeadersPayloadDecoder::PayloadState::kReadPadLength:
23       return out << "kReadPadLength";
24     case HeadersPayloadDecoder::PayloadState::kStartDecodingPriorityFields:
25       return out << "kStartDecodingPriorityFields";
26     case HeadersPayloadDecoder::PayloadState::kResumeDecodingPriorityFields:
27       return out << "kResumeDecodingPriorityFields";
28     case HeadersPayloadDecoder::PayloadState::kReadPayload:
29       return out << "kReadPayload";
30     case HeadersPayloadDecoder::PayloadState::kSkipPadding:
31       return out << "kSkipPadding";
32   }
33   // Since the value doesn't come over the wire, only a programming bug should
34   // result in reaching this point.
35   int unknown = static_cast<int>(v);
36   QUICHE_BUG(http2_bug_189_1)
37       << "Invalid HeadersPayloadDecoder::PayloadState: " << unknown;
38   return out << "HeadersPayloadDecoder::PayloadState(" << unknown << ")";
39 }
40 
StartDecodingPayload(FrameDecoderState * state,DecodeBuffer * db)41 DecodeStatus HeadersPayloadDecoder::StartDecodingPayload(
42     FrameDecoderState* state, DecodeBuffer* db) {
43   const Http2FrameHeader& frame_header = state->frame_header();
44   const uint32_t total_length = frame_header.payload_length;
45 
46   QUICHE_DVLOG(2) << "HeadersPayloadDecoder::StartDecodingPayload: "
47                   << frame_header;
48 
49   QUICHE_DCHECK_EQ(Http2FrameType::HEADERS, frame_header.type);
50   QUICHE_DCHECK_LE(db->Remaining(), total_length);
51   QUICHE_DCHECK_EQ(
52       0, frame_header.flags &
53              ~(Http2FrameFlag::END_STREAM | Http2FrameFlag::END_HEADERS |
54                Http2FrameFlag::PADDED | Http2FrameFlag::PRIORITY));
55 
56   // Special case for HEADERS frames that contain only the HPACK block
57   // (fragment or whole) and that fit fully into the decode buffer.
58   // Why? Unencoded browser GET requests are typically under 1K and HPACK
59   // commonly shrinks request headers by 80%, so we can expect this to
60   // be common.
61   // TODO(jamessynge) Add counters here and to Spdy for determining how
62   // common this situation is. A possible approach is to create a
63   // Http2FrameDecoderListener that counts the callbacks and then forwards
64   // them on to another listener, which makes it easy to add and remove
65   // counting on a connection or even frame basis.
66 
67   // PADDED and PRIORITY both extra steps to decode, but if neither flag is
68   // set then we can decode faster.
69   const auto payload_flags = Http2FrameFlag::PADDED | Http2FrameFlag::PRIORITY;
70   if (!frame_header.HasAnyFlags(payload_flags)) {
71     QUICHE_DVLOG(2) << "StartDecodingPayload !IsPadded && !HasPriority";
72     if (db->Remaining() == total_length) {
73       QUICHE_DVLOG(2) << "StartDecodingPayload all present";
74       // Note that we don't cache the listener field so that the callee can
75       // replace it if the frame is bad.
76       // If this case is common enough, consider combining the 3 callbacks
77       // into one, especially if END_HEADERS is also set.
78       state->listener()->OnHeadersStart(frame_header);
79       if (total_length > 0) {
80         state->listener()->OnHpackFragment(db->cursor(), total_length);
81         db->AdvanceCursor(total_length);
82       }
83       state->listener()->OnHeadersEnd();
84       return DecodeStatus::kDecodeDone;
85     }
86     payload_state_ = PayloadState::kReadPayload;
87   } else if (frame_header.IsPadded()) {
88     payload_state_ = PayloadState::kReadPadLength;
89   } else {
90     QUICHE_DCHECK(frame_header.HasPriority()) << frame_header;
91     payload_state_ = PayloadState::kStartDecodingPriorityFields;
92   }
93   state->InitializeRemainders();
94   state->listener()->OnHeadersStart(frame_header);
95   return ResumeDecodingPayload(state, db);
96 }
97 
ResumeDecodingPayload(FrameDecoderState * state,DecodeBuffer * db)98 DecodeStatus HeadersPayloadDecoder::ResumeDecodingPayload(
99     FrameDecoderState* state, DecodeBuffer* db) {
100   QUICHE_DVLOG(2) << "HeadersPayloadDecoder::ResumeDecodingPayload "
101                   << "remaining_payload=" << state->remaining_payload()
102                   << "; db->Remaining=" << db->Remaining();
103 
104   const Http2FrameHeader& frame_header = state->frame_header();
105 
106   QUICHE_DCHECK_EQ(Http2FrameType::HEADERS, frame_header.type);
107   QUICHE_DCHECK_LE(state->remaining_payload_and_padding(),
108                    frame_header.payload_length);
109   QUICHE_DCHECK_LE(db->Remaining(), state->remaining_payload_and_padding());
110   DecodeStatus status;
111   size_t avail;
112   while (true) {
113     QUICHE_DVLOG(2)
114         << "HeadersPayloadDecoder::ResumeDecodingPayload payload_state_="
115         << payload_state_;
116     switch (payload_state_) {
117       case PayloadState::kReadPadLength:
118         // ReadPadLength handles the OnPadLength callback, and updating the
119         // remaining_payload and remaining_padding fields. If the amount of
120         // padding is too large to fit in the frame's payload, ReadPadLength
121         // instead calls OnPaddingTooLong and returns kDecodeError.
122         status = state->ReadPadLength(db, /*report_pad_length*/ true);
123         if (status != DecodeStatus::kDecodeDone) {
124           return status;
125         }
126         if (!frame_header.HasPriority()) {
127           payload_state_ = PayloadState::kReadPayload;
128           continue;
129         }
130         ABSL_FALLTHROUGH_INTENDED;
131 
132       case PayloadState::kStartDecodingPriorityFields:
133         status = state->StartDecodingStructureInPayload(&priority_fields_, db);
134         if (status != DecodeStatus::kDecodeDone) {
135           payload_state_ = PayloadState::kResumeDecodingPriorityFields;
136           return status;
137         }
138         state->listener()->OnHeadersPriority(priority_fields_);
139         ABSL_FALLTHROUGH_INTENDED;
140 
141       case PayloadState::kReadPayload:
142         avail = state->AvailablePayload(db);
143         if (avail > 0) {
144           state->listener()->OnHpackFragment(db->cursor(), avail);
145           db->AdvanceCursor(avail);
146           state->ConsumePayload(avail);
147         }
148         if (state->remaining_payload() > 0) {
149           payload_state_ = PayloadState::kReadPayload;
150           return DecodeStatus::kDecodeInProgress;
151         }
152         ABSL_FALLTHROUGH_INTENDED;
153 
154       case PayloadState::kSkipPadding:
155         // SkipPadding handles the OnPadding callback.
156         if (state->SkipPadding(db)) {
157           state->listener()->OnHeadersEnd();
158           return DecodeStatus::kDecodeDone;
159         }
160         payload_state_ = PayloadState::kSkipPadding;
161         return DecodeStatus::kDecodeInProgress;
162 
163       case PayloadState::kResumeDecodingPriorityFields:
164         status = state->ResumeDecodingStructureInPayload(&priority_fields_, db);
165         if (status != DecodeStatus::kDecodeDone) {
166           return status;
167         }
168         state->listener()->OnHeadersPriority(priority_fields_);
169         payload_state_ = PayloadState::kReadPayload;
170         continue;
171     }
172     QUICHE_BUG(http2_bug_189_2) << "PayloadState: " << payload_state_;
173   }
174 }
175 
176 }  // namespace http2
177