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 |