#pragma once #include #include #include #include #include #include #include "envoy/access_log/access_log.h" #include "envoy/http/async_client.h" #include "envoy/http/codec.h" #include "envoy/http/conn_pool.h" #include "envoy/http/filter.h" #include "envoy/ssl/connection.h" #include "common/http/filter_manager.h" #include "common/http/header_map_impl.h" #include "common/http/utility.h" #include "test/mocks/common.h" #include "test/mocks/event/mocks.h" #include "test/mocks/http/conn_pool.h" #include "test/mocks/http/stream.h" #include "test/mocks/http/stream_decoder.h" #include "test/mocks/http/stream_encoder.h" #include "test/mocks/router/mocks.h" #include "test/mocks/stream_info/mocks.h" #include "test/mocks/tracing/mocks.h" #include "test/mocks/upstream/cluster_info.h" #include "test/mocks/upstream/host.h" #include "test/test_common/printers.h" #include "absl/strings/ascii.h" #include "absl/strings/str_cat.h" #include "absl/strings/str_join.h" #include "gmock/gmock.h" namespace Envoy { namespace Http { class MockConnectionCallbacks : public virtual ConnectionCallbacks { public: MockConnectionCallbacks(); ~MockConnectionCallbacks() override; // Http::ConnectionCallbacks MOCK_METHOD(void, onGoAway, (GoAwayErrorCode error_code)); }; class MockFilterManagerCallbacks : public FilterManagerCallbacks { public: MockFilterManagerCallbacks(); ~MockFilterManagerCallbacks() override; MOCK_METHOD(void, encodeHeaders, (ResponseHeaderMap&, bool)); MOCK_METHOD(void, encode100ContinueHeaders, (ResponseHeaderMap&)); MOCK_METHOD(void, encodeData, (Buffer::Instance&, bool)); MOCK_METHOD(void, encodeTrailers, (ResponseTrailerMap&)); MOCK_METHOD(void, encodeMetadata, (MetadataMapVector&)); MOCK_METHOD(void, chargeStats, (const ResponseHeaderMap&)); MOCK_METHOD(void, setRequestTrailers, (RequestTrailerMapPtr &&)); MOCK_METHOD(void, setContinueHeaders, (ResponseHeaderMapPtr &&)); MOCK_METHOD(void, setResponseHeaders_, (ResponseHeaderMap&)); void setResponseHeaders(ResponseHeaderMapPtr&& response_headers) override { // TODO(snowp): Repeat this pattern for all setters. response_headers_ = std::move(response_headers); setResponseHeaders_(*response_headers_); } MOCK_METHOD(void, setResponseTrailers, (ResponseTrailerMapPtr &&)); MOCK_METHOD(RequestHeaderMapOptRef, requestHeaders, ()); MOCK_METHOD(RequestTrailerMapOptRef, requestTrailers, ()); MOCK_METHOD(ResponseHeaderMapOptRef, continueHeaders, ()); MOCK_METHOD(ResponseHeaderMapOptRef, responseHeaders, ()); MOCK_METHOD(ResponseTrailerMapOptRef, responseTrailers, ()); MOCK_METHOD(void, endStream, ()); MOCK_METHOD(void, onDecoderFilterBelowWriteBufferLowWatermark, ()); MOCK_METHOD(void, onDecoderFilterAboveWriteBufferHighWatermark, ()); MOCK_METHOD(void, upgradeFilterChainCreated, ()); MOCK_METHOD(void, disarmRequestTimeout, ()); MOCK_METHOD(void, resetIdleTimer, ()); MOCK_METHOD(void, recreateStream, (StreamInfo::FilterStateSharedPtr filter_state)); MOCK_METHOD(void, resetStream, ()); MOCK_METHOD(const Router::RouteEntry::UpgradeMap*, upgradeMap, ()); MOCK_METHOD(Upstream::ClusterInfoConstSharedPtr, clusterInfo, ()); MOCK_METHOD(Router::RouteConstSharedPtr, route, (const Router::RouteCallback& cb)); MOCK_METHOD(void, clearRouteCache, ()); MOCK_METHOD(absl::optional, routeConfig, ()); MOCK_METHOD(void, requestRouteConfigUpdate, (Http::RouteConfigUpdatedCallbackSharedPtr)); MOCK_METHOD(Tracing::Span&, activeSpan, ()); MOCK_METHOD(void, onResponseDataTooLarge, ()); MOCK_METHOD(void, onRequestDataTooLarge, ()); MOCK_METHOD(Http1StreamEncoderOptionsOptRef, http1StreamEncoderOptions, ()); MOCK_METHOD(void, onLocalReply, (Code code)); MOCK_METHOD(Tracing::Config&, tracingConfig, ()); MOCK_METHOD(const ScopeTrackedObject&, scope, ()); ResponseHeaderMapPtr response_headers_; }; class MockServerConnectionCallbacks : public ServerConnectionCallbacks, public MockConnectionCallbacks { public: MockServerConnectionCallbacks(); ~MockServerConnectionCallbacks() override; // Http::ServerConnectionCallbacks MOCK_METHOD(RequestDecoder&, newStream, (ResponseEncoder & response_encoder, bool is_internally_created)); }; class MockStreamCallbacks : public StreamCallbacks { public: MockStreamCallbacks(); ~MockStreamCallbacks() override; // Http::StreamCallbacks MOCK_METHOD(void, onResetStream, (StreamResetReason reason, absl::string_view)); MOCK_METHOD(void, onAboveWriteBufferHighWatermark, ()); MOCK_METHOD(void, onBelowWriteBufferLowWatermark, ()); }; class MockServerConnection : public ServerConnection { public: MockServerConnection(); ~MockServerConnection() override; // Http::Connection MOCK_METHOD(Status, dispatch, (Buffer::Instance & data)); MOCK_METHOD(void, goAway, ()); MOCK_METHOD(Protocol, protocol, ()); MOCK_METHOD(void, shutdownNotice, ()); MOCK_METHOD(bool, wantsToWrite, ()); MOCK_METHOD(void, onUnderlyingConnectionAboveWriteBufferHighWatermark, ()); MOCK_METHOD(void, onUnderlyingConnectionBelowWriteBufferLowWatermark, ()); Protocol protocol_{Protocol::Http11}; }; class MockClientConnection : public ClientConnection { public: MockClientConnection(); ~MockClientConnection() override; // Http::Connection MOCK_METHOD(Status, dispatch, (Buffer::Instance & data)); MOCK_METHOD(void, goAway, ()); MOCK_METHOD(Protocol, protocol, ()); MOCK_METHOD(void, shutdownNotice, ()); MOCK_METHOD(bool, wantsToWrite, ()); MOCK_METHOD(void, onUnderlyingConnectionAboveWriteBufferHighWatermark, ()); MOCK_METHOD(void, onUnderlyingConnectionBelowWriteBufferLowWatermark, ()); // Http::ClientConnection MOCK_METHOD(RequestEncoder&, newStream, (ResponseDecoder & response_decoder)); }; class MockFilterChainFactory : public FilterChainFactory { public: MockFilterChainFactory(); ~MockFilterChainFactory() override; // Http::FilterChainFactory MOCK_METHOD(void, createFilterChain, (FilterChainFactoryCallbacks & callbacks)); MOCK_METHOD(bool, createUpgradeFilterChain, (absl::string_view upgrade_type, const FilterChainFactory::UpgradeMap* upgrade_map, FilterChainFactoryCallbacks& callbacks)); }; class MockStreamFilterCallbacksBase { public: Event::MockDispatcher dispatcher_; testing::NiceMock stream_info_; std::shared_ptr route_; std::shared_ptr cluster_info_; }; class MockStreamDecoderFilterCallbacks : public StreamDecoderFilterCallbacks, public MockStreamFilterCallbacksBase { public: MockStreamDecoderFilterCallbacks(); ~MockStreamDecoderFilterCallbacks() override; // Http::StreamFilterCallbacks MOCK_METHOD(const Network::Connection*, connection, ()); MOCK_METHOD(Event::Dispatcher&, dispatcher, ()); MOCK_METHOD(void, resetStream, ()); MOCK_METHOD(Upstream::ClusterInfoConstSharedPtr, clusterInfo, ()); MOCK_METHOD(Router::RouteConstSharedPtr, route, ()); MOCK_METHOD(Router::RouteConstSharedPtr, route, (const Router::RouteCallback&)); MOCK_METHOD(void, requestRouteConfigUpdate, (Http::RouteConfigUpdatedCallbackSharedPtr)); MOCK_METHOD(absl::optional, routeConfig, ()); MOCK_METHOD(void, clearRouteCache, ()); MOCK_METHOD(uint64_t, streamId, (), (const)); MOCK_METHOD(StreamInfo::StreamInfo&, streamInfo, ()); MOCK_METHOD(Tracing::Span&, activeSpan, ()); MOCK_METHOD(Tracing::Config&, tracingConfig, ()); MOCK_METHOD(const ScopeTrackedObject&, scope, ()); MOCK_METHOD(void, onDecoderFilterAboveWriteBufferHighWatermark, ()); MOCK_METHOD(void, onDecoderFilterBelowWriteBufferLowWatermark, ()); MOCK_METHOD(void, addDownstreamWatermarkCallbacks, (DownstreamWatermarkCallbacks&)); MOCK_METHOD(void, removeDownstreamWatermarkCallbacks, (DownstreamWatermarkCallbacks&)); MOCK_METHOD(void, setDecoderBufferLimit, (uint32_t)); MOCK_METHOD(uint32_t, decoderBufferLimit, ()); MOCK_METHOD(bool, recreateStream, (const ResponseHeaderMap* headers)); MOCK_METHOD(void, addUpstreamSocketOptions, (const Network::Socket::OptionsSharedPtr& options)); MOCK_METHOD(Network::Socket::OptionsSharedPtr, getUpstreamSocketOptions, (), (const)); // Http::StreamDecoderFilterCallbacks void sendLocalReply_(Code code, absl::string_view body, std::function modify_headers, const absl::optional grpc_status, absl::string_view details); void encode100ContinueHeaders(ResponseHeaderMapPtr&& headers) override { encode100ContinueHeaders_(*headers); } void encodeHeaders(ResponseHeaderMapPtr&& headers, bool end_stream, absl::string_view details) override { stream_info_.setResponseCodeDetails(details); encodeHeaders_(*headers, end_stream); } void encodeTrailers(ResponseTrailerMapPtr&& trailers) override { encodeTrailers_(*trailers); } void encodeMetadata(MetadataMapPtr&& metadata_map) override { encodeMetadata_(std::move(metadata_map)); } absl::string_view details() { if (stream_info_.responseCodeDetails()) { return stream_info_.responseCodeDetails().value(); } return ""; } MOCK_METHOD(void, continueDecoding, ()); MOCK_METHOD(void, addDecodedData, (Buffer::Instance & data, bool streaming)); MOCK_METHOD(void, injectDecodedDataToFilterChain, (Buffer::Instance & data, bool end_stream)); MOCK_METHOD(RequestTrailerMap&, addDecodedTrailers, ()); MOCK_METHOD(MetadataMapVector&, addDecodedMetadata, ()); MOCK_METHOD(const Buffer::Instance*, decodingBuffer, ()); MOCK_METHOD(void, modifyDecodingBuffer, (std::function)); MOCK_METHOD(void, encode100ContinueHeaders_, (HeaderMap & headers)); MOCK_METHOD(void, encodeHeaders_, (ResponseHeaderMap & headers, bool end_stream)); MOCK_METHOD(void, encodeData, (Buffer::Instance & data, bool end_stream)); MOCK_METHOD(void, encodeTrailers_, (ResponseTrailerMap & trailers)); MOCK_METHOD(void, encodeMetadata_, (MetadataMapPtr metadata_map)); MOCK_METHOD(void, sendLocalReply, (Code code, absl::string_view body, std::function modify_headers, const absl::optional grpc_status, absl::string_view details)); Buffer::InstancePtr buffer_; std::list callbacks_{}; testing::NiceMock active_span_; testing::NiceMock tracing_config_; testing::NiceMock scope_; bool is_grpc_request_{}; bool is_head_request_{false}; bool stream_destroyed_{}; }; class MockStreamEncoderFilterCallbacks : public StreamEncoderFilterCallbacks, public MockStreamFilterCallbacksBase { public: MockStreamEncoderFilterCallbacks(); ~MockStreamEncoderFilterCallbacks() override; // Http::StreamFilterCallbacks MOCK_METHOD(const Network::Connection*, connection, ()); MOCK_METHOD(Event::Dispatcher&, dispatcher, ()); MOCK_METHOD(void, resetStream, ()); MOCK_METHOD(Upstream::ClusterInfoConstSharedPtr, clusterInfo, ()); MOCK_METHOD(void, requestRouteConfigUpdate, (std::function)); MOCK_METHOD(bool, canRequestRouteConfigUpdate, ()); MOCK_METHOD(Router::RouteConstSharedPtr, route, ()); MOCK_METHOD(Router::RouteConstSharedPtr, route, (const Router::RouteCallback&)); MOCK_METHOD(void, clearRouteCache, ()); MOCK_METHOD(uint64_t, streamId, (), (const)); MOCK_METHOD(StreamInfo::StreamInfo&, streamInfo, ()); MOCK_METHOD(Tracing::Span&, activeSpan, ()); MOCK_METHOD(Tracing::Config&, tracingConfig, ()); MOCK_METHOD(const ScopeTrackedObject&, scope, ()); MOCK_METHOD(void, onEncoderFilterAboveWriteBufferHighWatermark, ()); MOCK_METHOD(void, onEncoderFilterBelowWriteBufferLowWatermark, ()); MOCK_METHOD(void, setEncoderBufferLimit, (uint32_t)); MOCK_METHOD(uint32_t, encoderBufferLimit, ()); // Http::StreamEncoderFilterCallbacks MOCK_METHOD(void, addEncodedData, (Buffer::Instance & data, bool streaming)); MOCK_METHOD(void, injectEncodedDataToFilterChain, (Buffer::Instance & data, bool end_stream)); MOCK_METHOD(ResponseTrailerMap&, addEncodedTrailers, ()); MOCK_METHOD(void, addEncodedMetadata, (Http::MetadataMapPtr &&)); MOCK_METHOD(void, continueEncoding, ()); MOCK_METHOD(const Buffer::Instance*, encodingBuffer, ()); MOCK_METHOD(void, modifyEncodingBuffer, (std::function)); MOCK_METHOD(void, sendLocalReply, (Code code, absl::string_view body, std::function modify_headers, const absl::optional grpc_status, absl::string_view details)); MOCK_METHOD(Http1StreamEncoderOptionsOptRef, http1StreamEncoderOptions, ()); Buffer::InstancePtr buffer_; testing::NiceMock active_span_; testing::NiceMock tracing_config_; testing::NiceMock scope_; }; class MockStreamDecoderFilter : public StreamDecoderFilter { public: MockStreamDecoderFilter(); ~MockStreamDecoderFilter() override; // Http::StreamFilterBase MOCK_METHOD(void, onStreamComplete, ()); MOCK_METHOD(void, onDestroy, ()); // Http::StreamDecoderFilter MOCK_METHOD(FilterHeadersStatus, decodeHeaders, (RequestHeaderMap & headers, bool end_stream)); MOCK_METHOD(FilterDataStatus, decodeData, (Buffer::Instance & data, bool end_stream)); MOCK_METHOD(FilterTrailersStatus, decodeTrailers, (RequestTrailerMap & trailers)); MOCK_METHOD(FilterMetadataStatus, decodeMetadata, (Http::MetadataMap & metadata_map)); MOCK_METHOD(void, setDecoderFilterCallbacks, (StreamDecoderFilterCallbacks & callbacks)); MOCK_METHOD(void, decodeComplete, ()); MOCK_METHOD(void, sendLocalReply, (bool is_grpc_request, Code code, absl::string_view body, const std::function& modify_headers, bool is_head_request, const absl::optional grpc_status, absl::string_view details)); Http::StreamDecoderFilterCallbacks* callbacks_{}; }; class MockStreamEncoderFilter : public StreamEncoderFilter { public: MockStreamEncoderFilter(); ~MockStreamEncoderFilter() override; // Http::StreamFilterBase MOCK_METHOD(void, onStreamComplete, ()); MOCK_METHOD(void, onDestroy, ()); // Http::MockStreamEncoderFilter MOCK_METHOD(FilterHeadersStatus, encode100ContinueHeaders, (ResponseHeaderMap & headers)); MOCK_METHOD(FilterHeadersStatus, encodeHeaders, (ResponseHeaderMap & headers, bool end_stream)); MOCK_METHOD(FilterDataStatus, encodeData, (Buffer::Instance & data, bool end_stream)); MOCK_METHOD(FilterTrailersStatus, encodeTrailers, (ResponseTrailerMap & trailers)); MOCK_METHOD(FilterMetadataStatus, encodeMetadata, (MetadataMap & metadata_map)); MOCK_METHOD(void, setEncoderFilterCallbacks, (StreamEncoderFilterCallbacks & callbacks)); MOCK_METHOD(void, encodeComplete, ()); Http::StreamEncoderFilterCallbacks* callbacks_{}; }; class MockStreamFilter : public StreamFilter { public: MockStreamFilter(); ~MockStreamFilter() override; // Http::StreamFilterBase MOCK_METHOD(void, onStreamComplete, ()); MOCK_METHOD(void, onDestroy, ()); // Http::StreamDecoderFilter MOCK_METHOD(FilterHeadersStatus, decodeHeaders, (RequestHeaderMap & headers, bool end_stream)); MOCK_METHOD(FilterDataStatus, decodeData, (Buffer::Instance & data, bool end_stream)); MOCK_METHOD(FilterTrailersStatus, decodeTrailers, (RequestTrailerMap & trailers)); MOCK_METHOD(FilterMetadataStatus, decodeMetadata, (Http::MetadataMap & metadata_map)); MOCK_METHOD(void, setDecoderFilterCallbacks, (StreamDecoderFilterCallbacks & callbacks)); // Http::MockStreamEncoderFilter MOCK_METHOD(FilterHeadersStatus, encode100ContinueHeaders, (ResponseHeaderMap & headers)); MOCK_METHOD(FilterHeadersStatus, encodeHeaders, (ResponseHeaderMap & headers, bool end_stream)); MOCK_METHOD(FilterDataStatus, encodeData, (Buffer::Instance & data, bool end_stream)); MOCK_METHOD(FilterTrailersStatus, encodeTrailers, (ResponseTrailerMap & trailers)); MOCK_METHOD(FilterMetadataStatus, encodeMetadata, (MetadataMap & metadata_map)); MOCK_METHOD(void, setEncoderFilterCallbacks, (StreamEncoderFilterCallbacks & callbacks)); Http::StreamDecoderFilterCallbacks* decoder_callbacks_{}; Http::StreamEncoderFilterCallbacks* encoder_callbacks_{}; }; class MockAsyncClient : public AsyncClient { public: MockAsyncClient(); ~MockAsyncClient() override; MOCK_METHOD(void, onRequestDestroy, ()); // Http::AsyncClient Request* send(RequestMessagePtr&& request, Callbacks& callbacks, const RequestOptions& args) override { return send_(request, callbacks, args); } MOCK_METHOD(Request*, send_, (RequestMessagePtr & request, Callbacks& callbacks, const RequestOptions& args)); MOCK_METHOD(Stream*, start, (StreamCallbacks & callbacks, const StreamOptions& args)); MOCK_METHOD(Event::Dispatcher&, dispatcher, ()); NiceMock dispatcher_; }; class MockAsyncClientCallbacks : public AsyncClient::Callbacks { public: MockAsyncClientCallbacks(); ~MockAsyncClientCallbacks() override; void onSuccess(const Http::AsyncClient::Request& request, ResponseMessagePtr&& response) override { onSuccess_(request, response.get()); } // Http::AsyncClient::Callbacks MOCK_METHOD(void, onSuccess_, (const Http::AsyncClient::Request&, ResponseMessage*)); MOCK_METHOD(void, onFailure, (const Http::AsyncClient::Request&, Http::AsyncClient::FailureReason)); MOCK_METHOD(void, onBeforeFinalizeUpstreamSpan, (Envoy::Tracing::Span&, const Http::ResponseHeaderMap*)); }; class MockAsyncClientStreamCallbacks : public AsyncClient::StreamCallbacks { public: MockAsyncClientStreamCallbacks(); ~MockAsyncClientStreamCallbacks() override; void onHeaders(ResponseHeaderMapPtr&& headers, bool end_stream) override { onHeaders_(*headers, end_stream); } void onTrailers(ResponseTrailerMapPtr&& trailers) override { onTrailers_(*trailers); } MOCK_METHOD(void, onHeaders_, (ResponseHeaderMap & headers, bool end_stream)); MOCK_METHOD(void, onData, (Buffer::Instance & data, bool end_stream)); MOCK_METHOD(void, onTrailers_, (ResponseTrailerMap & headers)); MOCK_METHOD(void, onComplete, ()); MOCK_METHOD(void, onReset, ()); }; class MockAsyncClientRequest : public AsyncClient::Request { public: MockAsyncClientRequest(MockAsyncClient* client); ~MockAsyncClientRequest() override; MOCK_METHOD(void, cancel, ()); MockAsyncClient* client_; }; class MockAsyncClientStream : public AsyncClient::Stream { public: MockAsyncClientStream(); ~MockAsyncClientStream() override; MOCK_METHOD(void, sendHeaders, (RequestHeaderMap & headers, bool end_stream)); MOCK_METHOD(void, sendData, (Buffer::Instance & data, bool end_stream)); MOCK_METHOD(void, sendTrailers, (RequestTrailerMap & trailers)); MOCK_METHOD(void, reset, ()); MOCK_METHOD(bool, isAboveWriteBufferHighWatermark, (), (const)); }; class MockFilterChainFactoryCallbacks : public Http::FilterChainFactoryCallbacks { public: MockFilterChainFactoryCallbacks(); ~MockFilterChainFactoryCallbacks() override; MOCK_METHOD(void, addStreamDecoderFilter, (Http::StreamDecoderFilterSharedPtr filter)); MOCK_METHOD(void, addStreamEncoderFilter, (Http::StreamEncoderFilterSharedPtr filter)); MOCK_METHOD(void, addStreamFilter, (Http::StreamFilterSharedPtr filter)); MOCK_METHOD(void, addAccessLogHandler, (AccessLog::InstanceSharedPtr handler)); }; class MockDownstreamWatermarkCallbacks : public DownstreamWatermarkCallbacks { public: MOCK_METHOD(void, onAboveWriteBufferHighWatermark, ()); MOCK_METHOD(void, onBelowWriteBufferLowWatermark, ()); }; } // namespace Http namespace Http { template class HeaderValueOfMatcherImpl : public testing::MatcherInterface { public: explicit HeaderValueOfMatcherImpl(LowerCaseString key, testing::Matcher matcher) : key_(std::move(key)), matcher_(std::move(matcher)) {} // NOLINTNEXTLINE(readability-identifier-naming) bool MatchAndExplain(HeaderMapT headers, testing::MatchResultListener* listener) const override { // Get all headers with matching keys. std::vector values; Envoy::Http::HeaderMap::ConstIterateCb get_headers_cb = [key = key_.get(), &values](const Envoy::Http::HeaderEntry& header) { if (header.key().getStringView() == key) { values.push_back(header.value().getStringView()); } return Envoy::Http::HeaderMap::Iterate::Continue; }; headers.iterate(get_headers_cb); if (values.empty()) { *listener << "which has no '" << key_.get() << "' header"; return false; } else if (values.size() > 1) { *listener << "which has " << values.size() << " '" << key_.get() << "' headers, with values: " << absl::StrJoin(values, ", "); return false; } absl::string_view value = values[0]; *listener << "which has a '" << key_.get() << "' header with value " << value << " "; return testing::ExplainMatchResult(matcher_, value, listener); } void DescribeTo(std::ostream* os) const override { *os << "has a '" << key_.get() << "' header with value that " << testing::DescribeMatcher(matcher_); } void DescribeNegationTo(std::ostream* os) const override { *os << "doesn't have a '" << key_.get() << "' header with value that " << testing::DescribeMatcher(matcher_); } private: const LowerCaseString key_; const testing::Matcher matcher_; }; class HeaderValueOfMatcher { public: explicit HeaderValueOfMatcher(LowerCaseString key, testing::Matcher matcher) : key_(std::move(key)), matcher_(std::move(matcher)) {} // Produces a testing::Matcher that is parameterized by HeaderMap& or const // HeaderMap& as requested. This is required since testing::Matcher // is not implicitly convertible to testing::Matcher. template operator testing::Matcher() const { return testing::Matcher(new HeaderValueOfMatcherImpl(key_, matcher_)); } private: const LowerCaseString key_; const testing::Matcher matcher_; }; // Test that a HeaderMap argument contains exactly one header with the given // key, whose value satisfies the given expectation. The expectation can be a // matcher, or a string that the value should equal. template HeaderValueOfMatcher HeaderValueOf(K key, const T& matcher) { return HeaderValueOfMatcher(LowerCaseString(key), testing::SafeMatcherCast(matcher)); } // Tests the provided Envoy HeaderMap for the provided HTTP status code. MATCHER_P(HttpStatusIs, expected_code, "") { const HeaderEntry* status = arg.Status(); if (status == nullptr) { *result_listener << "which has no status code"; return false; } const absl::string_view code = status->value().getStringView(); if (code != absl::StrCat(expected_code)) { *result_listener << "which has status code " << code; return false; } return true; } inline HeaderMap::ConstIterateCb saveHeaders(std::vector>* output) { return [output](const HeaderEntry& header) { output->push_back(std::make_pair(header.key().getStringView(), header.value().getStringView())); return HeaderMap::Iterate::Continue; }; } template class IsSubsetOfHeadersMatcherImpl : public testing::MatcherInterface { public: explicit IsSubsetOfHeadersMatcherImpl(const HeaderMap& expected_headers) : expected_headers_(expected_headers) {} IsSubsetOfHeadersMatcherImpl(IsSubsetOfHeadersMatcherImpl&& other) noexcept : expected_headers_(other.expected_headers_) {} IsSubsetOfHeadersMatcherImpl(const IsSubsetOfHeadersMatcherImpl& other) : expected_headers_(other.expected_headers_) {} // NOLINTNEXTLINE(readability-identifier-naming) bool MatchAndExplain(HeaderMapT headers, testing::MatchResultListener* listener) const override { // Collect header maps into vectors, to use for IsSubsetOf. std::vector> arg_headers_vec; headers.iterate(saveHeaders(&arg_headers_vec)); std::vector> expected_headers_vec; expected_headers_.iterate(saveHeaders(&expected_headers_vec)); return ExplainMatchResult(testing::IsSubsetOf(expected_headers_vec), arg_headers_vec, listener); } void DescribeTo(std::ostream* os) const override { *os << "is a subset of headers:\n" << expected_headers_; } const TestRequestHeaderMapImpl expected_headers_; }; class IsSubsetOfHeadersMatcher { public: IsSubsetOfHeadersMatcher(const HeaderMap& expected_headers) : expected_headers_(expected_headers) {} IsSubsetOfHeadersMatcher(IsSubsetOfHeadersMatcher&& other) noexcept : expected_headers_(static_cast(other.expected_headers_)) {} IsSubsetOfHeadersMatcher(const IsSubsetOfHeadersMatcher& other) : expected_headers_(static_cast(other.expected_headers_)) {} template operator testing::Matcher() const { return testing::MakeMatcher(new IsSubsetOfHeadersMatcherImpl(expected_headers_)); } private: TestRequestHeaderMapImpl expected_headers_; }; IsSubsetOfHeadersMatcher IsSubsetOfHeaders(const HeaderMap& expected_headers); template class IsSupersetOfHeadersMatcherImpl : public testing::MatcherInterface { public: explicit IsSupersetOfHeadersMatcherImpl(const HeaderMap& expected_headers) : expected_headers_(expected_headers) {} IsSupersetOfHeadersMatcherImpl(IsSupersetOfHeadersMatcherImpl&& other) noexcept : expected_headers_(other.expected_headers_) {} IsSupersetOfHeadersMatcherImpl(const IsSupersetOfHeadersMatcherImpl& other) : expected_headers_(other.expected_headers_) {} // NOLINTNEXTLINE(readability-identifier-naming) bool MatchAndExplain(HeaderMapT headers, testing::MatchResultListener* listener) const override { // Collect header maps into vectors, to use for IsSupersetOf. std::vector> arg_headers_vec; headers.iterate(saveHeaders(&arg_headers_vec)); std::vector> expected_headers_vec; expected_headers_.iterate(saveHeaders(&expected_headers_vec)); return ExplainMatchResult(testing::IsSupersetOf(expected_headers_vec), arg_headers_vec, listener); } void DescribeTo(std::ostream* os) const override { *os << "is a superset of headers:\n" << expected_headers_; } const TestRequestHeaderMapImpl expected_headers_; }; class IsSupersetOfHeadersMatcher { public: IsSupersetOfHeadersMatcher(const HeaderMap& expected_headers) : expected_headers_(expected_headers) {} IsSupersetOfHeadersMatcher(IsSupersetOfHeadersMatcher&& other) noexcept : expected_headers_(static_cast(other.expected_headers_)) {} IsSupersetOfHeadersMatcher(const IsSupersetOfHeadersMatcher& other) : expected_headers_(static_cast(other.expected_headers_)) {} template operator testing::Matcher() const { return testing::MakeMatcher(new IsSupersetOfHeadersMatcherImpl(expected_headers_)); } private: TestRequestHeaderMapImpl expected_headers_; }; IsSupersetOfHeadersMatcher IsSupersetOfHeaders(const HeaderMap& expected_headers); } // namespace Http MATCHER_P(HeaderMapEqual, rhs, "") { const bool equal = (*arg == *rhs); if (!equal) { *result_listener << "\n" << TestUtility::addLeftAndRightPadding("header map:") << "\n" << *rhs << TestUtility::addLeftAndRightPadding("is not equal to:") << "\n" << *arg << TestUtility::addLeftAndRightPadding("") // line full of padding << "\n"; } return equal; } MATCHER_P(HeaderMapEqualRef, rhs, "") { const bool equal = (arg == *rhs); if (!equal) { *result_listener << "\n" << TestUtility::addLeftAndRightPadding("header map:") << "\n" << *rhs << TestUtility::addLeftAndRightPadding("is not equal to:") << "\n" << arg << TestUtility::addLeftAndRightPadding("") // line full of padding << "\n"; } return equal; } // Test that a HeaderMapPtr argument includes a given key-value pair, e.g., // HeaderHasValue("Upgrade", "WebSocket") template testing::Matcher HeaderHasValue(K key, V value) { return testing::Pointee(Http::HeaderValueOf(key, value)); } // Like HeaderHasValue, but matches against a HeaderMap& argument. template Http::HeaderValueOfMatcher HeaderHasValueRef(K key, V value) { return Http::HeaderValueOf(key, value); } } // namespace Envoy