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