Skip to content
Snippets Groups Projects
Commit f4075216 authored by watermelo's avatar watermelo
Browse files

Add: add ip address match function

parent 21976a30
No related branches found
No related tags found
No related merge requests found
......@@ -18,8 +18,11 @@
package tag
import (
"errors"
"fmt"
"net"
"strconv"
"strings"
)
import (
......@@ -81,20 +84,27 @@ func (c *tagRouter) Route(invokers []protocol.Invoker, url *common.URL, invocati
addresses, _ = tagRouterRuleCopy.getTagNameToAddresses()[tag]
// filter by dynamic tag group first
if len(addresses) > 0 {
result = filterAddressMatches(invokers, addresses)
filterAddressMatches := func(invoker protocol.Invoker) bool {
url := invoker.GetUrl()
if len(addresses) > 0 && checkAddressMatch(addresses, url.Ip, url.Port) {
return true
}
return false
}
result = filterInvoker(invokers, filterAddressMatches)
if len(result) > 0 || tagRouterRuleCopy.Force {
return result
}
} else {
// dynamic tag group doesn't have any item about the requested app OR it's null after filtered by
// dynamic tag group but force=false. check static tag
cond := func(invoker protocol.Invoker) bool {
filter := func(invoker protocol.Invoker) bool {
if invoker.GetUrl().GetParam(constant.Tagkey, "") == tag {
return true
}
return false
}
result = filterCondition(invokers, cond)
result = filterInvoker(invokers, filter)
}
// If there's no tagged providers that can match the current tagged request. force.tag is set by default
// to false, which means it will invoke any providers without a tag unless it's explicitly disallowed.
......@@ -102,20 +112,33 @@ func (c *tagRouter) Route(invokers []protocol.Invoker, url *common.URL, invocati
return result
} else {
// FAILOVER: return all Providers without any tags.
result = filterAddressNotMatches(invokers, tagRouterRuleCopy.getAddresses())
cond := func(invoker protocol.Invoker) bool {
filterAddressNotMatches := func(invoker protocol.Invoker) bool {
url := invoker.GetUrl()
if len(addresses) == 0 || !checkAddressMatch(tagRouterRuleCopy.getAddresses(), url.Ip, url.Port) {
return true
}
return false
}
filterTagIsEmpty := func(invoker protocol.Invoker) bool {
if invoker.GetUrl().GetParam(constant.Tagkey, "") == "" {
return true
}
return false
}
return filterCondition(result, cond)
return filterInvoker(invokers, filterAddressNotMatches, filterTagIsEmpty)
}
} else {
// return all addresses in dynamic tag group.
addresses = tagRouterRuleCopy.getAddresses()
if len(addresses) > 0 {
result = filterAddressNotMatches(invokers, addresses)
filterAddressNotMatches := func(invoker protocol.Invoker) bool {
url := invoker.GetUrl()
if len(addresses) == 0 || !checkAddressMatch(addresses, url.Ip, url.Port) {
return true
}
return false
}
result = filterInvoker(invokers, filterAddressNotMatches)
// 1. all addresses are in dynamic tag group, return empty list.
if len(result) == 0 {
return result
......@@ -123,11 +146,11 @@ func (c *tagRouter) Route(invokers []protocol.Invoker, url *common.URL, invocati
// 2. if there are some addresses that are not in any dynamic tag group, continue to filter using the
// static tag group.
}
cond := func(invoker protocol.Invoker) bool {
filter := func(invoker protocol.Invoker) bool {
localTag := invoker.GetUrl().GetParam(constant.Tagkey, "")
return localTag == "" || !(tagRouterRuleCopy.hasTag(localTag))
}
return filterCondition(result, cond)
return filterInvoker(result, filter)
}
}
......@@ -185,53 +208,163 @@ func isForceUseTag(url *common.URL, invocation protocol.Invocation) bool {
return false
}
func filterAddressMatches(invokers []protocol.Invoker, addresses []string) []protocol.Invoker {
var idx int
type filter func(protocol.Invoker) bool
func filterInvoker(invokers []protocol.Invoker, filters ...filter) []protocol.Invoker {
var res []protocol.Invoker
OUTER:
for _, invoker := range invokers {
url := invoker.GetUrl()
if !(len(addresses) > 0 && checkAddressMatch(addresses, url.Ip, url.Port)) {
continue
for _, filter := range filters {
if !filter(invoker) {
continue OUTER
}
}
invokers[idx] = invoker
idx++
res = append(res, invoker)
}
return invokers[:idx]
return res
}
func filterAddressNotMatches(invokers []protocol.Invoker, addresses []string) []protocol.Invoker {
var idx int
for _, invoker := range invokers {
url := invoker.GetUrl()
if !(len(addresses) == 0 || !checkAddressMatch(addresses, url.Ip, url.Port)) {
continue
// TODO 需要搬到 dubbogo/gost, 可以先 review
func checkAddressMatch(addresses []string, host, port string) bool {
for _, address := range addresses {
if matchIp(address, host, port) {
return true
}
if address == constant.ANYHOST_VALUE+":"+port {
return true
}
invokers[idx] = invoker
idx++
}
return invokers[:idx]
return false
}
func filterCondition(invokers []protocol.Invoker, condition func(protocol.Invoker) bool) []protocol.Invoker {
var idx int
for _, invoker := range invokers {
if !condition(invoker) {
func matchIp(pattern, host, port string) bool {
// if the pattern is subnet format, it will not be allowed to config port param in pattern.
if strings.Contains(pattern, "/") {
_, subnet, _ := net.ParseCIDR(pattern)
if subnet != nil && subnet.Contains(net.ParseIP(host)) {
return true
}
return false
}
return matchIpRange(pattern, host, port)
}
func matchIpRange(pattern, host, port string) bool {
if pattern == "" || host == "" {
logger.Error("Illegal Argument pattern or hostName. Pattern:" + pattern + ", Host:" + host)
return false
}
pattern = strings.TrimSpace(pattern)
if "*.*.*.*" == pattern || "*" == pattern {
return true
}
isIpv4 := true
ip4 := net.ParseIP(host).To4()
if ip4 == nil {
isIpv4 = false
}
hostAndPort := getPatternHostAndPort(pattern, isIpv4)
if hostAndPort[1] != "" && hostAndPort[1] != port {
return false
}
pattern = hostAndPort[0]
// TODO 常量化
splitCharacter := "\\."
if !isIpv4 {
splitCharacter = ":"
}
mask := strings.Split(pattern, splitCharacter)
// check format of pattern
if err := checkHostPattern(pattern, mask, isIpv4); err != nil {
logger.Error(err)
return false
}
if pattern == host {
return true
}
// short name condition
if !ipPatternContains(pattern) {
return pattern == host
}
// ip 段
ipAddress := strings.Split(host, splitCharacter)
for i := 0; i < len(mask); i++ {
if "*" == mask[i] || mask[i] == ipAddress[i] {
continue
} else if strings.Contains(mask[i], "-") {
rangeNumStrs := strings.Split(mask[i], "-")
if len(rangeNumStrs) != 2 {
logger.Error("There is wrong format of ip Address: " + mask[i])
return false
}
min := getNumOfIpSegment(rangeNumStrs[0], isIpv4)
max := getNumOfIpSegment(rangeNumStrs[1], isIpv4)
ip := getNumOfIpSegment(ipAddress[i], isIpv4)
if ip < min || ip > max {
return false
}
} else if "0" == ipAddress[i] && "0" == mask[i] || "00" == mask[i] || "000" == mask[i] || "0000" == mask[i] {
continue
} else if mask[i] != ipAddress[i] {
return false
}
invokers[idx] = invoker
idx++
}
return invokers[:idx]
return true
}
func checkAddressMatch(addresses []string, host, port string) bool {
for _, address := range addresses {
// TODO ip match
if address == host+":"+port {
return true
func ipPatternContains(pattern string) bool {
return strings.Contains(pattern, "*") || strings.Contains(pattern, "-")
}
func checkHostPattern(pattern string, mask []string, isIpv4 bool) error {
if !isIpv4 {
if len(mask) != 8 && ipPatternContains(pattern) {
return errors.New("If you config ip expression that contains '*' or '-', please fill qualified ip pattern like 234e:0:4567:0:0:0:3d:*. ")
}
if address == constant.ANYHOST_VALUE+":"+port {
return true
if len(mask) != 8 && !strings.Contains(pattern, "::") {
return errors.New("The host is ipv6, but the pattern is not ipv6 pattern : " + pattern)
}
} else {
if len(mask) != 4 {
return errors.New("The host is ipv4, but the pattern is not ipv4 pattern : " + pattern)
}
}
return false
return nil
}
func getPatternHostAndPort(pattern string, isIpv4 bool) []string {
result := make([]string, 2)
if strings.HasPrefix(pattern, "[") && strings.Contains(pattern, "]:") {
end := strings.Index(pattern, "]:")
result[0] = pattern[1:end]
result[1] = pattern[end+2:]
} else if strings.HasPrefix(pattern, "[") && strings.HasSuffix(pattern, "]") {
result[0] = pattern[1 : len(pattern)-1]
result[1] = ""
} else if isIpv4 && strings.Contains(pattern, ":") {
end := strings.Index(pattern, ":")
result[0] = pattern[:end]
result[1] = pattern[end+1:]
} else {
result[0] = pattern
}
return result
}
func getNumOfIpSegment(ipSegment string, isIpv4 bool) int {
if isIpv4 {
ipSeg, _ := strconv.Atoi(ipSegment)
return ipSeg
}
ipSeg, _ := strconv.ParseInt(ipSegment, 0, 16)
return int(ipSeg)
}
......@@ -19,7 +19,6 @@ package tag
import (
"context"
"github.com/apache/dubbo-go/common/constant"
"testing"
)
......@@ -29,16 +28,18 @@ import (
import (
"github.com/apache/dubbo-go/common"
"github.com/apache/dubbo-go/common/constant"
"github.com/apache/dubbo-go/protocol"
"github.com/apache/dubbo-go/protocol/invocation"
)
const (
tagRouterTestHangZhouUrl = "dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=&version=2.6.0&enabled=true&dubbo.tag=hangzhou"
tagRouterTestShangHaiUrl = "dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=&version=2.6.0&enabled=true&dubbo.tag=shanghai"
tagRouterTestBeijingUrl = "dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=&version=2.6.0&enabled=true&dubbo.tag=beijing"
tagRouterTestUserConsumer = "dubbo://127.0.0.1:20000/com.ikurento.user.UserConsumer?interface=com.ikurento.user.UserConsumer&group=&version=2.6.0&enabled=true"
tagRouterTestUserConsumerTag = "dubbo://127.0.0.1:20000/com.ikurento.user.UserConsumer?interface=com.ikurento.user.UserConsumer&group=&version=2.6.0&enabled=true&dubbo.force.tag=true"
tagRouterTestHangZhouUrl = "dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=&version=2.6.0&enabled=true&dubbo.tag=hangzhou"
tagRouterTestShangHaiUrl = "dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=&version=2.6.0&enabled=true&dubbo.tag=shanghai"
tagRouterTestBeijingUrl = "dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=&version=2.6.0&enabled=true&dubbo.tag=beijing"
tagRouterTestEnabledBeijingUrl = "dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=&version=2.6.0&enabled=false&dubbo.tag=beijing"
tagRouterTestUserConsumer = "dubbo://127.0.0.1:20000/com.ikurento.user.UserConsumer?interface=com.ikurento.user.UserConsumer&group=&version=2.6.0&enabled=true"
tagRouterTestUserConsumerTag = "dubbo://127.0.0.1:20000/com.ikurento.user.UserConsumer?interface=com.ikurento.user.UserConsumer&group=&version=2.6.0&enabled=true&dubbo.force.tag=true"
tagRouterTestDubboTag = "dubbo.tag"
tagRouterTestDubboForceTag = "dubbo.force.tag"
......@@ -162,24 +163,36 @@ func TestTagRouterRouteNoForce(t *testing.T) {
assert.Equal(t, 3, len(invRst2))
}
func TestFilterCondition(t *testing.T) {
func TestFilterInvoker(t *testing.T) {
u2, e2 := common.NewURL(tagRouterTestHangZhouUrl)
u3, e3 := common.NewURL(tagRouterTestShangHaiUrl)
u4, e4 := common.NewURL(tagRouterTestBeijingUrl)
u5, e5 := common.NewURL(tagRouterTestEnabledBeijingUrl)
assert.Nil(t, e2)
assert.Nil(t, e3)
assert.Nil(t, e4)
assert.Nil(t, e5)
inv2 := NewMockInvoker(u2)
inv3 := NewMockInvoker(u3)
inv4 := NewMockInvoker(u4)
inv5 := NewMockInvoker(u5)
var invokers []protocol.Invoker
invokers = append(invokers, inv2, inv3, inv4)
cond := func(invoker protocol.Invoker) bool {
invokers = append(invokers, inv2, inv3, inv4, inv5)
filterTag := func(invoker protocol.Invoker) bool {
if invoker.GetUrl().GetParam(constant.Tagkey, "") == "beijing" {
return true
}
return false
}
res := filterCondition(invokers, cond)
assert.Equal(t, []protocol.Invoker{inv4}, res)
res := filterInvoker(invokers, filterTag)
assert.Equal(t, []protocol.Invoker{inv4, inv5}, res)
flag := true
filterEnabled := func(invoker protocol.Invoker) bool {
if invoker.GetUrl().GetParamBool(constant.RouterEnabled, false) == flag {
return true
}
return false
}
res2 := filterInvoker(invokers, filterTag, filterEnabled)
assert.Equal(t, []protocol.Invoker{inv4}, res2)
}
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment