Newer
Older
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

vito.he
committed
"sync"

AlexStocks
committed
"github.com/apache/dubbo-go/common/constant"
/////////////////////////////////
// dubbo role type
/////////////////////////////////
const (
CONSUMER = iota
CONFIGURATOR
ROUTER
PROVIDER
)
var (
DubboNodes = [...]string{"consumers", "configurators", "routers", "providers"}
DubboRole = [...]string{"consumer", "", "", "provider"}
)
type RoleType int
func (t RoleType) String() string {
return DubboNodes[t]
}
func (t RoleType) Role() string {
return DubboRole[t]

vito.he
committed
Protocol string
Location string // ip+port
Ip string
Port string
//url.Values is not safe map, add to avoid concurrent map read and map write error
Path string // like /com.ikurento.dubbo.UserProvider3
Username string
Password string
//special for registry
type option func(*URL)
func WithUsername(username string) option {
return func(url *URL) {
url.Username = username
}
}
func WithPassword(pwd string) option {
return func(url *URL) {
url.Password = pwd
}
}
func WithMethods(methods []string) option {
return func(url *URL) {
url.Methods = methods
}
}
func WithParams(params url.Values) option {
return func(url *URL) {
url.Params = params
}
}
func WithParamsValue(key, val string) option {
return func(url *URL) {
url.Params.Set(key, val)
}
}
func WithProtocol(proto string) option {
return func(url *URL) {
url.Protocol = proto
}
}
func WithIp(ip string) option {
return func(url *URL) {
url.Ip = ip
}
}
func WithPort(port string) option {
return func(url *URL) {
url.Port = port
}
}
func WithPath(path string) option {
return func(url *URL) {
url.Path = "/" + strings.TrimPrefix(path, "/")
func WithLocation(location string) option {
return func(url *URL) {
url.Location = location
}
}
func NewURLWithOptions(opts ...option) *URL {
url := &URL{}
for _, opt := range opts {
opt(url)
}
func NewURL(ctx context.Context, urlString string, opts ...option) (URL, error) {
var (
err error
rawUrlString string
serviceUrl *url.URL
// new a null instance
if urlString == "" {
return s, nil
}
rawUrlString, err = url.QueryUnescape(urlString)
if err != nil {
return s, perrors.Errorf("url.QueryUnescape(%s), error{%v}", urlString, err)

vito.he
committed
if strings.Index(rawUrlString, "//") < 0 {
t := URL{baseUrl: baseUrl{ctx: ctx}}
for _, opt := range opts {
opt(&t)
}
rawUrlString = t.Protocol + "://" + rawUrlString
}
serviceUrl, err = url.Parse(rawUrlString)
if err != nil {
return s, perrors.Errorf("url.Parse(url string{%s}), error{%v}", rawUrlString, err)
s.Params, err = url.ParseQuery(serviceUrl.RawQuery)
return s, perrors.Errorf("url.ParseQuery(raw url string{%s}), error{%v}", serviceUrl.RawQuery, err)
s.PrimitiveURL = urlString
s.Protocol = serviceUrl.Scheme
s.Username = serviceUrl.User.Username()
s.Password, _ = serviceUrl.User.Password()
s.Location = serviceUrl.Host
s.Path = serviceUrl.Path
if strings.Contains(s.Location, ":") {
s.Ip, s.Port, err = net.SplitHostPort(s.Location)
return s, perrors.Errorf("net.SplitHostPort(Url.Host{%s}), error{%v}", s.Location, err)
for _, opt := range opts {
func (c URL) URLEqual(url URL) bool {
cGroup := c.GetParam(constant.GROUP_KEY, "")
urlGroup := url.GetParam(constant.GROUP_KEY, "")
cKey := c.Key()
urlKey := url.Key()
if cGroup == constant.ANY_VALUE {
cKey = strings.Replace(cKey, "group=*", "group="+urlGroup, 1)
} else if urlGroup == constant.ANY_VALUE {
urlKey = strings.Replace(urlKey, "group=*", "group="+cGroup, 1)
}
if cKey != urlKey {
if url.GetParam(constant.ENABLED_KEY, "true") != "true" && url.GetParam(constant.ENABLED_KEY, "") != constant.ANY_VALUE {
return false
}
//TODO :may need add interface key any value condition
if !isMatchCategory(url.GetParam(constant.CATEGORY_KEY, constant.DEFAULT_CATEGORY), c.GetParam(constant.CATEGORY_KEY, constant.DEFAULT_CATEGORY)) {
return false
}
func isMatchCategory(category1 string, category2 string) bool {
if len(category2) == 0 {
return category1 == constant.DEFAULT_CATEGORY
} else if strings.Contains(category2, constant.ANY_VALUE) {
return true
} else if strings.Contains(category2, constant.REMOVE_VALUE_PREFIX) {
return !strings.Contains(category2, constant.REMOVE_VALUE_PREFIX+category1)
} else {
var buildString string
if len(c.Username) == 0 && len(c.Password) == 0 {
buildString = fmt.Sprintf(
"%s://%s:%s%s?",
c.Protocol, c.Ip, c.Port, c.Path)
} else {
buildString = fmt.Sprintf(
"%s://%s:%s@%s:%s%s?",
c.Protocol, c.Username, c.Password, c.Ip, c.Port, c.Path)
}
"%s://%s:%s@%s:%s/?interface=%s&group=%s&version=%s",
c.Protocol, c.Username, c.Password, c.Ip, c.Port, c.Service(), c.GetParam(constant.GROUP_KEY, ""), c.GetParam(constant.VERSION_KEY, ""))
//return c.ServiceKey()
}
func (c URL) ServiceKey() string {
intf := c.GetParam(constant.INTERFACE_KEY, strings.TrimPrefix(c.Path, "/"))
if intf == "" {
return ""
}
buf := &bytes.Buffer{}
group := c.GetParam(constant.GROUP_KEY, "")
if group != "" {
buf.WriteString(group)
buf.WriteString("/")
}
buf.WriteString(intf)
version := c.GetParam(constant.VERSION_KEY, "")
if version != "" && version != "0.0.0" {
buf.WriteString(":")
buf.WriteString(version)
}
return buf.String()
func (c *URL) EncodedServiceKey() string {
serviceKey := c.ServiceKey()
return strings.Replace(serviceKey, "/", "*", 1)
}
func (c URL) Context() context.Context {
service := c.GetParam(constant.INTERFACE_KEY, strings.TrimPrefix(c.Path, "/"))
if service != "" {
return service
} else if c.SubURL != nil {
service = c.GetParam(constant.INTERFACE_KEY, strings.TrimPrefix(c.Path, "/"))
if service != "" { //if url.path is "" then return suburl's path, special for registry Url
return service
}
}
return ""
func (c *URL) AddParam(key string, value string) {

vito.he
committed
c.paramsLock.Lock()
c.Params.Add(key, value)

vito.he
committed
c.paramsLock.Unlock()
}
func (c URL) GetParam(s string, d string) string {
ruleDec, err := base64.URLEncoding.DecodeString(c.GetParam(key, ""))
value := string(ruleDec)
return value, err
}
return c.Protocol
return c.Username
return strings.Split(c.Location, ":")[0]
return c.Password
return c.Port
return c.Path
}
}
// GetParamBool
func (c URL) GetParamBool(s string, d bool) bool {
var r bool
var err error
if r, err = strconv.ParseBool(c.Params.Get(s)); err != nil {
return d
}
return r
}
func (c URL) GetParamInt(s string, d int64) int64 {
var r int
var err error
if r, err = strconv.Atoi(c.Params.Get(s)); r == 0 || err != nil {
return d
}
return int64(r)
}
func (c URL) GetMethodParamInt(method string, key string, d int64) int64 {
var r int
var err error
if r, err = strconv.Atoi(c.Params.Get("methods." + method + "." + key)); r == 0 || err != nil {
return d
}
return int64(r)
}
func (c URL) GetMethodParamInt64(method string, key string, d int64) int64 {
r := c.GetMethodParamInt(method, key, math.MinInt64)
if r == math.MinInt64 {
return c.GetParamInt(key, d)
}
return r
}
func (c URL) GetMethodParam(method string, key string, d string) string {
if r = c.Params.Get("methods." + method + "." + key); r == "" {
r = d
}
return r
}
func (c *URL) RemoveParams(set *container.HashSet) {
for k, _ := range set.Items {
s := k.(string)
delete(c.Params, s)
}
}
func (c *URL) SetParams(m url.Values) {
for k, _ := range m {
c.Params.Set(k, m.Get(k))
}
}
// ToMap transfer URL to Map
func (c URL) ToMap() map[string]string {
paramsMap := make(map[string]string)
for k, v := range c.Params {
paramsMap[k] = v[0]
}
if c.Protocol != "" {
paramsMap["protocol"] = c.Protocol
}
if c.Username != "" {
paramsMap["username"] = c.Username
}
if c.Password != "" {
paramsMap["password"] = c.Password
}
if c.Location != "" {
paramsMap["host"] = strings.Split(c.Location, ":")[0]
var port string
if strings.Contains(c.Location, ":") {
port = strings.Split(c.Location, ":")[1]
} else {
port = "0"
}
paramsMap["port"] = port
}
if c.Protocol != "" {
paramsMap["protocol"] = c.Protocol
}
if c.Path != "" {
paramsMap["path"] = c.Path
}
// configuration > reference config >service config
// in this function we should merge the reference local url config into the service url from registry.
//TODO configuration merge, in the future , the configuration center's config should merge too.
func MergeUrl(serviceUrl *URL, referenceUrl *URL) *URL {
mergedUrl := serviceUrl.Clone()
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
var methodConfigMergeFcn = []func(method string){}
//iterator the referenceUrl if serviceUrl not have the key ,merge in
for k, v := range referenceUrl.Params {
if _, ok := mergedUrl.Params[k]; !ok {
mergedUrl.Params.Set(k, v[0])
}
}
//loadBalance strategy config
if v := referenceUrl.Params.Get(constant.LOADBALANCE_KEY); v != "" {
mergedUrl.Params.Set(constant.LOADBALANCE_KEY, v)
}
methodConfigMergeFcn = append(methodConfigMergeFcn, func(method string) {
if v := referenceUrl.Params.Get(method + "." + constant.LOADBALANCE_KEY); v != "" {
mergedUrl.Params.Set(method+"."+constant.LOADBALANCE_KEY, v)
}
})
//cluster strategy config
if v := referenceUrl.Params.Get(constant.CLUSTER_KEY); v != "" {
mergedUrl.Params.Set(constant.CLUSTER_KEY, v)
}
methodConfigMergeFcn = append(methodConfigMergeFcn, func(method string) {
if v := referenceUrl.Params.Get(method + "." + constant.CLUSTER_KEY); v != "" {
mergedUrl.Params.Set(method+"."+constant.CLUSTER_KEY, v)
}
})
//remote timestamp
if v := serviceUrl.Params.Get(constant.TIMESTAMP_KEY); v != "" {
mergedUrl.Params.Set(constant.REMOTE_TIMESTAMP_KEY, v)
mergedUrl.Params.Set(constant.TIMESTAMP_KEY, referenceUrl.Params.Get(constant.TIMESTAMP_KEY))
}
//finally execute methodConfigMergeFcn
for _, method := range referenceUrl.Methods {
for _, fcn := range methodConfigMergeFcn {
fcn("methods." + method)
}
}
return mergedUrl
}
func (c *URL) Clone() *URL {
newUrl := &URL{}
copier.Copy(newUrl, c)
newUrl.Params = url.Values{}
for k, v := range c.Params {
newUrl.Params[k] = v
}