Repository: cloudwego/hertz
Pull request #238 (2022-09-10 03:11:56)

refactor: client init m and ms logic
#### What type of PR is this?
refactor

#### Check the PR title.
<!--
The description of the title will be attached in Release Notes, 
so please describe it from user-oriented, what this PR does / why we need it.
Please check your PR title with the below requirements:
-->
- [x] This PR title match the format: \<type\>(optional scope): \<description\>
- [x] The description of this PR title is user-oriented and clear enough for others to understand.


#### (Optional) Translate the PR title into Chinese.
重构 client 初始化 m 和 ms 的逻辑

#### (Optional) More detail description for this PR(en: English/zh: Chinese).
<!--
Provide more detailed info for review. If it is a perf type PR, perf data is suggested to give.
-->
en:  client init m and ms logic
zh(optional): 重构 client 初始化 m 和 ms 的逻辑

#### Which issue(s) this PR fixes:
<!--
Automatically closes linked issue when PR is merged.
Eg: `Fixes #<issue number>`, or `Fixes (paste link of issue)`.
-->


codecov[bot]: # [Codecov](https://codecov.io/gh/cloudwego/hertz/pull/238?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudwego) Report
> Merging [#238](https://codecov.io/gh/cloudwego/hertz/pull/238?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudwego) (1891d91) into [develop](https://codecov.io/gh/cloudwego/hertz/commit/dcb0b5a1861a1e2e4629a8607a3ca9c89009ac55?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudwego) (dcb0b5a) will **decrease** coverage by `0.06%`.
> The diff coverage is `100.00%`.

```diff
@@             Coverage Diff             @@
##           develop     #238      +/-   ##
===========================================
- Coverage    59.14%   59.07%   -0.07%     
===========================================
  Files           79       79              
  Lines         8111     8107       -4     
===========================================
- Hits          4797     4789       -8     
- Misses        2960     2964       +4     
  Partials       354      354              
```


| [Impacted Files](https://codecov.io/gh/cloudwego/hertz/pull/238?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudwego) | Coverage Δ | |
|---|---|---|
| [pkg/app/client/client.go](https://codecov.io/gh/cloudwego/hertz/pull/238/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudwego#diff-cGtnL2FwcC9jbGllbnQvY2xpZW50Lmdv) | `71.42% <100.00%> (-0.80%)` | :arrow_down: |
| [pkg/common/timer/timer.go](https://codecov.io/gh/cloudwego/hertz/pull/238/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudwego#diff-cGtnL2NvbW1vbi90aW1lci90aW1lci5nbw==) | `61.90% <0.00%> (-19.05%)` | :arrow_down: |

Help us with your feedback. Take ten seconds to tell us [how you rate us](https://about.codecov.io/nps?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudwego). Have a feature suggestion? [Share it here.](https://app.codecov.io/gh/feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudwego)


Patch:
diff --git a/go.mod b/go.mod
index 7f9b9b5..cf0ca35 100644
--- a/go.mod
+++ b/go.mod
@@ -6,7 +6,7 @@ require (
 	github.com/bytedance/go-tagexpr/v2 v2.9.2
 	github.com/bytedance/gopkg v0.0.0-20220413063733-65bf48ffb3a7
 	github.com/bytedance/sonic v1.3.5
-	github.com/cloudwego/netpoll v0.2.4
+	github.com/cloudwego/netpoll v0.2.6
 	github.com/fsnotify/fsnotify v1.5.4
 	golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
 	golang.org/x/sys v0.0.0-20220412211240-33da011f77ad
diff --git a/go.sum b/go.sum
index 4ec04ce..1ed067c 100644
--- a/go.sum
+++ b/go.sum
@@ -2,14 +2,12 @@ github.com/bytedance/go-tagexpr/v2 v2.9.2 h1:QySJaAIQgOEDQBLS3x9BxOWrnhqu5sQ+f6H
 github.com/bytedance/go-tagexpr/v2 v2.9.2/go.mod h1:5qsx05dYOiUXOUgnQ7w3Oz8BYs2qtM/bJokdLb79wRM=
 github.com/bytedance/gopkg v0.0.0-20220413063733-65bf48ffb3a7 h1:PtwsQyQJGxf8iaPptPNaduEIu9BnrNms+pcRdHAxZaM=
 github.com/bytedance/gopkg v0.0.0-20220413063733-65bf48ffb3a7/go.mod h1:2ZlV9BaUH4+NXIBF0aMdKKAnHTzqH+iMU4KUjAbL23Q=
-github.com/bytedance/sonic v1.3.0 h1:T2rlvNytw6bTmczlAXvGqmuMzIqGJBOsJKYwRPWR7Y8=
-github.com/bytedance/sonic v1.3.0/go.mod h1:V973WhNhGmvHxW6nQmsHEfHaoU9F3zTF+93rH03hcUQ=
 github.com/bytedance/sonic v1.3.5 h1:xfBNhsG3QCC+AMCmCHxNQg0StI5IM/B9Jtwjqi5WlI0=
 github.com/bytedance/sonic v1.3.5/go.mod h1:V973WhNhGmvHxW6nQmsHEfHaoU9F3zTF+93rH03hcUQ=
 github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06 h1:1sDoSuDPWzhkdzNVxCxtIaKiAe96ESVPv8coGwc1gZ4=
 github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
-github.com/cloudwego/netpoll v0.2.4 h1:Kbo2HA1cXEgoy/bu1jSNrjcqZj2diENcJqLy6vKiROU=
-github.com/cloudwego/netpoll v0.2.4/go.mod h1:1T2WVuQ+MQw6h6DpE45MohSvDTKdy2DlzCx2KsnPI4E=
+github.com/cloudwego/netpoll v0.2.6 h1:vzN8cyayoa9RdCOG87tqkYO/j2hA4SMLC+vkcNUq6uI=
+github.com/cloudwego/netpoll v0.2.6/go.mod h1:1T2WVuQ+MQw6h6DpE45MohSvDTKdy2DlzCx2KsnPI4E=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
diff --git a/pkg/app/client/client.go b/pkg/app/client/client.go
index 9800638..dccfede 100644
--- a/pkg/app/client/client.go
+++ b/pkg/app/client/client.go
@@ -458,20 +458,12 @@ func (c *Client) do(ctx context.Context, req *protocol.Request, resp *protocol.R
 	startCleaner := false
 
 	c.mLock.Lock()
+
 	m := c.m
 	if isTLS {
 		m = c.ms
 	}
 
-	if m == nil {
-		m = make(map[string]client.HostClient)
-		if isTLS {
-			c.ms = m
-		} else {
-			c.m = m
-		}
-	}
-
 	h := string(host)
 	hc := m[h]
 	if hc == nil {
@@ -548,6 +540,8 @@ func NewClient(opts ...config.ClientOption) (*Client, error) {
 	}
 	c := &Client{
 		options: opt,
+		m:       make(map[string]client.HostClient),
+		ms:      make(map[string]client.HostClient),
 	}
 
 	return c, nil
diff --git a/pkg/protocol/client/client.go b/pkg/protocol/client/client.go
index 962abfb..e5e6cf1 100644
--- a/pkg/protocol/client/client.go
+++ b/pkg/protocol/client/client.go
@@ -43,7 +43,6 @@ package client
 
 import (
 	"context"
-	"crypto/tls"
 	"sync"
 	"time"
 
@@ -79,34 +78,6 @@ type Doer interface {
 	Do(ctx context.Context, req *protocol.Request, resp *protocol.Response) error
 }
 
-type HostClientConfig struct {
-	DynamicConfig
-
-	Name string
-
-	NoDefaultUserAgentHeader      bool
-	DialDualStack                 bool
-	DisableHeaderNamesNormalizing bool
-	DisablePathNormalizing        bool
-	IsTLS                         bool
-
-	TLSConfig *tls.Config
-
-	MaxConns                  int
-	MaxIdempotentCallAttempts int
-	MaxResponseBodySize       int
-
-	RetryIf            RetryIfFunc
-	ResponseBodyStream bool
-
-	DialTimeout         time.Duration
-	MaxIdleConnDuration time.Duration
-	MaxConnDuration     time.Duration
-	ReadTimeout         time.Duration
-	WriteTimeout        time.Duration
-	MaxConnWaitTimeout  time.Duration
-}
-
 // DynamicConfig is config set which will be confirmed when starts a request.
 type DynamicConfig struct {
 	Addr     string
diff --git a/pkg/protocol/http1/client.go b/pkg/protocol/http1/client.go
index 12d2ccc..4be7be1 100644
--- a/pkg/protocol/http1/client.go
+++ b/pkg/protocol/http1/client.go
@@ -90,6 +90,19 @@ type HostClient struct {
 
 	*ClientOptions
 
+	// Comma-separated list of upstream HTTP server host addresses,
+	// which are passed to Dialer in a round-robin manner.
+	//
+	// Each address may contain port if default dialer is used.
+	// For example,
+	//
+	//    - foobar.com:80
+	//    - foobar.com:443
+	//    - foobar.com:8080
+	Addr     string
+	IsTLS    bool
+	ProxyURI *protocol.URI
+
 	clientName  atomic.Value
 	lastUseTime uint32
 
@@ -1044,17 +1057,6 @@ func NewHostClient(c *ClientOptions) client.HostClient {
 }
 
 type ClientOptions struct {
-	// Comma-separated list of upstream HTTP server host addresses,
-	// which are passed to Dialer in a round-robin manner.
-	//
-	// Each address may contain port if default dialer is used.
-	// For example,
-	//
-	//    - foobar.com:80
-	//    - foobar.com:443
-	//    - foobar.com:8080
-	Addr string
-
 	// Client name. Used in User-Agent request header.
 	Name string
 
@@ -1086,8 +1088,6 @@ type ClientOptions struct {
 	// Optional TLS config.
 	TLSConfig *tls.Config
 
-	IsTLS bool
-
 	// Maximum number of connections which may be established to all hosts
 	// listed in Addr.
 	//
@@ -1168,6 +1168,4 @@ type ClientOptions struct {
 
 	// ResponseBodyStream enables response body streaming
 	ResponseBodyStream bool
-
-	ProxyURI *protocol.URI
 }
diff --git a/pkg/route/engine.go b/pkg/route/engine.go
index 06f1f62..a3e1baa 100644
--- a/pkg/route/engine.go
+++ b/pkg/route/engine.go
@@ -340,6 +340,7 @@ func (engine *Engine) alpnEnable() bool {
 }
 
 func (engine *Engine) listenAndServe() error {
+	hlog.Infof("HERTZ: Using network library=%s", GetTransporterName())
 	return engine.transport.ListenAndServe(engine.onData)
 }
 
diff --git a/pkg/route/tree.go b/pkg/route/tree.go
index 774c8a3..4c2d9de 100644
--- a/pkg/route/tree.go
+++ b/pkg/route/tree.go
@@ -361,7 +361,8 @@ func (r *router) find(path string, paramsPointer *param.Params, unescape bool) (
 				searchIndex = searchIndex + len(cn.prefix)
 			} else {
 				// not equal
-				if (len(cn.prefix) == len(search)+1) && (cn.prefix[len(search)]) == '/' && (cn.handlers != nil || cn.anyChild != nil) {
+				if (len(cn.prefix) == len(search)+1) &&
+					(cn.prefix[len(search)]) == '/' && cn.prefix[:len(search)] == search && (cn.handlers != nil || cn.anyChild != nil) {
 					res.tsr = true
 				}
 				// No matching prefix, let's backtrack to the first possible alternative node of the decision path
@@ -394,7 +395,7 @@ func (r *router) find(path string, paramsPointer *param.Params, unescape bool) (
 		}
 
 		if search == nilString {
-			if cd := cn.findChild('/'); cd != nil && cd.handlers != nil {
+			if cd := cn.findChild('/'); cd != nil && (cd.handlers != nil || cd.anyChild != nil) {
 				res.tsr = true
 			}
 		}
@@ -419,7 +420,7 @@ func (r *router) find(path string, paramsPointer *param.Params, unescape bool) (
 			search = search[i:]
 			searchIndex = searchIndex + i
 			if search == nilString {
-				if cd := cn.findChild('/'); cd != nil && cd.handlers != nil {
+				if cd := cn.findChild('/'); cd != nil && (cd.handlers != nil || cd.anyChild != nil) {
 					res.tsr = true
 				}
 			}
diff --git a/version.go b/version.go
index d71e454..8119d30 100644
--- a/version.go
+++ b/version.go
@@ -19,5 +19,5 @@ package hertz
 // Name and Version info of this framework, used for statistics and debug
 const (
 	Name    = "Hertz"
-	Version = "v0.3.0"
+	Version = "v0.3.1"
 )


Test patch:
diff --git a/pkg/app/client/client_test.go b/pkg/app/client/client_test.go
index 0efe406..20c25e7 100644
--- a/pkg/app/client/client_test.go
+++ b/pkg/app/client/client_test.go
@@ -392,8 +392,8 @@ func TestClientReadTimeout(t *testing.T) {
 			ReadTimeout:               time.Second * 4,
 			MaxIdempotentCallAttempts: 1,
 			Dialer:                    standard.NewDialer(),
-			Addr:                      opt.Addr,
 		},
+		Addr: opt.Addr,
 	}
 
 	req := protocol.AcquireRequest()
@@ -689,9 +689,9 @@ func TestHostClientPendingRequests(t *testing.T) {
 
 	c := &http1.HostClient{
 		ClientOptions: &http1.ClientOptions{
-			Addr:   "foobar",
 			Dialer: newMockDialerWithCustomFunc(opt.Network, opt.Addr, time.Second, nil),
 		},
+		Addr: "foobar",
 	}
 
 	pendingRequests := c.PendingRequests()
@@ -780,10 +780,10 @@ func TestHostClientMaxConnsWithDeadline(t *testing.T) {
 
 	c := &http1.HostClient{
 		ClientOptions: &http1.ClientOptions{
-			Addr:     "foobar",
 			Dialer:   newMockDialerWithCustomFunc(opt.Network, opt.Addr, time.Second, nil),
 			MaxConns: 1,
 		},
+		Addr: "foobar",
 	}
 
 	for i := 0; i < 5; i++ {
@@ -849,10 +849,10 @@ func TestHostClientMaxConnDuration(t *testing.T) {
 
 	c := &http1.HostClient{
 		ClientOptions: &http1.ClientOptions{
-			Addr:            "foobar",
 			Dialer:          newMockDialerWithCustomFunc(opt.Network, opt.Addr, time.Second, nil),
 			MaxConnDuration: 10 * time.Millisecond,
 		},
+		Addr: "foobar",
 	}
 
 	for i := 0; i < 5; i++ {
@@ -895,11 +895,11 @@ func TestHostClientMultipleAddrs(t *testing.T) {
 	dialsCount := make(map[string]int)
 	c := &http1.HostClient{
 		ClientOptions: &http1.ClientOptions{
-			Addr: "foo,bar,baz",
 			Dialer: newMockDialerWithCustomFunc(opt.Network, opt.Addr, 1*time.Second, func(network, addr string, timeout time.Duration, tlsConfig *tls.Config) {
 				dialsCount[addr]++
 			}),
 		},
+		Addr: "foo,bar,baz",
 	}
 
 	for i := 0; i < 9; i++ {
@@ -961,9 +961,9 @@ func TestClientFollowRedirects(t *testing.T) {
 
 	c := &http1.HostClient{
 		ClientOptions: &http1.ClientOptions{
-			Addr:   "xxx",
 			Dialer: newMockDialerWithCustomFunc(opt.Network, opt.Addr, 1*time.Second, nil),
 		},
+		Addr: "xxx",
 	}
 
 	for i := 0; i < 10; i++ {
@@ -1055,11 +1055,11 @@ func TestHostClientMaxConnWaitTimeoutSuccess(t *testing.T) {
 
 	c := &http1.HostClient{
 		ClientOptions: &http1.ClientOptions{
-			Addr:               "foobar",
 			Dialer:             newMockDialerWithCustomFunc(opt.Network, opt.Addr, time.Second, nil),
 			MaxConns:           1,
 			MaxConnWaitTimeout: 200 * time.Millisecond,
 		},
+		Addr: "foobar",
 	}
 
 	for i := 0; i < 5; i++ {
@@ -1124,11 +1124,11 @@ func TestHostClientMaxConnWaitTimeoutError(t *testing.T) {
 
 	c := &http1.HostClient{
 		ClientOptions: &http1.ClientOptions{
-			Addr:               "foobar",
 			Dialer:             newMockDialerWithCustomFunc(opt.Network, opt.Addr, time.Second, nil),
 			MaxConns:           1,
 			MaxConnWaitTimeout: 10 * time.Millisecond,
 		},
+		Addr: "foobar",
 	}
 
 	var errNoFreeConnsCount uint32
diff --git a/pkg/protocol/http1/client_test.go b/pkg/protocol/http1/client_test.go
index ae0871a..299a028 100644
--- a/pkg/protocol/http1/client_test.go
+++ b/pkg/protocol/http1/client_test.go
@@ -74,13 +74,13 @@ func TestHostClientMaxConnWaitTimeoutWithEarlierDeadline(t *testing.T) {
 
 	c := &HostClient{
 		ClientOptions: &ClientOptions{
-			Addr: "foobar",
 			Dialer: newSlowConnDialer(func(network, addr string) (network.Conn, error) {
 				return mock.SlowReadDialer(addr)
 			}),
 			MaxConns:           1,
 			MaxConnWaitTimeout: 50 * time.Millisecond,
 		},
+		Addr: "foobar",
 	}
 
 	var errTimeoutCount uint32
diff --git a/pkg/route/tree_test.go b/pkg/route/tree_test.go
index cdb190b..946bfd6 100644
--- a/pkg/route/tree_test.go
+++ b/pkg/route/tree_test.go
@@ -471,6 +471,14 @@ func TestTreeTrailingSlashRedirect(t *testing.T) {
 		"/no/a",
 		"/no/b",
 		"/api/hello/:name",
+		"/user/:name/*id",
+		"/resource",
+		"/r/*id",
+		"/book/biz/:name",
+		"/book/biz/abc",
+		"/book/biz/abc/bar",
+		"/book/:page/:name",
+		"/book/hello/:name/biz/",
 	}
 	for _, route := range routes {
 		recv := catchPanic(func() {
@@ -496,6 +504,11 @@ func TestTreeTrailingSlashRedirect(t *testing.T) {
 		"/admin/config/",
 		"/admin/config/permissions/",
 		"/doc/",
+		"/user/name",
+		"/r",
+		"/book/hello/a/biz",
+		"/book/biz/foo/",
+		"/book/biz/abc/bar/",
 	}
 	v := make(param.Params, 0, 10)
 	for _, route := range tsrRoutes {
@@ -514,6 +527,10 @@ func TestTreeTrailingSlashRedirect(t *testing.T) {
 		"/_",
 		"/_/",
 		"/api/world/abc",
+		"/book",
+		"/book/",
+		"/book/hello/a/abc",
+		"/book/biz/abc/biz",
 	}
 	for _, route := range noTsrRoutes {
 		value := tree.find(route, &v, false)