xref: /aosp_15_r20/external/cronet/net/third_party/quiche/src/quiche/http2/adapter/nghttp2_test_utils.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 #include "quiche/http2/adapter/nghttp2_test_utils.h"
2 
3 #include "quiche/http2/adapter/nghttp2_util.h"
4 #include "quiche/common/quiche_endian.h"
5 
6 namespace http2 {
7 namespace adapter {
8 namespace test {
9 
10 namespace {
11 
12 // Custom gMock matcher, used to implement HasFrameHeader().
13 class FrameHeaderMatcher {
14  public:
FrameHeaderMatcher(int32_t streamid,uint8_t type,const testing::Matcher<int> flags)15   FrameHeaderMatcher(int32_t streamid, uint8_t type,
16                      const testing::Matcher<int> flags)
17       : stream_id_(streamid), type_(type), flags_(flags) {}
18 
Match(const nghttp2_frame_hd & frame,testing::MatchResultListener * listener) const19   bool Match(const nghttp2_frame_hd& frame,
20              testing::MatchResultListener* listener) const {
21     bool matched = true;
22     if (stream_id_ != frame.stream_id) {
23       *listener << "; expected stream " << stream_id_ << ", saw "
24                 << frame.stream_id;
25       matched = false;
26     }
27     if (type_ != frame.type) {
28       *listener << "; expected frame type " << type_ << ", saw "
29                 << static_cast<int>(frame.type);
30       matched = false;
31     }
32     if (!flags_.MatchAndExplain(frame.flags, listener)) {
33       matched = false;
34     }
35     return matched;
36   }
37 
DescribeTo(std::ostream * os) const38   void DescribeTo(std::ostream* os) const {
39     *os << "contains a frame header with stream " << stream_id_ << ", type "
40         << type_ << ", ";
41     flags_.DescribeTo(os);
42   }
43 
DescribeNegationTo(std::ostream * os) const44   void DescribeNegationTo(std::ostream* os) const {
45     *os << "does not contain a frame header with stream " << stream_id_
46         << ", type " << type_ << ", ";
47     flags_.DescribeNegationTo(os);
48   }
49 
50  private:
51   const int32_t stream_id_;
52   const int type_;
53   const testing::Matcher<int> flags_;
54 };
55 
56 class PointerToFrameHeaderMatcher
57     : public FrameHeaderMatcher,
58       public testing::MatcherInterface<const nghttp2_frame_hd*> {
59  public:
PointerToFrameHeaderMatcher(int32_t streamid,uint8_t type,const testing::Matcher<int> flags)60   PointerToFrameHeaderMatcher(int32_t streamid, uint8_t type,
61                               const testing::Matcher<int> flags)
62       : FrameHeaderMatcher(streamid, type, flags) {}
63 
MatchAndExplain(const nghttp2_frame_hd * frame,testing::MatchResultListener * listener) const64   bool MatchAndExplain(const nghttp2_frame_hd* frame,
65                        testing::MatchResultListener* listener) const override {
66     return FrameHeaderMatcher::Match(*frame, listener);
67   }
68 
DescribeTo(std::ostream * os) const69   void DescribeTo(std::ostream* os) const override {
70     FrameHeaderMatcher::DescribeTo(os);
71   }
72 
DescribeNegationTo(std::ostream * os) const73   void DescribeNegationTo(std::ostream* os) const override {
74     FrameHeaderMatcher::DescribeNegationTo(os);
75   }
76 };
77 
78 class ReferenceToFrameHeaderMatcher
79     : public FrameHeaderMatcher,
80       public testing::MatcherInterface<const nghttp2_frame_hd&> {
81  public:
ReferenceToFrameHeaderMatcher(int32_t streamid,uint8_t type,const testing::Matcher<int> flags)82   ReferenceToFrameHeaderMatcher(int32_t streamid, uint8_t type,
83                                 const testing::Matcher<int> flags)
84       : FrameHeaderMatcher(streamid, type, flags) {}
85 
MatchAndExplain(const nghttp2_frame_hd & frame,testing::MatchResultListener * listener) const86   bool MatchAndExplain(const nghttp2_frame_hd& frame,
87                        testing::MatchResultListener* listener) const override {
88     return FrameHeaderMatcher::Match(frame, listener);
89   }
90 
DescribeTo(std::ostream * os) const91   void DescribeTo(std::ostream* os) const override {
92     FrameHeaderMatcher::DescribeTo(os);
93   }
94 
DescribeNegationTo(std::ostream * os) const95   void DescribeNegationTo(std::ostream* os) const override {
96     FrameHeaderMatcher::DescribeNegationTo(os);
97   }
98 };
99 
100 class DataMatcher : public testing::MatcherInterface<const nghttp2_frame*> {
101  public:
DataMatcher(const testing::Matcher<uint32_t> stream_id,const testing::Matcher<size_t> length,const testing::Matcher<int> flags,const testing::Matcher<size_t> padding)102   DataMatcher(const testing::Matcher<uint32_t> stream_id,
103               const testing::Matcher<size_t> length,
104               const testing::Matcher<int> flags,
105               const testing::Matcher<size_t> padding)
106       : stream_id_(stream_id),
107         length_(length),
108         flags_(flags),
109         padding_(padding) {}
110 
MatchAndExplain(const nghttp2_frame * frame,testing::MatchResultListener * listener) const111   bool MatchAndExplain(const nghttp2_frame* frame,
112                        testing::MatchResultListener* listener) const override {
113     if (frame->hd.type != NGHTTP2_DATA) {
114       *listener << "; expected DATA frame, saw frame of type "
115                 << static_cast<int>(frame->hd.type);
116       return false;
117     }
118     bool matched = true;
119     if (!stream_id_.MatchAndExplain(frame->hd.stream_id, listener)) {
120       matched = false;
121     }
122     if (!length_.MatchAndExplain(frame->hd.length, listener)) {
123       matched = false;
124     }
125     if (!flags_.MatchAndExplain(frame->hd.flags, listener)) {
126       matched = false;
127     }
128     if (!padding_.MatchAndExplain(frame->data.padlen, listener)) {
129       matched = false;
130     }
131     return matched;
132   }
133 
DescribeTo(std::ostream * os) const134   void DescribeTo(std::ostream* os) const override {
135     *os << "contains a DATA frame, ";
136     stream_id_.DescribeTo(os);
137     length_.DescribeTo(os);
138     flags_.DescribeTo(os);
139   }
140 
DescribeNegationTo(std::ostream * os) const141   void DescribeNegationTo(std::ostream* os) const override {
142     *os << "does not contain a DATA frame, ";
143     stream_id_.DescribeNegationTo(os);
144     length_.DescribeNegationTo(os);
145     flags_.DescribeNegationTo(os);
146   }
147 
148  private:
149   const testing::Matcher<uint32_t> stream_id_;
150   const testing::Matcher<size_t> length_;
151   const testing::Matcher<int> flags_;
152   const testing::Matcher<size_t> padding_;
153 };
154 
155 class HeadersMatcher : public testing::MatcherInterface<const nghttp2_frame*> {
156  public:
HeadersMatcher(const testing::Matcher<uint32_t> stream_id,const testing::Matcher<int> flags,const testing::Matcher<int> category)157   HeadersMatcher(const testing::Matcher<uint32_t> stream_id,
158                  const testing::Matcher<int> flags,
159                  const testing::Matcher<int> category)
160       : stream_id_(stream_id), flags_(flags), category_(category) {}
161 
MatchAndExplain(const nghttp2_frame * frame,testing::MatchResultListener * listener) const162   bool MatchAndExplain(const nghttp2_frame* frame,
163                        testing::MatchResultListener* listener) const override {
164     if (frame->hd.type != NGHTTP2_HEADERS) {
165       *listener << "; expected HEADERS frame, saw frame of type "
166                 << static_cast<int>(frame->hd.type);
167       return false;
168     }
169     bool matched = true;
170     if (!stream_id_.MatchAndExplain(frame->hd.stream_id, listener)) {
171       matched = false;
172     }
173     if (!flags_.MatchAndExplain(frame->hd.flags, listener)) {
174       matched = false;
175     }
176     if (!category_.MatchAndExplain(frame->headers.cat, listener)) {
177       matched = false;
178     }
179     return matched;
180   }
181 
DescribeTo(std::ostream * os) const182   void DescribeTo(std::ostream* os) const override {
183     *os << "contains a HEADERS frame, ";
184     stream_id_.DescribeTo(os);
185     flags_.DescribeTo(os);
186     category_.DescribeTo(os);
187   }
188 
DescribeNegationTo(std::ostream * os) const189   void DescribeNegationTo(std::ostream* os) const override {
190     *os << "does not contain a HEADERS frame, ";
191     stream_id_.DescribeNegationTo(os);
192     flags_.DescribeNegationTo(os);
193     category_.DescribeNegationTo(os);
194   }
195 
196  private:
197   const testing::Matcher<uint32_t> stream_id_;
198   const testing::Matcher<int> flags_;
199   const testing::Matcher<int> category_;
200 };
201 
202 class RstStreamMatcher
203     : public testing::MatcherInterface<const nghttp2_frame*> {
204  public:
RstStreamMatcher(const testing::Matcher<uint32_t> stream_id,const testing::Matcher<uint32_t> error_code)205   RstStreamMatcher(const testing::Matcher<uint32_t> stream_id,
206                    const testing::Matcher<uint32_t> error_code)
207       : stream_id_(stream_id), error_code_(error_code) {}
208 
MatchAndExplain(const nghttp2_frame * frame,testing::MatchResultListener * listener) const209   bool MatchAndExplain(const nghttp2_frame* frame,
210                        testing::MatchResultListener* listener) const override {
211     if (frame->hd.type != NGHTTP2_RST_STREAM) {
212       *listener << "; expected RST_STREAM frame, saw frame of type "
213                 << static_cast<int>(frame->hd.type);
214       return false;
215     }
216     bool matched = true;
217     if (!stream_id_.MatchAndExplain(frame->hd.stream_id, listener)) {
218       matched = false;
219     }
220     if (!error_code_.MatchAndExplain(frame->rst_stream.error_code, listener)) {
221       matched = false;
222     }
223     return matched;
224   }
225 
DescribeTo(std::ostream * os) const226   void DescribeTo(std::ostream* os) const override {
227     *os << "contains a RST_STREAM frame, ";
228     stream_id_.DescribeTo(os);
229     error_code_.DescribeTo(os);
230   }
231 
DescribeNegationTo(std::ostream * os) const232   void DescribeNegationTo(std::ostream* os) const override {
233     *os << "does not contain a RST_STREAM frame, ";
234     stream_id_.DescribeNegationTo(os);
235     error_code_.DescribeNegationTo(os);
236   }
237 
238  private:
239   const testing::Matcher<uint32_t> stream_id_;
240   const testing::Matcher<uint32_t> error_code_;
241 };
242 
243 class SettingsMatcher : public testing::MatcherInterface<const nghttp2_frame*> {
244  public:
SettingsMatcher(const testing::Matcher<std::vector<Http2Setting>> values)245   SettingsMatcher(const testing::Matcher<std::vector<Http2Setting>> values)
246       : values_(values) {}
247 
MatchAndExplain(const nghttp2_frame * frame,testing::MatchResultListener * listener) const248   bool MatchAndExplain(const nghttp2_frame* frame,
249                        testing::MatchResultListener* listener) const override {
250     if (frame->hd.type != NGHTTP2_SETTINGS) {
251       *listener << "; expected SETTINGS frame, saw frame of type "
252                 << static_cast<int>(frame->hd.type);
253       return false;
254     }
255     std::vector<Http2Setting> settings;
256     settings.reserve(frame->settings.niv);
257     for (size_t i = 0; i < frame->settings.niv; ++i) {
258       const auto& p = frame->settings.iv[i];
259       settings.push_back({static_cast<uint16_t>(p.settings_id), p.value});
260     }
261     return values_.MatchAndExplain(settings, listener);
262   }
263 
DescribeTo(std::ostream * os) const264   void DescribeTo(std::ostream* os) const override {
265     *os << "contains a SETTINGS frame, ";
266     values_.DescribeTo(os);
267   }
268 
DescribeNegationTo(std::ostream * os) const269   void DescribeNegationTo(std::ostream* os) const override {
270     *os << "does not contain a SETTINGS frame, ";
271     values_.DescribeNegationTo(os);
272   }
273 
274  private:
275   const testing::Matcher<std::vector<Http2Setting>> values_;
276 };
277 
278 class PingMatcher : public testing::MatcherInterface<const nghttp2_frame*> {
279  public:
PingMatcher(const testing::Matcher<uint64_t> id,bool is_ack)280   PingMatcher(const testing::Matcher<uint64_t> id, bool is_ack)
281       : id_(id), is_ack_(is_ack) {}
282 
MatchAndExplain(const nghttp2_frame * frame,testing::MatchResultListener * listener) const283   bool MatchAndExplain(const nghttp2_frame* frame,
284                        testing::MatchResultListener* listener) const override {
285     if (frame->hd.type != NGHTTP2_PING) {
286       *listener << "; expected PING frame, saw frame of type "
287                 << static_cast<int>(frame->hd.type);
288       return false;
289     }
290     bool matched = true;
291     bool frame_ack = frame->hd.flags & NGHTTP2_FLAG_ACK;
292     if (is_ack_ != frame_ack) {
293       *listener << "; expected is_ack=" << is_ack_ << ", saw " << frame_ack;
294       matched = false;
295     }
296     uint64_t data;
297     std::memcpy(&data, frame->ping.opaque_data, sizeof(data));
298     data = quiche::QuicheEndian::HostToNet64(data);
299     if (!id_.MatchAndExplain(data, listener)) {
300       matched = false;
301     }
302     return matched;
303   }
304 
DescribeTo(std::ostream * os) const305   void DescribeTo(std::ostream* os) const override {
306     *os << "contains a PING frame, ";
307     id_.DescribeTo(os);
308   }
309 
DescribeNegationTo(std::ostream * os) const310   void DescribeNegationTo(std::ostream* os) const override {
311     *os << "does not contain a PING frame, ";
312     id_.DescribeNegationTo(os);
313   }
314 
315  private:
316   const testing::Matcher<uint64_t> id_;
317   const bool is_ack_;
318 };
319 
320 class GoAwayMatcher : public testing::MatcherInterface<const nghttp2_frame*> {
321  public:
GoAwayMatcher(const testing::Matcher<uint32_t> last_stream_id,const testing::Matcher<uint32_t> error_code,const testing::Matcher<absl::string_view> opaque_data)322   GoAwayMatcher(const testing::Matcher<uint32_t> last_stream_id,
323                 const testing::Matcher<uint32_t> error_code,
324                 const testing::Matcher<absl::string_view> opaque_data)
325       : last_stream_id_(last_stream_id),
326         error_code_(error_code),
327         opaque_data_(opaque_data) {}
328 
MatchAndExplain(const nghttp2_frame * frame,testing::MatchResultListener * listener) const329   bool MatchAndExplain(const nghttp2_frame* frame,
330                        testing::MatchResultListener* listener) const override {
331     if (frame->hd.type != NGHTTP2_GOAWAY) {
332       *listener << "; expected GOAWAY frame, saw frame of type "
333                 << static_cast<int>(frame->hd.type);
334       return false;
335     }
336     bool matched = true;
337     if (!last_stream_id_.MatchAndExplain(frame->goaway.last_stream_id,
338                                          listener)) {
339       matched = false;
340     }
341     if (!error_code_.MatchAndExplain(frame->goaway.error_code, listener)) {
342       matched = false;
343     }
344     auto opaque_data =
345         ToStringView(frame->goaway.opaque_data, frame->goaway.opaque_data_len);
346     if (!opaque_data_.MatchAndExplain(opaque_data, listener)) {
347       matched = false;
348     }
349     return matched;
350   }
351 
DescribeTo(std::ostream * os) const352   void DescribeTo(std::ostream* os) const override {
353     *os << "contains a GOAWAY frame, ";
354     last_stream_id_.DescribeTo(os);
355     error_code_.DescribeTo(os);
356     opaque_data_.DescribeTo(os);
357   }
358 
DescribeNegationTo(std::ostream * os) const359   void DescribeNegationTo(std::ostream* os) const override {
360     *os << "does not contain a GOAWAY frame, ";
361     last_stream_id_.DescribeNegationTo(os);
362     error_code_.DescribeNegationTo(os);
363     opaque_data_.DescribeNegationTo(os);
364   }
365 
366  private:
367   const testing::Matcher<uint32_t> last_stream_id_;
368   const testing::Matcher<uint32_t> error_code_;
369   const testing::Matcher<absl::string_view> opaque_data_;
370 };
371 
372 class WindowUpdateMatcher
373     : public testing::MatcherInterface<const nghttp2_frame*> {
374  public:
WindowUpdateMatcher(const testing::Matcher<uint32_t> delta)375   WindowUpdateMatcher(const testing::Matcher<uint32_t> delta) : delta_(delta) {}
376 
MatchAndExplain(const nghttp2_frame * frame,testing::MatchResultListener * listener) const377   bool MatchAndExplain(const nghttp2_frame* frame,
378                        testing::MatchResultListener* listener) const override {
379     if (frame->hd.type != NGHTTP2_WINDOW_UPDATE) {
380       *listener << "; expected WINDOW_UPDATE frame, saw frame of type "
381                 << static_cast<int>(frame->hd.type);
382       return false;
383     }
384     return delta_.MatchAndExplain(frame->window_update.window_size_increment,
385                                   listener);
386   }
387 
DescribeTo(std::ostream * os) const388   void DescribeTo(std::ostream* os) const override {
389     *os << "contains a WINDOW_UPDATE frame, ";
390     delta_.DescribeTo(os);
391   }
392 
DescribeNegationTo(std::ostream * os) const393   void DescribeNegationTo(std::ostream* os) const override {
394     *os << "does not contain a WINDOW_UPDATE frame, ";
395     delta_.DescribeNegationTo(os);
396   }
397 
398  private:
399   const testing::Matcher<uint32_t> delta_;
400 };
401 
402 }  // namespace
403 
HasFrameHeader(uint32_t streamid,uint8_t type,const testing::Matcher<int> flags)404 testing::Matcher<const nghttp2_frame_hd*> HasFrameHeader(
405     uint32_t streamid, uint8_t type, const testing::Matcher<int> flags) {
406   return MakeMatcher(new PointerToFrameHeaderMatcher(streamid, type, flags));
407 }
408 
HasFrameHeaderRef(uint32_t streamid,uint8_t type,const testing::Matcher<int> flags)409 testing::Matcher<const nghttp2_frame_hd&> HasFrameHeaderRef(
410     uint32_t streamid, uint8_t type, const testing::Matcher<int> flags) {
411   return MakeMatcher(new ReferenceToFrameHeaderMatcher(streamid, type, flags));
412 }
413 
IsData(const testing::Matcher<uint32_t> stream_id,const testing::Matcher<size_t> length,const testing::Matcher<int> flags,const testing::Matcher<size_t> padding)414 testing::Matcher<const nghttp2_frame*> IsData(
415     const testing::Matcher<uint32_t> stream_id,
416     const testing::Matcher<size_t> length, const testing::Matcher<int> flags,
417     const testing::Matcher<size_t> padding) {
418   return MakeMatcher(new DataMatcher(stream_id, length, flags, padding));
419 }
420 
IsHeaders(const testing::Matcher<uint32_t> stream_id,const testing::Matcher<int> flags,const testing::Matcher<int> category)421 testing::Matcher<const nghttp2_frame*> IsHeaders(
422     const testing::Matcher<uint32_t> stream_id,
423     const testing::Matcher<int> flags, const testing::Matcher<int> category) {
424   return MakeMatcher(new HeadersMatcher(stream_id, flags, category));
425 }
426 
IsRstStream(const testing::Matcher<uint32_t> stream_id,const testing::Matcher<uint32_t> error_code)427 testing::Matcher<const nghttp2_frame*> IsRstStream(
428     const testing::Matcher<uint32_t> stream_id,
429     const testing::Matcher<uint32_t> error_code) {
430   return MakeMatcher(new RstStreamMatcher(stream_id, error_code));
431 }
432 
IsSettings(const testing::Matcher<std::vector<Http2Setting>> values)433 testing::Matcher<const nghttp2_frame*> IsSettings(
434     const testing::Matcher<std::vector<Http2Setting>> values) {
435   return MakeMatcher(new SettingsMatcher(values));
436 }
437 
IsPing(const testing::Matcher<uint64_t> id)438 testing::Matcher<const nghttp2_frame*> IsPing(
439     const testing::Matcher<uint64_t> id) {
440   return MakeMatcher(new PingMatcher(id, false));
441 }
442 
IsPingAck(const testing::Matcher<uint64_t> id)443 testing::Matcher<const nghttp2_frame*> IsPingAck(
444     const testing::Matcher<uint64_t> id) {
445   return MakeMatcher(new PingMatcher(id, true));
446 }
447 
IsGoAway(const testing::Matcher<uint32_t> last_stream_id,const testing::Matcher<uint32_t> error_code,const testing::Matcher<absl::string_view> opaque_data)448 testing::Matcher<const nghttp2_frame*> IsGoAway(
449     const testing::Matcher<uint32_t> last_stream_id,
450     const testing::Matcher<uint32_t> error_code,
451     const testing::Matcher<absl::string_view> opaque_data) {
452   return MakeMatcher(
453       new GoAwayMatcher(last_stream_id, error_code, opaque_data));
454 }
455 
IsWindowUpdate(const testing::Matcher<uint32_t> delta)456 testing::Matcher<const nghttp2_frame*> IsWindowUpdate(
457     const testing::Matcher<uint32_t> delta) {
458   return MakeMatcher(new WindowUpdateMatcher(delta));
459 }
460 
461 }  // namespace test
462 }  // namespace adapter
463 }  // namespace http2
464