From f3295d2ba7f39bee600b87957b481b8487bf982d Mon Sep 17 00:00:00 2001 From: GuangyuFan <97507466+FGYFFFF@users.noreply.github.com> Date: Mon, 3 Apr 2023 11:17:48 +0800 Subject: [PATCH 01/10] fix(hz): hz update failed for modifying of '.hz' on windows (#695) --- cmd/hz/meta/manifest.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/hz/meta/manifest.go b/cmd/hz/meta/manifest.go index a4a743433..0c6ecd05f 100644 --- a/cmd/hz/meta/manifest.go +++ b/cmd/hz/meta/manifest.go @@ -61,12 +61,12 @@ func (manifest *Manifest) InitAndValidate(dir string) error { return nil } -const hzTitle = "// Code generated by hz. DO NOT EDIT.\n" +const hzTitle = "// Code generated by hz. DO NOT EDIT." func (manifest *Manifest) String() string { conf, _ := yaml.Marshal(*manifest) - return hzTitle + "\n" + + return hzTitle + "\n\n" + string(conf) } From be628003b69db1a67657f74c776bccbef35062c5 Mon Sep 17 00:00:00 2001 From: GuangyuFan <97507466+FGYFFFF@users.noreply.github.com> Date: Tue, 4 Apr 2023 11:04:57 +0800 Subject: [PATCH 02/10] feat(hz): support container type as request/response type for thrift (#697) --- cmd/hz/thrift/ast.go | 8 +++--- cmd/hz/thrift/resolver.go | 54 +++++++++++++++++++++++++++++++++++++-- 2 files changed, 56 insertions(+), 6 deletions(-) diff --git a/cmd/hz/thrift/ast.go b/cmd/hz/thrift/ast.go index 9622ed8d5..bbee88d45 100644 --- a/cmd/hz/thrift/ast.go +++ b/cmd/hz/thrift/ast.go @@ -98,19 +98,19 @@ func astToService(ast *parser.Thrift, resolver *Resolver, cmdType string) ([]*ge if len(m.Arguments) > 1 { logs.Warnf("function '%s' has more than one argument, but only the first can be used in hertz now", m.GetName()) } - rt, err := resolver.ResolveIdentifier(m.Arguments[0].GetType().GetName()) + var err error + reqName, err = resolver.ResolveTypeName(m.Arguments[0].GetType()) if err != nil { return nil, err } - reqName = rt.Expression() } var respName string if !m.Oneway { - respType, err := resolver.ResolveIdentifier(m.GetFunctionType().GetName()) + var err error + respName, err = resolver.ResolveTypeName(m.GetFunctionType()) if err != nil { return nil, err } - respName = respType.Expression() } sr, _ := util.GetFirstKV(getAnnotations(m.Annotations, SerializerTags)) diff --git a/cmd/hz/thrift/resolver.go b/cmd/hz/thrift/resolver.go index 9b40b5c54..0bcb4e3eb 100644 --- a/cmd/hz/thrift/resolver.go +++ b/cmd/hz/thrift/resolver.go @@ -282,6 +282,44 @@ func (resolver *Resolver) ResolveIdentifier(id string) (ret ResolvedSymbol, err return } +func (resolver *Resolver) ResolveTypeName(typ *parser.Type) (string, error) { + switch typ.GetCategory() { + case parser.Category_Map: + keyType, err := resolver.ResolveTypeName(typ.GetKeyType()) + if err != nil { + return "", err + } + if typ.GetKeyType().GetCategory().IsStruct() { + keyType = "*" + keyType + } + valueType, err := resolver.ResolveTypeName(typ.GetValueType()) + if err != nil { + return "", err + } + if typ.GetValueType().GetCategory().IsStruct() { + valueType = "*" + valueType + } + return fmt.Sprintf("map[%s]%s", keyType, valueType), nil + case parser.Category_List, parser.Category_Set: + // list/set -> []element for thriftgo + // valueType refers the element type for list/set + elemType, err := resolver.ResolveTypeName(typ.GetValueType()) + if err != nil { + return "", err + } + if typ.GetValueType().GetCategory().IsStruct() { + elemType = "*" + elemType + } + return fmt.Sprintf("[]%s", elemType), err + } + rt, err := resolver.ResolveIdentifier(typ.GetName()) + if err != nil { + return "", err + } + + return rt.Expression(), nil +} + func (resolver *Resolver) Get(name string) *Symbol { s, ok := resolver.root[name] if ok { @@ -348,8 +386,8 @@ func (resolver *Resolver) LoadAll(ast *parser.Thrift) error { return nil } -func LoadBaseIdentifier() map[string]*Symbol { - ret := make(NameSpace, 13) +func LoadBaseIdentifier() NameSpace { + ret := make(NameSpace, 16) ret["true"] = &ConstTrue ret["false"] = &ConstFalse @@ -394,6 +432,18 @@ func LoadBaseIdentifier() map[string]*Symbol { Type: model.TypeBinary, Scope: &BaseThrift, } + ret["list"] = &Symbol{ + Type: model.TypeBaseList, + Scope: &BaseThrift, + } + ret["set"] = &Symbol{ + Type: model.TypeBaseSet, + Scope: &BaseThrift, + } + ret["map"] = &Symbol{ + Type: model.TypeBaseMap, + Scope: &BaseThrift, + } return ret } From 8e9fce9b047589fbb05fa13930e0da086fbb05e2 Mon Sep 17 00:00:00 2001 From: zekin Date: Fri, 7 Apr 2023 17:27:23 +0800 Subject: [PATCH 03/10] fix: stream client not close the conn when ConnectionClose is true (#702) --- pkg/app/client/client_test.go | 52 +++++++++++++++++++++++++++++++++++ pkg/protocol/http1/client.go | 16 +++++++++-- 2 files changed, 66 insertions(+), 2 deletions(-) diff --git a/pkg/app/client/client_test.go b/pkg/app/client/client_test.go index b6eb26340..8862d8401 100644 --- a/pkg/app/client/client_test.go +++ b/pkg/app/client/client_test.go @@ -1951,6 +1951,58 @@ func TestClientReadResponseBodyStreamWithDoubleRequest(t *testing.T) { } } +func TestClientReadResponseBodyStreamWithConnectionClose(t *testing.T) { + part1 := "" + for i := 0; i < 8192; i++ { + part1 += "a" + } + + opt := config.NewOptions([]config.Option{}) + opt.Addr = "127.0.0.1:10036" + engine := route.NewEngine(opt) + engine.POST("/", func(ctx context.Context, c *app.RequestContext) { + c.String(consts.StatusOK, part1) + }) + go engine.Run() + time.Sleep(100 * time.Millisecond) + + client, _ := NewClient(WithResponseBodyStream(true)) + + // first req + req, resp := protocol.AcquireRequest(), protocol.AcquireResponse() + defer func() { + protocol.ReleaseRequest(req) + protocol.ReleaseResponse(resp) + }() + req.SetConnectionClose() + req.SetMethod(consts.MethodPost) + req.SetRequestURI("http://127.0.0.1:10036") + + err := client.Do(context.Background(), req, resp) + if err != nil { + t.Fatalf("client Do error=%v", err.Error()) + } + + assert.DeepEqual(t, part1, string(resp.Body())) + + // second req + req1, resp1 := protocol.AcquireRequest(), protocol.AcquireResponse() + defer func() { + protocol.ReleaseRequest(req1) + protocol.ReleaseResponse(resp1) + }() + req1.SetConnectionClose() + req1.SetMethod(consts.MethodPost) + req1.SetRequestURI("http://127.0.0.1:10036") + + err = client.Do(context.Background(), req1, resp1) + if err != nil { + t.Fatalf("client Do error=%v", err.Error()) + } + + assert.DeepEqual(t, part1, string(resp1.Body())) +} + type mockDialer struct { network.Dialer customDialerFunc func(network, address string, timeout time.Duration, tlsConfig *tls.Config) diff --git a/pkg/protocol/http1/client.go b/pkg/protocol/http1/client.go index 8a35aed73..353ecc879 100644 --- a/pkg/protocol/http1/client.go +++ b/pkg/protocol/http1/client.go @@ -619,11 +619,21 @@ func (c *HostClient) doNonNilReqResp(req *protocol.Request, resp *protocol.Respo } zr := c.acquireReader(conn) + // init here for passing in ReadBodyStream's closure + // and this value will be assigned after reading Response's Header + // + // This is to solve the circular dependency problem of Response and BodyStream + shouldCloseConn := false + if !c.ResponseBodyStream { err = respI.ReadHeaderAndLimitBody(resp, zr, c.MaxResponseBodySize) } else { err = respI.ReadBodyStream(resp, zr, c.MaxResponseBodySize, func() error { - c.releaseConn(cc) + if shouldCloseConn { + c.closeConn(cc) + } else { + c.releaseConn(cc) + } return nil }) } @@ -638,11 +648,13 @@ func (c *HostClient) doNonNilReqResp(req *protocol.Request, resp *protocol.Respo zr.Release() //nolint:errcheck + shouldCloseConn = resetConnection || req.ConnectionClose() || resp.ConnectionClose() + if c.ResponseBodyStream { return false, err } - if resetConnection || req.ConnectionClose() || resp.ConnectionClose() { + if shouldCloseConn { c.closeConn(cc) } else { c.releaseConn(cc) From c5b81ded562f2268ca5be368e240dd6572f86716 Mon Sep 17 00:00:00 2001 From: zekin Date: Fri, 7 Apr 2023 17:29:10 +0800 Subject: [PATCH 04/10] fix: standard conn may panic after close (#698) Co-authored-by: yinxuran.lucky --- go.mod | 1 + go.sum | 16 +++++++ pkg/common/bytebufferpool/pool_test.go | 2 +- pkg/network/standard/connection.go | 64 +++++++++++++++---------- pkg/network/standard/connection_test.go | 44 +++++++++++++++-- 5 files changed, 96 insertions(+), 31 deletions(-) diff --git a/go.mod b/go.mod index 2c7a97539..79b5fabe2 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.16 require ( github.com/bytedance/go-tagexpr/v2 v2.9.2 github.com/bytedance/gopkg v0.0.0-20220413063733-65bf48ffb3a7 + github.com/bytedance/mockey v1.2.1 github.com/bytedance/sonic v1.8.1 github.com/cloudwego/netpoll v0.3.1 github.com/fsnotify/fsnotify v1.5.4 diff --git a/go.sum b/go.sum index fe7ecbf0a..f9ea5da26 100644 --- a/go.sum +++ b/go.sum @@ -2,6 +2,8 @@ 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/mockey v1.2.1 h1:g84ngI88hz1DR4wZTL3yOuqlEcq67MretBfQUdXwrmw= +github.com/bytedance/mockey v1.2.1/go.mod h1:+Jm/fzWZAuhEDrPXVjDf/jLM2BlLXJkwk94zf2JZ3X4= github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= github.com/bytedance/sonic v1.8.1 h1:NqAHCaGaTzro0xMmnTCLUyRlbEP6r8MCA1cJUrH3Pu4= github.com/bytedance/sonic v1.8.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= @@ -20,17 +22,25 @@ github.com/golang/protobuf v1.5.0 h1:LUVKkCeviFUMKqHa4tXIIij/lbhnMbP7Fn5wKdKkRh4 github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/henrylee2cn/ameda v1.4.8/go.mod h1:liZulR8DgHxdK+MEwvZIylGnmcjzQ6N6f2PlWe7nEO4= github.com/henrylee2cn/ameda v1.4.10 h1:JdvI2Ekq7tapdPsuhrc4CaFiqw6QXFvZIULWJgQyCAk= github.com/henrylee2cn/ameda v1.4.10/go.mod h1:liZulR8DgHxdK+MEwvZIylGnmcjzQ6N6f2PlWe7nEO4= github.com/henrylee2cn/goutil v0.0.0-20210127050712-89660552f6f8 h1:yE9ULgp02BhYIrO6sdV/FPe0xQM6fNHkVQW2IAymfM0= github.com/henrylee2cn/goutil v0.0.0-20210127050712-89660552f6f8/go.mod h1:Nhe/DM3671a5udlv2AdV2ni/MZzgfv2qrPL5nIi3EGQ= +github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/nyaruka/phonenumbers v1.0.55 h1:bj0nTO88Y68KeUQ/n3Lo2KgK7lM1hF7L9NFuwcCl3yg= github.com/nyaruka/phonenumbers v1.0.55/go.mod h1:sDaTZ/KPX5f8qyV9qN+hIm+4ZBARJrupC6LuhshJq1U= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= @@ -50,13 +60,19 @@ github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= +golang.org/x/arch v0.0.0-20201008161808-52c3e6f60cff/go.mod h1:flIaEI6LNU6xOCD5PaJvn9wGP0agmIOqjrtsKGRguv4= golang.org/x/arch v0.0.0-20210923205945-b76863e36670 h1:18EFjUmQOcUvxNYSkA6jO9VAiXCnxFY6NyDX0bHDmkU= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20220110181412-a018aaa089fe/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad h1:ntjMns5wyP/fN65tdBD4g8J5w8n015+iIIs9rtjXkY0= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= diff --git a/pkg/common/bytebufferpool/pool_test.go b/pkg/common/bytebufferpool/pool_test.go index 1c2bc7e9f..0a26197e4 100644 --- a/pkg/common/bytebufferpool/pool_test.go +++ b/pkg/common/bytebufferpool/pool_test.go @@ -97,7 +97,7 @@ func TestPoolVariousSizesConcurrent(t *testing.T) { for i := 0; i < concurrency; i++ { select { case <-ch: - case <-time.After(3 * time.Second): + case <-time.After(10 * time.Second): t.Fatalf("timeout") } } diff --git a/pkg/network/standard/connection.go b/pkg/network/standard/connection.go index 5ab9a8c02..65fd08f25 100644 --- a/pkg/network/standard/connection.go +++ b/pkg/network/standard/connection.go @@ -21,6 +21,7 @@ import ( "errors" "io" "net" + "runtime" "strconv" "syscall" "time" @@ -174,9 +175,6 @@ func (c *Conn) ReadFrom(r io.Reader) (n int64, err error) { // Close closes the connection func (c *Conn) Close() error { - c.inputBuffer.release() - c.outputBuffer.release() - c.inputBuffer, c.outputBuffer = nil, nil return c.c.Close() } @@ -548,20 +546,27 @@ func newConn(c net.Conn, size int) network.Conn { if size > maxSize { maxSize = size } - inputNode := newBufferNode(maxSize) + + node := newBufferNode(maxSize) + inputBuffer := &linkBuffer{ + head: node, + read: node, + write: node, + } + runtime.SetFinalizer(inputBuffer, (*linkBuffer).release) + outputNode := newBufferNode(0) + outputBuffer := &linkBuffer{ + head: outputNode, + write: outputNode, + } + runtime.SetFinalizer(outputBuffer, (*linkBuffer).release) + return &Conn{ - c: c, - inputBuffer: &linkBuffer{ - head: inputNode, - read: inputNode, - write: inputNode, - }, - outputBuffer: &linkBuffer{ - head: outputNode, - write: outputNode, - }, - maxSize: maxSize, + c: c, + inputBuffer: inputBuffer, + outputBuffer: outputBuffer, + maxSize: maxSize, } } @@ -570,21 +575,28 @@ func newTLSConn(c net.Conn, size int) network.Conn { if size > maxSize { maxSize = size } + node := newBufferNode(maxSize) + inputBuffer := &linkBuffer{ + head: node, + read: node, + write: node, + } + runtime.SetFinalizer(inputBuffer, (*linkBuffer).release) + outputNode := newBufferNode(0) + outputBuffer := &linkBuffer{ + head: outputNode, + write: outputNode, + } + runtime.SetFinalizer(outputBuffer, (*linkBuffer).release) + return &TLSConn{ Conn{ - c: c, - inputBuffer: &linkBuffer{ - head: node, - read: node, - write: node, - }, - outputBuffer: &linkBuffer{ - head: outputNode, - write: outputNode, - }, - maxSize: maxSize, + c: c, + inputBuffer: inputBuffer, + outputBuffer: outputBuffer, + maxSize: maxSize, }, } } diff --git a/pkg/network/standard/connection_test.go b/pkg/network/standard/connection_test.go index 9b0fafd38..6fcc456bf 100644 --- a/pkg/network/standard/connection_test.go +++ b/pkg/network/standard/connection_test.go @@ -22,11 +22,14 @@ import ( "errors" "io" "net" + "runtime" "strings" + "sync/atomic" "syscall" "testing" "time" + . "github.com/bytedance/mockey" "github.com/cloudwego/hertz/pkg/common/test/assert" ) @@ -145,11 +148,11 @@ func TestPeekRelease(t *testing.T) { } // test cross node - b, _ = conn.Peek(100000000) - if len(b) != 100000000 { + b, _ = conn.Peek(1000000) + if len(b) != 1000000 { t.Errorf("unexpected len(b): %v, expected 1", len(b)) } - conn.Skip(100000000) + conn.Skip(1000000) conn.Release() // test maxSize @@ -184,7 +187,7 @@ func TestReadBytes(t *testing.T) { } bbb, _ := conn.ReadByte() if bbb != 0 { - t.Errorf("unexpected bbb: %v, expected nil", bbb) + t.Errorf("unexpected bbb: %v, expected nil", string(bbb)) } if conn.Len() != 4094 { t.Errorf("unexpected conn.Len: %v, expected 4094", conn.Len()) @@ -286,6 +289,9 @@ func (m *mockConn) ConnectionState() tls.ConnectionState { func (m mockConn) Read(b []byte) (n int, err error) { length := len(b) + for i := 0; i < length; i++ { + b[i] = 0 + } if length > 8192 { return 8192, nil } @@ -342,3 +348,33 @@ func (m *mockAddr) Network() string { func (m *mockAddr) String() string { return m.address } + +var release_count uint32 = 0 + +func mockLinkBufferNodeRelease(b *linkBufferNode) { + atomic.AddUint32(&release_count, 1) + + if !b.readOnly { + free(b.buf) + } + b.readOnly = false + b.buf = nil + b.next = nil + b.malloc, b.off = 0, 0 + bufferPool.Put(b) +} + +func TestConnSetFinalizer(t *testing.T) { + runtime.GC() + time.Sleep(time.Millisecond * 100) + + Mock((*linkBufferNode).Release).To(mockLinkBufferNodeRelease).Build() + + atomic.StoreUint32(&release_count, 0) + _ = newConn(&mockConn{}, 4096) + + runtime.GC() + time.Sleep(time.Millisecond * 100) + + assert.DeepEqual(t, uint32(2), atomic.LoadUint32(&release_count)) +} From b43b9c8c3714458b20638af7b3e1dd87928ce3fb Mon Sep 17 00:00:00 2001 From: zekin Date: Mon, 10 Apr 2023 11:10:49 +0800 Subject: [PATCH 05/10] ci: add compatibility test for old go version (#710) --- .github/workflows/tests.yml | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index b3e284c6f..fbe4130a5 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -1,13 +1,30 @@ name: Tests -on: [ push, pull_request ] +on: [push, pull_request] jobs: + compat-test: + strategy: + matrix: + version: ["1.16", "1.17"] + runs-on: [self-hosted, X64] + steps: + - uses: actions/checkout@v3 + + - name: Set up Go + uses: actions/setup-go@v3 + with: + go-version: ${{ matrix.version }} + + # Just build tests without running them + - name: Build Test + run: go test -run=nope ./... + lint-and-ut: strategy: matrix: - version: [ '1.18', '1.19', '1.20' ] - runs-on: [ self-hosted, X64 ] + version: ["1.18", "1.19", "1.20"] + runs-on: [self-hosted, X64] steps: - uses: actions/checkout@v3 @@ -31,7 +48,7 @@ jobs: ut-windows: strategy: matrix: - version: [ '1.18', '1.19', '1.20' ] + version: ["1.18", "1.19", "1.20"] runs-on: windows-latest steps: - uses: actions/checkout@v3 From 6e52d9a9a87fe92adcfef8c86cf650dc8fbaf3bc Mon Sep 17 00:00:00 2001 From: kinggo Date: Mon, 10 Apr 2023 11:57:23 +0800 Subject: [PATCH 06/10] feat: add CreateUtRequestContext function (#708) --- pkg/common/ut/context.go | 65 +++++++++++++++++++++++++++++++++++ pkg/common/ut/context_test.go | 41 ++++++++++++++++++++++ pkg/common/ut/request.go | 31 +---------------- 3 files changed, 107 insertions(+), 30 deletions(-) create mode 100644 pkg/common/ut/context.go create mode 100644 pkg/common/ut/context_test.go diff --git a/pkg/common/ut/context.go b/pkg/common/ut/context.go new file mode 100644 index 000000000..30023dd0d --- /dev/null +++ b/pkg/common/ut/context.go @@ -0,0 +1,65 @@ +/* + * Copyright 2023 CloudWeGo Authors + * + * Licensed 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. + */ + +package ut + +import ( + "io" + "io/ioutil" + + "github.com/cloudwego/hertz/pkg/app" + "github.com/cloudwego/hertz/pkg/common/config" + "github.com/cloudwego/hertz/pkg/protocol" + "github.com/cloudwego/hertz/pkg/route" +) + +// CreateUtRequestContext returns an app.RequestContext for testing purposes +func CreateUtRequestContext(method, url string, body *Body, headers ...Header) *app.RequestContext { + engine := route.NewEngine(config.NewOptions([]config.Option{})) + return createUtRequestContext(engine, method, url, body, headers...) +} + +func createUtRequestContext(engine *route.Engine, method, url string, body *Body, headers ...Header) *app.RequestContext { + ctx := engine.NewContext() + + var r *protocol.Request + if body != nil && body.Body != nil { + r = protocol.NewRequest(method, url, body.Body) + r.CopyTo(&ctx.Request) + if engine.IsStreamRequestBody() || body.Len == -1 { + ctx.Request.SetBodyStream(body.Body, body.Len) + } else { + buf, err := ioutil.ReadAll(&io.LimitedReader{R: body.Body, N: int64(body.Len)}) + ctx.Request.SetBody(buf) + if err != nil && err != io.EOF { + panic(err) + } + } + } else { + r = protocol.NewRequest(method, url, nil) + r.CopyTo(&ctx.Request) + } + + for _, v := range headers { + if ctx.Request.Header.Get(v.Key) != "" { + ctx.Request.Header.Add(v.Key, v.Value) + } else { + ctx.Request.Header.Set(v.Key, v.Value) + } + } + + return ctx +} diff --git a/pkg/common/ut/context_test.go b/pkg/common/ut/context_test.go new file mode 100644 index 000000000..023945c26 --- /dev/null +++ b/pkg/common/ut/context_test.go @@ -0,0 +1,41 @@ +/* + * Copyright 2023 CloudWeGo Authors + * + * Licensed 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. + */ + +package ut + +import ( + "bytes" + "testing" + + "github.com/cloudwego/hertz/pkg/common/test/assert" +) + +func TestCreateUtRequestContext(t *testing.T) { + body := "1" + method := "PUT" + path := "/hey/dy" + headerKey := "Connection" + headerValue := "close" + ctx := CreateUtRequestContext(method, path, &Body{bytes.NewBufferString(body), len(body)}, + Header{headerKey, headerValue}) + + assert.DeepEqual(t, method, string(ctx.Method())) + assert.DeepEqual(t, path, string(ctx.Path())) + body1, err := ctx.Body() + assert.DeepEqual(t, nil, err) + assert.DeepEqual(t, body, string(body1)) + assert.DeepEqual(t, headerValue, string(ctx.GetHeader(headerKey))) +} diff --git a/pkg/common/ut/request.go b/pkg/common/ut/request.go index f6dcfc400..93f8f9186 100644 --- a/pkg/common/ut/request.go +++ b/pkg/common/ut/request.go @@ -20,9 +20,7 @@ package ut import ( "context" "io" - "io/ioutil" - "github.com/cloudwego/hertz/pkg/protocol" "github.com/cloudwego/hertz/pkg/route" ) @@ -49,34 +47,7 @@ type Body struct { // // See ./request_test.go for more examples func PerformRequest(engine *route.Engine, method, url string, body *Body, headers ...Header) *ResponseRecorder { - ctx := engine.NewContext() - - var r *protocol.Request - if body != nil && body.Body != nil { - r = protocol.NewRequest(method, url, body.Body) - r.CopyTo(&ctx.Request) - if engine.IsStreamRequestBody() || body.Len == -1 { - ctx.Request.SetBodyStream(body.Body, body.Len) - } else { - buf, err := ioutil.ReadAll(&io.LimitedReader{R: body.Body, N: int64(body.Len)}) - ctx.Request.SetBody(buf) - if err != nil && err != io.EOF { - panic(err) - } - } - } else { - r = protocol.NewRequest(method, url, nil) - r.CopyTo(&ctx.Request) - } - - for _, v := range headers { - if ctx.Request.Header.Get(v.Key) != "" { - ctx.Request.Header.Add(v.Key, v.Value) - } else { - ctx.Request.Header.Set(v.Key, v.Value) - } - } - + ctx := createUtRequestContext(engine, method, url, body, headers...) engine.ServeHTTP(context.Background(), ctx) w := NewRecorder() From ab37d29a6c961d714ff5a754d03206a97748c322 Mon Sep 17 00:00:00 2001 From: GuangyuFan <97507466+FGYFFFF@users.noreply.github.com> Date: Mon, 10 Apr 2023 15:29:43 +0800 Subject: [PATCH 07/10] ci(hz): add hz test workflows (#645) --- .github/workflows/tests.yml | 62 ++++++++ cmd/hz/test_hz_unix.sh | 105 +++++++++++++ cmd/hz/test_hz_windows.sh | 101 ++++++++++++ cmd/hz/testdata/protobuf2/api.proto | 57 +++++++ cmd/hz/testdata/protobuf2/other/other.proto | 12 ++ .../testdata/protobuf2/other/other_base.proto | 9 ++ cmd/hz/testdata/protobuf2/psm/base.proto | 14 ++ cmd/hz/testdata/protobuf2/psm/psm.proto | 148 ++++++++++++++++++ cmd/hz/testdata/protobuf3/api.proto | 57 +++++++ cmd/hz/testdata/protobuf3/other/other.proto | 12 ++ .../testdata/protobuf3/other/other_base.proto | 9 ++ cmd/hz/testdata/protobuf3/psm/base.proto | 9 ++ cmd/hz/testdata/protobuf3/psm/psm.proto | 126 +++++++++++++++ cmd/hz/testdata/thrift/common.thrift | 13 ++ cmd/hz/testdata/thrift/data/basic_data.thrift | 5 + cmd/hz/testdata/thrift/data/data.thrift | 7 + cmd/hz/testdata/thrift/psm.thrift | 117 ++++++++++++++ 17 files changed, 863 insertions(+) create mode 100644 cmd/hz/test_hz_unix.sh create mode 100644 cmd/hz/test_hz_windows.sh create mode 100644 cmd/hz/testdata/protobuf2/api.proto create mode 100644 cmd/hz/testdata/protobuf2/other/other.proto create mode 100644 cmd/hz/testdata/protobuf2/other/other_base.proto create mode 100644 cmd/hz/testdata/protobuf2/psm/base.proto create mode 100644 cmd/hz/testdata/protobuf2/psm/psm.proto create mode 100644 cmd/hz/testdata/protobuf3/api.proto create mode 100644 cmd/hz/testdata/protobuf3/other/other.proto create mode 100644 cmd/hz/testdata/protobuf3/other/other_base.proto create mode 100644 cmd/hz/testdata/protobuf3/psm/base.proto create mode 100644 cmd/hz/testdata/protobuf3/psm/psm.proto create mode 100644 cmd/hz/testdata/thrift/common.thrift create mode 100644 cmd/hz/testdata/thrift/data/basic_data.thrift create mode 100644 cmd/hz/testdata/thrift/data/data.thrift create mode 100644 cmd/hz/testdata/thrift/psm.thrift diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index fbe4130a5..0325befc1 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -60,3 +60,65 @@ jobs: - name: Unit Test run: go test -race -covermode=atomic ./... + + hz-test-unix: + strategy: + matrix: + version: [ '1.20' ] + runs-on: [ self-hosted, X64 ] + steps: + - uses: actions/checkout@v3 + + - name: Set up Go + uses: actions/setup-go@v3 + with: + go-version: ${{ matrix.version }} + + - name: Setup Environment + run: | + echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV + echo "$(go env GOPATH)/bin" >> $GITHUB_PATH + + - name: Get changed files + id: changed-files + uses: tj-actions/changed-files@v35 + + - name: Hz Test + if: contains(steps.changed-files.outputs.all_changed_files, 'cmd/hz') + run: | + cd cmd/hz + sh test_hz_unix.sh + + + hz-test-windows: + strategy: + matrix: + version: [ '1.20'] + runs-on: windows-latest + steps: + - uses: actions/checkout@v3 + + - name: Set up Go + uses: actions/setup-go@v3 + with: + go-version: ${{ matrix.version }} + + - name: Get changed files + id: changed-files + uses: tj-actions/changed-files@v35 + + - name: Install Protobuf + shell: pwsh + run: | + Invoke-WebRequest https://github.com/protocolbuffers/protobuf/releases/download/v3.19.4/protoc-3.19.4-win64.zip -OutFile protoc-3.19.4-win64.zip + Expand-Archive protoc-3.19.4-win64.zip -DestinationPath protoc-3.19.4-win64 + $GOPATH=go env GOPATH + Copy-Item -Path protoc-3.19.4-win64\bin\protoc.exe -Destination $GOPATH\bin + Copy-Item -Path protoc-3.19.4-win64\include\* -Destination cmd\hz\testdata\include\google -Recurse + protoc --version + + - name: Hz Test + if: contains(steps.changed-files.outputs.all_changed_files, 'cmd/hz') + run: | + cd cmd/hz + sh test_hz_windows.sh diff --git a/cmd/hz/test_hz_unix.sh b/cmd/hz/test_hz_unix.sh new file mode 100644 index 000000000..133f90bed --- /dev/null +++ b/cmd/hz/test_hz_unix.sh @@ -0,0 +1,105 @@ +#! /usr/bin/env bash + +# const value define +moduleName="github.com/cloudwego/hertz/cmd/hz/test" +curDir=`pwd` +thriftIDL=$curDir"/testdata/thrift/psm.thrift" +protobuf2IDL=$curDir"/testdata/protobuf2/psm/psm.proto" +proto2Search=$curDir"/testdata/protobuf2" +protobuf3IDL=$curDir"/testdata/protobuf3/psm/psm.proto" +proto3Search=$curDir"/testdata/protobuf3" +protoSearch="/usr/local/include" + +judge_exit() { + code=$1 + if [ $code != 0 ]; then + exit $code + fi +} + +compile_hz() { + go build -o hz + judge_exit "$?" +} + +install_dependent_tools() { + # install thriftgo + go install github.com/cloudwego/thriftgo@latest + + # install protoc + wget https://github.com/protocolbuffers/protobuf/releases/download/v3.19.4/protoc-3.19.4-linux-x86_64.zip + unzip -d protoc-3.19.4-linux-x86_64 protoc-3.19.4-linux-x86_64.zip + cp protoc-3.19.4-linux-x86_64/bin/protoc /usr/local/bin/protoc + cp -r protoc-3.19.4-linux-x86_64/include/google /usr/local/include/google +} + +test_thrift() { + mkdir -p test + cd test + ../hz new --idl=$thriftIDL --mod=$moduleName -f --model_dir=hertz_model --handler_dir=hertz_handler --router_dir=hertz_router + judge_exit "$?" + go mod tidy && go build . + judge_exit "$?" + ../hz update --idl=$thriftIDL + judge_exit "$?" + ../hz model --idl=$thriftIDL --model_dir=hertz_model + judge_exit "$?" + ../hz client --idl=$thriftIDL --client_dir=hertz_client + judge_exit "$?" + cd .. + rm -rf test +} + +test_protobuf2() { + # test protobuf2 + mkdir -p test + cd test + ../hz new -I=$protoSearch -I=$proto2Search --idl=$protobuf2IDL --mod=$moduleName -f --model_dir=hertz_model --handler_dir=hertz_handler --router_dir=hertz_router + judge_exit "$?" + go mod tidy && go build . + judge_exit "$?" + ../hz update -I=$protoSearch -I=$proto2Search --idl=$protobuf2IDL + judge_exit "$?" + ../hz model -I=$protoSearch -I=$proto2Search --idl=$protobuf2IDL --model_dir=hertz_model + judge_exit "$?" + ../hz client -I=$protoSearch -I=$proto2Search --idl=$protobuf2IDL --client_dir=hertz_client + judge_exit "$?" + cd .. + rm -rf test +} + +test_protobuf3() { + # test protobuf2 + mkdir -p test + cd test + ../hz new -I=$protoSearch -I=$proto3Search --idl=$protobuf3IDL --mod=$moduleName -f --model_dir=hertz_model --handler_dir=hertz_handler --router_dir=hertz_router + judge_exit "$?" + go mod tidy && go build . + judge_exit "$?" + ../hz update -I=$protoSearch -I=$proto3Search --idl=$protobuf3IDL + judge_exit "$?" + ../hz model -I=$protoSearch -I=$proto3Search --idl=$protobuf3IDL --model_dir=hertz_model + judge_exit "$?" + ../hz client -I=$protoSearch -I=$proto3Search --idl=$protobuf3IDL --client_dir=hertz_client + judge_exit "$?" + cd .. + rm -rf test +} + +main() { + compile_hz + judge_exit "$?" + install_dependent_tools + judge_exit "$?" + echo "test thrift......" + test_thrift + judge_exit "$?" + echo "test protobuf2......" + test_protobuf2 + judge_exit "$?" + echo "test protobuf3......" + test_protobuf3 + judge_exit "$?" + echo "hz execute success" +} +main diff --git a/cmd/hz/test_hz_windows.sh b/cmd/hz/test_hz_windows.sh new file mode 100644 index 000000000..b421babf6 --- /dev/null +++ b/cmd/hz/test_hz_windows.sh @@ -0,0 +1,101 @@ +#! /usr/bin/env bash + +# const value define +moduleName="github.com/cloudwego/hertz/cmd/hz/test" +curDir=`pwd` +thriftIDL=$curDir"/testdata/thrift/psm.thrift" +protobuf2IDL=$curDir"/testdata/protobuf2/psm/psm.proto" +proto2Search=$curDir"/testdata/protobuf2" +protobuf3IDL=$curDir"/testdata/protobuf3/psm/psm.proto" +proto3Search=$curDir"/testdata/protobuf3" +protoSearch=$curDir"/testdata/include" + +judge_exit() { + code=$1 + if [ $code != 0 ]; then + exit $code + fi +} + +compile_hz() { + go build -o hz + judge_exit "$?" +} + +install_dependent_tools() { + # install thriftgo + go install github.com/cloudwego/thriftgo@latest +} + +test_thrift() { + # test thrift + mkdir -p test + cd test + ../hz new --idl=$thriftIDL --mod=$moduleName -f --model_dir=hertz_model --handler_dir=hertz_handler --router_dir=hertz_router + judge_exit "$?" + go mod tidy && go build . + judge_exit "$?" + ../hz update --idl=$thriftIDL + judge_exit "$?" + ../hz model --idl=$thriftIDL --model_dir=hertz_model + judge_exit "$?" + ../hz client --idl=$thriftIDL --client_dir=hertz_client + judge_exit "$?" + cd .. + rm -rf test +} + +test_protobuf2() { + # test protobuf2 + mkdir -p test + cd test + ../hz new -I=$protoSearch -I=$proto2Search --idl=$protobuf2IDL --mod=$moduleName -f --model_dir=hertz_model --handler_dir=hertz_handler --router_dir=hertz_router + judge_exit "$?" + go mod tidy && go build . + judge_exit "$?" + ../hz update -I=$protoSearch -I=$proto2Search --idl=$protobuf2IDL + judge_exit "$?" + ../hz model -I=$protoSearch -I=$proto2Search --idl=$protobuf2IDL --model_dir=hertz_model + judge_exit "$?" + ../hz client -I=$protoSearch -I=$proto2Search --idl=$protobuf2IDL --client_dir=hertz_client + judge_exit "$?" + cd .. + rm -rf test +} + +test_protobuf3() { + # test protobuf2 + mkdir -p test + cd test + ../hz new -I=$protoSearch -I=$proto3Search --idl=$protobuf3IDL --mod=$moduleName -f --model_dir=hertz_model --handler_dir=hertz_handler --router_dir=hertz_router + judge_exit "$?" + go mod tidy && go build . + judge_exit "$?" + ../hz update -I=$protoSearch -I=$proto3Search --idl=$protobuf3IDL + judge_exit "$?" + ../hz model -I=$protoSearch -I=$proto3Search --idl=$protobuf3IDL --model_dir=hertz_model + judge_exit "$?" + ../hz client -I=$protoSearch -I=$proto3Search --idl=$protobuf3IDL --client_dir=hertz_client + judge_exit "$?" + cd .. + rm -rf test +} + +main() { + compile_hz + judge_exit "$?" + install_dependent_tools + judge_exit "$?" +# todo: add thrift test when thriftgo fixed windows + echo "test thrift......" + test_thrift + judge_exit "$?" + echo "test protobuf2......" + test_protobuf2 + judge_exit "$?" + echo "test protobuf3......" + test_protobuf3 + judge_exit "$?" + echo "hz execute success" +} +main diff --git a/cmd/hz/testdata/protobuf2/api.proto b/cmd/hz/testdata/protobuf2/api.proto new file mode 100644 index 000000000..15330da60 --- /dev/null +++ b/cmd/hz/testdata/protobuf2/api.proto @@ -0,0 +1,57 @@ +syntax = "proto2"; + +package api; + +import "google/protobuf/descriptor.proto"; + +option go_package = "github.com/cloudwego/hertz/cmd/hz/test/hertz_model/api"; + +extend google.protobuf.FieldOptions { + optional string raw_body = 50101; + optional string query = 50102; + optional string header = 50103; + optional string cookie = 50104; + optional string body = 50105; + optional string path = 50106; + optional string vd = 50107; + optional string form = 50108; + optional string go_tag = 51001; + optional string js_conv = 50109; + optional string file_name = 50110; +} + +extend google.protobuf.MethodOptions { + optional string get = 50201; + optional string post = 50202; + optional string put = 50203; + optional string delete = 50204; + optional string patch = 50205; + optional string options = 50206; + optional string head = 50207; + optional string any = 50208; + optional string re_get = 50211; + optional string re_post = 50212; + optional string re_put = 50213; + optional string re_delete = 50214; + optional string re_patch = 50215; + optional string re_options = 50216; + optional string re_head = 50217; + optional string re_any = 50218; + optional string gen_path = 50301; + optional string api_version = 50302; + optional string tag = 50303; + optional string name = 50304; + optional string api_level = 50305; + optional string serializer = 50306; + optional string param = 50307; + optional string baseurl = 50308; + optional string handler_path = 50309; +} + +extend google.protobuf.EnumValueOptions { + optional int32 http_code = 50401; +} + +extend google.protobuf.ServiceOptions { + optional string base_domain = 50402; +} \ No newline at end of file diff --git a/cmd/hz/testdata/protobuf2/other/other.proto b/cmd/hz/testdata/protobuf2/other/other.proto new file mode 100644 index 000000000..d5d883de0 --- /dev/null +++ b/cmd/hz/testdata/protobuf2/other/other.proto @@ -0,0 +1,12 @@ +syntax = "proto2"; + +package hertz.other; + +import "other/other_base.proto"; + +option go_package = "github.com/cloudwego/hertz/cmd/hz/test/hertz_model/other"; + +message OtherType { + optional string IsBaseString = 1; + optional OtherBaseType IsOtherBaseType = 2; +} \ No newline at end of file diff --git a/cmd/hz/testdata/protobuf2/other/other_base.proto b/cmd/hz/testdata/protobuf2/other/other_base.proto new file mode 100644 index 000000000..32c4b3207 --- /dev/null +++ b/cmd/hz/testdata/protobuf2/other/other_base.proto @@ -0,0 +1,9 @@ +syntax = "proto2"; + +package hertz.other; + +option go_package = "github.com/cloudwego/hertz/cmd/hz/test/hertz_model/other"; + +message OtherBaseType { + optional string IsOtherBaseTypeString = 1; +} \ No newline at end of file diff --git a/cmd/hz/testdata/protobuf2/psm/base.proto b/cmd/hz/testdata/protobuf2/psm/base.proto new file mode 100644 index 000000000..6489492e0 --- /dev/null +++ b/cmd/hz/testdata/protobuf2/psm/base.proto @@ -0,0 +1,14 @@ +syntax = "proto2"; + +package base; + +option go_package = "github.com/cloudwego/hertz/cmd/hz/test/hertz_model/psm"; + +message Base { + optional string IsBaseString = 1; +} + +enum BaseEnumType { + TWEET = 0; + RETWEET = 1; +} \ No newline at end of file diff --git a/cmd/hz/testdata/protobuf2/psm/psm.proto b/cmd/hz/testdata/protobuf2/psm/psm.proto new file mode 100644 index 000000000..0561d601e --- /dev/null +++ b/cmd/hz/testdata/protobuf2/psm/psm.proto @@ -0,0 +1,148 @@ +syntax = "proto2"; + +package psm; + +option go_package = "github.com/cloudwego/hertz/cmd/hz/test/hertz_model/psm"; + +import "api.proto"; +import "base.proto"; +import "other/other.proto"; + +enum EnumType { + TWEET = 0; + RETWEET = 1; +} +message UnusedMessageType { + optional string IsUnusedMessageType = 1; +} + +message BaseType { + optional base.Base IsBaseType = 1; +} + +message MultiTypeReq { + // basic type (leading comments) + optional bool IsBoolOpt = 1; + required bool IsBoolReq = 2; + optional int32 IsInt32Opt = 3; + required int32 IsInt32Req = 4; + optional int64 IsInt64Opt = 5; + optional uint32 IsUInt32Opt = 6; + optional uint64 IsUInt64Opt = 7; + optional sint32 IsSInt32Opt = 8; + optional sint64 IsSInt64Opt = 9; + optional fixed32 IsFix32Opt = 10; + optional fixed64 IsFix64Opt = 11; + optional sfixed32 IsSFix32Opt = 12; + optional sfixed64 IsSFix64Opt = 13; + optional double IsDoubleOpt = 14; + required double IsDoubleReq = 15; + optional float IsFloatOpt = 16; + optional string IsStringOpt = 17; + required string IsStringReq = 18; + optional bytes IsBytesOpt = 19; + optional bytes IsBytesReq = 20; + + // slice + repeated string IsRepeatedString = 21; + repeated BaseType IsRepeatedBaseType = 22; + + // map + map IsStringMap = 23; + map IsBaseTypeMap = 24; + + // oneof + // multiple comments + oneof TestOneof { + string IsOneofString = 25; + BaseType IsOneofBaseType = 26; + int32 IsOneofInt = 100; + bool IsOneofBool = 101; + double IsOneoDouble = 102; + bytes IsOneofBytes = 103; + } + + // this is oneof2, one field in oneof + oneof TestOneof2 { + string IsOneof2String = 104; + } + + message NestedMessageType { + optional string IsNestedString = 1; + optional BaseType IsNestedBaseType = 2; + repeated BaseType IsNestedRepeatedBaseType = 3; + // nested oneof + oneof NestedMsgOneof { + string IsNestedMsgOneofString = 4; + EnumType IsNestedMsgOneofEnumType =5; + } + } + // nested message + optional NestedMessageType IsNestedType = 27; + + // other dependency + optional base.Base IsCurrentPackageBase = 28; + optional hertz.other.OtherType IsOtherType = 29; + + // enum + optional EnumType IsEnumTypeOpt = 30; + required EnumType IsEnumTypeReq = 31; + repeated EnumType IsEnumTypeList = 32; + optional base.BaseEnumType IsBaseEnumType = 33; +} + +message MultiTagReq { + optional string QueryTag = 1 [(api.query)="query"]; + optional string RawBodyTag = 2 [(api.raw_body)="raw_body"]; + optional string CookieTag = 3 [(api.cookie)="cookie"]; + optional string BodyTag = 4 [(api.body)="body"]; + optional string PathTag = 5 [(api.path)="path"]; + optional string VdTag = 6 [(api.vd)="$!='?'"]; + optional string FormTag = 7 [(api.form)="form"]; + optional string DefaultTag = 8 [(api.go_tag)="FFF:\"fff\" json:\"json\""]; +} + +message Resp { + optional string Resp = 1; +} + +message MultiNameStyleMessage { + optional string hertz = 1; + optional string Hertz = 2; + optional string hertz_demo = 3; + optional string hertz_demo_idl = 4; + optional string hertz_Idl = 5; + optional string hertzDemo = 6; + optional string h = 7; + optional string H = 8; + optional string hertz_ = 9; +} + +service Hertz { + rpc Method1(MultiTypeReq) returns(Resp) { + option (api.get)="/company/department/group/user:id/name"; + } + rpc Method2(MultiTypeReq) returns(Resp) { + option (api.post)="/company/department/group/user:id/sex"; + } + rpc Method3(MultiTypeReq) returns(Resp) { + option (api.put)="/company/department/group/user:id/number"; + } + rpc Method4(MultiTypeReq) returns(Resp) { + option (api.delete)="/company/department/group/user:id/age"; + } + + + rpc Method5(MultiTagReq) returns(Resp) { + option (api.options)="/school/class/student/name"; + } + rpc Method6(MultiTagReq) returns(Resp) { + option (api.head)="/school/class/student/number"; + } + rpc Method7(MultiTagReq) returns(Resp) { + option (api.patch)="/school/class/student/sex"; + } + rpc Method8(MultiTagReq) returns(Resp) { + option (api.any)="/school/class/student/grade/*subjects"; + } +} \ No newline at end of file diff --git a/cmd/hz/testdata/protobuf3/api.proto b/cmd/hz/testdata/protobuf3/api.proto new file mode 100644 index 000000000..15330da60 --- /dev/null +++ b/cmd/hz/testdata/protobuf3/api.proto @@ -0,0 +1,57 @@ +syntax = "proto2"; + +package api; + +import "google/protobuf/descriptor.proto"; + +option go_package = "github.com/cloudwego/hertz/cmd/hz/test/hertz_model/api"; + +extend google.protobuf.FieldOptions { + optional string raw_body = 50101; + optional string query = 50102; + optional string header = 50103; + optional string cookie = 50104; + optional string body = 50105; + optional string path = 50106; + optional string vd = 50107; + optional string form = 50108; + optional string go_tag = 51001; + optional string js_conv = 50109; + optional string file_name = 50110; +} + +extend google.protobuf.MethodOptions { + optional string get = 50201; + optional string post = 50202; + optional string put = 50203; + optional string delete = 50204; + optional string patch = 50205; + optional string options = 50206; + optional string head = 50207; + optional string any = 50208; + optional string re_get = 50211; + optional string re_post = 50212; + optional string re_put = 50213; + optional string re_delete = 50214; + optional string re_patch = 50215; + optional string re_options = 50216; + optional string re_head = 50217; + optional string re_any = 50218; + optional string gen_path = 50301; + optional string api_version = 50302; + optional string tag = 50303; + optional string name = 50304; + optional string api_level = 50305; + optional string serializer = 50306; + optional string param = 50307; + optional string baseurl = 50308; + optional string handler_path = 50309; +} + +extend google.protobuf.EnumValueOptions { + optional int32 http_code = 50401; +} + +extend google.protobuf.ServiceOptions { + optional string base_domain = 50402; +} \ No newline at end of file diff --git a/cmd/hz/testdata/protobuf3/other/other.proto b/cmd/hz/testdata/protobuf3/other/other.proto new file mode 100644 index 000000000..d5d883de0 --- /dev/null +++ b/cmd/hz/testdata/protobuf3/other/other.proto @@ -0,0 +1,12 @@ +syntax = "proto2"; + +package hertz.other; + +import "other/other_base.proto"; + +option go_package = "github.com/cloudwego/hertz/cmd/hz/test/hertz_model/other"; + +message OtherType { + optional string IsBaseString = 1; + optional OtherBaseType IsOtherBaseType = 2; +} \ No newline at end of file diff --git a/cmd/hz/testdata/protobuf3/other/other_base.proto b/cmd/hz/testdata/protobuf3/other/other_base.proto new file mode 100644 index 000000000..32c4b3207 --- /dev/null +++ b/cmd/hz/testdata/protobuf3/other/other_base.proto @@ -0,0 +1,9 @@ +syntax = "proto2"; + +package hertz.other; + +option go_package = "github.com/cloudwego/hertz/cmd/hz/test/hertz_model/other"; + +message OtherBaseType { + optional string IsOtherBaseTypeString = 1; +} \ No newline at end of file diff --git a/cmd/hz/testdata/protobuf3/psm/base.proto b/cmd/hz/testdata/protobuf3/psm/base.proto new file mode 100644 index 000000000..ea8891aaa --- /dev/null +++ b/cmd/hz/testdata/protobuf3/psm/base.proto @@ -0,0 +1,9 @@ +syntax = "proto2"; + +package base; + +option go_package = "github.com/cloudwego/hertz/cmd/hz/test/hertz_model/psm"; + +message Base { + optional string IsBaseString = 1; +} \ No newline at end of file diff --git a/cmd/hz/testdata/protobuf3/psm/psm.proto b/cmd/hz/testdata/protobuf3/psm/psm.proto new file mode 100644 index 000000000..972a85c85 --- /dev/null +++ b/cmd/hz/testdata/protobuf3/psm/psm.proto @@ -0,0 +1,126 @@ +syntax = "proto3"; + +package psm; + +option go_package = "github.com/cloudwego/hertz/cmd/hz/test/hertz_model/psm"; + +import "api.proto"; +import "base.proto"; +import "other/other.proto"; + +enum EnumType { + TWEET = 0; + RETWEET = 1; +} +message UnusedMessageType { + optional string IsUnusedMessageType = 1; +} + +message BaseType { + optional base.Base IsBaseType = 1; +} + +message MultiTypeReq { + // basic type (leading comments) + optional bool IsBoolOpt = 1; + optional int32 IsInt32Opt = 3; + int64 IsInt64Default = 5; + optional uint32 IsUInt32Opt = 6; + uint64 IsUInt64Default = 7; + optional sint32 IsSInt32Opt = 8; + sint64 IsSInt64Default = 9; + optional fixed32 IsFix32Opt = 10; + optional fixed64 IsFix64Opt = 11; + optional sfixed32 IsSFix32Opt = 12; + optional sfixed64 IsSFix64Opt = 13; + optional double IsDoubleOpt = 14; + optional float IsFloatOpt = 16; + optional string IsStringOpt = 17; + optional bytes IsBytesOpt = 19; + bytes IsBytesDefault = 20; + + // slice + repeated string IsRepeatedString = 21; + repeated BaseType IsRepeatedBaseType = 22; + + // map + map IsStringMap = 23; + map IsBaseTypeMap = 24; + + // oneof + oneof TestOneof { + string IsOneofString = 25; + BaseType IsOneofBaseTypeString = 26; + } + + oneof TestOneof2 { + string IsOneofString2 = 100; + } + + // nested message + message NestedMessageType { + oneof NestedOneof { + string YYY = 4; + string GGG = 5; + } + optional string IsNestedString = 1; + optional BaseType IsNestedBaseType = 2; + repeated BaseType IsNestedRepeatedBaseType = 3; + } + optional NestedMessageType IsNestedType = 27; + + // other dependency + optional base.Base IsCurrentPackageBase = 28; + optional hertz.other.OtherType IsOtherType = 29; + + // enum + optional EnumType IsEnumTypeOpt = 30; + EnumType IsEnumTypeDefault = 31; +} + +message MultiTagReq { + optional string QueryTag = 1 [(api.query)="query"]; + optional string RawBodyTag = 2 [(api.raw_body)="raw_body"]; + optional string CookieTag = 3 [(api.cookie)="cookie"]; + optional string BodyTag = 4 [(api.body)="body"]; + optional string PathTag = 5 [(api.path)="path"]; + optional string VdTag = 6 [(api.vd)="$!='?'"]; + optional string DefaultTag = 7; + oneof TestOneof { + string IsOneofString = 25; + BaseType IsOneofBaseTypeString = 26; + } +} + +message Resp { + optional string Resp = 1; +} + +service Hertz { + rpc Method1(MultiTypeReq) returns(Resp) { + option (api.get)="/company/department/group/user:id/name"; + } + rpc Method2(MultiTypeReq) returns(Resp) { + option (api.post)="/company/department/group/user:id/sex"; + } + rpc Method3(MultiTypeReq) returns(Resp) { + option (api.put)="/company/department/group/user:id/number"; + } + rpc Method4(MultiTypeReq) returns(Resp) { + option (api.delete)="/company/department/group/user:id/age"; + } + + + rpc Method5(MultiTagReq) returns(Resp) { + option (api.options)="/school/class/student/name"; + } + rpc Method6(MultiTagReq) returns(Resp) { + option (api.head)="/school/class/student/number"; + } + rpc Method7(MultiTagReq) returns(Resp) { + option (api.patch)="/school/class/student/sex"; + } + rpc Method8(MultiTagReq) returns(Resp) { + option (api.any)="/school/class/student/grade/*subjects"; + } +} \ No newline at end of file diff --git a/cmd/hz/testdata/thrift/common.thrift b/cmd/hz/testdata/thrift/common.thrift new file mode 100644 index 000000000..4efbb822a --- /dev/null +++ b/cmd/hz/testdata/thrift/common.thrift @@ -0,0 +1,13 @@ +namespace go toutiao.middleware.hertz + +struct CommonType { + 1: required string IsCommonString; + 2: optional string TTT; + 3: required bool HHH; + 4: required Base GGG; +} + +struct Base { + 1: optional string AAA; + 2: optional i32 BBB; +} \ No newline at end of file diff --git a/cmd/hz/testdata/thrift/data/basic_data.thrift b/cmd/hz/testdata/thrift/data/basic_data.thrift new file mode 100644 index 000000000..70f6b5ae2 --- /dev/null +++ b/cmd/hz/testdata/thrift/data/basic_data.thrift @@ -0,0 +1,5 @@ +namespace go toutiao.middleware.hertz_data + +struct BasicDataType { + 1: optional string IsBasicDataString; +} \ No newline at end of file diff --git a/cmd/hz/testdata/thrift/data/data.thrift b/cmd/hz/testdata/thrift/data/data.thrift new file mode 100644 index 000000000..a0b761522 --- /dev/null +++ b/cmd/hz/testdata/thrift/data/data.thrift @@ -0,0 +1,7 @@ +include "basic_data.thrift" + +namespace go toutiao.middleware.hertz_data + +struct DataType { + 1: optional basic_data.BasicDataType IsDataString; +} \ No newline at end of file diff --git a/cmd/hz/testdata/thrift/psm.thrift b/cmd/hz/testdata/thrift/psm.thrift new file mode 100644 index 000000000..5baedc81c --- /dev/null +++ b/cmd/hz/testdata/thrift/psm.thrift @@ -0,0 +1,117 @@ +include "common.thrift" +include "data/data.thrift" + +namespace go toutiao.middleware.hertz + +const string STRING_CONST = "hertz"; + +enum EnumType { + TWEET, + RETWEET = 2, +} + +typedef i32 MyInteger + +struct BaseType { + 1: string GoTag = "test" (go.tag="json:\"go\" goTag:\"tag\""); + 2: optional string IsBaseString = "test"; + 3: optional common.CommonType IsDepCommonType = {"IsCommonString":"test", "TTT":"test", "HHH":true, "GGG": {"AAA":"test","BBB":32}}; + 4: optional EnumType IsBaseTypeEnum = 1; +} + +typedef common.CommonType FFF + +typedef BaseType MyBaseType + +struct MultiTypeReq { + // basic type (leading comments) + 1: optional bool IsBoolOpt = true; // trailing comments + 2: required bool IsBoolReq; + 3: optional byte IsByteOpt = 8; + 4: required byte IsByteReq; + //5: optional i8 IsI8Opt; // unsupported i8, suggest byte + //6: required i8 IsI8Req = 5; // default + 7: optional i16 IsI16Opt = 16; + 8: optional i32 IsI32Opt; + 9: optional i64 IsI64Opt; + 10: optional double IsDoubleOpt; + 11: required double IsDoubleReq; + 12: optional string IsStringOpt = "test"; + 13: required string IsStringReq; + + 14: optional list IsList; + 22: required list IsListReq; + 15: optional set IsSet; + 16: optional map IsMap; + 21: optional map IsStructMap; + + // struct type + 17: optional BaseType IsBaseType; // use struct name + 18: optional MyBaseType IsMyBaseType; // use typedef for struct + 19: optional common.CommonType IsCommonType = {"IsCommonString": "fffff"}; + 20: optional data.DataType IsDataType; // multi-dependent struct +} + +typedef data.DataType IsMyDataType + +struct MultiTagReq { + 1: string QueryTag (api.query="query"); + 2: string RawBodyTag (api.raw_body="raw_body"); + 3: string PathTag (api.path="path"); + 4: string FormTag (api.form="form"); + 5: string CookieTag (api.cookie="cookie"); + 6: string HeaderTag (api.header="header"); + 7: string ProtobufTag (api.protobuf="protobuf"); + 8: string BodyTag (api.body="body"); + 9: string GoTag (go.tag="json:\"go\" goTag:\"tag\""); + 10: string VdTag (api.vd="$!='?'"); + 11: string DefaultTag; +} + +struct Resp { + 1: string Resp = "this is Resp"; +} + +struct MultiNameStyleReq { + 1: optional string hertz; + 2: optional string Hertz; + 3: optional string hertz_demo; + 4: optional string hertz_demo_idl; + 5: optional string hertz_Idl; + 6: optional string hertzDemo; + 7: optional string h; + 8: optional string H; + 9: optional string hertz_; +} + +struct MultiDefaultReq { + 1: optional bool IsBoolOpt = true; + 2: required bool IsBoolReq = false; + 3: optional i32 IsI32Opt = 32; + 4: required i32 IsI32Req = 32; + 5: optional string IsStringOpt = "test"; + 6: required string IsStringReq = "test"; + + 14: optional list IsListOpt = ["test", "ttt", "sdsds"]; + 22: required list IsListReq = ["test", "ttt", "sdsds"]; + 15: optional set IsSet = ["test", "ttt", "sdsds"]; + 16: optional map IsMapOpt = {"test": "ttt", "ttt": "lll"}; + 17: required map IsMapReq = {"test": "ttt", "ttt": "lll"}; + 21: optional map IsStructMapOpt = {"test": {"GoTag":"fff", "IsBaseTypeEnum":1, "IsBaseString":"ddd", "IsDepCommonType": {"IsCommonString":"fffffff", "TTT":"ttt", "HHH":true, "GGG": {"AAA":"test","BBB":32}}}}; + 25: required map IsStructMapReq = {"test": {"GoTag":"fff", "IsBaseTypeEnum":1, "IsBaseString":"ddd", "IsDepCommonType": {"IsCommonString":"fffffff", "TTT":"ttt", "HHH":true, "GGG": {"AAA":"test","BBB":32}}}}; + + 23: optional common.CommonType IsDepCommonTypeOpt = {"IsCommonString":"fffffff", "TTT":"ttt", "HHH":true, "GGG": {"AAA":"test","BBB":32}}; + 24: required common.CommonType IsDepCommonTypeReq = {"IsCommonString":"fffffff", "TTT":"ttt", "HHH":true, "GGG": {"AAA":"test","BBB":32}}; +} + +service Hertz { + Resp Method1(1: MultiTypeReq request) (api.get="/company/department/group/user:id/name", api.handler_path="v1"); + Resp Method2(1: MultiTagReq request) (api.post="/company/department/group/user:id/sex", api.handler_path="v1"); + Resp Method3(1: BaseType request) (api.put="/company/department/group/user:id/number", api.handler_path="v1"); + Resp Method4(1: data.DataType request) (api.delete="/company/department/group/user:id/age", api.handler_path="v1"); + + Resp Method5(1: MultiTypeReq request) (api.options="/school/class/student/name", api.handler_path="v2"); + Resp Method6(1: MultiTagReq request) (api.head="/school/class/student/number", api.handler_path="v2"); + Resp Method7(1: MultiTagReq request) (api.patch="/school/class/student/sex", api.handler_path="v2"); + Resp Method8(1: BaseType request) (api.any="/school/class/student/grade/*subjects", api.handler_path="v2"); +} \ No newline at end of file From 1906ecb26c693ca37c971ff364acdf9d14f2a4d0 Mon Sep 17 00:00:00 2001 From: GuangyuFan <97507466+FGYFFFF@users.noreply.github.com> Date: Mon, 10 Apr 2023 16:20:18 +0800 Subject: [PATCH 08/10] feat(hz): optimize the naming style for middleware (#660) --- cmd/hz/config/argument.go | 1 + cmd/hz/generator/package.go | 33 +++++----- cmd/hz/generator/package_tpl.go | 14 ++++- cmd/hz/generator/router.go | 107 +++++++++++++++++++++++++------- cmd/hz/protobuf/plugin.go | 15 ++--- cmd/hz/thrift/plugin.go | 15 ++--- cmd/hz/util/data.go | 16 ++++- 7 files changed, 147 insertions(+), 54 deletions(-) diff --git a/cmd/hz/config/argument.go b/cmd/hz/config/argument.go index 6e42b014c..27e376c80 100644 --- a/cmd/hz/config/argument.go +++ b/cmd/hz/config/argument.go @@ -68,6 +68,7 @@ type Argument struct { NoRecurse bool HandlerByMethod bool ForceNew bool + SnakeStyleMiddleware bool CustomizeLayout string CustomizeLayoutData string diff --git a/cmd/hz/generator/package.go b/cmd/hz/generator/package.go index 8613a5b21..af8e95063 100644 --- a/cmd/hz/generator/package.go +++ b/cmd/hz/generator/package.go @@ -46,22 +46,25 @@ type Service struct { BaseDomain string // base domain for client code } +// HttpPackageGenerator is used to record the configuration related to generating hertz http code. type HttpPackageGenerator struct { - ConfigPath string - Backend meta.Backend - Options []Option - CmdType string - ProjPackage string - HandlerDir string - RouterDir string - ModelDir string - UseDir string - ClientDir string - IdlClientDir string - ForceClientDir string - NeedModel bool - HandlerByMethod bool - BaseDomain string + ConfigPath string // package template path + Backend meta.Backend // model template + Options []Option + CmdType string + ProjPackage string // go module for project + HandlerDir string + RouterDir string + ModelDir string + UseDir string // model dir for third repo + ClientDir string // client dir for "new"/"update" command + IdlClientDir string // client dir for "client" command + ForceClientDir string // client dir without namespace for "client" command + BaseDomain string // request domain for "client" command + + NeedModel bool + HandlerByMethod bool // generate handler files with method dimension + SnakeStyleMiddleware bool // use snake name style for middleware loadedBackend Backend curModel *model.Model diff --git a/cmd/hz/generator/package_tpl.go b/cmd/hz/generator/package_tpl.go index f2a0f2e8f..1b9bcdc25 100644 --- a/cmd/hz/generator/package_tpl.go +++ b/cmd/hz/generator/package_tpl.go @@ -120,10 +120,10 @@ import ( {{define "G"}} {{- if ne .Handler ""}} - {{- .GroupName}}.{{.HttpMethod}}("{{.Path}}", append({{.MiddleWare}}Mw(), {{.Handler}})...) + {{- .GroupName}}.{{.HttpMethod}}("{{.Path}}", append({{.HandlerMiddleware}}Mw(), {{.Handler}})...) {{- end}} {{- if ne (len .Children) 0}} -{{.MiddleWare}} := {{template "g" .}}.Group("{{.Path}}", {{.MiddleWare}}Mw()...) +{{.MiddleWare}} := {{template "g" .}}.Group("{{.Path}}", {{.GroupMiddleware}}Mw()...) {{- end}} {{- range $_, $router := .Children}} {{- if ne .Handler ""}} @@ -177,10 +177,18 @@ import ( ) {{define "M"}} -func {{.MiddleWare}}Mw() []app.HandlerFunc { +{{- if ne .Children.Len 0}} +func {{.GroupMiddleware}}Mw() []app.HandlerFunc { // your code... return nil } +{{end}} +{{- if ne .Handler ""}} +func {{.HandlerMiddleware}}Mw() []app.HandlerFunc { + // your code... + return nil +} +{{end}} {{range $_, $router := $.Children}}{{template "M" $router}}{{end}} {{- end}} diff --git a/cmd/hz/generator/router.go b/cmd/hz/generator/router.go index e335a26cd..16378a887 100644 --- a/cmd/hz/generator/router.go +++ b/cmd/hz/generator/router.go @@ -36,10 +36,14 @@ type Router struct { } type RouterNode struct { - GroupName string - MiddleWare string + GroupName string // current group name(the parent middleware name), used to register route. example: {{.GroupName}}.{{HttpMethod}} + MiddleWare string // current node middleware, used to be group name for children. + HandlerMiddleware string + GroupMiddleware string + PathPrefix string Path string + Parent *RouterNode Children childrenRouterInfo Handler string // {{HandlerPackage}}.{{HandlerName}} @@ -57,9 +61,11 @@ type RegisterInfo struct { // NewRouterTree contains "/" as root node func NewRouterTree() *RouterNode { return &RouterNode{ - GroupName: "root", - MiddleWare: "root", - Path: "/", + GroupName: "root", + MiddleWare: "root", + GroupMiddleware: "root", + Path: "/", + Parent: nil, } } @@ -85,8 +91,15 @@ func (routerNode *RouterNode) Update(method *HttpMethod, handlerType, handlerPkg return nil } -// DyeGroupName traverses the routing tree in depth and names the middleware for each node. -func (routerNode *RouterNode) DyeGroupName() error { +func (routerNode *RouterNode) RawHandlerName() string { + parts := strings.Split(routerNode.Handler, ".") + handlerName := parts[len(parts)-1] + return handlerName +} + +// DyeGroupName traverses the routing tree in depth and names the handler/group middleware for each node. +// If snakeStyleMiddleware is set to true, the name style of the middleware will use snake name style. +func (routerNode *RouterNode) DyeGroupName(snakeStyleMiddleware bool) error { groups := []string{"root"} hook := func(layer int, node *RouterNode) error { @@ -96,18 +109,56 @@ func (routerNode *RouterNode) DyeGroupName() error { if len(pname) > 1 && pname[0] == '/' { pname = pname[1:] } - if len(node.Children) == 0 && node.Handler != "" { - handleName := strings.Split(node.Handler, ".") - pname = handleName[len(handleName)-1] + + if node.Parent != nil { + node.PathPrefix = node.Parent.PathPrefix + "_" + util.ToGoFuncName(pname) + } else { + node.PathPrefix = "_" + util.ToGoFuncName(pname) } - pname = util.ToVarName([]string{pname}) - // The tolow operation is placed here, unifying the middleware raw name - pname = strings.ToLower(pname) - pname, err := util.GetMiddlewareUniqueName(pname) - if err != nil { - return fmt.Errorf("get unique name for middleware '%s' failed, err: %v", pname, err) + + handlerMiddlewareName := "" + isLeafNode := false + if len(node.Handler) != 0 { + handlerMiddlewareName = node.RawHandlerName() + // If it is a leaf node, then "group middleware name" and "handler middleware name" are the same + if len(node.Children) == 0 { + pname = handlerMiddlewareName + isLeafNode = true + } + } + + pname = convertToMiddlewareName(pname) + handlerMiddlewareName = convertToMiddlewareName(handlerMiddlewareName) + + if isLeafNode { + name, err := util.GetMiddlewareUniqueName(pname) + if err != nil { + return fmt.Errorf("get unique name for middleware '%s' failed, err: %v", name, err) + } + pname = name + handlerMiddlewareName = name + } else { + var err error + pname, err = util.GetMiddlewareUniqueName(pname) + if err != nil { + return fmt.Errorf("get unique name for middleware '%s' failed, err: %v", pname, err) + } + handlerMiddlewareName, err = util.GetMiddlewareUniqueName(handlerMiddlewareName) + if err != nil { + return fmt.Errorf("get unique name for middleware '%s' failed, err: %v", handlerMiddlewareName, err) + } } node.MiddleWare = "_" + pname + if len(node.Handler) != 0 { + node.HandlerMiddleware = "_" + handlerMiddlewareName + if snakeStyleMiddleware { + node.HandlerMiddleware = "_" + node.RawHandlerName() + } + } + node.GroupMiddleware = node.MiddleWare + if snakeStyleMiddleware { + node.GroupMiddleware = node.PathPrefix + } } if layer >= len(groups)-1 { groups = append(groups, node.MiddleWare) @@ -145,7 +196,8 @@ func (routerNode *RouterNode) Insert(name string, method *HttpMethod, handlerTyp cur := routerNode for i, p := range paths { c := &RouterNode{ - Path: "/" + p, + Path: "/" + p, + Parent: cur, } if i == len(paths)-1 { // generate handler by method @@ -285,7 +337,7 @@ func (pkgGen *HttpPackageGenerator) updateRegister(pkg, rDir, pkgName string) er } func (pkgGen *HttpPackageGenerator) genRouter(pkg *HttpPackage, root *RouterNode, handlerPackage, routerDir, routerPackage string) error { - err := root.DyeGroupName() + err := root.DyeGroupName(pkgGen.SnakeStyleMiddleware) if err != nil { return err } @@ -334,10 +386,16 @@ func (pkgGen *HttpPackageGenerator) updateMiddlewareReg(router interface{}, midd if !isExist { return pkgGen.TemplateGenerator.Generate(router, middlewareTpl, filePath, false) } - middlewareList := make([]string, 1) + var middlewareList []string _ = router.(Router).Router.DFS(0, func(layer int, node *RouterNode) error { - middlewareList = append(middlewareList, node.MiddleWare) + // non-leaf node will generate group middleware + if node.Children.Len() > 0 && len(node.GroupMiddleware) > 0 { + middlewareList = append(middlewareList, node.GroupMiddleware) + } + if len(node.HandlerMiddleware) > 0 { + middlewareList = append(middlewareList, node.HandlerMiddleware) + } return nil }) @@ -347,7 +405,7 @@ func (pkgGen *HttpPackageGenerator) updateMiddlewareReg(router interface{}, midd } for _, mw := range middlewareList { - if bytes.Contains(file, []byte(mw+"Mw")) { + if bytes.Contains(file, []byte(mw)) { continue } middlewareSingleTpl := pkgGen.tpls[middlewareSingleTplName] @@ -378,3 +436,10 @@ func (pkgGen *HttpPackageGenerator) updateMiddlewareReg(router interface{}, midd return nil } + +// convertToMiddlewareName converts a route path to a middleware name +func convertToMiddlewareName(path string) string { + path = util.ToVarName([]string{path}) + path = strings.ToLower(path) + return path +} diff --git a/cmd/hz/protobuf/plugin.go b/cmd/hz/protobuf/plugin.go index 6d9afc7d3..82b01cd6a 100644 --- a/cmd/hz/protobuf/plugin.go +++ b/cmd/hz/protobuf/plugin.go @@ -600,13 +600,14 @@ func (plugin *Plugin) genHttpPackage(ast *descriptorpb.FileDescriptorProto, deps OutputDir: args.OutDir, Excludes: args.Excludes, }, - ProjPackage: pkg, - Options: options, - HandlerByMethod: args.HandlerByMethod, - CmdType: args.CmdType, - IdlClientDir: plugin.IdlClientDir, - ForceClientDir: args.ForceClientDir, - BaseDomain: args.BaseDomain, + ProjPackage: pkg, + Options: options, + HandlerByMethod: args.HandlerByMethod, + CmdType: args.CmdType, + IdlClientDir: plugin.IdlClientDir, + ForceClientDir: args.ForceClientDir, + BaseDomain: args.BaseDomain, + SnakeStyleMiddleware: args.SnakeStyleMiddleware, } if args.ModelBackend != "" { diff --git a/cmd/hz/thrift/plugin.go b/cmd/hz/thrift/plugin.go index 9392553d3..b2903988e 100644 --- a/cmd/hz/thrift/plugin.go +++ b/cmd/hz/thrift/plugin.go @@ -138,13 +138,14 @@ func (plugin *Plugin) Run() int { OutputDir: args.OutDir, Excludes: args.Excludes, }, - ProjPackage: pkg, - Options: options, - HandlerByMethod: args.HandlerByMethod, - CmdType: args.CmdType, - IdlClientDir: util.SubDir(modelDir, pkgInfo.Package), - ForceClientDir: args.ForceClientDir, - BaseDomain: args.BaseDomain, + ProjPackage: pkg, + Options: options, + HandlerByMethod: args.HandlerByMethod, + CmdType: args.CmdType, + IdlClientDir: util.SubDir(modelDir, pkgInfo.Package), + ForceClientDir: args.ForceClientDir, + BaseDomain: args.BaseDomain, + SnakeStyleMiddleware: args.SnakeStyleMiddleware, } if args.ModelBackend != "" { sg.Backend = meta.Backend(args.ModelBackend) diff --git a/cmd/hz/util/data.go b/cmd/hz/util/data.go index ad191585f..52c367430 100644 --- a/cmd/hz/util/data.go +++ b/cmd/hz/util/data.go @@ -22,6 +22,7 @@ import ( "net/url" "path/filepath" "reflect" + "regexp" "strconv" "strings" @@ -307,7 +308,7 @@ func ToVarName(paths []string) string { if c == ':' || c == '*' { continue } - if (c >= '0' && c <= '9' && i != 0) || (c >= 'a' && c <= 'z') || (c > 'A' && c <= 'Z') || (c == '_') { + if (c >= '0' && c <= '9' && i != 0) || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c == '_') { out = append(out, c) } else { out = append(out, '_') @@ -420,3 +421,16 @@ func SubPackageDir(path string) string { } return path[:index] } + +var validFuncReg = regexp.MustCompile("[_0-9a-zA-Z]") + +// ToGoFuncName converts a string to a function naming style for go +func ToGoFuncName(s string) string { + ss := []byte(s) + for i := range ss { + if !validFuncReg.Match([]byte{s[i]}) { + ss[i] = '_' + } + } + return string(ss) +} From b3b4f90df724b0134c8eb7103a59b528aa289aa1 Mon Sep 17 00:00:00 2001 From: GuangyuFan <97507466+FGYFFFF@users.noreply.github.com> Date: Mon, 10 Apr 2023 19:14:01 +0800 Subject: [PATCH 09/10] chore: release hz v0.6.2 (#714) --- cmd/hz/meta/const.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/hz/meta/const.go b/cmd/hz/meta/const.go index 8e9813ca5..7e5e5db7c 100644 --- a/cmd/hz/meta/const.go +++ b/cmd/hz/meta/const.go @@ -19,7 +19,7 @@ package meta import "runtime" // Version hz version -const Version = "v0.6.1" +const Version = "v0.6.2" // Mode hz run modes type Mode int From 9e731c6cfc300e87a194bbac592b6bae3c5c3aaf Mon Sep 17 00:00:00 2001 From: alice <90381261+alice-yyds@users.noreply.github.com> Date: Mon, 10 Apr 2023 19:30:29 +0800 Subject: [PATCH 10/10] chore: update version v0.6.2 --- version.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.go b/version.go index b0b607ba1..208f61c48 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.6.1" + Version = "v0.6.2" )