| #include "quiche/http2/adapter/nghttp2_test_utils.h" | 
 |  | 
 | #include <cstring> | 
 | #include <ostream> | 
 | #include <vector> | 
 |  | 
 | #include "quiche/http2/adapter/nghttp2_util.h" | 
 | #include "quiche/common/quiche_endian.h" | 
 |  | 
 | namespace http2 { | 
 | namespace adapter { | 
 | namespace test { | 
 |  | 
 | namespace { | 
 |  | 
 | // Custom gMock matcher, used to implement HasFrameHeader(). | 
 | class FrameHeaderMatcher { | 
 |  public: | 
 |   FrameHeaderMatcher(int32_t streamid, uint8_t type, | 
 |                      const testing::Matcher<int> flags) | 
 |       : stream_id_(streamid), type_(type), flags_(flags) {} | 
 |  | 
 |   bool Match(const nghttp2_frame_hd& frame, | 
 |              testing::MatchResultListener* listener) const { | 
 |     bool matched = true; | 
 |     if (stream_id_ != frame.stream_id) { | 
 |       *listener << "; expected stream " << stream_id_ << ", saw " | 
 |                 << frame.stream_id; | 
 |       matched = false; | 
 |     } | 
 |     if (type_ != frame.type) { | 
 |       *listener << "; expected frame type " << type_ << ", saw " | 
 |                 << static_cast<int>(frame.type); | 
 |       matched = false; | 
 |     } | 
 |     if (!flags_.MatchAndExplain(frame.flags, listener)) { | 
 |       matched = false; | 
 |     } | 
 |     return matched; | 
 |   } | 
 |  | 
 |   void DescribeTo(std::ostream* os) const { | 
 |     *os << "contains a frame header with stream " << stream_id_ << ", type " | 
 |         << type_ << ", "; | 
 |     flags_.DescribeTo(os); | 
 |   } | 
 |  | 
 |   void DescribeNegationTo(std::ostream* os) const { | 
 |     *os << "does not contain a frame header with stream " << stream_id_ | 
 |         << ", type " << type_ << ", "; | 
 |     flags_.DescribeNegationTo(os); | 
 |   } | 
 |  | 
 |  private: | 
 |   const int32_t stream_id_; | 
 |   const int type_; | 
 |   const testing::Matcher<int> flags_; | 
 | }; | 
 |  | 
 | class PointerToFrameHeaderMatcher | 
 |     : public FrameHeaderMatcher, | 
 |       public testing::MatcherInterface<const nghttp2_frame_hd*> { | 
 |  public: | 
 |   PointerToFrameHeaderMatcher(int32_t streamid, uint8_t type, | 
 |                               const testing::Matcher<int> flags) | 
 |       : FrameHeaderMatcher(streamid, type, flags) {} | 
 |  | 
 |   bool MatchAndExplain(const nghttp2_frame_hd* frame, | 
 |                        testing::MatchResultListener* listener) const override { | 
 |     return FrameHeaderMatcher::Match(*frame, listener); | 
 |   } | 
 |  | 
 |   void DescribeTo(std::ostream* os) const override { | 
 |     FrameHeaderMatcher::DescribeTo(os); | 
 |   } | 
 |  | 
 |   void DescribeNegationTo(std::ostream* os) const override { | 
 |     FrameHeaderMatcher::DescribeNegationTo(os); | 
 |   } | 
 | }; | 
 |  | 
 | class ReferenceToFrameHeaderMatcher | 
 |     : public FrameHeaderMatcher, | 
 |       public testing::MatcherInterface<const nghttp2_frame_hd&> { | 
 |  public: | 
 |   ReferenceToFrameHeaderMatcher(int32_t streamid, uint8_t type, | 
 |                                 const testing::Matcher<int> flags) | 
 |       : FrameHeaderMatcher(streamid, type, flags) {} | 
 |  | 
 |   bool MatchAndExplain(const nghttp2_frame_hd& frame, | 
 |                        testing::MatchResultListener* listener) const override { | 
 |     return FrameHeaderMatcher::Match(frame, listener); | 
 |   } | 
 |  | 
 |   void DescribeTo(std::ostream* os) const override { | 
 |     FrameHeaderMatcher::DescribeTo(os); | 
 |   } | 
 |  | 
 |   void DescribeNegationTo(std::ostream* os) const override { | 
 |     FrameHeaderMatcher::DescribeNegationTo(os); | 
 |   } | 
 | }; | 
 |  | 
 | class DataMatcher : public testing::MatcherInterface<const nghttp2_frame*> { | 
 |  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) | 
 |       : stream_id_(stream_id), | 
 |         length_(length), | 
 |         flags_(flags), | 
 |         padding_(padding) {} | 
 |  | 
 |   bool MatchAndExplain(const nghttp2_frame* frame, | 
 |                        testing::MatchResultListener* listener) const override { | 
 |     if (frame->hd.type != NGHTTP2_DATA) { | 
 |       *listener << "; expected DATA frame, saw frame of type " | 
 |                 << static_cast<int>(frame->hd.type); | 
 |       return false; | 
 |     } | 
 |     bool matched = true; | 
 |     if (!stream_id_.MatchAndExplain(frame->hd.stream_id, listener)) { | 
 |       matched = false; | 
 |     } | 
 |     if (!length_.MatchAndExplain(frame->hd.length, listener)) { | 
 |       matched = false; | 
 |     } | 
 |     if (!flags_.MatchAndExplain(frame->hd.flags, listener)) { | 
 |       matched = false; | 
 |     } | 
 |     if (!padding_.MatchAndExplain(frame->data.padlen, listener)) { | 
 |       matched = false; | 
 |     } | 
 |     return matched; | 
 |   } | 
 |  | 
 |   void DescribeTo(std::ostream* os) const override { | 
 |     *os << "contains a DATA frame, "; | 
 |     stream_id_.DescribeTo(os); | 
 |     length_.DescribeTo(os); | 
 |     flags_.DescribeTo(os); | 
 |   } | 
 |  | 
 |   void DescribeNegationTo(std::ostream* os) const override { | 
 |     *os << "does not contain a DATA frame, "; | 
 |     stream_id_.DescribeNegationTo(os); | 
 |     length_.DescribeNegationTo(os); | 
 |     flags_.DescribeNegationTo(os); | 
 |   } | 
 |  | 
 |  private: | 
 |   const testing::Matcher<uint32_t> stream_id_; | 
 |   const testing::Matcher<size_t> length_; | 
 |   const testing::Matcher<int> flags_; | 
 |   const testing::Matcher<size_t> padding_; | 
 | }; | 
 |  | 
 | class HeadersMatcher : public testing::MatcherInterface<const nghttp2_frame*> { | 
 |  public: | 
 |   HeadersMatcher(const testing::Matcher<uint32_t> stream_id, | 
 |                  const testing::Matcher<int> flags, | 
 |                  const testing::Matcher<int> category) | 
 |       : stream_id_(stream_id), flags_(flags), category_(category) {} | 
 |  | 
 |   bool MatchAndExplain(const nghttp2_frame* frame, | 
 |                        testing::MatchResultListener* listener) const override { | 
 |     if (frame->hd.type != NGHTTP2_HEADERS) { | 
 |       *listener << "; expected HEADERS frame, saw frame of type " | 
 |                 << static_cast<int>(frame->hd.type); | 
 |       return false; | 
 |     } | 
 |     bool matched = true; | 
 |     if (!stream_id_.MatchAndExplain(frame->hd.stream_id, listener)) { | 
 |       matched = false; | 
 |     } | 
 |     if (!flags_.MatchAndExplain(frame->hd.flags, listener)) { | 
 |       matched = false; | 
 |     } | 
 |     if (!category_.MatchAndExplain(frame->headers.cat, listener)) { | 
 |       matched = false; | 
 |     } | 
 |     return matched; | 
 |   } | 
 |  | 
 |   void DescribeTo(std::ostream* os) const override { | 
 |     *os << "contains a HEADERS frame, "; | 
 |     stream_id_.DescribeTo(os); | 
 |     flags_.DescribeTo(os); | 
 |     category_.DescribeTo(os); | 
 |   } | 
 |  | 
 |   void DescribeNegationTo(std::ostream* os) const override { | 
 |     *os << "does not contain a HEADERS frame, "; | 
 |     stream_id_.DescribeNegationTo(os); | 
 |     flags_.DescribeNegationTo(os); | 
 |     category_.DescribeNegationTo(os); | 
 |   } | 
 |  | 
 |  private: | 
 |   const testing::Matcher<uint32_t> stream_id_; | 
 |   const testing::Matcher<int> flags_; | 
 |   const testing::Matcher<int> category_; | 
 | }; | 
 |  | 
 | class RstStreamMatcher | 
 |     : public testing::MatcherInterface<const nghttp2_frame*> { | 
 |  public: | 
 |   RstStreamMatcher(const testing::Matcher<uint32_t> stream_id, | 
 |                    const testing::Matcher<uint32_t> error_code) | 
 |       : stream_id_(stream_id), error_code_(error_code) {} | 
 |  | 
 |   bool MatchAndExplain(const nghttp2_frame* frame, | 
 |                        testing::MatchResultListener* listener) const override { | 
 |     if (frame->hd.type != NGHTTP2_RST_STREAM) { | 
 |       *listener << "; expected RST_STREAM frame, saw frame of type " | 
 |                 << static_cast<int>(frame->hd.type); | 
 |       return false; | 
 |     } | 
 |     bool matched = true; | 
 |     if (!stream_id_.MatchAndExplain(frame->hd.stream_id, listener)) { | 
 |       matched = false; | 
 |     } | 
 |     if (!error_code_.MatchAndExplain(frame->rst_stream.error_code, listener)) { | 
 |       matched = false; | 
 |     } | 
 |     return matched; | 
 |   } | 
 |  | 
 |   void DescribeTo(std::ostream* os) const override { | 
 |     *os << "contains a RST_STREAM frame, "; | 
 |     stream_id_.DescribeTo(os); | 
 |     error_code_.DescribeTo(os); | 
 |   } | 
 |  | 
 |   void DescribeNegationTo(std::ostream* os) const override { | 
 |     *os << "does not contain a RST_STREAM frame, "; | 
 |     stream_id_.DescribeNegationTo(os); | 
 |     error_code_.DescribeNegationTo(os); | 
 |   } | 
 |  | 
 |  private: | 
 |   const testing::Matcher<uint32_t> stream_id_; | 
 |   const testing::Matcher<uint32_t> error_code_; | 
 | }; | 
 |  | 
 | class SettingsMatcher : public testing::MatcherInterface<const nghttp2_frame*> { | 
 |  public: | 
 |   SettingsMatcher(const testing::Matcher<std::vector<Http2Setting>> values) | 
 |       : values_(values) {} | 
 |  | 
 |   bool MatchAndExplain(const nghttp2_frame* frame, | 
 |                        testing::MatchResultListener* listener) const override { | 
 |     if (frame->hd.type != NGHTTP2_SETTINGS) { | 
 |       *listener << "; expected SETTINGS frame, saw frame of type " | 
 |                 << static_cast<int>(frame->hd.type); | 
 |       return false; | 
 |     } | 
 |     std::vector<Http2Setting> settings; | 
 |     settings.reserve(frame->settings.niv); | 
 |     for (size_t i = 0; i < frame->settings.niv; ++i) { | 
 |       const auto& p = frame->settings.iv[i]; | 
 |       settings.push_back({static_cast<uint16_t>(p.settings_id), p.value}); | 
 |     } | 
 |     return values_.MatchAndExplain(settings, listener); | 
 |   } | 
 |  | 
 |   void DescribeTo(std::ostream* os) const override { | 
 |     *os << "contains a SETTINGS frame, "; | 
 |     values_.DescribeTo(os); | 
 |   } | 
 |  | 
 |   void DescribeNegationTo(std::ostream* os) const override { | 
 |     *os << "does not contain a SETTINGS frame, "; | 
 |     values_.DescribeNegationTo(os); | 
 |   } | 
 |  | 
 |  private: | 
 |   const testing::Matcher<std::vector<Http2Setting>> values_; | 
 | }; | 
 |  | 
 | class PingMatcher : public testing::MatcherInterface<const nghttp2_frame*> { | 
 |  public: | 
 |   PingMatcher(const testing::Matcher<uint64_t> id, bool is_ack) | 
 |       : id_(id), is_ack_(is_ack) {} | 
 |  | 
 |   bool MatchAndExplain(const nghttp2_frame* frame, | 
 |                        testing::MatchResultListener* listener) const override { | 
 |     if (frame->hd.type != NGHTTP2_PING) { | 
 |       *listener << "; expected PING frame, saw frame of type " | 
 |                 << static_cast<int>(frame->hd.type); | 
 |       return false; | 
 |     } | 
 |     bool matched = true; | 
 |     bool frame_ack = frame->hd.flags & NGHTTP2_FLAG_ACK; | 
 |     if (is_ack_ != frame_ack) { | 
 |       *listener << "; expected is_ack=" << is_ack_ << ", saw " << frame_ack; | 
 |       matched = false; | 
 |     } | 
 |     uint64_t data; | 
 |     std::memcpy(&data, frame->ping.opaque_data, sizeof(data)); | 
 |     data = quiche::QuicheEndian::HostToNet64(data); | 
 |     if (!id_.MatchAndExplain(data, listener)) { | 
 |       matched = false; | 
 |     } | 
 |     return matched; | 
 |   } | 
 |  | 
 |   void DescribeTo(std::ostream* os) const override { | 
 |     *os << "contains a PING frame, "; | 
 |     id_.DescribeTo(os); | 
 |   } | 
 |  | 
 |   void DescribeNegationTo(std::ostream* os) const override { | 
 |     *os << "does not contain a PING frame, "; | 
 |     id_.DescribeNegationTo(os); | 
 |   } | 
 |  | 
 |  private: | 
 |   const testing::Matcher<uint64_t> id_; | 
 |   const bool is_ack_; | 
 | }; | 
 |  | 
 | class GoAwayMatcher : public testing::MatcherInterface<const nghttp2_frame*> { | 
 |  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) | 
 |       : last_stream_id_(last_stream_id), | 
 |         error_code_(error_code), | 
 |         opaque_data_(opaque_data) {} | 
 |  | 
 |   bool MatchAndExplain(const nghttp2_frame* frame, | 
 |                        testing::MatchResultListener* listener) const override { | 
 |     if (frame->hd.type != NGHTTP2_GOAWAY) { | 
 |       *listener << "; expected GOAWAY frame, saw frame of type " | 
 |                 << static_cast<int>(frame->hd.type); | 
 |       return false; | 
 |     } | 
 |     bool matched = true; | 
 |     if (!last_stream_id_.MatchAndExplain(frame->goaway.last_stream_id, | 
 |                                          listener)) { | 
 |       matched = false; | 
 |     } | 
 |     if (!error_code_.MatchAndExplain(frame->goaway.error_code, listener)) { | 
 |       matched = false; | 
 |     } | 
 |     auto opaque_data = | 
 |         ToStringView(frame->goaway.opaque_data, frame->goaway.opaque_data_len); | 
 |     if (!opaque_data_.MatchAndExplain(opaque_data, listener)) { | 
 |       matched = false; | 
 |     } | 
 |     return matched; | 
 |   } | 
 |  | 
 |   void DescribeTo(std::ostream* os) const override { | 
 |     *os << "contains a GOAWAY frame, "; | 
 |     last_stream_id_.DescribeTo(os); | 
 |     error_code_.DescribeTo(os); | 
 |     opaque_data_.DescribeTo(os); | 
 |   } | 
 |  | 
 |   void DescribeNegationTo(std::ostream* os) const override { | 
 |     *os << "does not contain a GOAWAY frame, "; | 
 |     last_stream_id_.DescribeNegationTo(os); | 
 |     error_code_.DescribeNegationTo(os); | 
 |     opaque_data_.DescribeNegationTo(os); | 
 |   } | 
 |  | 
 |  private: | 
 |   const testing::Matcher<uint32_t> last_stream_id_; | 
 |   const testing::Matcher<uint32_t> error_code_; | 
 |   const testing::Matcher<absl::string_view> opaque_data_; | 
 | }; | 
 |  | 
 | class WindowUpdateMatcher | 
 |     : public testing::MatcherInterface<const nghttp2_frame*> { | 
 |  public: | 
 |   WindowUpdateMatcher(const testing::Matcher<uint32_t> delta) : delta_(delta) {} | 
 |  | 
 |   bool MatchAndExplain(const nghttp2_frame* frame, | 
 |                        testing::MatchResultListener* listener) const override { | 
 |     if (frame->hd.type != NGHTTP2_WINDOW_UPDATE) { | 
 |       *listener << "; expected WINDOW_UPDATE frame, saw frame of type " | 
 |                 << static_cast<int>(frame->hd.type); | 
 |       return false; | 
 |     } | 
 |     return delta_.MatchAndExplain(frame->window_update.window_size_increment, | 
 |                                   listener); | 
 |   } | 
 |  | 
 |   void DescribeTo(std::ostream* os) const override { | 
 |     *os << "contains a WINDOW_UPDATE frame, "; | 
 |     delta_.DescribeTo(os); | 
 |   } | 
 |  | 
 |   void DescribeNegationTo(std::ostream* os) const override { | 
 |     *os << "does not contain a WINDOW_UPDATE frame, "; | 
 |     delta_.DescribeNegationTo(os); | 
 |   } | 
 |  | 
 |  private: | 
 |   const testing::Matcher<uint32_t> delta_; | 
 | }; | 
 |  | 
 | }  // namespace | 
 |  | 
 | testing::Matcher<const nghttp2_frame_hd*> HasFrameHeader( | 
 |     uint32_t streamid, uint8_t type, const testing::Matcher<int> flags) { | 
 |   return MakeMatcher(new PointerToFrameHeaderMatcher(streamid, type, flags)); | 
 | } | 
 |  | 
 | testing::Matcher<const nghttp2_frame_hd&> HasFrameHeaderRef( | 
 |     uint32_t streamid, uint8_t type, const testing::Matcher<int> flags) { | 
 |   return MakeMatcher(new ReferenceToFrameHeaderMatcher(streamid, type, flags)); | 
 | } | 
 |  | 
 | testing::Matcher<const nghttp2_frame*> 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) { | 
 |   return MakeMatcher(new DataMatcher(stream_id, length, flags, padding)); | 
 | } | 
 |  | 
 | testing::Matcher<const nghttp2_frame*> IsHeaders( | 
 |     const testing::Matcher<uint32_t> stream_id, | 
 |     const testing::Matcher<int> flags, const testing::Matcher<int> category) { | 
 |   return MakeMatcher(new HeadersMatcher(stream_id, flags, category)); | 
 | } | 
 |  | 
 | testing::Matcher<const nghttp2_frame*> IsRstStream( | 
 |     const testing::Matcher<uint32_t> stream_id, | 
 |     const testing::Matcher<uint32_t> error_code) { | 
 |   return MakeMatcher(new RstStreamMatcher(stream_id, error_code)); | 
 | } | 
 |  | 
 | testing::Matcher<const nghttp2_frame*> IsSettings( | 
 |     const testing::Matcher<std::vector<Http2Setting>> values) { | 
 |   return MakeMatcher(new SettingsMatcher(values)); | 
 | } | 
 |  | 
 | testing::Matcher<const nghttp2_frame*> IsPing( | 
 |     const testing::Matcher<uint64_t> id) { | 
 |   return MakeMatcher(new PingMatcher(id, false)); | 
 | } | 
 |  | 
 | testing::Matcher<const nghttp2_frame*> IsPingAck( | 
 |     const testing::Matcher<uint64_t> id) { | 
 |   return MakeMatcher(new PingMatcher(id, true)); | 
 | } | 
 |  | 
 | testing::Matcher<const nghttp2_frame*> IsGoAway( | 
 |     const testing::Matcher<uint32_t> last_stream_id, | 
 |     const testing::Matcher<uint32_t> error_code, | 
 |     const testing::Matcher<absl::string_view> opaque_data) { | 
 |   return MakeMatcher( | 
 |       new GoAwayMatcher(last_stream_id, error_code, opaque_data)); | 
 | } | 
 |  | 
 | testing::Matcher<const nghttp2_frame*> IsWindowUpdate( | 
 |     const testing::Matcher<uint32_t> delta) { | 
 |   return MakeMatcher(new WindowUpdateMatcher(delta)); | 
 | } | 
 |  | 
 | }  // namespace test | 
 | }  // namespace adapter | 
 | }  // namespace http2 |