Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(637)

Unified Diff: net/spdy/spdy_deframer_visitor.cc

Issue 2248343004: Add SpdyDeframerVisitor. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fix MSVC compile error. Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « net/spdy/spdy_deframer_visitor.h ('k') | net/spdy/spdy_deframer_visitor_test.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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
« no previous file with comments | « net/spdy/spdy_deframer_visitor.h ('k') | net/spdy/spdy_deframer_visitor_test.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698