mirror of
https://github.com/klzgrad/naiveproxy.git
synced 2024-11-24 22:36:09 +03:00
249 lines
11 KiB
C++
249 lines
11 KiB
C++
// Copyright 2016 The Chromium Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#ifndef NET_THIRD_PARTY_HTTP2_DECODER_FRAME_DECODER_STATE_H_
|
|
#define NET_THIRD_PARTY_HTTP2_DECODER_FRAME_DECODER_STATE_H_
|
|
|
|
// FrameDecoderState provides state and behaviors in support of decoding
|
|
// the common frame header and the payload of all frame types.
|
|
// It is an input to all of the payload decoders.
|
|
|
|
// TODO(jamessynge): Since FrameDecoderState has far more than state in it,
|
|
// rename to FrameDecoderHelper, or similar.
|
|
|
|
#include <stddef.h>
|
|
|
|
#include "base/logging.h"
|
|
#include "net/third_party/http2/decoder/decode_buffer.h"
|
|
#include "net/third_party/http2/decoder/decode_status.h"
|
|
#include "net/third_party/http2/decoder/http2_frame_decoder_listener.h"
|
|
#include "net/third_party/http2/decoder/http2_structure_decoder.h"
|
|
#include "net/third_party/http2/http2_constants.h"
|
|
#include "net/third_party/http2/http2_structures.h"
|
|
#include "net/third_party/http2/platform/api/http2_export.h"
|
|
|
|
namespace http2 {
|
|
namespace test {
|
|
class FrameDecoderStatePeer;
|
|
} // namespace test
|
|
|
|
class HTTP2_EXPORT_PRIVATE FrameDecoderState {
|
|
public:
|
|
FrameDecoderState() {}
|
|
|
|
// Sets the listener which the decoders should call as they decode HTTP/2
|
|
// frames. The listener can be changed at any time, which allows for replacing
|
|
// it with a no-op listener when an error is detected, either by the payload
|
|
// decoder (OnPaddingTooLong or OnFrameSizeError) or by the "real" listener.
|
|
// That in turn allows us to define Http2FrameDecoderListener such that all
|
|
// methods have return type void, with no direct way to indicate whether the
|
|
// decoder should stop, and to eliminate from the decoder all checks of the
|
|
// return value. Instead the listener/caller can simply replace the current
|
|
// listener with a no-op listener implementation.
|
|
// TODO(jamessynge): Make set_listener private as only Http2FrameDecoder
|
|
// and tests need to set it, so it doesn't need to be public.
|
|
void set_listener(Http2FrameDecoderListener* listener) {
|
|
listener_ = listener;
|
|
}
|
|
Http2FrameDecoderListener* listener() const { return listener_; }
|
|
|
|
// The most recently decoded frame header.
|
|
const Http2FrameHeader& frame_header() const { return frame_header_; }
|
|
|
|
// Decode a structure in the payload, adjusting remaining_payload_ to account
|
|
// for the consumed portion of the payload. Returns kDecodeDone when fully
|
|
// decoded, kDecodeError if it ran out of payload before decoding completed,
|
|
// and kDecodeInProgress if the decode buffer didn't have enough of the
|
|
// remaining payload.
|
|
template <class S>
|
|
DecodeStatus StartDecodingStructureInPayload(S* out, DecodeBuffer* db) {
|
|
DVLOG(2) << __func__ << "\n\tdb->Remaining=" << db->Remaining()
|
|
<< "\n\tremaining_payload_=" << remaining_payload_
|
|
<< "\n\tneed=" << S::EncodedSize();
|
|
DecodeStatus status =
|
|
structure_decoder_.Start(out, db, &remaining_payload_);
|
|
if (status != DecodeStatus::kDecodeError) {
|
|
return status;
|
|
}
|
|
DVLOG(2) << "StartDecodingStructureInPayload: detected frame size error";
|
|
return ReportFrameSizeError();
|
|
}
|
|
|
|
// Resume decoding of a structure that has been split across buffers,
|
|
// adjusting remaining_payload_ to account for the consumed portion of
|
|
// the payload. Returns values are as for StartDecodingStructureInPayload.
|
|
template <class S>
|
|
DecodeStatus ResumeDecodingStructureInPayload(S* out, DecodeBuffer* db) {
|
|
DVLOG(2) << __func__ << "\n\tdb->Remaining=" << db->Remaining()
|
|
<< "\n\tremaining_payload_=" << remaining_payload_;
|
|
if (structure_decoder_.Resume(out, db, &remaining_payload_)) {
|
|
return DecodeStatus::kDecodeDone;
|
|
} else if (remaining_payload_ > 0) {
|
|
return DecodeStatus::kDecodeInProgress;
|
|
} else {
|
|
DVLOG(2) << "ResumeDecodingStructureInPayload: detected frame size error";
|
|
return ReportFrameSizeError();
|
|
}
|
|
}
|
|
|
|
// Initializes the two remaining* fields, which is needed if the frame's
|
|
// payload is split across buffers, or the decoder calls ReadPadLength or
|
|
// StartDecodingStructureInPayload, and of course the methods below which
|
|
// read those fields, as their names imply.
|
|
void InitializeRemainders() {
|
|
remaining_payload_ = frame_header().payload_length;
|
|
// Note that remaining_total_payload() relies on remaining_padding_ being
|
|
// zero for frames that have no padding.
|
|
remaining_padding_ = 0;
|
|
}
|
|
|
|
// Returns the number of bytes of the frame's payload that remain to be
|
|
// decoded, including any trailing padding. This method must only be called
|
|
// after the variables have been initialized, which in practice means once a
|
|
// payload decoder has called InitializeRemainders and/or ReadPadLength.
|
|
size_t remaining_total_payload() const {
|
|
DCHECK(IsPaddable() || remaining_padding_ == 0) << frame_header();
|
|
return remaining_payload_ + remaining_padding_;
|
|
}
|
|
|
|
// Returns the number of bytes of the frame's payload that remain to be
|
|
// decoded, excluding any trailing padding. This method must only be called
|
|
// after the variable has been initialized, which in practice means once a
|
|
// payload decoder has called InitializeRemainders; ReadPadLength will deduct
|
|
// the total number of padding bytes from remaining_payload_, including the
|
|
// size of the Pad Length field itself (1 byte).
|
|
size_t remaining_payload() const { return remaining_payload_; }
|
|
|
|
// Returns the number of bytes of the frame's payload that remain to be
|
|
// decoded, including any trailing padding. This method must only be called if
|
|
// the frame type allows padding, and after the variable has been initialized,
|
|
// which in practice means once a payload decoder has called
|
|
// InitializeRemainders and/or ReadPadLength.
|
|
size_t remaining_payload_and_padding() const {
|
|
DCHECK(IsPaddable()) << frame_header();
|
|
return remaining_payload_ + remaining_padding_;
|
|
}
|
|
|
|
// Returns the number of bytes of trailing padding after the payload that
|
|
// remain to be decoded. This method must only be called if the frame type
|
|
// allows padding, and after the variable has been initialized, which in
|
|
// practice means once a payload decoder has called InitializeRemainders,
|
|
// and isn't set to a non-zero value until ReadPadLength has been called.
|
|
uint32_t remaining_padding() const {
|
|
DCHECK(IsPaddable()) << frame_header();
|
|
return remaining_padding_;
|
|
}
|
|
|
|
// How many bytes of the remaining payload are in db?
|
|
size_t AvailablePayload(DecodeBuffer* db) const {
|
|
return db->MinLengthRemaining(remaining_payload_);
|
|
}
|
|
|
|
// How many bytes of the remaining payload and padding are in db?
|
|
// Call only for frames whose type is paddable.
|
|
size_t AvailablePayloadAndPadding(DecodeBuffer* db) const {
|
|
DCHECK(IsPaddable()) << frame_header();
|
|
return db->MinLengthRemaining(remaining_payload_ + remaining_padding_);
|
|
}
|
|
|
|
// How many bytes of the padding that have not yet been skipped are in db?
|
|
// Call only after remaining_padding_ has been set (for padded frames), or
|
|
// been cleared (for unpadded frames); and after all of the non-padding
|
|
// payload has been decoded.
|
|
size_t AvailablePadding(DecodeBuffer* db) const {
|
|
DCHECK(IsPaddable()) << frame_header();
|
|
DCHECK_EQ(remaining_payload_, 0u);
|
|
return db->MinLengthRemaining(remaining_padding_);
|
|
}
|
|
|
|
// Reduces remaining_payload_ by amount. To be called by a payload decoder
|
|
// after it has passed a variable length portion of the payload to the
|
|
// listener; remaining_payload_ will be automatically reduced when fixed
|
|
// size structures and padding, including the Pad Length field, are decoded.
|
|
void ConsumePayload(size_t amount) {
|
|
DCHECK_LE(amount, remaining_payload_);
|
|
remaining_payload_ -= amount;
|
|
}
|
|
|
|
// Reads the Pad Length field into remaining_padding_, and appropriately sets
|
|
// remaining_payload_. When present, the Pad Length field is always the first
|
|
// field in the payload, which this method relies on so that the caller need
|
|
// not set remaining_payload_ before calling this method.
|
|
// If report_pad_length is true, calls the listener's OnPadLength method when
|
|
// it decodes the Pad Length field.
|
|
// Returns kDecodeDone if the decode buffer was not empty (i.e. because the
|
|
// field is only a single byte long, it can always be decoded if the buffer is
|
|
// not empty).
|
|
// Returns kDecodeError if the buffer is empty because the frame has no
|
|
// payload (i.e. payload_length() == 0).
|
|
// Returns kDecodeInProgress if the buffer is empty but the frame has a
|
|
// payload.
|
|
DecodeStatus ReadPadLength(DecodeBuffer* db, bool report_pad_length);
|
|
|
|
// Skip the trailing padding bytes; only call once remaining_payload_==0.
|
|
// Returns true when the padding has been skipped.
|
|
// Does NOT check that the padding is all zeroes.
|
|
bool SkipPadding(DecodeBuffer* db);
|
|
|
|
// Calls the listener's OnFrameSizeError method and returns kDecodeError.
|
|
DecodeStatus ReportFrameSizeError();
|
|
|
|
private:
|
|
friend class Http2FrameDecoder;
|
|
friend class test::FrameDecoderStatePeer;
|
|
|
|
// Starts the decoding of a common frame header. Returns true if completed the
|
|
// decoding, false if the decode buffer didn't have enough data in it, in
|
|
// which case the decode buffer will have been drained and the caller should
|
|
// call ResumeDecodingFrameHeader when more data is available. This is called
|
|
// from Http2FrameDecoder, a friend class.
|
|
bool StartDecodingFrameHeader(DecodeBuffer* db) {
|
|
return structure_decoder_.Start(&frame_header_, db);
|
|
}
|
|
|
|
// Resumes decoding the common frame header after the preceding call to
|
|
// StartDecodingFrameHeader returned false, as did any subsequent calls to
|
|
// ResumeDecodingFrameHeader. This is called from Http2FrameDecoder,
|
|
// a friend class.
|
|
bool ResumeDecodingFrameHeader(DecodeBuffer* db) {
|
|
return structure_decoder_.Resume(&frame_header_, db);
|
|
}
|
|
|
|
// Clear any of the flags in the frame header that aren't set in valid_flags.
|
|
void RetainFlags(uint8_t valid_flags) {
|
|
frame_header_.RetainFlags(valid_flags);
|
|
}
|
|
|
|
// Clear all of the flags in the frame header; for use with frame types that
|
|
// don't define any flags, such as WINDOW_UPDATE.
|
|
void ClearFlags() { frame_header_.flags = Http2FrameFlag(); }
|
|
|
|
// Returns true if the type of frame being decoded can have padding.
|
|
bool IsPaddable() const {
|
|
return frame_header().type == Http2FrameType::DATA ||
|
|
frame_header().type == Http2FrameType::HEADERS ||
|
|
frame_header().type == Http2FrameType::PUSH_PROMISE;
|
|
}
|
|
|
|
Http2FrameDecoderListener* listener_ = nullptr;
|
|
Http2FrameHeader frame_header_;
|
|
|
|
// Number of bytes remaining to be decoded, if set; does not include the
|
|
// trailing padding once the length of padding has been determined.
|
|
// See ReadPadLength.
|
|
uint32_t remaining_payload_;
|
|
|
|
// The amount of trailing padding after the payload that remains to be
|
|
// decoded. See ReadPadLength.
|
|
uint32_t remaining_padding_;
|
|
|
|
// Generic decoder of structures, which takes care of buffering the needed
|
|
// bytes if the encoded structure is split across decode buffers.
|
|
Http2StructureDecoder structure_decoder_;
|
|
};
|
|
|
|
} // namespace http2
|
|
|
|
#endif // NET_THIRD_PARTY_HTTP2_DECODER_FRAME_DECODER_STATE_H_
|