1 #include "quiche/http2/adapter/test_utils.h"
2
3 #include <optional>
4 #include <ostream>
5
6 #include "absl/strings/str_format.h"
7 #include "quiche/http2/adapter/http2_visitor_interface.h"
8 #include "quiche/common/quiche_data_reader.h"
9 #include "quiche/spdy/core/hpack/hpack_encoder.h"
10 #include "quiche/spdy/core/spdy_protocol.h"
11
12 namespace http2 {
13 namespace adapter {
14 namespace test {
15 namespace {
16
17 using ConnectionError = Http2VisitorInterface::ConnectionError;
18
19 } // anonymous namespace
20
TestDataFrameSource(Http2VisitorInterface & visitor,bool has_fin)21 TestDataFrameSource::TestDataFrameSource(Http2VisitorInterface& visitor,
22 bool has_fin)
23 : visitor_(visitor), has_fin_(has_fin) {}
24
AppendPayload(absl::string_view payload)25 void TestDataFrameSource::AppendPayload(absl::string_view payload) {
26 QUICHE_CHECK(!end_data_);
27 if (!payload.empty()) {
28 payload_fragments_.push_back(std::string(payload));
29 current_fragment_ = payload_fragments_.front();
30 }
31 }
32
EndData()33 void TestDataFrameSource::EndData() { end_data_ = true; }
34
SelectPayloadLength(size_t max_length)35 std::pair<int64_t, bool> TestDataFrameSource::SelectPayloadLength(
36 size_t max_length) {
37 if (return_error_) {
38 return {DataFrameSource::kError, false};
39 }
40 // The stream is done if there's no more data, or if |max_length| is at least
41 // as large as the remaining data.
42 const bool end_data = end_data_ && (current_fragment_.empty() ||
43 (payload_fragments_.size() == 1 &&
44 max_length >= current_fragment_.size()));
45 const int64_t length = std::min(max_length, current_fragment_.size());
46 return {length, end_data};
47 }
48
Send(absl::string_view frame_header,size_t payload_length)49 bool TestDataFrameSource::Send(absl::string_view frame_header,
50 size_t payload_length) {
51 QUICHE_LOG_IF(DFATAL, payload_length > current_fragment_.size())
52 << "payload_length: " << payload_length
53 << " current_fragment_size: " << current_fragment_.size();
54 const std::string concatenated =
55 absl::StrCat(frame_header, current_fragment_.substr(0, payload_length));
56 const int64_t result = visitor_.OnReadyToSend(concatenated);
57 if (result < 0) {
58 // Write encountered error.
59 visitor_.OnConnectionError(ConnectionError::kSendError);
60 current_fragment_ = {};
61 payload_fragments_.clear();
62 return false;
63 } else if (result == 0) {
64 // Write blocked.
65 return false;
66 } else if (static_cast<size_t>(result) < concatenated.size()) {
67 // Probably need to handle this better within this test class.
68 QUICHE_LOG(DFATAL)
69 << "DATA frame not fully flushed. Connection will be corrupt!";
70 visitor_.OnConnectionError(ConnectionError::kSendError);
71 current_fragment_ = {};
72 payload_fragments_.clear();
73 return false;
74 }
75 if (payload_length > 0) {
76 current_fragment_.remove_prefix(payload_length);
77 }
78 if (current_fragment_.empty() && !payload_fragments_.empty()) {
79 payload_fragments_.erase(payload_fragments_.begin());
80 if (!payload_fragments_.empty()) {
81 current_fragment_ = payload_fragments_.front();
82 }
83 }
84 return true;
85 }
86
EncodeHeaders(const spdy::Http2HeaderBlock & entries)87 std::string EncodeHeaders(const spdy::Http2HeaderBlock& entries) {
88 spdy::HpackEncoder encoder;
89 encoder.DisableCompression();
90 return encoder.EncodeHeaderBlock(entries);
91 }
92
TestMetadataSource(const spdy::Http2HeaderBlock & entries)93 TestMetadataSource::TestMetadataSource(const spdy::Http2HeaderBlock& entries)
94 : encoded_entries_(EncodeHeaders(entries)) {
95 remaining_ = encoded_entries_;
96 }
97
Pack(uint8_t * dest,size_t dest_len)98 std::pair<int64_t, bool> TestMetadataSource::Pack(uint8_t* dest,
99 size_t dest_len) {
100 const size_t copied = std::min(dest_len, remaining_.size());
101 std::memcpy(dest, remaining_.data(), copied);
102 remaining_.remove_prefix(copied);
103 return std::make_pair(copied, remaining_.empty());
104 }
105
106 namespace {
107
108 using TypeAndOptionalLength =
109 std::pair<spdy::SpdyFrameType, std::optional<size_t>>;
110
operator <<(std::ostream & os,const std::vector<TypeAndOptionalLength> & types_and_lengths)111 std::ostream& operator<<(
112 std::ostream& os,
113 const std::vector<TypeAndOptionalLength>& types_and_lengths) {
114 for (const auto& type_and_length : types_and_lengths) {
115 os << "(" << spdy::FrameTypeToString(type_and_length.first) << ", "
116 << (type_and_length.second ? absl::StrCat(type_and_length.second.value())
117 : "<unspecified>")
118 << ") ";
119 }
120 return os;
121 }
122
FrameTypeToString(uint8_t frame_type)123 std::string FrameTypeToString(uint8_t frame_type) {
124 if (spdy::IsDefinedFrameType(frame_type)) {
125 return spdy::FrameTypeToString(spdy::ParseFrameType(frame_type));
126 } else {
127 return absl::StrFormat("0x%x", static_cast<int>(frame_type));
128 }
129 }
130
131 // Custom gMock matcher, used to implement EqualsFrames().
132 class SpdyControlFrameMatcher
133 : public testing::MatcherInterface<absl::string_view> {
134 public:
SpdyControlFrameMatcher(std::vector<TypeAndOptionalLength> types_and_lengths)135 explicit SpdyControlFrameMatcher(
136 std::vector<TypeAndOptionalLength> types_and_lengths)
137 : expected_types_and_lengths_(std::move(types_and_lengths)) {}
138
MatchAndExplain(absl::string_view s,testing::MatchResultListener * listener) const139 bool MatchAndExplain(absl::string_view s,
140 testing::MatchResultListener* listener) const override {
141 quiche::QuicheDataReader reader(s.data(), s.size());
142
143 for (TypeAndOptionalLength expected : expected_types_and_lengths_) {
144 if (!MatchAndExplainOneFrame(expected.first, expected.second, &reader,
145 listener)) {
146 return false;
147 }
148 }
149 if (!reader.IsDoneReading()) {
150 *listener << "; " << reader.BytesRemaining() << " bytes left to read!";
151 return false;
152 }
153 return true;
154 }
155
MatchAndExplainOneFrame(spdy::SpdyFrameType expected_type,std::optional<size_t> expected_length,quiche::QuicheDataReader * reader,testing::MatchResultListener * listener) const156 bool MatchAndExplainOneFrame(spdy::SpdyFrameType expected_type,
157 std::optional<size_t> expected_length,
158 quiche::QuicheDataReader* reader,
159 testing::MatchResultListener* listener) const {
160 uint32_t payload_length;
161 if (!reader->ReadUInt24(&payload_length)) {
162 *listener << "; unable to read length field for expected_type "
163 << FrameTypeToString(expected_type) << ". data too short!";
164 return false;
165 }
166
167 if (expected_length && payload_length != expected_length.value()) {
168 *listener << "; actual length: " << payload_length
169 << " but expected length: " << expected_length.value();
170 return false;
171 }
172
173 uint8_t raw_type;
174 if (!reader->ReadUInt8(&raw_type)) {
175 *listener << "; unable to read type field for expected_type "
176 << FrameTypeToString(expected_type) << ". data too short!";
177 return false;
178 }
179
180 if (raw_type != static_cast<uint8_t>(expected_type)) {
181 *listener << "; actual type: " << FrameTypeToString(raw_type)
182 << " but expected type: " << FrameTypeToString(expected_type);
183 return false;
184 }
185
186 // Seek past flags (1B), stream ID (4B), and payload. Reach the next frame.
187 reader->Seek(5 + payload_length);
188 return true;
189 }
190
DescribeTo(std::ostream * os) const191 void DescribeTo(std::ostream* os) const override {
192 *os << "Data contains frames of types in sequence "
193 << expected_types_and_lengths_;
194 }
195
DescribeNegationTo(std::ostream * os) const196 void DescribeNegationTo(std::ostream* os) const override {
197 *os << "Data does not contain frames of types in sequence "
198 << expected_types_and_lengths_;
199 }
200
201 private:
202 const std::vector<TypeAndOptionalLength> expected_types_and_lengths_;
203 };
204
205 } // namespace
206
EqualsFrames(std::vector<std::pair<spdy::SpdyFrameType,std::optional<size_t>>> types_and_lengths)207 testing::Matcher<absl::string_view> EqualsFrames(
208 std::vector<std::pair<spdy::SpdyFrameType, std::optional<size_t>>>
209 types_and_lengths) {
210 return MakeMatcher(new SpdyControlFrameMatcher(std::move(types_and_lengths)));
211 }
212
EqualsFrames(std::vector<spdy::SpdyFrameType> types)213 testing::Matcher<absl::string_view> EqualsFrames(
214 std::vector<spdy::SpdyFrameType> types) {
215 std::vector<std::pair<spdy::SpdyFrameType, std::optional<size_t>>>
216 types_and_lengths;
217 types_and_lengths.reserve(types.size());
218 for (spdy::SpdyFrameType type : types) {
219 types_and_lengths.push_back({type, std::nullopt});
220 }
221 return MakeMatcher(new SpdyControlFrameMatcher(std::move(types_and_lengths)));
222 }
223
224 } // namespace test
225 } // namespace adapter
226 } // namespace http2
227