| Index: net/spdy/spdy_deframer_visitor.cc
|
| diff --git a/net/spdy/spdy_deframer_visitor.cc b/net/spdy/spdy_deframer_visitor.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..3d7a5d73b85a46f078acc6d8715bbcbc67ea43c9
|
| --- /dev/null
|
| +++ b/net/spdy/spdy_deframer_visitor.cc
|
| @@ -0,0 +1,1098 @@
|
| +// 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.
|
| +
|
| +#include "net/spdy/spdy_deframer_visitor.h"
|
| +
|
| +#include <stdlib.h>
|
| +#include <string.h>
|
| +
|
| +#include <algorithm>
|
| +#include <limits>
|
| +
|
| +#include "base/logging.h"
|
| +#include "base/memory/ptr_util.h"
|
| +#include "net/spdy/hpack/hpack_constants.h"
|
| +#include "net/spdy/mock_spdy_framer_visitor.h"
|
| +#include "net/spdy/spdy_frame_builder.h"
|
| +#include "net/spdy/spdy_frame_reader.h"
|
| +#include "net/spdy/spdy_protocol.h"
|
| +#include "net/spdy/spdy_test_utils.h"
|
| +
|
| +using ::base::MakeUnique;
|
| +using ::base::StringPiece;
|
| +using ::std::string;
|
| +using ::testing::AssertionFailure;
|
| +using ::testing::AssertionResult;
|
| +using ::testing::AssertionSuccess;
|
| +
|
| +namespace net {
|
| +namespace test {
|
| +
|
| +// Specify whether to process headers as request or response in visitor-related
|
| +// params.
|
| +enum class HeaderDirection { REQUEST, RESPONSE };
|
| +
|
| +// Types of HTTP/2 frames, per RFC 7540.
|
| +// TODO(jamessynge): Switch to using //net/http2/http2_constants.h when ready.
|
| +enum Http2FrameType {
|
| + DATA = 0,
|
| + HEADERS = 1,
|
| + PRIORITY = 2,
|
| + RST_STREAM = 3,
|
| + SETTINGS = 4,
|
| + PUSH_PROMISE = 5,
|
| + PING = 6,
|
| + GOAWAY = 7,
|
| + WINDOW_UPDATE = 8,
|
| + CONTINUATION = 9,
|
| + ALTSVC = 10,
|
| +
|
| + // Not a frame type.
|
| + UNSET = -1,
|
| + UNKNOWN = -2,
|
| +};
|
| +
|
| +// TODO(jamessynge): Switch to using //net/http2/http2_constants.h when ready.
|
| +const char* Http2FrameTypeToString(Http2FrameType v) {
|
| + switch (v) {
|
| + case DATA:
|
| + return "DATA";
|
| + case HEADERS:
|
| + return "HEADERS";
|
| + case PRIORITY:
|
| + return "PRIORITY";
|
| + case RST_STREAM:
|
| + return "RST_STREAM";
|
| + case SETTINGS:
|
| + return "SETTINGS";
|
| + case PUSH_PROMISE:
|
| + return "PUSH_PROMISE";
|
| + case PING:
|
| + return "PING";
|
| + case GOAWAY:
|
| + return "GOAWAY";
|
| + case WINDOW_UPDATE:
|
| + return "WINDOW_UPDATE";
|
| + case CONTINUATION:
|
| + return "CONTINUATION";
|
| + case ALTSVC:
|
| + return "ALTSVC";
|
| + case UNSET:
|
| + return "UNSET";
|
| + case UNKNOWN:
|
| + return "UNKNOWN";
|
| + default:
|
| + return "Invalid Http2FrameType";
|
| + }
|
| +}
|
| +
|
| +// TODO(jamessynge): Switch to using //net/http2/http2_constants.h when ready.
|
| +inline std::ostream& operator<<(std::ostream& out, Http2FrameType v) {
|
| + return out << Http2FrameTypeToString(v);
|
| +}
|
| +
|
| +// Flag bits in the flag field of the common header of HTTP/2 frames
|
| +// (see https://httpwg.github.io/specs/rfc7540.html#FrameHeader for details on
|
| +// the fixed 9-octet header structure shared by all frames).
|
| +// Flag bits are only valid for specified frame types.
|
| +// TODO(jamessynge): Switch to using //net/http2/http2_constants.h when ready.
|
| +enum Http2HeaderFlag {
|
| + NO_FLAGS = 0,
|
| +
|
| + END_STREAM_FLAG = 0x1,
|
| + ACK_FLAG = 0x1,
|
| + END_HEADERS_FLAG = 0x4,
|
| + PADDED_FLAG = 0x8,
|
| + PRIORITY_FLAG = 0x20,
|
| +};
|
| +
|
| +// Returns name of frame type.
|
| +// TODO(jamessynge): Switch to using //net/http2/http2_constants.h when ready.
|
| +const char* Http2FrameTypeToString(Http2FrameType v);
|
| +
|
| +void SpdyDeframerVisitorInterface::OnPingAck(
|
| + std::unique_ptr<SpdyPingIR> frame) {
|
| + OnPing(std::move(frame));
|
| +}
|
| +
|
| +void SpdyDeframerVisitorInterface::OnSettingsAck(
|
| + std::unique_ptr<SpdySettingsIR> frame) {
|
| + OnSettings(std::move(frame), nullptr);
|
| +}
|
| +
|
| +class SpdyTestDeframerImpl : public SpdyTestDeframer,
|
| + public SpdyHeadersHandlerInterface {
|
| + public:
|
| + explicit SpdyTestDeframerImpl(
|
| + std::unique_ptr<SpdyDeframerVisitorInterface> listener)
|
| + : listener_(std::move(listener)) {
|
| + CHECK(listener_);
|
| + }
|
| + ~SpdyTestDeframerImpl() override {}
|
| +
|
| + bool AtFrameEnd() override;
|
| +
|
| + // Callbacks defined in SpdyFramerVisitorInterface. These are in the
|
| + // alphabetical order for ease of navigation, and are not in same order
|
| + // as in SpdyFramerVisitorInterface.
|
| + void OnAltSvc(SpdyStreamId stream_id,
|
| + StringPiece origin,
|
| + const SpdyAltSvcWireFormat::AlternativeServiceVector&
|
| + altsvc_vector) override;
|
| + void OnBlocked(SpdyStreamId stream_id) override;
|
| + void OnContinuation(SpdyStreamId stream_id, bool end) override;
|
| + SpdyHeadersHandlerInterface* OnHeaderFrameStart(
|
| + SpdyStreamId stream_id) override;
|
| + void OnHeaderFrameEnd(SpdyStreamId stream_id, bool end_headers) override;
|
| + bool OnControlFrameHeaderData(SpdyStreamId stream_id,
|
| + const char* header_data,
|
| + size_t header_data_len) override;
|
| + void OnDataFrameHeader(SpdyStreamId stream_id,
|
| + size_t length,
|
| + bool fin) override;
|
| + void OnError(SpdyFramer* framer) override;
|
| + void OnGoAway(SpdyStreamId last_accepted_stream_id,
|
| + SpdyGoAwayStatus status) override;
|
| + bool OnGoAwayFrameData(const char* goaway_data, size_t len) override;
|
| + void OnHeaders(SpdyStreamId stream_id,
|
| + bool has_priority,
|
| + int weight,
|
| + SpdyStreamId parent_stream_id,
|
| + bool exclusive,
|
| + bool fin,
|
| + bool end) override;
|
| + void OnPing(SpdyPingId unique_id, bool is_ack) override;
|
| + void OnPriority(SpdyStreamId stream_id,
|
| + SpdyStreamId parent_stream_id,
|
| + int weight,
|
| + bool exclusive) override;
|
| + void OnPushPromise(SpdyStreamId stream_id,
|
| + SpdyStreamId promised_stream_id,
|
| + bool end) override;
|
| + void OnRstStream(SpdyStreamId stream_id, SpdyRstStreamStatus status) override;
|
| + bool OnRstStreamFrameData(const char* rst_stream_data, size_t len) override;
|
| + void OnSetting(SpdySettingsIds id, uint8_t flags, uint32_t value) override;
|
| + void OnSettings(bool clear_persisted) override;
|
| + void OnSettingsAck() override;
|
| + void OnSettingsEnd() override;
|
| + void OnStreamFrameData(SpdyStreamId stream_id,
|
| + const char* data,
|
| + size_t len) override;
|
| + void OnStreamEnd(SpdyStreamId stream_id) override;
|
| + void OnStreamPadding(SpdyStreamId stream_id, size_t len) override;
|
| + void OnSynReply(SpdyStreamId stream_id, bool fin) override;
|
| + void OnSynStream(SpdyStreamId stream_id,
|
| + SpdyStreamId associated_stream_id,
|
| + SpdyPriority priority,
|
| + bool fin,
|
| + bool unidirectional) override;
|
| + bool OnUnknownFrame(SpdyStreamId stream_id, int frame_type) override;
|
| + void OnWindowUpdate(SpdyStreamId stream_id, int delta_window_size) override;
|
| +
|
| + // Callbacks defined in SpdyHeadersHandlerInterface.
|
| +
|
| + void OnHeaderBlockStart() override;
|
| + void OnHeader(StringPiece key, StringPiece value) override;
|
| + void OnHeaderBlockEnd(size_t header_bytes_parsed) override;
|
| +
|
| + protected:
|
| + void AtDataEnd();
|
| + void AtGoAwayEnd();
|
| + void AtHeadersEnd();
|
| + void AtPushPromiseEnd();
|
| +
|
| + // Per-physical frame state.
|
| + // Frame type of the frame currently being processed.
|
| + Http2FrameType frame_type_ = UNSET;
|
| + // Stream id of the frame currently being processed.
|
| + SpdyStreamId stream_id_;
|
| + // Did the most recent frame header include the END_HEADERS flag?
|
| + bool end_ = false;
|
| + // Did the most recent frame header include the ack flag?
|
| + bool ack_ = false;
|
| +
|
| + // Per-HPACK block state. Only valid while processing a HEADERS or
|
| + // PUSH_PROMISE frame, and its CONTINUATION frames.
|
| + // Did the most recent HEADERS or PUSH_PROMISE include the END_STREAM flag?
|
| + // Note that this does not necessarily indicate that the current frame is
|
| + // the last frame for the stream (may be followed by CONTINUATION frames,
|
| + // may only half close).
|
| + bool fin_ = false;
|
| + bool got_hpack_end_ = false;
|
| +
|
| + std::unique_ptr<string> data_;
|
| +
|
| + // Total length of the data frame.
|
| + size_t data_len_ = 0;
|
| +
|
| + // Amount of skipped padding (i.e. total length of padding, including Pad
|
| + // Length field).
|
| + size_t padding_len_ = 0;
|
| +
|
| + std::unique_ptr<string> goaway_description_;
|
| + std::unique_ptr<StringPairVector> headers_;
|
| + std::unique_ptr<SettingVector> settings_;
|
| + std::unique_ptr<TestHeadersHandler> headers_handler_;
|
| +
|
| + std::unique_ptr<SpdyGoAwayIR> goaway_ir_;
|
| + std::unique_ptr<SpdyHeadersIR> headers_ir_;
|
| + std::unique_ptr<SpdyPushPromiseIR> push_promise_ir_;
|
| + std::unique_ptr<SpdySettingsIR> settings_ir_;
|
| +
|
| + private:
|
| + std::unique_ptr<SpdyDeframerVisitorInterface> listener_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(SpdyTestDeframerImpl);
|
| +};
|
| +
|
| +// static
|
| +std::unique_ptr<SpdyTestDeframer> SpdyTestDeframer::CreateConverter(
|
| + std::unique_ptr<SpdyDeframerVisitorInterface> listener) {
|
| + return MakeUnique<SpdyTestDeframerImpl>(std::move(listener));
|
| +}
|
| +
|
| +void SpdyTestDeframerImpl::AtDataEnd() {
|
| + DVLOG(1) << "AtDataEnd";
|
| + CHECK_EQ(data_len_, padding_len_ + data_->size());
|
| + auto ptr = MakeUnique<SpdyDataIR>(stream_id_, std::move(*data_));
|
| + CHECK_EQ(0u, data_->size());
|
| + data_.reset();
|
| +
|
| + CHECK_LE(0u, padding_len_);
|
| + CHECK_LE(padding_len_, 256u);
|
| + if (padding_len_ > 0) {
|
| + ptr->set_padding_len(padding_len_);
|
| + }
|
| + padding_len_ = 0;
|
| +
|
| + ptr->set_fin(fin_);
|
| + listener_->OnData(std::move(ptr));
|
| + frame_type_ = UNSET;
|
| + fin_ = false;
|
| + data_len_ = 0;
|
| +}
|
| +
|
| +void SpdyTestDeframerImpl::AtGoAwayEnd() {
|
| + DVLOG(1) << "AtDataEnd";
|
| + CHECK_EQ(frame_type_, GOAWAY);
|
| + CHECK(goaway_description_);
|
| + if (goaway_description_->empty()) {
|
| + listener_->OnGoAway(std::move(goaway_ir_));
|
| + } else {
|
| + listener_->OnGoAway(MakeUnique<SpdyGoAwayIR>(
|
| + goaway_ir_->last_good_stream_id(), goaway_ir_->status(),
|
| + std::move(*goaway_description_)));
|
| + CHECK_EQ(0u, goaway_description_->size());
|
| + }
|
| + goaway_description_.reset();
|
| + goaway_ir_.reset();
|
| + frame_type_ = UNSET;
|
| +}
|
| +
|
| +void SpdyTestDeframerImpl::AtHeadersEnd() {
|
| + DVLOG(1) << "AtDataEnd";
|
| + CHECK(frame_type_ == HEADERS || frame_type_ == CONTINUATION)
|
| + << " frame_type_=" << Http2FrameTypeToString(frame_type_);
|
| + CHECK(end_) << " frame_type_=" << Http2FrameTypeToString(frame_type_);
|
| + CHECK(got_hpack_end_);
|
| +
|
| + CHECK(headers_ir_);
|
| + CHECK(headers_);
|
| + CHECK(headers_handler_);
|
| +
|
| + CHECK_LE(0u, padding_len_);
|
| + CHECK_LE(padding_len_, 256u);
|
| + if (padding_len_ > 0) {
|
| + headers_ir_->set_padding_len(padding_len_);
|
| + }
|
| + padding_len_ = 0;
|
| +
|
| + headers_ir_->set_header_block(headers_handler_->decoded_block().Clone());
|
| + headers_handler_.reset();
|
| + listener_->OnHeaders(std::move(headers_ir_), std::move(headers_));
|
| +
|
| + frame_type_ = UNSET;
|
| + fin_ = false;
|
| + end_ = false;
|
| + got_hpack_end_ = false;
|
| +}
|
| +
|
| +void SpdyTestDeframerImpl::AtPushPromiseEnd() {
|
| + DVLOG(1) << "AtDataEnd";
|
| + CHECK(frame_type_ == PUSH_PROMISE || frame_type_ == CONTINUATION)
|
| + << " frame_type_=" << Http2FrameTypeToString(frame_type_);
|
| + CHECK(end_) << " frame_type_=" << Http2FrameTypeToString(frame_type_);
|
| +
|
| + CHECK(push_promise_ir_);
|
| + CHECK(headers_);
|
| + CHECK(headers_handler_);
|
| +
|
| + CHECK_EQ(headers_ir_.get(), nullptr);
|
| +
|
| + CHECK_LE(0u, padding_len_);
|
| + CHECK_LE(padding_len_, 256u);
|
| + if (padding_len_ > 0) {
|
| + push_promise_ir_->set_padding_len(padding_len_);
|
| + }
|
| + padding_len_ = 0;
|
| +
|
| + push_promise_ir_->set_header_block(headers_handler_->decoded_block().Clone());
|
| + headers_handler_.reset();
|
| + listener_->OnPushPromise(std::move(push_promise_ir_), std::move(headers_));
|
| +
|
| + frame_type_ = UNSET;
|
| + end_ = false;
|
| +}
|
| +
|
| +bool SpdyTestDeframerImpl::AtFrameEnd() {
|
| + bool incomplete_logical_header = false;
|
| + // The caller says that the SpdyFrame has reached the end of the frame,
|
| + // so if we have any accumulated data, flush it.
|
| + switch (frame_type_) {
|
| + case DATA:
|
| + AtDataEnd();
|
| + break;
|
| +
|
| + case GOAWAY:
|
| + AtGoAwayEnd();
|
| + break;
|
| +
|
| + case HEADERS:
|
| + if (end_) {
|
| + AtHeadersEnd();
|
| + } else {
|
| + incomplete_logical_header = true;
|
| + }
|
| + break;
|
| +
|
| + case PUSH_PROMISE:
|
| + if (end_) {
|
| + AtPushPromiseEnd();
|
| + } else {
|
| + incomplete_logical_header = true;
|
| + }
|
| + break;
|
| +
|
| + case CONTINUATION:
|
| + if (end_) {
|
| + if (headers_ir_) {
|
| + AtHeadersEnd();
|
| + } else if (push_promise_ir_) {
|
| + AtPushPromiseEnd();
|
| + } else {
|
| + LOG(FATAL) << "Where is the SpdyFrameIR for the headers!";
|
| + }
|
| + } else {
|
| + incomplete_logical_header = true;
|
| + }
|
| + break;
|
| +
|
| + case UNSET:
|
| + // Except for the frame types above, the others don't leave any record
|
| + // in the state of this object. Make sure nothing got left by accident.
|
| + CHECK_EQ(data_.get(), nullptr);
|
| + CHECK_EQ(goaway_description_.get(), nullptr);
|
| + CHECK_EQ(goaway_ir_.get(), nullptr);
|
| + CHECK_EQ(headers_.get(), nullptr);
|
| + CHECK_EQ(headers_handler_.get(), nullptr);
|
| + CHECK_EQ(headers_ir_.get(), nullptr);
|
| + CHECK_EQ(push_promise_ir_.get(), nullptr);
|
| + CHECK_EQ(settings_.get(), nullptr);
|
| + CHECK_EQ(settings_ir_.get(), nullptr);
|
| + break;
|
| +
|
| + default:
|
| + SPDY_BUG << "Expected UNSET, instead frame_type_==" << frame_type_;
|
| + return false;
|
| + }
|
| + frame_type_ = UNSET;
|
| + stream_id_ = 0;
|
| + end_ = false;
|
| + ack_ = false;
|
| + if (!incomplete_logical_header) {
|
| + fin_ = false;
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +// Overridden methods from SpdyFramerVisitorInterface in alpha order...
|
| +
|
| +void SpdyTestDeframerImpl::OnAltSvc(
|
| + SpdyStreamId stream_id,
|
| + StringPiece origin,
|
| + const SpdyAltSvcWireFormat::AlternativeServiceVector& altsvc_vector) {
|
| + DVLOG(1) << "OnAltSvc stream_id: " << stream_id;
|
| + CHECK_EQ(frame_type_, UNSET) << " frame_type_="
|
| + << Http2FrameTypeToString(frame_type_);
|
| + CHECK_GT(stream_id, 0u);
|
| + auto ptr = MakeUnique<SpdyAltSvcIR>(stream_id);
|
| + ptr->set_origin(origin.as_string());
|
| + for (auto& altsvc : altsvc_vector) {
|
| + ptr->add_altsvc(altsvc);
|
| + }
|
| + listener_->OnAltSvc(std::move(ptr));
|
| +}
|
| +
|
| +// Frame type BLOCKED was removed in draft 12 of HTTP/2. The intent appears to
|
| +// have been to support debugging; it is not expected to be seen except if the
|
| +// peer "thinks" that a bug exists in the flow control such that the peer can't
|
| +// send because the receiver hasn't sent WINDOW_UPDATE frames. Since we might
|
| +// be talking to multiple backends, it is quite plausible that one backend
|
| +// is unable to take more input from the client (hence no WINDOW_UPDATE), yet
|
| +// other backends can take more input.
|
| +void SpdyTestDeframerImpl::OnBlocked(SpdyStreamId stream_id) {
|
| + LOG(FATAL) << "OnBlocked stream_id: " << stream_id;
|
| + CHECK_EQ(frame_type_, UNSET) << " frame_type_="
|
| + << Http2FrameTypeToString(frame_type_);
|
| + CHECK_GT(stream_id, 0u);
|
| + frame_type_ = UNSET;
|
| + stream_id_ = stream_id;
|
| +}
|
| +
|
| +// A CONTINUATION frame contains a Header Block Fragment, and immediately
|
| +// follows another frame that contains a Header Block Fragment (HEADERS,
|
| +// PUSH_PROMISE or CONTINUATION). The last such frame has the END flag set.
|
| +// SpdyFramer ensures that the behavior is correct before calling the visitor.
|
| +void SpdyTestDeframerImpl::OnContinuation(SpdyStreamId stream_id, bool end) {
|
| + DVLOG(1) << "OnContinuation stream_id: " << stream_id;
|
| + CHECK_EQ(frame_type_, UNSET) << " frame_type_="
|
| + << Http2FrameTypeToString(frame_type_);
|
| + CHECK_GT(stream_id, 0u);
|
| + CHECK_NE(nullptr, headers_.get());
|
| + frame_type_ = CONTINUATION;
|
| +
|
| + stream_id_ = stream_id;
|
| + end_ = end;
|
| +}
|
| +
|
| +// Called with the decompressed contents of headers, in zero or more pieces
|
| +// (i.e. an empty headers frame doesn't have any non-zero length data).
|
| +// Note that the end of the headers is indicated by header_data==nullptr
|
| +// AND header_data_len==0, even if there were no non-zero pieces.
|
| +// SpdyHeadersBlockParser decodes these.
|
| +// Returning false kills the connection (SpdyFramer enters state SPDY_ERROR).
|
| +bool SpdyTestDeframerImpl::OnControlFrameHeaderData(SpdyStreamId stream_id,
|
| + const char* header_data,
|
| + size_t header_data_len) {
|
| + DVLOG(1) << "OnControlFrameHeaderData stream_id: " << stream_id
|
| + << " len: " << header_data_len;
|
| + CHECK(frame_type_ == HEADERS || frame_type_ == CONTINUATION ||
|
| + frame_type_ == PUSH_PROMISE)
|
| + << " frame_type_=" << Http2FrameTypeToString(frame_type_);
|
| + CHECK_EQ(stream_id_, stream_id);
|
| + LOG(FATAL) << "Must call SpdyFramer::set_use_new_methods_for_test(true)";
|
| + return true;
|
| +}
|
| +
|
| +// Note that length includes the padding length (0 to 256, when the optional
|
| +// padding length field is counted). Padding comes after the payload, both
|
| +// for DATA frames and for control frames.
|
| +void SpdyTestDeframerImpl::OnDataFrameHeader(SpdyStreamId stream_id,
|
| + size_t length,
|
| + bool fin) {
|
| + DVLOG(1) << "OnDataFrameHeader stream_id: " << stream_id;
|
| + CHECK_EQ(frame_type_, UNSET) << " frame_type_="
|
| + << Http2FrameTypeToString(frame_type_);
|
| + CHECK_GT(stream_id, 0u);
|
| + CHECK_EQ(data_.get(), nullptr);
|
| + frame_type_ = DATA;
|
| +
|
| + stream_id_ = stream_id;
|
| + fin_ = fin;
|
| + data_len_ = length;
|
| + data_.reset(new string());
|
| +}
|
| +
|
| +// The SpdyFramer will not process any more data at this point.
|
| +void SpdyTestDeframerImpl::OnError(SpdyFramer* framer) {
|
| + DVLOG(1) << "SpdyFramer detected an error in the stream: "
|
| + << SpdyFramer::ErrorCodeToString(framer->error_code())
|
| + << " frame_type_: " << Http2FrameTypeToString(frame_type_);
|
| + listener_->OnError(framer, this);
|
| +}
|
| +
|
| +// Received a GOAWAY frame from the peer. The last stream id it accepted from us
|
| +// is |last_accepted_stream_id|. |status| is a protocol defined error code.
|
| +// The frame may also contain data. After this OnGoAwayFrameData will be called
|
| +// for any non-zero amount of data, and after that it will be called with len==0
|
| +// to indicate the end of the GOAWAY frame.
|
| +void SpdyTestDeframerImpl::OnGoAway(SpdyStreamId last_good_stream_id,
|
| + SpdyGoAwayStatus status) {
|
| + DVLOG(1) << "OnGoAway last_good_stream_id: " << last_good_stream_id
|
| + << " status: " << status;
|
| + CHECK_EQ(frame_type_, UNSET) << " frame_type_="
|
| + << Http2FrameTypeToString(frame_type_);
|
| + frame_type_ = GOAWAY;
|
| + goaway_ir_ = MakeUnique<SpdyGoAwayIR>(last_good_stream_id, status, "");
|
| + goaway_description_.reset(new string());
|
| +}
|
| +
|
| +// If len==0 then we've reached the end of the GOAWAY frame.
|
| +bool SpdyTestDeframerImpl::OnGoAwayFrameData(const char* goaway_data,
|
| + size_t len) {
|
| + DVLOG(1) << "OnGoAwayFrameData";
|
| + CHECK_EQ(frame_type_, GOAWAY) << " frame_type_="
|
| + << Http2FrameTypeToString(frame_type_);
|
| + CHECK(goaway_description_);
|
| + StringPiece(goaway_data, len).AppendToString(goaway_description_.get());
|
| + return true;
|
| +}
|
| +
|
| +SpdyHeadersHandlerInterface* SpdyTestDeframerImpl::OnHeaderFrameStart(
|
| + SpdyStreamId stream_id) {
|
| + return this;
|
| +}
|
| +
|
| +void SpdyTestDeframerImpl::OnHeaderFrameEnd(SpdyStreamId stream_id,
|
| + bool end_headers) {
|
| + DVLOG(1) << "OnHeaderFrameEnd stream_id: " << stream_id
|
| + << " end_headers: " << (end_headers ? "true" : "false");
|
| +}
|
| +
|
| +// Received the fixed portion of a HEADERS frame. Called before the variable
|
| +// length (including zero length) Header Block Fragment is processed. If fin
|
| +// is true then there will be no DATA or trailing HEADERS after this HEADERS
|
| +// frame.
|
| +// If end is true, then there will be no CONTINUATION frame(s) following this
|
| +// frame; else if true then there will be CONTINATION frames(s) immediately
|
| +// following this frame, terminated by a CONTINUATION frame with end==true.
|
| +void SpdyTestDeframerImpl::OnHeaders(SpdyStreamId stream_id,
|
| + bool has_priority,
|
| + int weight,
|
| + SpdyStreamId parent_stream_id,
|
| + bool exclusive,
|
| + bool fin,
|
| + bool end) {
|
| + DVLOG(1) << "OnHeaders stream_id: " << stream_id;
|
| + CHECK_EQ(frame_type_, UNSET) << " frame_type_="
|
| + << Http2FrameTypeToString(frame_type_);
|
| + CHECK_GT(stream_id, 0u);
|
| + frame_type_ = HEADERS;
|
| +
|
| + stream_id_ = stream_id;
|
| + fin_ = fin;
|
| + end_ = end;
|
| +
|
| + headers_.reset(new StringPairVector());
|
| + headers_handler_.reset(new TestHeadersHandler());
|
| + headers_ir_ = MakeUnique<SpdyHeadersIR>(stream_id);
|
| + headers_ir_->set_fin(fin);
|
| + if (has_priority) {
|
| + headers_ir_->set_has_priority(true);
|
| + headers_ir_->set_weight(weight);
|
| + headers_ir_->set_parent_stream_id(parent_stream_id);
|
| + headers_ir_->set_exclusive(exclusive);
|
| + }
|
| +}
|
| +
|
| +// The HTTP/2 protocol refers to the payload, |unique_id| here, as 8 octets of
|
| +// opaque data that is to be echoed back to the sender, with the ACK bit added.
|
| +// It isn't defined as a counter,
|
| +// or frame id, as the SpdyPingId naming might imply.
|
| +// Responding to a PING is supposed to be at the highest priority.
|
| +void SpdyTestDeframerImpl::OnPing(uint64_t unique_id, bool is_ack) {
|
| + DVLOG(1) << "OnPing unique_id: " << unique_id
|
| + << " is_ack: " << (is_ack ? "true" : "false");
|
| + CHECK_EQ(frame_type_, UNSET) << " frame_type_="
|
| + << Http2FrameTypeToString(frame_type_);
|
| + auto ptr = MakeUnique<SpdyPingIR>(unique_id);
|
| + if (is_ack) {
|
| + ptr->set_is_ack(is_ack);
|
| + listener_->OnPingAck(std::move(ptr));
|
| + } else {
|
| + listener_->OnPing(std::move(ptr));
|
| + }
|
| +}
|
| +
|
| +void SpdyTestDeframerImpl::OnPriority(SpdyStreamId stream_id,
|
| + SpdyStreamId parent_stream_id,
|
| + int weight,
|
| + bool exclusive) {
|
| + DVLOG(1) << "OnPriority stream_id: " << stream_id;
|
| + CHECK_EQ(frame_type_, UNSET) << " frame_type_="
|
| + << Http2FrameTypeToString(frame_type_);
|
| + CHECK_GT(stream_id, 0u);
|
| +
|
| + listener_->OnPriority(MakeUnique<SpdyPriorityIR>(stream_id, parent_stream_id,
|
| + weight, exclusive));
|
| +}
|
| +
|
| +void SpdyTestDeframerImpl::OnPushPromise(SpdyStreamId stream_id,
|
| + SpdyStreamId promised_stream_id,
|
| + bool end) {
|
| + DVLOG(1) << "OnPushPromise stream_id: " << stream_id;
|
| + CHECK_EQ(frame_type_, UNSET) << " frame_type_="
|
| + << Http2FrameTypeToString(frame_type_);
|
| + CHECK_GT(stream_id, 0u);
|
| +
|
| + frame_type_ = PUSH_PROMISE;
|
| + stream_id_ = stream_id;
|
| + end_ = end;
|
| +
|
| + headers_.reset(new StringPairVector());
|
| + headers_handler_.reset(new TestHeadersHandler());
|
| + push_promise_ir_ =
|
| + MakeUnique<SpdyPushPromiseIR>(stream_id, promised_stream_id);
|
| +}
|
| +
|
| +// Closes the specified stream. After this the sender may still send PRIORITY
|
| +// frames for this stream, which we can ignore.
|
| +void SpdyTestDeframerImpl::OnRstStream(SpdyStreamId stream_id,
|
| + SpdyRstStreamStatus status) {
|
| + DVLOG(1) << "OnRstStream stream_id: " << stream_id
|
| + << " status: " << status;
|
| + CHECK_EQ(frame_type_, UNSET) << " frame_type_="
|
| + << Http2FrameTypeToString(frame_type_);
|
| + CHECK_GT(stream_id, 0u);
|
| +
|
| + listener_->OnRstStream(MakeUnique<SpdyRstStreamIR>(stream_id, status));
|
| +}
|
| +
|
| +// The HTTP/2 spec states that there is no data in the frame other that the
|
| +// SpdyRstStreamStatus passed to OnRstStream, so it appears that SpdyFramer's
|
| +// comments w.r.t. this method are obsolete. We still receive a zero length
|
| +// invocation when RST_STREAM frame processing completes.
|
| +bool SpdyTestDeframerImpl::OnRstStreamFrameData(const char* rst_stream_data,
|
| + size_t len) {
|
| + DVLOG(1) << "OnRstStreamFrameData";
|
| + CHECK_EQ(0u, len);
|
| + return true;
|
| +}
|
| +
|
| +// Called for an individual setting. There is no negotiation, the sender is
|
| +// stating the value that the sender is using.
|
| +void SpdyTestDeframerImpl::OnSetting(SpdySettingsIds id,
|
| + uint8_t flags,
|
| + uint32_t value) {
|
| + DVLOG(1) << "OnSetting id: " << id << std::hex << " flags: " << flags
|
| + << " value: " << value;
|
| + CHECK_EQ(frame_type_, SETTINGS) << " frame_type_="
|
| + << Http2FrameTypeToString(frame_type_);
|
| + CHECK(settings_);
|
| + settings_->push_back(std::make_pair(id, value));
|
| + settings_ir_->AddSetting(id, true, true, value);
|
| +}
|
| +
|
| +// Called at the start of a SETTINGS frame with setting entries, but not the
|
| +// (required) ACK of a SETTINGS frame. There is no stream_id because
|
| +// the settings apply to the entire connection, not to an individual stream.
|
| +// The |clear_persisted| flag is a pre-HTTP/2 remnant.
|
| +void SpdyTestDeframerImpl::OnSettings(bool /*clear_persisted*/) {
|
| + DVLOG(1) << "OnSettings";
|
| + CHECK_EQ(frame_type_, UNSET) << " frame_type_="
|
| + << Http2FrameTypeToString(frame_type_);
|
| + CHECK_EQ(nullptr, settings_ir_.get());
|
| + CHECK_EQ(nullptr, settings_.get());
|
| + frame_type_ = SETTINGS;
|
| + ack_ = false;
|
| +
|
| + settings_.reset(new SettingVector());
|
| + settings_ir_.reset(new SpdySettingsIR());
|
| +}
|
| +
|
| +void SpdyTestDeframerImpl::OnSettingsAck() {
|
| + DVLOG(1) << "OnSettingsAck";
|
| + CHECK_EQ(frame_type_, UNSET) << " frame_type_="
|
| + << Http2FrameTypeToString(frame_type_);
|
| + auto ptr = MakeUnique<SpdySettingsIR>();
|
| + ptr->set_is_ack(true);
|
| + listener_->OnSettingsAck(std::move(ptr));
|
| +}
|
| +
|
| +void SpdyTestDeframerImpl::OnSettingsEnd() {
|
| + DVLOG(1) << "OnSettingsEnd";
|
| + CHECK_EQ(frame_type_, SETTINGS) << " frame_type_="
|
| + << Http2FrameTypeToString(frame_type_);
|
| + CHECK(!ack_);
|
| + CHECK_NE(nullptr, settings_ir_.get());
|
| + CHECK_NE(nullptr, settings_.get());
|
| + listener_->OnSettings(std::move(settings_ir_), std::move(settings_));
|
| + frame_type_ = UNSET;
|
| +}
|
| +
|
| +// Called for a zero length DATA frame with the END_STREAM flag set, or at the
|
| +// end a complete HPACK block (and its padding) that started with a HEADERS
|
| +// frame with the END_STREAM flag set. Doesn't apply to PUSH_PROMISE frames
|
| +// because they don't have END_STREAM flags.
|
| +void SpdyTestDeframerImpl::OnStreamEnd(SpdyStreamId stream_id) {
|
| + DVLOG(1) << "OnStreamEnd stream_id: " << stream_id;
|
| + CHECK_EQ(stream_id_, stream_id);
|
| + CHECK(frame_type_ == DATA || frame_type_ == HEADERS ||
|
| + frame_type_ == CONTINUATION)
|
| + << " frame_type_=" << Http2FrameTypeToString(frame_type_);
|
| + CHECK(fin_);
|
| +}
|
| +
|
| +// The data arg points into the non-padding payload of a DATA frame.
|
| +// This must be a DATA frame (i.e. this method will not be
|
| +// called for HEADERS or CONTINUATION frames).
|
| +// This method may be called multiple times for a single DATA frame, depending
|
| +// upon buffer boundaries.
|
| +void SpdyTestDeframerImpl::OnStreamFrameData(SpdyStreamId stream_id,
|
| + const char* data,
|
| + size_t len) {
|
| + DVLOG(1) << "OnStreamFrameData stream_id: " << stream_id
|
| + << " len: " << len;
|
| + CHECK_EQ(stream_id_, stream_id);
|
| + CHECK_EQ(frame_type_, DATA);
|
| + StringPiece(data, len).AppendToString(data_.get());
|
| +}
|
| +
|
| +// Called when padding is skipped over, including the padding length field at
|
| +// the start of the frame payload, and the actual padding at the end. len will
|
| +// be in the range 1 to 255.
|
| +void SpdyTestDeframerImpl::OnStreamPadding(SpdyStreamId stream_id, size_t len) {
|
| + DVLOG(1) << "OnStreamPadding stream_id: " << stream_id << " len: " << len;
|
| + CHECK(frame_type_ == DATA || frame_type_ == HEADERS ||
|
| + frame_type_ == PUSH_PROMISE)
|
| + << " frame_type_=" << Http2FrameTypeToString(frame_type_);
|
| + CHECK_EQ(stream_id_, stream_id);
|
| + CHECK_LE(1u, len);
|
| + CHECK_GE(255u, len);
|
| + padding_len_ += len;
|
| + CHECK_LE(padding_len_, 256u) << "len=" << len;
|
| +}
|
| +
|
| +// Obsolete.
|
| +void SpdyTestDeframerImpl::OnSynStream(SpdyStreamId stream_id,
|
| + SpdyStreamId associated_stream_id,
|
| + SpdyPriority priority,
|
| + bool fin,
|
| + bool unidirectional) {
|
| + DVLOG(1) << "OnSynStream stream_id: " << stream_id;
|
| + CHECK_EQ(frame_type_, UNSET) << " frame_type_="
|
| + << Http2FrameTypeToString(frame_type_);
|
| + frame_type_ = UNKNOWN;
|
| + stream_id_ = stream_id;
|
| + fin_ = fin;
|
| + LOG(DFATAL) << "SYN_STREAM is not a valid HTTP/2 frame type.";
|
| +}
|
| +
|
| +// Obsolete.
|
| +void SpdyTestDeframerImpl::OnSynReply(SpdyStreamId stream_id, bool fin) {
|
| + DVLOG(1) << "OnSynReply stream_id: " << stream_id;
|
| + CHECK_EQ(frame_type_, UNSET) << " frame_type_="
|
| + << Http2FrameTypeToString(frame_type_);
|
| + frame_type_ = UNKNOWN;
|
| + stream_id_ = stream_id;
|
| + fin_ = fin;
|
| + LOG(DFATAL) << "SYN_REPLY is not a valid HTTP/2 frame type.";
|
| +}
|
| +
|
| +// WINDOW_UPDATE is supposed to be hop-by-hop, according to the spec.
|
| +// stream_id is 0 if the update applies to the connection, else stream_id
|
| +// will be the id of a stream previously seen, which maybe half or fully
|
| +// closed.
|
| +void SpdyTestDeframerImpl::OnWindowUpdate(SpdyStreamId stream_id,
|
| + int delta_window_size) {
|
| + DVLOG(1) << "OnWindowUpdate stream_id: " << stream_id
|
| + << " delta_window_size: " << delta_window_size;
|
| + CHECK_EQ(frame_type_, UNSET) << " frame_type_="
|
| + << Http2FrameTypeToString(frame_type_);
|
| + CHECK_NE(0, delta_window_size);
|
| +
|
| + listener_->OnWindowUpdate(
|
| + MakeUnique<SpdyWindowUpdateIR>(stream_id, delta_window_size));
|
| +}
|
| +
|
| +// Return true to indicate that the stream_id is valid; if not valid then
|
| +// SpdyFramer considers the connection corrupted. Requires keeping track
|
| +// of the set of currently open streams. For now we'll assume that unknown
|
| +// frame types are unsupported.
|
| +bool SpdyTestDeframerImpl::OnUnknownFrame(SpdyStreamId stream_id,
|
| + int frame_type) {
|
| + DVLOG(1) << "OnAltSvc stream_id: " << stream_id;
|
| + CHECK_EQ(frame_type_, UNSET) << " frame_type_="
|
| + << Http2FrameTypeToString(frame_type_);
|
| + frame_type_ = UNKNOWN;
|
| +
|
| + stream_id_ = stream_id;
|
| + return false;
|
| +}
|
| +
|
| +// Callbacks defined in SpdyHeadersHandlerInterface.
|
| +
|
| +void SpdyTestDeframerImpl::OnHeaderBlockStart() {
|
| + CHECK(frame_type_ == HEADERS || frame_type_ == PUSH_PROMISE)
|
| + << " frame_type_=" << Http2FrameTypeToString(frame_type_);
|
| + CHECK(headers_);
|
| + CHECK_EQ(0u, headers_->size());
|
| + got_hpack_end_ = false;
|
| +}
|
| +
|
| +void SpdyTestDeframerImpl::OnHeader(StringPiece key, StringPiece value) {
|
| + CHECK(frame_type_ == HEADERS || frame_type_ == CONTINUATION ||
|
| + frame_type_ == PUSH_PROMISE)
|
| + << " frame_type_=" << Http2FrameTypeToString(frame_type_);
|
| + CHECK(!got_hpack_end_);
|
| + CHECK(headers_);
|
| + headers_->emplace_back(key.as_string(), value.as_string());
|
| + CHECK(headers_handler_);
|
| + headers_handler_->OnHeader(key, value);
|
| +}
|
| +
|
| +void SpdyTestDeframerImpl::OnHeaderBlockEnd(size_t header_bytes_parsed) {
|
| + CHECK(headers_);
|
| + CHECK(frame_type_ == HEADERS || frame_type_ == CONTINUATION ||
|
| + frame_type_ == PUSH_PROMISE)
|
| + << " frame_type_=" << Http2FrameTypeToString(frame_type_);
|
| + CHECK(end_);
|
| + CHECK(!got_hpack_end_);
|
| + got_hpack_end_ = true;
|
| +}
|
| +
|
| +class LoggingSpdyDeframerDelegate : public SpdyDeframerVisitorInterface {
|
| + public:
|
| + explicit LoggingSpdyDeframerDelegate(
|
| + std::unique_ptr<SpdyDeframerVisitorInterface> wrapped)
|
| + : wrapped_(std::move(wrapped)) {
|
| + if (!wrapped_) {
|
| + wrapped_ = MakeUnique<SpdyDeframerVisitorInterface>();
|
| + }
|
| + }
|
| + ~LoggingSpdyDeframerDelegate() override {}
|
| +
|
| + void OnAltSvc(std::unique_ptr<SpdyAltSvcIR> frame) override {
|
| + DVLOG(1) << "LoggingSpdyDeframerDelegate::OnAltSvc";
|
| + wrapped_->OnAltSvc(std::move(frame));
|
| + }
|
| + void OnData(std::unique_ptr<SpdyDataIR> frame) override {
|
| + DVLOG(1) << "LoggingSpdyDeframerDelegate::OnData";
|
| + wrapped_->OnData(std::move(frame));
|
| + }
|
| + void OnGoAway(std::unique_ptr<SpdyGoAwayIR> frame) override {
|
| + DVLOG(1) << "LoggingSpdyDeframerDelegate::OnGoAway";
|
| + wrapped_->OnGoAway(std::move(frame));
|
| + }
|
| +
|
| + // SpdyHeadersIR and SpdyPushPromiseIR each has a SpdyHeaderBlock which
|
| + // significantly modifies the headers, so the actual header entries (name
|
| + // and value strings) are provided in a vector.
|
| + void OnHeaders(std::unique_ptr<SpdyHeadersIR> frame,
|
| + std::unique_ptr<StringPairVector> headers) override {
|
| + DVLOG(1) << "LoggingSpdyDeframerDelegate::OnHeaders";
|
| + wrapped_->OnHeaders(std::move(frame), std::move(headers));
|
| + }
|
| +
|
| + void OnPing(std::unique_ptr<SpdyPingIR> frame) override {
|
| + DVLOG(1) << "LoggingSpdyDeframerDelegate::OnPing";
|
| + wrapped_->OnPing(std::move(frame));
|
| + }
|
| + void OnPingAck(std::unique_ptr<SpdyPingIR> frame) override {
|
| + DVLOG(1) << "LoggingSpdyDeframerDelegate::OnPingAck";
|
| + wrapped_->OnPingAck(std::move(frame));
|
| + }
|
| +
|
| + void OnPriority(std::unique_ptr<SpdyPriorityIR> frame) override {
|
| + DVLOG(1) << "LoggingSpdyDeframerDelegate::OnPriority";
|
| + wrapped_->OnPriority(std::move(frame));
|
| + }
|
| +
|
| + // SpdyHeadersIR and SpdyPushPromiseIR each has a SpdyHeaderBlock which
|
| + // significantly modifies the headers, so the actual header entries (name
|
| + // and value strings) are provided in a vector.
|
| + void OnPushPromise(std::unique_ptr<SpdyPushPromiseIR> frame,
|
| + std::unique_ptr<StringPairVector> headers) override {
|
| + DVLOG(1) << "LoggingSpdyDeframerDelegate::OnPushPromise";
|
| + wrapped_->OnPushPromise(std::move(frame), std::move(headers));
|
| + }
|
| +
|
| + void OnRstStream(std::unique_ptr<SpdyRstStreamIR> frame) override {
|
| + DVLOG(1) << "LoggingSpdyDeframerDelegate::OnRstStream";
|
| + wrapped_->OnRstStream(std::move(frame));
|
| + }
|
| +
|
| + // SpdySettingsIR has a map for settings, so loses info about the order of
|
| + // settings, and whether the same setting appeared more than once, so the
|
| + // the actual settings (parameter and value) are provided in a vector.
|
| + void OnSettings(std::unique_ptr<SpdySettingsIR> frame,
|
| + std::unique_ptr<SettingVector> settings) override {
|
| + DVLOG(1) << "LoggingSpdyDeframerDelegate::OnSettings";
|
| + wrapped_->OnSettings(std::move(frame), std::move(settings));
|
| + }
|
| +
|
| + // A settings frame with an ACK has no content, but for uniformity passing
|
| + // a frame with the ACK flag set.
|
| + void OnSettingsAck(std::unique_ptr<SpdySettingsIR> frame) override {
|
| + DVLOG(1) << "LoggingSpdyDeframerDelegate::OnSettingsAck";
|
| + wrapped_->OnSettingsAck(std::move(frame));
|
| + }
|
| +
|
| + void OnWindowUpdate(std::unique_ptr<SpdyWindowUpdateIR> frame) override {
|
| + DVLOG(1) << "LoggingSpdyDeframerDelegate::OnWindowUpdate";
|
| + wrapped_->OnWindowUpdate(std::move(frame));
|
| + }
|
| +
|
| + // The SpdyFramer will not process any more data at this point.
|
| + void OnError(SpdyFramer* framer, SpdyTestDeframer* deframer) override {
|
| + DVLOG(1) << "LoggingSpdyDeframerDelegate::OnError";
|
| + wrapped_->OnError(framer, deframer);
|
| + }
|
| +
|
| + private:
|
| + std::unique_ptr<SpdyDeframerVisitorInterface> wrapped_;
|
| +};
|
| +
|
| +// static
|
| +std::unique_ptr<SpdyDeframerVisitorInterface>
|
| +SpdyDeframerVisitorInterface::LogBeforeVisiting(
|
| + std::unique_ptr<SpdyDeframerVisitorInterface> wrapped_listener) {
|
| + return MakeUnique<LoggingSpdyDeframerDelegate>(std::move(wrapped_listener));
|
| +}
|
| +
|
| +CollectedFrame::CollectedFrame() {}
|
| +
|
| +CollectedFrame::CollectedFrame(CollectedFrame&& other)
|
| + : frame_ir(std::move(other.frame_ir)),
|
| + headers(std::move(other.headers)),
|
| + settings(std::move(other.settings)),
|
| + error_reported(other.error_reported) {}
|
| +
|
| +CollectedFrame::~CollectedFrame() {}
|
| +
|
| +CollectedFrame& CollectedFrame::operator=(CollectedFrame&& other) {
|
| + frame_ir = std::move(other.frame_ir);
|
| + headers = std::move(other.headers);
|
| + settings = std::move(other.settings);
|
| + error_reported = other.error_reported;
|
| + return *this;
|
| +}
|
| +
|
| +AssertionResult CollectedFrame::VerifyHasHeaders(
|
| + const StringPairVector& expected_headers) const {
|
| + if (headers.get() == nullptr)
|
| + return AssertionFailure();
|
| + if (*headers != expected_headers)
|
| + return AssertionFailure();
|
| +
|
| + return AssertionSuccess();
|
| +}
|
| +
|
| +AssertionResult CollectedFrame::VerifyHasSettings(
|
| + const SettingVector& expected_settings) const {
|
| + if (settings.get() == nullptr)
|
| + return AssertionFailure();
|
| + if (*settings != expected_settings)
|
| + return AssertionFailure();
|
| +
|
| + return AssertionSuccess();
|
| +}
|
| +
|
| +DeframerCallbackCollector::DeframerCallbackCollector(
|
| + std::vector<CollectedFrame>* collected_frames)
|
| + : collected_frames_(collected_frames) {
|
| + CHECK(collected_frames);
|
| +}
|
| +
|
| +void DeframerCallbackCollector::OnAltSvc(
|
| + std::unique_ptr<SpdyAltSvcIR> frame_ir) {
|
| + CollectedFrame cf;
|
| + cf.frame_ir = std::move(frame_ir);
|
| + collected_frames_->push_back(std::move(cf));
|
| +}
|
| +void DeframerCallbackCollector::OnData(std::unique_ptr<SpdyDataIR> frame_ir) {
|
| + CollectedFrame cf;
|
| + cf.frame_ir = std::move(frame_ir);
|
| + collected_frames_->push_back(std::move(cf));
|
| +}
|
| +void DeframerCallbackCollector::OnGoAway(
|
| + std::unique_ptr<SpdyGoAwayIR> frame_ir) {
|
| + CollectedFrame cf;
|
| + cf.frame_ir = std::move(frame_ir);
|
| + collected_frames_->push_back(std::move(cf));
|
| +}
|
| +
|
| +// SpdyHeadersIR and SpdyPushPromiseIR each has a SpdyHeaderBlock which
|
| +// significantly modifies the headers, so the actual header entries (name
|
| +// and value strings) are provided in a vector.
|
| +void DeframerCallbackCollector::OnHeaders(
|
| + std::unique_ptr<SpdyHeadersIR> frame_ir,
|
| + std::unique_ptr<StringPairVector> headers) {
|
| + CollectedFrame cf;
|
| + cf.frame_ir = std::move(frame_ir);
|
| + cf.headers = std::move(headers);
|
| + collected_frames_->push_back(std::move(cf));
|
| +}
|
| +
|
| +void DeframerCallbackCollector::OnPing(std::unique_ptr<SpdyPingIR> frame_ir) {
|
| + EXPECT_TRUE(frame_ir && !frame_ir->is_ack());
|
| + CollectedFrame cf;
|
| + cf.frame_ir = std::move(frame_ir);
|
| + collected_frames_->push_back(std::move(cf));
|
| +}
|
| +
|
| +void DeframerCallbackCollector::OnPingAck(
|
| + std::unique_ptr<SpdyPingIR> frame_ir) {
|
| + EXPECT_TRUE(frame_ir && frame_ir->is_ack());
|
| + CollectedFrame cf;
|
| + cf.frame_ir = std::move(frame_ir);
|
| + collected_frames_->push_back(std::move(cf));
|
| +}
|
| +
|
| +void DeframerCallbackCollector::OnPriority(
|
| + std::unique_ptr<SpdyPriorityIR> frame_ir) {
|
| + CollectedFrame cf;
|
| + cf.frame_ir = std::move(frame_ir);
|
| + collected_frames_->push_back(std::move(cf));
|
| +}
|
| +
|
| +// SpdyHeadersIR and SpdyPushPromiseIR each has a SpdyHeaderBlock which
|
| +// significantly modifies the headers, so the actual header entries (name
|
| +// and value strings) are provided in a vector.
|
| +void DeframerCallbackCollector::OnPushPromise(
|
| + std::unique_ptr<SpdyPushPromiseIR> frame_ir,
|
| + std::unique_ptr<StringPairVector> headers) {
|
| + CollectedFrame cf;
|
| + cf.frame_ir = std::move(frame_ir);
|
| + cf.headers = std::move(headers);
|
| + collected_frames_->push_back(std::move(cf));
|
| +}
|
| +
|
| +void DeframerCallbackCollector::OnRstStream(
|
| + std::unique_ptr<SpdyRstStreamIR> frame_ir) {
|
| + CollectedFrame cf;
|
| + cf.frame_ir = std::move(frame_ir);
|
| + collected_frames_->push_back(std::move(cf));
|
| +}
|
| +
|
| +// SpdySettingsIR has a map for settings, so loses info about the order of
|
| +// settings, and whether the same setting appeared more than once, so the
|
| +// the actual settings (parameter and value) are provided in a vector.
|
| +void DeframerCallbackCollector::OnSettings(
|
| + std::unique_ptr<SpdySettingsIR> frame_ir,
|
| + std::unique_ptr<SettingVector> settings) {
|
| + EXPECT_TRUE(frame_ir && !frame_ir->is_ack());
|
| + CollectedFrame cf;
|
| + cf.frame_ir = std::move(frame_ir);
|
| + cf.settings = std::move(settings);
|
| + collected_frames_->push_back(std::move(cf));
|
| +}
|
| +
|
| +// A settings frame_ir with an ACK has no content, but for uniformity passing
|
| +// a frame_ir with the ACK flag set.
|
| +void DeframerCallbackCollector::OnSettingsAck(
|
| + std::unique_ptr<SpdySettingsIR> frame_ir) {
|
| + EXPECT_TRUE(frame_ir && frame_ir->is_ack());
|
| + CollectedFrame cf;
|
| + cf.frame_ir = std::move(frame_ir);
|
| + collected_frames_->push_back(std::move(cf));
|
| +}
|
| +
|
| +void DeframerCallbackCollector::OnWindowUpdate(
|
| + std::unique_ptr<SpdyWindowUpdateIR> frame_ir) {
|
| + CollectedFrame cf;
|
| + cf.frame_ir = std::move(frame_ir);
|
| + collected_frames_->push_back(std::move(cf));
|
| +}
|
| +
|
| +// The SpdyFramer will not process any more data at this point.
|
| +void DeframerCallbackCollector::OnError(SpdyFramer* framer,
|
| + SpdyTestDeframer* deframer) {
|
| + CollectedFrame cf;
|
| + cf.error_reported = true;
|
| + collected_frames_->push_back(std::move(cf));
|
| +}
|
| +
|
| +} // namespace test
|
| +} // namespace net
|
|
|