blob: b44d29b12af30db7c39b541d7378dec15ac2957c [file] [log] [blame]
QUICHE teamfd50a402018-12-07 22:54:05 -05001// 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#ifndef QUICHE_HTTP2_DECODER_FRAME_DECODER_STATE_H_
6#define QUICHE_HTTP2_DECODER_FRAME_DECODER_STATE_H_
7
8// FrameDecoderState provides state and behaviors in support of decoding
9// the common frame header and the payload of all frame types.
10// It is an input to all of the payload decoders.
11
12// TODO(jamessynge): Since FrameDecoderState has far more than state in it,
13// rename to FrameDecoderHelper, or similar.
14
15#include <stddef.h>
16
17#include <cstdint>
18
QUICHE teamfd50a402018-12-07 22:54:05 -050019#include "net/third_party/quiche/src/http2/decoder/decode_buffer.h"
20#include "net/third_party/quiche/src/http2/decoder/decode_status.h"
21#include "net/third_party/quiche/src/http2/decoder/http2_frame_decoder_listener.h"
22#include "net/third_party/quiche/src/http2/decoder/http2_structure_decoder.h"
23#include "net/third_party/quiche/src/http2/http2_constants.h"
24#include "net/third_party/quiche/src/http2/http2_structures.h"
25#include "net/third_party/quiche/src/http2/platform/api/http2_export.h"
QUICHE team61940b42019-03-07 23:32:27 -050026#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
QUICHE teamfd50a402018-12-07 22:54:05 -050027
28namespace http2 {
29namespace test {
30class FrameDecoderStatePeer;
31} // namespace test
32
33class HTTP2_EXPORT_PRIVATE FrameDecoderState {
34 public:
35 FrameDecoderState() {}
36
37 // Sets the listener which the decoders should call as they decode HTTP/2
38 // frames. The listener can be changed at any time, which allows for replacing
39 // it with a no-op listener when an error is detected, either by the payload
40 // decoder (OnPaddingTooLong or OnFrameSizeError) or by the "real" listener.
41 // That in turn allows us to define Http2FrameDecoderListener such that all
42 // methods have return type void, with no direct way to indicate whether the
43 // decoder should stop, and to eliminate from the decoder all checks of the
44 // return value. Instead the listener/caller can simply replace the current
45 // listener with a no-op listener implementation.
46 // TODO(jamessynge): Make set_listener private as only Http2FrameDecoder
47 // and tests need to set it, so it doesn't need to be public.
48 void set_listener(Http2FrameDecoderListener* listener) {
49 listener_ = listener;
50 }
51 Http2FrameDecoderListener* listener() const { return listener_; }
52
53 // The most recently decoded frame header.
54 const Http2FrameHeader& frame_header() const { return frame_header_; }
55
56 // Decode a structure in the payload, adjusting remaining_payload_ to account
57 // for the consumed portion of the payload. Returns kDecodeDone when fully
58 // decoded, kDecodeError if it ran out of payload before decoding completed,
59 // and kDecodeInProgress if the decode buffer didn't have enough of the
60 // remaining payload.
61 template <class S>
62 DecodeStatus StartDecodingStructureInPayload(S* out, DecodeBuffer* db) {
QUICHE team61940b42019-03-07 23:32:27 -050063 HTTP2_DVLOG(2) << __func__ << "\n\tdb->Remaining=" << db->Remaining()
64 << "\n\tremaining_payload_=" << remaining_payload_
65 << "\n\tneed=" << S::EncodedSize();
QUICHE teamfd50a402018-12-07 22:54:05 -050066 DecodeStatus status =
67 structure_decoder_.Start(out, db, &remaining_payload_);
68 if (status != DecodeStatus::kDecodeError) {
69 return status;
70 }
QUICHE team61940b42019-03-07 23:32:27 -050071 HTTP2_DVLOG(2)
72 << "StartDecodingStructureInPayload: detected frame size error";
QUICHE teamfd50a402018-12-07 22:54:05 -050073 return ReportFrameSizeError();
74 }
75
76 // Resume decoding of a structure that has been split across buffers,
77 // adjusting remaining_payload_ to account for the consumed portion of
78 // the payload. Returns values are as for StartDecodingStructureInPayload.
79 template <class S>
80 DecodeStatus ResumeDecodingStructureInPayload(S* out, DecodeBuffer* db) {
QUICHE team61940b42019-03-07 23:32:27 -050081 HTTP2_DVLOG(2) << __func__ << "\n\tdb->Remaining=" << db->Remaining()
82 << "\n\tremaining_payload_=" << remaining_payload_;
QUICHE teamfd50a402018-12-07 22:54:05 -050083 if (structure_decoder_.Resume(out, db, &remaining_payload_)) {
84 return DecodeStatus::kDecodeDone;
85 } else if (remaining_payload_ > 0) {
86 return DecodeStatus::kDecodeInProgress;
87 } else {
QUICHE team61940b42019-03-07 23:32:27 -050088 HTTP2_DVLOG(2)
89 << "ResumeDecodingStructureInPayload: detected frame size error";
QUICHE teamfd50a402018-12-07 22:54:05 -050090 return ReportFrameSizeError();
91 }
92 }
93
94 // Initializes the two remaining* fields, which is needed if the frame's
95 // payload is split across buffers, or the decoder calls ReadPadLength or
96 // StartDecodingStructureInPayload, and of course the methods below which
97 // read those fields, as their names imply.
98 void InitializeRemainders() {
99 remaining_payload_ = frame_header().payload_length;
100 // Note that remaining_total_payload() relies on remaining_padding_ being
101 // zero for frames that have no padding.
102 remaining_padding_ = 0;
103 }
104
105 // Returns the number of bytes of the frame's payload that remain to be
106 // decoded, including any trailing padding. This method must only be called
107 // after the variables have been initialized, which in practice means once a
108 // payload decoder has called InitializeRemainders and/or ReadPadLength.
109 size_t remaining_total_payload() const {
110 DCHECK(IsPaddable() || remaining_padding_ == 0) << frame_header();
111 return remaining_payload_ + remaining_padding_;
112 }
113
114 // Returns the number of bytes of the frame's payload that remain to be
115 // decoded, excluding any trailing padding. This method must only be called
116 // after the variable has been initialized, which in practice means once a
117 // payload decoder has called InitializeRemainders; ReadPadLength will deduct
118 // the total number of padding bytes from remaining_payload_, including the
119 // size of the Pad Length field itself (1 byte).
120 size_t remaining_payload() const { return remaining_payload_; }
121
122 // Returns the number of bytes of the frame's payload that remain to be
123 // decoded, including any trailing padding. This method must only be called if
124 // the frame type allows padding, and after the variable has been initialized,
125 // which in practice means once a payload decoder has called
126 // InitializeRemainders and/or ReadPadLength.
127 size_t remaining_payload_and_padding() const {
128 DCHECK(IsPaddable()) << frame_header();
129 return remaining_payload_ + remaining_padding_;
130 }
131
132 // Returns the number of bytes of trailing padding after the payload that
133 // remain to be decoded. This method must only be called if the frame type
134 // allows padding, and after the variable has been initialized, which in
135 // practice means once a payload decoder has called InitializeRemainders,
136 // and isn't set to a non-zero value until ReadPadLength has been called.
137 uint32_t remaining_padding() const {
138 DCHECK(IsPaddable()) << frame_header();
139 return remaining_padding_;
140 }
141
142 // How many bytes of the remaining payload are in db?
143 size_t AvailablePayload(DecodeBuffer* db) const {
144 return db->MinLengthRemaining(remaining_payload_);
145 }
146
147 // How many bytes of the remaining payload and padding are in db?
148 // Call only for frames whose type is paddable.
149 size_t AvailablePayloadAndPadding(DecodeBuffer* db) const {
150 DCHECK(IsPaddable()) << frame_header();
151 return db->MinLengthRemaining(remaining_payload_ + remaining_padding_);
152 }
153
154 // How many bytes of the padding that have not yet been skipped are in db?
155 // Call only after remaining_padding_ has been set (for padded frames), or
156 // been cleared (for unpadded frames); and after all of the non-padding
157 // payload has been decoded.
158 size_t AvailablePadding(DecodeBuffer* db) const {
159 DCHECK(IsPaddable()) << frame_header();
160 DCHECK_EQ(remaining_payload_, 0u);
161 return db->MinLengthRemaining(remaining_padding_);
162 }
163
164 // Reduces remaining_payload_ by amount. To be called by a payload decoder
165 // after it has passed a variable length portion of the payload to the
166 // listener; remaining_payload_ will be automatically reduced when fixed
167 // size structures and padding, including the Pad Length field, are decoded.
168 void ConsumePayload(size_t amount) {
169 DCHECK_LE(amount, remaining_payload_);
170 remaining_payload_ -= amount;
171 }
172
173 // Reads the Pad Length field into remaining_padding_, and appropriately sets
174 // remaining_payload_. When present, the Pad Length field is always the first
175 // field in the payload, which this method relies on so that the caller need
176 // not set remaining_payload_ before calling this method.
177 // If report_pad_length is true, calls the listener's OnPadLength method when
178 // it decodes the Pad Length field.
179 // Returns kDecodeDone if the decode buffer was not empty (i.e. because the
180 // field is only a single byte long, it can always be decoded if the buffer is
181 // not empty).
182 // Returns kDecodeError if the buffer is empty because the frame has no
183 // payload (i.e. payload_length() == 0).
184 // Returns kDecodeInProgress if the buffer is empty but the frame has a
185 // payload.
186 DecodeStatus ReadPadLength(DecodeBuffer* db, bool report_pad_length);
187
188 // Skip the trailing padding bytes; only call once remaining_payload_==0.
189 // Returns true when the padding has been skipped.
190 // Does NOT check that the padding is all zeroes.
191 bool SkipPadding(DecodeBuffer* db);
192
193 // Calls the listener's OnFrameSizeError method and returns kDecodeError.
194 DecodeStatus ReportFrameSizeError();
195
196 private:
197 friend class Http2FrameDecoder;
198 friend class test::FrameDecoderStatePeer;
199
200 // Starts the decoding of a common frame header. Returns true if completed the
201 // decoding, false if the decode buffer didn't have enough data in it, in
202 // which case the decode buffer will have been drained and the caller should
203 // call ResumeDecodingFrameHeader when more data is available. This is called
204 // from Http2FrameDecoder, a friend class.
205 bool StartDecodingFrameHeader(DecodeBuffer* db) {
206 return structure_decoder_.Start(&frame_header_, db);
207 }
208
209 // Resumes decoding the common frame header after the preceding call to
210 // StartDecodingFrameHeader returned false, as did any subsequent calls to
211 // ResumeDecodingFrameHeader. This is called from Http2FrameDecoder,
212 // a friend class.
213 bool ResumeDecodingFrameHeader(DecodeBuffer* db) {
214 return structure_decoder_.Resume(&frame_header_, db);
215 }
216
217 // Clear any of the flags in the frame header that aren't set in valid_flags.
218 void RetainFlags(uint8_t valid_flags) {
219 frame_header_.RetainFlags(valid_flags);
220 }
221
222 // Clear all of the flags in the frame header; for use with frame types that
223 // don't define any flags, such as WINDOW_UPDATE.
224 void ClearFlags() { frame_header_.flags = Http2FrameFlag(); }
225
226 // Returns true if the type of frame being decoded can have padding.
227 bool IsPaddable() const {
228 return frame_header().type == Http2FrameType::DATA ||
229 frame_header().type == Http2FrameType::HEADERS ||
230 frame_header().type == Http2FrameType::PUSH_PROMISE;
231 }
232
233 Http2FrameDecoderListener* listener_ = nullptr;
234 Http2FrameHeader frame_header_;
235
236 // Number of bytes remaining to be decoded, if set; does not include the
237 // trailing padding once the length of padding has been determined.
238 // See ReadPadLength.
239 uint32_t remaining_payload_;
240
241 // The amount of trailing padding after the payload that remains to be
242 // decoded. See ReadPadLength.
243 uint32_t remaining_padding_;
244
245 // Generic decoder of structures, which takes care of buffering the needed
246 // bytes if the encoded structure is split across decode buffers.
247 Http2StructureDecoder structure_decoder_;
248};
249
250} // namespace http2
251
252#endif // QUICHE_HTTP2_DECODER_FRAME_DECODER_STATE_H_