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/data_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,DataPayloadDecoder::PayloadState v)19 std::ostream& operator<<(std::ostream& out,
20                          DataPayloadDecoder::PayloadState v) {
21   switch (v) {
22     case DataPayloadDecoder::PayloadState::kReadPadLength:
23       return out << "kReadPadLength";
24     case DataPayloadDecoder::PayloadState::kReadPayload:
25       return out << "kReadPayload";
26     case DataPayloadDecoder::PayloadState::kSkipPadding:
27       return out << "kSkipPadding";
28   }
29   // Since the value doesn't come over the wire, only a programming bug should
30   // result in reaching this point.
31   int unknown = static_cast<int>(v);
32   QUICHE_BUG(http2_bug_174_1)
33       << "Invalid DataPayloadDecoder::PayloadState: " << unknown;
34   return out << "DataPayloadDecoder::PayloadState(" << unknown << ")";
35 }
36 
StartDecodingPayload(FrameDecoderState * state,DecodeBuffer * db)37 DecodeStatus DataPayloadDecoder::StartDecodingPayload(FrameDecoderState* state,
38                                                       DecodeBuffer* db) {
39   const Http2FrameHeader& frame_header = state->frame_header();
40   const uint32_t total_length = frame_header.payload_length;
41 
42   QUICHE_DVLOG(2) << "DataPayloadDecoder::StartDecodingPayload: "
43                   << frame_header;
44   QUICHE_DCHECK_EQ(Http2FrameType::DATA, frame_header.type);
45   QUICHE_DCHECK_LE(db->Remaining(), total_length);
46   QUICHE_DCHECK_EQ(0, frame_header.flags & ~(Http2FrameFlag::END_STREAM |
47                                              Http2FrameFlag::PADDED));
48 
49   // Special case for the hoped for common case: unpadded and fits fully into
50   // the decode buffer. TO BE SEEN if that is true. It certainly requires that
51   // the transport buffers be large (e.g. >> 16KB typically).
52   // TODO(jamessynge) Add counters.
53   QUICHE_DVLOG(2) << "StartDecodingPayload total_length=" << total_length;
54   if (!frame_header.IsPadded()) {
55     QUICHE_DVLOG(2) << "StartDecodingPayload !IsPadded";
56     if (db->Remaining() == total_length) {
57       QUICHE_DVLOG(2) << "StartDecodingPayload all present";
58       // Note that we don't cache the listener field so that the callee can
59       // replace it if the frame is bad.
60       // If this case is common enough, consider combining the 3 callbacks
61       // into one.
62       state->listener()->OnDataStart(frame_header);
63       if (total_length > 0) {
64         state->listener()->OnDataPayload(db->cursor(), total_length);
65         db->AdvanceCursor(total_length);
66       }
67       state->listener()->OnDataEnd();
68       return DecodeStatus::kDecodeDone;
69     }
70     payload_state_ = PayloadState::kReadPayload;
71   } else {
72     payload_state_ = PayloadState::kReadPadLength;
73   }
74   state->InitializeRemainders();
75   state->listener()->OnDataStart(frame_header);
76   return ResumeDecodingPayload(state, db);
77 }
78 
ResumeDecodingPayload(FrameDecoderState * state,DecodeBuffer * db)79 DecodeStatus DataPayloadDecoder::ResumeDecodingPayload(FrameDecoderState* state,
80                                                        DecodeBuffer* db) {
81   QUICHE_DVLOG(2) << "DataPayloadDecoder::ResumeDecodingPayload payload_state_="
82                   << payload_state_;
83   const Http2FrameHeader& frame_header = state->frame_header();
84   QUICHE_DCHECK_EQ(Http2FrameType::DATA, frame_header.type);
85   QUICHE_DCHECK_LE(state->remaining_payload_and_padding(),
86                    frame_header.payload_length);
87   QUICHE_DCHECK_LE(db->Remaining(), state->remaining_payload_and_padding());
88   DecodeStatus status;
89   size_t avail;
90   switch (payload_state_) {
91     case PayloadState::kReadPadLength:
92       // ReadPadLength handles the OnPadLength callback, and updating the
93       // remaining_payload and remaining_padding fields. If the amount of
94       // padding is too large to fit in the frame's payload, ReadPadLength
95       // instead calls OnPaddingTooLong and returns kDecodeError.
96       status = state->ReadPadLength(db, /*report_pad_length*/ true);
97       if (status != DecodeStatus::kDecodeDone) {
98         return status;
99       }
100       ABSL_FALLTHROUGH_INTENDED;
101 
102     case PayloadState::kReadPayload:
103       avail = state->AvailablePayload(db);
104       if (avail > 0) {
105         state->listener()->OnDataPayload(db->cursor(), avail);
106         db->AdvanceCursor(avail);
107         state->ConsumePayload(avail);
108       }
109       if (state->remaining_payload() > 0) {
110         payload_state_ = PayloadState::kReadPayload;
111         return DecodeStatus::kDecodeInProgress;
112       }
113       ABSL_FALLTHROUGH_INTENDED;
114 
115     case PayloadState::kSkipPadding:
116       // SkipPadding handles the OnPadding callback.
117       if (state->SkipPadding(db)) {
118         state->listener()->OnDataEnd();
119         return DecodeStatus::kDecodeDone;
120       }
121       payload_state_ = PayloadState::kSkipPadding;
122       return DecodeStatus::kDecodeInProgress;
123   }
124   QUICHE_BUG(http2_bug_174_2) << "PayloadState: " << payload_state_;
125   return DecodeStatus::kDecodeError;
126 }
127 
128 }  // namespace http2
129