From 1f0cffdfba3a5f8bfbe8939c827f234397ca88ed Mon Sep 17 00:00:00 2001 From: "leilei.gll" Date: Fri, 11 Jan 2019 14:44:22 +0800 Subject: [PATCH 1/9] Adds a routing matcher for the Dubbo protocol. Signed-off-by: leilei.gll --- .../filter/network/dubbo_proxy/v2alpha1/BUILD | 10 +- .../dubbo_proxy/v2alpha1/dubbo_proxy.proto | 45 +- .../network/dubbo_proxy/v2alpha1/route.proto | 124 ++++++ .../filters/network/dubbo_proxy/BUILD | 10 + .../filters/network/dubbo_proxy/config.cc | 2 +- .../filters/network/dubbo_proxy/config.h | 4 +- .../filters/network/dubbo_proxy/filter.cc | 33 +- .../filters/network/dubbo_proxy/filter.h | 45 +- .../filters/network/dubbo_proxy/router/BUILD | 15 + .../dubbo_proxy/router/route_matcher.cc | 276 ++++++++++++ .../dubbo_proxy/router/route_matcher.h | 172 ++++++++ .../filters/network/dubbo_proxy/stats.h | 53 +++ .../filters/network/dubbo_proxy/BUILD | 12 + .../network/dubbo_proxy/config_test.cc | 13 +- .../network/dubbo_proxy/filter_test.cc | 8 +- .../network/dubbo_proxy/route_matcher_test.cc | 392 ++++++++++++++++++ 16 files changed, 1132 insertions(+), 82 deletions(-) create mode 100644 api/envoy/config/filter/network/dubbo_proxy/v2alpha1/route.proto create mode 100644 source/extensions/filters/network/dubbo_proxy/router/route_matcher.cc create mode 100644 source/extensions/filters/network/dubbo_proxy/router/route_matcher.h create mode 100644 source/extensions/filters/network/dubbo_proxy/stats.h create mode 100644 test/extensions/filters/network/dubbo_proxy/route_matcher_test.cc diff --git a/api/envoy/config/filter/network/dubbo_proxy/v2alpha1/BUILD b/api/envoy/config/filter/network/dubbo_proxy/v2alpha1/BUILD index 98107dafb584..7f8c6b3eb768 100644 --- a/api/envoy/config/filter/network/dubbo_proxy/v2alpha1/BUILD +++ b/api/envoy/config/filter/network/dubbo_proxy/v2alpha1/BUILD @@ -4,5 +4,13 @@ licenses(["notice"]) # Apache 2 api_proto_library( name = "dubbo_proxy", - srcs = ["dubbo_proxy.proto"], + srcs = [ + "dubbo_proxy.proto", + "route.proto", + ], + deps = [ + "//envoy/api/v2/core:base", + "//envoy/api/v2/route", + "//envoy/type:range", + ], ) diff --git a/api/envoy/config/filter/network/dubbo_proxy/v2alpha1/dubbo_proxy.proto b/api/envoy/config/filter/network/dubbo_proxy/v2alpha1/dubbo_proxy.proto index fd7a02009084..6786aa3d3fd5 100644 --- a/api/envoy/config/filter/network/dubbo_proxy/v2alpha1/dubbo_proxy.proto +++ b/api/envoy/config/filter/network/dubbo_proxy/v2alpha1/dubbo_proxy.proto @@ -1,27 +1,54 @@ syntax = "proto3"; -package envoy.extensions.filters.network.dubbo_proxy.v2alpha1; -option java_package = "io.envoyproxy.envoy.extensions.filters.network.dubbo_proxy.v2alpha1"; +package envoy.config.filter.network.dubbo_proxy.v2alpha1; +option java_package = "io.envoyproxy.envoy.config.filter.network.dubbo_proxy.v2alpha1"; option go_package = "v2"; +import "envoy/config/filter/network/dubbo_proxy/v2alpha1/route.proto"; + +import "google/protobuf/struct.proto"; + import "validate/validate.proto"; +import "gogoproto/gogo.proto"; // [#protodoc-title: Dubbo Proxy] // Dubbo Proxy filter configuration. - message DubboProxy { // The human readable prefix to use when emitting statistics. string stat_prefix = 1 [(validate.rules).string.min_bytes = 1]; // Configure the protocol used. - enum ProtocolType { - Dubbo = 0; // the default protocol. - } ProtocolType protocol_type = 2 [(validate.rules).enum.defined_only = true]; // Configure the serialization protocol used. - enum SerializationType { - Hessian2 = 0; // the default serialization protocol. - } SerializationType serialization_type = 3 [(validate.rules).enum.defined_only = true]; + + // The route table for the connection manager is static and is specified in this property. + repeated RouteConfiguration route_config = 4; + + // A list of individual Dubbo filters that make up the filter chain for requests made to the + // Dubbo proxy. Order matters as the filters are processed sequentially. For backwards + // compatibility, if no dubbo_filters are specified, a default Dubbo router filter + // (`envoy.filters.dubbo.router`) is used. + repeated DubboFilter dubbo_filters = 5; } + +enum ProtocolType { + Dubbo = 0; // the default protocol. +} + +enum SerializationType { + Hessian2 = 0; // the default serialization protocol. +} + +// DubboFilter configures a Dubbo filter. +// [#comment:next free field: 3] +message DubboFilter { + // The name of the filter to instantiate. The name must match a supported + // filter. + string name = 1 [(validate.rules).string.min_bytes = 1]; + + // Filter specific configuration which depends on the filter being + // instantiated. See the supported filters for further documentation. + google.protobuf.Struct config = 2; +} \ No newline at end of file diff --git a/api/envoy/config/filter/network/dubbo_proxy/v2alpha1/route.proto b/api/envoy/config/filter/network/dubbo_proxy/v2alpha1/route.proto new file mode 100644 index 000000000000..e1c21cf6a02a --- /dev/null +++ b/api/envoy/config/filter/network/dubbo_proxy/v2alpha1/route.proto @@ -0,0 +1,124 @@ +syntax = "proto3"; + +package envoy.config.filter.network.dubbo_proxy.v2alpha1; +option java_package = "io.envoyproxy.envoy.config.filter.network.thrift_proxy.v2alpha1"; +option java_multiple_files = true; +option go_package = "v2"; + +import "envoy/api/v2/route/route.proto"; +import "envoy/type/range.proto"; + +import "google/protobuf/wrappers.proto"; + +import "validate/validate.proto"; +import "gogoproto/gogo.proto"; + +// [#protodoc-title: Dubbo route configuration] + +message RouteConfiguration { + // The name of the route configuration. Reserved for future use in asynchronous route discovery. + string name = 1; + + // The interface name of the service. + string interface = 2; + + // Which group does the interface belong to. + string group = 3; + + // The version number of the interface. + string version = 4; + + // The list of routes that will be matched, in order, against incoming requests. The first route + // that matches will be used. + repeated Route routes = 5 [(gogoproto.nullable) = false]; +} + +message Route { + // Route matching prarameters. + RouteMatch match = 1 [(validate.rules).message.required = true, (gogoproto.nullable) = false]; + + // Route request to some upstream cluster. + RouteAction route = 2 [(validate.rules).message.required = true, (gogoproto.nullable) = false]; +} + +message MethodMatch { + // The name of the method. + string name = 1; + + message Parameter { + // Parameter index, starting from 0. + uint32 index = 1; + + // Parameter types, only basic data types are supported. + string type = 2; + + oneof parameter_match_specifier { + // If specified, header match will be performed based on the value of the header. + string exact_match = 3; + + // If specified, header match will be performed based on range. + // The rule will match if the request header value is within this range. + // The entire request header value must represent an integer in base 10 notation: consisting + // of an optional plus or minus sign followed by a sequence of digits. The rule will not match + // if the header value does not represent an integer. Match will fail for empty values, + // floating point numbers or if only a subsequence of the header value is an integer. + // + // Examples: + // + // * For range [-10,0), route will match for header value -1, but not for 0, + // "somestring", 10.9, + // "-1somestring" + envoy.type.Int64Range range_match = 4; + } + } + + // Method parameter definition. + repeated Parameter params_match = 2; +} + +message RouteMatch { + oneof match_specifier { + // If specified, the route must exactly match the request method name. As a special case, an + // empty string matches any request method name. + MethodMatch method = 1; + } + + // Specifies a set of headers that the route should match on. The router will check the request’s + // headers against all the specified headers in the route config. A match will happen if all the + // headers in the route are present in the request with the same values (or based on presence if + // the value field is not in the config). + repeated envoy.api.v2.route.HeaderMatcher headers = 2; +} + +// [#comment:next free field: 2] +message RouteAction { + oneof cluster_specifier { + option (validate.required) = true; + + // Indicates the upstream cluster to which the request should be routed. + string cluster = 1; + + // Multiple upstream clusters can be specified for a given route. The + // request is routed to one of the upstream clusters based on weights + // assigned to each cluster. + WeightedCluster weighted_clusters = 2; + } +} + +// Allows for specification of multiple upstream clusters along with weights that indicate the +// percentage of traffic to be forwarded to each cluster. The router selects an upstream cluster +// based on these weights. +message WeightedCluster { + message ClusterWeight { + // Name of the upstream cluster. + string name = 1 [(validate.rules).string.min_bytes = 1]; + + // When a request matches the route, the choice of an upstream cluster is determined by its + // weight. The sum of weights across all entries in the clusters array determines the total + // weight. + google.protobuf.UInt32Value weight = 2 [(validate.rules).uint32.gte = 1]; + } + + // Specifies one or more upstream clusters associated with the route. + repeated ClusterWeight clusters = 1 [(validate.rules).repeated .min_items = 1]; +} diff --git a/source/extensions/filters/network/dubbo_proxy/BUILD b/source/extensions/filters/network/dubbo_proxy/BUILD index 5905adc08702..b95792467bef 100644 --- a/source/extensions/filters/network/dubbo_proxy/BUILD +++ b/source/extensions/filters/network/dubbo_proxy/BUILD @@ -100,6 +100,7 @@ envoy_cc_library( hdrs = ["filter.h"], deps = [ ":decoder_lib", + ":stats_lib", "//include/envoy/network:connection_interface", "//include/envoy/network:filter_interface", "//include/envoy/stats:stats_interface", @@ -151,3 +152,12 @@ envoy_cc_library( "//source/common/buffer:buffer_lib", ], ) + +envoy_cc_library( + name = "stats_lib", + hdrs = ["stats.h"], + deps = [ + "//include/envoy/stats:stats_interface", + "//include/envoy/stats:stats_macros", + ], +) diff --git a/source/extensions/filters/network/dubbo_proxy/config.cc b/source/extensions/filters/network/dubbo_proxy/config.cc index 01bf22ed8923..9634ff23bb28 100644 --- a/source/extensions/filters/network/dubbo_proxy/config.cc +++ b/source/extensions/filters/network/dubbo_proxy/config.cc @@ -10,7 +10,7 @@ namespace NetworkFilters { namespace DubboProxy { Network::FilterFactoryCb DubboProxyFilterConfigFactory::createFilterFactoryFromProtoTyped( - const envoy::extensions::filters::network::dubbo_proxy::v2alpha1::DubboProxy& proto_config, + const envoy::config::filter::network::dubbo_proxy::v2alpha1::DubboProxy& proto_config, Server::Configuration::FactoryContext& context) { ASSERT(!proto_config.stat_prefix().empty()); diff --git a/source/extensions/filters/network/dubbo_proxy/config.h b/source/extensions/filters/network/dubbo_proxy/config.h index 568800b3e672..0963ce001949 100644 --- a/source/extensions/filters/network/dubbo_proxy/config.h +++ b/source/extensions/filters/network/dubbo_proxy/config.h @@ -16,13 +16,13 @@ namespace DubboProxy { */ class DubboProxyFilterConfigFactory : public Common::FactoryBase< - envoy::extensions::filters::network::dubbo_proxy::v2alpha1::DubboProxy> { + envoy::config::filter::network::dubbo_proxy::v2alpha1::DubboProxy> { public: DubboProxyFilterConfigFactory() : FactoryBase(NetworkFilterNames::get().DubboProxy) {} private: Network::FilterFactoryCb createFilterFactoryFromProtoTyped( - const envoy::extensions::filters::network::dubbo_proxy::v2alpha1::DubboProxy& proto_config, + const envoy::config::filter::network::dubbo_proxy::v2alpha1::DubboProxy& proto_config, Server::Configuration::FactoryContext& context) override; }; diff --git a/source/extensions/filters/network/dubbo_proxy/filter.cc b/source/extensions/filters/network/dubbo_proxy/filter.cc index 2be701555163..823b389143ef 100644 --- a/source/extensions/filters/network/dubbo_proxy/filter.cc +++ b/source/extensions/filters/network/dubbo_proxy/filter.cc @@ -15,16 +15,14 @@ namespace DubboProxy { namespace { -using ConfigProtocolType = - envoy::extensions::filters::network::dubbo_proxy::v2alpha1::DubboProxy_ProtocolType; +using ConfigProtocolType = envoy::config::filter::network::dubbo_proxy::v2alpha1::ProtocolType; typedef std::map ProtocolTypeMap; static const ProtocolTypeMap& protocolTypeMap() { - CONSTRUCT_ON_FIRST_USE( - ProtocolTypeMap, { - {ConfigProtocolType::DubboProxy_ProtocolType_Dubbo, ProtocolType::Dubbo}, - }); + CONSTRUCT_ON_FIRST_USE(ProtocolTypeMap, { + {ConfigProtocolType::Dubbo, ProtocolType::Dubbo}, + }); } ProtocolType lookupProtocolType(ConfigProtocolType config_type) { @@ -32,31 +30,29 @@ ProtocolType lookupProtocolType(ConfigProtocolType config_type) { if (iter == protocolTypeMap().end()) { throw EnvoyException(fmt::format( "unknown protocol {}", - envoy::extensions::filters::network::dubbo_proxy::v2alpha1::DubboProxy_ProtocolType_Name( - config_type))); + envoy::config::filter::network::dubbo_proxy::v2alpha1::ProtocolType_Name(config_type))); } return iter->second; } using ConfigSerializationType = - envoy::extensions::filters::network::dubbo_proxy::v2alpha1::DubboProxy_SerializationType; + envoy::config::filter::network::dubbo_proxy::v2alpha1::SerializationType; typedef std::map SerializationTypeMap; static const SerializationTypeMap& serializationTypeMap() { CONSTRUCT_ON_FIRST_USE(SerializationTypeMap, { - {ConfigSerializationType::DubboProxy_SerializationType_Hessian2, - SerializationType::Hessian}, + {ConfigSerializationType::Hessian2, SerializationType::Hessian}, }); } SerializationType lookupSerializationType(ConfigSerializationType type) { const auto& iter = serializationTypeMap().find(type); if (iter == serializationTypeMap().end()) { - throw EnvoyException(fmt::format("unknown deserializer {}", - envoy::extensions::filters::network::dubbo_proxy::v2alpha1:: - DubboProxy_SerializationType_Name(type))); + throw EnvoyException(fmt::format( + "unknown deserializer {}", + envoy::config::filter::network::dubbo_proxy::v2alpha1::SerializationType_Name(type))); } return iter->second; @@ -67,7 +63,8 @@ SerializationType lookupSerializationType(ConfigSerializationType type) { Filter::Filter(const std::string& stat_prefix, ConfigProtocolType protocol_type, ConfigSerializationType serialization_type, Stats::Scope& scope, TimeSource& time_source) - : stats_(generateStats(stat_prefix, scope)), protocol_type_(lookupProtocolType(protocol_type)), + : stats_(DubboFilterStats::generateStats(stat_prefix, scope)), + protocol_type_(lookupProtocolType(protocol_type)), serialization_type_(lookupSerializationType(serialization_type)), time_source_(time_source) {} Filter::~Filter() = default; @@ -221,12 +218,6 @@ void Filter::onRpcResult(RpcResultPtr&& res) { } } -DubboFilterStats Filter::generateStats(const std::string& prefix, Stats::Scope& scope) { - return DubboFilterStats{ALL_DUBBO_FILTER_STATS(POOL_COUNTER_PREFIX(scope, prefix), - POOL_GAUGE_PREFIX(scope, prefix), - POOL_HISTOGRAM_PREFIX(scope, prefix))}; -} - DecoderPtr Filter::createDecoder(ProtocolCallbacks& prot_callback) { auto parser = createProtocol(prot_callback); auto serializer = createDeserializer(); diff --git a/source/extensions/filters/network/dubbo_proxy/filter.h b/source/extensions/filters/network/dubbo_proxy/filter.h index 4af8cfb8cc21..c8e24d0a58c4 100644 --- a/source/extensions/filters/network/dubbo_proxy/filter.h +++ b/source/extensions/filters/network/dubbo_proxy/filter.h @@ -15,53 +15,25 @@ #include "extensions/filters/network/dubbo_proxy/decoder.h" #include "extensions/filters/network/dubbo_proxy/deserializer.h" #include "extensions/filters/network/dubbo_proxy/protocol.h" +#include "extensions/filters/network/dubbo_proxy/stats.h" namespace Envoy { namespace Extensions { namespace NetworkFilters { namespace DubboProxy { -/** - * All dubbo filter stats. @see stats_macros.h - */ -// clang-format off -#define ALL_DUBBO_FILTER_STATS(COUNTER, GAUGE, HISTOGRAM) \ - COUNTER(request) \ - COUNTER(request_twoway) \ - COUNTER(request_oneway) \ - COUNTER(request_event) \ - COUNTER(request_invalid_type) \ - COUNTER(request_decoding_error) \ - GAUGE(request_active) \ - HISTOGRAM(request_time_ms) \ - COUNTER(response) \ - COUNTER(response_success) \ - COUNTER(response_error) \ - COUNTER(response_exception) \ - COUNTER(response_decoding_error) \ - COUNTER(cx_destroy_local_with_active_rq) \ - COUNTER(cx_destroy_remote_with_active_rq) \ -// clang-format on - -/** - * Struct definition for all dubbo proxy stats. @see stats_macros.h - */ -struct DubboFilterStats { - ALL_DUBBO_FILTER_STATS(GENERATE_COUNTER_STRUCT, GENERATE_GAUGE_STRUCT, GENERATE_HISTOGRAM_STRUCT) -}; - class Filter : public Network::Filter, public Network::ConnectionCallbacks, public ProtocolCallbacks, public DecoderCallbacks, Logger::Loggable { public: - using ConfigProtocolType = envoy::extensions::filters::network::dubbo_proxy::v2alpha1::DubboProxy_ProtocolType; - using ConfigSerializationType = envoy::extensions::filters::network::dubbo_proxy::v2alpha1::DubboProxy_SerializationType; + using ConfigProtocolType = envoy::config::filter::network::dubbo_proxy::v2alpha1::ProtocolType; + using ConfigSerializationType = + envoy::config::filter::network::dubbo_proxy::v2alpha1::SerializationType; Filter(const std::string& stat_prefix, ConfigProtocolType protocol_type, - ConfigSerializationType serialization_type, Stats::Scope& scope, - TimeSource& time_source); + ConfigSerializationType serialization_type, Stats::Scope& scope, TimeSource& time_source); virtual ~Filter(); // Network::ReadFilter @@ -93,8 +65,8 @@ class Filter : public Network::Filter, // ActiveMessage tracks downstream requests for which no response has been received. struct ActiveMessage { ActiveMessage(Filter& parent, int32_t request_id) - : parent_(parent), - request_timer_(new Stats::Timespan(parent_.stats_.request_time_ms_, parent_.time_source_)), + : parent_(parent), request_timer_(new Stats::Timespan(parent_.stats_.request_time_ms_, + parent_.time_source_)), request_id_(request_id) { parent_.stats_.request_active_.inc(); } @@ -110,9 +82,6 @@ class Filter : public Network::Filter, }; typedef std::unique_ptr ActiveMessagePtr; - DubboFilterStats generateStats(const std::string& prefix, - Stats::Scope& scope); - // Downstream request decoder, callbacks, and buffer. DecoderPtr request_decoder_; Buffer::OwnedImpl request_buffer_; diff --git a/source/extensions/filters/network/dubbo_proxy/router/BUILD b/source/extensions/filters/network/dubbo_proxy/router/BUILD index f3c69b76c5c6..911ecf7fddf3 100644 --- a/source/extensions/filters/network/dubbo_proxy/router/BUILD +++ b/source/extensions/filters/network/dubbo_proxy/router/BUILD @@ -16,3 +16,18 @@ envoy_cc_library( "//source/extensions/filters/network/dubbo_proxy:metadata_lib", ], ) + +envoy_cc_library( + name = "router_matcher", + srcs = ["route_matcher.cc"], + hdrs = ["route_matcher.h"], + deps = [ + ":router_interface", + "//include/envoy/router:router_interface", + "//source/common/common:logger_lib", + "//source/common/http:header_utility_lib", + "//source/common/protobuf:utility_lib", + "//source/extensions/filters/network/dubbo_proxy:metadata_lib", + "@envoy_api//envoy/config/filter/network/dubbo_proxy/v2alpha1:dubbo_proxy_cc", + ], +) diff --git a/source/extensions/filters/network/dubbo_proxy/router/route_matcher.cc b/source/extensions/filters/network/dubbo_proxy/router/route_matcher.cc new file mode 100644 index 000000000000..1870d2235f4a --- /dev/null +++ b/source/extensions/filters/network/dubbo_proxy/router/route_matcher.cc @@ -0,0 +1,276 @@ +#include "extensions/filters/network/dubbo_proxy/router/route_matcher.h" + +#include "envoy/config/filter/network/dubbo_proxy/v2alpha1/dubbo_proxy.pb.h" + +#include "common/protobuf/utility.h" + +namespace Envoy { +namespace Extensions { +namespace NetworkFilters { +namespace DubboProxy { +namespace Router { + +bool Utility::isContainWildcard(const std::string& input) { + return (input.find('*') != std::string::npos) || (input.find('?') != std::string::npos); +} + +bool Utility::wildcardMatch(const char* input, const char* pattern) { + while (*pattern) { + if (*pattern == '?') { + if (!*input) { + return false; + } + + ++input; + ++pattern; + } else if (*pattern == '*') { + if (wildcardMatch(input, pattern + 1)) { + return true; + } + + if (*input && wildcardMatch(input + 1, pattern)) { + return true; + } + + return false; + } else { + if (*input++ != *pattern++) { + return false; + } + } + } + + return !*input && !*pattern; +} + +RouteEntryImplBase::RouteEntryImplBase( + const envoy::config::filter::network::dubbo_proxy::v2alpha1::Route& route) + : cluster_name_(route.route().cluster()) { + for (const auto& header_map : route.match().headers()) { + config_headers_.push_back(header_map); + } + + if (route.route().cluster_specifier_case() == + envoy::config::filter::network::dubbo_proxy::v2alpha1::RouteAction::kWeightedClusters) { + total_cluster_weight_ = 0UL; + for (const auto& cluster : route.route().weighted_clusters().clusters()) { + weighted_clusters_.emplace_back(new WeightedClusterEntry(*this, cluster)); + total_cluster_weight_ += weighted_clusters_.back()->clusterWeight(); + } + ENVOY_LOG(debug, "dubbo route matcher: weighted_clusters_size {}", weighted_clusters_.size()); + } +} + +const std::string& RouteEntryImplBase::clusterName() const { return cluster_name_; } + +const RouteEntry* RouteEntryImplBase::routeEntry() const { return this; } + +RouteConstSharedPtr RouteEntryImplBase::clusterEntry(uint64_t random_value) const { + if (weighted_clusters_.empty()) { + ENVOY_LOG(debug, "dubbo route matcher: weighted_clusters_size {}", weighted_clusters_.size()); + return shared_from_this(); + } + + return WeightedClusterUtil::pickCluster(weighted_clusters_, total_cluster_weight_, random_value, + false); +} + +bool RouteEntryImplBase::headersMatch(const Http::HeaderMap& headers) const { + ENVOY_LOG(debug, "dubbo route matcher: headers size {}, metadata headers size {}", + config_headers_.size(), headers.size()); + return Http::HeaderUtility::matchHeaders(headers, config_headers_); +} + +RouteEntryImplBase::WeightedClusterEntry::WeightedClusterEntry(const RouteEntryImplBase& parent, + const WeightedCluster& cluster) + : parent_(parent), cluster_name_(cluster.name()), + cluster_weight_(PROTOBUF_GET_WRAPPED_REQUIRED(cluster, weight)) {} + +ParamterRouteEntryImpl::ParamterRouteEntryImpl( + const envoy::config::filter::network::dubbo_proxy::v2alpha1::Route& route) + : RouteEntryImplBase(route), method_name_(route.match().method().name()) { + for (auto& config : route.match().method().params_match()) { + paramter_data_list_.emplace_back(config); + } +} + +ParamterRouteEntryImpl::~ParamterRouteEntryImpl() {} + +bool ParamterRouteEntryImpl::matchParameter(const std::string& request_data, + const ParamterData& config_data) const { + switch (config_data.match_type_) { + case Http::HeaderUtility::HeaderMatchType::Value: + return config_data.value_.empty() || request_data == config_data.value_; + case Http::HeaderUtility::HeaderMatchType::Range: { + int64_t value = 0; + return StringUtil::atol(request_data.c_str(), value, 10) && + value >= config_data.range_.start() && value < config_data.range_.end(); + } + default: + NOT_REACHED_GCOVR_EXCL_LINE; + } +} + +RouteConstSharedPtr ParamterRouteEntryImpl::matches(const MessageMetadata& metadata, + uint64_t random_value) const { + if (!metadata.hasParameters()) { + return nullptr; + } + + ENVOY_LOG(debug, "dubbo route matcher: paramter name match"); + for (auto& config_data : paramter_data_list_) { + const std::string& data = metadata.getParameterValue(config_data.index_); + if (data.empty()) { + ENVOY_LOG(debug, + "dubbo route matcher: parameter matching failed, there are no parameters in the " + "user request, index '{}'", + config_data.index_); + return nullptr; + } + + if (!matchParameter(data, config_data)) { + ENVOY_LOG(debug, "dubbo route matcher: parameter matching failed, index '{}', value '{}'", + config_data.index_, data); + return nullptr; + } + } + + return clusterEntry(random_value); +} + +ParamterRouteEntryImpl::ParamterData::ParamterData(const ParamterConfig& config) { + index_ = config.index(); + type_ = config.type(); + switch (config.parameter_match_specifier_case()) { + case ParamterConfig::kExactMatch: + match_type_ = Http::HeaderUtility::HeaderMatchType::Value; + value_ = config.exact_match(); + break; + case ParamterConfig::kRangeMatch: + match_type_ = Http::HeaderUtility::HeaderMatchType::Range; + range_.set_start(config.range_match().start()); + range_.set_end(config.range_match().end()); + break; + default: + match_type_ = Http::HeaderUtility::HeaderMatchType::Value; + break; + } +} + +MethodRouteEntryImpl::MethodRouteEntryImpl( + const envoy::config::filter::network::dubbo_proxy::v2alpha1::Route& route) + : RouteEntryImplBase(route), method_name_(route.match().method().name()), + is_contain_wildcard_(Utility::isContainWildcard(method_name_)) { + if (route.match().method().params_match_size() != 0) { + paramter_route_ = std::make_shared(route); + } + ENVOY_LOG(debug, "dubbo route matcher: method name {}", method_name_); +} + +MethodRouteEntryImpl::~MethodRouteEntryImpl() {} + +RouteConstSharedPtr MethodRouteEntryImpl::matches(const MessageMetadata& metadata, + uint64_t random_value) const { + if (metadata.hasHeaders() && !RouteEntryImplBase::headersMatch(metadata.headers())) { + ENVOY_LOG(error, "dubbo route matcher: headers not match"); + return nullptr; + } + ENVOY_LOG(debug, "dubbo route matcher: method name match"); + if (!metadata.method_name().has_value()) { + ENVOY_LOG(error, "dubbo route matcher: there is no method name in the metadata"); + return nullptr; + } + + if (method_name_ == "*") { + return clusterEntry(random_value); + } + + if (is_contain_wildcard_) { + if (!Utility::wildcardMatch(metadata.method_name().value().c_str(), method_name_.c_str())) { + ENVOY_LOG( + debug, + "dubbo route matcher: method matching failed, origin method '{}', input method '{}'", + method_name_, metadata.method_name().value()); + return nullptr; + } + } else { + if (metadata.method_name() != method_name_) { + ENVOY_LOG( + debug, + "dubbo route matcher: method matching failed, origin method '{}', input method '{}'", + method_name_, metadata.method_name().value()); + return nullptr; + } + } + + if (paramter_route_.has_value()) { + ENVOY_LOG(debug, "dubbo route matcher: parameter matching is required"); + return paramter_route_.value()->matches(metadata, random_value); + } + + return clusterEntry(random_value); +} + +RouteMatcher::RouteMatcher(const RouteConfig& config) + : interface_name_(config.interface()), group_(config.group()), version_(config.version()) { + using envoy::config::filter::network::dubbo_proxy::v2alpha1::RouteMatch; + + for (const auto& route : config.routes()) { + switch (route.match().match_specifier_case()) { + case RouteMatch::MatchSpecifierCase::kMethod: + routes_.emplace_back(new MethodRouteEntryImpl(route)); + ENVOY_LOG(debug, "dubbo route matcher: create the method route entry"); + break; + default: + NOT_IMPLEMENTED_GCOVR_EXCL_LINE; + } + } + ENVOY_LOG(debug, "dubbo route matcher: routes list size {}", routes_.size()); +} + +RouteConstSharedPtr RouteMatcher::route(const MessageMetadata& metadata, + uint64_t random_value) const { + if (interface_name_ == metadata.service_name() && + (group_.value().empty() || + (metadata.service_group().has_value() && metadata.service_group().value() == group_)) && + (version_.value().empty() || (metadata.service_version().has_value() && + metadata.service_version().value() == version_))) { + for (const auto& route : routes_) { + RouteConstSharedPtr route_entry = route->matches(metadata, random_value); + if (nullptr != route_entry) { + return route_entry; + } + } + } else { + ENVOY_LOG(debug, "dubbo route matcher: interface matching failed"); + } + + ENVOY_LOG(debug, "dubbo route matcher: route matching failed"); + return nullptr; +} + +MultiRouteMatcher::MultiRouteMatcher(const RouteConfigList& route_config_list) { + for (const auto& route_config : route_config_list) { + route_matcher_list_.emplace_back(std::make_unique(route_config)); + } + ENVOY_LOG(debug, "route matcher list size {}", route_matcher_list_.size()); +} + +RouteConstSharedPtr MultiRouteMatcher::route(const MessageMetadata& metadata, + uint64_t random_value) const { + for (const auto& route_matcher : route_matcher_list_) { + ENVOY_LOG(debug, "dubbo route matcher: route matching failed"); + auto route = route_matcher->route(metadata, random_value); + if (nullptr != route) { + return route; + } + } + + return nullptr; +} + +} // namespace Router +} // namespace DubboProxy +} // namespace NetworkFilters +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/filters/network/dubbo_proxy/router/route_matcher.h b/source/extensions/filters/network/dubbo_proxy/router/route_matcher.h new file mode 100644 index 000000000000..e09dd9517158 --- /dev/null +++ b/source/extensions/filters/network/dubbo_proxy/router/route_matcher.h @@ -0,0 +1,172 @@ +#pragma once + +#include +#include +#include + +#include "envoy/config/filter/network/dubbo_proxy/v2alpha1/dubbo_proxy.pb.h" +#include "envoy/config/filter/network/dubbo_proxy/v2alpha1/route.pb.h" + +#include "common/common/logger.h" +#include "common/http/header_utility.h" +#include "common/protobuf/protobuf.h" + +#include "extensions/filters/network/dubbo_proxy/metadata.h" +#include "extensions/filters/network/dubbo_proxy/router/router.h" + +#include "absl/types/optional.h" + +namespace Envoy { +namespace Extensions { +namespace NetworkFilters { +namespace DubboProxy { +namespace Router { + +class Utility { +public: + static bool isContainWildcard(const std::string& input); + static bool wildcardMatch(const char* input, const char* pattern); +}; + +class RouteEntryImplBase : public RouteEntry, + public Route, + public std::enable_shared_from_this, + public Logger::Loggable { +public: + RouteEntryImplBase(const envoy::config::filter::network::dubbo_proxy::v2alpha1::Route& route); + virtual ~RouteEntryImplBase() {} + + // Router::RouteEntry + const std::string& clusterName() const override; + const Envoy::Router::MetadataMatchCriteria* metadataMatchCriteria() const override { + NOT_IMPLEMENTED_GCOVR_EXCL_LINE; + } + + // Router::Route + const RouteEntry* routeEntry() const override; + + virtual RouteConstSharedPtr matches(const MessageMetadata& metadata, + uint64_t random_value) const PURE; + +protected: + RouteConstSharedPtr clusterEntry(uint64_t random_value) const; + bool headersMatch(const Http::HeaderMap& headers) const; + +private: + class WeightedClusterEntry : public RouteEntry, public Route { + public: + using WeightedCluster = + envoy::config::filter::network::dubbo_proxy::v2alpha1::WeightedCluster_ClusterWeight; + WeightedClusterEntry(const RouteEntryImplBase& parent, const WeightedCluster& cluster); + + uint64_t clusterWeight() const { return cluster_weight_; } + + // Router::RouteEntry + const std::string& clusterName() const override { return cluster_name_; } + const Envoy::Router::MetadataMatchCriteria* metadataMatchCriteria() const override { + return metadata_match_criteria_ ? metadata_match_criteria_.get() + : parent_.metadataMatchCriteria(); + } + + // Router::Route + const RouteEntry* routeEntry() const override { return this; } + + private: + const RouteEntryImplBase& parent_; + const std::string cluster_name_; + const uint64_t cluster_weight_; + Envoy::Router::MetadataMatchCriteriaConstPtr metadata_match_criteria_; + }; + + typedef std::shared_ptr WeightedClusterEntrySharedPtr; + + uint64_t total_cluster_weight_; + const std::string cluster_name_; + std::vector config_headers_; + std::vector weighted_clusters_; + + // TODO(leilei.gll) Implement it. + Envoy::Router::MetadataMatchCriteriaConstPtr metadata_match_criteria_; +}; + +typedef std::shared_ptr RouteEntryImplBaseConstSharedPtr; + +class ParamterRouteEntryImpl : public RouteEntryImplBase { +public: + ParamterRouteEntryImpl(const envoy::config::filter::network::dubbo_proxy::v2alpha1::Route& route); + ~ParamterRouteEntryImpl() override; + + struct ParamterData { + using ParamterConfig = + envoy::config::filter::network::dubbo_proxy::v2alpha1::MethodMatch_Parameter; + ParamterData(const ParamterConfig& config); + + Http::HeaderUtility::HeaderMatchType match_type_; + std::string value_; + envoy::type::Int64Range range_; + uint32_t index_; + std::string type_; + }; + + // RoutEntryImplBase + RouteConstSharedPtr matches(const MessageMetadata& metadata, + uint64_t random_value) const override; + +private: + bool matchParameter(const std::string& request_data, const ParamterData& config_data) const; + + const std::string method_name_; + std::vector paramter_data_list_; +}; + +class MethodRouteEntryImpl : public RouteEntryImplBase { +public: + MethodRouteEntryImpl(const envoy::config::filter::network::dubbo_proxy::v2alpha1::Route& route); + ~MethodRouteEntryImpl() override; + + const std::string& methodName() const { return method_name_; } + + // RoutEntryImplBase + RouteConstSharedPtr matches(const MessageMetadata& metadata, + uint64_t random_value) const override; + +private: + const std::string method_name_; + bool is_contain_wildcard_; + absl::optional> paramter_route_; +}; + +class RouteMatcher : public Logger::Loggable { +public: + using RouteConfig = envoy::config::filter::network::dubbo_proxy::v2alpha1::RouteConfiguration; + RouteMatcher(const RouteConfig& config); + + RouteConstSharedPtr route(const MessageMetadata& metadata, uint64_t random_value) const; + +private: + std::vector routes_; + const std::string interface_name_; + const absl::optional group_; + const absl::optional version_; +}; + +typedef std::shared_ptr RouteMatcherConstSharedPtr; +typedef std::unique_ptr RouteMatcherPtr; + +class MultiRouteMatcher : public Logger::Loggable { +public: + using RouteConfigList = Envoy::Protobuf::RepeatedPtrField< + ::envoy::config::filter::network::dubbo_proxy::v2alpha1::RouteConfiguration>; + MultiRouteMatcher(const RouteConfigList& route_config_list); + + RouteConstSharedPtr route(const MessageMetadata& metadata, uint64_t random_value) const; + +private: + std::vector route_matcher_list_; +}; + +} // namespace Router +} // namespace DubboProxy +} // namespace NetworkFilters +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/filters/network/dubbo_proxy/stats.h b/source/extensions/filters/network/dubbo_proxy/stats.h new file mode 100644 index 000000000000..b488401854d7 --- /dev/null +++ b/source/extensions/filters/network/dubbo_proxy/stats.h @@ -0,0 +1,53 @@ +#pragma once + +#include + +#include "envoy/stats/scope.h" +#include "envoy/stats/stats_macros.h" + +namespace Envoy { +namespace Extensions { +namespace NetworkFilters { +namespace DubboProxy { + +/** + * All dubbo filter stats. @see stats_macros.h + */ +// clang-format off +#define ALL_DUBBO_FILTER_STATS(COUNTER, GAUGE, HISTOGRAM) \ + COUNTER(request) \ + COUNTER(request_twoway) \ + COUNTER(request_oneway) \ + COUNTER(request_event) \ + COUNTER(request_invalid_type) \ + COUNTER(request_decoding_error) \ + GAUGE(request_active) \ + HISTOGRAM(request_time_ms) \ + COUNTER(response) \ + COUNTER(response_success) \ + COUNTER(response_error) \ + COUNTER(response_exception) \ + COUNTER(response_decoding_error) \ + COUNTER(cx_destroy_local_with_active_rq) \ + COUNTER(cx_destroy_remote_with_active_rq) \ + COUNTER(downstream_flow_control_paused_reading_total) \ + COUNTER(downstream_flow_control_resumed_reading_total) \ +// clang-format on + +/** + * Struct definition for all dubbo proxy stats. @see stats_macros.h + */ +struct DubboFilterStats { + ALL_DUBBO_FILTER_STATS(GENERATE_COUNTER_STRUCT, GENERATE_GAUGE_STRUCT, GENERATE_HISTOGRAM_STRUCT) + + static DubboFilterStats generateStats(const std::string& prefix, Stats::Scope& scope) { + return DubboFilterStats{ALL_DUBBO_FILTER_STATS(POOL_COUNTER_PREFIX(scope, prefix), + POOL_GAUGE_PREFIX(scope, prefix), + POOL_HISTOGRAM_PREFIX(scope, prefix))}; + } +}; + +} // namespace DubboProxy +} // namespace NetworkFilters +} // namespace Extensions +} // namespace Envoy diff --git a/test/extensions/filters/network/dubbo_proxy/BUILD b/test/extensions/filters/network/dubbo_proxy/BUILD index e9aef77ed58a..0f77b98b12b9 100644 --- a/test/extensions/filters/network/dubbo_proxy/BUILD +++ b/test/extensions/filters/network/dubbo_proxy/BUILD @@ -106,3 +106,15 @@ envoy_extension_cc_test( "//source/extensions/filters/network/dubbo_proxy:metadata_lib", ], ) + +envoy_extension_cc_test( + name = "route_matcher_test", + srcs = ["route_matcher_test.cc"], + extension_name = "envoy.filters.network.dubbo_proxy", + deps = [ + "//source/extensions/filters/network/dubbo_proxy:metadata_lib", + "//source/extensions/filters/network/dubbo_proxy/router:router_matcher", + "//test/mocks/server:server_mocks", + "@envoy_api//envoy/config/filter/network/dubbo_proxy/v2alpha1:dubbo_proxy_cc", + ], +) diff --git a/test/extensions/filters/network/dubbo_proxy/config_test.cc b/test/extensions/filters/network/dubbo_proxy/config_test.cc index a245e29ca74a..11e5e3f5ef01 100644 --- a/test/extensions/filters/network/dubbo_proxy/config_test.cc +++ b/test/extensions/filters/network/dubbo_proxy/config_test.cc @@ -16,14 +16,13 @@ namespace DubboProxy { TEST(DubboFilterConfigTest, ValidateFail) { NiceMock context; - EXPECT_THROW( - DubboProxyFilterConfigFactory().createFilterFactoryFromProto( - envoy::extensions::filters::network::dubbo_proxy::v2alpha1::DubboProxy(), context), - ProtoValidationException); + EXPECT_THROW(DubboProxyFilterConfigFactory().createFilterFactoryFromProto( + envoy::config::filter::network::dubbo_proxy::v2alpha1::DubboProxy(), context), + ProtoValidationException); } TEST(DubboFilterConfigTest, ValidProtoConfiguration) { - envoy::extensions::filters::network::dubbo_proxy::v2alpha1::DubboProxy config{}; + envoy::config::filter::network::dubbo_proxy::v2alpha1::DubboProxy config{}; config.set_stat_prefix("my_stat_prefix"); @@ -38,8 +37,8 @@ TEST(DubboFilterConfigTest, ValidProtoConfiguration) { TEST(DubboFilterConfigTest, DubboProxyWithEmptyProto) { NiceMock context; DubboProxyFilterConfigFactory factory; - envoy::extensions::filters::network::dubbo_proxy::v2alpha1::DubboProxy config = - *dynamic_cast( + envoy::config::filter::network::dubbo_proxy::v2alpha1::DubboProxy config = + *dynamic_cast( factory.createEmptyConfigProto().get()); config.set_stat_prefix("my_stat_prefix"); diff --git a/test/extensions/filters/network/dubbo_proxy/filter_test.cc b/test/extensions/filters/network/dubbo_proxy/filter_test.cc index 8503c8533c1b..024224ce6335 100644 --- a/test/extensions/filters/network/dubbo_proxy/filter_test.cc +++ b/test/extensions/filters/network/dubbo_proxy/filter_test.cc @@ -21,7 +21,9 @@ namespace Extensions { namespace NetworkFilters { namespace DubboProxy { -using ConfigDubboProxy = envoy::extensions::filters::network::dubbo_proxy::v2alpha1::DubboProxy; +using ConfigProtocolType = envoy::config::filter::network::dubbo_proxy::v2alpha1::ProtocolType; +using ConfigSerializationType = + envoy::config::filter::network::dubbo_proxy::v2alpha1::SerializationType; class DubboFilterTest : public testing::Test { public: @@ -34,8 +36,8 @@ class DubboFilterTest : public testing::Test { counter->reset(); } - filter_ = std::make_unique("test.", ConfigDubboProxy::Dubbo, ConfigDubboProxy::Hessian2, - store_, timeSystem()); + filter_ = std::make_unique("test.", ConfigProtocolType::Dubbo, + ConfigSerializationType::Hessian2, store_, timeSystem()); filter_->initializeReadFilterCallbacks(read_filter_callbacks_); filter_->onNewConnection(); diff --git a/test/extensions/filters/network/dubbo_proxy/route_matcher_test.cc b/test/extensions/filters/network/dubbo_proxy/route_matcher_test.cc new file mode 100644 index 000000000000..d263750404b5 --- /dev/null +++ b/test/extensions/filters/network/dubbo_proxy/route_matcher_test.cc @@ -0,0 +1,392 @@ +#include "envoy/config/filter/network/dubbo_proxy/v2alpha1/dubbo_proxy.pb.h" +#include "envoy/config/filter/network/dubbo_proxy/v2alpha1/dubbo_proxy.pb.validate.h" +#include "envoy/config/filter/network/dubbo_proxy/v2alpha1/route.pb.h" +#include "envoy/config/filter/network/dubbo_proxy/v2alpha1/route.pb.validate.h" + +#include "common/protobuf/protobuf.h" + +#include "extensions/filters/network/dubbo_proxy/router/route_matcher.h" + +#include "gtest/gtest.h" + +namespace Envoy { +namespace Extensions { +namespace NetworkFilters { +namespace DubboProxy { +namespace Router { + +namespace { + +envoy::config::filter::network::dubbo_proxy::v2alpha1::RouteConfiguration +parseRouteConfigurationFromV2Yaml(const std::string& yaml) { + envoy::config::filter::network::dubbo_proxy::v2alpha1::RouteConfiguration route_config; + MessageUtil::loadFromYaml(yaml, route_config); + MessageUtil::validate(route_config); + return route_config; +} + +envoy::config::filter::network::dubbo_proxy::v2alpha1::DubboProxy +parseDubboProxyFromV2Yaml(const std::string& yaml) { + envoy::config::filter::network::dubbo_proxy::v2alpha1::DubboProxy config; + MessageUtil::loadFromYaml(yaml, config); + MessageUtil::validate(config); + return config; +} + +} // namespace + +TEST(RouteMatcherTest, WildcardMatchTest) { + std::string input("add123"); + std::string pattern("add*"); + EXPECT_EQ(true, Utility::wildcardMatch(input.c_str(), pattern.c_str())); + + input = "add123test"; + pattern = "add*test"; + EXPECT_EQ(true, Utility::wildcardMatch(input.c_str(), pattern.c_str())); + + input = "123testadd"; + pattern = "*test*"; + EXPECT_EQ(true, Utility::wildcardMatch(input.c_str(), pattern.c_str())); + + input = "123testadd"; + pattern = "*test"; + EXPECT_EQ(false, Utility::wildcardMatch(input.c_str(), pattern.c_str())); + + pattern = "test*"; + EXPECT_EQ(false, Utility::wildcardMatch(input.c_str(), pattern.c_str())); +} + +TEST(RouteMatcherTest, RouteByServiceNameWithAnyMethod) { + { + const std::string yaml = R"EOF( +name: local_route +interface: org.apache.dubbo.demo.DemoService +routes: + - match: + method: + name: "*" + route: + cluster: user_service_dubbo_server +)EOF"; + + envoy::config::filter::network::dubbo_proxy::v2alpha1::RouteConfiguration config = + parseRouteConfigurationFromV2Yaml(yaml); + + RouteMatcher matcher(config); + MessageMetadata metadata; + metadata.setMethodName("test"); + EXPECT_EQ(nullptr, matcher.route(metadata, 0)); + + metadata.setServiceName("unknown"); + EXPECT_EQ(nullptr, matcher.route(metadata, 0)); + + metadata.setServiceGroup("test"); + EXPECT_EQ(nullptr, matcher.route(metadata, 0)); + + metadata.setServiceVersion("1.0.0"); + EXPECT_EQ(nullptr, matcher.route(metadata, 0)); + + metadata.setServiceName("org.apache.dubbo.demo.DemoService"); + EXPECT_EQ("user_service_dubbo_server", matcher.route(metadata, 0)->routeEntry()->clusterName()); + + // Ignore version matches if there is no version field in the configuration information. + metadata.setServiceVersion("1.0.1"); + EXPECT_EQ("user_service_dubbo_server", matcher.route(metadata, 0)->routeEntry()->clusterName()); + + metadata.setServiceGroup("test_one"); + EXPECT_EQ("user_service_dubbo_server", matcher.route(metadata, 0)->routeEntry()->clusterName()); + } + + // Service name with optional(version and group) matches. + { + const std::string yaml = R"EOF( +name: local_route +interface: org.apache.dubbo.demo.DemoService +version: 1.0.0 +group: test +routes: + - match: + method: + name: "*" + route: + cluster: user_service_dubbo_server +)EOF"; + + envoy::config::filter::network::dubbo_proxy::v2alpha1::RouteConfiguration config = + parseRouteConfigurationFromV2Yaml(yaml); + + RouteMatcher matcher(config); + MessageMetadata metadata; + metadata.setMethodName("test"); + metadata.setServiceName("org.apache.dubbo.demo.DemoService"); + EXPECT_EQ(nullptr, matcher.route(metadata, 0)); + + metadata.setServiceGroup("test"); + EXPECT_EQ(nullptr, matcher.route(metadata, 0)); + + metadata.setServiceVersion("1.0.0"); + EXPECT_EQ("user_service_dubbo_server", matcher.route(metadata, 0)->routeEntry()->clusterName()); + } + + // Service name with version matches. + { + const std::string yaml = R"EOF( +name: local_route +interface: org.apache.dubbo.demo.DemoService +version: 1.0.0 +routes: + - match: + method: + name: "*" + route: + cluster: user_service_dubbo_server +)EOF"; + + envoy::config::filter::network::dubbo_proxy::v2alpha1::RouteConfiguration config = + parseRouteConfigurationFromV2Yaml(yaml); + + RouteMatcher matcher(config); + MessageMetadata metadata; + metadata.setMethodName("test"); + metadata.setServiceName("org.apache.dubbo.demo.DemoService"); + EXPECT_EQ(nullptr, matcher.route(metadata, 0)); + + metadata.setServiceGroup("test"); + EXPECT_EQ(nullptr, matcher.route(metadata, 0)); + + metadata.setServiceVersion("1.0.0"); + EXPECT_NE(nullptr, matcher.route(metadata, 0)); + EXPECT_EQ("user_service_dubbo_server", matcher.route(metadata, 0)->routeEntry()->clusterName()); + + // Ignore group matches if there is no group field in the configuration information. + metadata.setServiceGroup("test_1"); + EXPECT_EQ("user_service_dubbo_server", matcher.route(metadata, 0)->routeEntry()->clusterName()); + } + + // Service name with group matches. + { + const std::string yaml = R"EOF( +name: local_route +interface: org.apache.dubbo.demo.DemoService +group: HSF +routes: + - match: + method: + name: "*" + route: + cluster: user_service_dubbo_server +)EOF"; + + envoy::config::filter::network::dubbo_proxy::v2alpha1::RouteConfiguration config = + parseRouteConfigurationFromV2Yaml(yaml); + + RouteMatcher matcher(config); + MessageMetadata metadata; + metadata.setMethodName("test"); + metadata.setServiceName("org.apache.dubbo.demo.DemoService"); + EXPECT_EQ(nullptr, matcher.route(metadata, 0)); + + metadata.setServiceGroup("test"); + EXPECT_EQ(nullptr, matcher.route(metadata, 0)); + + metadata.setServiceVersion("1.0.0"); + EXPECT_EQ(nullptr, matcher.route(metadata, 0)); + + metadata.setServiceGroup("HSF"); + EXPECT_EQ("user_service_dubbo_server", matcher.route(metadata, 0)->routeEntry()->clusterName()); + } +} + +TEST(RouteMatcherTest, RouteByMethod) { + const std::string yaml = R"EOF( +name: local_route +interface: org.apache.dubbo.demo.DemoService +routes: + - match: + method: + name: add + route: + cluster: user_service_dubbo_server +)EOF"; + + envoy::config::filter::network::dubbo_proxy::v2alpha1::RouteConfiguration config = + parseRouteConfigurationFromV2Yaml(yaml); + MessageMetadata metadata; + metadata.setServiceName("org.apache.dubbo.demo.DemoService"); + + RouteMatcher matcher(config); + EXPECT_EQ(nullptr, matcher.route(metadata, 0)); + + metadata.setMethodName("sub"); + EXPECT_EQ(nullptr, matcher.route(metadata, 0)); + + metadata.setMethodName("add"); + EXPECT_EQ("user_service_dubbo_server", matcher.route(metadata, 0)->routeEntry()->clusterName()); +} + +TEST(RouteMatcherTest, RouteByMethodWithWildcard) { + const std::string yaml = R"EOF( +name: local_route +interface: org.apache.dubbo.demo.DemoService +routes: + - match: + method: + name: add*test + route: + cluster: user_service_dubbo_server +)EOF"; + + envoy::config::filter::network::dubbo_proxy::v2alpha1::RouteConfiguration config = + parseRouteConfigurationFromV2Yaml(yaml); + MessageMetadata metadata; + metadata.setServiceName("org.apache.dubbo.demo.DemoService"); + + RouteMatcher matcher(config); + EXPECT_EQ(nullptr, matcher.route(metadata, 0)); + + metadata.setMethodName("sub"); + EXPECT_EQ(nullptr, matcher.route(metadata, 0)); + + metadata.setMethodName("add123test"); + EXPECT_EQ("user_service_dubbo_server", matcher.route(metadata, 0)->routeEntry()->clusterName()); +} + +TEST(RouteMatcherTest, RouteByParameterWithRangeMatch) { + const std::string yaml = R"EOF( +name: local_route +interface: org.apache.dubbo.demo.DemoService +routes: + - match: + method: + name: add + params_match: + - index: 0 + type: int + range_match: + start: 100 + end: 200 + route: + cluster: user_service_dubbo_server +)EOF"; + + envoy::config::filter::network::dubbo_proxy::v2alpha1::RouteConfiguration config = + parseRouteConfigurationFromV2Yaml(yaml); + MessageMetadata metadata; + metadata.setServiceName("org.apache.dubbo.demo.DemoService"); + metadata.setMethodName("add"); + metadata.addParameterValue(0, "150"); + + RouteMatcher matcher(config); + EXPECT_EQ("user_service_dubbo_server", matcher.route(metadata, 0)->routeEntry()->clusterName()); +} + +TEST(RouteMatcherTest, RouteByParameterWithExactMatch) { + const std::string yaml = R"EOF( +name: local_route +interface: org.apache.dubbo.demo.DemoService +routes: + - match: + method: + name: add + params_match: + - index: 1 + type: string + exact_match: "user_id:94562" + route: + cluster: user_service_dubbo_server +)EOF"; + + envoy::config::filter::network::dubbo_proxy::v2alpha1::RouteConfiguration config = + parseRouteConfigurationFromV2Yaml(yaml); + MessageMetadata metadata; + metadata.setServiceName("org.apache.dubbo.demo.DemoService"); + metadata.setMethodName("add"); + metadata.addParameterValue(1, "user_id:94562"); + + RouteMatcher matcher(config); + EXPECT_EQ("user_service_dubbo_server", matcher.route(metadata, 0)->routeEntry()->clusterName()); +} + +TEST(RouteMatcherTest, RouteWithHeaders) { + const std::string yaml = R"EOF( +name: local_route +interface: org.apache.dubbo.demo.DemoService +routes: + - match: + method: + name: add + headers: + - name: custom + exact_match: "123" + - name: custom1 + exact_match: "123" + invert_match: true + route: + cluster: user_service_dubbo_server +)EOF"; + + envoy::config::filter::network::dubbo_proxy::v2alpha1::RouteConfiguration config = + parseRouteConfigurationFromV2Yaml(yaml); + MessageMetadata metadata; + metadata.setServiceName("org.apache.dubbo.demo.DemoService"); + metadata.setMethodName("add"); + metadata.addHeader("custom", "123"); + std::string test_value("123"); + + Envoy::Http::LowerCaseString test_key("custom1"); + metadata.addHeaderReference(test_key, test_value); + + RouteMatcher matcher(config); + EXPECT_EQ(nullptr, matcher.route(metadata, 0)); + + test_value = "456"; + EXPECT_EQ("user_service_dubbo_server", matcher.route(metadata, 0)->routeEntry()->clusterName()); + + test_value = "123"; + EXPECT_EQ(nullptr, matcher.route(metadata, 0)); +} + +TEST(MultiRouteMatcherTest, Route) { + const std::string yaml = R"EOF( +stat_prefix: dubbo_incomming_stats +protocol_type: Dubbo +serialization_type: Hessian2 +route_config: + - name: test1 + interface: org.apache.dubbo.demo.DemoService + routes: + - match: + method: + name: add + params_match: + - index: 1 + type: string + exact_match: "user_id" + route: + cluster: user_service_dubbo_server + - name: test2 + interface: org.apache.dubbo.demo.FormatService + routes: + - match: + method: + name: format + route: + cluster: format_service +)EOF"; + + envoy::config::filter::network::dubbo_proxy::v2alpha1::DubboProxy config = + parseDubboProxyFromV2Yaml(yaml); + MessageMetadata metadata; + metadata.setServiceName("org.apache.dubbo.demo.DemoService"); + metadata.setMethodName("add"); + metadata.addParameterValue(1, "user_id"); + + MultiRouteMatcher matcher(config.route_config()); + EXPECT_EQ("user_service_dubbo_server", matcher.route(metadata, 0)->routeEntry()->clusterName()); +} + +} // namespace Router +} // namespace DubboProxy +} // namespace NetworkFilters +} // namespace Extensions +} // namespace Envoy From af379bc4957caa03942fbb6ba6653adaa228308e Mon Sep 17 00:00:00 2001 From: "leilei.gll" Date: Fri, 11 Jan 2019 17:12:36 +0800 Subject: [PATCH 2/9] Fixed spelling errors in word parameter. Signed-off-by: leilei.gll --- .../dubbo_proxy/router/route_matcher.cc | 30 +++++++++---------- .../dubbo_proxy/router/route_matcher.h | 19 ++++++------ 2 files changed, 25 insertions(+), 24 deletions(-) diff --git a/source/extensions/filters/network/dubbo_proxy/router/route_matcher.cc b/source/extensions/filters/network/dubbo_proxy/router/route_matcher.cc index 1870d2235f4a..da8ce280deca 100644 --- a/source/extensions/filters/network/dubbo_proxy/router/route_matcher.cc +++ b/source/extensions/filters/network/dubbo_proxy/router/route_matcher.cc @@ -86,18 +86,18 @@ RouteEntryImplBase::WeightedClusterEntry::WeightedClusterEntry(const RouteEntryI : parent_(parent), cluster_name_(cluster.name()), cluster_weight_(PROTOBUF_GET_WRAPPED_REQUIRED(cluster, weight)) {} -ParamterRouteEntryImpl::ParamterRouteEntryImpl( +ParameterRouteEntryImpl::ParameterRouteEntryImpl( const envoy::config::filter::network::dubbo_proxy::v2alpha1::Route& route) : RouteEntryImplBase(route), method_name_(route.match().method().name()) { for (auto& config : route.match().method().params_match()) { - paramter_data_list_.emplace_back(config); + parameter_data_list_.emplace_back(config); } } -ParamterRouteEntryImpl::~ParamterRouteEntryImpl() {} +ParameterRouteEntryImpl::~ParameterRouteEntryImpl() {} -bool ParamterRouteEntryImpl::matchParameter(const std::string& request_data, - const ParamterData& config_data) const { +bool ParameterRouteEntryImpl::matchParameter(const std::string& request_data, + const ParameterData& config_data) const { switch (config_data.match_type_) { case Http::HeaderUtility::HeaderMatchType::Value: return config_data.value_.empty() || request_data == config_data.value_; @@ -111,14 +111,14 @@ bool ParamterRouteEntryImpl::matchParameter(const std::string& request_data, } } -RouteConstSharedPtr ParamterRouteEntryImpl::matches(const MessageMetadata& metadata, - uint64_t random_value) const { +RouteConstSharedPtr ParameterRouteEntryImpl::matches(const MessageMetadata& metadata, + uint64_t random_value) const { if (!metadata.hasParameters()) { return nullptr; } - ENVOY_LOG(debug, "dubbo route matcher: paramter name match"); - for (auto& config_data : paramter_data_list_) { + ENVOY_LOG(debug, "dubbo route matcher: parameter name match"); + for (auto& config_data : parameter_data_list_) { const std::string& data = metadata.getParameterValue(config_data.index_); if (data.empty()) { ENVOY_LOG(debug, @@ -138,15 +138,15 @@ RouteConstSharedPtr ParamterRouteEntryImpl::matches(const MessageMetadata& metad return clusterEntry(random_value); } -ParamterRouteEntryImpl::ParamterData::ParamterData(const ParamterConfig& config) { +ParameterRouteEntryImpl::ParameterData::ParameterData(const ParameterConfig& config) { index_ = config.index(); type_ = config.type(); switch (config.parameter_match_specifier_case()) { - case ParamterConfig::kExactMatch: + case ParameterConfig::kExactMatch: match_type_ = Http::HeaderUtility::HeaderMatchType::Value; value_ = config.exact_match(); break; - case ParamterConfig::kRangeMatch: + case ParameterConfig::kRangeMatch: match_type_ = Http::HeaderUtility::HeaderMatchType::Range; range_.set_start(config.range_match().start()); range_.set_end(config.range_match().end()); @@ -162,7 +162,7 @@ MethodRouteEntryImpl::MethodRouteEntryImpl( : RouteEntryImplBase(route), method_name_(route.match().method().name()), is_contain_wildcard_(Utility::isContainWildcard(method_name_)) { if (route.match().method().params_match_size() != 0) { - paramter_route_ = std::make_shared(route); + parameter_route_ = std::make_shared(route); } ENVOY_LOG(debug, "dubbo route matcher: method name {}", method_name_); } @@ -203,9 +203,9 @@ RouteConstSharedPtr MethodRouteEntryImpl::matches(const MessageMetadata& metadat } } - if (paramter_route_.has_value()) { + if (parameter_route_.has_value()) { ENVOY_LOG(debug, "dubbo route matcher: parameter matching is required"); - return paramter_route_.value()->matches(metadata, random_value); + return parameter_route_.value()->matches(metadata, random_value); } return clusterEntry(random_value); diff --git a/source/extensions/filters/network/dubbo_proxy/router/route_matcher.h b/source/extensions/filters/network/dubbo_proxy/router/route_matcher.h index e09dd9517158..7ded5736fcf5 100644 --- a/source/extensions/filters/network/dubbo_proxy/router/route_matcher.h +++ b/source/extensions/filters/network/dubbo_proxy/router/route_matcher.h @@ -91,15 +91,16 @@ class RouteEntryImplBase : public RouteEntry, typedef std::shared_ptr RouteEntryImplBaseConstSharedPtr; -class ParamterRouteEntryImpl : public RouteEntryImplBase { +class ParameterRouteEntryImpl : public RouteEntryImplBase { public: - ParamterRouteEntryImpl(const envoy::config::filter::network::dubbo_proxy::v2alpha1::Route& route); - ~ParamterRouteEntryImpl() override; + ParameterRouteEntryImpl( + const envoy::config::filter::network::dubbo_proxy::v2alpha1::Route& route); + ~ParameterRouteEntryImpl() override; - struct ParamterData { - using ParamterConfig = + struct ParameterData { + using ParameterConfig = envoy::config::filter::network::dubbo_proxy::v2alpha1::MethodMatch_Parameter; - ParamterData(const ParamterConfig& config); + ParameterData(const ParameterConfig& config); Http::HeaderUtility::HeaderMatchType match_type_; std::string value_; @@ -113,10 +114,10 @@ class ParamterRouteEntryImpl : public RouteEntryImplBase { uint64_t random_value) const override; private: - bool matchParameter(const std::string& request_data, const ParamterData& config_data) const; + bool matchParameter(const std::string& request_data, const ParameterData& config_data) const; const std::string method_name_; - std::vector paramter_data_list_; + std::vector parameter_data_list_; }; class MethodRouteEntryImpl : public RouteEntryImplBase { @@ -133,7 +134,7 @@ class MethodRouteEntryImpl : public RouteEntryImplBase { private: const std::string method_name_; bool is_contain_wildcard_; - absl::optional> paramter_route_; + absl::optional> parameter_route_; }; class RouteMatcher : public Logger::Loggable { From 9ea279267cd534542615a9753330df40d8db4cb3 Mon Sep 17 00:00:00 2001 From: "leilei.gll" Date: Tue, 15 Jan 2019 00:58:30 +0800 Subject: [PATCH 3/9] Delete useless code and add utility.h file. Signed-off-by: leilei.gll --- .../network/dubbo_proxy/v2alpha1/route.proto | 5 +-- .../filters/network/dubbo_proxy/BUILD | 6 +++ .../filters/network/dubbo_proxy/router/BUILD | 1 + .../dubbo_proxy/router/route_matcher.cc | 43 +++--------------- .../dubbo_proxy/router/route_matcher.h | 11 +---- .../filters/network/dubbo_proxy/utility.cc | 44 +++++++++++++++++++ .../filters/network/dubbo_proxy/utility.h | 17 +++++++ .../filters/network/dubbo_proxy/BUILD | 1 + .../network/dubbo_proxy/route_matcher_test.cc | 4 +- 9 files changed, 78 insertions(+), 54 deletions(-) create mode 100644 source/extensions/filters/network/dubbo_proxy/utility.cc create mode 100644 source/extensions/filters/network/dubbo_proxy/utility.h diff --git a/api/envoy/config/filter/network/dubbo_proxy/v2alpha1/route.proto b/api/envoy/config/filter/network/dubbo_proxy/v2alpha1/route.proto index e1c21cf6a02a..fcd160632274 100644 --- a/api/envoy/config/filter/network/dubbo_proxy/v2alpha1/route.proto +++ b/api/envoy/config/filter/network/dubbo_proxy/v2alpha1/route.proto @@ -1,7 +1,7 @@ syntax = "proto3"; package envoy.config.filter.network.dubbo_proxy.v2alpha1; -option java_package = "io.envoyproxy.envoy.config.filter.network.thrift_proxy.v2alpha1"; +option java_package = "io.envoyproxy.envoy.config.filter.network.dubbo_proxy.v2alpha1"; option java_multiple_files = true; option go_package = "v2"; @@ -49,9 +49,6 @@ message MethodMatch { // Parameter index, starting from 0. uint32 index = 1; - // Parameter types, only basic data types are supported. - string type = 2; - oneof parameter_match_specifier { // If specified, header match will be performed based on the value of the header. string exact_match = 3; diff --git a/source/extensions/filters/network/dubbo_proxy/BUILD b/source/extensions/filters/network/dubbo_proxy/BUILD index b95792467bef..427d016f6cec 100644 --- a/source/extensions/filters/network/dubbo_proxy/BUILD +++ b/source/extensions/filters/network/dubbo_proxy/BUILD @@ -161,3 +161,9 @@ envoy_cc_library( "//include/envoy/stats:stats_macros", ], ) + +envoy_cc_library( + name = "utility_lib", + srcs = ["utility.cc"], + hdrs = ["utility.h"], +) diff --git a/source/extensions/filters/network/dubbo_proxy/router/BUILD b/source/extensions/filters/network/dubbo_proxy/router/BUILD index 911ecf7fddf3..a12c0b5e30c3 100644 --- a/source/extensions/filters/network/dubbo_proxy/router/BUILD +++ b/source/extensions/filters/network/dubbo_proxy/router/BUILD @@ -28,6 +28,7 @@ envoy_cc_library( "//source/common/http:header_utility_lib", "//source/common/protobuf:utility_lib", "//source/extensions/filters/network/dubbo_proxy:metadata_lib", + "//source/extensions/filters/network/dubbo_proxy:utility_lib", "@envoy_api//envoy/config/filter/network/dubbo_proxy/v2alpha1:dubbo_proxy_cc", ], ) diff --git a/source/extensions/filters/network/dubbo_proxy/router/route_matcher.cc b/source/extensions/filters/network/dubbo_proxy/router/route_matcher.cc index da8ce280deca..4c3707b748e0 100644 --- a/source/extensions/filters/network/dubbo_proxy/router/route_matcher.cc +++ b/source/extensions/filters/network/dubbo_proxy/router/route_matcher.cc @@ -4,50 +4,19 @@ #include "common/protobuf/utility.h" +#include "extensions/filters/network/dubbo_proxy/utility.h" + namespace Envoy { namespace Extensions { namespace NetworkFilters { namespace DubboProxy { namespace Router { -bool Utility::isContainWildcard(const std::string& input) { - return (input.find('*') != std::string::npos) || (input.find('?') != std::string::npos); -} - -bool Utility::wildcardMatch(const char* input, const char* pattern) { - while (*pattern) { - if (*pattern == '?') { - if (!*input) { - return false; - } - - ++input; - ++pattern; - } else if (*pattern == '*') { - if (wildcardMatch(input, pattern + 1)) { - return true; - } - - if (*input && wildcardMatch(input + 1, pattern)) { - return true; - } - - return false; - } else { - if (*input++ != *pattern++) { - return false; - } - } - } - - return !*input && !*pattern; -} - RouteEntryImplBase::RouteEntryImplBase( const envoy::config::filter::network::dubbo_proxy::v2alpha1::Route& route) : cluster_name_(route.route().cluster()) { for (const auto& header_map : route.match().headers()) { - config_headers_.push_back(header_map); + config_headers_.emplace_back(header_map); } if (route.route().cluster_specifier_case() == @@ -140,7 +109,6 @@ RouteConstSharedPtr ParameterRouteEntryImpl::matches(const MessageMetadata& meta ParameterRouteEntryImpl::ParameterData::ParameterData(const ParameterConfig& config) { index_ = config.index(); - type_ = config.type(); switch (config.parameter_match_specifier_case()) { case ParameterConfig::kExactMatch: match_type_ = Http::HeaderUtility::HeaderMatchType::Value; @@ -212,7 +180,7 @@ RouteConstSharedPtr MethodRouteEntryImpl::matches(const MessageMetadata& metadat } RouteMatcher::RouteMatcher(const RouteConfig& config) - : interface_name_(config.interface()), group_(config.group()), version_(config.version()) { + : service_name_(config.interface()), group_(config.group()), version_(config.version()) { using envoy::config::filter::network::dubbo_proxy::v2alpha1::RouteMatch; for (const auto& route : config.routes()) { @@ -230,7 +198,7 @@ RouteMatcher::RouteMatcher(const RouteConfig& config) RouteConstSharedPtr RouteMatcher::route(const MessageMetadata& metadata, uint64_t random_value) const { - if (interface_name_ == metadata.service_name() && + if (service_name_ == metadata.service_name() && (group_.value().empty() || (metadata.service_group().has_value() && metadata.service_group().value() == group_)) && (version_.value().empty() || (metadata.service_version().has_value() && @@ -259,7 +227,6 @@ MultiRouteMatcher::MultiRouteMatcher(const RouteConfigList& route_config_list) { RouteConstSharedPtr MultiRouteMatcher::route(const MessageMetadata& metadata, uint64_t random_value) const { for (const auto& route_matcher : route_matcher_list_) { - ENVOY_LOG(debug, "dubbo route matcher: route matching failed"); auto route = route_matcher->route(metadata, random_value); if (nullptr != route) { return route; diff --git a/source/extensions/filters/network/dubbo_proxy/router/route_matcher.h b/source/extensions/filters/network/dubbo_proxy/router/route_matcher.h index 7ded5736fcf5..adabbef7170e 100644 --- a/source/extensions/filters/network/dubbo_proxy/router/route_matcher.h +++ b/source/extensions/filters/network/dubbo_proxy/router/route_matcher.h @@ -22,19 +22,13 @@ namespace NetworkFilters { namespace DubboProxy { namespace Router { -class Utility { -public: - static bool isContainWildcard(const std::string& input); - static bool wildcardMatch(const char* input, const char* pattern); -}; - class RouteEntryImplBase : public RouteEntry, public Route, public std::enable_shared_from_this, public Logger::Loggable { public: RouteEntryImplBase(const envoy::config::filter::network::dubbo_proxy::v2alpha1::Route& route); - virtual ~RouteEntryImplBase() {} + virtual ~RouteEntryImplBase() = default; // Router::RouteEntry const std::string& clusterName() const override; @@ -106,7 +100,6 @@ class ParameterRouteEntryImpl : public RouteEntryImplBase { std::string value_; envoy::type::Int64Range range_; uint32_t index_; - std::string type_; }; // RoutEntryImplBase @@ -146,7 +139,7 @@ class RouteMatcher : public Logger::Loggable { private: std::vector routes_; - const std::string interface_name_; + const std::string service_name_; const absl::optional group_; const absl::optional version_; }; diff --git a/source/extensions/filters/network/dubbo_proxy/utility.cc b/source/extensions/filters/network/dubbo_proxy/utility.cc new file mode 100644 index 000000000000..b27f424dcfb5 --- /dev/null +++ b/source/extensions/filters/network/dubbo_proxy/utility.cc @@ -0,0 +1,44 @@ +#include "extensions/filters/network/dubbo_proxy/utility.h" + +namespace Envoy { +namespace Extensions { +namespace NetworkFilters { +namespace DubboProxy { + +bool Utility::isContainWildcard(const std::string& input) { + return (input.find('*') != std::string::npos) || (input.find('?') != std::string::npos); +} + +bool Utility::wildcardMatch(const char* input, const char* pattern) { + while (*pattern) { + if (*pattern == '?') { + if (!*input) { + return false; + } + + ++input; + ++pattern; + } else if (*pattern == '*') { + if (wildcardMatch(input, pattern + 1)) { + return true; + } + + if (*input && wildcardMatch(input + 1, pattern)) { + return true; + } + + return false; + } else { + if (*input++ != *pattern++) { + return false; + } + } + } + + return !*input && !*pattern; +} + +} // namespace DubboProxy +} // namespace NetworkFilters +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/filters/network/dubbo_proxy/utility.h b/source/extensions/filters/network/dubbo_proxy/utility.h new file mode 100644 index 000000000000..6faffd2b9188 --- /dev/null +++ b/source/extensions/filters/network/dubbo_proxy/utility.h @@ -0,0 +1,17 @@ +#include + +namespace Envoy { +namespace Extensions { +namespace NetworkFilters { +namespace DubboProxy { + +class Utility { +public: + static bool isContainWildcard(const std::string& input); + static bool wildcardMatch(const char* input, const char* pattern); +}; + +} // namespace DubboProxy +} // namespace NetworkFilters +} // namespace Extensions +} // namespace Envoy diff --git a/test/extensions/filters/network/dubbo_proxy/BUILD b/test/extensions/filters/network/dubbo_proxy/BUILD index 0f77b98b12b9..e634e4c7c1b4 100644 --- a/test/extensions/filters/network/dubbo_proxy/BUILD +++ b/test/extensions/filters/network/dubbo_proxy/BUILD @@ -113,6 +113,7 @@ envoy_extension_cc_test( extension_name = "envoy.filters.network.dubbo_proxy", deps = [ "//source/extensions/filters/network/dubbo_proxy:metadata_lib", + "//source/extensions/filters/network/dubbo_proxy:utility_lib", "//source/extensions/filters/network/dubbo_proxy/router:router_matcher", "//test/mocks/server:server_mocks", "@envoy_api//envoy/config/filter/network/dubbo_proxy/v2alpha1:dubbo_proxy_cc", diff --git a/test/extensions/filters/network/dubbo_proxy/route_matcher_test.cc b/test/extensions/filters/network/dubbo_proxy/route_matcher_test.cc index d263750404b5..79690e45b957 100644 --- a/test/extensions/filters/network/dubbo_proxy/route_matcher_test.cc +++ b/test/extensions/filters/network/dubbo_proxy/route_matcher_test.cc @@ -6,6 +6,7 @@ #include "common/protobuf/protobuf.h" #include "extensions/filters/network/dubbo_proxy/router/route_matcher.h" +#include "extensions/filters/network/dubbo_proxy/utility.h" #include "gtest/gtest.h" @@ -261,7 +262,6 @@ interface: org.apache.dubbo.demo.DemoService name: add params_match: - index: 0 - type: int range_match: start: 100 end: 200 @@ -290,7 +290,6 @@ interface: org.apache.dubbo.demo.DemoService name: add params_match: - index: 1 - type: string exact_match: "user_id:94562" route: cluster: user_service_dubbo_server @@ -360,7 +359,6 @@ serialization_type: Hessian2 name: add params_match: - index: 1 - type: string exact_match: "user_id" route: cluster: user_service_dubbo_server From 496b88b1f25710665ace0e4cffafbeb87148bff7 Mon Sep 17 00:00:00 2001 From: "leilei.gll" Date: Tue, 15 Jan 2019 19:12:21 +0800 Subject: [PATCH 4/9] Add unit test cases for wildcards(?). Signed-off-by: leilei.gll --- .../dubbo_proxy/router/route_matcher.cc | 5 ++-- .../network/dubbo_proxy/route_matcher_test.cc | 30 +++++++++++++++++++ 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/source/extensions/filters/network/dubbo_proxy/router/route_matcher.cc b/source/extensions/filters/network/dubbo_proxy/router/route_matcher.cc index 4c3707b748e0..01d55bda5188 100644 --- a/source/extensions/filters/network/dubbo_proxy/router/route_matcher.cc +++ b/source/extensions/filters/network/dubbo_proxy/router/route_matcher.cc @@ -23,7 +23,7 @@ RouteEntryImplBase::RouteEntryImplBase( envoy::config::filter::network::dubbo_proxy::v2alpha1::RouteAction::kWeightedClusters) { total_cluster_weight_ = 0UL; for (const auto& cluster : route.route().weighted_clusters().clusters()) { - weighted_clusters_.emplace_back(new WeightedClusterEntry(*this, cluster)); + weighted_clusters_.emplace_back(std::make_shared(*this, cluster)); total_cluster_weight_ += weighted_clusters_.back()->clusterWeight(); } ENVOY_LOG(debug, "dubbo route matcher: weighted_clusters_size {}", weighted_clusters_.size()); @@ -186,7 +186,7 @@ RouteMatcher::RouteMatcher(const RouteConfig& config) for (const auto& route : config.routes()) { switch (route.match().match_specifier_case()) { case RouteMatch::MatchSpecifierCase::kMethod: - routes_.emplace_back(new MethodRouteEntryImpl(route)); + routes_.emplace_back(std::make_shared(route)); ENVOY_LOG(debug, "dubbo route matcher: create the method route entry"); break; default: @@ -213,7 +213,6 @@ RouteConstSharedPtr RouteMatcher::route(const MessageMetadata& metadata, ENVOY_LOG(debug, "dubbo route matcher: interface matching failed"); } - ENVOY_LOG(debug, "dubbo route matcher: route matching failed"); return nullptr; } diff --git a/test/extensions/filters/network/dubbo_proxy/route_matcher_test.cc b/test/extensions/filters/network/dubbo_proxy/route_matcher_test.cc index 79690e45b957..d1605aa10428 100644 --- a/test/extensions/filters/network/dubbo_proxy/route_matcher_test.cc +++ b/test/extensions/filters/network/dubbo_proxy/route_matcher_test.cc @@ -252,6 +252,36 @@ interface: org.apache.dubbo.demo.DemoService EXPECT_EQ("user_service_dubbo_server", matcher.route(metadata, 0)->routeEntry()->clusterName()); } +TEST(RouteMatcherTest, RouteByMethodWithMultiWildcard) { + const std::string yaml = R"EOF( +name: local_route +interface: org.apache.dubbo.demo.DemoService +routes: + - match: + method: + name: "*d?test" + route: + cluster: user_service_dubbo_server +)EOF"; + + envoy::config::filter::network::dubbo_proxy::v2alpha1::RouteConfiguration config = + parseRouteConfigurationFromV2Yaml(yaml); + MessageMetadata metadata; + metadata.setServiceName("org.apache.dubbo.demo.DemoService"); + + RouteMatcher matcher(config); + EXPECT_EQ(nullptr, matcher.route(metadata, 0)); + + metadata.setMethodName("ab12test"); + EXPECT_EQ(nullptr, matcher.route(metadata, 0)); + + metadata.setMethodName("test12d2test"); + EXPECT_EQ("user_service_dubbo_server", matcher.route(metadata, 0)->routeEntry()->clusterName()); + + metadata.setMethodName("datest"); + EXPECT_EQ("user_service_dubbo_server", matcher.route(metadata, 0)->routeEntry()->clusterName()); +} + TEST(RouteMatcherTest, RouteByParameterWithRangeMatch) { const std::string yaml = R"EOF( name: local_route From 0b6a7bcb42b731b6454a9c9030dc109d6bc07a29 Mon Sep 17 00:00:00 2001 From: "leilei.gll" Date: Wed, 16 Jan 2019 13:53:06 +0800 Subject: [PATCH 5/9] Modify the route.proto and dubbo_proxy.proto definition. Signed-off-by: leilei.gll --- .../dubbo_proxy/v2alpha1/dubbo_proxy.proto | 4 +- .../network/dubbo_proxy/v2alpha1/route.proto | 49 +++++++------------ .../dubbo_proxy/router/route_matcher.cc | 20 +++----- .../dubbo_proxy/router/route_matcher.h | 9 ++-- .../network/dubbo_proxy/route_matcher_test.cc | 16 +++--- 5 files changed, 40 insertions(+), 58 deletions(-) diff --git a/api/envoy/config/filter/network/dubbo_proxy/v2alpha1/dubbo_proxy.proto b/api/envoy/config/filter/network/dubbo_proxy/v2alpha1/dubbo_proxy.proto index 6786aa3d3fd5..e38e50a50a3e 100644 --- a/api/envoy/config/filter/network/dubbo_proxy/v2alpha1/dubbo_proxy.proto +++ b/api/envoy/config/filter/network/dubbo_proxy/v2alpha1/dubbo_proxy.proto @@ -6,7 +6,7 @@ option go_package = "v2"; import "envoy/config/filter/network/dubbo_proxy/v2alpha1/route.proto"; -import "google/protobuf/struct.proto"; +import "google/protobuf/any.proto"; import "validate/validate.proto"; import "gogoproto/gogo.proto"; @@ -50,5 +50,5 @@ message DubboFilter { // Filter specific configuration which depends on the filter being // instantiated. See the supported filters for further documentation. - google.protobuf.Struct config = 2; + google.protobuf.Any config = 2; } \ No newline at end of file diff --git a/api/envoy/config/filter/network/dubbo_proxy/v2alpha1/route.proto b/api/envoy/config/filter/network/dubbo_proxy/v2alpha1/route.proto index fcd160632274..409b9583871d 100644 --- a/api/envoy/config/filter/network/dubbo_proxy/v2alpha1/route.proto +++ b/api/envoy/config/filter/network/dubbo_proxy/v2alpha1/route.proto @@ -42,13 +42,18 @@ message Route { } message MethodMatch { - // The name of the method. + // The name of the method. wildcard names are supported in the form of ``*Add`` or + // ``*cluster?Name``. + // + // .. note:: + // + // e.g. ``*AddOrderNumber`` will match ``bizAddOrderNumber`` or ``AddOrderNumber``. + // ``*AddOrder?`` will match ``bizAddOrderA`` but not ``bizAddOrderAB``. + // Additionally, a special entry ``*`` is allowed which will match any name. string name = 1; - message Parameter { - // Parameter index, starting from 0. - uint32 index = 1; - + // The parameter matching type. + message ParameterMatchSpecifier { oneof parameter_match_specifier { // If specified, header match will be performed based on the value of the header. string exact_match = 3; @@ -70,15 +75,14 @@ message MethodMatch { } // Method parameter definition. - repeated Parameter params_match = 2; + // The key is the parameter index, starting from 0. + // The value is the parameter matching type. + map params_match = 2; } message RouteMatch { - oneof match_specifier { - // If specified, the route must exactly match the request method name. As a special case, an - // empty string matches any request method name. - MethodMatch method = 1; - } + // Method level routing matching. + MethodMatch method = 1; // Specifies a set of headers that the route should match on. The router will check the request’s // headers against all the specified headers in the route config. A match will happen if all the @@ -98,24 +102,9 @@ message RouteAction { // Multiple upstream clusters can be specified for a given route. The // request is routed to one of the upstream clusters based on weights // assigned to each cluster. - WeightedCluster weighted_clusters = 2; - } -} - -// Allows for specification of multiple upstream clusters along with weights that indicate the -// percentage of traffic to be forwarded to each cluster. The router selects an upstream cluster -// based on these weights. -message WeightedCluster { - message ClusterWeight { - // Name of the upstream cluster. - string name = 1 [(validate.rules).string.min_bytes = 1]; - - // When a request matches the route, the choice of an upstream cluster is determined by its - // weight. The sum of weights across all entries in the clusters array determines the total - // weight. - google.protobuf.UInt32Value weight = 2 [(validate.rules).uint32.gte = 1]; + // + // .. note:: + // Currently ClusterWeight only supports the name and weight fields. + envoy.api.v2.route.WeightedCluster weighted_clusters = 2; } - - // Specifies one or more upstream clusters associated with the route. - repeated ClusterWeight clusters = 1 [(validate.rules).repeated .min_items = 1]; } diff --git a/source/extensions/filters/network/dubbo_proxy/router/route_matcher.cc b/source/extensions/filters/network/dubbo_proxy/router/route_matcher.cc index 01d55bda5188..c6ba685974e3 100644 --- a/source/extensions/filters/network/dubbo_proxy/router/route_matcher.cc +++ b/source/extensions/filters/network/dubbo_proxy/router/route_matcher.cc @@ -59,7 +59,7 @@ ParameterRouteEntryImpl::ParameterRouteEntryImpl( const envoy::config::filter::network::dubbo_proxy::v2alpha1::Route& route) : RouteEntryImplBase(route), method_name_(route.match().method().name()) { for (auto& config : route.match().method().params_match()) { - parameter_data_list_.emplace_back(config); + parameter_data_list_.emplace_back(config.first, config.second); } } @@ -107,14 +107,15 @@ RouteConstSharedPtr ParameterRouteEntryImpl::matches(const MessageMetadata& meta return clusterEntry(random_value); } -ParameterRouteEntryImpl::ParameterData::ParameterData(const ParameterConfig& config) { - index_ = config.index(); +ParameterRouteEntryImpl::ParameterData::ParameterData(uint32_t index, + const ParameterMatchSpecifier& config) { + index_ = index; switch (config.parameter_match_specifier_case()) { - case ParameterConfig::kExactMatch: + case ParameterMatchSpecifier::kExactMatch: match_type_ = Http::HeaderUtility::HeaderMatchType::Value; value_ = config.exact_match(); break; - case ParameterConfig::kRangeMatch: + case ParameterMatchSpecifier::kRangeMatch: match_type_ = Http::HeaderUtility::HeaderMatchType::Range; range_.set_start(config.range_match().start()); range_.set_end(config.range_match().end()); @@ -184,14 +185,7 @@ RouteMatcher::RouteMatcher(const RouteConfig& config) using envoy::config::filter::network::dubbo_proxy::v2alpha1::RouteMatch; for (const auto& route : config.routes()) { - switch (route.match().match_specifier_case()) { - case RouteMatch::MatchSpecifierCase::kMethod: - routes_.emplace_back(std::make_shared(route)); - ENVOY_LOG(debug, "dubbo route matcher: create the method route entry"); - break; - default: - NOT_IMPLEMENTED_GCOVR_EXCL_LINE; - } + routes_.emplace_back(std::make_shared(route)); } ENVOY_LOG(debug, "dubbo route matcher: routes list size {}", routes_.size()); } diff --git a/source/extensions/filters/network/dubbo_proxy/router/route_matcher.h b/source/extensions/filters/network/dubbo_proxy/router/route_matcher.h index adabbef7170e..63690c7d2e43 100644 --- a/source/extensions/filters/network/dubbo_proxy/router/route_matcher.h +++ b/source/extensions/filters/network/dubbo_proxy/router/route_matcher.h @@ -49,8 +49,7 @@ class RouteEntryImplBase : public RouteEntry, private: class WeightedClusterEntry : public RouteEntry, public Route { public: - using WeightedCluster = - envoy::config::filter::network::dubbo_proxy::v2alpha1::WeightedCluster_ClusterWeight; + using WeightedCluster = envoy::api::v2::route::WeightedCluster_ClusterWeight; WeightedClusterEntry(const RouteEntryImplBase& parent, const WeightedCluster& cluster); uint64_t clusterWeight() const { return cluster_weight_; } @@ -92,9 +91,9 @@ class ParameterRouteEntryImpl : public RouteEntryImplBase { ~ParameterRouteEntryImpl() override; struct ParameterData { - using ParameterConfig = - envoy::config::filter::network::dubbo_proxy::v2alpha1::MethodMatch_Parameter; - ParameterData(const ParameterConfig& config); + using ParameterMatchSpecifier = + envoy::config::filter::network::dubbo_proxy::v2alpha1::MethodMatch_ParameterMatchSpecifier; + ParameterData(uint32_t index, const ParameterMatchSpecifier& config); Http::HeaderUtility::HeaderMatchType match_type_; std::string value_; diff --git a/test/extensions/filters/network/dubbo_proxy/route_matcher_test.cc b/test/extensions/filters/network/dubbo_proxy/route_matcher_test.cc index d1605aa10428..e68afc768dcb 100644 --- a/test/extensions/filters/network/dubbo_proxy/route_matcher_test.cc +++ b/test/extensions/filters/network/dubbo_proxy/route_matcher_test.cc @@ -291,10 +291,10 @@ interface: org.apache.dubbo.demo.DemoService method: name: add params_match: - - index: 0 - range_match: - start: 100 - end: 200 + 0: + range_match: + start: 100 + end: 200 route: cluster: user_service_dubbo_server )EOF"; @@ -319,8 +319,8 @@ interface: org.apache.dubbo.demo.DemoService method: name: add params_match: - - index: 1 - exact_match: "user_id:94562" + 1: + exact_match: "user_id:94562" route: cluster: user_service_dubbo_server )EOF"; @@ -388,8 +388,8 @@ serialization_type: Hessian2 method: name: add params_match: - - index: 1 - exact_match: "user_id" + 1: + exact_match: "user_id" route: cluster: user_service_dubbo_server - name: test2 From 62f8268b8f9a8546bf96ba3ec7837a6cfa5ec8ad Mon Sep 17 00:00:00 2001 From: "leilei.gll" Date: Thu, 17 Jan 2019 11:39:47 +0800 Subject: [PATCH 6/9] Change the method name type to StringMatcher. Signed-off-by: leilei.gll --- .../filter/network/dubbo_proxy/v2alpha1/BUILD | 1 + .../network/dubbo_proxy/v2alpha1/route.proto | 12 +-- .../filters/network/dubbo_proxy/router/BUILD | 1 + .../dubbo_proxy/router/route_matcher.cc | 32 ++------ .../dubbo_proxy/router/route_matcher.h | 7 +- .../network/dubbo_proxy/route_matcher_test.cc | 75 +++++++++++++++---- 6 files changed, 73 insertions(+), 55 deletions(-) diff --git a/api/envoy/config/filter/network/dubbo_proxy/v2alpha1/BUILD b/api/envoy/config/filter/network/dubbo_proxy/v2alpha1/BUILD index 7f8c6b3eb768..2e88b619e524 100644 --- a/api/envoy/config/filter/network/dubbo_proxy/v2alpha1/BUILD +++ b/api/envoy/config/filter/network/dubbo_proxy/v2alpha1/BUILD @@ -12,5 +12,6 @@ api_proto_library( "//envoy/api/v2/core:base", "//envoy/api/v2/route", "//envoy/type:range", + "//envoy/type/matcher:string", ], ) diff --git a/api/envoy/config/filter/network/dubbo_proxy/v2alpha1/route.proto b/api/envoy/config/filter/network/dubbo_proxy/v2alpha1/route.proto index 409b9583871d..9060b490f39b 100644 --- a/api/envoy/config/filter/network/dubbo_proxy/v2alpha1/route.proto +++ b/api/envoy/config/filter/network/dubbo_proxy/v2alpha1/route.proto @@ -6,6 +6,7 @@ option java_multiple_files = true; option go_package = "v2"; import "envoy/api/v2/route/route.proto"; +import "envoy/type/matcher/string.proto"; import "envoy/type/range.proto"; import "google/protobuf/wrappers.proto"; @@ -42,15 +43,8 @@ message Route { } message MethodMatch { - // The name of the method. wildcard names are supported in the form of ``*Add`` or - // ``*cluster?Name``. - // - // .. note:: - // - // e.g. ``*AddOrderNumber`` will match ``bizAddOrderNumber`` or ``AddOrderNumber``. - // ``*AddOrder?`` will match ``bizAddOrderA`` but not ``bizAddOrderAB``. - // Additionally, a special entry ``*`` is allowed which will match any name. - string name = 1; + // The name of the method. + envoy.type.matcher.StringMatcher name = 1; // The parameter matching type. message ParameterMatchSpecifier { diff --git a/source/extensions/filters/network/dubbo_proxy/router/BUILD b/source/extensions/filters/network/dubbo_proxy/router/BUILD index a12c0b5e30c3..520cc37e1be4 100644 --- a/source/extensions/filters/network/dubbo_proxy/router/BUILD +++ b/source/extensions/filters/network/dubbo_proxy/router/BUILD @@ -25,6 +25,7 @@ envoy_cc_library( ":router_interface", "//include/envoy/router:router_interface", "//source/common/common:logger_lib", + "//source/common/common:matchers_lib", "//source/common/http:header_utility_lib", "//source/common/protobuf:utility_lib", "//source/extensions/filters/network/dubbo_proxy:metadata_lib", diff --git a/source/extensions/filters/network/dubbo_proxy/router/route_matcher.cc b/source/extensions/filters/network/dubbo_proxy/router/route_matcher.cc index c6ba685974e3..9202698dfd6f 100644 --- a/source/extensions/filters/network/dubbo_proxy/router/route_matcher.cc +++ b/source/extensions/filters/network/dubbo_proxy/router/route_matcher.cc @@ -57,7 +57,7 @@ RouteEntryImplBase::WeightedClusterEntry::WeightedClusterEntry(const RouteEntryI ParameterRouteEntryImpl::ParameterRouteEntryImpl( const envoy::config::filter::network::dubbo_proxy::v2alpha1::Route& route) - : RouteEntryImplBase(route), method_name_(route.match().method().name()) { + : RouteEntryImplBase(route) { for (auto& config : route.match().method().params_match()) { parameter_data_list_.emplace_back(config.first, config.second); } @@ -128,12 +128,10 @@ ParameterRouteEntryImpl::ParameterData::ParameterData(uint32_t index, MethodRouteEntryImpl::MethodRouteEntryImpl( const envoy::config::filter::network::dubbo_proxy::v2alpha1::Route& route) - : RouteEntryImplBase(route), method_name_(route.match().method().name()), - is_contain_wildcard_(Utility::isContainWildcard(method_name_)) { + : RouteEntryImplBase(route), method_name_(route.match().method().name()) { if (route.match().method().params_match_size() != 0) { parameter_route_ = std::make_shared(route); } - ENVOY_LOG(debug, "dubbo route matcher: method name {}", method_name_); } MethodRouteEntryImpl::~MethodRouteEntryImpl() {} @@ -144,32 +142,16 @@ RouteConstSharedPtr MethodRouteEntryImpl::matches(const MessageMetadata& metadat ENVOY_LOG(error, "dubbo route matcher: headers not match"); return nullptr; } - ENVOY_LOG(debug, "dubbo route matcher: method name match"); + if (!metadata.method_name().has_value()) { ENVOY_LOG(error, "dubbo route matcher: there is no method name in the metadata"); return nullptr; } - if (method_name_ == "*") { - return clusterEntry(random_value); - } - - if (is_contain_wildcard_) { - if (!Utility::wildcardMatch(metadata.method_name().value().c_str(), method_name_.c_str())) { - ENVOY_LOG( - debug, - "dubbo route matcher: method matching failed, origin method '{}', input method '{}'", - method_name_, metadata.method_name().value()); - return nullptr; - } - } else { - if (metadata.method_name() != method_name_) { - ENVOY_LOG( - debug, - "dubbo route matcher: method matching failed, origin method '{}', input method '{}'", - method_name_, metadata.method_name().value()); - return nullptr; - } + if (!method_name_.match(metadata.method_name().value())) { + ENVOY_LOG(debug, "dubbo route matcher: method matching failed, input method '{}'", + metadata.method_name().value()); + return nullptr; } if (parameter_route_.has_value()) { diff --git a/source/extensions/filters/network/dubbo_proxy/router/route_matcher.h b/source/extensions/filters/network/dubbo_proxy/router/route_matcher.h index 63690c7d2e43..378a466d007c 100644 --- a/source/extensions/filters/network/dubbo_proxy/router/route_matcher.h +++ b/source/extensions/filters/network/dubbo_proxy/router/route_matcher.h @@ -8,6 +8,7 @@ #include "envoy/config/filter/network/dubbo_proxy/v2alpha1/route.pb.h" #include "common/common/logger.h" +#include "common/common/matchers.h" #include "common/http/header_utility.h" #include "common/protobuf/protobuf.h" @@ -108,7 +109,6 @@ class ParameterRouteEntryImpl : public RouteEntryImplBase { private: bool matchParameter(const std::string& request_data, const ParameterData& config_data) const; - const std::string method_name_; std::vector parameter_data_list_; }; @@ -117,15 +117,12 @@ class MethodRouteEntryImpl : public RouteEntryImplBase { MethodRouteEntryImpl(const envoy::config::filter::network::dubbo_proxy::v2alpha1::Route& route); ~MethodRouteEntryImpl() override; - const std::string& methodName() const { return method_name_; } - // RoutEntryImplBase RouteConstSharedPtr matches(const MessageMetadata& metadata, uint64_t random_value) const override; private: - const std::string method_name_; - bool is_contain_wildcard_; + const Matchers::StringMatcher method_name_; absl::optional> parameter_route_; }; diff --git a/test/extensions/filters/network/dubbo_proxy/route_matcher_test.cc b/test/extensions/filters/network/dubbo_proxy/route_matcher_test.cc index e68afc768dcb..30787970ed85 100644 --- a/test/extensions/filters/network/dubbo_proxy/route_matcher_test.cc +++ b/test/extensions/filters/network/dubbo_proxy/route_matcher_test.cc @@ -65,7 +65,8 @@ interface: org.apache.dubbo.demo.DemoService routes: - match: method: - name: "*" + name: + regex: "(.*?)" route: cluster: user_service_dubbo_server )EOF"; @@ -108,7 +109,8 @@ group: test routes: - match: method: - name: "*" + name: + regex: "(.*?)" route: cluster: user_service_dubbo_server )EOF"; @@ -138,7 +140,8 @@ version: 1.0.0 routes: - match: method: - name: "*" + name: + regex: "(.*?)" route: cluster: user_service_dubbo_server )EOF"; @@ -173,7 +176,8 @@ group: HSF routes: - match: method: - name: "*" + name: + regex: "(.*?)" route: cluster: user_service_dubbo_server )EOF"; @@ -198,14 +202,15 @@ group: HSF } } -TEST(RouteMatcherTest, RouteByMethod) { +TEST(RouteMatcherTest, RouteByMethodWithExactMatch) { const std::string yaml = R"EOF( name: local_route interface: org.apache.dubbo.demo.DemoService routes: - match: method: - name: add + name: + exact: "add" route: cluster: user_service_dubbo_server )EOF"; @@ -225,14 +230,15 @@ interface: org.apache.dubbo.demo.DemoService EXPECT_EQ("user_service_dubbo_server", matcher.route(metadata, 0)->routeEntry()->clusterName()); } -TEST(RouteMatcherTest, RouteByMethodWithWildcard) { +TEST(RouteMatcherTest, RouteByMethodWithSuffixMatch) { const std::string yaml = R"EOF( name: local_route interface: org.apache.dubbo.demo.DemoService routes: - match: method: - name: add*test + name: + suffix: "test" route: cluster: user_service_dubbo_server )EOF"; @@ -252,14 +258,15 @@ interface: org.apache.dubbo.demo.DemoService EXPECT_EQ("user_service_dubbo_server", matcher.route(metadata, 0)->routeEntry()->clusterName()); } -TEST(RouteMatcherTest, RouteByMethodWithMultiWildcard) { +TEST(RouteMatcherTest, RouteByMethodWithPrefixMatch) { const std::string yaml = R"EOF( name: local_route interface: org.apache.dubbo.demo.DemoService routes: - match: method: - name: "*d?test" + name: + prefix: "test" route: cluster: user_service_dubbo_server )EOF"; @@ -278,10 +285,41 @@ interface: org.apache.dubbo.demo.DemoService metadata.setMethodName("test12d2test"); EXPECT_EQ("user_service_dubbo_server", matcher.route(metadata, 0)->routeEntry()->clusterName()); - metadata.setMethodName("datest"); + metadata.setMethodName("testme"); EXPECT_EQ("user_service_dubbo_server", matcher.route(metadata, 0)->routeEntry()->clusterName()); } +TEST(RouteMatcherTest, RouteByMethodWithRegexMatch) { + const std::string yaml = R"EOF( +name: local_route +interface: org.apache.dubbo.demo.DemoService +routes: + - match: + method: + name: + regex: "\\d{3}test" + route: + cluster: user_service_dubbo_server +)EOF"; + + envoy::config::filter::network::dubbo_proxy::v2alpha1::RouteConfiguration config = + parseRouteConfigurationFromV2Yaml(yaml); + MessageMetadata metadata; + metadata.setServiceName("org.apache.dubbo.demo.DemoService"); + + RouteMatcher matcher(config); + EXPECT_EQ(nullptr, matcher.route(metadata, 0)); + + metadata.setMethodName("12test"); + EXPECT_EQ(nullptr, matcher.route(metadata, 0)); + + metadata.setMethodName("456test"); + EXPECT_EQ("user_service_dubbo_server", matcher.route(metadata, 0)->routeEntry()->clusterName()); + + metadata.setMethodName("4567test"); + EXPECT_EQ(nullptr, matcher.route(metadata, 0)); +} + TEST(RouteMatcherTest, RouteByParameterWithRangeMatch) { const std::string yaml = R"EOF( name: local_route @@ -289,7 +327,8 @@ interface: org.apache.dubbo.demo.DemoService routes: - match: method: - name: add + name: + exact: "add" params_match: 0: range_match: @@ -317,7 +356,8 @@ interface: org.apache.dubbo.demo.DemoService routes: - match: method: - name: add + name: + exact: "add" params_match: 1: exact_match: "user_id:94562" @@ -343,7 +383,8 @@ interface: org.apache.dubbo.demo.DemoService routes: - match: method: - name: add + name: + exact: "add" headers: - name: custom exact_match: "123" @@ -386,7 +427,8 @@ serialization_type: Hessian2 routes: - match: method: - name: add + name: + exact: "add" params_match: 1: exact_match: "user_id" @@ -397,7 +439,8 @@ serialization_type: Hessian2 routes: - match: method: - name: format + name: + exact: "format" route: cluster: format_service )EOF"; From f66d586d68aadd206f0ef857040712ef5d6f5000 Mon Sep 17 00:00:00 2001 From: "leilei.gll" Date: Fri, 18 Jan 2019 15:03:29 +0800 Subject: [PATCH 7/9] Add validation for the RouteConfiguration rule and remove the useless utility class. Signed-off-by: leilei.gll --- .../filters/network/dubbo_proxy/BUILD | 6 --- .../filters/network/dubbo_proxy/router/BUILD | 1 - .../dubbo_proxy/router/route_matcher.cc | 6 ++- .../filters/network/dubbo_proxy/utility.cc | 44 ------------------- .../filters/network/dubbo_proxy/utility.h | 17 ------- .../filters/network/dubbo_proxy/BUILD | 1 - .../network/dubbo_proxy/route_matcher_test.cc | 22 ---------- 7 files changed, 4 insertions(+), 93 deletions(-) delete mode 100644 source/extensions/filters/network/dubbo_proxy/utility.cc delete mode 100644 source/extensions/filters/network/dubbo_proxy/utility.h diff --git a/source/extensions/filters/network/dubbo_proxy/BUILD b/source/extensions/filters/network/dubbo_proxy/BUILD index 427d016f6cec..b95792467bef 100644 --- a/source/extensions/filters/network/dubbo_proxy/BUILD +++ b/source/extensions/filters/network/dubbo_proxy/BUILD @@ -161,9 +161,3 @@ envoy_cc_library( "//include/envoy/stats:stats_macros", ], ) - -envoy_cc_library( - name = "utility_lib", - srcs = ["utility.cc"], - hdrs = ["utility.h"], -) diff --git a/source/extensions/filters/network/dubbo_proxy/router/BUILD b/source/extensions/filters/network/dubbo_proxy/router/BUILD index 520cc37e1be4..674713cb072e 100644 --- a/source/extensions/filters/network/dubbo_proxy/router/BUILD +++ b/source/extensions/filters/network/dubbo_proxy/router/BUILD @@ -29,7 +29,6 @@ envoy_cc_library( "//source/common/http:header_utility_lib", "//source/common/protobuf:utility_lib", "//source/extensions/filters/network/dubbo_proxy:metadata_lib", - "//source/extensions/filters/network/dubbo_proxy:utility_lib", "@envoy_api//envoy/config/filter/network/dubbo_proxy/v2alpha1:dubbo_proxy_cc", ], ) diff --git a/source/extensions/filters/network/dubbo_proxy/router/route_matcher.cc b/source/extensions/filters/network/dubbo_proxy/router/route_matcher.cc index 9202698dfd6f..6a185b9074bb 100644 --- a/source/extensions/filters/network/dubbo_proxy/router/route_matcher.cc +++ b/source/extensions/filters/network/dubbo_proxy/router/route_matcher.cc @@ -1,11 +1,10 @@ #include "extensions/filters/network/dubbo_proxy/router/route_matcher.h" #include "envoy/config/filter/network/dubbo_proxy/v2alpha1/dubbo_proxy.pb.h" +#include "envoy/config/filter/network/dubbo_proxy/v2alpha1/route.pb.validate.h" #include "common/protobuf/utility.h" -#include "extensions/filters/network/dubbo_proxy/utility.h" - namespace Envoy { namespace Extensions { namespace NetworkFilters { @@ -166,6 +165,9 @@ RouteMatcher::RouteMatcher(const RouteConfig& config) : service_name_(config.interface()), group_(config.group()), version_(config.version()) { using envoy::config::filter::network::dubbo_proxy::v2alpha1::RouteMatch; + // Verify configuration rules. + MessageUtil::validate(config); + for (const auto& route : config.routes()) { routes_.emplace_back(std::make_shared(route)); } diff --git a/source/extensions/filters/network/dubbo_proxy/utility.cc b/source/extensions/filters/network/dubbo_proxy/utility.cc deleted file mode 100644 index b27f424dcfb5..000000000000 --- a/source/extensions/filters/network/dubbo_proxy/utility.cc +++ /dev/null @@ -1,44 +0,0 @@ -#include "extensions/filters/network/dubbo_proxy/utility.h" - -namespace Envoy { -namespace Extensions { -namespace NetworkFilters { -namespace DubboProxy { - -bool Utility::isContainWildcard(const std::string& input) { - return (input.find('*') != std::string::npos) || (input.find('?') != std::string::npos); -} - -bool Utility::wildcardMatch(const char* input, const char* pattern) { - while (*pattern) { - if (*pattern == '?') { - if (!*input) { - return false; - } - - ++input; - ++pattern; - } else if (*pattern == '*') { - if (wildcardMatch(input, pattern + 1)) { - return true; - } - - if (*input && wildcardMatch(input + 1, pattern)) { - return true; - } - - return false; - } else { - if (*input++ != *pattern++) { - return false; - } - } - } - - return !*input && !*pattern; -} - -} // namespace DubboProxy -} // namespace NetworkFilters -} // namespace Extensions -} // namespace Envoy diff --git a/source/extensions/filters/network/dubbo_proxy/utility.h b/source/extensions/filters/network/dubbo_proxy/utility.h deleted file mode 100644 index 6faffd2b9188..000000000000 --- a/source/extensions/filters/network/dubbo_proxy/utility.h +++ /dev/null @@ -1,17 +0,0 @@ -#include - -namespace Envoy { -namespace Extensions { -namespace NetworkFilters { -namespace DubboProxy { - -class Utility { -public: - static bool isContainWildcard(const std::string& input); - static bool wildcardMatch(const char* input, const char* pattern); -}; - -} // namespace DubboProxy -} // namespace NetworkFilters -} // namespace Extensions -} // namespace Envoy diff --git a/test/extensions/filters/network/dubbo_proxy/BUILD b/test/extensions/filters/network/dubbo_proxy/BUILD index e634e4c7c1b4..0f77b98b12b9 100644 --- a/test/extensions/filters/network/dubbo_proxy/BUILD +++ b/test/extensions/filters/network/dubbo_proxy/BUILD @@ -113,7 +113,6 @@ envoy_extension_cc_test( extension_name = "envoy.filters.network.dubbo_proxy", deps = [ "//source/extensions/filters/network/dubbo_proxy:metadata_lib", - "//source/extensions/filters/network/dubbo_proxy:utility_lib", "//source/extensions/filters/network/dubbo_proxy/router:router_matcher", "//test/mocks/server:server_mocks", "@envoy_api//envoy/config/filter/network/dubbo_proxy/v2alpha1:dubbo_proxy_cc", diff --git a/test/extensions/filters/network/dubbo_proxy/route_matcher_test.cc b/test/extensions/filters/network/dubbo_proxy/route_matcher_test.cc index 30787970ed85..f5461e9ac071 100644 --- a/test/extensions/filters/network/dubbo_proxy/route_matcher_test.cc +++ b/test/extensions/filters/network/dubbo_proxy/route_matcher_test.cc @@ -6,7 +6,6 @@ #include "common/protobuf/protobuf.h" #include "extensions/filters/network/dubbo_proxy/router/route_matcher.h" -#include "extensions/filters/network/dubbo_proxy/utility.h" #include "gtest/gtest.h" @@ -36,27 +35,6 @@ parseDubboProxyFromV2Yaml(const std::string& yaml) { } // namespace -TEST(RouteMatcherTest, WildcardMatchTest) { - std::string input("add123"); - std::string pattern("add*"); - EXPECT_EQ(true, Utility::wildcardMatch(input.c_str(), pattern.c_str())); - - input = "add123test"; - pattern = "add*test"; - EXPECT_EQ(true, Utility::wildcardMatch(input.c_str(), pattern.c_str())); - - input = "123testadd"; - pattern = "*test*"; - EXPECT_EQ(true, Utility::wildcardMatch(input.c_str(), pattern.c_str())); - - input = "123testadd"; - pattern = "*test"; - EXPECT_EQ(false, Utility::wildcardMatch(input.c_str(), pattern.c_str())); - - pattern = "test*"; - EXPECT_EQ(false, Utility::wildcardMatch(input.c_str(), pattern.c_str())); -} - TEST(RouteMatcherTest, RouteByServiceNameWithAnyMethod) { { const std::string yaml = R"EOF( From bbe4d8607a8650fd0242b461a50ae68bf9835d56 Mon Sep 17 00:00:00 2001 From: "leilei.gll" Date: Fri, 18 Jan 2019 17:44:31 +0800 Subject: [PATCH 8/9] Changed the parameter_router_ type tto shared_ptr. Signed-off-by: leilei.gll --- .../filters/network/dubbo_proxy/router/route_matcher.cc | 7 ++----- .../filters/network/dubbo_proxy/router/route_matcher.h | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/source/extensions/filters/network/dubbo_proxy/router/route_matcher.cc b/source/extensions/filters/network/dubbo_proxy/router/route_matcher.cc index 6a185b9074bb..5b08076fe31e 100644 --- a/source/extensions/filters/network/dubbo_proxy/router/route_matcher.cc +++ b/source/extensions/filters/network/dubbo_proxy/router/route_matcher.cc @@ -153,9 +153,9 @@ RouteConstSharedPtr MethodRouteEntryImpl::matches(const MessageMetadata& metadat return nullptr; } - if (parameter_route_.has_value()) { + if (parameter_route_) { ENVOY_LOG(debug, "dubbo route matcher: parameter matching is required"); - return parameter_route_.value()->matches(metadata, random_value); + return parameter_route_->matches(metadata, random_value); } return clusterEntry(random_value); @@ -165,9 +165,6 @@ RouteMatcher::RouteMatcher(const RouteConfig& config) : service_name_(config.interface()), group_(config.group()), version_(config.version()) { using envoy::config::filter::network::dubbo_proxy::v2alpha1::RouteMatch; - // Verify configuration rules. - MessageUtil::validate(config); - for (const auto& route : config.routes()) { routes_.emplace_back(std::make_shared(route)); } diff --git a/source/extensions/filters/network/dubbo_proxy/router/route_matcher.h b/source/extensions/filters/network/dubbo_proxy/router/route_matcher.h index 378a466d007c..9eefcbe241bc 100644 --- a/source/extensions/filters/network/dubbo_proxy/router/route_matcher.h +++ b/source/extensions/filters/network/dubbo_proxy/router/route_matcher.h @@ -123,7 +123,7 @@ class MethodRouteEntryImpl : public RouteEntryImplBase { private: const Matchers::StringMatcher method_name_; - absl::optional> parameter_route_; + std::shared_ptr parameter_route_; }; class RouteMatcher : public Logger::Loggable { From 2ca11f1f91f35bc1ae9a8d54650379741701e3b4 Mon Sep 17 00:00:00 2001 From: "leilei.gll" Date: Wed, 23 Jan 2019 20:04:35 +0800 Subject: [PATCH 9/9] Remove "java_multiple_files" declaration from the route.proto file Signed-off-by: leilei.gll --- api/envoy/config/filter/network/dubbo_proxy/v2alpha1/route.proto | 1 - 1 file changed, 1 deletion(-) diff --git a/api/envoy/config/filter/network/dubbo_proxy/v2alpha1/route.proto b/api/envoy/config/filter/network/dubbo_proxy/v2alpha1/route.proto index 9060b490f39b..26ee220953e9 100644 --- a/api/envoy/config/filter/network/dubbo_proxy/v2alpha1/route.proto +++ b/api/envoy/config/filter/network/dubbo_proxy/v2alpha1/route.proto @@ -2,7 +2,6 @@ syntax = "proto3"; package envoy.config.filter.network.dubbo_proxy.v2alpha1; option java_package = "io.envoyproxy.envoy.config.filter.network.dubbo_proxy.v2alpha1"; -option java_multiple_files = true; option go_package = "v2"; import "envoy/api/v2/route/route.proto";