|  | // 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/third_party/quiche/src/spdy/core/spdy_deframer_visitor.h" | 
|  |  | 
|  | #include <stdlib.h> | 
|  |  | 
|  | #include <algorithm> | 
|  | #include <cstdint> | 
|  | #include <limits> | 
|  | #include <memory> | 
|  |  | 
|  | #include "net/third_party/quiche/src/http2/platform/api/http2_macros.h" | 
|  | #include "net/third_party/quiche/src/spdy/core/hpack/hpack_constants.h" | 
|  | #include "net/third_party/quiche/src/spdy/core/mock_spdy_framer_visitor.h" | 
|  | #include "net/third_party/quiche/src/spdy/core/spdy_frame_reader.h" | 
|  | #include "net/third_party/quiche/src/spdy/core/spdy_protocol.h" | 
|  | #include "net/third_party/quiche/src/spdy/core/spdy_test_utils.h" | 
|  | #include "net/third_party/quiche/src/spdy/platform/api/spdy_flags.h" | 
|  | #include "net/third_party/quiche/src/spdy/platform/api/spdy_logging.h" | 
|  | #include "net/third_party/quiche/src/spdy/platform/api/spdy_ptr_util.h" | 
|  | #include "net/third_party/quiche/src/spdy/platform/api/spdy_string_piece.h" | 
|  |  | 
|  | using ::testing::AssertionFailure; | 
|  | using ::testing::AssertionResult; | 
|  | using ::testing::AssertionSuccess; | 
|  |  | 
|  | namespace spdy { | 
|  | 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 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 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 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 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 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_ != nullptr); | 
|  | } | 
|  | SpdyTestDeframerImpl(const SpdyTestDeframerImpl&) = delete; | 
|  | SpdyTestDeframerImpl& operator=(const SpdyTestDeframerImpl&) = delete; | 
|  | ~SpdyTestDeframerImpl() override = default; | 
|  |  | 
|  | 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, | 
|  | SpdyStringPiece origin, | 
|  | const SpdyAltSvcWireFormat::AlternativeServiceVector& | 
|  | altsvc_vector) override; | 
|  | void OnContinuation(SpdyStreamId stream_id, bool end) override; | 
|  | SpdyHeadersHandlerInterface* OnHeaderFrameStart( | 
|  | SpdyStreamId stream_id) override; | 
|  | void OnHeaderFrameEnd(SpdyStreamId stream_id) override; | 
|  | void OnDataFrameHeader(SpdyStreamId stream_id, | 
|  | size_t length, | 
|  | bool fin) override; | 
|  | void OnError(http2::Http2DecoderAdapter::SpdyFramerError error) override; | 
|  | void OnGoAway(SpdyStreamId last_accepted_stream_id, | 
|  | SpdyErrorCode error_code) 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, SpdyErrorCode error_code) override; | 
|  | void OnSetting(SpdySettingsId id, uint32_t value) override; | 
|  | void OnSettings() 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 OnStreamPadLength(SpdyStreamId stream_id, size_t value) override; | 
|  | void OnStreamPadding(SpdyStreamId stream_id, size_t len) override; | 
|  | bool OnUnknownFrame(SpdyStreamId stream_id, uint8_t frame_type) override; | 
|  | void OnWindowUpdate(SpdyStreamId stream_id, int delta_window_size) override; | 
|  |  | 
|  | // Callbacks defined in SpdyHeadersHandlerInterface. | 
|  |  | 
|  | void OnHeaderBlockStart() override; | 
|  | void OnHeader(SpdyStringPiece key, SpdyStringPiece value) override; | 
|  | void OnHeaderBlockEnd(size_t header_bytes_parsed, | 
|  | size_t compressed_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<std::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<std::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_; | 
|  | }; | 
|  |  | 
|  | // static | 
|  | std::unique_ptr<SpdyTestDeframer> SpdyTestDeframer::CreateConverter( | 
|  | std::unique_ptr<SpdyDeframerVisitorInterface> listener) { | 
|  | return SpdyMakeUnique<SpdyTestDeframerImpl>(std::move(listener)); | 
|  | } | 
|  |  | 
|  | void SpdyTestDeframerImpl::AtDataEnd() { | 
|  | SPDY_DVLOG(1) << "AtDataEnd"; | 
|  | CHECK_EQ(data_len_, padding_len_ + data_->size()); | 
|  | auto ptr = SpdyMakeUnique<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() { | 
|  | SPDY_DVLOG(1) << "AtDataEnd"; | 
|  | CHECK_EQ(frame_type_, GOAWAY); | 
|  | if (HTTP2_DIE_IF_NULL(goaway_description_)->empty()) { | 
|  | listener_->OnGoAway(std::move(goaway_ir_)); | 
|  | } else { | 
|  | listener_->OnGoAway(SpdyMakeUnique<SpdyGoAwayIR>( | 
|  | goaway_ir_->last_good_stream_id(), goaway_ir_->error_code(), | 
|  | std::move(*goaway_description_))); | 
|  | CHECK_EQ(0u, goaway_description_->size()); | 
|  | } | 
|  | goaway_description_.reset(); | 
|  | goaway_ir_.reset(); | 
|  | frame_type_ = UNSET; | 
|  | } | 
|  |  | 
|  | void SpdyTestDeframerImpl::AtHeadersEnd() { | 
|  | SPDY_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_ != nullptr); | 
|  | CHECK(headers_ != nullptr); | 
|  | CHECK(headers_handler_ != nullptr); | 
|  |  | 
|  | 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() { | 
|  | SPDY_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_ != nullptr); | 
|  | CHECK(headers_ != nullptr); | 
|  | CHECK(headers_handler_ != nullptr); | 
|  |  | 
|  | 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 { | 
|  | SPDY_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, | 
|  | SpdyStringPiece origin, | 
|  | const SpdyAltSvcWireFormat::AlternativeServiceVector& altsvc_vector) { | 
|  | SPDY_DVLOG(1) << "OnAltSvc stream_id: " << stream_id; | 
|  | CHECK_EQ(frame_type_, UNSET) | 
|  | << "   frame_type_=" << Http2FrameTypeToString(frame_type_); | 
|  | CHECK_GT(stream_id, 0u); | 
|  | auto ptr = SpdyMakeUnique<SpdyAltSvcIR>(stream_id); | 
|  | ptr->set_origin(std::string(origin)); | 
|  | for (auto& altsvc : altsvc_vector) { | 
|  | ptr->add_altsvc(altsvc); | 
|  | } | 
|  | listener_->OnAltSvc(std::move(ptr)); | 
|  | } | 
|  |  | 
|  | // 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) { | 
|  | SPDY_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; | 
|  | } | 
|  |  | 
|  | // 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) { | 
|  | SPDY_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_ = SpdyMakeUnique<std::string>(); | 
|  | } | 
|  |  | 
|  | // The SpdyFramer will not process any more data at this point. | 
|  | void SpdyTestDeframerImpl::OnError( | 
|  | http2::Http2DecoderAdapter::SpdyFramerError error) { | 
|  | SPDY_DVLOG(1) << "SpdyFramer detected an error in the stream: " | 
|  | << http2::Http2DecoderAdapter::SpdyFramerErrorToString(error) | 
|  | << "     frame_type_: " << Http2FrameTypeToString(frame_type_); | 
|  | listener_->OnError(error, 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, | 
|  | SpdyErrorCode error_code) { | 
|  | SPDY_DVLOG(1) << "OnGoAway last_good_stream_id: " << last_good_stream_id | 
|  | << "     error code: " << error_code; | 
|  | CHECK_EQ(frame_type_, UNSET) | 
|  | << "   frame_type_=" << Http2FrameTypeToString(frame_type_); | 
|  | frame_type_ = GOAWAY; | 
|  | goaway_ir_ = | 
|  | SpdyMakeUnique<SpdyGoAwayIR>(last_good_stream_id, error_code, ""); | 
|  | goaway_description_ = SpdyMakeUnique<std::string>(); | 
|  | } | 
|  |  | 
|  | // If len==0 then we've reached the end of the GOAWAY frame. | 
|  | bool SpdyTestDeframerImpl::OnGoAwayFrameData(const char* goaway_data, | 
|  | size_t len) { | 
|  | SPDY_DVLOG(1) << "OnGoAwayFrameData"; | 
|  | CHECK_EQ(frame_type_, GOAWAY) | 
|  | << "   frame_type_=" << Http2FrameTypeToString(frame_type_); | 
|  | CHECK(goaway_description_ != nullptr); | 
|  | goaway_description_->append(goaway_data, len); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | SpdyHeadersHandlerInterface* SpdyTestDeframerImpl::OnHeaderFrameStart( | 
|  | SpdyStreamId /*stream_id*/) { | 
|  | return this; | 
|  | } | 
|  |  | 
|  | void SpdyTestDeframerImpl::OnHeaderFrameEnd(SpdyStreamId stream_id) { | 
|  | SPDY_DVLOG(1) << "OnHeaderFrameEnd stream_id: " << stream_id; | 
|  | } | 
|  |  | 
|  | // 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) { | 
|  | SPDY_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_ = SpdyMakeUnique<StringPairVector>(); | 
|  | headers_handler_ = SpdyMakeUnique<TestHeadersHandler>(); | 
|  | headers_ir_ = SpdyMakeUnique<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) { | 
|  | SPDY_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 = SpdyMakeUnique<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) { | 
|  | SPDY_DVLOG(1) << "OnPriority stream_id: " << stream_id; | 
|  | CHECK_EQ(frame_type_, UNSET) | 
|  | << "   frame_type_=" << Http2FrameTypeToString(frame_type_); | 
|  | CHECK_GT(stream_id, 0u); | 
|  |  | 
|  | listener_->OnPriority(SpdyMakeUnique<SpdyPriorityIR>( | 
|  | stream_id, parent_stream_id, weight, exclusive)); | 
|  | } | 
|  |  | 
|  | void SpdyTestDeframerImpl::OnPushPromise(SpdyStreamId stream_id, | 
|  | SpdyStreamId promised_stream_id, | 
|  | bool end) { | 
|  | SPDY_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_ = SpdyMakeUnique<StringPairVector>(); | 
|  | headers_handler_ = SpdyMakeUnique<TestHeadersHandler>(); | 
|  | push_promise_ir_ = | 
|  | SpdyMakeUnique<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, | 
|  | SpdyErrorCode error_code) { | 
|  | SPDY_DVLOG(1) << "OnRstStream stream_id: " << stream_id | 
|  | << "     error code: " << error_code; | 
|  | CHECK_EQ(frame_type_, UNSET) | 
|  | << "   frame_type_=" << Http2FrameTypeToString(frame_type_); | 
|  | CHECK_GT(stream_id, 0u); | 
|  |  | 
|  | listener_->OnRstStream( | 
|  | SpdyMakeUnique<SpdyRstStreamIR>(stream_id, error_code)); | 
|  | } | 
|  |  | 
|  | // Called for an individual setting. There is no negotiation; the sender is | 
|  | // stating the value that the sender is using. | 
|  | void SpdyTestDeframerImpl::OnSetting(SpdySettingsId id, uint32_t value) { | 
|  | SPDY_DVLOG(1) << "OnSetting id: " << id << std::hex << "    value: " << value; | 
|  | CHECK_EQ(frame_type_, SETTINGS) | 
|  | << "   frame_type_=" << Http2FrameTypeToString(frame_type_); | 
|  | CHECK(settings_ != nullptr); | 
|  | SpdyKnownSettingsId known_id; | 
|  | if (ParseSettingsId(id, &known_id)) { | 
|  | settings_->push_back(std::make_pair(known_id, value)); | 
|  | settings_ir_->AddSetting(known_id, 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. | 
|  | void SpdyTestDeframerImpl::OnSettings() { | 
|  | SPDY_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_ = SpdyMakeUnique<SettingVector>(); | 
|  | settings_ir_ = SpdyMakeUnique<SpdySettingsIR>(); | 
|  | } | 
|  |  | 
|  | void SpdyTestDeframerImpl::OnSettingsAck() { | 
|  | SPDY_DVLOG(1) << "OnSettingsAck"; | 
|  | CHECK_EQ(frame_type_, UNSET) | 
|  | << "   frame_type_=" << Http2FrameTypeToString(frame_type_); | 
|  | auto ptr = SpdyMakeUnique<SpdySettingsIR>(); | 
|  | ptr->set_is_ack(true); | 
|  | listener_->OnSettingsAck(std::move(ptr)); | 
|  | } | 
|  |  | 
|  | void SpdyTestDeframerImpl::OnSettingsEnd() { | 
|  | SPDY_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) { | 
|  | SPDY_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) { | 
|  | SPDY_DVLOG(1) << "OnStreamFrameData stream_id: " << stream_id | 
|  | << "    len: " << len; | 
|  | CHECK_EQ(stream_id_, stream_id); | 
|  | CHECK_EQ(frame_type_, DATA); | 
|  | data_->append(data, len); | 
|  | } | 
|  |  | 
|  | // Called when receiving the padding length field at the start of the DATA frame | 
|  | // payload. value will be in the range 0 to 255. | 
|  | void SpdyTestDeframerImpl::OnStreamPadLength(SpdyStreamId stream_id, | 
|  | size_t value) { | 
|  | SPDY_DVLOG(1) << "OnStreamPadding stream_id: " << stream_id | 
|  | << "    value: " << value; | 
|  | CHECK(frame_type_ == DATA || frame_type_ == HEADERS || | 
|  | frame_type_ == PUSH_PROMISE) | 
|  | << "   frame_type_=" << Http2FrameTypeToString(frame_type_); | 
|  | CHECK_EQ(stream_id_, stream_id); | 
|  | CHECK_GE(255u, value); | 
|  | // Count the padding length byte against total padding. | 
|  | padding_len_ += 1; | 
|  | CHECK_EQ(1u, padding_len_); | 
|  | } | 
|  |  | 
|  | // Called when padding is skipped over at the end of the DATA frame. len will | 
|  | // be in the range 1 to 255. | 
|  | void SpdyTestDeframerImpl::OnStreamPadding(SpdyStreamId stream_id, size_t len) { | 
|  | SPDY_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; | 
|  | } | 
|  |  | 
|  | // 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) { | 
|  | SPDY_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( | 
|  | SpdyMakeUnique<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, | 
|  | uint8_t /*frame_type*/) { | 
|  | SPDY_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_ != nullptr); | 
|  | CHECK_EQ(0u, headers_->size()); | 
|  | got_hpack_end_ = false; | 
|  | } | 
|  |  | 
|  | void SpdyTestDeframerImpl::OnHeader(SpdyStringPiece key, | 
|  | SpdyStringPiece value) { | 
|  | CHECK(frame_type_ == HEADERS || frame_type_ == CONTINUATION || | 
|  | frame_type_ == PUSH_PROMISE) | 
|  | << "   frame_type_=" << Http2FrameTypeToString(frame_type_); | 
|  | CHECK(!got_hpack_end_); | 
|  | HTTP2_DIE_IF_NULL(headers_)->emplace_back(std::string(key), | 
|  | std::string(value)); | 
|  | HTTP2_DIE_IF_NULL(headers_handler_)->OnHeader(key, value); | 
|  | } | 
|  |  | 
|  | void SpdyTestDeframerImpl::OnHeaderBlockEnd( | 
|  | size_t /* header_bytes_parsed */, | 
|  | size_t /* compressed_header_bytes_parsed */) { | 
|  | CHECK(headers_ != nullptr); | 
|  | 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_ = SpdyMakeUnique<SpdyDeframerVisitorInterface>(); | 
|  | } | 
|  | } | 
|  | ~LoggingSpdyDeframerDelegate() override = default; | 
|  |  | 
|  | void OnAltSvc(std::unique_ptr<SpdyAltSvcIR> frame) override { | 
|  | SPDY_DVLOG(1) << "LoggingSpdyDeframerDelegate::OnAltSvc"; | 
|  | wrapped_->OnAltSvc(std::move(frame)); | 
|  | } | 
|  | void OnData(std::unique_ptr<SpdyDataIR> frame) override { | 
|  | SPDY_DVLOG(1) << "LoggingSpdyDeframerDelegate::OnData"; | 
|  | wrapped_->OnData(std::move(frame)); | 
|  | } | 
|  | void OnGoAway(std::unique_ptr<SpdyGoAwayIR> frame) override { | 
|  | SPDY_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 { | 
|  | SPDY_DVLOG(1) << "LoggingSpdyDeframerDelegate::OnHeaders"; | 
|  | wrapped_->OnHeaders(std::move(frame), std::move(headers)); | 
|  | } | 
|  |  | 
|  | void OnPing(std::unique_ptr<SpdyPingIR> frame) override { | 
|  | SPDY_DVLOG(1) << "LoggingSpdyDeframerDelegate::OnPing"; | 
|  | wrapped_->OnPing(std::move(frame)); | 
|  | } | 
|  | void OnPingAck(std::unique_ptr<SpdyPingIR> frame) override { | 
|  | SPDY_DVLOG(1) << "LoggingSpdyDeframerDelegate::OnPingAck"; | 
|  | wrapped_->OnPingAck(std::move(frame)); | 
|  | } | 
|  |  | 
|  | void OnPriority(std::unique_ptr<SpdyPriorityIR> frame) override { | 
|  | SPDY_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 { | 
|  | SPDY_DVLOG(1) << "LoggingSpdyDeframerDelegate::OnPushPromise"; | 
|  | wrapped_->OnPushPromise(std::move(frame), std::move(headers)); | 
|  | } | 
|  |  | 
|  | void OnRstStream(std::unique_ptr<SpdyRstStreamIR> frame) override { | 
|  | SPDY_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 { | 
|  | SPDY_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 { | 
|  | SPDY_DVLOG(1) << "LoggingSpdyDeframerDelegate::OnSettingsAck"; | 
|  | wrapped_->OnSettingsAck(std::move(frame)); | 
|  | } | 
|  |  | 
|  | void OnWindowUpdate(std::unique_ptr<SpdyWindowUpdateIR> frame) override { | 
|  | SPDY_DVLOG(1) << "LoggingSpdyDeframerDelegate::OnWindowUpdate"; | 
|  | wrapped_->OnWindowUpdate(std::move(frame)); | 
|  | } | 
|  |  | 
|  | // The SpdyFramer will not process any more data at this point. | 
|  | void OnError(http2::Http2DecoderAdapter::SpdyFramerError error, | 
|  | SpdyTestDeframer* deframer) override { | 
|  | SPDY_DVLOG(1) << "LoggingSpdyDeframerDelegate::OnError"; | 
|  | wrapped_->OnError(error, deframer); | 
|  | } | 
|  |  | 
|  | private: | 
|  | std::unique_ptr<SpdyDeframerVisitorInterface> wrapped_; | 
|  | }; | 
|  |  | 
|  | // static | 
|  | std::unique_ptr<SpdyDeframerVisitorInterface> | 
|  | SpdyDeframerVisitorInterface::LogBeforeVisiting( | 
|  | std::unique_ptr<SpdyDeframerVisitorInterface> wrapped_listener) { | 
|  | return SpdyMakeUnique<LoggingSpdyDeframerDelegate>( | 
|  | std::move(wrapped_listener)); | 
|  | } | 
|  |  | 
|  | CollectedFrame::CollectedFrame() = default; | 
|  |  | 
|  | 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() = default; | 
|  |  | 
|  | 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 { | 
|  | VERIFY_NE(headers.get(), nullptr); | 
|  | VERIFY_THAT(*headers, ::testing::ContainerEq(expected_headers)); | 
|  | return AssertionSuccess(); | 
|  | } | 
|  |  | 
|  | AssertionResult CollectedFrame::VerifyHasSettings( | 
|  | const SettingVector& expected_settings) const { | 
|  | VERIFY_NE(settings.get(), nullptr); | 
|  | VERIFY_THAT(*settings, testing::ContainerEq(expected_settings)); | 
|  | return AssertionSuccess(); | 
|  | } | 
|  |  | 
|  | DeframerCallbackCollector::DeframerCallbackCollector( | 
|  | std::vector<CollectedFrame>* collected_frames) | 
|  | : collected_frames_(HTTP2_DIE_IF_NULL(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( | 
|  | http2::Http2DecoderAdapter::SpdyFramerError /*error*/, | 
|  | SpdyTestDeframer* /*deframer*/) { | 
|  | CollectedFrame cf; | 
|  | cf.error_reported = true; | 
|  | collected_frames_->push_back(std::move(cf)); | 
|  | } | 
|  |  | 
|  | }  // namespace test | 
|  | }  // namespace spdy |