Project import generated by Copybara. PiperOrigin-RevId: 224614037 Change-Id: I14e53449d4aeccb328f86828c76b5f09dea0d4b8
diff --git a/http2/test_tools/frame_parts.cc b/http2/test_tools/frame_parts.cc new file mode 100644 index 0000000..3d0453e --- /dev/null +++ b/http2/test_tools/frame_parts.cc
@@ -0,0 +1,526 @@ +// 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/http2/test_tools/frame_parts.h" + +#include <type_traits> + +#include "base/logging.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "net/third_party/quiche/src/http2/http2_structures_test_util.h" +#include "net/third_party/quiche/src/http2/platform/api/http2_string_utils.h" +#include "net/third_party/quiche/src/http2/platform/api/http2_test_helpers.h" + +using ::testing::AssertionFailure; +using ::testing::AssertionResult; +using ::testing::AssertionSuccess; +using ::testing::ContainerEq; + +namespace http2 { +namespace test { +namespace { + +static_assert(std::is_base_of<Http2FrameDecoderListener, FrameParts>::value && + !std::is_abstract<FrameParts>::value, + "FrameParts needs to implement all of the methods of " + "Http2FrameDecoderListener"); + +// Compare two optional variables of the same type. +// TODO(jamessynge): Maybe create a ::testing::Matcher for this. +template <class T> +AssertionResult VerifyOptionalEq(const T& opt_a, const T& opt_b) { + if (opt_a) { + if (opt_b) { + VERIFY_EQ(opt_a.value(), opt_b.value()); + } else { + return AssertionFailure() + << "opt_b is not set; opt_a.value()=" << opt_a.value(); + } + } else if (opt_b) { + return AssertionFailure() + << "opt_a is not set; opt_b.value()=" << opt_b.value(); + } + return AssertionSuccess(); +} + +} // namespace + +FrameParts::FrameParts(const Http2FrameHeader& header) : frame_header_(header) { + VLOG(1) << "FrameParts, header: " << frame_header_; +} + +FrameParts::FrameParts(const Http2FrameHeader& header, Http2StringPiece payload) + : FrameParts(header) { + VLOG(1) << "FrameParts with payload.size() = " << payload.size(); + this->payload_.append(payload.data(), payload.size()); + opt_payload_length_ = payload.size(); +} +FrameParts::FrameParts(const Http2FrameHeader& header, + Http2StringPiece payload, + size_t total_pad_length) + : FrameParts(header, payload) { + VLOG(1) << "FrameParts with total_pad_length=" << total_pad_length; + SetTotalPadLength(total_pad_length); +} + +FrameParts::FrameParts(const FrameParts& header) = default; + +FrameParts::~FrameParts() = default; + +AssertionResult FrameParts::VerifyEquals(const FrameParts& that) const { +#define COMMON_MESSAGE "\n this: " << *this << "\n that: " << that + + VERIFY_EQ(frame_header_, that.frame_header_) << COMMON_MESSAGE; + VERIFY_EQ(payload_, that.payload_) << COMMON_MESSAGE; + VERIFY_EQ(padding_, that.padding_) << COMMON_MESSAGE; + VERIFY_EQ(altsvc_origin_, that.altsvc_origin_) << COMMON_MESSAGE; + VERIFY_EQ(altsvc_value_, that.altsvc_value_) << COMMON_MESSAGE; + VERIFY_THAT(settings_, ContainerEq(that.settings_)) << COMMON_MESSAGE; + +#define VERIFY_OPTIONAL_FIELD(field_name) \ + VERIFY_SUCCESS(VerifyOptionalEq(field_name, that.field_name)) + + VERIFY_OPTIONAL_FIELD(opt_altsvc_origin_length_) << COMMON_MESSAGE; + VERIFY_OPTIONAL_FIELD(opt_altsvc_value_length_) << COMMON_MESSAGE; + VERIFY_OPTIONAL_FIELD(opt_goaway_) << COMMON_MESSAGE; + VERIFY_OPTIONAL_FIELD(opt_missing_length_) << COMMON_MESSAGE; + VERIFY_OPTIONAL_FIELD(opt_pad_length_) << COMMON_MESSAGE; + VERIFY_OPTIONAL_FIELD(opt_ping_) << COMMON_MESSAGE; + VERIFY_OPTIONAL_FIELD(opt_priority_) << COMMON_MESSAGE; + VERIFY_OPTIONAL_FIELD(opt_push_promise_) << COMMON_MESSAGE; + VERIFY_OPTIONAL_FIELD(opt_rst_stream_error_code_) << COMMON_MESSAGE; + VERIFY_OPTIONAL_FIELD(opt_window_update_increment_) << COMMON_MESSAGE; + +#undef VERIFY_OPTIONAL_FIELD + + return AssertionSuccess(); +} + +void FrameParts::SetTotalPadLength(size_t total_pad_length) { + opt_pad_length_.reset(); + padding_.clear(); + if (total_pad_length > 0) { + ASSERT_LE(total_pad_length, 256u); + ASSERT_TRUE(frame_header_.IsPadded()); + opt_pad_length_ = total_pad_length - 1; + char zero = 0; + padding_.append(opt_pad_length_.value(), zero); + } + + if (opt_pad_length_) { + VLOG(1) << "SetTotalPadLength: pad_length=" << opt_pad_length_.value(); + } else { + VLOG(1) << "SetTotalPadLength: has no pad length"; + } +} + +void FrameParts::SetAltSvcExpected(Http2StringPiece origin, + Http2StringPiece value) { + altsvc_origin_.append(origin.data(), origin.size()); + altsvc_value_.append(value.data(), value.size()); + opt_altsvc_origin_length_ = origin.size(); + opt_altsvc_value_length_ = value.size(); +} + +bool FrameParts::OnFrameHeader(const Http2FrameHeader& header) { + ADD_FAILURE() << "OnFrameHeader: " << *this; + return true; +} + +void FrameParts::OnDataStart(const Http2FrameHeader& header) { + VLOG(1) << "OnDataStart: " << header; + ASSERT_TRUE(StartFrameOfType(header, Http2FrameType::DATA)) << *this; + opt_payload_length_ = header.payload_length; +} + +void FrameParts::OnDataPayload(const char* data, size_t len) { + VLOG(1) << "OnDataPayload: len=" << len + << "; frame_header_: " << frame_header_; + ASSERT_TRUE(InFrameOfType(Http2FrameType::DATA)) << *this; + ASSERT_TRUE(AppendString(Http2StringPiece(data, len), &payload_, + &opt_payload_length_)); +} + +void FrameParts::OnDataEnd() { + VLOG(1) << "OnDataEnd; frame_header_: " << frame_header_; + ASSERT_TRUE(EndFrameOfType(Http2FrameType::DATA)) << *this; +} + +void FrameParts::OnHeadersStart(const Http2FrameHeader& header) { + VLOG(1) << "OnHeadersStart: " << header; + ASSERT_TRUE(StartFrameOfType(header, Http2FrameType::HEADERS)) << *this; + opt_payload_length_ = header.payload_length; +} + +void FrameParts::OnHeadersPriority(const Http2PriorityFields& priority) { + VLOG(1) << "OnHeadersPriority: priority: " << priority + << "; frame_header_: " << frame_header_; + ASSERT_TRUE(InFrameOfType(Http2FrameType::HEADERS)) << *this; + ASSERT_FALSE(opt_priority_); + opt_priority_ = priority; + ASSERT_TRUE(opt_payload_length_); + opt_payload_length_ = + opt_payload_length_.value() - Http2PriorityFields::EncodedSize(); +} + +void FrameParts::OnHpackFragment(const char* data, size_t len) { + VLOG(1) << "OnHpackFragment: len=" << len + << "; frame_header_: " << frame_header_; + ASSERT_TRUE(got_start_callback_); + ASSERT_FALSE(got_end_callback_); + ASSERT_TRUE(FrameCanHaveHpackPayload(frame_header_)) << *this; + ASSERT_TRUE(AppendString(Http2StringPiece(data, len), &payload_, + &opt_payload_length_)); +} + +void FrameParts::OnHeadersEnd() { + VLOG(1) << "OnHeadersEnd; frame_header_: " << frame_header_; + ASSERT_TRUE(EndFrameOfType(Http2FrameType::HEADERS)) << *this; +} + +void FrameParts::OnPriorityFrame(const Http2FrameHeader& header, + const Http2PriorityFields& priority) { + VLOG(1) << "OnPriorityFrame: " << header << "; priority: " << priority; + ASSERT_TRUE(StartFrameOfType(header, Http2FrameType::PRIORITY)) << *this; + ASSERT_FALSE(opt_priority_); + opt_priority_ = priority; + ASSERT_TRUE(EndFrameOfType(Http2FrameType::PRIORITY)) << *this; +} + +void FrameParts::OnContinuationStart(const Http2FrameHeader& header) { + VLOG(1) << "OnContinuationStart: " << header; + ASSERT_TRUE(StartFrameOfType(header, Http2FrameType::CONTINUATION)) << *this; + opt_payload_length_ = header.payload_length; +} + +void FrameParts::OnContinuationEnd() { + VLOG(1) << "OnContinuationEnd; frame_header_: " << frame_header_; + ASSERT_TRUE(EndFrameOfType(Http2FrameType::CONTINUATION)) << *this; +} + +void FrameParts::OnPadLength(size_t trailing_length) { + VLOG(1) << "OnPadLength: trailing_length=" << trailing_length; + ASSERT_TRUE(InPaddedFrame()) << *this; + ASSERT_FALSE(opt_pad_length_); + ASSERT_TRUE(opt_payload_length_); + size_t total_padding_length = trailing_length + 1; + ASSERT_GE(opt_payload_length_.value(), total_padding_length); + opt_payload_length_ = opt_payload_length_.value() - total_padding_length; + opt_pad_length_ = trailing_length; +} + +void FrameParts::OnPadding(const char* pad, size_t skipped_length) { + VLOG(1) << "OnPadding: skipped_length=" << skipped_length; + ASSERT_TRUE(InPaddedFrame()) << *this; + ASSERT_TRUE(opt_pad_length_); + ASSERT_TRUE(AppendString(Http2StringPiece(pad, skipped_length), &padding_, + &opt_pad_length_)); +} + +void FrameParts::OnRstStream(const Http2FrameHeader& header, + Http2ErrorCode error_code) { + VLOG(1) << "OnRstStream: " << header << "; code=" << error_code; + ASSERT_TRUE(StartFrameOfType(header, Http2FrameType::RST_STREAM)) << *this; + ASSERT_FALSE(opt_rst_stream_error_code_); + opt_rst_stream_error_code_ = error_code; + ASSERT_TRUE(EndFrameOfType(Http2FrameType::RST_STREAM)) << *this; +} + +void FrameParts::OnSettingsStart(const Http2FrameHeader& header) { + VLOG(1) << "OnSettingsStart: " << header; + ASSERT_TRUE(StartFrameOfType(header, Http2FrameType::SETTINGS)) << *this; + ASSERT_EQ(0u, settings_.size()); + ASSERT_FALSE(header.IsAck()) << header; +} + +void FrameParts::OnSetting(const Http2SettingFields& setting_fields) { + VLOG(1) << "OnSetting: " << setting_fields; + ASSERT_TRUE(InFrameOfType(Http2FrameType::SETTINGS)) << *this; + settings_.push_back(setting_fields); +} + +void FrameParts::OnSettingsEnd() { + VLOG(1) << "OnSettingsEnd; frame_header_: " << frame_header_; + ASSERT_TRUE(EndFrameOfType(Http2FrameType::SETTINGS)) << *this; +} + +void FrameParts::OnSettingsAck(const Http2FrameHeader& header) { + VLOG(1) << "OnSettingsAck: " << header; + ASSERT_TRUE(StartFrameOfType(header, Http2FrameType::SETTINGS)) << *this; + ASSERT_EQ(0u, settings_.size()); + ASSERT_TRUE(header.IsAck()); + ASSERT_TRUE(EndFrameOfType(Http2FrameType::SETTINGS)) << *this; +} + +void FrameParts::OnPushPromiseStart(const Http2FrameHeader& header, + const Http2PushPromiseFields& promise, + size_t total_padding_length) { + VLOG(1) << "OnPushPromiseStart header: " << header << "; promise: " << promise + << "; total_padding_length: " << total_padding_length; + ASSERT_TRUE(StartFrameOfType(header, Http2FrameType::PUSH_PROMISE)) << *this; + ASSERT_GE(header.payload_length, Http2PushPromiseFields::EncodedSize()); + opt_payload_length_ = + header.payload_length - Http2PushPromiseFields::EncodedSize(); + ASSERT_FALSE(opt_push_promise_); + opt_push_promise_ = promise; + if (total_padding_length > 0) { + ASSERT_GE(opt_payload_length_.value(), total_padding_length); + OnPadLength(total_padding_length - 1); + } else { + ASSERT_FALSE(header.IsPadded()); + } +} + +void FrameParts::OnPushPromiseEnd() { + VLOG(1) << "OnPushPromiseEnd; frame_header_: " << frame_header_; + ASSERT_TRUE(EndFrameOfType(Http2FrameType::PUSH_PROMISE)) << *this; +} + +void FrameParts::OnPing(const Http2FrameHeader& header, + const Http2PingFields& ping) { + VLOG(1) << "OnPing header: " << header << " ping: " << ping; + ASSERT_TRUE(StartFrameOfType(header, Http2FrameType::PING)) << *this; + ASSERT_FALSE(header.IsAck()); + ASSERT_FALSE(opt_ping_); + opt_ping_ = ping; + ASSERT_TRUE(EndFrameOfType(Http2FrameType::PING)) << *this; +} + +void FrameParts::OnPingAck(const Http2FrameHeader& header, + const Http2PingFields& ping) { + VLOG(1) << "OnPingAck header: " << header << " ping: " << ping; + ASSERT_TRUE(StartFrameOfType(header, Http2FrameType::PING)) << *this; + ASSERT_TRUE(header.IsAck()); + ASSERT_FALSE(opt_ping_); + opt_ping_ = ping; + ASSERT_TRUE(EndFrameOfType(Http2FrameType::PING)) << *this; +} + +void FrameParts::OnGoAwayStart(const Http2FrameHeader& header, + const Http2GoAwayFields& goaway) { + VLOG(1) << "OnGoAwayStart: " << goaway; + ASSERT_TRUE(StartFrameOfType(header, Http2FrameType::GOAWAY)) << *this; + ASSERT_FALSE(opt_goaway_); + opt_goaway_ = goaway; + opt_payload_length_ = + header.payload_length - Http2GoAwayFields::EncodedSize(); +} + +void FrameParts::OnGoAwayOpaqueData(const char* data, size_t len) { + VLOG(1) << "OnGoAwayOpaqueData: len=" << len; + ASSERT_TRUE(InFrameOfType(Http2FrameType::GOAWAY)) << *this; + ASSERT_TRUE(AppendString(Http2StringPiece(data, len), &payload_, + &opt_payload_length_)); +} + +void FrameParts::OnGoAwayEnd() { + VLOG(1) << "OnGoAwayEnd; frame_header_: " << frame_header_; + ASSERT_TRUE(EndFrameOfType(Http2FrameType::GOAWAY)) << *this; +} + +void FrameParts::OnWindowUpdate(const Http2FrameHeader& header, + uint32_t increment) { + VLOG(1) << "OnWindowUpdate header: " << header + << " increment=" << increment; + ASSERT_TRUE(StartFrameOfType(header, Http2FrameType::WINDOW_UPDATE)) << *this; + ASSERT_FALSE(opt_window_update_increment_); + opt_window_update_increment_ = increment; + ASSERT_TRUE(EndFrameOfType(Http2FrameType::WINDOW_UPDATE)) << *this; +} + +void FrameParts::OnAltSvcStart(const Http2FrameHeader& header, + size_t origin_length, + size_t value_length) { + VLOG(1) << "OnAltSvcStart: " << header + << " origin_length: " << origin_length + << " value_length: " << value_length; + ASSERT_TRUE(StartFrameOfType(header, Http2FrameType::ALTSVC)) << *this; + ASSERT_FALSE(opt_altsvc_origin_length_); + opt_altsvc_origin_length_ = origin_length; + ASSERT_FALSE(opt_altsvc_value_length_); + opt_altsvc_value_length_ = value_length; +} + +void FrameParts::OnAltSvcOriginData(const char* data, size_t len) { + VLOG(1) << "OnAltSvcOriginData: len=" << len; + ASSERT_TRUE(InFrameOfType(Http2FrameType::ALTSVC)) << *this; + ASSERT_TRUE(AppendString(Http2StringPiece(data, len), &altsvc_origin_, + &opt_altsvc_origin_length_)); +} + +void FrameParts::OnAltSvcValueData(const char* data, size_t len) { + VLOG(1) << "OnAltSvcValueData: len=" << len; + ASSERT_TRUE(InFrameOfType(Http2FrameType::ALTSVC)) << *this; + ASSERT_TRUE(AppendString(Http2StringPiece(data, len), &altsvc_value_, + &opt_altsvc_value_length_)); +} + +void FrameParts::OnAltSvcEnd() { + VLOG(1) << "OnAltSvcEnd; frame_header_: " << frame_header_; + ASSERT_TRUE(EndFrameOfType(Http2FrameType::ALTSVC)) << *this; +} + +void FrameParts::OnUnknownStart(const Http2FrameHeader& header) { + VLOG(1) << "OnUnknownStart: " << header; + ASSERT_FALSE(IsSupportedHttp2FrameType(header.type)) << header; + ASSERT_FALSE(got_start_callback_); + ASSERT_EQ(frame_header_, header); + got_start_callback_ = true; + opt_payload_length_ = header.payload_length; +} + +void FrameParts::OnUnknownPayload(const char* data, size_t len) { + VLOG(1) << "OnUnknownPayload: len=" << len; + ASSERT_FALSE(IsSupportedHttp2FrameType(frame_header_.type)) << *this; + ASSERT_TRUE(got_start_callback_); + ASSERT_FALSE(got_end_callback_); + ASSERT_TRUE(AppendString(Http2StringPiece(data, len), &payload_, + &opt_payload_length_)); +} + +void FrameParts::OnUnknownEnd() { + VLOG(1) << "OnUnknownEnd; frame_header_: " << frame_header_; + ASSERT_FALSE(IsSupportedHttp2FrameType(frame_header_.type)) << *this; + ASSERT_TRUE(got_start_callback_); + ASSERT_FALSE(got_end_callback_); + got_end_callback_ = true; +} + +void FrameParts::OnPaddingTooLong(const Http2FrameHeader& header, + size_t missing_length) { + VLOG(1) << "OnPaddingTooLong: " << header + << "; missing_length: " << missing_length; + ASSERT_EQ(frame_header_, header); + ASSERT_FALSE(got_end_callback_); + ASSERT_TRUE(FrameIsPadded(header)); + ASSERT_FALSE(opt_pad_length_); + ASSERT_FALSE(opt_missing_length_); + opt_missing_length_ = missing_length; + got_start_callback_ = true; + got_end_callback_ = true; +} + +void FrameParts::OnFrameSizeError(const Http2FrameHeader& header) { + VLOG(1) << "OnFrameSizeError: " << header; + ASSERT_EQ(frame_header_, header); + ASSERT_FALSE(got_end_callback_); + ASSERT_FALSE(has_frame_size_error_); + has_frame_size_error_ = true; + got_end_callback_ = true; +} + +void FrameParts::OutputTo(std::ostream& out) const { + out << "FrameParts{\n frame_header_: " << frame_header_ << "\n"; + if (!payload_.empty()) { + out << " payload_=\"" << Http2HexEscape(payload_) << "\"\n"; + } + if (!padding_.empty()) { + out << " padding_=\"" << Http2HexEscape(padding_) << "\"\n"; + } + if (!altsvc_origin_.empty()) { + out << " altsvc_origin_=\"" << Http2HexEscape(altsvc_origin_) << "\"\n"; + } + if (!altsvc_value_.empty()) { + out << " altsvc_value_=\"" << Http2HexEscape(altsvc_value_) << "\"\n"; + } + if (opt_priority_) { + out << " priority=" << opt_priority_.value() << "\n"; + } + if (opt_rst_stream_error_code_) { + out << " rst_stream=" << opt_rst_stream_error_code_.value() << "\n"; + } + if (opt_push_promise_) { + out << " push_promise=" << opt_push_promise_.value() << "\n"; + } + if (opt_ping_) { + out << " ping=" << opt_ping_.value() << "\n"; + } + if (opt_goaway_) { + out << " goaway=" << opt_goaway_.value() << "\n"; + } + if (opt_window_update_increment_) { + out << " window_update=" << opt_window_update_increment_.value() << "\n"; + } + if (opt_payload_length_) { + out << " payload_length=" << opt_payload_length_.value() << "\n"; + } + if (opt_pad_length_) { + out << " pad_length=" << opt_pad_length_.value() << "\n"; + } + if (opt_missing_length_) { + out << " missing_length=" << opt_missing_length_.value() << "\n"; + } + if (opt_altsvc_origin_length_) { + out << " origin_length=" << opt_altsvc_origin_length_.value() << "\n"; + } + if (opt_altsvc_value_length_) { + out << " value_length=" << opt_altsvc_value_length_.value() << "\n"; + } + if (has_frame_size_error_) { + out << " has_frame_size_error\n"; + } + if (got_start_callback_) { + out << " got_start_callback\n"; + } + if (got_end_callback_) { + out << " got_end_callback\n"; + } + for (size_t ndx = 0; ndx < settings_.size(); ++ndx) { + out << " setting[" << ndx << "]=" << settings_[ndx]; + } + out << "}"; +} + +AssertionResult FrameParts::StartFrameOfType( + const Http2FrameHeader& header, + Http2FrameType expected_frame_type) { + VERIFY_EQ(header.type, expected_frame_type); + VERIFY_FALSE(got_start_callback_); + VERIFY_FALSE(got_end_callback_); + VERIFY_EQ(frame_header_, header); + got_start_callback_ = true; + return AssertionSuccess(); +} + +AssertionResult FrameParts::InFrameOfType(Http2FrameType expected_frame_type) { + VERIFY_TRUE(got_start_callback_); + VERIFY_FALSE(got_end_callback_); + VERIFY_EQ(frame_header_.type, expected_frame_type); + return AssertionSuccess(); +} + +AssertionResult FrameParts::EndFrameOfType(Http2FrameType expected_frame_type) { + VERIFY_SUCCESS(InFrameOfType(expected_frame_type)); + got_end_callback_ = true; + return AssertionSuccess(); +} + +AssertionResult FrameParts::InPaddedFrame() { + VERIFY_TRUE(got_start_callback_); + VERIFY_FALSE(got_end_callback_); + VERIFY_TRUE(FrameIsPadded(frame_header_)); + return AssertionSuccess(); +} + +AssertionResult FrameParts::AppendString(Http2StringPiece source, + Http2String* target, + Http2Optional<size_t>* opt_length) { + target->append(source.data(), source.size()); + if (opt_length != nullptr) { + VERIFY_TRUE(*opt_length) << "Length is not set yet\n" << *this; + VERIFY_LE(target->size(), opt_length->value()) + << "String too large; source.size() = " << source.size() << "\n" + << *this; + } + return ::testing::AssertionSuccess(); +} + +std::ostream& operator<<(std::ostream& out, const FrameParts& v) { + v.OutputTo(out); + return out; +} + +} // namespace test +} // namespace http2
diff --git a/http2/test_tools/frame_parts.h b/http2/test_tools/frame_parts.h new file mode 100644 index 0000000..b531174 --- /dev/null +++ b/http2/test_tools/frame_parts.h
@@ -0,0 +1,249 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef QUICHE_HTTP2_TEST_TOOLS_FRAME_PARTS_H_ +#define QUICHE_HTTP2_TEST_TOOLS_FRAME_PARTS_H_ + +// FrameParts implements Http2FrameDecoderListener, recording the callbacks +// during the decoding of a single frame. It is also used for comparing the +// info that a test expects to be recorded during the decoding of a frame +// with the actual recorded value (i.e. by providing a comparator). + +#include <stddef.h> + +#include <cstdint> +#include <vector> + +#include "base/logging.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "net/third_party/quiche/src/http2/decoder/http2_frame_decoder_listener.h" +#include "net/third_party/quiche/src/http2/http2_constants.h" +#include "net/third_party/quiche/src/http2/http2_structures.h" +#include "net/third_party/quiche/src/http2/platform/api/http2_optional.h" +#include "net/third_party/quiche/src/http2/platform/api/http2_string.h" +#include "net/third_party/quiche/src/http2/platform/api/http2_string_piece.h" + +namespace http2 { +namespace test { + +class FrameParts : public Http2FrameDecoderListener { + public: + // The first callback for every type of frame includes the frame header; this + // is the only constructor used during decoding of a frame. + explicit FrameParts(const Http2FrameHeader& header); + + // For use in tests where the expected frame has a variable size payload. + FrameParts(const Http2FrameHeader& header, Http2StringPiece payload); + + // For use in tests where the expected frame has a variable size payload + // and may be padded. + FrameParts(const Http2FrameHeader& header, + Http2StringPiece payload, + size_t total_pad_length); + + // Copy constructor. + FrameParts(const FrameParts& header); + + ~FrameParts() override; + + // Returns AssertionSuccess() if they're equal, else AssertionFailure() + // with info about the difference. + ::testing::AssertionResult VerifyEquals(const FrameParts& other) const; + + // Format this FrameParts object. + void OutputTo(std::ostream& out) const; + + // Set the total padding length (0 to 256). + void SetTotalPadLength(size_t total_pad_length); + + // Set the origin and value expected in an ALTSVC frame. + void SetAltSvcExpected(Http2StringPiece origin, Http2StringPiece value); + + // Http2FrameDecoderListener methods: + bool OnFrameHeader(const Http2FrameHeader& header) override; + void OnDataStart(const Http2FrameHeader& header) override; + void OnDataPayload(const char* data, size_t len) override; + void OnDataEnd() override; + void OnHeadersStart(const Http2FrameHeader& header) override; + void OnHeadersPriority(const Http2PriorityFields& priority) override; + void OnHpackFragment(const char* data, size_t len) override; + void OnHeadersEnd() override; + void OnPriorityFrame(const Http2FrameHeader& header, + const Http2PriorityFields& priority) override; + void OnContinuationStart(const Http2FrameHeader& header) override; + void OnContinuationEnd() override; + void OnPadLength(size_t trailing_length) override; + void OnPadding(const char* pad, size_t skipped_length) override; + void OnRstStream(const Http2FrameHeader& header, + Http2ErrorCode error_code) override; + void OnSettingsStart(const Http2FrameHeader& header) override; + void OnSetting(const Http2SettingFields& setting_fields) override; + void OnSettingsEnd() override; + void OnSettingsAck(const Http2FrameHeader& header) override; + void OnPushPromiseStart(const Http2FrameHeader& header, + const Http2PushPromiseFields& promise, + size_t total_padding_length) override; + void OnPushPromiseEnd() override; + void OnPing(const Http2FrameHeader& header, + const Http2PingFields& ping) override; + void OnPingAck(const Http2FrameHeader& header, + const Http2PingFields& ping) override; + void OnGoAwayStart(const Http2FrameHeader& header, + const Http2GoAwayFields& goaway) override; + void OnGoAwayOpaqueData(const char* data, size_t len) override; + void OnGoAwayEnd() override; + void OnWindowUpdate(const Http2FrameHeader& header, + uint32_t increment) override; + void OnAltSvcStart(const Http2FrameHeader& header, + size_t origin_length, + size_t value_length) override; + void OnAltSvcOriginData(const char* data, size_t len) override; + void OnAltSvcValueData(const char* data, size_t len) override; + void OnAltSvcEnd() override; + void OnUnknownStart(const Http2FrameHeader& header) override; + void OnUnknownPayload(const char* data, size_t len) override; + void OnUnknownEnd() override; + void OnPaddingTooLong(const Http2FrameHeader& header, + size_t missing_length) override; + void OnFrameSizeError(const Http2FrameHeader& header) override; + + void AppendSetting(const Http2SettingFields& setting_fields) { + settings_.push_back(setting_fields); + } + + const Http2FrameHeader& GetFrameHeader() const { return frame_header_; } + + Http2Optional<Http2PriorityFields> GetOptPriority() const { + return opt_priority_; + } + Http2Optional<Http2ErrorCode> GetOptRstStreamErrorCode() const { + return opt_rst_stream_error_code_; + } + Http2Optional<Http2PushPromiseFields> GetOptPushPromise() const { + return opt_push_promise_; + } + Http2Optional<Http2PingFields> GetOptPing() const { return opt_ping_; } + Http2Optional<Http2GoAwayFields> GetOptGoaway() const { return opt_goaway_; } + Http2Optional<size_t> GetOptPadLength() const { return opt_pad_length_; } + Http2Optional<size_t> GetOptPayloadLength() const { + return opt_payload_length_; + } + Http2Optional<size_t> GetOptMissingLength() const { + return opt_missing_length_; + } + Http2Optional<size_t> GetOptAltsvcOriginLength() const { + return opt_altsvc_origin_length_; + } + Http2Optional<size_t> GetOptAltsvcValueLength() const { + return opt_altsvc_value_length_; + } + Http2Optional<size_t> GetOptWindowUpdateIncrement() const { + return opt_window_update_increment_; + } + bool GetHasFrameSizeError() const { return has_frame_size_error_; } + + void SetOptPriority(Http2Optional<Http2PriorityFields> opt_priority) { + opt_priority_ = opt_priority; + } + void SetOptRstStreamErrorCode( + Http2Optional<Http2ErrorCode> opt_rst_stream_error_code) { + opt_rst_stream_error_code_ = opt_rst_stream_error_code; + } + void SetOptPushPromise( + Http2Optional<Http2PushPromiseFields> opt_push_promise) { + opt_push_promise_ = opt_push_promise; + } + void SetOptPing(Http2Optional<Http2PingFields> opt_ping) { + opt_ping_ = opt_ping; + } + void SetOptGoaway(Http2Optional<Http2GoAwayFields> opt_goaway) { + opt_goaway_ = opt_goaway; + } + void SetOptPadLength(Http2Optional<size_t> opt_pad_length) { + opt_pad_length_ = opt_pad_length; + } + void SetOptPayloadLength(Http2Optional<size_t> opt_payload_length) { + opt_payload_length_ = opt_payload_length; + } + void SetOptMissingLength(Http2Optional<size_t> opt_missing_length) { + opt_missing_length_ = opt_missing_length; + } + void SetOptAltsvcOriginLength( + Http2Optional<size_t> opt_altsvc_origin_length) { + opt_altsvc_origin_length_ = opt_altsvc_origin_length; + } + void SetOptAltsvcValueLength(Http2Optional<size_t> opt_altsvc_value_length) { + opt_altsvc_value_length_ = opt_altsvc_value_length; + } + void SetOptWindowUpdateIncrement( + Http2Optional<size_t> opt_window_update_increment) { + opt_window_update_increment_ = opt_window_update_increment; + } + + void SetHasFrameSizeError(bool has_frame_size_error) { + has_frame_size_error_ = has_frame_size_error; + } + + private: + // ASSERT during an On* method that we're handling a frame of type + // expected_frame_type, and have not already received other On* methods + // (i.e. got_start_callback is false). + ::testing::AssertionResult StartFrameOfType( + const Http2FrameHeader& header, + Http2FrameType expected_frame_type); + + // ASSERT that StartFrameOfType has already been called with + // expected_frame_type (i.e. got_start_callback has been called), and that + // EndFrameOfType has not yet been called (i.e. got_end_callback is false). + ::testing::AssertionResult InFrameOfType(Http2FrameType expected_frame_type); + + // ASSERT that we're InFrameOfType, and then sets got_end_callback=true. + ::testing::AssertionResult EndFrameOfType(Http2FrameType expected_frame_type); + + // ASSERT that we're in the middle of processing a frame that is padded. + ::testing::AssertionResult InPaddedFrame(); + + // Append source to target. If opt_length is not nullptr, then verifies that + // the optional has a value (i.e. that the necessary On*Start method has been + // called), and that target is not longer than opt_length->value(). + ::testing::AssertionResult AppendString(Http2StringPiece source, + Http2String* target, + Http2Optional<size_t>* opt_length); + + const Http2FrameHeader frame_header_; + + Http2String payload_; + Http2String padding_; + Http2String altsvc_origin_; + Http2String altsvc_value_; + + Http2Optional<Http2PriorityFields> opt_priority_; + Http2Optional<Http2ErrorCode> opt_rst_stream_error_code_; + Http2Optional<Http2PushPromiseFields> opt_push_promise_; + Http2Optional<Http2PingFields> opt_ping_; + Http2Optional<Http2GoAwayFields> opt_goaway_; + + Http2Optional<size_t> opt_pad_length_; + Http2Optional<size_t> opt_payload_length_; + Http2Optional<size_t> opt_missing_length_; + Http2Optional<size_t> opt_altsvc_origin_length_; + Http2Optional<size_t> opt_altsvc_value_length_; + + Http2Optional<size_t> opt_window_update_increment_; + + bool has_frame_size_error_ = false; + + std::vector<Http2SettingFields> settings_; + + // These booleans are not checked by CompareCollectedFrames. + bool got_start_callback_ = false; + bool got_end_callback_ = false; +}; + +std::ostream& operator<<(std::ostream& out, const FrameParts& v); + +} // namespace test +} // namespace http2 + +#endif // QUICHE_HTTP2_TEST_TOOLS_FRAME_PARTS_H_
diff --git a/http2/test_tools/frame_parts_collector.cc b/http2/test_tools/frame_parts_collector.cc new file mode 100644 index 0000000..be4d986 --- /dev/null +++ b/http2/test_tools/frame_parts_collector.cc
@@ -0,0 +1,113 @@ +// 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/http2/test_tools/frame_parts_collector.h" + +#include <utility> + +#include "base/logging.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "net/third_party/quiche/src/http2/http2_structures_test_util.h" +#include "net/third_party/quiche/src/http2/platform/api/http2_ptr_util.h" + +namespace http2 { +namespace test { + +FramePartsCollector::FramePartsCollector() = default; +FramePartsCollector::~FramePartsCollector() = default; + +void FramePartsCollector::Reset() { + current_frame_.reset(); + collected_frames_.clear(); + expected_header_set_ = false; +} + +const FrameParts* FramePartsCollector::frame(size_t n) const { + if (n < size()) { + return collected_frames_.at(n).get(); + } + CHECK(n == size()); + return current_frame(); +} + +void FramePartsCollector::ExpectFrameHeader(const Http2FrameHeader& header) { + EXPECT_FALSE(IsInProgress()); + EXPECT_FALSE(expected_header_set_) + << "expected_header_: " << expected_header_; + expected_header_ = header; + expected_header_set_ = true; + // OnFrameHeader is called before the flags are scrubbed, but the other + // methods are called after, so scrub the invalid flags from expected_header_. + ScrubFlagsOfHeader(&expected_header_); +} + +void FramePartsCollector::TestExpectedHeader(const Http2FrameHeader& header) { + if (expected_header_set_) { + EXPECT_EQ(header, expected_header_); + expected_header_set_ = false; + } +} + +Http2FrameDecoderListener* FramePartsCollector::StartFrame( + const Http2FrameHeader& header) { + TestExpectedHeader(header); + EXPECT_FALSE(IsInProgress()); + if (current_frame_ == nullptr) { + current_frame_ = Http2MakeUnique<FrameParts>(header); + } + return current_frame(); +} + +Http2FrameDecoderListener* FramePartsCollector::StartAndEndFrame( + const Http2FrameHeader& header) { + TestExpectedHeader(header); + EXPECT_FALSE(IsInProgress()); + if (current_frame_ == nullptr) { + current_frame_ = Http2MakeUnique<FrameParts>(header); + } + Http2FrameDecoderListener* result = current_frame(); + collected_frames_.push_back(std::move(current_frame_)); + return result; +} + +Http2FrameDecoderListener* FramePartsCollector::CurrentFrame() { + EXPECT_TRUE(IsInProgress()); + if (current_frame_ == nullptr) { + return &failing_listener_; + } + return current_frame(); +} + +Http2FrameDecoderListener* FramePartsCollector::EndFrame() { + EXPECT_TRUE(IsInProgress()); + if (current_frame_ == nullptr) { + return &failing_listener_; + } + Http2FrameDecoderListener* result = current_frame(); + collected_frames_.push_back(std::move(current_frame_)); + return result; +} + +Http2FrameDecoderListener* FramePartsCollector::FrameError( + const Http2FrameHeader& header) { + TestExpectedHeader(header); + if (current_frame_ == nullptr) { + // The decoder may detect an error before making any calls to the listener + // regarding the frame, in which case current_frame_==nullptr and we need + // to create a FrameParts instance. + current_frame_ = Http2MakeUnique<FrameParts>(header); + } else { + // Similarly, the decoder may have made calls to the listener regarding the + // frame before detecting the error; for example, the DATA payload decoder + // calls OnDataStart before it can detect padding errors, hence before it + // can call OnPaddingTooLong. + EXPECT_EQ(header, current_frame_->GetFrameHeader()); + } + Http2FrameDecoderListener* result = current_frame(); + collected_frames_.push_back(std::move(current_frame_)); + return result; +} + +} // namespace test +} // namespace http2
diff --git a/http2/test_tools/frame_parts_collector.h b/http2/test_tools/frame_parts_collector.h new file mode 100644 index 0000000..a35740a --- /dev/null +++ b/http2/test_tools/frame_parts_collector.h
@@ -0,0 +1,111 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef QUICHE_HTTP2_TEST_TOOLS_FRAME_PARTS_COLLECTOR_H_ +#define QUICHE_HTTP2_TEST_TOOLS_FRAME_PARTS_COLLECTOR_H_ + +// FramePartsCollector is a base class for Http2FrameDecoderListener +// implementations that create one FrameParts instance for each decoded frame. + +#include <stddef.h> + +#include <memory> +#include <vector> + +#include "net/third_party/quiche/src/http2/decoder/http2_frame_decoder_listener.h" +#include "net/third_party/quiche/src/http2/decoder/http2_frame_decoder_listener_test_util.h" +#include "net/third_party/quiche/src/http2/http2_structures.h" +#include "net/third_party/quiche/src/http2/test_tools/frame_parts.h" + +namespace http2 { +namespace test { + +class FramePartsCollector : public FailingHttp2FrameDecoderListener { + public: + FramePartsCollector(); + ~FramePartsCollector() override; + + // Toss out the collected data. + void Reset(); + + // Returns true if has started recording the info for a frame and has not yet + // finished doing so. + bool IsInProgress() const { return current_frame_ != nullptr; } + + // Returns the FrameParts instance into which we're currently recording + // callback info if IsInProgress, else nullptr. + const FrameParts* current_frame() const { return current_frame_.get(); } + + // Returns the number of completely collected FrameParts instances. + size_t size() const { return collected_frames_.size(); } + + // Returns the n'th frame, where 0 is the oldest of the collected frames, + // and n==size() is the frame currently being collected, if there is one. + // Returns nullptr if the requested index is not valid. + const FrameParts* frame(size_t n) const; + + protected: + // In support of OnFrameHeader, set the header that we expect to be used in + // the next call. + // TODO(jamessynge): Remove ExpectFrameHeader et al. once done with supporting + // SpdyFramer's exact states. + void ExpectFrameHeader(const Http2FrameHeader& header); + + // For use in implementing On*Start methods of Http2FrameDecoderListener, + // returns a FrameParts instance, which will be newly created if + // IsInProgress==false (which the caller should ensure), else will be the + // current_frame(); never returns nullptr. + // If called when IsInProgress==true, a test failure will be recorded. + Http2FrameDecoderListener* StartFrame(const Http2FrameHeader& header); + + // For use in implementing On* callbacks, such as OnPingAck, that are the only + // call expected for the frame being decoded; not for On*Start methods. + // Returns a FrameParts instance, which will be newly created if + // IsInProgress==false (which the caller should ensure), else will be the + // current_frame(); never returns nullptr. + // If called when IsInProgress==true, a test failure will be recorded. + Http2FrameDecoderListener* StartAndEndFrame(const Http2FrameHeader& header); + + // If IsInProgress==true, returns the FrameParts into which the current + // frame is being recorded; else records a test failure and returns + // failing_listener_, which will record a test failure when any of its + // On* methods is called. + Http2FrameDecoderListener* CurrentFrame(); + + // For use in implementing On*End methods, pushes the current frame onto + // the vector of completed frames, and returns a pointer to it for recording + // the info in the final call. If IsInProgress==false, records a test failure + // and returns failing_listener_, which will record a test failure when any + // of its On* methods is called. + Http2FrameDecoderListener* EndFrame(); + + // For use in implementing OnPaddingTooLong and OnFrameSizeError, is + // equivalent to EndFrame() if IsInProgress==true, else equivalent to + // StartAndEndFrame(). + Http2FrameDecoderListener* FrameError(const Http2FrameHeader& header); + + private: + // Returns the mutable FrameParts instance into which we're currently + // recording callback info if IsInProgress, else nullptr. + FrameParts* current_frame() { return current_frame_.get(); } + + // If expected header is set, verify that it matches the header param. + // TODO(jamessynge): Remove TestExpectedHeader et al. once done + // with supporting SpdyFramer's exact states. + void TestExpectedHeader(const Http2FrameHeader& header); + + std::unique_ptr<FrameParts> current_frame_; + std::vector<std::unique_ptr<FrameParts>> collected_frames_; + FailingHttp2FrameDecoderListener failing_listener_; + + // TODO(jamessynge): Remove expected_header_ et al. once done with supporting + // SpdyFramer's exact states. + Http2FrameHeader expected_header_; + bool expected_header_set_ = false; +}; + +} // namespace test +} // namespace http2 + +#endif // QUICHE_HTTP2_TEST_TOOLS_FRAME_PARTS_COLLECTOR_H_
diff --git a/http2/test_tools/frame_parts_collector_listener.cc b/http2/test_tools/frame_parts_collector_listener.cc new file mode 100644 index 0000000..8d9da78 --- /dev/null +++ b/http2/test_tools/frame_parts_collector_listener.cc
@@ -0,0 +1,230 @@ +// 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/http2/test_tools/frame_parts_collector_listener.h" + +#include "base/logging.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace http2 { +namespace test { + +bool FramePartsCollectorListener::OnFrameHeader( + const Http2FrameHeader& header) { + VLOG(1) << "OnFrameHeader: " << header; + ExpectFrameHeader(header); + return true; +} + +void FramePartsCollectorListener::OnDataStart(const Http2FrameHeader& header) { + VLOG(1) << "OnDataStart: " << header; + StartFrame(header)->OnDataStart(header); +} + +void FramePartsCollectorListener::OnDataPayload(const char* data, size_t len) { + VLOG(1) << "OnDataPayload: len=" << len; + CurrentFrame()->OnDataPayload(data, len); +} + +void FramePartsCollectorListener::OnDataEnd() { + VLOG(1) << "OnDataEnd"; + EndFrame()->OnDataEnd(); +} + +void FramePartsCollectorListener::OnHeadersStart( + const Http2FrameHeader& header) { + VLOG(1) << "OnHeadersStart: " << header; + StartFrame(header)->OnHeadersStart(header); +} + +void FramePartsCollectorListener::OnHeadersPriority( + const Http2PriorityFields& priority) { + VLOG(1) << "OnHeadersPriority: " << priority; + CurrentFrame()->OnHeadersPriority(priority); +} + +void FramePartsCollectorListener::OnHpackFragment(const char* data, + size_t len) { + VLOG(1) << "OnHpackFragment: len=" << len; + CurrentFrame()->OnHpackFragment(data, len); +} + +void FramePartsCollectorListener::OnHeadersEnd() { + VLOG(1) << "OnHeadersEnd"; + EndFrame()->OnHeadersEnd(); +} + +void FramePartsCollectorListener::OnPriorityFrame( + const Http2FrameHeader& header, + const Http2PriorityFields& priority_fields) { + VLOG(1) << "OnPriority: " << header << "; " << priority_fields; + StartAndEndFrame(header)->OnPriorityFrame(header, priority_fields); +} + +void FramePartsCollectorListener::OnContinuationStart( + const Http2FrameHeader& header) { + VLOG(1) << "OnContinuationStart: " << header; + StartFrame(header)->OnContinuationStart(header); +} + +void FramePartsCollectorListener::OnContinuationEnd() { + VLOG(1) << "OnContinuationEnd"; + EndFrame()->OnContinuationEnd(); +} + +void FramePartsCollectorListener::OnPadLength(size_t pad_length) { + VLOG(1) << "OnPadLength: " << pad_length; + CurrentFrame()->OnPadLength(pad_length); +} + +void FramePartsCollectorListener::OnPadding(const char* padding, + size_t skipped_length) { + VLOG(1) << "OnPadding: " << skipped_length; + CurrentFrame()->OnPadding(padding, skipped_length); +} + +void FramePartsCollectorListener::OnRstStream(const Http2FrameHeader& header, + Http2ErrorCode error_code) { + VLOG(1) << "OnRstStream: " << header << "; error_code=" << error_code; + StartAndEndFrame(header)->OnRstStream(header, error_code); +} + +void FramePartsCollectorListener::OnSettingsStart( + const Http2FrameHeader& header) { + VLOG(1) << "OnSettingsStart: " << header; + EXPECT_EQ(Http2FrameType::SETTINGS, header.type) << header; + EXPECT_EQ(Http2FrameFlag(), header.flags) << header; + StartFrame(header)->OnSettingsStart(header); +} + +void FramePartsCollectorListener::OnSetting( + const Http2SettingFields& setting_fields) { + VLOG(1) << "Http2SettingFields: setting_fields=" << setting_fields; + CurrentFrame()->OnSetting(setting_fields); +} + +void FramePartsCollectorListener::OnSettingsEnd() { + VLOG(1) << "OnSettingsEnd"; + EndFrame()->OnSettingsEnd(); +} + +void FramePartsCollectorListener::OnSettingsAck( + const Http2FrameHeader& header) { + VLOG(1) << "OnSettingsAck: " << header; + StartAndEndFrame(header)->OnSettingsAck(header); +} + +void FramePartsCollectorListener::OnPushPromiseStart( + const Http2FrameHeader& header, + const Http2PushPromiseFields& promise, + size_t total_padding_length) { + VLOG(1) << "OnPushPromiseStart header: " << header << " promise: " << promise + << " total_padding_length: " << total_padding_length; + EXPECT_EQ(Http2FrameType::PUSH_PROMISE, header.type); + StartFrame(header)->OnPushPromiseStart(header, promise, total_padding_length); +} + +void FramePartsCollectorListener::OnPushPromiseEnd() { + VLOG(1) << "OnPushPromiseEnd"; + EndFrame()->OnPushPromiseEnd(); +} + +void FramePartsCollectorListener::OnPing(const Http2FrameHeader& header, + const Http2PingFields& ping) { + VLOG(1) << "OnPing: " << header << "; " << ping; + StartAndEndFrame(header)->OnPing(header, ping); +} + +void FramePartsCollectorListener::OnPingAck(const Http2FrameHeader& header, + const Http2PingFields& ping) { + VLOG(1) << "OnPingAck: " << header << "; " << ping; + StartAndEndFrame(header)->OnPingAck(header, ping); +} + +void FramePartsCollectorListener::OnGoAwayStart( + const Http2FrameHeader& header, + const Http2GoAwayFields& goaway) { + VLOG(1) << "OnGoAwayStart header: " << header << "; goaway: " << goaway; + StartFrame(header)->OnGoAwayStart(header, goaway); +} + +void FramePartsCollectorListener::OnGoAwayOpaqueData(const char* data, + size_t len) { + VLOG(1) << "OnGoAwayOpaqueData: len=" << len; + CurrentFrame()->OnGoAwayOpaqueData(data, len); +} + +void FramePartsCollectorListener::OnGoAwayEnd() { + VLOG(1) << "OnGoAwayEnd"; + EndFrame()->OnGoAwayEnd(); +} + +void FramePartsCollectorListener::OnWindowUpdate( + const Http2FrameHeader& header, + uint32_t window_size_increment) { + VLOG(1) << "OnWindowUpdate: " << header + << "; window_size_increment=" << window_size_increment; + EXPECT_EQ(Http2FrameType::WINDOW_UPDATE, header.type); + StartAndEndFrame(header)->OnWindowUpdate(header, window_size_increment); +} + +void FramePartsCollectorListener::OnAltSvcStart(const Http2FrameHeader& header, + size_t origin_length, + size_t value_length) { + VLOG(1) << "OnAltSvcStart header: " << header + << "; origin_length=" << origin_length + << "; value_length=" << value_length; + StartFrame(header)->OnAltSvcStart(header, origin_length, value_length); +} + +void FramePartsCollectorListener::OnAltSvcOriginData(const char* data, + size_t len) { + VLOG(1) << "OnAltSvcOriginData: len=" << len; + CurrentFrame()->OnAltSvcOriginData(data, len); +} + +void FramePartsCollectorListener::OnAltSvcValueData(const char* data, + size_t len) { + VLOG(1) << "OnAltSvcValueData: len=" << len; + CurrentFrame()->OnAltSvcValueData(data, len); +} + +void FramePartsCollectorListener::OnAltSvcEnd() { + VLOG(1) << "OnAltSvcEnd"; + EndFrame()->OnAltSvcEnd(); +} + +void FramePartsCollectorListener::OnUnknownStart( + const Http2FrameHeader& header) { + VLOG(1) << "OnUnknownStart: " << header; + StartFrame(header)->OnUnknownStart(header); +} + +void FramePartsCollectorListener::OnUnknownPayload(const char* data, + size_t len) { + VLOG(1) << "OnUnknownPayload: len=" << len; + CurrentFrame()->OnUnknownPayload(data, len); +} + +void FramePartsCollectorListener::OnUnknownEnd() { + VLOG(1) << "OnUnknownEnd"; + EndFrame()->OnUnknownEnd(); +} + +void FramePartsCollectorListener::OnPaddingTooLong( + const Http2FrameHeader& header, + size_t missing_length) { + VLOG(1) << "OnPaddingTooLong: " << header + << " missing_length: " << missing_length; + EndFrame()->OnPaddingTooLong(header, missing_length); +} + +void FramePartsCollectorListener::OnFrameSizeError( + const Http2FrameHeader& header) { + VLOG(1) << "OnFrameSizeError: " << header; + FrameError(header)->OnFrameSizeError(header); +} + +} // namespace test +} // namespace http2
diff --git a/http2/test_tools/frame_parts_collector_listener.h b/http2/test_tools/frame_parts_collector_listener.h new file mode 100644 index 0000000..07a82aa --- /dev/null +++ b/http2/test_tools/frame_parts_collector_listener.h
@@ -0,0 +1,85 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef QUICHE_HTTP2_TEST_TOOLS_FRAME_PARTS_COLLECTOR_LISTENER_H_ +#define QUICHE_HTTP2_TEST_TOOLS_FRAME_PARTS_COLLECTOR_LISTENER_H_ + +// FramePartsCollectorListener extends FramePartsCollector with an +// implementation of every method of Http2FrameDecoderListener; it is +// essentially the union of all the Listener classes in the tests of the +// payload decoders (i.e. in ./payload_decoders/*_test.cc files), with the +// addition of the OnFrameHeader method. +// FramePartsCollectorListener supports tests of Http2FrameDecoder. + +#include <stddef.h> + +#include <cstdint> + +#include "net/third_party/quiche/src/http2/decoder/http2_frame_decoder_listener.h" +#include "net/third_party/quiche/src/http2/http2_constants.h" +#include "net/third_party/quiche/src/http2/http2_structures.h" +#include "net/third_party/quiche/src/http2/test_tools/frame_parts_collector.h" + +namespace http2 { +namespace test { + +class FramePartsCollectorListener : public FramePartsCollector { + public: + FramePartsCollectorListener() {} + ~FramePartsCollectorListener() override {} + + // TODO(jamessynge): Remove OnFrameHeader once done with supporting + // SpdyFramer's exact states. + bool OnFrameHeader(const Http2FrameHeader& header) override; + void OnDataStart(const Http2FrameHeader& header) override; + void OnDataPayload(const char* data, size_t len) override; + void OnDataEnd() override; + void OnHeadersStart(const Http2FrameHeader& header) override; + void OnHeadersPriority(const Http2PriorityFields& priority) override; + void OnHpackFragment(const char* data, size_t len) override; + void OnHeadersEnd() override; + void OnPriorityFrame(const Http2FrameHeader& header, + const Http2PriorityFields& priority_fields) override; + void OnContinuationStart(const Http2FrameHeader& header) override; + void OnContinuationEnd() override; + void OnPadLength(size_t pad_length) override; + void OnPadding(const char* padding, size_t skipped_length) override; + void OnRstStream(const Http2FrameHeader& header, + Http2ErrorCode error_code) override; + void OnSettingsStart(const Http2FrameHeader& header) override; + void OnSetting(const Http2SettingFields& setting_fields) override; + void OnSettingsEnd() override; + void OnSettingsAck(const Http2FrameHeader& header) override; + void OnPushPromiseStart(const Http2FrameHeader& header, + const Http2PushPromiseFields& promise, + size_t total_padding_length) override; + void OnPushPromiseEnd() override; + void OnPing(const Http2FrameHeader& header, + const Http2PingFields& ping) override; + void OnPingAck(const Http2FrameHeader& header, + const Http2PingFields& ping) override; + void OnGoAwayStart(const Http2FrameHeader& header, + const Http2GoAwayFields& goaway) override; + void OnGoAwayOpaqueData(const char* data, size_t len) override; + void OnGoAwayEnd() override; + void OnWindowUpdate(const Http2FrameHeader& header, + uint32_t window_size_increment) override; + void OnAltSvcStart(const Http2FrameHeader& header, + size_t origin_length, + size_t value_length) override; + void OnAltSvcOriginData(const char* data, size_t len) override; + void OnAltSvcValueData(const char* data, size_t len) override; + void OnAltSvcEnd() override; + void OnUnknownStart(const Http2FrameHeader& header) override; + void OnUnknownPayload(const char* data, size_t len) override; + void OnUnknownEnd() override; + void OnPaddingTooLong(const Http2FrameHeader& header, + size_t missing_length) override; + void OnFrameSizeError(const Http2FrameHeader& header) override; +}; + +} // namespace test +} // namespace http2 + +#endif // QUICHE_HTTP2_TEST_TOOLS_FRAME_PARTS_COLLECTOR_LISTENER_H_
diff --git a/http2/test_tools/http2_random.cc b/http2/test_tools/http2_random.cc new file mode 100644 index 0000000..fc577f4 --- /dev/null +++ b/http2/test_tools/http2_random.cc
@@ -0,0 +1,72 @@ +#include "net/third_party/quiche/src/http2/test_tools/http2_random.h" + +#include "base/logging.h" +#include "net/third_party/quiche/src/http2/platform/api/http2_string_utils.h" +#include "third_party/boringssl/src/include/openssl/chacha.h" +#include "third_party/boringssl/src/include/openssl/rand.h" + +static const uint8_t kZeroNonce[12] = {0}; + +namespace http2 { +namespace test { + +Http2Random::Http2Random() { + RAND_bytes(key_, sizeof(key_)); + + LOG(INFO) << "Initialized test RNG with the following key: " << Key(); +} + +Http2Random::Http2Random(Http2StringPiece key) { + Http2String decoded_key = Http2HexDecode(key); + CHECK_EQ(sizeof(key_), decoded_key.size()); + memcpy(key_, decoded_key.data(), sizeof(key_)); +} + +Http2String Http2Random::Key() const { + return Http2HexEncode(key_, sizeof(key_)); +} + +void Http2Random::FillRandom(void* buffer, size_t buffer_size) { + memset(buffer, 0, buffer_size); + uint8_t* buffer_u8 = reinterpret_cast<uint8_t*>(buffer); + CRYPTO_chacha_20(buffer_u8, buffer_u8, buffer_size, key_, kZeroNonce, + counter_++); +} + +Http2String Http2Random::RandString(int length) { + Http2String result; + result.resize(length); + FillRandom(&result[0], length); + return result; +} + +uint64_t Http2Random::Rand64() { + union { + uint64_t number; + uint8_t bytes[sizeof(uint64_t)]; + } result; + FillRandom(result.bytes, sizeof(result.bytes)); + return result.number; +} + +double Http2Random::RandDouble() { + union { + double f; + uint64_t i; + } value; + value.i = (1023ull << 52ull) | (Rand64() & 0xfffffffffffffu); + return value.f - 1.0; +} + +Http2String Http2Random::RandStringWithAlphabet(int length, + Http2StringPiece alphabet) { + Http2String result; + result.resize(length); + for (int i = 0; i < length; i++) { + result[i] = alphabet[Uniform(alphabet.size())]; + } + return result; +} + +} // namespace test +} // namespace http2
diff --git a/http2/test_tools/http2_random.h b/http2/test_tools/http2_random.h new file mode 100644 index 0000000..def60f8 --- /dev/null +++ b/http2/test_tools/http2_random.h
@@ -0,0 +1,88 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef QUICHE_HTTP2_TEST_TOOLS_HTTP2_RANDOM_H_ +#define QUICHE_HTTP2_TEST_TOOLS_HTTP2_RANDOM_H_ + +#include <cmath> +#include <cstdint> +#include <limits> +#include <random> + +#include "net/third_party/quiche/src/http2/platform/api/http2_string.h" +#include "net/third_party/quiche/src/http2/platform/api/http2_string_piece.h" + +namespace http2 { +namespace test { + +// The random number generator used for unit tests. Since the algorithm is +// deterministic and fixed, this can be used to reproduce flakes in the unit +// tests caused by specific random values. +class Http2Random { + public: + Http2Random(); + + Http2Random(const Http2Random&) = delete; + Http2Random& operator=(const Http2Random&) = delete; + + // Reproducible random number generation: by using the same key, the same + // sequence of results is obtained. + explicit Http2Random(Http2StringPiece key); + Http2String Key() const; + + void FillRandom(void* buffer, size_t buffer_size); + Http2String RandString(int length); + + // Returns a random 64-bit value. + uint64_t Rand64(); + + // Return a uniformly distrubted random number in [0, n). + uint32_t Uniform(uint32_t n) { return Rand64() % n; } + // Return a uniformly distrubted random number in [lo, hi). + uint64_t UniformInRange(uint64_t lo, uint64_t hi) { + return lo + Rand64() % (hi - lo); + } + // Return an integer of logarithmically random scale. + uint32_t Skewed(uint32_t max_log) { + const uint32_t base = Rand32() % (max_log + 1); + const uint32_t mask = ((base < 32) ? (1u << base) : 0u) - 1u; + return Rand32() & mask; + } + // Return a random number in [0, max] range that skews low. + uint64_t RandomSizeSkewedLow(uint64_t max) { + return std::round(max * std::pow(RandDouble(), 2)); + } + + // Returns a random double between 0 and 1. + double RandDouble(); + float RandFloat() { return RandDouble(); } + + // Has 1/n chance of returning true. + bool OneIn(int n) { return Uniform(n) == 0; } + + uint8_t Rand8() { return Rand64(); } + uint16_t Rand16() { return Rand64(); } + uint32_t Rand32() { return Rand64(); } + + // Return a random string consisting of the characters from the specified + // alphabet. + Http2String RandStringWithAlphabet(int length, Http2StringPiece alphabet); + + // STL UniformRandomNumberGenerator implementation. + using result_type = uint64_t; + static constexpr result_type min() { return 0; } + static constexpr result_type max() { + return std::numeric_limits<result_type>::max(); + } + result_type operator()() { return Rand64(); } + + private: + uint8_t key_[32]; + uint32_t counter_ = 0; +}; + +} // namespace test +} // namespace http2 + +#endif // QUICHE_HTTP2_TEST_TOOLS_HTTP2_RANDOM_H_
diff --git a/http2/test_tools/http2_random_test.cc b/http2/test_tools/http2_random_test.cc new file mode 100644 index 0000000..a1b74f6 --- /dev/null +++ b/http2/test_tools/http2_random_test.cc
@@ -0,0 +1,94 @@ +#include "net/third_party/quiche/src/http2/test_tools/http2_random.h" + +#include <set> + +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace http2 { +namespace test { +namespace { + +TEST(Http2RandomTest, ProducesDifferentNumbers) { + Http2Random random; + uint64_t value1 = random.Rand64(); + uint64_t value2 = random.Rand64(); + uint64_t value3 = random.Rand64(); + + EXPECT_NE(value1, value2); + EXPECT_NE(value2, value3); + EXPECT_NE(value3, value1); +} + +TEST(Http2RandomTest, StartsWithDifferentKeys) { + Http2Random random1; + Http2Random random2; + + EXPECT_NE(random1.Key(), random2.Key()); + EXPECT_NE(random1.Rand64(), random2.Rand64()); + EXPECT_NE(random1.Rand64(), random2.Rand64()); + EXPECT_NE(random1.Rand64(), random2.Rand64()); +} + +TEST(Http2RandomTest, ReproducibleRandom) { + Http2Random random; + uint64_t value1 = random.Rand64(); + uint64_t value2 = random.Rand64(); + + Http2Random clone_random(random.Key()); + EXPECT_EQ(clone_random.Key(), random.Key()); + EXPECT_EQ(value1, clone_random.Rand64()); + EXPECT_EQ(value2, clone_random.Rand64()); +} + +TEST(Http2RandomTest, STLShuffle) { + Http2Random random; + const Http2String original = "abcdefghijklmonpqrsuvwxyz"; + + Http2String shuffled = original; + std::shuffle(shuffled.begin(), shuffled.end(), random); + EXPECT_NE(original, shuffled); +} + +TEST(Http2RandomTest, RandFloat) { + Http2Random random; + for (int i = 0; i < 10000; i++) { + float value = random.RandFloat(); + ASSERT_GE(value, 0.f); + ASSERT_LE(value, 1.f); + } +} + +TEST(Http2RandomTest, RandStringWithAlphabet) { + Http2Random random; + Http2String str = random.RandStringWithAlphabet(1000, "xyz"); + EXPECT_EQ(1000u, str.size()); + + std::set<char> characters(str.begin(), str.end()); + EXPECT_THAT(characters, testing::ElementsAre('x', 'y', 'z')); +} + +TEST(Http2RandomTest, SkewedLow) { + Http2Random random; + constexpr size_t kMax = 1234; + for (int i = 0; i < 10000; i++) { + size_t value = random.RandomSizeSkewedLow(kMax); + ASSERT_GE(value, 0u); + ASSERT_LE(value, kMax); + } +} + +// Checks that SkewedLow() generates full range. This is required, since in +// some unit tests would infinitely loop. +TEST(Http2RandomTest, SkewedLowFullRange) { + Http2Random random; + std::set<size_t> values; + for (int i = 0; i < 1000; i++) { + values.insert(random.RandomSizeSkewedLow(3)); + } + EXPECT_THAT(values, testing::ElementsAre(0, 1, 2, 3)); +} + +} // namespace +} // namespace test +} // namespace http2