├── OWNERS ├── samples └── helloworld │ ├── kitex │ ├── kitex_info.yaml │ ├── kitex_gen │ │ └── hello │ │ │ ├── k-consts.go │ │ │ ├── greetservice │ │ │ ├── server.go │ │ │ ├── invoker.go │ │ │ ├── client.go │ │ │ └── greetservice.go │ │ │ ├── api.go │ │ │ ├── hessian2-register-api.go │ │ │ └── k-api.go │ ├── build.sh │ ├── api.thrift │ ├── script │ │ └── bootstrap.sh │ ├── handler.go │ ├── main.go │ ├── client │ │ └── main.go │ └── go.mod │ └── dubbo │ ├── README.md │ ├── src │ └── main │ │ └── java │ │ └── org │ │ └── cloudwego │ │ └── kitex │ │ └── samples │ │ ├── api │ │ ├── GreetProvider.java │ │ ├── GreetRequest.java │ │ └── GreetResponse.java │ │ ├── provider │ │ ├── GreetProviderImpl.java │ │ └── Application.java │ │ └── client │ │ └── Application.java │ └── pom.xml ├── _typos.toml ├── java ├── java.thrift ├── java.go └── java-reflection.go ├── go.mod ├── .gitignore ├── .github ├── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md ├── workflows │ ├── release-check.yml │ ├── tests.yml │ └── pr-check.yml └── PULL_REQUEST_TEMPLATE.md ├── registries ├── zookeeper │ ├── go.mod │ ├── resolver │ │ ├── resolver_test.go │ │ ├── options.go │ │ └── resolver.go │ └── registry │ │ ├── options.go │ │ └── registry.go ├── common_test.go └── common.go ├── pkg ├── iface │ ├── base_protocol.go │ └── protocol.go ├── hessian2 │ ├── const.go │ ├── hessian2.go │ ├── exception │ │ ├── exception.go │ │ └── exception_test.go │ ├── annotation.go │ ├── utils.go │ ├── parameter_test.go │ ├── parameter.go │ ├── response.go │ └── response_test.go ├── dubbo_spec │ ├── payload.go │ ├── attachment.go │ ├── service.go │ ├── header_test.go │ └── header.go ├── server_trans_handler_factory.go ├── options.go └── codec.go ├── go_format.sh ├── check_branch_name.sh ├── profile └── README.md ├── .golangci.yaml ├── cliff.toml ├── CONTRIBUTING.md ├── .licenserc.yaml ├── CODE_OF_CONDUCT.md └── LICENSE-APACHE /OWNERS: -------------------------------------------------------------------------------- 1 | approvers: 2 | - DMwangnima 3 | - felix021 4 | -------------------------------------------------------------------------------- /samples/helloworld/kitex/kitex_info.yaml: -------------------------------------------------------------------------------- 1 | kitexinfo: 2 | ServiceName: 'GreetService' 3 | ToolVersion: 'v0.7.2' 4 | -------------------------------------------------------------------------------- /_typos.toml: -------------------------------------------------------------------------------- 1 | # Typo check: https://github.com/crate-ci/typos 2 | 3 | [files] 4 | extend-exclude = ["go.mod", "go.sum", "check_branch_name.sh"] 5 | -------------------------------------------------------------------------------- /samples/helloworld/kitex/kitex_gen/hello/k-consts.go: -------------------------------------------------------------------------------- 1 | package hello 2 | 3 | // KitexUnusedProtection is used to prevent 'imported and not used' error. 4 | var KitexUnusedProtection = struct{}{} 5 | -------------------------------------------------------------------------------- /java/java.thrift: -------------------------------------------------------------------------------- 1 | namespace go java 2 | 3 | struct Object {} (JavaClassName="java.lang.Object") 4 | 5 | struct Date {} (JavaClassName="java.util.Date") 6 | 7 | struct Exception {} (JavaClassName="java.lang.Exception") 8 | -------------------------------------------------------------------------------- /samples/helloworld/kitex/build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | RUN_NAME="GreetService" 3 | 4 | mkdir -p output/bin 5 | cp script/* output/ 6 | chmod +x output/bootstrap.sh 7 | 8 | if [ "$IS_SYSTEM_TEST_ENV" != "1" ]; then 9 | go build -o output/bin/${RUN_NAME} 10 | else 11 | go test -c -covermode=set -o output/bin/${RUN_NAME} -coverpkg=./... 12 | fi 13 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/kitex-contrib/codec-dubbo 2 | 3 | go 1.13 4 | 5 | require ( 6 | github.com/apache/dubbo-go-hessian2 v1.12.4 7 | github.com/bytedance/gopkg v0.0.0-20230728082804-614d0af6619b 8 | github.com/cloudwego/kitex v0.8.0 9 | github.com/cloudwego/netpoll v0.5.1 10 | github.com/cloudwego/thriftgo v0.3.3 11 | github.com/stretchr/testify v1.8.2 12 | ) 13 | -------------------------------------------------------------------------------- /samples/helloworld/kitex/api.thrift: -------------------------------------------------------------------------------- 1 | namespace go hello 2 | 3 | struct GreetRequest { 4 | 1: required string req, 5 | }(JavaClassName="org.cloudwego.kitex.samples.api.GreetRequest") 6 | 7 | struct GreetResponse { 8 | 1: required string resp, 9 | }(JavaClassName="org.cloudwego.kitex.samples.api.GreetResponse") 10 | 11 | service GreetService { 12 | string Greet(1: string req) 13 | GreetResponse GreetWithStruct(1: GreetRequest req) 14 | } -------------------------------------------------------------------------------- /samples/helloworld/dubbo/README.md: -------------------------------------------------------------------------------- 1 | # About this directory 2 | 3 | This code is used for kitex-dubbo interoperability samples. 4 | 5 | ## Start the service consumer 6 | 7 | ```bash 8 | mvn clean package 9 | mvn -Djava.net.preferIPv4Stack=true -Dexec.mainClass=org.cloudwego.kitex.samples.client.Application exec:java 10 | ``` 11 | 12 | ## Start the service provider 13 | 14 | ```bash 15 | mvn clean package 16 | mvn -Djava.net.preferIPv4Stack=true -Dexec.mainClass=org.cloudwego.kitex.samples.provider.Application exec:java 17 | ``` 18 | -------------------------------------------------------------------------------- /samples/helloworld/kitex/script/bootstrap.sh: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | CURDIR=$(cd $(dirname $0); pwd) 3 | 4 | if [ "X$1" != "X" ]; then 5 | RUNTIME_ROOT=$1 6 | else 7 | RUNTIME_ROOT=${CURDIR} 8 | fi 9 | 10 | export KITEX_RUNTIME_ROOT=$RUNTIME_ROOT 11 | export KITEX_LOG_DIR="$RUNTIME_ROOT/log" 12 | 13 | if [ ! -d "$KITEX_LOG_DIR/app" ]; then 14 | mkdir -p "$KITEX_LOG_DIR/app" 15 | fi 16 | 17 | if [ ! -d "$KITEX_LOG_DIR/rpc" ]; then 18 | mkdir -p "$KITEX_LOG_DIR/rpc" 19 | fi 20 | 21 | exec "$CURDIR/bin/GreetService" 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, built with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # Dependency directories (remove the comment below to include it) 15 | # vendor/ 16 | 17 | # the result of the go build 18 | output* 19 | output/* 20 | 21 | # Files generated by IDEs 22 | .idea/ 23 | *.iml 24 | 25 | # Vim swap files 26 | *.swp 27 | 28 | # Vscode files 29 | .vscode 30 | 31 | # Maven generated files 32 | /samples/helloworld/dubbo/target 33 | -------------------------------------------------------------------------------- /samples/helloworld/kitex/kitex_gen/hello/greetservice/server.go: -------------------------------------------------------------------------------- 1 | // Code generated by Kitex v0.7.2. DO NOT EDIT. 2 | package greetservice 3 | 4 | import ( 5 | server "github.com/cloudwego/kitex/server" 6 | hello "github.com/kitex-contrib/codec-dubbo/samples/helloworld/kitex/kitex_gen/hello" 7 | ) 8 | 9 | // NewServer creates a server.Server with the given handler and options. 10 | func NewServer(handler hello.GreetService, opts ...server.Option) server.Server { 11 | var options []server.Option 12 | 13 | options = append(options, opts...) 14 | 15 | svr := server.NewServer(options...) 16 | if err := svr.RegisterService(serviceInfo(), handler); err != nil { 17 | panic(err) 18 | } 19 | return svr 20 | } 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | 12 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 13 | 14 | **Describe the solution you'd like** 15 | 16 | A clear and concise description of what you want to happen. 17 | 18 | **Describe alternatives you've considered** 19 | 20 | A clear and concise description of any alternative solutions or features you've considered. 21 | 22 | **Additional context** 23 | 24 | Add any other context or screenshots about the feature request here. 25 | -------------------------------------------------------------------------------- /samples/helloworld/kitex/handler.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | 6 | hello "github.com/kitex-contrib/codec-dubbo/samples/helloworld/kitex/kitex_gen/hello" 7 | ) 8 | 9 | // GreetServiceImpl implements the last service interface defined in the IDL. 10 | type GreetServiceImpl struct{} 11 | 12 | // Greet implements the GreetServiceImpl interface. 13 | func (s *GreetServiceImpl) Greet(ctx context.Context, req string) (resp string, err error) { 14 | return "Hello " + req, nil 15 | } 16 | 17 | // GreetWithStruct implements the GreetServiceImpl interface. 18 | func (s *GreetServiceImpl) GreetWithStruct(ctx context.Context, req *hello.GreetRequest) (resp *hello.GreetResponse, err error) { 19 | return &hello.GreetResponse{Resp: "Hello " + req.Req}, nil 20 | } 21 | -------------------------------------------------------------------------------- /samples/helloworld/kitex/kitex_gen/hello/greetservice/invoker.go: -------------------------------------------------------------------------------- 1 | // Code generated by Kitex v0.7.2. DO NOT EDIT. 2 | 3 | package greetservice 4 | 5 | import ( 6 | server "github.com/cloudwego/kitex/server" 7 | hello "github.com/kitex-contrib/codec-dubbo/samples/helloworld/kitex/kitex_gen/hello" 8 | ) 9 | 10 | // NewInvoker creates a server.Invoker with the given handler and options. 11 | func NewInvoker(handler hello.GreetService, opts ...server.Option) server.Invoker { 12 | var options []server.Option 13 | 14 | options = append(options, opts...) 15 | 16 | s := server.NewInvoker(options...) 17 | if err := s.RegisterService(serviceInfo(), handler); err != nil { 18 | panic(err) 19 | } 20 | if err := s.Init(); err != nil { 21 | panic(err) 22 | } 23 | return s 24 | } 25 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | 12 | A clear and concise description of what the bug is. 13 | 14 | **To Reproduce** 15 | 16 | Steps to reproduce the behavior: 17 | 1. Go to '...' 18 | 2. Click on '....' 19 | 3. Scroll down to '....' 20 | 4. See error 21 | 22 | **Expected behavior** 23 | 24 | A clear and concise description of what you expected to happen. 25 | 26 | **Screenshots** 27 | 28 | If applicable, add screenshots to help explain your problem. 29 | 30 | **Kitex version:** 31 | 32 | Please provide the version of Kitex you are using. 33 | 34 | **Environment:** 35 | 36 | The output of `go env`. 37 | 38 | **Additional context** 39 | 40 | Add any other context about the problem here. 41 | -------------------------------------------------------------------------------- /registries/zookeeper/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/kitex-contrib/codec-dubbo/registries/zookeeper 2 | 3 | go 1.20 4 | 5 | require ( 6 | github.com/cloudwego/kitex v0.8.0 7 | github.com/go-zookeeper/zk v1.0.3 8 | github.com/kitex-contrib/codec-dubbo v0.2.1 9 | github.com/stretchr/testify v1.8.2 10 | ) 11 | 12 | require ( 13 | github.com/apache/thrift v0.13.0 // indirect 14 | github.com/bytedance/gopkg v0.0.0-20230728082804-614d0af6619b // indirect 15 | github.com/choleraehyq/pid v0.0.17 // indirect 16 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect 17 | github.com/google/pprof v0.0.0-20220608213341-c488b8fa1db3 // indirect 18 | github.com/pmezard/go-difflib v1.0.0 // indirect 19 | google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6 // indirect 20 | google.golang.org/protobuf v1.30.0 // indirect 21 | gopkg.in/yaml.v3 v3.0.1 // indirect 22 | ) 23 | -------------------------------------------------------------------------------- /.github/workflows/release-check.yml: -------------------------------------------------------------------------------- 1 | name: Release Check 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v3 13 | 14 | - name: Check Source Branch 15 | run: python2 -c "exit(0 if '${{ github.head_ref }}'.startswith('release') or '${{ github.head_ref }}'.startswith('hotfix') else 1)" 16 | 17 | - name: Check Version 18 | run: | 19 | # get version code, runner not support grep -E here 20 | SOURCE_VERSION=`grep 'Version\s*=\s*\"v[0-9]\{1,\}\.[0-9]\{1,\}\.[0-9]\{1,\}\"' *.go | awk -F '\"' '{print $(NF-1)}'` 21 | git checkout main 22 | MASTER_VERSION=`grep 'Version\s*=\s*\"v[0-9]\{1,\}\.[0-9]\{1,\}\.[0-9]\{1,\}\"' *.go | awk -F '\"' '{print $(NF-1)}'` 23 | git checkout ${{Head.SHA}} 24 | # check version update 25 | python2 -c "exit(0 if list(map(int,'${SOURCE_VERSION#v}'.split('.')[:3])) > list(map(int,'${MASTER_VERSION#v}'.split('.')[:3])) else 1)" 26 | -------------------------------------------------------------------------------- /pkg/iface/base_protocol.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 CloudWeGo Authors 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one or more 5 | * contributor license agreements. See the NOTICE file distributed with 6 | * this work for additional information regarding copyright ownership. 7 | * The ASF licenses this file to You under the Apache License, Version 2.0 8 | * (the "License"); you may not use this file except in compliance with 9 | * the License. You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package iface 21 | 22 | type BaseProtocol interface { 23 | // WriteByte ... 24 | WriteByte(value byte) error 25 | 26 | // ReadByte ... 27 | ReadByte() (value byte, err error) 28 | } 29 | -------------------------------------------------------------------------------- /samples/helloworld/kitex/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | "net" 7 | 8 | "github.com/cloudwego/kitex/server" 9 | dubbo "github.com/kitex-contrib/codec-dubbo/pkg" 10 | hello "github.com/kitex-contrib/codec-dubbo/samples/helloworld/kitex/kitex_gen/hello/greetservice" 11 | 12 | "github.com/kitex-contrib/obs-opentelemetry/provider" 13 | "github.com/kitex-contrib/obs-opentelemetry/tracing" 14 | ) 15 | 16 | func main() { 17 | serviceName := "helloworld" 18 | 19 | p := provider.NewOpenTelemetryProvider( 20 | provider.WithServiceName(serviceName), 21 | provider.WithExportEndpoint("localhost:4317"), 22 | provider.WithInsecure(), 23 | ) 24 | defer p.Shutdown(context.Background()) 25 | 26 | addr, _ := net.ResolveTCPAddr("tcp", ":21001") 27 | svr := hello.NewServer(new(GreetServiceImpl), 28 | server.WithServiceAddr(addr), 29 | server.WithCodec(dubbo.NewDubboCodec( 30 | dubbo.WithJavaClassName("org.cloudwego.kitex.samples.api.GreetProvider"), 31 | )), 32 | server.WithSuite(tracing.NewServerSuite()), 33 | ) 34 | 35 | err := svr.Run() 36 | if err != nil { 37 | log.Println(err.Error()) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /pkg/hessian2/const.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 CloudWeGo Authors 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one or more 5 | * contributor license agreements. See the NOTICE file distributed with 6 | * this work for additional information regarding copyright ownership. 7 | * The ASF licenses this file to You under the Apache License, Version 2.0 8 | * (the "License"); you may not use this file except in compliance with 9 | * the License. You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package hessian2 21 | 22 | import hessian "github.com/apache/dubbo-go-hessian2" 23 | 24 | const ( 25 | NULL = hessian.BC_NULL 26 | 27 | HESSIAN_ARGS_TYPE_TAG = "hessian.argsType" 28 | HESSIAN_JAVA_METHOD_NAME_TAG = "JavaMethodName" 29 | ) 30 | -------------------------------------------------------------------------------- /samples/helloworld/dubbo/src/main/java/org/cloudwego/kitex/samples/api/GreetProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 CloudWeGo Authors 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one or more 5 | * contributor license agreements. See the NOTICE file distributed with 6 | * this work for additional information regarding copyright ownership. 7 | * The ASF licenses this file to You under the Apache License, Version 2.0 8 | * (the "License"); you may not use this file except in compliance with 9 | * the License. You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package org.cloudwego.kitex.samples.api; 21 | 22 | public interface GreetProvider { 23 | String Greet(String req) throws Exception; 24 | 25 | GreetResponse GreetWithStruct(GreetRequest req) throws Exception; 26 | } -------------------------------------------------------------------------------- /pkg/iface/protocol.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 CloudWeGo Authors 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one or more 5 | * contributor license agreements. See the NOTICE file distributed with 6 | * this work for additional information regarding copyright ownership. 7 | * The ASF licenses this file to You under the Apache License, Version 2.0 8 | * (the "License"); you may not use this file except in compliance with 9 | * the License. You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package iface 21 | 22 | type Message interface { 23 | Encode(e Encoder) error 24 | Decode(d Decoder) error 25 | } 26 | 27 | type Encoder interface { 28 | Encode(interface{}) error 29 | Buffer() []byte 30 | } 31 | 32 | type Decoder interface { 33 | Decode() (interface{}, error) 34 | } 35 | -------------------------------------------------------------------------------- /go_format.sh: -------------------------------------------------------------------------------- 1 | # Copyright 2023 CloudWeGo Authors 2 | # 3 | # Licensed to the Apache Software Foundation (ASF) under one or more 4 | # contributor license agreements. See the NOTICE file distributed with 5 | # this work for additional information regarding copyright ownership. 6 | # The ASF licenses this file to You under the Apache License, Version 2.0 7 | # (the "License"); you may not use this file except in compliance with 8 | # the License. You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | 18 | # format go imports style 19 | go install golang.org/x/tools/cmd/goimports 20 | goimports -local git@github.com/kitex-contrib/codec-dubbo -w . 21 | 22 | # format licence style 23 | go install github.com/apache/skywalking-eyes/cmd/license-eye@latest 24 | license-eye header fix 25 | # check dependency licence is valid 26 | license-eye dependency check 27 | 28 | # format go.mod 29 | go mod tidy -------------------------------------------------------------------------------- /samples/helloworld/dubbo/src/main/java/org/cloudwego/kitex/samples/api/GreetRequest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 CloudWeGo Authors 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one or more 5 | * contributor license agreements. See the NOTICE file distributed with 6 | * this work for additional information regarding copyright ownership. 7 | * The ASF licenses this file to You under the Apache License, Version 2.0 8 | * (the "License"); you may not use this file except in compliance with 9 | * the License. You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package org.cloudwego.kitex.samples.api; 21 | 22 | import java.io.Serializable; 23 | 24 | public class GreetRequest implements Serializable { 25 | String req; 26 | 27 | public GreetRequest(String req) { 28 | this.req = req; 29 | } 30 | 31 | public String getReq() { 32 | return req; 33 | } 34 | } -------------------------------------------------------------------------------- /samples/helloworld/dubbo/src/main/java/org/cloudwego/kitex/samples/api/GreetResponse.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 CloudWeGo Authors 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one or more 5 | * contributor license agreements. See the NOTICE file distributed with 6 | * this work for additional information regarding copyright ownership. 7 | * The ASF licenses this file to You under the Apache License, Version 2.0 8 | * (the "License"); you may not use this file except in compliance with 9 | * the License. You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package org.cloudwego.kitex.samples.api; 21 | 22 | import java.io.Serializable; 23 | 24 | public class GreetResponse implements Serializable { 25 | String resp; 26 | 27 | public GreetResponse(String resp) { 28 | this.resp = resp; 29 | } 30 | 31 | public String getResp() { 32 | return resp; 33 | } 34 | } -------------------------------------------------------------------------------- /check_branch_name.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Copyright 2023 CloudWeGo Authors 3 | # 4 | # Licensed to the Apache Software Foundation (ASF) under one or more 5 | # contributor license agreements. See the NOTICE file distributed with 6 | # this work for additional information regarding copyright ownership. 7 | # The ASF licenses this file to You under the Apache License, Version 2.0 8 | # (the "License"); you may not use this file except in compliance with 9 | # the License. You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, 15 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | # See the License for the specific language governing permissions and 17 | # limitations under the License. 18 | 19 | 20 | current=$(git status | head -n1 | sed 's/On branch //') 21 | name=${1:-$current} 22 | if [[ ! $name =~ ^(((opt(imize)?|feat(ure)?|(bug|hot)?fix|test|refact(or)?|ci)/.+)|(main|develop)|(release-v[0-9]+\.[0-9]+)|(release/v[0-9]+\.[0-9]+\.[0-9]+(-[a-z0-9.]+(\+[a-z0-9.]+)?)?)|revert-[a-z0-9]+)$ ]]; then 23 | echo "branch name '$name' is invalid" 24 | exit 1 25 | else 26 | echo "branch name '$name' is valid" 27 | fi 28 | -------------------------------------------------------------------------------- /profile/README.md: -------------------------------------------------------------------------------- 1 | ## Hi there 👋 2 | 3 | 🙋‍♀️ A short introduction - CloudWeGo is an open-source middleware set launched by ByteDance that can be used to quickly build enterprise-class cloud native architectures. The common characteristics of CloudWeGo projects are high performance, high scalability, high reliability and focusing on microservices communication and governance. 4 | 5 | 🌈 Community Membership - the [Responsibilities and Requirements](https://github.com/cloudwego/community/blob/main/COMMUNITY_MEMBERSHIP.md) of contributor roles in CloudWeGo. 6 | 7 | 👩‍💻 Useful resources - [Portal](https://www.cloudwego.io/), [Community](https://www.cloudwego.io/zh/community/), [Blogs](https://www.cloudwego.io/zh/blog/), [Use Cases](https://www.cloudwego.io/zh/cooperation/) 8 | 9 | 🍿 Security - [Vulnerability Reporting](https://www.cloudwego.io/zh/security/vulnerability-reporting/), [Safety Bulletin](https://www.cloudwego.io/zh/security/safety-bulletin/) 10 | 11 | 🌲 Ecosystem - [Kitex-contrib](https://github.com/kitex-contrib), [Hertz-contrib](https://github.com/hertz-contrib), [Volo-rs](https://github.com/volo-rs) 12 | 13 | 🎊 Example - [kitex-example](https://github.com/cloudwego/kitex-examples), [hertz-example](https://github.com/cloudwego/hertz-examples), [biz-demo](https://github.com/cloudwego/biz-demo), [netpoll-example](https://github.com/cloudwego/netpoll-examples) 14 | -------------------------------------------------------------------------------- /java/java.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 CloudWeGo Authors 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one or more 5 | * contributor license agreements. See the NOTICE file distributed with 6 | * this work for additional information regarding copyright ownership. 7 | * The ASF licenses this file to You under the Apache License, Version 2.0 8 | * (the "License"); you may not use this file except in compliance with 9 | * the License. You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package java 21 | 22 | import ( 23 | "time" 24 | 25 | hessian2_exception "github.com/kitex-contrib/codec-dubbo/pkg/hessian2/exception" 26 | ) 27 | 28 | type Object = interface{} 29 | 30 | func NewObject() *Object { 31 | return new(Object) 32 | } 33 | 34 | type Date = time.Time 35 | 36 | func NewDate() *Date { 37 | return new(Date) 38 | } 39 | 40 | type Exception = hessian2_exception.Exception 41 | 42 | func NewException(detailMessage string) *Exception { 43 | return hessian2_exception.NewException(detailMessage) 44 | } 45 | -------------------------------------------------------------------------------- /samples/helloworld/dubbo/src/main/java/org/cloudwego/kitex/samples/provider/GreetProviderImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 CloudWeGo Authors 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one or more 5 | * contributor license agreements. See the NOTICE file distributed with 6 | * this work for additional information regarding copyright ownership. 7 | * The ASF licenses this file to You under the Apache License, Version 2.0 8 | * (the "License"); you may not use this file except in compliance with 9 | * the License. You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package org.cloudwego.kitex.samples.provider; 21 | 22 | import org.cloudwego.kitex.samples.api.*; 23 | 24 | public class GreetProviderImpl implements GreetProvider { 25 | @Override 26 | public String Greet(String req) throws Exception { 27 | return req; 28 | } 29 | 30 | @Override 31 | public GreetResponse GreetWithStruct(GreetRequest req) throws Exception { 32 | GreetResponse resp = new GreetResponse("Hello "+req.getReq()); 33 | return resp; 34 | } 35 | } -------------------------------------------------------------------------------- /pkg/hessian2/hessian2.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 CloudWeGo Authors 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one or more 5 | * contributor license agreements. See the NOTICE file distributed with 6 | * this work for additional information regarding copyright ownership. 7 | * The ASF licenses this file to You under the Apache License, Version 2.0 8 | * (the "License"); you may not use this file except in compliance with 9 | * the License. You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package hessian2 21 | 22 | import ( 23 | hessian "github.com/apache/dubbo-go-hessian2" 24 | "github.com/kitex-contrib/codec-dubbo/pkg/iface" 25 | ) 26 | 27 | func NewEncoder() iface.Encoder { 28 | return hessian.NewEncoder() 29 | } 30 | 31 | func NewDecoder(b []byte) iface.Decoder { 32 | return hessian.NewDecoder(b) 33 | } 34 | 35 | type ( 36 | Encoder struct { 37 | hessian.Encoder 38 | } 39 | Decoder struct { 40 | hessian.Decoder 41 | } 42 | ) 43 | 44 | func Register(pojos []interface{}) { 45 | for _, i := range pojos { 46 | hessian.RegisterPOJO(i.(hessian.POJO)) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /samples/helloworld/kitex/client/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/cloudwego/kitex/client" 7 | "github.com/cloudwego/kitex/pkg/klog" 8 | dubbo "github.com/kitex-contrib/codec-dubbo/pkg" 9 | "github.com/kitex-contrib/codec-dubbo/samples/helloworld/kitex/kitex_gen/hello" 10 | "github.com/kitex-contrib/codec-dubbo/samples/helloworld/kitex/kitex_gen/hello/greetservice" 11 | "github.com/kitex-contrib/obs-opentelemetry/provider" 12 | "github.com/kitex-contrib/obs-opentelemetry/tracing" 13 | ) 14 | 15 | func main() { 16 | serviceName := "helloworld-client" 17 | p := provider.NewOpenTelemetryProvider( 18 | provider.WithServiceName(serviceName), 19 | provider.WithExportEndpoint("localhost:4317"), 20 | provider.WithInsecure(), 21 | ) 22 | defer p.Shutdown(context.Background()) 23 | 24 | cli, err := greetservice.NewClient("helloworld", 25 | client.WithHostPorts("127.0.0.1:21000"), 26 | client.WithCodec( 27 | dubbo.NewDubboCodec( 28 | dubbo.WithJavaClassName("org.cloudwego.kitex.samples.api.GreetProvider"), 29 | ), 30 | ), 31 | client.WithSuite(tracing.NewClientSuite()), 32 | ) 33 | if err != nil { 34 | panic(err) 35 | } 36 | 37 | resp, err := cli.Greet(context.Background(), "world") 38 | if err != nil { 39 | klog.Error(err) 40 | return 41 | } 42 | klog.Infof("resp: %s", resp) 43 | 44 | respWithStruct, err := cli.GreetWithStruct(context.Background(), &hello.GreetRequest{Req: "world"}) 45 | if err != nil { 46 | klog.Error(err) 47 | return 48 | } 49 | klog.Infof("respWithStruct: %s", respWithStruct.Resp) 50 | } 51 | -------------------------------------------------------------------------------- /samples/helloworld/kitex/kitex_gen/hello/api.go: -------------------------------------------------------------------------------- 1 | // Code generated by thriftgo (0.3.2). DO NOT EDIT. 2 | 3 | package hello 4 | 5 | import ( 6 | "context" 7 | "fmt" 8 | ) 9 | 10 | type GreetRequest struct { 11 | Req string `thrift:"req,1,required" frugal:"1,required,string" json:"req"` 12 | } 13 | 14 | func NewGreetRequest() *GreetRequest { 15 | return &GreetRequest{} 16 | } 17 | 18 | func (p *GreetRequest) InitDefault() { 19 | *p = GreetRequest{} 20 | } 21 | 22 | func (p *GreetRequest) GetReq() (v string) { 23 | return p.Req 24 | } 25 | func (p *GreetRequest) SetReq(val string) { 26 | p.Req = val 27 | } 28 | 29 | func (p *GreetRequest) String() string { 30 | if p == nil { 31 | return "" 32 | } 33 | return fmt.Sprintf("GreetRequest(%+v)", *p) 34 | } 35 | 36 | type GreetResponse struct { 37 | Resp string `thrift:"resp,1,required" frugal:"1,required,string" json:"resp"` 38 | } 39 | 40 | func NewGreetResponse() *GreetResponse { 41 | return &GreetResponse{} 42 | } 43 | 44 | func (p *GreetResponse) InitDefault() { 45 | *p = GreetResponse{} 46 | } 47 | 48 | func (p *GreetResponse) GetResp() (v string) { 49 | return p.Resp 50 | } 51 | func (p *GreetResponse) SetResp(val string) { 52 | p.Resp = val 53 | } 54 | 55 | func (p *GreetResponse) String() string { 56 | if p == nil { 57 | return "" 58 | } 59 | return fmt.Sprintf("GreetResponse(%+v)", *p) 60 | } 61 | 62 | type GreetService interface { 63 | Greet(ctx context.Context, req string) (r string, err error) 64 | 65 | GreetWithStruct(ctx context.Context, req *GreetRequest) (r *GreetResponse, err error) 66 | } 67 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: [ push, pull_request ] 4 | 5 | jobs: 6 | integration-test: 7 | runs-on: [ self-hosted, X64 ] 8 | steps: 9 | - uses: actions/checkout@v3 10 | 11 | - name: Set up Go 12 | uses: actions/setup-go@v3 13 | with: 14 | go-version: '1.17' 15 | 16 | - name: Set up Java 17 | uses: actions/setup-java@v3 18 | with: 19 | distribution: 'zulu' 20 | java-version: 8 21 | 22 | - name: Set up Maven 23 | uses: stCarolas/setup-maven@v4.5 24 | with: 25 | maven-version: 3.9.4 26 | 27 | - name: Run Test 28 | run: | 29 | cd .. 30 | rm -rf codec-dubbo-tests 31 | git clone https://github.com/kitex-contrib/codec-dubbo-tests.git 32 | cd codec-dubbo-tests 33 | ./run.sh ${{ github.workspace }} 34 | cd ${{ github.workspace }} 35 | 36 | unit-benchmark-test: 37 | strategy: 38 | matrix: 39 | go: [ '1.17', '1.18', '1.19', '1.20', '1.21' ] 40 | os: [ X64, ARM64 ] 41 | runs-on: ${{ matrix.os }} 42 | steps: 43 | - uses: actions/checkout@v3 44 | 45 | - name: Set up Go 46 | uses: actions/setup-go@v3 47 | with: 48 | go-version: ${{ matrix.go }} 49 | 50 | # block scenario, comment temporarily 51 | # - uses: actions/cache@v3 52 | # with: 53 | # path: ~/go/pkg/mod 54 | # key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} 55 | # restore-keys: | 56 | # ${{ runner.os }}-go- 57 | 58 | - name: Unit Test 59 | run: go test -race -covermode=atomic -coverprofile=coverage.out ./... 60 | 61 | - name: Benchmark 62 | run: go test -bench=. -benchmem -run=none ./... 63 | -------------------------------------------------------------------------------- /samples/helloworld/dubbo/src/main/java/org/cloudwego/kitex/samples/provider/Application.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 CloudWeGo Authors 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one or more 5 | * contributor license agreements. See the NOTICE file distributed with 6 | * this work for additional information regarding copyright ownership. 7 | * The ASF licenses this file to You under the Apache License, Version 2.0 8 | * (the "License"); you may not use this file except in compliance with 9 | * the License. You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package org.cloudwego.kitex.samples.provider; 21 | 22 | import org.apache.dubbo.config.ProtocolConfig; 23 | import org.apache.dubbo.config.RegistryConfig; 24 | import org.apache.dubbo.config.ServiceConfig; 25 | import org.apache.dubbo.config.bootstrap.DubboBootstrap; 26 | import org.cloudwego.kitex.samples.api.GreetProvider; 27 | 28 | public class Application { 29 | 30 | public static void main(String[] args) { 31 | ServiceConfig service = new ServiceConfig<>(); 32 | service.setInterface(GreetProvider.class); 33 | service.setRef(new GreetProviderImpl()); 34 | 35 | DubboBootstrap.getInstance() 36 | .application("first-dubbo-provider") 37 | .protocol(new ProtocolConfig("dubbo", 21001)) 38 | .service(service) 39 | .start() 40 | .await(); 41 | } 42 | } -------------------------------------------------------------------------------- /.github/workflows/pr-check.yml: -------------------------------------------------------------------------------- 1 | name: Pull Request Check 2 | 3 | on: [ pull_request ] 4 | 5 | jobs: 6 | compliant: 7 | runs-on: [ self-hosted, X64 ] 8 | steps: 9 | - uses: actions/checkout@v3 10 | 11 | - name: Check License Header 12 | uses: apache/skywalking-eyes/header@main 13 | env: 14 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 15 | 16 | - name: Check Spell 17 | uses: crate-ci/typos@master 18 | 19 | staticcheck: 20 | runs-on: [ self-hosted, X64 ] 21 | steps: 22 | - uses: actions/checkout@v3 23 | - name: Set up Go 24 | uses: actions/setup-go@v3 25 | with: 26 | go-version: 1.19 27 | 28 | - uses: actions/cache@v3 29 | with: 30 | path: ~/go/pkg/mod 31 | key: reviewdog-${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} 32 | restore-keys: | 33 | reviewdog-${{ runner.os }}-go- 34 | 35 | - uses: reviewdog/action-staticcheck@v1 36 | with: 37 | github_token: ${{ secrets.github_token }} 38 | # Change reviewdog reporter if you need [github-pr-check,github-check,github-pr-review]. 39 | reporter: github-pr-review 40 | # Report all results. 41 | filter_mode: nofilter 42 | # Exit with 1 when it find at least one finding. 43 | fail_on_error: true 44 | # Set staticcheck flags 45 | staticcheck_flags: -checks=inherit,-SA1029 46 | 47 | lint: 48 | runs-on: [ self-hosted, X64 ] 49 | steps: 50 | - uses: actions/checkout@v3 51 | - name: Set up Go 52 | uses: actions/setup-go@v3 53 | with: 54 | go-version: 1.19 55 | 56 | - name: Golangci Lint 57 | # https://golangci-lint.run/ 58 | uses: golangci/golangci-lint-action@v3 59 | with: 60 | version: latest 61 | -------------------------------------------------------------------------------- /.golangci.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 CloudWeGo Authors 2 | # 3 | # Licensed to the Apache Software Foundation (ASF) under one or more 4 | # contributor license agreements. See the NOTICE file distributed with 5 | # this work for additional information regarding copyright ownership. 6 | # The ASF licenses this file to You under the Apache License, Version 2.0 7 | # (the "License"); you may not use this file except in compliance with 8 | # the License. You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | 18 | # Options for analysis running. 19 | run: 20 | # include `vendor` `third_party` `testdata` `examples` `Godeps` `builtin` 21 | skip-dirs-use-default: true 22 | skip-dirs: 23 | - kitex_gen 24 | skip-files: 25 | - ".*\\.mock\\.go$" 26 | # output configuration options 27 | output: 28 | # Format: colored-line-number|line-number|json|tab|checkstyle|code-climate|junit-xml|github-actions 29 | format: colored-line-number 30 | # All available settings of specific linters. 31 | # Refer to https://golangci-lint.run/usage/linters 32 | linters-settings: 33 | gofumpt: 34 | # Choose whether to use the extra rules. 35 | # Default: false 36 | extra-rules: true 37 | govet: 38 | # Disable analyzers by name. 39 | # Run `go tool vet help` to see all analyzers. 40 | disable: 41 | - stdmethods 42 | linters: 43 | enable: 44 | - gofumpt 45 | - gofmt 46 | disable: 47 | - errcheck 48 | - typecheck 49 | - deadcode 50 | - varcheck 51 | - staticcheck 52 | issues: 53 | exclude-use-default: true 54 | -------------------------------------------------------------------------------- /pkg/hessian2/exception/exception.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 CloudWeGo Authors 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one or more 5 | * contributor license agreements. See the NOTICE file distributed with 6 | * this work for additional information regarding copyright ownership. 7 | * The ASF licenses this file to You under the Apache License, Version 2.0 8 | * (the "License"); you may not use this file except in compliance with 9 | * the License. You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package exception 21 | 22 | import ( 23 | "github.com/apache/dubbo-go-hessian2/java_exception" 24 | ) 25 | 26 | type Throwabler = java_exception.Throwabler 27 | 28 | type Exception = java_exception.Exception 29 | 30 | func NewException(detailMessage string) *Exception { 31 | return java_exception.NewException(detailMessage) 32 | } 33 | 34 | // FromError extracts Throwabler from passed err. 35 | // 36 | // - If err is nil, it returns nil and false 37 | // 38 | // - If err implements Unwrap(), it would unwrap err until getting the real cause. 39 | // Then it would check cause whether implementing Throwabler. If yes, it returns 40 | // Throwabler and true. 41 | // 42 | // If not, it checks err whether implementing Throwabler directly. If yes, 43 | // it returns Throwabler and true. 44 | func FromError(err error) (Throwabler, bool) { 45 | if err == nil { 46 | return nil, false 47 | } 48 | for { 49 | if wrapper, ok := err.(interface{ Unwrap() error }); ok { 50 | err = wrapper.Unwrap() 51 | } else { 52 | break 53 | } 54 | } 55 | if exception, ok := err.(Throwabler); ok { 56 | return exception, true 57 | } 58 | return nil, false 59 | } 60 | -------------------------------------------------------------------------------- /samples/helloworld/kitex/kitex_gen/hello/greetservice/client.go: -------------------------------------------------------------------------------- 1 | // Code generated by Kitex v0.7.2. DO NOT EDIT. 2 | 3 | package greetservice 4 | 5 | import ( 6 | "context" 7 | client "github.com/cloudwego/kitex/client" 8 | callopt "github.com/cloudwego/kitex/client/callopt" 9 | hello "github.com/kitex-contrib/codec-dubbo/samples/helloworld/kitex/kitex_gen/hello" 10 | ) 11 | 12 | // Client is designed to provide IDL-compatible methods with call-option parameter for kitex framework. 13 | type Client interface { 14 | Greet(ctx context.Context, req string, callOptions ...callopt.Option) (r string, err error) 15 | GreetWithStruct(ctx context.Context, req *hello.GreetRequest, callOptions ...callopt.Option) (r *hello.GreetResponse, err error) 16 | } 17 | 18 | // NewClient creates a client for the service defined in IDL. 19 | func NewClient(destService string, opts ...client.Option) (Client, error) { 20 | var options []client.Option 21 | options = append(options, client.WithDestService(destService)) 22 | 23 | options = append(options, opts...) 24 | 25 | kc, err := client.NewClient(serviceInfo(), options...) 26 | if err != nil { 27 | return nil, err 28 | } 29 | return &kGreetServiceClient{ 30 | kClient: newServiceClient(kc), 31 | }, nil 32 | } 33 | 34 | // MustNewClient creates a client for the service defined in IDL. It panics if any error occurs. 35 | func MustNewClient(destService string, opts ...client.Option) Client { 36 | kc, err := NewClient(destService, opts...) 37 | if err != nil { 38 | panic(err) 39 | } 40 | return kc 41 | } 42 | 43 | type kGreetServiceClient struct { 44 | *kClient 45 | } 46 | 47 | func (p *kGreetServiceClient) Greet(ctx context.Context, req string, callOptions ...callopt.Option) (r string, err error) { 48 | ctx = client.NewCtxWithCallOptions(ctx, callOptions) 49 | return p.kClient.Greet(ctx, req) 50 | } 51 | 52 | func (p *kGreetServiceClient) GreetWithStruct(ctx context.Context, req *hello.GreetRequest, callOptions ...callopt.Option) (r *hello.GreetResponse, err error) { 53 | ctx = client.NewCtxWithCallOptions(ctx, callOptions) 54 | return p.kClient.GreetWithStruct(ctx, req) 55 | } 56 | -------------------------------------------------------------------------------- /pkg/dubbo_spec/payload.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 CloudWeGo Authors 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one or more 5 | * contributor license agreements. See the NOTICE file distributed with 6 | * this work for additional information regarding copyright ownership. 7 | * The ASF licenses this file to You under the Apache License, Version 2.0 8 | * (the "License"); you may not use this file except in compliance with 9 | * the License. You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package dubbo_spec 21 | 22 | import ( 23 | "fmt" 24 | 25 | "github.com/kitex-contrib/codec-dubbo/pkg/iface" 26 | ) 27 | 28 | type PayloadType int32 29 | 30 | // Response payload type enum 31 | const ( 32 | RESPONSE_WITH_EXCEPTION PayloadType = iota 33 | RESPONSE_VALUE 34 | RESPONSE_NULL_VALUE 35 | RESPONSE_WITH_EXCEPTION_WITH_ATTACHMENTS 36 | RESPONSE_VALUE_WITH_ATTACHMENTS 37 | RESPONSE_NULL_VALUE_WITH_ATTACHMENTS 38 | ) 39 | 40 | var attachmentsSet = map[PayloadType]struct{}{ 41 | RESPONSE_WITH_EXCEPTION_WITH_ATTACHMENTS: {}, 42 | RESPONSE_VALUE_WITH_ATTACHMENTS: {}, 43 | RESPONSE_NULL_VALUE_WITH_ATTACHMENTS: {}, 44 | } 45 | 46 | // IsAttachmentsPayloadType determines whether typ is an attachments PayloadType 47 | func IsAttachmentsPayloadType(typ PayloadType) bool { 48 | _, ok := attachmentsSet[typ] 49 | return ok 50 | } 51 | 52 | func DecodePayloadType(decoder iface.Decoder) (PayloadType, error) { 53 | payloadTypeRaw, err := decoder.Decode() 54 | if err != nil { 55 | return 0, err 56 | } 57 | payloadTypeInt32, ok := payloadTypeRaw.(int32) 58 | if !ok { 59 | return 0, fmt.Errorf("dubbo PayloadType decoded failed, got: %v", payloadTypeRaw) 60 | } 61 | return PayloadType(payloadTypeInt32), nil 62 | } 63 | -------------------------------------------------------------------------------- /pkg/dubbo_spec/attachment.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 CloudWeGo Authors 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one or more 5 | * contributor license agreements. See the NOTICE file distributed with 6 | * this work for additional information regarding copyright ownership. 7 | * The ASF licenses this file to You under the Apache License, Version 2.0 8 | * (the "License"); you may not use this file except in compliance with 9 | * the License. You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package dubbo_spec 21 | 22 | import ( 23 | "strconv" 24 | "strings" 25 | "time" 26 | 27 | "github.com/bytedance/gopkg/cloud/metainfo" 28 | "github.com/cloudwego/kitex/pkg/remote" 29 | ) 30 | 31 | const ( 32 | PATH_KEY = "path" 33 | GROUP_KEY = "group" 34 | INTERFACE_KEY = "interface" 35 | VERSION_KEY = "version" 36 | TIMEOUT_KEY = "timeout" 37 | 38 | lenPT = len(metainfo.PrefixTransient) 39 | ) 40 | 41 | type Attachment = map[string]interface{} 42 | 43 | func NewAttachment(path, group, iface, version string, timeout time.Duration, transInfo remote.TransInfo) Attachment { 44 | result := Attachment{} 45 | if len(path) > 0 { 46 | result[PATH_KEY] = path 47 | } 48 | if len(group) > 0 { 49 | result[GROUP_KEY] = group 50 | } 51 | if len(iface) > 0 { 52 | result[INTERFACE_KEY] = iface 53 | } 54 | if len(version) > 0 { 55 | result[VERSION_KEY] = version 56 | } 57 | if timeout > 0 { 58 | result[TIMEOUT_KEY] = strconv.Itoa(int(timeout.Milliseconds())) 59 | } 60 | for k, v := range transInfo.TransStrInfo() { 61 | // The prefix needs to be removed 62 | if strings.HasPrefix(k, metainfo.PrefixTransient) { 63 | result[k[lenPT:]] = v 64 | } else { 65 | result[k] = v 66 | } 67 | } 68 | return result 69 | } 70 | -------------------------------------------------------------------------------- /pkg/hessian2/annotation.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 CloudWeGo Authors 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one or more 5 | * contributor license agreements. See the NOTICE file distributed with 6 | * this work for additional information regarding copyright ownership. 7 | * The ASF licenses this file to You under the Apache License, Version 2.0 8 | * (the "License"); you may not use this file except in compliance with 9 | * the License. You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package hessian2 21 | 22 | import ( 23 | "strings" 24 | ) 25 | 26 | // MethodAnnotation Used to store parameter types and parameter names in method annotations. 27 | type MethodAnnotation struct { 28 | argsAnno string 29 | javaMethodName string // read from IDL annotation 30 | fieldTypes []string 31 | } 32 | 33 | // NewMethodAnnotation is used to create a method annotation object. 34 | func NewMethodAnnotation(annos map[string][]string) *MethodAnnotation { 35 | ma := new(MethodAnnotation) 36 | if v, ok := annos[HESSIAN_ARGS_TYPE_TAG]; ok && len(v) > 0 { 37 | ma.argsAnno = v[0] 38 | ma.fieldTypes = strings.Split(ma.argsAnno, ",") 39 | } 40 | if v, ok := annos[HESSIAN_JAVA_METHOD_NAME_TAG]; ok && len(v) > 0 { 41 | ma.javaMethodName = v[0] 42 | } 43 | return ma 44 | } 45 | 46 | // GetFieldType retrieves the type annotation for a field by its index. 47 | func (ma *MethodAnnotation) GetFieldType(i int) string { 48 | if ma != nil && len(ma.fieldTypes) > i { 49 | return ma.fieldTypes[i] 50 | } 51 | return "" 52 | } 53 | 54 | // GetMethodName get the method name specified by the method annotation. 55 | func (ma *MethodAnnotation) GetMethodName() (string, bool) { 56 | if ma == nil || ma.javaMethodName == "" { 57 | return "", false 58 | } 59 | return ma.javaMethodName, true 60 | } 61 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | #### What type of PR is this? 2 | 17 | 18 | #### Check the PR title. 19 | 24 | - [ ] This PR title match the format: \(optional scope): \ 25 | - [ ] The description of this PR title is user-oriented and clear enough for others to understand. 26 | - [ ] Attach the PR updating the user documentation if the current PR requires user awareness at the usage level. [User docs repo](https://github.com/cloudwego/cloudwego.github.io) 27 | 28 | 29 | #### (Optional) Translate the PR title into Chinese. 30 | 31 | 32 | #### (Optional) More detailed description for this PR(en: English/zh: Chinese). 33 | 36 | en: 37 | zh(optional): 38 | 39 | 40 | #### (Optional) Which issue(s) this PR fixes: 41 | 45 | 46 | #### (optional) The PR that updates user documentation: 47 | 50 | -------------------------------------------------------------------------------- /pkg/dubbo_spec/service.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 CloudWeGo Authors 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one or more 5 | * contributor license agreements. See the NOTICE file distributed with 6 | * this work for additional information regarding copyright ownership. 7 | * The ASF licenses this file to You under the Apache License, Version 2.0 8 | * (the "License"); you may not use this file except in compliance with 9 | * the License. You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package dubbo_spec 21 | 22 | import ( 23 | "fmt" 24 | "time" 25 | 26 | "github.com/cloudwego/kitex/pkg/remote" 27 | "github.com/kitex-contrib/codec-dubbo/pkg/iface" 28 | ) 29 | 30 | const DEFAULT_DUBBO_PROTOCOL_VERSION = "2.0.2" 31 | 32 | type Service struct { 33 | ProtocolVersion string 34 | Path string 35 | Version string 36 | Method string 37 | Timeout time.Duration 38 | Group string 39 | TransInfo remote.TransInfo 40 | } 41 | 42 | func (svc *Service) Decode(decoder iface.Decoder) error { 43 | if err := decodeString(decoder, &svc.ProtocolVersion, "ProtocolVersion"); err != nil { 44 | return err 45 | } 46 | if err := decodeString(decoder, &svc.Path, "Path"); err != nil { 47 | return err 48 | } 49 | if err := decodeString(decoder, &svc.Version, "Version"); err != nil { 50 | return err 51 | } 52 | if err := decodeString(decoder, &svc.Method, "Method"); err != nil { 53 | return err 54 | } 55 | return nil 56 | } 57 | 58 | // decodeString decodes dubbo Service string field 59 | func decodeString(decoder iface.Decoder, target *string, targetName string) error { 60 | strRaw, err := decoder.Decode() 61 | if err != nil { 62 | return err 63 | } 64 | str, ok := strRaw.(string) 65 | if !ok { 66 | return fmt.Errorf("decode dubbo Service field %s failed, got %v", targetName, strRaw) 67 | } 68 | *target = str 69 | return nil 70 | } 71 | -------------------------------------------------------------------------------- /samples/helloworld/dubbo/src/main/java/org/cloudwego/kitex/samples/client/Application.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 CloudWeGo Authors 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one or more 5 | * contributor license agreements. See the NOTICE file distributed with 6 | * this work for additional information regarding copyright ownership. 7 | * The ASF licenses this file to You under the Apache License, Version 2.0 8 | * (the "License"); you may not use this file except in compliance with 9 | * the License. You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package org.cloudwego.kitex.samples.client; 21 | 22 | import java.io.IOException; 23 | 24 | import org.apache.dubbo.config.ReferenceConfig; 25 | import org.apache.dubbo.config.bootstrap.DubboBootstrap; 26 | import org.cloudwego.kitex.samples.api.*; 27 | 28 | public class Application { 29 | public static void main(String[] args) throws IOException { 30 | ReferenceConfig reference = new ReferenceConfig<>(); 31 | reference.setInterface(GreetProvider.class); 32 | reference.setUrl("127.0.0.1:21000"); 33 | 34 | DubboBootstrap.getInstance() 35 | .application("first-dubbo-consumer") 36 | .reference(reference) 37 | .start(); 38 | 39 | GreetProvider service = reference.get(); 40 | try { 41 | String req = "world"; 42 | String resp = service.Greet(req); 43 | System.out.printf("resp: %s\n", resp); 44 | } catch (Exception e) { 45 | System.out.printf("catch exception: %s\n", e); 46 | } 47 | 48 | try { 49 | GreetRequest reqWithStruct = new GreetRequest("world"); 50 | GreetResponse respWithStruct = service.GreetWithStruct(reqWithStruct); 51 | System.out.printf("respWithStruct: %s\n", respWithStruct.getResp()); 52 | } catch (Exception e) { 53 | System.out.printf("catch exception: %s\n", e); 54 | } 55 | 56 | return; 57 | } 58 | } -------------------------------------------------------------------------------- /registries/zookeeper/resolver/resolver_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 CloudWeGo Authors 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one or more 5 | * contributor license agreements. See the NOTICE file distributed with 6 | * this work for additional information regarding copyright ownership. 7 | * The ASF licenses this file to You under the Apache License, Version 2.0 8 | * (the "License"); you may not use this file except in compliance with 9 | * the License. You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package resolver 21 | 22 | import ( 23 | "testing" 24 | 25 | "github.com/stretchr/testify/assert" 26 | ) 27 | 28 | func TestExtractGroupVersion(t *testing.T) { 29 | tests := []struct { 30 | desc string 31 | expected func(t *testing.T, remaining, group, version string) 32 | }{ 33 | { 34 | desc: "/dubbo/interface:g1:v1", 35 | expected: func(t *testing.T, remaining, group, version string) { 36 | assert.Equal(t, "/dubbo/interface", remaining) 37 | assert.Equal(t, "g1", group) 38 | assert.Equal(t, "v1", version) 39 | }, 40 | }, 41 | { 42 | desc: "/dubbo/interface:g1:", 43 | expected: func(t *testing.T, remaining, group, version string) { 44 | assert.Equal(t, "/dubbo/interface", remaining) 45 | assert.Equal(t, "g1", group) 46 | assert.Empty(t, version) 47 | }, 48 | }, 49 | { 50 | desc: "/dubbo/interface::v1", 51 | expected: func(t *testing.T, remaining, group, version string) { 52 | assert.Equal(t, "/dubbo/interface", remaining) 53 | assert.Empty(t, group) 54 | assert.Equal(t, "v1", version) 55 | }, 56 | }, 57 | { 58 | desc: "/dubbo/interface::", 59 | expected: func(t *testing.T, remaining, group, version string) { 60 | assert.Equal(t, "/dubbo/interface", remaining) 61 | assert.Empty(t, group) 62 | assert.Empty(t, version) 63 | }, 64 | }, 65 | } 66 | 67 | for _, test := range tests { 68 | remaining, group, version := extractGroupVersion(test.desc) 69 | test.expected(t, remaining, group, version) 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /cliff.toml: -------------------------------------------------------------------------------- 1 | # git-cliff ~ default configuration file 2 | # https://git-cliff.org/docs/configuration 3 | # 4 | # Lines starting with "#" are comments. 5 | # Configuration options are organized into tables and keys. 6 | # See documentation for more information on available options. 7 | 8 | [changelog] 9 | # changelog header 10 | header = """ 11 | # Changelog\n 12 | All notable changes to this project will be documented in this file.\n 13 | """ 14 | # template for the changelog body 15 | # https://tera.netlify.app/docs 16 | body = """ 17 | {% if version %}\ 18 | ## [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }} 19 | {% else %}\ 20 | ## [unreleased] 21 | {% endif %}\ 22 | {% for group, commits in commits | group_by(attribute="group") %} 23 | ### {{ group | upper_first }} 24 | {% for commit in commits %} 25 | - {% if commit.breaking %}[**breaking**] {% endif %}{{ commit.message | upper_first }}\ 26 | {% endfor %} 27 | {% endfor %}\n 28 | """ 29 | # remove the leading and trailing whitespace from the template 30 | trim = true 31 | # changelog footer 32 | footer = """ 33 | 34 | """ 35 | 36 | [git] 37 | # parse the commits based on https://www.conventionalcommits.org 38 | conventional_commits = true 39 | # filter out the commits that are not conventional 40 | filter_unconventional = true 41 | # process each line of a commit as an individual commit 42 | split_commits = false 43 | # regex for preprocessing the commit messages 44 | commit_preprocessors = [ 45 | # { pattern = '\((\w+\s)?#([0-9]+)\)', replace = "([#${2}](https://github.com/orhun/git-cliff/issues/${2}))"}, # replace issue numbers 46 | ] 47 | # regex for parsing and grouping commits 48 | commit_parsers = [ 49 | { message = "^feat", group = "Features" }, 50 | { message = "^fix", group = "Bug Fixes" }, 51 | { message = "^doc", group = "Documentation" }, 52 | { message = "^perf", group = "Performance" }, 53 | { message = "^refactor", group = "Refactor" }, 54 | { message = "^style", group = "Styling" }, 55 | { message = "^test", group = "Testing" }, 56 | { message = "^chore\\(release\\): prepare for", skip = true }, 57 | { message = "^chore", group = "Miscellaneous Tasks" }, 58 | { body = ".*security", group = "Security" }, 59 | ] 60 | # protect breaking changes from being skipped due to matching a skipping commit_parser 61 | protect_breaking_commits = false 62 | # filter out the commits that are not matched by commit parsers 63 | filter_commits = false 64 | # glob pattern for matching git tags 65 | tag_pattern = "v[0-9]*" 66 | # regex for skipping tags 67 | skip_tags = "v0.1.0-beta.1" 68 | # regex for ignoring tags 69 | ignore_tags = "" 70 | # sort the tags topologically 71 | topo_order = false 72 | # sort the commits inside sections by oldest/newest order 73 | sort_commits = "oldest" 74 | # limit the number of commits included in the changelog. 75 | # limit_commits = 42 76 | -------------------------------------------------------------------------------- /java/java-reflection.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 CloudWeGo Authors 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one or more 5 | * contributor license agreements. See the NOTICE file distributed with 6 | * this work for additional information regarding copyright ownership. 7 | * The ASF licenses this file to You under the Apache License, Version 2.0 8 | * (the "License"); you may not use this file except in compliance with 9 | * the License. You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package java 21 | 22 | import ( 23 | "reflect" 24 | 25 | "github.com/cloudwego/thriftgo/thrift_reflection" 26 | ) 27 | 28 | // IDL Name: java 29 | // IDL Path: java.thrift 30 | 31 | var file_java_thrift_go_types = []interface{}{ 32 | (*Object)(nil), // Struct 0: java.Object 33 | (*Date)(nil), // Struct 1: java.Date 34 | (*Exception)(nil), // Struct 2: java.Exception 35 | } 36 | 37 | var ( 38 | file_java_thrift *thrift_reflection.FileDescriptor 39 | file_idl_java_rawDesc = []byte{ 40 | 0x1f, 0x8b, 0x8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0x8c, 0x90, 0x41, 0xae, 0x82, 0x30, 41 | 0x18, 0x84, 0xe7, 0x41, 0xe1, 0x89, 0x3f, 0x58, 0x13, 0xef, 0xc1, 0x25, 0xd4, 0x8d, 0xb, 0x3d, 42 | 0xc3, 0x2f, 0xa9, 0x8, 0xa9, 0x60, 0xa4, 0x12, 0x8f, 0x6f, 0x5a, 0x8a, 0x2e, 0x8c, 0x91, 0xd5, 43 | 0xd7, 0xa4, 0x33, 0xdf, 0xa4, 0x25, 0xfc, 0x1, 0xa0, 0x9a, 0x7b, 0xce, 0xcd, 0xf9, 0x56, 0x9d, 44 | 0x4c, 0x86, 0x80, 0x8, 0x0, 0x32, 0x84, 0xee, 0x60, 0x3, 0x41, 0xd9, 0x2, 0x10, 0x36, 0x26, 45 | 0x21, 0x52, 0x7b, 0x2d, 0x11, 0x59, 0x86, 0x9f, 0x6, 0x42, 0x0, 0x20, 0x3e, 0x1c, 0x6b, 0x55, 46 | 0x18, 0x89, 0x30, 0x1d, 0x6c, 0x82, 0xa4, 0xb7, 0x65, 0x3b, 0xee, 0x79, 0xad, 0xb9, 0xeb, 0xf6, 47 | 0x7c, 0x51, 0xe3, 0xc6, 0xd2, 0x29, 0x34, 0x37, 0x65, 0x3e, 0x54, 0x9, 0x91, 0x6d, 0xe2, 0xdb, 48 | 0x82, 0xd8, 0xb0, 0x51, 0xd3, 0xfd, 0xb, 0x27, 0xb8, 0x9b, 0x4a, 0xe7, 0xb6, 0xf8, 0xcb, 0x9e, 49 | 0x6c, 0x1f, 0x85, 0xba, 0x9a, 0xaa, 0x6d, 0xa6, 0x4f, 0xac, 0xde, 0x4f, 0x78, 0xb5, 0xc7, 0x1d, 50 | 0x89, 0xd8, 0xff, 0xdb, 0xbf, 0xe7, 0xcc, 0x33, 0xf1, 0x9c, 0x3b, 0xe2, 0x19, 0x0, 0x0, 0xff, 51 | 0xff, 0xf9, 0xde, 0xba, 0xfa, 0x93, 0x1, 0x0, 0x0, 52 | } 53 | ) 54 | 55 | func init() { 56 | if file_java_thrift != nil { 57 | return 58 | } 59 | type x struct{} 60 | builder := &thrift_reflection.FileDescriptorBuilder{ 61 | Bytes: file_idl_java_rawDesc, 62 | GoTypes: file_java_thrift_go_types, 63 | GoPackagePath: reflect.TypeOf(x{}).PkgPath(), 64 | } 65 | file_java_thrift = thrift_reflection.BuildFileDescriptor(builder) 66 | } 67 | 68 | func GetFileDescriptorForJava() *thrift_reflection.FileDescriptor { 69 | return file_java_thrift 70 | } 71 | -------------------------------------------------------------------------------- /pkg/hessian2/exception/exception_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 CloudWeGo Authors 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one or more 5 | * contributor license agreements. See the NOTICE file distributed with 6 | * this work for additional information regarding copyright ownership. 7 | * The ASF licenses this file to You under the Apache License, Version 2.0 8 | * (the "License"); you may not use this file except in compliance with 9 | * the License. You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package exception 21 | 22 | import ( 23 | "io" 24 | "testing" 25 | 26 | "github.com/cloudwego/kitex/pkg/remote" 27 | 28 | "github.com/cloudwego/kitex/pkg/kerrors" 29 | "github.com/stretchr/testify/assert" 30 | ) 31 | 32 | func TestFromError(t *testing.T) { 33 | tests := []struct { 34 | desc string 35 | inputErr error 36 | expected func(t *testing.T, exception Throwabler, ok bool) 37 | }{ 38 | { 39 | desc: "nil err", 40 | inputErr: nil, 41 | expected: func(t *testing.T, exception Throwabler, ok bool) { 42 | assert.Nil(t, exception) 43 | assert.False(t, ok) 44 | }, 45 | }, 46 | { 47 | desc: "Throwabler err", 48 | inputErr: NewException("FromError test"), 49 | expected: func(t *testing.T, exception Throwabler, ok bool) { 50 | assert.Equal(t, "java.lang.Exception", exception.JavaClassName()) 51 | assert.Equal(t, "FromError test", exception.Error()) 52 | assert.True(t, ok) 53 | }, 54 | }, 55 | { 56 | desc: "DetailedError wraps Throwabler", 57 | inputErr: kerrors.ErrRemoteOrNetwork.WithCause(NewException("FromError test")), 58 | expected: func(t *testing.T, exception Throwabler, ok bool) { 59 | assert.Equal(t, "java.lang.Exception", exception.JavaClassName()) 60 | assert.Equal(t, "FromError test", exception.Error()) 61 | assert.True(t, ok) 62 | }, 63 | }, 64 | { 65 | desc: "TransError wraps DetailedError, DetailedError wraps Throwabler", 66 | inputErr: remote.NewTransError(remote.InternalError, kerrors.ErrRemoteOrNetwork.WithCause(NewException("FromError test"))), 67 | expected: func(t *testing.T, exception Throwabler, ok bool) { 68 | assert.Equal(t, "java.lang.Exception", exception.JavaClassName()) 69 | assert.Equal(t, "FromError test", exception.Error()) 70 | assert.True(t, ok) 71 | }, 72 | }, 73 | { 74 | desc: "TransError wraps DetailedError, DetailedError wraps non-Throwabler", 75 | inputErr: remote.NewTransError(remote.InternalError, kerrors.ErrRemoteOrNetwork.WithCause(io.EOF)), 76 | expected: func(t *testing.T, exception Throwabler, ok bool) { 77 | assert.Nil(t, exception) 78 | assert.False(t, ok) 79 | }, 80 | }, 81 | } 82 | 83 | for _, test := range tests { 84 | t.Run(test.desc, func(t *testing.T) { 85 | exception, ok := FromError(test.inputErr) 86 | test.expected(t, exception, ok) 87 | }) 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to Contribute 2 | 3 | ## Your First Pull Request 4 | We use github for our codebase. You can start by reading [How To Pull Request](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-requests). 5 | 6 | ## Branch Organization 7 | We use [git-flow](https://nvie.com/posts/a-successful-git-branching-model/) as our branch organization, as known as [FDD](https://en.wikipedia.org/wiki/Feature-driven_development) 8 | 9 | ## Bugs 10 | ### 1. How to Find Known Issues 11 | We are using [Github Issues](https://github.com/cloudwego/kitex/issues) for our public bugs. We keep a close eye on this and try to make it clear when we have an internal fix in progress. Before filing a new task, try to make sure your problem doesn’t already exist. 12 | 13 | ### 2. Reporting New Issues 14 | Providing a reduced test code is a recommended way for reporting issues. Then can placed in: 15 | - Just in issues 16 | - [Golang Playground](https://play.golang.org/) 17 | 18 | ### 3. Security Bugs 19 | Please do not report the safe disclosure of bugs to public issues. Contact us by [Support Email](mailto:conduct@cloudwego.io) 20 | 21 | ## How to Get in Touch 22 | - [Email](mailto:conduct@cloudwego.io) 23 | 24 | ## Submit a Pull Request 25 | Before you submit your Pull Request (PR) consider the following guidelines: 26 | 1. Search [GitHub](https://github.com/cloudwego/kitex/pulls) for an open or closed PR that relates to your submission. You don't want to duplicate existing efforts. 27 | 2. Be sure that an issue describes the problem you're fixing, or documents the design for the feature you'd like to add. Discussing the design upfront helps to ensure that we're ready to accept your work. 28 | 3. [Fork](https://docs.github.com/en/github/getting-started-with-github/fork-a-repo) the cloudwego/kitex repo. 29 | 4. In your forked repository, make your changes in a new git branch: 30 | ``` 31 | git checkout -b my-fix-branch develop 32 | ``` 33 | 5. Create your patch, including appropriate test cases. 34 | 6. Follow our [Style Guides](#code-style-guides). 35 | 7. Commit your changes using a descriptive commit message that follows [AngularJS Git Commit Message Conventions](https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit). 36 | Adherence to these conventions is necessary because release notes are automatically generated from these messages. 37 | 8. Push your branch to GitHub: 38 | ``` 39 | git push origin my-fix-branch 40 | ``` 41 | 9. In GitHub, send a pull request to `kitex:develop` 42 | 43 | ## Contribution Prerequisites 44 | - Our development environment keeps up with [Go Official](https://golang.org/project/). 45 | - You need fully checking with lint tools before submit your pull request. [gofmt](https://golang.org/pkg/cmd/gofmt/) and [golangci-lint](https://github.com/golangci/golangci-lint) 46 | - You are familiar with [Github](https://github.com) 47 | - Maybe you need familiar with [Actions](https://github.com/features/actions)(our default workflow tool). 48 | 49 | ## Code Style Guides 50 | Also see [Pingcap General advice](https://pingcap.github.io/style-guide/general.html). 51 | 52 | Good resources: 53 | - [Effective Go](https://golang.org/doc/effective_go) 54 | - [Go Code Review Comments](https://github.com/golang/go/wiki/CodeReviewComments) 55 | - [Uber Go Style Guide](https://github.com/uber-go/guide/blob/master/style.md) 56 | -------------------------------------------------------------------------------- /samples/helloworld/dubbo/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 18 | 19 | 22 | 23 | org.apache 24 | apache 25 | 23 26 | 27 | 28 | 29 | 30 | 31 | 32 | org.apache.maven.plugins 33 | maven-compiler-plugin 34 | 3.8.1 35 | 36 | 8 37 | 8 38 | 39 | 40 | 41 | 42 | 43 | org.cloudwego.kitex 44 | 1.0-SNAPSHOT 45 | 46 | 4.0.0 47 | 48 | kitex-samples 49 | Kitex Samples 50 | Kitex Samples 51 | 52 | 53 | 3.2.0 54 | 5.9.2 55 | 56 | 57 | 58 | 59 | org.apache.dubbo 60 | dubbo 61 | ${dubbo.version} 62 | 63 | 64 | 65 | org.apache.dubbo 66 | dubbo-dependencies-zookeeper-curator5 67 | ${dubbo.version} 68 | pom 69 | 70 | 71 | 72 | org.junit.jupiter 73 | junit-jupiter-engine 74 | ${junit5.version} 75 | test 76 | 77 | 78 | org.junit.jupiter 79 | junit-jupiter-api 80 | ${junit5.version} 81 | test 82 | 83 | 84 | org.junit.jupiter 85 | junit-jupiter-params 86 | ${junit5.version} 87 | test 88 | 89 | 90 | -------------------------------------------------------------------------------- /pkg/server_trans_handler_factory.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 CloudWeGo Authors 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one or more 5 | * contributor license agreements. See the NOTICE file distributed with 6 | * this work for additional information regarding copyright ownership. 7 | * The ASF licenses this file to You under the Apache License, Version 2.0 8 | * (the "License"); you may not use this file except in compliance with 9 | * the License. You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package dubbo 21 | 22 | import ( 23 | "context" 24 | "errors" 25 | "net" 26 | 27 | "github.com/cloudwego/kitex/pkg/endpoint" 28 | "github.com/cloudwego/kitex/pkg/remote" 29 | "github.com/cloudwego/kitex/pkg/remote/trans/netpoll" 30 | cnetpoll "github.com/cloudwego/netpoll" 31 | 32 | "github.com/kitex-contrib/codec-dubbo/pkg/dubbo_spec" 33 | ) 34 | 35 | // NewSvrTransHandlerFactory the factory expand the implementation of DetectableServerTransHandler for 36 | // hessian protocol to support the dubbo protocol probing. 37 | func NewSvrTransHandlerFactory(opts ...Option) remote.ServerTransHandlerFactory { 38 | return &svrTransHandlerFactory{ 39 | ServerTransHandlerFactory: netpoll.NewSvrTransHandlerFactory(), 40 | codec: NewDubboCodec(opts...), 41 | } 42 | } 43 | 44 | type svrTransHandlerFactory struct { 45 | remote.ServerTransHandlerFactory 46 | // the codec should be set with dubbo codec to keep consistent 47 | // with the function ProtocolMatch. 48 | codec remote.Codec 49 | } 50 | 51 | // NewTransHandler the wrapper of ServerTransHandlerFactory.NewTransHandler, replace the codec with dubbo codec when 52 | // invoke the function NewTransHandler, and than restore it. 53 | func (f *svrTransHandlerFactory) NewTransHandler(opt *remote.ServerOption) (remote.ServerTransHandler, error) { 54 | sourceCodec := opt.Codec 55 | opt.Codec = f.codec 56 | defer func() { 57 | opt.Codec = sourceCodec 58 | }() 59 | 60 | handler, err := f.ServerTransHandlerFactory.NewTransHandler(opt) 61 | if err != nil { 62 | return nil, err 63 | } 64 | return &svrTransHandler{ 65 | ServerTransHandler: handler, 66 | }, nil 67 | } 68 | 69 | type svrTransHandler struct { 70 | remote.ServerTransHandler 71 | } 72 | 73 | func (svr *svrTransHandler) ProtocolMatch(ctx context.Context, conn net.Conn) (err error) { 74 | // Check the validity of client preface. 75 | npReader := conn.(interface{ Reader() cnetpoll.Reader }).Reader() 76 | // read at most avoid block 77 | header, err := npReader.Peek(dubbo_spec.HEADER_SIZE) 78 | if err != nil { 79 | return err 80 | } 81 | if header[0] == dubbo_spec.MAGIC_HIGH && header[1] == dubbo_spec.MAGIC_LOW { 82 | return nil 83 | } 84 | return errors.New("error protocol not match dubbo") 85 | } 86 | 87 | func (svr *svrTransHandler) GracefulShutdown(ctx context.Context) error { 88 | if g, ok := svr.ServerTransHandler.(remote.GracefulShutdown); ok { 89 | g.GracefulShutdown(ctx) 90 | } 91 | return nil 92 | } 93 | 94 | func (svr *svrTransHandler) SetInvokeHandleFunc(inkHdlFunc endpoint.Endpoint) { 95 | if s, ok := svr.ServerTransHandler.(remote.InvokeHandleFuncSetter); ok { 96 | s.SetInvokeHandleFunc(inkHdlFunc) 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /samples/helloworld/kitex/kitex_gen/hello/greetservice/greetservice.go: -------------------------------------------------------------------------------- 1 | // Code generated by Kitex v0.7.2. DO NOT EDIT. 2 | 3 | package greetservice 4 | 5 | import ( 6 | "context" 7 | client "github.com/cloudwego/kitex/client" 8 | kitex "github.com/cloudwego/kitex/pkg/serviceinfo" 9 | hello "github.com/kitex-contrib/codec-dubbo/samples/helloworld/kitex/kitex_gen/hello" 10 | ) 11 | 12 | func serviceInfo() *kitex.ServiceInfo { 13 | return greetServiceServiceInfo 14 | } 15 | 16 | var greetServiceServiceInfo = NewServiceInfo() 17 | 18 | func NewServiceInfo() *kitex.ServiceInfo { 19 | serviceName := "GreetService" 20 | handlerType := (*hello.GreetService)(nil) 21 | methods := map[string]kitex.MethodInfo{ 22 | "Greet": kitex.NewMethodInfo(greetHandler, newGreetServiceGreetArgs, newGreetServiceGreetResult, false), 23 | "GreetWithStruct": kitex.NewMethodInfo(greetWithStructHandler, newGreetServiceGreetWithStructArgs, newGreetServiceGreetWithStructResult, false), 24 | } 25 | extra := map[string]interface{}{ 26 | "PackageName": "hello", 27 | "ServiceFilePath": `api.thrift`, 28 | } 29 | svcInfo := &kitex.ServiceInfo{ 30 | ServiceName: serviceName, 31 | HandlerType: handlerType, 32 | Methods: methods, 33 | KiteXGenVersion: "v0.7.2", 34 | Extra: extra, 35 | } 36 | return svcInfo 37 | } 38 | 39 | func greetHandler(ctx context.Context, handler interface{}, arg, result interface{}) error { 40 | realArg := arg.(*hello.GreetServiceGreetArgs) 41 | realResult := result.(*hello.GreetServiceGreetResult) 42 | success, err := handler.(hello.GreetService).Greet(ctx, realArg.Req) 43 | if err != nil { 44 | return err 45 | } 46 | realResult.Success = &success 47 | return nil 48 | } 49 | func newGreetServiceGreetArgs() interface{} { 50 | return hello.NewGreetServiceGreetArgs() 51 | } 52 | 53 | func newGreetServiceGreetResult() interface{} { 54 | return hello.NewGreetServiceGreetResult() 55 | } 56 | 57 | func greetWithStructHandler(ctx context.Context, handler interface{}, arg, result interface{}) error { 58 | realArg := arg.(*hello.GreetServiceGreetWithStructArgs) 59 | realResult := result.(*hello.GreetServiceGreetWithStructResult) 60 | success, err := handler.(hello.GreetService).GreetWithStruct(ctx, realArg.Req) 61 | if err != nil { 62 | return err 63 | } 64 | realResult.Success = success 65 | return nil 66 | } 67 | func newGreetServiceGreetWithStructArgs() interface{} { 68 | return hello.NewGreetServiceGreetWithStructArgs() 69 | } 70 | 71 | func newGreetServiceGreetWithStructResult() interface{} { 72 | return hello.NewGreetServiceGreetWithStructResult() 73 | } 74 | 75 | type kClient struct { 76 | c client.Client 77 | } 78 | 79 | func newServiceClient(c client.Client) *kClient { 80 | return &kClient{ 81 | c: c, 82 | } 83 | } 84 | 85 | func (p *kClient) Greet(ctx context.Context, req string) (r string, err error) { 86 | var _args hello.GreetServiceGreetArgs 87 | _args.Req = req 88 | var _result hello.GreetServiceGreetResult 89 | if err = p.c.Call(ctx, "Greet", &_args, &_result); err != nil { 90 | return 91 | } 92 | return _result.GetSuccess(), nil 93 | } 94 | 95 | func (p *kClient) GreetWithStruct(ctx context.Context, req *hello.GreetRequest) (r *hello.GreetResponse, err error) { 96 | var _args hello.GreetServiceGreetWithStructArgs 97 | _args.Req = req 98 | var _result hello.GreetServiceGreetWithStructResult 99 | if err = p.c.Call(ctx, "GreetWithStruct", &_args, &_result); err != nil { 100 | return 101 | } 102 | return _result.GetSuccess(), nil 103 | } 104 | -------------------------------------------------------------------------------- /pkg/dubbo_spec/header_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 CloudWeGo Authors 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one or more 5 | * contributor license agreements. See the NOTICE file distributed with 6 | * this work for additional information regarding copyright ownership. 7 | * The ASF licenses this file to You under the Apache License, Version 2.0 8 | * (the "License"); you may not use this file except in compliance with 9 | * the License. You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package dubbo_spec 21 | 22 | import ( 23 | "reflect" 24 | "testing" 25 | ) 26 | 27 | func TestDubboHeader_RequestResponseByte(t *testing.T) { 28 | type fields struct { 29 | IsRequest bool 30 | } 31 | tests := []struct { 32 | name string 33 | fields fields 34 | want byte 35 | }{ 36 | { 37 | name: "request", 38 | fields: fields{ 39 | IsRequest: true, 40 | }, 41 | want: IS_REQUEST << REQUEST_BIT_SHIFT, 42 | }, 43 | { 44 | name: "response", 45 | fields: fields{ 46 | IsRequest: false, 47 | }, 48 | want: IS_RESPONSE, 49 | }, 50 | } 51 | for _, tt := range tests { 52 | t.Run(tt.name, func(t *testing.T) { 53 | h := &DubboHeader{ 54 | IsRequest: tt.fields.IsRequest, 55 | } 56 | if got := h.RequestResponseByte(); got != tt.want { 57 | t.Errorf("RequestResponseByte() = %v, want %v", got, tt.want) 58 | } 59 | }) 60 | } 61 | } 62 | 63 | func TestDubboHeader_EncodeToByteSlice(t *testing.T) { 64 | type fields struct { 65 | IsRequest bool 66 | IsOneWay bool 67 | IsEvent bool 68 | SerializationID uint8 69 | Status StatusCode 70 | RequestID uint64 71 | DataLength uint32 72 | } 73 | tests := []struct { 74 | name string 75 | fields fields 76 | want []byte 77 | }{ 78 | { 79 | name: "Request/PingPong/Event/Hessian/OK/0x1234567887654321/0x12344321", 80 | fields: fields{ 81 | IsRequest: true, 82 | IsOneWay: false, 83 | IsEvent: true, 84 | SerializationID: SERIALIZATION_ID_HESSIAN, 85 | Status: StatusOK, 86 | RequestID: 0x1234567887654321, 87 | DataLength: 0x12344321, 88 | }, 89 | want: []byte{ 90 | MAGIC_HIGH, 91 | MAGIC_LOW, 92 | (IS_REQUEST << REQUEST_BIT_SHIFT) | (IS_PINGPONG << ONEWAY_BIT_SHIFT) | (IS_EVENT << EVENT_BIT_SHIFT) | SERIALIZATION_ID_HESSIAN, 93 | byte(StatusOK), 94 | 0x12, 0x34, 0x56, 0x78, 0x87, 0x65, 0x43, 0x21, 95 | 0x12, 0x34, 0x43, 0x21, 96 | }, 97 | }, 98 | } 99 | for _, tt := range tests { 100 | t.Run(tt.name, func(t *testing.T) { 101 | h := &DubboHeader{ 102 | IsRequest: tt.fields.IsRequest, 103 | IsOneWay: tt.fields.IsOneWay, 104 | IsEvent: tt.fields.IsEvent, 105 | SerializationID: tt.fields.SerializationID, 106 | Status: tt.fields.Status, 107 | RequestID: tt.fields.RequestID, 108 | DataLength: tt.fields.DataLength, 109 | } 110 | if got := h.EncodeToByteSlice(); !reflect.DeepEqual(got, tt.want) { 111 | t.Errorf("EncodeToByteSlice() = %v, want %v", got, tt.want) 112 | } 113 | }) 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /registries/zookeeper/registry/options.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 CloudWeGo Authors 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one or more 5 | * contributor license agreements. See the NOTICE file distributed with 6 | * this work for additional information regarding copyright ownership. 7 | * The ASF licenses this file to You under the Apache License, Version 2.0 8 | * (the "License"); you may not use this file except in compliance with 9 | * the License. You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package registry 21 | 22 | import ( 23 | "time" 24 | 25 | "github.com/kitex-contrib/codec-dubbo/registries" 26 | ) 27 | 28 | type Options struct { 29 | Servers []string 30 | RegistryGroup string 31 | Username string 32 | Password string 33 | SessionTimeout time.Duration 34 | } 35 | 36 | func (o *Options) Apply(opts []Option) { 37 | for _, opt := range opts { 38 | opt.F(o) 39 | } 40 | } 41 | 42 | func newOptions(opts []Option) *Options { 43 | o := &Options{} 44 | 45 | o.Apply(opts) 46 | 47 | if len(o.Servers) <= 0 { 48 | panic("Please specify at least one zookeeper server address. e.g. WithServers(\"127.0.0.1:2181\")") 49 | } 50 | 51 | if o.RegistryGroup == "" { 52 | o.RegistryGroup = registries.DefaultRegistryGroup 53 | } 54 | if o.SessionTimeout == 0 { 55 | o.SessionTimeout = defaultSessionTimeout 56 | } 57 | return o 58 | } 59 | 60 | type Option struct { 61 | F func(o *Options) 62 | } 63 | 64 | // WithServers configures target zookeeper servers that zookeeperResolver would connect to. 65 | // Please specify at least one server address, e.g. WithServers("127.0.0.1:2181") 66 | func WithServers(servers ...string) Option { 67 | return Option{F: func(o *Options) { 68 | o.Servers = servers 69 | }} 70 | } 71 | 72 | // WithRegistryGroup configures the group of the zookeepers serving the target dubbo Service. 73 | // In dubbo side, this group is referred to RegistryConfig.group. 74 | func WithRegistryGroup(group string) Option { 75 | return Option{F: func(o *Options) { 76 | o.RegistryGroup = group 77 | }} 78 | } 79 | 80 | // WithSessionTimeout configures the amount of time for which a session 81 | // is considered valid after losing connection to a server. 82 | // Within the session timeout it's possible to reestablish a connection 83 | // to a different server and keep the same session. 84 | // The default SessionTimeout would be 30 * time.Second 85 | func WithSessionTimeout(timeout time.Duration) Option { 86 | return Option{F: func(o *Options) { 87 | o.SessionTimeout = timeout 88 | }} 89 | } 90 | 91 | // WithUsername configures the username to connect to zookeeper servers. 92 | // Please specify this Option with WithPassword together otherwise it 93 | // would not make any sense. 94 | func WithUsername(name string) Option { 95 | return Option{F: func(o *Options) { 96 | o.Username = name 97 | }} 98 | } 99 | 100 | // WithPassword configures the password to connect to zookeeper servers. 101 | // Please specify this Option with WithUsername together otherwise it 102 | // would not make any sense. 103 | func WithPassword(password string) Option { 104 | return Option{F: func(o *Options) { 105 | o.Password = password 106 | }} 107 | } 108 | -------------------------------------------------------------------------------- /registries/zookeeper/resolver/options.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 CloudWeGo Authors 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one or more 5 | * contributor license agreements. See the NOTICE file distributed with 6 | * this work for additional information regarding copyright ownership. 7 | * The ASF licenses this file to You under the Apache License, Version 2.0 8 | * (the "License"); you may not use this file except in compliance with 9 | * the License. You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package resolver 21 | 22 | import ( 23 | "time" 24 | 25 | "github.com/kitex-contrib/codec-dubbo/registries" 26 | ) 27 | 28 | type Options struct { 29 | Servers []string 30 | RegistryGroup string 31 | SessionTimeout time.Duration 32 | Username string 33 | Password string 34 | } 35 | 36 | func (o *Options) Apply(opts []Option) { 37 | for _, opt := range opts { 38 | opt.F(o) 39 | } 40 | } 41 | 42 | func newOptions(opts []Option) *Options { 43 | o := &Options{} 44 | 45 | o.Apply(opts) 46 | 47 | if len(o.Servers) <= 0 { 48 | panic("Please specify at least one zookeeper server address. e.g. WithServers(\"127.0.0.1:2181\")") 49 | } 50 | 51 | if o.RegistryGroup == "" { 52 | o.RegistryGroup = registries.DefaultRegistryGroup 53 | } 54 | if o.SessionTimeout == 0 { 55 | o.SessionTimeout = defaultSessionTimeout 56 | } 57 | return o 58 | } 59 | 60 | type Option struct { 61 | F func(o *Options) 62 | } 63 | 64 | // WithServers configures target zookeeper servers that zookeeperResolver would connect to. 65 | // Please specify at least one server address, e.g. WithServers("127.0.0.1:2181") 66 | func WithServers(servers ...string) Option { 67 | return Option{F: func(o *Options) { 68 | o.Servers = servers 69 | }} 70 | } 71 | 72 | // WithRegistryGroup configures the group of the zookeepers serving the target dubbo Service. 73 | // In dubbo side, this group is referred to RegistryConfig.group. 74 | func WithRegistryGroup(group string) Option { 75 | return Option{F: func(o *Options) { 76 | o.RegistryGroup = group 77 | }} 78 | } 79 | 80 | // WithSessionTimeout configures the amount of time for which a session 81 | // is considered valid after losing connection to a server. 82 | // Within the session timeout it's possible to reestablish a connection 83 | // to a different server and keep the same session. 84 | // The default SessionTimeout would be 30 * time.Second 85 | func WithSessionTimeout(timeout time.Duration) Option { 86 | return Option{F: func(o *Options) { 87 | o.SessionTimeout = timeout 88 | }} 89 | } 90 | 91 | // WithUsername configures the username to connect to zookeeper servers. 92 | // Please specify this Option with WithPassword together otherwise it 93 | // would not make any sense. 94 | func WithUsername(name string) Option { 95 | return Option{F: func(o *Options) { 96 | o.Username = name 97 | }} 98 | } 99 | 100 | // WithPassword configures the password to connect to zookeeper servers. 101 | // Please specify this Option with WithUsername together otherwise it 102 | // would not make any sense. 103 | func WithPassword(password string) Option { 104 | return Option{F: func(o *Options) { 105 | o.Password = password 106 | }} 107 | } 108 | -------------------------------------------------------------------------------- /samples/helloworld/kitex/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/kitex-contrib/codec-dubbo/samples/helloworld/kitex 2 | 3 | go 1.21 4 | 5 | replace github.com/apache/thrift => github.com/apache/thrift v0.13.0 6 | 7 | replace github.com/kitex-contrib/codec-dubbo => ../../../ 8 | 9 | require ( 10 | github.com/apache/thrift v0.16.0 11 | github.com/cloudwego/kitex v0.9.0 12 | github.com/kitex-contrib/codec-dubbo v0.0.0-20231009160704-aad6a2705290 13 | github.com/kitex-contrib/obs-opentelemetry v0.2.6 14 | github.com/pkg/errors v0.9.1 15 | ) 16 | 17 | require ( 18 | github.com/apache/dubbo-go-hessian2 v1.12.4 // indirect 19 | github.com/bytedance/gopkg v0.0.0-20230728082804-614d0af6619b // indirect 20 | github.com/bytedance/sonic v1.11.1 // indirect 21 | github.com/cenkalti/backoff/v4 v4.2.1 // indirect 22 | github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect 23 | github.com/chenzhuoyu/iasm v0.9.1 // indirect 24 | github.com/choleraehyq/pid v0.0.18 // indirect 25 | github.com/cloudwego/configmanager v0.2.0 // indirect 26 | github.com/cloudwego/dynamicgo v0.2.0 // indirect 27 | github.com/cloudwego/fastpb v0.0.4 // indirect 28 | github.com/cloudwego/frugal v0.1.14 // indirect 29 | github.com/cloudwego/localsession v0.0.2 // indirect 30 | github.com/cloudwego/netpoll v0.6.0 // indirect 31 | github.com/cloudwego/thriftgo v0.3.6 // indirect 32 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect 33 | github.com/dubbogo/gost v1.14.0 // indirect 34 | github.com/fatih/structtag v1.2.0 // indirect 35 | github.com/go-logr/logr v1.3.0 // indirect 36 | github.com/go-logr/stdr v1.2.2 // indirect 37 | github.com/golang/protobuf v1.5.3 // indirect 38 | github.com/google/pprof v0.0.0-20230509042627-b1315fad0c5a // indirect 39 | github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0 // indirect 40 | github.com/iancoleman/strcase v0.2.0 // indirect 41 | github.com/jhump/protoreflect v1.8.2 // indirect 42 | github.com/json-iterator/go v1.1.12 // indirect 43 | github.com/klauspost/cpuid/v2 v2.2.4 // indirect 44 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 45 | github.com/modern-go/gls v0.0.0-20220109145502-612d0167dce5 // indirect 46 | github.com/modern-go/reflect2 v1.0.2 // indirect 47 | github.com/oleiade/lane v1.0.1 // indirect 48 | github.com/pmezard/go-difflib v1.0.0 // indirect 49 | github.com/stretchr/testify v1.8.4 // indirect 50 | github.com/tidwall/gjson v1.9.3 // indirect 51 | github.com/tidwall/match v1.1.1 // indirect 52 | github.com/tidwall/pretty v1.2.0 // indirect 53 | github.com/twitchyliquid64/golang-asm v0.15.1 // indirect 54 | go.opentelemetry.io/contrib/instrumentation/runtime v0.45.0 // indirect 55 | go.opentelemetry.io/contrib/propagators/b3 v1.20.0 // indirect 56 | go.opentelemetry.io/contrib/propagators/ot v1.20.0 // indirect 57 | go.opentelemetry.io/otel v1.19.0 // indirect 58 | go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.42.0 // indirect 59 | go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.42.0 // indirect 60 | go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 // indirect 61 | go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0 // indirect 62 | go.opentelemetry.io/otel/metric v1.19.0 // indirect 63 | go.opentelemetry.io/otel/sdk v1.19.0 // indirect 64 | go.opentelemetry.io/otel/sdk/metric v1.19.0 // indirect 65 | go.opentelemetry.io/otel/trace v1.19.0 // indirect 66 | go.opentelemetry.io/proto/otlp v1.0.0 // indirect 67 | go.uber.org/atomic v1.10.0 // indirect 68 | go.uber.org/multierr v1.11.0 // indirect 69 | golang.org/x/arch v0.2.0 // indirect 70 | golang.org/x/net v0.17.0 // indirect 71 | golang.org/x/sync v0.3.0 // indirect 72 | golang.org/x/sys v0.13.0 // indirect 73 | golang.org/x/text v0.13.0 // indirect 74 | google.golang.org/genproto v0.0.0-20231012201019-e917dd12ba7a // indirect 75 | google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b // indirect 76 | google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b // indirect 77 | google.golang.org/grpc v1.59.0 // indirect 78 | google.golang.org/protobuf v1.31.0 // indirect 79 | gopkg.in/yaml.v3 v3.0.1 // indirect 80 | ) 81 | -------------------------------------------------------------------------------- /pkg/options.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 CloudWeGo Authors 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one or more 5 | * contributor license agreements. See the NOTICE file distributed with 6 | * this work for additional information regarding copyright ownership. 7 | * The ASF licenses this file to You under the Apache License, Version 2.0 8 | * (the "License"); you may not use this file except in compliance with 9 | * the License. You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package dubbo 21 | 22 | import ( 23 | "fmt" 24 | "reflect" 25 | 26 | "github.com/cloudwego/thriftgo/thrift_reflection" 27 | "github.com/kitex-contrib/codec-dubbo/pkg/hessian2" 28 | ) 29 | 30 | type Options struct { 31 | JavaClassName string 32 | MethodAnnotations map[string]*hessian2.MethodAnnotation 33 | // store method name mapping of java -> go. 34 | // use the annotation method name + parameter types as the unique identifier. 35 | MethodNames map[string]string 36 | } 37 | 38 | func (o *Options) Apply(opts []Option) { 39 | for _, opt := range opts { 40 | opt.F(o) 41 | } 42 | } 43 | 44 | func newOptions(opts []Option) *Options { 45 | o := &Options{} 46 | 47 | o.Apply(opts) 48 | if o.JavaClassName == "" { 49 | panic("DubboCodec must be initialized with JavaClassName. Please use dubbo.WithJavaClassName().") 50 | } 51 | return o 52 | } 53 | 54 | type Option struct { 55 | F func(o *Options) 56 | } 57 | 58 | // WithJavaClassName configures InterfaceName for server-side service and specifies target InterfaceName for client. 59 | // Each client and service must have its own corresponding DubboCodec. 60 | func WithJavaClassName(name string) Option { 61 | return Option{F: func(o *Options) { 62 | o.JavaClassName = name 63 | }} 64 | } 65 | 66 | // WithFileDescriptor provides method annotations for DubboCodec. 67 | // Adding method annotations allows you to specify the method parameters and method name on the Java side. 68 | func WithFileDescriptor(fd *thrift_reflection.FileDescriptor) Option { 69 | if fd == nil { 70 | panic("Please pass in a valid FileDescriptor.") 71 | } 72 | 73 | return Option{F: func(o *Options) { 74 | parseAnnotations(o, fd) 75 | }} 76 | } 77 | 78 | // parseAnnotations parse method annotations and store them in options. 79 | func parseAnnotations(o *Options, fd *thrift_reflection.FileDescriptor) { 80 | o.MethodAnnotations = make(map[string]*hessian2.MethodAnnotation) 81 | o.MethodNames = make(map[string]string) 82 | 83 | for _, svc := range fd.GetServices() { 84 | prefix := svc.GetName() + "." 85 | 86 | for _, m := range svc.GetMethods() { 87 | ma := hessian2.NewMethodAnnotation(m.GetAnnotations()) 88 | o.MethodAnnotations[prefix+m.GetName()] = ma 89 | params := getMethodParams(m, ma) 90 | 91 | if method, exists := ma.GetMethodName(); exists { 92 | types, err := hessian2.GetParamsTypeList(params) 93 | if err != nil { 94 | panic(fmt.Sprintf("Get method %s parameter types failed: %s", m.GetName(), err.Error())) 95 | } 96 | o.MethodNames[method+types] = m.GetName() 97 | } 98 | } 99 | } 100 | } 101 | 102 | // getMethodParams get the parameter list of a method. 103 | func getMethodParams(m *thrift_reflection.MethodDescriptor, ma *hessian2.MethodAnnotation) []*hessian2.Parameter { 104 | params := make([]*hessian2.Parameter, len(m.GetArgs())) 105 | for i, a := range m.GetArgs() { 106 | typ, err := a.GetGoType() 107 | if err != nil { 108 | panic(fmt.Sprintf("obtain the type of parameter %s in method %s failed: %s", a.GetName(), m.GetName(), err.Error())) 109 | } 110 | val := reflect.New(typ).Elem().Interface() 111 | params[i] = hessian2.NewParameter(val, ma.GetFieldType(i)) 112 | } 113 | return params 114 | } 115 | -------------------------------------------------------------------------------- /.licenserc.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 CloudWeGo Authors 2 | # 3 | # Licensed to the Apache Software Foundation (ASF) under one or more 4 | # contributor license agreements. See the NOTICE file distributed with 5 | # this work for additional information regarding copyright ownership. 6 | # The ASF licenses this file to You under the Apache License, Version 2.0 7 | # (the "License"); you may not use this file except in compliance with 8 | # the License. You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | 18 | header: # `header` section is configurations for source codes license header. 19 | license: 20 | spdx-id: Apache-2.0 # the spdx id of the license, it's convenient when your license is standard SPDX license. 21 | copyright-owner: CloudWeGo Authors 22 | content: | # `license` will be used as the content when `fix` command needs to insert a license header. 23 | Copyright 2023 CloudWeGo Authors 24 | 25 | Licensed to the Apache Software Foundation (ASF) under one or more 26 | contributor license agreements. See the NOTICE file distributed with 27 | this work for additional information regarding copyright ownership. 28 | The ASF licenses this file to You under the Apache License, Version 2.0 29 | (the "License"); you may not use this file except in compliance with 30 | the License. You may obtain a copy of the License at 31 | 32 | http://www.apache.org/licenses/LICENSE-2.0 33 | 34 | Unless required by applicable law or agreed to in writing, software 35 | distributed under the License is distributed on an "AS IS" BASIS, 36 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 37 | See the License for the specific language governing permissions and 38 | limitations under the License. 39 | # `pattern` is optional regexp if all the file headers are the same as `license` or the license of `spdx-id` and `copyright-owner`. 40 | pattern: | 41 | Copyright 202[34] CloudWeGo Authors 42 | 43 | Licensed to the Apache Software Foundation under one or more contributor 44 | license agreements. See the NOTICE file distributed with 45 | this work for additional information regarding copyright 46 | ownership. The Apache Software Foundation licenses this file to you under 47 | the Apache License, Version 2.0 \(the "License"\); you may 48 | not use this file except in compliance with the License. 49 | You may obtain a copy of the License at 50 | http://www.apache.org/licenses/LICENSE-2.0 51 | Unless required by applicable law or agreed to in writing, 52 | software distributed under the License is distributed on an 53 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 54 | KIND, either express or implied. See the License for the 55 | specific language governing permissions and limitations 56 | under the License. 57 | paths: # `paths` are the path list that will be checked (and fixed) by license-eye, default is ['**']. 58 | - '**' 59 | 60 | paths-ignore: # `paths-ignore` are the path list that will be ignored by license-eye. 61 | - '**/*.md' 62 | - '**/testdata/**' 63 | - '**/go.mod' 64 | - '**/go.sum' 65 | - 'LICENSE' 66 | - 'NOTICE' 67 | - '.gitignore' 68 | - ".golangci.yml" 69 | - '.github' 70 | - '*.toml' 71 | - 'LICENSE-APACHE' 72 | - 'OWNERS' 73 | - 'tests' 74 | - 'samples' 75 | - '**/*.thrift' 76 | comment: on-failure # on what condition license-eye will comment on the pull request, `on-failure`, `always`, `never`. 77 | 78 | language: 79 | Go: 80 | extensions: 81 | - ".go" 82 | comment_style_id: SlashAsterisk 83 | comment_start: //# 84 | start: //# 85 | 86 | # license-location-threshold specifies the index threshold where the license header can be located, 87 | # after all, a "header" cannot be TOO far from the file start. 88 | license-location-threshold: 80 89 | 90 | dependency: 91 | files: 92 | - go.mod 93 | -------------------------------------------------------------------------------- /samples/helloworld/kitex/kitex_gen/hello/hessian2-register-api.go: -------------------------------------------------------------------------------- 1 | package hello 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/kitex-contrib/codec-dubbo/pkg/hessian2" 7 | codec "github.com/kitex-contrib/codec-dubbo/pkg/iface" 8 | "github.com/pkg/errors" 9 | ) 10 | 11 | var objectsApi = []interface{}{ 12 | &GreetRequest{}, 13 | &GreetResponse{}, 14 | } 15 | 16 | func init() { 17 | hessian2.Register(objectsApi) 18 | } 19 | 20 | func GetGreetServiceIDLAnnotations() map[string][]string { 21 | return map[string][]string{} 22 | } 23 | 24 | func (p *GreetRequest) Encode(e codec.Encoder) error { 25 | var err error 26 | err = e.Encode(p.Req) 27 | if err != nil { 28 | return err 29 | } 30 | 31 | return nil 32 | } 33 | 34 | func (p *GreetRequest) Decode(d codec.Decoder) error { 35 | var ( 36 | err error 37 | v interface{} 38 | ) 39 | v, err = d.Decode() 40 | if err != nil { 41 | return err 42 | } 43 | err = hessian2.ReflectResponse(v, &p.Req) 44 | if err != nil { 45 | return errors.Wrap(err, fmt.Sprintf("invalid data type: %T", v)) 46 | } 47 | 48 | return nil 49 | } 50 | 51 | func (p *GreetRequest) JavaClassName() string { 52 | return "org.cloudwego.kitex.samples.api.GreetRequest" 53 | } 54 | 55 | func (p *GreetResponse) Encode(e codec.Encoder) error { 56 | var err error 57 | err = e.Encode(p.Resp) 58 | if err != nil { 59 | return err 60 | } 61 | 62 | return nil 63 | } 64 | 65 | func (p *GreetResponse) Decode(d codec.Decoder) error { 66 | var ( 67 | err error 68 | v interface{} 69 | ) 70 | v, err = d.Decode() 71 | if err != nil { 72 | return err 73 | } 74 | err = hessian2.ReflectResponse(v, &p.Resp) 75 | if err != nil { 76 | return errors.Wrap(err, fmt.Sprintf("invalid data type: %T", v)) 77 | } 78 | 79 | return nil 80 | } 81 | 82 | func (p *GreetResponse) JavaClassName() string { 83 | return "org.cloudwego.kitex.samples.api.GreetResponse" 84 | } 85 | 86 | func (p *GreetServiceGreetArgs) Encode(e codec.Encoder) error { 87 | var err error 88 | err = e.Encode(p.Req) 89 | if err != nil { 90 | return err 91 | } 92 | 93 | return nil 94 | } 95 | 96 | func (p *GreetServiceGreetArgs) Decode(d codec.Decoder) error { 97 | var ( 98 | err error 99 | v interface{} 100 | ) 101 | v, err = d.Decode() 102 | if err != nil { 103 | return err 104 | } 105 | err = hessian2.ReflectResponse(v, &p.Req) 106 | if err != nil { 107 | return errors.Wrap(err, fmt.Sprintf("invalid data type: %T", v)) 108 | } 109 | 110 | return nil 111 | } 112 | 113 | func (p *GreetServiceGreetResult) Encode(e codec.Encoder) error { 114 | var err error 115 | err = e.Encode(p.Success) 116 | if err != nil { 117 | return err 118 | } 119 | 120 | return nil 121 | } 122 | 123 | func (p *GreetServiceGreetResult) Decode(d codec.Decoder) error { 124 | var ( 125 | err error 126 | v interface{} 127 | ) 128 | v, err = d.Decode() 129 | if err != nil { 130 | return err 131 | } 132 | err = hessian2.ReflectResponse(v, &p.Success) 133 | if err != nil { 134 | return errors.Wrap(err, fmt.Sprintf("invalid data type: %T", v)) 135 | } 136 | 137 | return nil 138 | } 139 | 140 | func (p *GreetServiceGreetWithStructArgs) Encode(e codec.Encoder) error { 141 | var err error 142 | err = e.Encode(p.Req) 143 | if err != nil { 144 | return err 145 | } 146 | 147 | return nil 148 | } 149 | 150 | func (p *GreetServiceGreetWithStructArgs) Decode(d codec.Decoder) error { 151 | var ( 152 | err error 153 | v interface{} 154 | ) 155 | v, err = d.Decode() 156 | if err != nil { 157 | return err 158 | } 159 | err = hessian2.ReflectResponse(v, &p.Req) 160 | if err != nil { 161 | return errors.Wrap(err, fmt.Sprintf("invalid data type: %T", v)) 162 | } 163 | 164 | return nil 165 | } 166 | 167 | func (p *GreetServiceGreetWithStructResult) Encode(e codec.Encoder) error { 168 | var err error 169 | err = e.Encode(p.Success) 170 | if err != nil { 171 | return err 172 | } 173 | 174 | return nil 175 | } 176 | 177 | func (p *GreetServiceGreetWithStructResult) Decode(d codec.Decoder) error { 178 | var ( 179 | err error 180 | v interface{} 181 | ) 182 | v, err = d.Decode() 183 | if err != nil { 184 | return err 185 | } 186 | err = hessian2.ReflectResponse(v, &p.Success) 187 | if err != nil { 188 | return errors.Wrap(err, fmt.Sprintf("invalid data type: %T", v)) 189 | } 190 | 191 | return nil 192 | } 193 | -------------------------------------------------------------------------------- /pkg/dubbo_spec/header.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 CloudWeGo Authors 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one or more 5 | * contributor license agreements. See the NOTICE file distributed with 6 | * this work for additional information regarding copyright ownership. 7 | * The ASF licenses this file to You under the Apache License, Version 2.0 8 | * (the "License"); you may not use this file except in compliance with 9 | * the License. You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package dubbo_spec 21 | 22 | import ( 23 | "encoding/binary" 24 | "fmt" 25 | "io" 26 | ) 27 | 28 | /* 29 | * Dubbo Protocol detail: 30 | * https://dubbo.apache.org/zh-cn/blog/2018/10/05/dubbo-%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3/ 31 | */ 32 | 33 | const ( 34 | HEADER_SIZE = 16 35 | 36 | MAGIC_HIGH = 0xda 37 | MAGIC_LOW = 0xbb 38 | 39 | IS_REQUEST = 1 40 | IS_RESPONSE = 0 41 | REQUEST_BIT_SHIFT = 7 42 | 43 | IS_ONEWAY = 0 44 | IS_PINGPONG = 1 45 | ONEWAY_BIT_SHIFT = 6 46 | 47 | IS_EVENT = 1 48 | EVENT_BIT_SHIFT = 5 49 | 50 | SERIALIZATION_ID_HESSIAN = 2 51 | SERIALIZATION_ID_MASK = 0x1F 52 | 53 | StatusOK StatusCode = 20 54 | StatusClientTimeout StatusCode = 30 55 | StatusServerTimeout StatusCode = 31 56 | StatusBadRequest StatusCode = 40 57 | StatusBadResponse StatusCode = 50 58 | StatusServiceNotFound StatusCode = 60 59 | StatusServiceError StatusCode = 70 60 | StatusServerError StatusCode = 80 61 | StatusClientError StatusCode = 90 62 | StatusServerPoolExhausted StatusCode = 100 63 | ) 64 | 65 | var ErrInvalidHeader = fmt.Errorf("INVALID HEADER") 66 | 67 | type StatusCode uint8 68 | 69 | type DubboHeader struct { 70 | IsRequest bool // 1 bit 71 | IsOneWay bool // 1 bit 72 | IsEvent bool // 1 bit 73 | SerializationID uint8 // 5 bits 74 | Status StatusCode // 8 bits 75 | RequestID uint64 // 8 bytes 76 | DataLength uint32 // 4 bytes 77 | } 78 | 79 | func (h *DubboHeader) RequestResponseByte() byte { 80 | if h.IsRequest { 81 | return IS_REQUEST << REQUEST_BIT_SHIFT 82 | } 83 | return 0 84 | } 85 | 86 | func (h *DubboHeader) OnewayByte() byte { 87 | if !h.IsOneWay { 88 | return IS_PINGPONG << ONEWAY_BIT_SHIFT 89 | } 90 | return IS_ONEWAY 91 | } 92 | 93 | func (h *DubboHeader) EventByte() byte { 94 | if h.IsEvent { 95 | return IS_EVENT << EVENT_BIT_SHIFT 96 | } 97 | return 0 98 | } 99 | 100 | func (h *DubboHeader) EncodeToByteSlice() []byte { 101 | buf := make([]byte, HEADER_SIZE) 102 | buf[0] = MAGIC_HIGH 103 | buf[1] = MAGIC_LOW 104 | buf[2] = h.RequestResponseByte() | h.OnewayByte() | h.EventByte() | getSerializationID(h.SerializationID) 105 | buf[3] = byte(h.Status) 106 | binary.BigEndian.PutUint64(buf[4:12], h.RequestID) 107 | binary.BigEndian.PutUint32(buf[12:HEADER_SIZE], h.DataLength) 108 | return buf 109 | } 110 | 111 | func (h *DubboHeader) Encode(w io.Writer) error { 112 | _, err := w.Write(h.EncodeToByteSlice()) 113 | return err 114 | } 115 | 116 | func (h *DubboHeader) DecodeFromByteSlice(buf []byte) error { 117 | if buf[0] != MAGIC_HIGH || buf[1] != MAGIC_LOW { 118 | return ErrInvalidHeader 119 | } 120 | h.IsRequest = isRequest(buf[2]) 121 | h.IsOneWay = isOneWay(buf[2]) 122 | h.IsEvent = isEvent(buf[2]) 123 | h.SerializationID = getSerializationID(buf[2]) 124 | h.Status = StatusCode(buf[3]) 125 | h.RequestID = binary.BigEndian.Uint64(buf[4:12]) 126 | h.DataLength = binary.BigEndian.Uint32(buf[12:]) 127 | return nil 128 | } 129 | 130 | func (h *DubboHeader) Decode(r io.Reader) error { 131 | buf := make([]byte, HEADER_SIZE) 132 | if _, err := r.Read(buf); err != nil { 133 | return err 134 | } 135 | return h.DecodeFromByteSlice(buf) 136 | } 137 | 138 | func getSerializationID(b byte) uint8 { 139 | return b & SERIALIZATION_ID_MASK 140 | } 141 | 142 | func BitTest(b byte, shift int, expected byte) bool { 143 | return (b & (1 << shift) >> shift) == expected 144 | } 145 | 146 | func isRequest(b byte) bool { 147 | return BitTest(b, REQUEST_BIT_SHIFT, IS_REQUEST) 148 | } 149 | 150 | func isOneWay(b byte) bool { 151 | return BitTest(b, ONEWAY_BIT_SHIFT, IS_ONEWAY) 152 | } 153 | 154 | func isEvent(b byte) bool { 155 | return BitTest(b, EVENT_BIT_SHIFT, IS_EVENT) 156 | } 157 | -------------------------------------------------------------------------------- /registries/zookeeper/resolver/resolver.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 CloudWeGo Authors 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one or more 5 | * contributor license agreements. See the NOTICE file distributed with 6 | * this work for additional information regarding copyright ownership. 7 | * The ASF licenses this file to You under the Apache License, Version 2.0 8 | * (the "License"); you may not use this file except in compliance with 9 | * the License. You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package resolver 21 | 22 | import ( 23 | "context" 24 | "fmt" 25 | "strings" 26 | "time" 27 | 28 | "github.com/cloudwego/kitex/pkg/discovery" 29 | "github.com/cloudwego/kitex/pkg/klog" 30 | "github.com/cloudwego/kitex/pkg/rpcinfo" 31 | "github.com/go-zookeeper/zk" 32 | "github.com/kitex-contrib/codec-dubbo/registries" 33 | ) 34 | 35 | const ( 36 | defaultSessionTimeout = 30 * time.Second 37 | groupVersionSeparator = ":" 38 | ) 39 | 40 | type zookeeperResolver struct { 41 | conn *zk.Conn 42 | opt *Options 43 | uniqueName string 44 | } 45 | 46 | func NewZookeeperResolver(opts ...Option) (discovery.Resolver, error) { 47 | o := newOptions(opts) 48 | conn, _, err := zk.Connect(o.Servers, o.SessionTimeout) 49 | if err != nil { 50 | return nil, err 51 | } 52 | if o.Username != "" && o.Password != "" { 53 | if err := conn.AddAuth("digest", []byte(fmt.Sprintf("%s:%s", o.Username, o.Password))); err != nil { 54 | return nil, err 55 | } 56 | } 57 | uniName := "dubbo-zookeeper" + "/" + o.RegistryGroup 58 | return &zookeeperResolver{ 59 | conn: conn, 60 | opt: o, 61 | uniqueName: uniName, 62 | }, nil 63 | } 64 | 65 | func (z *zookeeperResolver) Target(ctx context.Context, target rpcinfo.EndpointInfo) (description string) { 66 | interfaceName, ok := target.Tag(registries.DubboServiceInterfaceKey) 67 | if !ok { 68 | panic("please specify target dubbo interface with \"client.WithTag(registries.DubboServiceInterfaceKey, )") 69 | } 70 | group := target.DefaultTag(registries.DubboServiceGroupKey, "") 71 | version := target.DefaultTag(registries.DubboServiceVersionKey, "") 72 | regSvcKey := fmt.Sprintf(registries.RegistryServicesKeyTemplate, z.opt.RegistryGroup, interfaceName) 73 | return regSvcKey + groupVersionSeparator + group + groupVersionSeparator + version 74 | } 75 | 76 | func (z *zookeeperResolver) Resolve(ctx context.Context, desc string) (discovery.Result, error) { 77 | regSvcKey, svcGroup, svcVersion := extractGroupVersion(desc) 78 | rawURLs, _, err := z.conn.Children(regSvcKey) 79 | if err != nil { 80 | return discovery.Result{}, err 81 | } 82 | instances := make([]discovery.Instance, 0, len(rawURLs)) 83 | for _, rawURL := range rawURLs { 84 | u := new(registries.URL) 85 | if err := u.FromString(rawURL); err != nil { 86 | klog.Errorf("invalid dubbo URL from zookeeper: %s, err :%s", rawURL, err) 87 | continue 88 | } 89 | tmpInstance := u.ToInstance() 90 | if group, _ := tmpInstance.Tag(registries.DubboServiceGroupKey); group != svcGroup { 91 | continue 92 | } 93 | if ver, _ := tmpInstance.Tag(registries.DubboServiceVersionKey); ver != svcVersion { 94 | continue 95 | } 96 | instances = append(instances, tmpInstance) 97 | } 98 | return discovery.Result{ 99 | Cacheable: true, 100 | CacheKey: desc, 101 | Instances: instances, 102 | }, nil 103 | } 104 | 105 | func (z *zookeeperResolver) Diff(cacheKey string, prev, next discovery.Result) (discovery.Change, bool) { 106 | return discovery.DefaultDiff(cacheKey, prev, next) 107 | } 108 | 109 | func (z *zookeeperResolver) Name() string { 110 | return z.uniqueName 111 | } 112 | 113 | // extractGroupVersion extract group and version from desc returned by Target() 114 | // e.g. 115 | // input: desc /dubbo/interfaceName:g1:v1 116 | // 117 | // output: remaining /dubbo/interfaceName 118 | // 119 | // group g1 120 | // version v1 121 | func extractGroupVersion(desc string) (remaining, group, version string) { 122 | // retrieve version 123 | verSepIdx := strings.LastIndex(desc, groupVersionSeparator) 124 | version = desc[verSepIdx+1:] 125 | remaining = desc[:verSepIdx] 126 | 127 | // retrieve group 128 | groSepIdx := strings.LastIndex(remaining, groupVersionSeparator) 129 | group = remaining[groSepIdx+1:] 130 | remaining = remaining[:groSepIdx] 131 | 132 | return 133 | } 134 | -------------------------------------------------------------------------------- /samples/helloworld/kitex/kitex_gen/hello/k-api.go: -------------------------------------------------------------------------------- 1 | // Code generated by Kitex v0.7.2. DO NOT EDIT. 2 | 3 | package hello 4 | 5 | import ( 6 | "bytes" 7 | "fmt" 8 | "reflect" 9 | "strings" 10 | 11 | "github.com/apache/thrift/lib/go/thrift" 12 | ) 13 | 14 | // unused protection 15 | var ( 16 | _ = fmt.Formatter(nil) 17 | _ = (*bytes.Buffer)(nil) 18 | _ = (*strings.Builder)(nil) 19 | _ = reflect.Type(nil) 20 | _ = thrift.TProtocol(nil) 21 | ) 22 | 23 | type GreetServiceGreetArgs struct { 24 | Req string `thrift:"req,1" frugal:"1,default,string" json:"req"` 25 | } 26 | 27 | func NewGreetServiceGreetArgs() *GreetServiceGreetArgs { 28 | return &GreetServiceGreetArgs{} 29 | } 30 | 31 | func (p *GreetServiceGreetArgs) InitDefault() { 32 | *p = GreetServiceGreetArgs{} 33 | } 34 | 35 | func (p *GreetServiceGreetArgs) GetReq() (v string) { 36 | return p.Req 37 | } 38 | func (p *GreetServiceGreetArgs) SetReq(val string) { 39 | p.Req = val 40 | } 41 | 42 | func (p *GreetServiceGreetArgs) String() string { 43 | if p == nil { 44 | return "" 45 | } 46 | return fmt.Sprintf("GreetServiceGreetArgs(%+v)", *p) 47 | } 48 | func (p *GreetServiceGreetArgs) GetFirstArgument() interface{} { 49 | return p.Req 50 | } 51 | 52 | type GreetServiceGreetResult struct { 53 | Success *string `thrift:"success,0,optional" frugal:"0,optional,string" json:"success,omitempty"` 54 | } 55 | 56 | func NewGreetServiceGreetResult() *GreetServiceGreetResult { 57 | return &GreetServiceGreetResult{} 58 | } 59 | 60 | func (p *GreetServiceGreetResult) InitDefault() { 61 | *p = GreetServiceGreetResult{} 62 | } 63 | 64 | var GreetServiceGreetResult_Success_DEFAULT string 65 | 66 | func (p *GreetServiceGreetResult) GetSuccess() (v string) { 67 | if !p.IsSetSuccess() { 68 | return GreetServiceGreetResult_Success_DEFAULT 69 | } 70 | return *p.Success 71 | } 72 | func (p *GreetServiceGreetResult) SetSuccess(x interface{}) { 73 | p.Success = x.(*string) 74 | } 75 | 76 | func (p *GreetServiceGreetResult) IsSetSuccess() bool { 77 | return p.Success != nil 78 | } 79 | 80 | func (p *GreetServiceGreetResult) String() string { 81 | if p == nil { 82 | return "" 83 | } 84 | return fmt.Sprintf("GreetServiceGreetResult(%+v)", *p) 85 | } 86 | func (p *GreetServiceGreetResult) GetResult() interface{} { 87 | return p.Success 88 | } 89 | 90 | type GreetServiceGreetWithStructArgs struct { 91 | Req *GreetRequest `thrift:"req,1" frugal:"1,default,GreetRequest" json:"req"` 92 | } 93 | 94 | func NewGreetServiceGreetWithStructArgs() *GreetServiceGreetWithStructArgs { 95 | return &GreetServiceGreetWithStructArgs{} 96 | } 97 | 98 | func (p *GreetServiceGreetWithStructArgs) InitDefault() { 99 | *p = GreetServiceGreetWithStructArgs{} 100 | } 101 | 102 | var GreetServiceGreetWithStructArgs_Req_DEFAULT *GreetRequest 103 | 104 | func (p *GreetServiceGreetWithStructArgs) GetReq() (v *GreetRequest) { 105 | if !p.IsSetReq() { 106 | return GreetServiceGreetWithStructArgs_Req_DEFAULT 107 | } 108 | return p.Req 109 | } 110 | func (p *GreetServiceGreetWithStructArgs) SetReq(val *GreetRequest) { 111 | p.Req = val 112 | } 113 | 114 | func (p *GreetServiceGreetWithStructArgs) IsSetReq() bool { 115 | return p.Req != nil 116 | } 117 | 118 | func (p *GreetServiceGreetWithStructArgs) String() string { 119 | if p == nil { 120 | return "" 121 | } 122 | return fmt.Sprintf("GreetServiceGreetWithStructArgs(%+v)", *p) 123 | } 124 | func (p *GreetServiceGreetWithStructArgs) GetFirstArgument() interface{} { 125 | return p.Req 126 | } 127 | 128 | type GreetServiceGreetWithStructResult struct { 129 | Success *GreetResponse `thrift:"success,0,optional" frugal:"0,optional,GreetResponse" json:"success,omitempty"` 130 | } 131 | 132 | func NewGreetServiceGreetWithStructResult() *GreetServiceGreetWithStructResult { 133 | return &GreetServiceGreetWithStructResult{} 134 | } 135 | 136 | func (p *GreetServiceGreetWithStructResult) InitDefault() { 137 | *p = GreetServiceGreetWithStructResult{} 138 | } 139 | 140 | var GreetServiceGreetWithStructResult_Success_DEFAULT *GreetResponse 141 | 142 | func (p *GreetServiceGreetWithStructResult) GetSuccess() (v *GreetResponse) { 143 | if !p.IsSetSuccess() { 144 | return GreetServiceGreetWithStructResult_Success_DEFAULT 145 | } 146 | return p.Success 147 | } 148 | func (p *GreetServiceGreetWithStructResult) SetSuccess(x interface{}) { 149 | p.Success = x.(*GreetResponse) 150 | } 151 | 152 | func (p *GreetServiceGreetWithStructResult) IsSetSuccess() bool { 153 | return p.Success != nil 154 | } 155 | 156 | func (p *GreetServiceGreetWithStructResult) String() string { 157 | if p == nil { 158 | return "" 159 | } 160 | return fmt.Sprintf("GreetServiceGreetWithStructResult(%+v)", *p) 161 | } 162 | func (p *GreetServiceGreetWithStructResult) GetResult() interface{} { 163 | return p.Success 164 | } 165 | -------------------------------------------------------------------------------- /registries/common_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 CloudWeGo Authors 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one or more 5 | * contributor license agreements. See the NOTICE file distributed with 6 | * this work for additional information regarding copyright ownership. 7 | * The ASF licenses this file to You under the Apache License, Version 2.0 8 | * (the "License"); you may not use this file except in compliance with 9 | * the License. You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package registries 21 | 22 | import ( 23 | "net" 24 | "testing" 25 | 26 | "github.com/cloudwego/kitex/pkg/registry" 27 | "github.com/stretchr/testify/assert" 28 | ) 29 | 30 | func TestURL_FromInfo(t *testing.T) { 31 | commonAddrFunc := func() net.Addr { 32 | addr, _ := net.ResolveTCPAddr("tcp", "0.0.0.0:8888") 33 | return addr 34 | } 35 | commonTags := map[string]string{ 36 | DubboServiceInterfaceKey: "testInterface", 37 | } 38 | tests := []struct { 39 | desc string 40 | initAddr func() net.Addr 41 | info *registry.Info 42 | expected func(t *testing.T, u *URL, err error) 43 | }{ 44 | { 45 | desc: "Info without port", 46 | initAddr: func() net.Addr { 47 | addr, _ := net.ResolveTCPAddr("tcp", "0.0.0.0") 48 | return addr 49 | }, 50 | info: ®istry.Info{ 51 | Tags: commonTags, 52 | }, 53 | expected: func(t *testing.T, u *URL, err error) { 54 | assert.NotNil(t, err) 55 | }, 56 | }, 57 | { 58 | desc: "Info with port-only ipv4 addr", 59 | initAddr: func() net.Addr { 60 | addr, _ := net.ResolveTCPAddr("tcp", ":8888") 61 | return addr 62 | }, 63 | info: ®istry.Info{ 64 | Tags: commonTags, 65 | }, 66 | expected: func(t *testing.T, u *URL, err error) { 67 | assert.Nil(t, err) 68 | assert.NotEmpty(t, u.host) 69 | }, 70 | }, 71 | { 72 | desc: "Info with port-only ipv6 addr", 73 | initAddr: func() net.Addr { 74 | addr, _ := net.ResolveTCPAddr("tcp", "[::]:8888") 75 | return addr 76 | }, 77 | info: ®istry.Info{ 78 | Tags: commonTags, 79 | }, 80 | expected: func(t *testing.T, u *URL, err error) { 81 | assert.Nil(t, err) 82 | assert.NotEmpty(t, u.host) 83 | }, 84 | }, 85 | { 86 | desc: "Info with ipv4 addr", 87 | initAddr: func() net.Addr { 88 | addr, _ := net.ResolveTCPAddr("tcp", "0.0.0.0:8888") 89 | return addr 90 | }, 91 | info: ®istry.Info{ 92 | Tags: commonTags, 93 | }, 94 | expected: func(t *testing.T, u *URL, err error) { 95 | assert.Nil(t, err) 96 | assert.Equal(t, "0.0.0.0:8888", u.host) 97 | }, 98 | }, 99 | { 100 | desc: "Info with ipv6 addr", 101 | initAddr: func() net.Addr { 102 | addr, _ := net.ResolveTCPAddr("tcp", "[::1]:8888") 103 | return addr 104 | }, 105 | info: ®istry.Info{ 106 | Tags: commonTags, 107 | }, 108 | expected: func(t *testing.T, u *URL, err error) { 109 | assert.Nil(t, err) 110 | assert.Equal(t, "[::1]:8888", u.host) 111 | }, 112 | }, 113 | { 114 | desc: "Info without params", 115 | initAddr: commonAddrFunc, 116 | info: ®istry.Info{ 117 | Tags: nil, 118 | }, 119 | expected: func(t *testing.T, u *URL, err error) { 120 | assert.Equal(t, errMissingInterface, err) 121 | }, 122 | }, 123 | { 124 | desc: "Info without Interface specified", 125 | initAddr: commonAddrFunc, 126 | info: ®istry.Info{ 127 | Tags: map[string]string{ 128 | "key": "val", 129 | }, 130 | }, 131 | expected: func(t *testing.T, u *URL, err error) { 132 | assert.Equal(t, errMissingInterface, err) 133 | }, 134 | }, 135 | { 136 | desc: "Info with Interface and other information specified", 137 | initAddr: commonAddrFunc, 138 | info: ®istry.Info{ 139 | Tags: map[string]string{ 140 | DubboServiceInterfaceKey: "interface-val", 141 | DubboServiceGroupKey: "g1", 142 | }, 143 | }, 144 | expected: func(t *testing.T, u *URL, err error) { 145 | assert.Nil(t, err) 146 | assert.Equal(t, "interface-val", u.params[dubboInternalInterfaceKey][0]) 147 | assert.Equal(t, "g1", u.params[dubboInternalGroupKey][0]) 148 | }, 149 | }, 150 | } 151 | 152 | for _, test := range tests { 153 | t.Run(test.desc, func(t *testing.T) { 154 | u := new(URL) 155 | addr := test.initAddr() 156 | test.info.Addr = addr 157 | err := u.FromInfo(test.info) 158 | test.expected(t, u, err) 159 | }) 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | conduct@cloudwego.io. 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at 128 | https://www.contributor-covenant.org/translations. 129 | -------------------------------------------------------------------------------- /pkg/hessian2/utils.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 CloudWeGo Authors 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one or more 5 | * contributor license agreements. See the NOTICE file distributed with 6 | * this work for additional information regarding copyright ownership. 7 | * The ASF licenses this file to You under the Apache License, Version 2.0 8 | * (the "License"); you may not use this file except in compliance with 9 | * the License. You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * 19 | * This source file has been replicated from the original golang.org/x 20 | * repository, and we extend our sincere appreciation to the golang 21 | * development team for their valuable contribution. 22 | */ 23 | 24 | package hessian2 25 | 26 | import ( 27 | "bytes" 28 | "errors" 29 | "fmt" 30 | "runtime" 31 | "runtime/debug" 32 | "sync" 33 | ) 34 | 35 | var errGoexit = errors.New("runtime.Goexit was called") 36 | 37 | type panicError struct { 38 | value interface{} 39 | stack []byte 40 | } 41 | 42 | func (p *panicError) Error() string { 43 | return fmt.Sprintf("%v\n\n%s", p.value, p.stack) 44 | } 45 | 46 | func newPanicError(v interface{}) error { 47 | stack := debug.Stack() 48 | 49 | // The first line of the stack trace is of the form "goroutine N [status]:" 50 | // but by the time the panic reaches Do the goroutine may no longer exist 51 | // and its status will have changed. Trim out the misleading line. 52 | if line := bytes.IndexByte(stack[:], '\n'); line >= 0 { 53 | stack = stack[line+1:] 54 | } 55 | return &panicError{value: v, stack: stack} 56 | } 57 | 58 | type call struct { 59 | wg sync.WaitGroup 60 | 61 | val interface{} 62 | err error 63 | 64 | forgotten bool 65 | 66 | dups int 67 | chans []chan<- Result 68 | } 69 | 70 | // Group represents a class of work and forms a namespace in 71 | // which units of work can be executed with duplicate suppression. 72 | // The only difference between this Group and golang.org/x/sync/singleflight is that key is reflect.Type 73 | // cause reflect.Type.String() could not be guaranteed to be unique. 74 | type Group struct { 75 | mu sync.Mutex // protects m 76 | m map[methodKey]*call // lazily initialized 77 | } 78 | 79 | type Result struct { 80 | Val interface{} 81 | Err error 82 | Shared bool 83 | } 84 | 85 | func (g *Group) Do(key methodKey, fn func() (interface{}, error)) (v interface{}, err error, shared bool) { 86 | g.mu.Lock() 87 | if g.m == nil { 88 | g.m = make(map[methodKey]*call) 89 | } 90 | if c, ok := g.m[key]; ok { 91 | c.dups++ 92 | g.mu.Unlock() 93 | c.wg.Wait() 94 | 95 | if e, ok := c.err.(*panicError); ok { 96 | panic(e) 97 | } else if c.err == errGoexit { 98 | runtime.Goexit() 99 | } 100 | return c.val, c.err, true 101 | } 102 | c := new(call) 103 | c.wg.Add(1) 104 | g.m[key] = c 105 | g.mu.Unlock() 106 | 107 | g.doCall(c, key, fn) 108 | return c.val, c.err, c.dups > 0 109 | } 110 | 111 | func (g *Group) DoChan(key methodKey, fn func() (interface{}, error)) <-chan Result { 112 | ch := make(chan Result, 1) 113 | g.mu.Lock() 114 | if g.m == nil { 115 | g.m = make(map[methodKey]*call) 116 | } 117 | if c, ok := g.m[key]; ok { 118 | c.dups++ 119 | c.chans = append(c.chans, ch) 120 | g.mu.Unlock() 121 | return ch 122 | } 123 | c := &call{chans: []chan<- Result{ch}} 124 | c.wg.Add(1) 125 | g.m[key] = c 126 | g.mu.Unlock() 127 | 128 | go g.doCall(c, key, fn) 129 | 130 | return ch 131 | } 132 | 133 | func (g *Group) doCall(c *call, key methodKey, fn func() (interface{}, error)) { 134 | normalReturn := false 135 | recovered := false 136 | 137 | // use double-defer to distinguish panic from runtime.Goexit, 138 | // more details see https://golang.org/cl/134395 139 | defer func() { 140 | // the given function invoked runtime.Goexit 141 | if !normalReturn && !recovered { 142 | c.err = errGoexit 143 | } 144 | 145 | c.wg.Done() 146 | g.mu.Lock() 147 | defer g.mu.Unlock() 148 | if !c.forgotten { 149 | delete(g.m, key) 150 | } 151 | 152 | if e, ok := c.err.(*panicError); ok { 153 | // In order to prevent the waiting channels from being blocked forever, 154 | // needs to ensure that this panic cannot be recovered. 155 | if len(c.chans) > 0 { 156 | go panic(e) 157 | select {} // Keep this goroutine around so that it will appear in the crash dump. 158 | } else { 159 | panic(e) 160 | } 161 | } else if c.err == errGoexit { 162 | // Already in the process of goexit, no need to call again 163 | } else { 164 | // Normal return 165 | for _, ch := range c.chans { 166 | ch <- Result{c.val, c.err, c.dups > 0} 167 | } 168 | } 169 | }() 170 | 171 | func() { 172 | defer func() { 173 | if !normalReturn { 174 | // Ideally, we would wait to take a stack trace until we've determined 175 | // whether this is a panic or a runtime.Goexit. 176 | // 177 | // Unfortunately, the only way we can distinguish the two is to see 178 | // whether the recover stopped the goroutine from terminating, and by 179 | // the time we know that, the part of the stack trace relevant to the 180 | // panic has been discarded. 181 | if r := recover(); r != nil { 182 | c.err = newPanicError(r) 183 | } 184 | } 185 | }() 186 | 187 | c.val, c.err = fn() 188 | normalReturn = true 189 | }() 190 | 191 | if !normalReturn { 192 | recovered = true 193 | } 194 | } 195 | 196 | func (g *Group) Forget(key methodKey) { 197 | g.mu.Lock() 198 | if c, ok := g.m[key]; ok { 199 | c.forgotten = true 200 | } 201 | delete(g.m, key) 202 | g.mu.Unlock() 203 | } 204 | -------------------------------------------------------------------------------- /registries/common.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 CloudWeGo Authors 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one or more 5 | * contributor license agreements. See the NOTICE file distributed with 6 | * this work for additional information regarding copyright ownership. 7 | * The ASF licenses this file to You under the Apache License, Version 2.0 8 | * (the "License"); you may not use this file except in compliance with 9 | * the License. You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package registries 21 | 22 | import ( 23 | "errors" 24 | "fmt" 25 | "net" 26 | "net/url" 27 | "strconv" 28 | "strings" 29 | 30 | "github.com/cloudwego/kitex/pkg/discovery" 31 | "github.com/cloudwego/kitex/pkg/registry" 32 | ) 33 | 34 | const ( 35 | // these keys prefixed with "DubboService" are used for user configuring dubbo specific information 36 | // and are passed within the kitex. 37 | DubboServiceGroupKey = "dubbo-service-group" 38 | DubboServiceVersionKey = "dubbo-service-version" 39 | DubboServiceInterfaceKey = "dubbo-service-interface" 40 | DubboServiceWeightKey = "dubbo-service-weight" 41 | DubboServiceApplicationKey = "dubbo-service-application" 42 | 43 | // these keys prefixed with "dubboInternal" are used for interacting with dubbo-ecosystem 44 | // and are transferred out of bounds. 45 | dubboInternalGroupKey = "group" 46 | dubboInternalVersionKey = "version" 47 | dubboInternalInterfaceKey = "interface" 48 | dubboInternalWeightKey = "weight" 49 | dubboInternalApplicationKey = "application" 50 | 51 | DefaultRegistryGroup = "dubbo" 52 | DefaultProtocol = "dubbo" 53 | DefaultDubboServiceWeight = 100 54 | 55 | RegistryServicesKeyTemplate = "/%s/%s/providers" 56 | ) 57 | 58 | var ( 59 | outboundDubboRegistryKeysMapping = map[string]string{ 60 | DubboServiceGroupKey: dubboInternalGroupKey, 61 | DubboServiceVersionKey: dubboInternalVersionKey, 62 | DubboServiceInterfaceKey: dubboInternalInterfaceKey, 63 | DubboServiceWeightKey: dubboInternalWeightKey, 64 | DubboServiceApplicationKey: dubboInternalApplicationKey, 65 | } 66 | 67 | errMissingInterface = errors.New("tags must contain DubboServiceInterfaceKey: pair") 68 | ) 69 | 70 | type URL struct { 71 | protocol string 72 | host string 73 | interfaceName string 74 | params url.Values 75 | } 76 | 77 | func (u *URL) FromString(raw string) error { 78 | decodedRaw, err := url.PathUnescape(raw) 79 | if err != nil { 80 | return err 81 | } 82 | rawURL, err := url.Parse(decodedRaw) 83 | if err != nil { 84 | return err 85 | } 86 | u.protocol = rawURL.Scheme 87 | u.host = rawURL.Host 88 | u.interfaceName = strings.TrimPrefix(rawURL.Path, "/") 89 | u.params = rawURL.Query() 90 | return nil 91 | } 92 | 93 | func (u *URL) ToString() string { 94 | paramsPart, _ := url.QueryUnescape(u.params.Encode()) 95 | raw := fmt.Sprintf("%s://%s/%s?%s", u.protocol, u.host, u.interfaceName, paramsPart) 96 | return url.QueryEscape(raw) 97 | } 98 | 99 | func (u *URL) ToInstance() discovery.Instance { 100 | weight := DefaultDubboServiceWeight 101 | if weightStr := u.params.Get(dubboInternalWeightKey); weightStr != "" { 102 | if weightParam, err := strconv.Atoi(weightStr); err == nil { 103 | weight = weightParam 104 | } 105 | } 106 | params := map[string]string{ 107 | DubboServiceGroupKey: u.params.Get(dubboInternalGroupKey), 108 | DubboServiceVersionKey: u.params.Get(dubboInternalVersionKey), 109 | } 110 | return discovery.NewInstance("tcp", u.host, weight, params) 111 | } 112 | 113 | func (u *URL) FromInfo(info *registry.Info) error { 114 | u.protocol = DefaultProtocol 115 | if err := u.checkAndSetHost(info.Addr.String()); err != nil { 116 | return err 117 | } 118 | if err := u.filterAndSetParams(info.Tags); err != nil { 119 | return err 120 | } 121 | return nil 122 | } 123 | 124 | func (u *URL) GetRegistryServiceKey(registryGroup string) string { 125 | return fmt.Sprintf(RegistryServicesKeyTemplate, registryGroup, u.interfaceName) 126 | } 127 | 128 | func (u *URL) checkAndSetHost(addr string) error { 129 | sepInd := strings.LastIndex(addr, ":") 130 | // there is no port part 131 | if sepInd < 0 { 132 | return fmt.Errorf("addr %s missing port", addr) 133 | } 134 | host := addr[:sepInd] 135 | port := addr[sepInd+1:] 136 | finalHost := host 137 | if host == "" || host == "[::]" { 138 | ipv4, err := getLocalIPV4Address() 139 | if err != nil { 140 | return fmt.Errorf("get local ipv4 error, cause %s", err) 141 | } 142 | finalHost = ipv4 143 | u.host = ipv4 + ":" + port 144 | } 145 | 146 | u.host = finalHost + ":" + port 147 | 148 | return nil 149 | } 150 | 151 | func (u *URL) filterAndSetParams(params map[string]string) error { 152 | missingInterfaceFlag := true 153 | if len(params) <= 0 { 154 | return errMissingInterface 155 | } 156 | finalParams := make(url.Values) 157 | for key, val := range params { 158 | if dubboKey, ok := outboundDubboRegistryKeysMapping[key]; ok { 159 | finalParams.Set(dubboKey, val) 160 | } 161 | if key == DubboServiceInterfaceKey { 162 | u.interfaceName = val 163 | missingInterfaceFlag = false 164 | } 165 | } 166 | if missingInterfaceFlag { 167 | return errMissingInterface 168 | } 169 | u.params = finalParams 170 | return nil 171 | } 172 | 173 | func getLocalIPV4Address() (string, error) { 174 | addrs, err := net.InterfaceAddrs() 175 | if err != nil { 176 | return "", err 177 | } 178 | 179 | for _, addr := range addrs { 180 | ipNet, isIpNet := addr.(*net.IPNet) 181 | if isIpNet && !ipNet.IP.IsLoopback() { 182 | ipv4 := ipNet.IP.To4() 183 | if ipv4 != nil { 184 | return ipv4.String(), nil 185 | } 186 | } 187 | } 188 | return "", errors.New("there is not valid ip address found") 189 | } 190 | -------------------------------------------------------------------------------- /registries/zookeeper/registry/registry.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 CloudWeGo Authors 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one or more 5 | * contributor license agreements. See the NOTICE file distributed with 6 | * this work for additional information regarding copyright ownership. 7 | * The ASF licenses this file to You under the Apache License, Version 2.0 8 | * (the "License"); you may not use this file except in compliance with 9 | * the License. You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package registry 21 | 22 | import ( 23 | "context" 24 | "errors" 25 | "fmt" 26 | "strings" 27 | "sync" 28 | "time" 29 | 30 | "github.com/cloudwego/kitex/pkg/registry" 31 | "github.com/go-zookeeper/zk" 32 | "github.com/kitex-contrib/codec-dubbo/registries" 33 | ) 34 | 35 | const ( 36 | defaultSessionTimeout = 30 * time.Second 37 | ) 38 | 39 | type zookeeperRegistry struct { 40 | conn *zk.Conn 41 | opt *Options 42 | canceler *canceler 43 | } 44 | 45 | func NewZookeeperRegistry(opts ...Option) (registry.Registry, error) { 46 | o := newOptions(opts) 47 | conn, eventChan, err := zk.Connect(o.Servers, o.SessionTimeout) 48 | if err != nil { 49 | return nil, err 50 | } 51 | if o.Username != "" && o.Password != "" { 52 | if err := conn.AddAuth("digest", []byte(fmt.Sprintf("%s:%s", o.Username, o.Password))); err != nil { 53 | return nil, err 54 | } 55 | } 56 | // This connection timeout should not exceed sessionTimeout and should not be too small. 57 | // So just pick halfTimeout in the middle range. 58 | halfTimeout := o.SessionTimeout / 2 59 | ticker := time.NewTimer(halfTimeout) 60 | for { 61 | select { 62 | case event := <-eventChan: 63 | if event.State == zk.StateConnected { 64 | return &zookeeperRegistry{ 65 | conn: conn, 66 | opt: o, 67 | canceler: newCanceler(), 68 | }, nil 69 | } 70 | case <-ticker.C: 71 | return nil, fmt.Errorf("waiting for zookeeper connected time out: elapsed %d seconds", halfTimeout/time.Second) 72 | } 73 | } 74 | } 75 | 76 | func (z *zookeeperRegistry) Register(info *registry.Info) error { 77 | if info == nil { 78 | return nil 79 | } 80 | u := new(registries.URL) 81 | if err := u.FromInfo(info); err != nil { 82 | return err 83 | } 84 | path := u.GetRegistryServiceKey(z.opt.RegistryGroup) 85 | content := u.ToString() 86 | finalPath := path + "/" + content 87 | if err := z.createNode(finalPath, nil, true); err != nil { 88 | return err 89 | } 90 | ctx, cancel := context.WithCancel(context.Background()) 91 | z.canceler.add(finalPath, cancel) 92 | go z.keepalive(ctx, finalPath, nil) 93 | return nil 94 | } 95 | 96 | func (z *zookeeperRegistry) createNode(path string, content []byte, ephemeral bool) error { 97 | exists, stat, err := z.conn.Exists(path) 98 | if err != nil { 99 | return err 100 | } 101 | // ephemeral nodes handling after restart 102 | // fixes a race condition if the server crashes without using CreateProtectedEphemeralSequential() 103 | // https://github.com/go-kratos/kratos/blob/main/contrib/registry/zookeeper/register.go 104 | if exists && ephemeral { 105 | err = z.conn.Delete(path, stat.Version) 106 | if err != nil && err != zk.ErrNoNode { 107 | return err 108 | } 109 | exists = false 110 | } 111 | if !exists { 112 | i := strings.LastIndex(path, "/") 113 | if i > 0 { 114 | err := z.createNode(path[0:i], nil, false) 115 | if err != nil && !errors.Is(err, zk.ErrNodeExists) { 116 | return err 117 | } 118 | } 119 | var flag int32 120 | if ephemeral { 121 | flag = zk.FlagEphemeral 122 | } 123 | if z.opt.Username != "" && z.opt.Password != "" { 124 | _, err = z.conn.Create(path, content, flag, zk.DigestACL(zk.PermAll, z.opt.Username, z.opt.Password)) 125 | } else { 126 | _, err = z.conn.Create(path, content, flag, zk.WorldACL(zk.PermAll)) 127 | } 128 | if err != nil { 129 | return err 130 | } 131 | } 132 | return nil 133 | } 134 | 135 | func (z *zookeeperRegistry) keepalive(ctx context.Context, path string, content []byte) { 136 | sessionID := z.conn.SessionID() 137 | ticker := time.NewTicker(time.Second) 138 | defer ticker.Stop() 139 | for { 140 | select { 141 | case <-ctx.Done(): 142 | return 143 | case <-ticker.C: 144 | cur := z.conn.SessionID() 145 | if cur != 0 && sessionID != cur { 146 | if err := z.createNode(path, content, true); err == nil { 147 | sessionID = cur 148 | } 149 | } 150 | } 151 | } 152 | } 153 | 154 | func (z *zookeeperRegistry) Deregister(info *registry.Info) error { 155 | if info == nil { 156 | return nil 157 | } 158 | u := new(registries.URL) 159 | if err := u.FromInfo(info); err != nil { 160 | return err 161 | } 162 | path := u.GetRegistryServiceKey(z.opt.RegistryGroup) 163 | content := u.ToString() 164 | finalPath := path + "/" + content 165 | cancel, ok := z.canceler.remove(finalPath) 166 | if ok { 167 | cancel() 168 | } 169 | return z.deleteNode(finalPath) 170 | } 171 | 172 | func (z *zookeeperRegistry) deleteNode(path string) error { 173 | return z.conn.Delete(path, -1) 174 | } 175 | 176 | type canceler struct { 177 | mu sync.Mutex 178 | // key: zookeeper path, val: CancelFunc to stop the keepalive functionality of this zookeeper path 179 | cancelMap map[string]context.CancelFunc 180 | } 181 | 182 | func newCanceler() *canceler { 183 | return &canceler{ 184 | cancelMap: make(map[string]context.CancelFunc), 185 | } 186 | } 187 | 188 | func (c *canceler) add(path string, f context.CancelFunc) { 189 | c.mu.Lock() 190 | c.cancelMap[path] = f 191 | c.mu.Unlock() 192 | } 193 | 194 | // remove deletes the CancelFunc specified by path. 195 | // If this CancelFunc exists, returns it and true. 196 | // If not, returns nil and false. 197 | func (c *canceler) remove(path string) (context.CancelFunc, bool) { 198 | c.mu.Lock() 199 | cancel, ok := c.cancelMap[path] 200 | if ok { 201 | delete(c.cancelMap, path) 202 | c.mu.Unlock() 203 | return cancel, true 204 | } 205 | c.mu.Unlock() 206 | return nil, false 207 | } 208 | -------------------------------------------------------------------------------- /pkg/hessian2/parameter_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 CloudWeGo Authors 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one or more 5 | * contributor license agreements. See the NOTICE file distributed with 6 | * this work for additional information regarding copyright ownership. 7 | * The ASF licenses this file to You under the Apache License, Version 2.0 8 | * (the "License"); you may not use this file except in compliance with 9 | * the License. You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package hessian2 21 | 22 | import ( 23 | "fmt" 24 | "reflect" 25 | "testing" 26 | 27 | "github.com/stretchr/testify/assert" 28 | ) 29 | 30 | type testInternalStruct struct { 31 | Field int8 32 | } 33 | 34 | type testStructA struct { 35 | Field int8 36 | } 37 | 38 | type testStructB struct { 39 | Internal *testInternalStruct 40 | } 41 | 42 | type testStructC struct { 43 | FieldA int8 44 | FieldB int64 45 | FieldC float64 46 | FieldD string 47 | FieldE []int32 48 | FieldF []string 49 | } 50 | 51 | func TestTypesCache_getTypes(t *testing.T) { 52 | tests := []struct { 53 | desc string 54 | datum []Parameter 55 | expected func(t *testing.T, c *MethodCache) 56 | }{ 57 | { 58 | desc: "same structs with basic Type", 59 | datum: []Parameter{ 60 | {value: &testStructA{Field: 1}}, 61 | {value: &testStructA{Field: 2}}, 62 | }, 63 | expected: func(t *testing.T, c *MethodCache) { 64 | assert.Equal(t, 1, c.len()) 65 | data := &testStructA{Field: 3} 66 | key := methodKey{typ: reflect.ValueOf(data).Type()} 67 | types, ok := c.get(key) 68 | assert.Equal(t, true, ok) 69 | assert.Equal(t, "Ljava/lang/Byte;", types) 70 | }, 71 | }, 72 | { 73 | desc: "same structs with embedded Type", 74 | datum: []Parameter{ 75 | {value: &testStructB{ 76 | Internal: &testInternalStruct{ 77 | Field: 1, 78 | }, 79 | }}, 80 | {value: &testStructB{ 81 | Internal: &testInternalStruct{ 82 | Field: 2, 83 | }, 84 | }}, 85 | }, 86 | expected: func(t *testing.T, c *MethodCache) { 87 | assert.Equal(t, 1, c.len()) 88 | data := &testStructB{ 89 | Internal: &testInternalStruct{ 90 | Field: 3, 91 | }, 92 | } 93 | key := methodKey{typ: reflect.ValueOf(data).Type()} 94 | types, ok := c.get(key) 95 | assert.Equal(t, true, ok) 96 | assert.Equal(t, "Ljava/lang/Object;", types) 97 | }, 98 | }, 99 | { 100 | desc: "different structs", 101 | datum: []Parameter{ 102 | {value: &testStructA{Field: 1}}, 103 | {value: &testStructB{ 104 | Internal: &testInternalStruct{ 105 | Field: 2, 106 | }, 107 | }}, 108 | }, 109 | expected: func(t *testing.T, c *MethodCache) { 110 | assert.Equal(t, 2, c.len()) 111 | dataA := &testStructA{Field: 3} 112 | dataB := &testStructB{ 113 | Internal: &testInternalStruct{ 114 | Field: 3, 115 | }, 116 | } 117 | keyA := methodKey{typ: reflect.ValueOf(dataA).Type()} 118 | keyB := methodKey{typ: reflect.ValueOf(dataB).Type()} 119 | types, ok := c.get(keyA) 120 | assert.Equal(t, true, ok) 121 | assert.Equal(t, "Ljava/lang/Byte;", types) 122 | types, ok = c.get(keyB) 123 | assert.Equal(t, true, ok) 124 | assert.Equal(t, "Ljava/lang/Object;", types) 125 | }, 126 | }, 127 | { 128 | desc: "use annotations to specify types", 129 | datum: []Parameter{ 130 | { 131 | value: &testStructA{Field: 1}, 132 | typeAnno: "byte", 133 | }, 134 | { 135 | value: &testStructB{ 136 | Internal: &testInternalStruct{ 137 | Field: 2, 138 | }, 139 | }, 140 | typeAnno: "java.lang.Object", 141 | }, 142 | { 143 | value: &testStructC{ 144 | FieldA: 3, 145 | FieldB: 4, 146 | FieldC: 5.0, 147 | FieldD: "6", 148 | FieldE: []int32{7, 8}, 149 | FieldF: []string{"9", "10"}, 150 | }, 151 | typeAnno: "byte,long,double,java.lang.String,int[],java.lang.String[]", 152 | }, 153 | { 154 | value: &testStructC{ 155 | FieldA: 3, 156 | FieldB: 4, 157 | FieldC: 5.0, 158 | FieldD: "6", 159 | FieldE: []int32{7, 8}, 160 | FieldF: []string{"9", "10"}, 161 | }, 162 | typeAnno: "-,-,-,-,-,-", 163 | }, 164 | }, 165 | expected: func(t *testing.T, c *MethodCache) { 166 | assert.Equal(t, 4, c.len()) 167 | 168 | keyA := methodKey{typ: reflect.ValueOf(&testStructA{}).Type(), anno: "byte"} 169 | types, ok := c.get(keyA) 170 | assert.Equal(t, true, ok) 171 | assert.Equal(t, "B", types) 172 | 173 | keyB := methodKey{typ: reflect.ValueOf(&testStructB{}).Type(), anno: "java.lang.Object"} 174 | types, ok = c.get(keyB) 175 | assert.Equal(t, true, ok) 176 | assert.Equal(t, "Ljava/lang/Object;", types) 177 | 178 | keyC := methodKey{typ: reflect.ValueOf(&testStructC{}).Type(), anno: "byte,long,double,java.lang.String,int[],java.lang.String[]"} 179 | types, ok = c.get(keyC) 180 | assert.Equal(t, true, ok) 181 | assert.Equal(t, "BJDLjava/lang/String;[I[Ljava/lang/String;", types) 182 | 183 | keyD := methodKey{typ: reflect.ValueOf(&testStructC{}).Type(), anno: "-,-,-,-,-,-"} 184 | types, ok = c.get(keyD) 185 | assert.Equal(t, true, ok) 186 | assert.Equal(t, "Ljava/lang/Byte;Ljava/lang/Long;Ljava/lang/Double;Ljava/lang/String;Ljava/util/List;Ljava/util/List;", types) 187 | }, 188 | }, 189 | } 190 | 191 | for _, test := range tests { 192 | t.Run(test.desc, func(t *testing.T) { 193 | tc := new(MethodCache) 194 | // run getByData concurrently 195 | for i, data := range test.datum { 196 | testData := data 197 | t.Run(fmt.Sprintf("struct%d", i), func(t *testing.T) { 198 | t.Parallel() 199 | _, err := tc.GetTypes(testData.value, NewMethodAnnotation(map[string][]string{HESSIAN_ARGS_TYPE_TAG: {testData.typeAnno}})) 200 | if err != nil { 201 | t.Fatal(err) 202 | } 203 | }) 204 | } 205 | t.Cleanup(func() { 206 | test.expected(t, tc) 207 | }) 208 | }) 209 | } 210 | } 211 | -------------------------------------------------------------------------------- /pkg/hessian2/parameter.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 CloudWeGo Authors 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one or more 5 | * contributor license agreements. See the NOTICE file distributed with 6 | * this work for additional information regarding copyright ownership. 7 | * The ASF licenses this file to You under the Apache License, Version 2.0 8 | * (the "License"); you may not use this file except in compliance with 9 | * the License. You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * 19 | * This source file has been replicated from the original dubbo-go project 20 | * repository, and we extend our sincere appreciation to the dubbo-go 21 | * development team for their valuable contribution. 22 | */ 23 | 24 | package hessian2 25 | 26 | import ( 27 | "fmt" 28 | "reflect" 29 | "strings" 30 | "sync" 31 | "time" 32 | 33 | hessian "github.com/apache/dubbo-go-hessian2" 34 | ) 35 | 36 | // MethodCache maintains a cache from method parameter types (reflect.Type) and method annotations to the type strings used by Hessian2. 37 | type MethodCache struct { 38 | group Group 39 | typesMap sync.Map 40 | } 41 | 42 | type methodKey struct { 43 | typ reflect.Type 44 | anno string 45 | } 46 | 47 | // GetTypes returns the Types string for the given method parameter data and method annotations. 48 | // It reads embedded sync.Map firstly. If cache misses, using singleFlight to process reflection and GetParamsTypeList. 49 | func (mc *MethodCache) GetTypes(data interface{}, ma *MethodAnnotation) (string, error) { 50 | val := reflect.ValueOf(data) 51 | key := methodKey{typ: val.Type()} 52 | if ma != nil { 53 | key.anno = ma.argsAnno 54 | } 55 | typesRaw, ok := mc.typesMap.Load(key) 56 | if ok { 57 | return typesRaw.(string), nil 58 | } 59 | 60 | typesRaw, err, _ := mc.group.Do(key, func() (interface{}, error) { 61 | elem := val.Elem() 62 | numField := elem.NumField() 63 | fields := make([]*Parameter, numField) 64 | for i := 0; i < numField; i++ { 65 | fields[i] = NewParameter(elem.Field(i).Interface(), ma.GetFieldType(i)) 66 | } 67 | 68 | types, err := GetParamsTypeList(fields) 69 | if err != nil { 70 | return "", err 71 | } 72 | mc.typesMap.Store(key, types) 73 | 74 | return types, nil 75 | }) 76 | if err != nil { 77 | return "", err 78 | } 79 | 80 | return typesRaw.(string), nil 81 | } 82 | 83 | // get retrieves Types string from reflect.Type directly. 84 | // For test. 85 | func (mc *MethodCache) get(key methodKey) (string, bool) { 86 | typesRaw, ok := mc.typesMap.Load(key) 87 | if !ok { 88 | return "", false 89 | } 90 | 91 | return typesRaw.(string), true 92 | } 93 | 94 | // len returns the length of embedded sync.Map. 95 | // For test. 96 | func (mc *MethodCache) len() int { 97 | var length int 98 | mc.typesMap.Range(func(key, value interface{}) bool { 99 | length++ 100 | return true 101 | }) 102 | return length 103 | } 104 | 105 | // GetParamsTypeList is copied from dubbo-go, it should be rewritten 106 | func GetParamsTypeList(params []*Parameter) (string, error) { 107 | var ( 108 | typ string 109 | types string 110 | ) 111 | 112 | for i := range params { 113 | typ = params[i].getType() 114 | if typ == "" { 115 | return types, fmt.Errorf("cat not get arg %#v type", params[i]) 116 | } 117 | if !strings.Contains(typ, ".") { 118 | types += typ 119 | } else if strings.Index(typ, "[") == 0 { 120 | types += strings.Replace(typ, ".", "/", -1) 121 | } else { 122 | // java.util.List -> Ljava/util/List; 123 | types += "L" + strings.Replace(typ, ".", "/", -1) + ";" 124 | } 125 | } 126 | 127 | return types, nil 128 | } 129 | 130 | // Parameter is used to store information about parameters. 131 | // value stores the actual value of the parameter, and typeAnno records the type annotation added by IDL to this parameter. 132 | type Parameter struct { 133 | value interface{} 134 | typeAnno string 135 | } 136 | 137 | // NewParameter create a Parameter object. 138 | func NewParameter(value interface{}, typeAnno string) *Parameter { 139 | return &Parameter{value: value, typeAnno: typeAnno} 140 | } 141 | 142 | // getType retrieves the parameter's type either through type annotation or by reflecting on the value. 143 | func (p *Parameter) getType() string { 144 | if p == nil { 145 | return "V" 146 | } 147 | 148 | // Preferentially use the type specified in the type annotation. 149 | if ta := p.getTypeByAnno(); len(ta) > 0 { 150 | return ta 151 | } 152 | 153 | return p.getTypeByValue() 154 | } 155 | 156 | func (p *Parameter) getTypeByAnno() string { 157 | switch p.typeAnno { 158 | // When the annotation is "-", it will be skipped, 159 | // use the default parsing method without annotations. 160 | case "-": 161 | return "" 162 | case "byte": 163 | return "B" 164 | case "byte[]": 165 | return "[B" 166 | case "Byte": 167 | return "java.lang.Byte" 168 | case "Byte[]": 169 | return "[Ljava.lang.Byte;" 170 | case "short": 171 | return "S" 172 | case "short[]": 173 | return "[S" 174 | case "Short": 175 | return "java.lang.Short" 176 | case "Short[]": 177 | return "[Ljava.lang.Short;" 178 | case "int": 179 | return "I" 180 | case "int[]": 181 | return "[I" 182 | case "Integer": 183 | return "java.lang.Integer" 184 | case "Integer[]": 185 | return "[Ljava.lang.Integer;" 186 | case "long": 187 | return "J" 188 | case "long[]": 189 | return "[J" 190 | case "Long": 191 | return "java.lang.Long" 192 | case "Long[]": 193 | return "[Ljava.lang.Long;" 194 | case "float": 195 | return "F" 196 | case "float[]": 197 | return "[F" 198 | case "Float": 199 | return "java.lang.Float" 200 | case "Float[]": 201 | return "[Ljava.lang.Float;" 202 | case "double": 203 | return "D" 204 | case "double[]": 205 | return "[D" 206 | case "Double": 207 | return "java.lang.Double" 208 | case "Double[]": 209 | return "[Ljava.lang.Double;" 210 | case "boolean": 211 | return "Z" 212 | case "boolean[]": 213 | return "[Z" 214 | case "Boolean": 215 | return "java.lang.Boolean" 216 | case "Boolean[]": 217 | return "[Ljava.lang.Boolean;" 218 | case "char": 219 | return "C" 220 | case "char[]": 221 | return "[C" 222 | case "Character": 223 | return "java.lang.Character" 224 | case "Character[]": 225 | return "[Ljava.lang.Character;" 226 | case "String": 227 | return "java.lang.String" 228 | case "String[]": 229 | return "[Ljava.lang.String;" 230 | case "Object": 231 | return "java.lang.Object" 232 | case "Object[]": 233 | return "[Ljava.lang.Object;" 234 | default: 235 | if strings.HasSuffix(p.typeAnno, "[]") { 236 | return "[L" + p.typeAnno[:len(p.typeAnno)-2] + ";" 237 | } 238 | return p.typeAnno 239 | } 240 | } 241 | 242 | func (p *Parameter) getTypeByValue() string { 243 | if p.value == nil { 244 | return "V" 245 | } 246 | 247 | switch typ := p.value.(type) { 248 | // Serialized tags for base types 249 | case nil: 250 | return "V" 251 | case bool: 252 | return "java.lang.Boolean" 253 | case int8: 254 | return "java.lang.Byte" 255 | case int16: 256 | return "java.lang.Short" 257 | case int32: 258 | return "java.lang.Integer" 259 | case int64: 260 | return "java.lang.Long" 261 | case float64: 262 | return "java.lang.Double" 263 | case []byte: 264 | return "[B" 265 | case time.Time: 266 | return "java.util.Date" 267 | case *time.Time: 268 | return "java.util.Date" 269 | case []time.Time: 270 | return "[Ljava.util.Date" 271 | case string: 272 | return "java.lang.String" 273 | case []hessian.Object: 274 | return "[Ljava.lang.Object;" 275 | case map[interface{}]interface{}: 276 | // return "java.util.HashMap" 277 | return "java.util.Map" 278 | case hessian.POJOEnum: 279 | return typ.JavaClassName() 280 | // Serialized tags for complex types 281 | default: 282 | reflectTyp := reflect.TypeOf(typ) 283 | if reflect.Ptr == reflectTyp.Kind() { 284 | reflectTyp = reflect.TypeOf(reflect.ValueOf(typ).Elem()) 285 | } 286 | switch reflectTyp.Kind() { 287 | case reflect.Struct: 288 | hessianParam, ok := typ.(hessian.Param) 289 | if ok { 290 | return hessianParam.JavaParamName() 291 | } 292 | hessianPojo, ok := typ.(hessian.POJO) 293 | if ok { 294 | return hessianPojo.JavaClassName() 295 | } 296 | return "java.lang.Object" 297 | case reflect.Slice, reflect.Array: 298 | if reflectTyp.Elem().Kind() == reflect.Struct { 299 | return "[Ljava.lang.Object;" 300 | } 301 | // return "java.util.ArrayList" 302 | return "java.util.List" 303 | case reflect.Map: // Enter here, map may be map[string]int 304 | return "java.util.Map" 305 | default: 306 | return "" 307 | } 308 | } 309 | } 310 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | -------------------------------------------------------------------------------- /pkg/hessian2/response.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 CloudWeGo Authors 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one or more 5 | * contributor license agreements. See the NOTICE file distributed with 6 | * this work for additional information regarding copyright ownership. 7 | * The ASF licenses this file to You under the Apache License, Version 2.0 8 | * (the "License"); you may not use this file except in compliance with 9 | * the License. You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * 19 | * This source file has been replicated from the original dubbo-go-hessian2 20 | * project repository, and we extend our sincere appreciation to the 21 | * dubbo-go development team for their valuable contribution. 22 | */ 23 | 24 | package hessian2 25 | 26 | import ( 27 | "errors" 28 | "fmt" 29 | "reflect" 30 | ) 31 | 32 | // _Rune is an alias for rune, so that to get the correct runtime type of rune. 33 | // The runtime type of rune is int32, which is not expected. 34 | type _Rune rune 35 | 36 | var ( 37 | _varRune = _Rune(0) 38 | _typeOfRunePtr = reflect.TypeOf(&_varRune) 39 | ) 40 | 41 | var ( 42 | // reflect.PointerTo is supported only from go1.20. 43 | // So we use a dummy variable to get the pointer type. 44 | _varInt = 0 45 | _varInt8 = int8(0) 46 | _varInt16 = int16(0) 47 | _varInt32 = int32(0) 48 | _varInt64 = int64(0) 49 | _varUint = uint(0) 50 | _varUint8 = uint8(0) 51 | _varUint16 = uint16(0) 52 | _varUint32 = uint32(0) 53 | _varUint64 = uint64(0) 54 | _varFloat32 = float32(0) 55 | _varFloat64 = float64(0) 56 | ) 57 | 58 | var ( 59 | _typeOfIntPtr = reflect.TypeOf(&_varInt) 60 | _typeOfInt8Ptr = reflect.TypeOf(&_varInt8) 61 | _typeOfInt16Ptr = reflect.TypeOf(&_varInt16) 62 | _typeOfInt32Ptr = reflect.TypeOf(&_varInt32) 63 | _typeOfInt64Ptr = reflect.TypeOf(&_varInt64) 64 | _typeOfUintPtr = reflect.TypeOf(&_varUint) 65 | _typeOfUint8Ptr = reflect.TypeOf(&_varUint8) 66 | _typeOfUint16Ptr = reflect.TypeOf(&_varUint16) 67 | _typeOfUint32Ptr = reflect.TypeOf(&_varUint32) 68 | _typeOfUint64Ptr = reflect.TypeOf(&_varUint64) 69 | _typeOfFloat32Ptr = reflect.TypeOf(&_varFloat32) 70 | _typeOfFloat64Ptr = reflect.TypeOf(&_varFloat64) 71 | ) 72 | 73 | // _refHolder is used to record decode list, the address of which may change when appending more element. 74 | type _refHolder struct { 75 | // destinations 76 | destinations []reflect.Value 77 | } 78 | 79 | // add destination 80 | func (h *_refHolder) add(dest reflect.Value) { 81 | h.destinations = append(h.destinations, dest) 82 | } 83 | 84 | // the ref holder pointer type. 85 | var _refHolderPtrType = reflect.TypeOf(&_refHolder{}) 86 | 87 | // ReflectResponse reflect return value 88 | func ReflectResponse(in, out interface{}) error { 89 | if out == nil { 90 | return fmt.Errorf("@out is nil") 91 | } 92 | if reflect.TypeOf(out).Kind() != reflect.Ptr { 93 | return fmt.Errorf("@out should be a pointer") 94 | } 95 | 96 | inValue := ensurePackValue(in) 97 | outValue := ensurePackValue(out) 98 | 99 | if !inValue.IsValid() { 100 | outElem := outValue.Elem() 101 | switch outElem.Type().Kind() { 102 | case reflect.Slice, reflect.Array, reflect.Map, reflect.Ptr: 103 | return nil 104 | default: 105 | return fmt.Errorf("@in is nil") 106 | } 107 | } 108 | 109 | outType := outValue.Type().String() 110 | if outType == "interface {}" || outType == "*interface {}" { 111 | setValue(outValue, inValue) 112 | return nil 113 | } 114 | 115 | switch inValue.Type().Kind() { 116 | case reflect.Slice, reflect.Array: 117 | return copySlice(inValue, outValue) 118 | case reflect.Map: 119 | return copyMap(inValue, outValue) 120 | default: 121 | setValue(outValue, inValue) 122 | } 123 | 124 | return nil 125 | } 126 | 127 | // setValue set the value to dest. 128 | // It will auto check the Ptr pack level and unpack/pack to the right level. 129 | // It makes sure success to set value 130 | func setValue(dest, v reflect.Value) { 131 | // zero value not need to set 132 | if !v.IsValid() { 133 | return 134 | } 135 | 136 | vType := v.Type() 137 | // get the real value and real type 138 | if vType.String() == "interface {}" { 139 | realIntf := v.Interface() 140 | v = reflect.ValueOf(realIntf) 141 | vType = v.Type() 142 | } 143 | destType := dest.Type() 144 | 145 | // for most cases, the types are the same and can set the value directly. 146 | if dest.CanSet() && destType == vType { 147 | dest.Set(v) 148 | return 149 | } 150 | 151 | // check whether the v is a ref holder 152 | if vType == _refHolderPtrType { 153 | h := v.Interface().(*_refHolder) 154 | h.add(dest) 155 | return 156 | } 157 | 158 | vRawType, vPtrDepth := unpackType(vType) 159 | 160 | // unpack to the root addressable value, so that to set the value. 161 | dest = unpackToRootAddressableValue(dest) 162 | destType = dest.Type() 163 | destRawType, destPtrDepth := unpackType(destType) 164 | 165 | // it can set the value directly if the raw types are of the same type. 166 | if destRawType == vRawType { 167 | if destPtrDepth > vPtrDepth { 168 | // pack to the same level of dest 169 | for i := 0; i < destPtrDepth-vPtrDepth; i++ { 170 | v = packPtr(v) 171 | } 172 | } else if destPtrDepth < vPtrDepth { 173 | // unpack to the same level of dest 174 | for i := 0; i < vPtrDepth-destPtrDepth; i++ { 175 | v = v.Elem() 176 | } 177 | } 178 | 179 | dest.Set(v) 180 | 181 | return 182 | } 183 | 184 | if vRawType.String() == "interface {}" { 185 | v = v.Elem() 186 | } 187 | switch destType.Kind() { 188 | case reflect.Float32, reflect.Float64: 189 | dest.SetFloat(v.Float()) 190 | return 191 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 192 | dest.SetInt(v.Int()) 193 | return 194 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 195 | // hessian only support 64-bit signed long integer. 196 | dest.SetUint(uint64(v.Int())) 197 | return 198 | case reflect.Ptr: 199 | setValueToPtrDest(dest, v) 200 | return 201 | case reflect.Bool: 202 | dest.SetBool(v.Bool()) 203 | default: 204 | // It's ok when the dest is an interface{}, while the v is a pointer. 205 | dest.Set(v) 206 | } 207 | } 208 | 209 | // copySlice copy from inSlice to outSlice 210 | func copySlice(inSlice, outSlice reflect.Value) error { 211 | if inSlice.IsNil() { 212 | return errors.New("@in is nil") 213 | } 214 | if inSlice.Kind() != reflect.Slice { 215 | return fmt.Errorf("@in is not slice, but %v", inSlice.Kind()) 216 | } 217 | 218 | for outSlice.Kind() == reflect.Ptr { 219 | outSlice = outSlice.Elem() 220 | } 221 | 222 | size := inSlice.Len() 223 | outSlice.Set(reflect.MakeSlice(outSlice.Type(), size, size)) 224 | 225 | for i := 0; i < size; i++ { 226 | inSliceValue := inSlice.Index(i) 227 | outSliceValue := reflect.New(outSlice.Index(i).Type()).Elem() 228 | setValue(outSliceValue, inSliceValue) 229 | outSlice.Index(i).Set(outSliceValue) 230 | } 231 | 232 | return nil 233 | } 234 | 235 | // copyMap copy from in map to out map 236 | func copyMap(inMapValue, outMapValue reflect.Value) error { 237 | if inMapValue.IsNil() { 238 | return errors.New("@in is nil") 239 | } 240 | if !inMapValue.CanInterface() { 241 | return errors.New("@in's Interface can not be used") 242 | } 243 | if inMapValue.Kind() != reflect.Map { 244 | return fmt.Errorf("@in is not map, but %v", inMapValue.Kind()) 245 | } 246 | 247 | outMapType := unpackPtrType(outMapValue.Type()) 248 | setValue(outMapValue, reflect.MakeMap(outMapType)) 249 | 250 | outKeyType := outMapType.Key() 251 | 252 | outMapValue = unpackPtrValue(outMapValue) 253 | outValueType := outMapValue.Type().Elem() 254 | 255 | for _, inKey := range inMapValue.MapKeys() { 256 | inValue := inMapValue.MapIndex(inKey) 257 | outKey := reflect.New(outKeyType).Elem() 258 | setValue(outKey, inKey) 259 | outValue := reflect.New(outValueType).Elem() 260 | setValue(outValue, inValue) 261 | 262 | outMapValue.SetMapIndex(outKey, outValue) 263 | } 264 | 265 | return nil 266 | } 267 | 268 | // unpackPtrType unpack pointer type to original type 269 | func unpackPtrType(typ reflect.Type) reflect.Type { 270 | for typ.Kind() == reflect.Ptr { 271 | typ = typ.Elem() 272 | } 273 | return typ 274 | } 275 | 276 | // unpackPtrValue unpack pointer value to original value 277 | // return the pointer if its elem is zero value, because lots of operations on zero value is invalid 278 | func unpackPtrValue(v reflect.Value) reflect.Value { 279 | for v.Kind() == reflect.Ptr && v.Elem().IsValid() { 280 | v = v.Elem() 281 | } 282 | return v 283 | } 284 | 285 | // ensurePackValue pack the interface with value 286 | func ensurePackValue(in interface{}) reflect.Value { 287 | if v, ok := in.(reflect.Value); ok { 288 | return v 289 | } 290 | return reflect.ValueOf(in) 291 | } 292 | 293 | // unpackType unpack pointer type to original type and return the pointer depth. 294 | func unpackType(typ reflect.Type) (reflect.Type, int) { 295 | depth := 0 296 | for typ.Kind() == reflect.Ptr { 297 | typ = typ.Elem() 298 | depth++ 299 | } 300 | return typ, depth 301 | } 302 | 303 | // unpackToRootAddressableValue unpack pointer value to the root addressable value. 304 | func unpackToRootAddressableValue(v reflect.Value) reflect.Value { 305 | for v.Kind() == reflect.Ptr && v.Elem().CanAddr() { 306 | v = v.Elem() 307 | } 308 | return v 309 | } 310 | 311 | // packPtr pack a Ptr value 312 | func packPtr(v reflect.Value) reflect.Value { 313 | vv := reflect.New(v.Type()) 314 | vv.Elem().Set(v) 315 | return vv 316 | } 317 | 318 | // setValueToPtrDest set the raw value to a pointer dest. 319 | func setValueToPtrDest(dest, v reflect.Value) { 320 | // for number, the type of value may be different with the dest, 321 | // must convert it to the correct type of value then set. 322 | switch dest.Type() { 323 | case _typeOfIntPtr: 324 | vv := v.Int() 325 | dest.Set(reflect.ValueOf(&vv)) 326 | return 327 | case _typeOfInt8Ptr: 328 | vv := int8(v.Int()) 329 | dest.Set(reflect.ValueOf(&vv)) 330 | return 331 | case _typeOfInt16Ptr: 332 | vv := int16(v.Int()) 333 | dest.Set(reflect.ValueOf(&vv)) 334 | return 335 | case _typeOfInt32Ptr: 336 | if v.Kind() == reflect.String { 337 | vv := rune(v.String()[0]) 338 | dest.Set(reflect.ValueOf(&vv)) 339 | return 340 | } 341 | vv := int32(v.Int()) 342 | dest.Set(reflect.ValueOf(&vv)) 343 | return 344 | case _typeOfInt64Ptr: 345 | vv := v.Int() 346 | dest.Set(reflect.ValueOf(&vv)) 347 | return 348 | case _typeOfUintPtr: 349 | vv := uint(v.Uint()) 350 | dest.Set(reflect.ValueOf(&vv)) 351 | return 352 | case _typeOfUint8Ptr: 353 | // v is a int32 here. 354 | vv := uint8(v.Int()) 355 | dest.Set(reflect.ValueOf(&vv)) 356 | return 357 | case _typeOfUint16Ptr: 358 | vv := uint16(v.Uint()) 359 | dest.Set(reflect.ValueOf(&vv)) 360 | return 361 | case _typeOfUint32Ptr: 362 | vv := uint32(v.Uint()) 363 | dest.Set(reflect.ValueOf(&vv)) 364 | return 365 | case _typeOfUint64Ptr: 366 | vv := v.Uint() 367 | dest.Set(reflect.ValueOf(&vv)) 368 | return 369 | case _typeOfFloat32Ptr: 370 | vv := float32(v.Float()) 371 | dest.Set(reflect.ValueOf(&vv)) 372 | case _typeOfFloat64Ptr: 373 | vv := v.Float() 374 | dest.Set(reflect.ValueOf(&vv)) 375 | return 376 | case _typeOfRunePtr: 377 | if v.Kind() == reflect.String { 378 | vv := _Rune(v.String()[0]) 379 | dest.Set(reflect.ValueOf(&vv)) 380 | return 381 | } 382 | vv := _Rune(v.Int()) 383 | dest.Set(reflect.ValueOf(&vv)) 384 | return 385 | default: 386 | dest.Set(v) 387 | } 388 | } 389 | -------------------------------------------------------------------------------- /pkg/hessian2/response_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 CloudWeGo Authors 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one or more 5 | * contributor license agreements. See the NOTICE file distributed with 6 | * this work for additional information regarding copyright ownership. 7 | * The ASF licenses this file to You under the Apache License, Version 2.0 8 | * (the "License"); you may not use this file except in compliance with 9 | * the License. You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package hessian2 21 | 22 | import ( 23 | "reflect" 24 | "testing" 25 | "time" 26 | 27 | "github.com/stretchr/testify/assert" 28 | ) 29 | 30 | func TestReflectResponse(t *testing.T) { 31 | t.Run("map", func(t *testing.T) { 32 | tests := []struct { 33 | desc string 34 | testFunc func(t *testing.T, expectedErr bool) 35 | expectErr bool 36 | }{ 37 | { 38 | desc: "map[bool]bool", 39 | testFunc: func(t *testing.T, expectedErr bool) { 40 | var dest map[bool]bool 41 | src := map[bool]bool{ 42 | true: true, 43 | } 44 | testReflectResponse(t, src, &dest, expectedErr) 45 | if !reflect.DeepEqual(src, dest) { 46 | t.Fatalf("src: %+v, dest: %+v, they are not equal", src, dest) 47 | } 48 | }, 49 | }, 50 | { 51 | desc: "map[bool]int8", 52 | testFunc: func(t *testing.T, expectedErr bool) { 53 | var dest map[bool]int8 54 | src := map[bool]int8{ 55 | true: 12, 56 | } 57 | testReflectResponse(t, src, &dest, expectedErr) 58 | if !reflect.DeepEqual(src, dest) { 59 | t.Fatalf("src: %+v, dest: %+v, they are not equal", src, dest) 60 | } 61 | }, 62 | }, 63 | { 64 | desc: "map[bool]int16", 65 | testFunc: func(t *testing.T, expectedErr bool) { 66 | var dest map[bool]int16 67 | src := map[bool]int16{ 68 | true: 12, 69 | } 70 | testReflectResponse(t, src, &dest, expectedErr) 71 | if !reflect.DeepEqual(src, dest) { 72 | t.Fatalf("src: %+v, dest: %+v, they are not equal", src, dest) 73 | } 74 | }, 75 | }, 76 | { 77 | desc: "map[bool]int32", 78 | testFunc: func(t *testing.T, expectedErr bool) { 79 | var dest map[bool]int32 80 | src := map[bool]int32{ 81 | true: 12, 82 | } 83 | testReflectResponse(t, src, &dest, expectedErr) 84 | if !reflect.DeepEqual(src, dest) { 85 | t.Fatalf("src: %+v, dest: %+v, they are not equal", src, dest) 86 | } 87 | }, 88 | }, 89 | { 90 | desc: "map[bool]int64", 91 | testFunc: func(t *testing.T, expectedErr bool) { 92 | var dest map[bool]int64 93 | src := map[bool]int64{ 94 | true: 12, 95 | } 96 | testReflectResponse(t, src, &dest, expectedErr) 97 | if !reflect.DeepEqual(src, dest) { 98 | t.Fatalf("src: %+v, dest: %+v, they are not equal", src, dest) 99 | } 100 | }, 101 | }, 102 | { 103 | desc: "map[bool]float64", 104 | testFunc: func(t *testing.T, expectedErr bool) { 105 | var dest map[bool]float64 106 | src := map[bool]float64{ 107 | true: 12.34, 108 | } 109 | testReflectResponse(t, src, &dest, expectedErr) 110 | if !reflect.DeepEqual(src, dest) { 111 | t.Fatalf("src: %+v, dest: %+v, they are not equal", src, dest) 112 | } 113 | }, 114 | }, 115 | { 116 | desc: "map[bool]string", 117 | testFunc: func(t *testing.T, expectedErr bool) { 118 | var dest map[bool]string 119 | src := map[bool]string{ 120 | true: "12", 121 | } 122 | testReflectResponse(t, src, &dest, expectedErr) 123 | if !reflect.DeepEqual(src, dest) { 124 | t.Fatalf("src: %+v, dest: %+v, they are not equal", src, dest) 125 | } 126 | }, 127 | }, 128 | { 129 | desc: "map[bool][]byte", 130 | testFunc: func(t *testing.T, expectedErr bool) { 131 | var dest map[bool][]byte 132 | src := map[bool][]byte{ 133 | true: { 134 | '1', 135 | '2', 136 | }, 137 | } 138 | testReflectResponse(t, src, &dest, expectedErr) 139 | if !reflect.DeepEqual(src, dest) { 140 | t.Fatalf("src: %+v, dest: %+v, they are not equal", src, dest) 141 | } 142 | }, 143 | }, 144 | { 145 | desc: "map[bool][]bool", 146 | testFunc: func(t *testing.T, expectedErr bool) { 147 | var dest map[bool][]bool 148 | src := map[bool][]bool{ 149 | true: { 150 | true, 151 | true, 152 | }, 153 | } 154 | testReflectResponse(t, src, &dest, expectedErr) 155 | if !reflect.DeepEqual(src, dest) { 156 | t.Fatalf("src: %+v, dest: %+v, they are not equal", src, dest) 157 | } 158 | }, 159 | }, 160 | { 161 | desc: "map[bool][]int8", 162 | testFunc: func(t *testing.T, expectedErr bool) { 163 | var dest map[bool][]int8 164 | src := map[bool][]int8{ 165 | true: { 166 | 1, 167 | 2, 168 | }, 169 | } 170 | testReflectResponse(t, src, &dest, expectedErr) 171 | if !reflect.DeepEqual(src, dest) { 172 | t.Fatalf("src: %+v, dest: %+v, they are not equal", src, dest) 173 | } 174 | }, 175 | }, 176 | { 177 | desc: "map[bool][]int16", 178 | testFunc: func(t *testing.T, expectedErr bool) { 179 | var dest map[bool][]int16 180 | src := map[bool][]int16{ 181 | true: { 182 | 1, 183 | 2, 184 | }, 185 | } 186 | testReflectResponse(t, src, &dest, expectedErr) 187 | if !reflect.DeepEqual(src, dest) { 188 | t.Fatalf("src: %+v, dest: %+v, they are not equal", src, dest) 189 | } 190 | }, 191 | }, 192 | { 193 | desc: "map[bool][]int32", 194 | testFunc: func(t *testing.T, expectedErr bool) { 195 | var dest map[bool][]int32 196 | src := map[bool][]int32{ 197 | true: { 198 | 1, 199 | 2, 200 | }, 201 | } 202 | testReflectResponse(t, src, &dest, expectedErr) 203 | if !reflect.DeepEqual(src, dest) { 204 | t.Fatalf("src: %+v, dest: %+v, they are not equal", src, dest) 205 | } 206 | }, 207 | }, 208 | { 209 | desc: "map[bool][]int64", 210 | testFunc: func(t *testing.T, expectedErr bool) { 211 | var dest map[bool][]int64 212 | src := map[bool][]int64{ 213 | true: { 214 | 1, 215 | 2, 216 | }, 217 | } 218 | testReflectResponse(t, src, &dest, expectedErr) 219 | if !reflect.DeepEqual(src, dest) { 220 | t.Fatalf("src: %+v, dest: %+v, they are not equal", src, dest) 221 | } 222 | }, 223 | }, 224 | { 225 | desc: "map[bool][]float64", 226 | testFunc: func(t *testing.T, expectedErr bool) { 227 | var dest map[bool][]float64 228 | src := map[bool][]float64{ 229 | true: { 230 | 1.2, 231 | 3.4, 232 | }, 233 | } 234 | testReflectResponse(t, src, &dest, expectedErr) 235 | if !reflect.DeepEqual(src, dest) { 236 | t.Fatalf("src: %+v, dest: %+v, they are not equal", src, dest) 237 | } 238 | }, 239 | }, 240 | { 241 | desc: "map[bool][]string", 242 | testFunc: func(t *testing.T, expectedErr bool) { 243 | var dest map[bool][]string 244 | src := map[bool][]string{ 245 | true: { 246 | "12", 247 | "34", 248 | }, 249 | } 250 | testReflectResponse(t, src, &dest, expectedErr) 251 | if !reflect.DeepEqual(src, dest) { 252 | t.Fatalf("src: %+v, dest: %+v, they are not equal", src, dest) 253 | } 254 | }, 255 | }, 256 | { 257 | desc: "map[bool][][]byte", 258 | testFunc: func(t *testing.T, expectedErr bool) { 259 | var dest map[bool][][]byte 260 | src := map[bool][][]byte{ 261 | true: { 262 | { 263 | '1', 264 | '2', 265 | }, 266 | { 267 | '3', 268 | '4', 269 | }, 270 | }, 271 | } 272 | testReflectResponse(t, src, &dest, expectedErr) 273 | if !reflect.DeepEqual(src, dest) { 274 | t.Fatalf("src: %+v, dest: %+v, they are not equal", src, dest) 275 | } 276 | }, 277 | }, 278 | } 279 | 280 | for _, test := range tests { 281 | t.Run(test.desc, func(t *testing.T) { 282 | test.testFunc(t, test.expectErr) 283 | }) 284 | } 285 | }) 286 | t.Run("list", func(t *testing.T) { 287 | tests := []struct { 288 | desc string 289 | testFunc func(t *testing.T, expectedErr bool) 290 | expectErr bool 291 | }{ 292 | { 293 | desc: "[]bool", 294 | testFunc: func(t *testing.T, expectedErr bool) { 295 | var dest []bool 296 | src := []bool{ 297 | true, true, 298 | } 299 | testReflectResponse(t, src, &dest, expectedErr) 300 | if !reflect.DeepEqual(src, dest) { 301 | t.Fatalf("src: %+v, dest: %+v, they are not equal", src, dest) 302 | } 303 | }, 304 | }, 305 | { 306 | desc: "[]int8", 307 | testFunc: func(t *testing.T, expectedErr bool) { 308 | var dest []int8 309 | src := []int8{ 310 | 12, 34, 311 | } 312 | testReflectResponse(t, src, &dest, expectedErr) 313 | if !reflect.DeepEqual(src, dest) { 314 | t.Fatalf("src: %+v, dest: %+v, they are not equal", src, dest) 315 | } 316 | }, 317 | }, 318 | { 319 | desc: "[]int16", 320 | testFunc: func(t *testing.T, expectedErr bool) { 321 | var dest []int16 322 | src := []int16{ 323 | 12, 34, 324 | } 325 | testReflectResponse(t, src, &dest, expectedErr) 326 | if !reflect.DeepEqual(src, dest) { 327 | t.Fatalf("src: %+v, dest: %+v, they are not equal", src, dest) 328 | } 329 | }, 330 | }, 331 | { 332 | desc: "[]int32", 333 | testFunc: func(t *testing.T, expectedErr bool) { 334 | var dest []int32 335 | src := []int32{ 336 | 12, 34, 337 | } 338 | testReflectResponse(t, src, &dest, expectedErr) 339 | if !reflect.DeepEqual(src, dest) { 340 | t.Fatalf("src: %+v, dest: %+v, they are not equal", src, dest) 341 | } 342 | }, 343 | }, 344 | { 345 | desc: "[]int64", 346 | testFunc: func(t *testing.T, expectedErr bool) { 347 | var dest []int64 348 | src := []int64{ 349 | 12, 34, 350 | } 351 | testReflectResponse(t, src, &dest, expectedErr) 352 | if !reflect.DeepEqual(src, dest) { 353 | t.Fatalf("src: %+v, dest: %+v, they are not equal", src, dest) 354 | } 355 | }, 356 | }, 357 | { 358 | desc: "[]float64", 359 | testFunc: func(t *testing.T, expectedErr bool) { 360 | var dest []float64 361 | src := []float64{ 362 | 12.34, 56.78, 363 | } 364 | testReflectResponse(t, src, &dest, expectedErr) 365 | if !reflect.DeepEqual(src, dest) { 366 | t.Fatalf("src: %+v, dest: %+v, they are not equal", src, dest) 367 | } 368 | }, 369 | }, 370 | { 371 | desc: "[]string", 372 | testFunc: func(t *testing.T, expectedErr bool) { 373 | var dest []string 374 | src := []string{ 375 | "12", "34", 376 | } 377 | testReflectResponse(t, src, &dest, expectedErr) 378 | if !reflect.DeepEqual(src, dest) { 379 | t.Fatalf("src: %+v, dest: %+v, they are not equal", src, dest) 380 | } 381 | }, 382 | }, 383 | { 384 | desc: "[][]byte", 385 | testFunc: func(t *testing.T, expectedErr bool) { 386 | var dest [][]byte 387 | src := [][]byte{ 388 | {1, 2}, {3, 4}, 389 | } 390 | testReflectResponse(t, src, &dest, expectedErr) 391 | if !reflect.DeepEqual(src, dest) { 392 | t.Fatalf("src: %+v, dest: %+v, they are not equal", src, dest) 393 | } 394 | }, 395 | }, 396 | { 397 | desc: "[]time.Time", 398 | testFunc: func(t *testing.T, expectedErr bool) { 399 | var dest []time.Time 400 | src := []interface{}{ 401 | time.Unix(1000, 0), 402 | time.Unix(1001, 0), 403 | } 404 | testReflectResponse(t, src, &dest, expectedErr) 405 | assert.Equal(t, len(src), len(dest)) 406 | for i, ptr := range dest { 407 | assert.Equal(t, src[i], ptr) 408 | } 409 | }, 410 | }, 411 | { 412 | desc: "[]*time.Time", 413 | testFunc: func(t *testing.T, expectedErr bool) { 414 | var dest []*time.Time 415 | src := []interface{}{ 416 | time.Unix(1000, 0), 417 | time.Unix(1001, 0), 418 | } 419 | testReflectResponse(t, src, &dest, expectedErr) 420 | assert.Equal(t, len(src), len(dest)) 421 | for i, ptr := range dest { 422 | assert.Equal(t, src[i], *ptr) 423 | } 424 | }, 425 | }, 426 | } 427 | 428 | for _, test := range tests { 429 | t.Run(test.desc, func(t *testing.T) { 430 | test.testFunc(t, test.expectErr) 431 | }) 432 | } 433 | }) 434 | } 435 | 436 | func testReflectResponse(t *testing.T, src, dest interface{}, expectErr bool) { 437 | if err := ReflectResponse(src, dest); err != nil { 438 | if !expectErr { 439 | t.Fatal(err) 440 | } 441 | return 442 | } 443 | } 444 | -------------------------------------------------------------------------------- /pkg/codec.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 CloudWeGo Authors 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one or more 5 | * contributor license agreements. See the NOTICE file distributed with 6 | * this work for additional information regarding copyright ownership. 7 | * The ASF licenses this file to You under the Apache License, Version 2.0 8 | * (the "License"); you may not use this file except in compliance with 9 | * the License. You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package dubbo 21 | 22 | import ( 23 | "context" 24 | "fmt" 25 | 26 | hessian2_exception "github.com/kitex-contrib/codec-dubbo/pkg/hessian2/exception" 27 | 28 | "github.com/bytedance/gopkg/cloud/metainfo" 29 | "github.com/cloudwego/kitex/pkg/remote" 30 | "github.com/cloudwego/kitex/pkg/remote/codec" 31 | "github.com/kitex-contrib/codec-dubbo/pkg/dubbo_spec" 32 | "github.com/kitex-contrib/codec-dubbo/pkg/hessian2" 33 | "github.com/kitex-contrib/codec-dubbo/pkg/iface" 34 | "github.com/kitex-contrib/codec-dubbo/registries" 35 | ) 36 | 37 | var _ remote.Codec = (*DubboCodec)(nil) 38 | 39 | // DubboCodec NewDubboCodec creates the dubbo codec. 40 | type DubboCodec struct { 41 | opt *Options 42 | methodCache hessian2.MethodCache 43 | } 44 | 45 | // NewDubboCodec creates a new codec instance. 46 | func NewDubboCodec(opts ...Option) *DubboCodec { 47 | o := newOptions(opts) 48 | return &DubboCodec{opt: o} 49 | } 50 | 51 | // Name codec name 52 | func (m *DubboCodec) Name() string { 53 | return "dubbo" 54 | } 55 | 56 | // Marshal encode method 57 | func (m *DubboCodec) Encode(ctx context.Context, message remote.Message, out remote.ByteBuffer) error { 58 | var payload []byte 59 | var err error 60 | var status dubbo_spec.StatusCode 61 | // indicate whether this pkg is event 62 | var eventFlag bool 63 | msgType := message.MessageType() 64 | switch msgType { 65 | case remote.Call, remote.Oneway: 66 | payload, err = m.encodeRequestPayload(ctx, message) 67 | case remote.Exception: 68 | payload, err = m.encodeExceptionPayload(ctx, message) 69 | // todo(DMwangnima): refer to exception processing logic of dubbo-java, use status to determine if this exception 70 | // is in outside layer.(eg. non-exist InterfaceName) 71 | // for now, use StatusOK by default, regardless of whether it is in outside layer. 72 | status = dubbo_spec.StatusOK 73 | case remote.Reply: 74 | payload, err = m.encodeResponsePayload(ctx, message) 75 | status = dubbo_spec.StatusOK 76 | case remote.Heartbeat: 77 | payload, err = m.encodeHeartbeatPayload(ctx, message) 78 | status = dubbo_spec.StatusOK 79 | eventFlag = true 80 | default: 81 | return fmt.Errorf("unsupported MessageType: %v", msgType) 82 | } 83 | 84 | if err != nil { 85 | return err 86 | } 87 | 88 | header := m.buildDubboHeader(message, status, len(payload), eventFlag) 89 | 90 | // write header 91 | if err := header.Encode(out); err != nil { 92 | return err 93 | } 94 | 95 | // write payload 96 | if _, err := out.WriteBinary(payload); err != nil { 97 | return err 98 | } 99 | return nil 100 | } 101 | 102 | func (m *DubboCodec) encodeRequestPayload(ctx context.Context, message remote.Message) (buf []byte, err error) { 103 | encoder := hessian2.NewEncoder() 104 | 105 | service := &dubbo_spec.Service{ 106 | ProtocolVersion: dubbo_spec.DEFAULT_DUBBO_PROTOCOL_VERSION, 107 | Path: m.opt.JavaClassName, 108 | Version: message.RPCInfo().To().DefaultTag(registries.DubboServiceVersionKey, ""), 109 | Method: message.RPCInfo().Invocation().MethodName(), 110 | Timeout: message.RPCInfo().Config().RPCTimeout(), 111 | Group: message.RPCInfo().To().DefaultTag(registries.DubboServiceGroupKey, ""), 112 | TransInfo: message.TransInfo(), 113 | } 114 | methodAnno := m.getMethodAnnotation(message) 115 | 116 | // if a method name annotation exists, update the method name to the annotation value. 117 | if methodName, exists := methodAnno.GetMethodName(); exists { 118 | service.Method = methodName 119 | } 120 | 121 | if err = m.messageServiceInfo(ctx, service, encoder); err != nil { 122 | return nil, err 123 | } 124 | 125 | if err = m.messageData(message, methodAnno, encoder); err != nil { 126 | return nil, err 127 | } 128 | 129 | if err = m.messageAttachment(ctx, service, encoder); err != nil { 130 | return nil, err 131 | } 132 | 133 | return encoder.Buffer(), nil 134 | } 135 | 136 | func (m *DubboCodec) encodeResponsePayload(ctx context.Context, message remote.Message) (buf []byte, err error) { 137 | encoder := hessian2.NewEncoder() 138 | var payloadType dubbo_spec.PayloadType 139 | if len(message.Tags()) != 0 { 140 | payloadType = dubbo_spec.RESPONSE_VALUE_WITH_ATTACHMENTS 141 | } else { 142 | payloadType = dubbo_spec.RESPONSE_VALUE 143 | } 144 | 145 | if err := encoder.Encode(payloadType); err != nil { 146 | return nil, err 147 | } 148 | 149 | // encode data 150 | data, ok := message.Data().(iface.Message) 151 | if !ok { 152 | return nil, fmt.Errorf("invalid data: not hessian2.MessageWriter") 153 | } 154 | 155 | if err := data.Encode(encoder); err != nil { 156 | return nil, err 157 | } 158 | 159 | // encode attachments if needed 160 | if dubbo_spec.IsAttachmentsPayloadType(payloadType) { 161 | if err := encoder.Encode(message.Tags()); err != nil { 162 | return nil, err 163 | } 164 | } 165 | 166 | return encoder.Buffer(), nil 167 | } 168 | 169 | func (m *DubboCodec) encodeExceptionPayload(ctx context.Context, message remote.Message) (buf []byte, err error) { 170 | encoder := hessian2.NewEncoder() 171 | var payloadType dubbo_spec.PayloadType 172 | if len(message.Tags()) != 0 { 173 | payloadType = dubbo_spec.RESPONSE_WITH_EXCEPTION_WITH_ATTACHMENTS 174 | } else { 175 | payloadType = dubbo_spec.RESPONSE_WITH_EXCEPTION 176 | } 177 | 178 | if err := encoder.Encode(payloadType); err != nil { 179 | return nil, err 180 | } 181 | 182 | // encode exception 183 | data := message.Data() 184 | errRaw, ok := data.(error) 185 | if !ok { 186 | return nil, fmt.Errorf("%v exception does not implement Error", data) 187 | } 188 | // exception is wrapped by kerrors.DetailedError 189 | if exception, ok := hessian2_exception.FromError(errRaw); ok { 190 | if err := encoder.Encode(exception); err != nil { 191 | return nil, err 192 | } 193 | } else { 194 | if err := encoder.Encode(hessian2_exception.NewException(errRaw.Error())); err != nil { 195 | return nil, err 196 | } 197 | } 198 | 199 | if dubbo_spec.IsAttachmentsPayloadType(payloadType) { 200 | if err := encoder.Encode(message.Tags()); err != nil { 201 | return nil, err 202 | } 203 | } 204 | 205 | return encoder.Buffer(), nil 206 | } 207 | 208 | // Event Flag set in dubbo header and 'N' body determines that this pkg is heartbeat. 209 | // For dubbo-go, it does not decode the body of the pkg when Event Flag is set in dubbo header. 210 | // For dubbo-java, it reads the body of the pkg and use this statement to judge when Event Flag is set in dubbo header. 211 | // Arrays.equals(payload, getNullBytesOf(getSerializationById(proto))) 212 | // For hessian2, NullByte is 'N'. 213 | // As a result, we need to encode nil in heartbeat response body for both dubbo-go side and dubbo-java side. 214 | func (m *DubboCodec) encodeHeartbeatPayload(ctx context.Context, message remote.Message) (buf []byte, err error) { 215 | encoder := hessian2.NewEncoder() 216 | 217 | if err := encoder.Encode(nil); err != nil { 218 | return nil, err 219 | } 220 | 221 | return encoder.Buffer(), nil 222 | } 223 | 224 | func (m *DubboCodec) buildDubboHeader(message remote.Message, status dubbo_spec.StatusCode, size int, eventFlag bool) *dubbo_spec.DubboHeader { 225 | msgType := message.MessageType() 226 | return &dubbo_spec.DubboHeader{ 227 | IsRequest: msgType == remote.Call || msgType == remote.Oneway, 228 | IsEvent: eventFlag, 229 | IsOneWay: msgType == remote.Oneway, 230 | SerializationID: dubbo_spec.SERIALIZATION_ID_HESSIAN, 231 | Status: status, 232 | RequestID: uint64(message.RPCInfo().Invocation().SeqID()), 233 | DataLength: uint32(size), 234 | } 235 | } 236 | 237 | func (m *DubboCodec) messageData(message remote.Message, methodAnno *hessian2.MethodAnnotation, e iface.Encoder) error { 238 | data, ok := message.Data().(iface.Message) 239 | if !ok { 240 | return fmt.Errorf("invalid data: not hessian2.MessageWriter") 241 | } 242 | 243 | types, err := m.methodCache.GetTypes(data, methodAnno) 244 | if err != nil { 245 | return err 246 | } 247 | if err := e.Encode(types); err != nil { 248 | return err 249 | } 250 | return data.Encode(e) 251 | } 252 | 253 | func (m *DubboCodec) messageServiceInfo(ctx context.Context, service *dubbo_spec.Service, e iface.Encoder) error { 254 | if err := e.Encode(service.ProtocolVersion); err != nil { 255 | return err 256 | } 257 | if err := e.Encode(service.Path); err != nil { 258 | return err 259 | } 260 | if err := e.Encode(service.Version); err != nil { 261 | return err 262 | } 263 | if err := e.Encode(service.Method); err != nil { 264 | return err 265 | } 266 | return nil 267 | } 268 | 269 | func (m *DubboCodec) messageAttachment(ctx context.Context, service *dubbo_spec.Service, e iface.Encoder) error { 270 | attachment := dubbo_spec.NewAttachment( 271 | service.Path, 272 | service.Group, 273 | service.Path, 274 | service.Version, 275 | service.Timeout, 276 | service.TransInfo, 277 | ) 278 | return e.Encode(attachment) 279 | } 280 | 281 | func (m *DubboCodec) getMethodAnnotation(message remote.Message) *hessian2.MethodAnnotation { 282 | methodKey := message.ServiceInfo().ServiceName + "." + message.RPCInfo().To().Method() 283 | if m.opt.MethodAnnotations != nil { 284 | if t, ok := m.opt.MethodAnnotations[methodKey]; ok { 285 | return t 286 | } 287 | } 288 | return nil 289 | } 290 | 291 | // Unmarshal decode method 292 | func (m *DubboCodec) Decode(ctx context.Context, message remote.Message, in remote.ByteBuffer) error { 293 | // parse header part 294 | header := new(dubbo_spec.DubboHeader) 295 | if err := header.Decode(in); err != nil { 296 | return err 297 | } 298 | if err := codec.SetOrCheckSeqID(int32(header.RequestID), message); err != nil { 299 | return err 300 | } 301 | 302 | // parse body part 303 | if header.IsRequest { 304 | // heartbeat package 305 | if header.IsEvent { 306 | return m.decodeEventBody(ctx, header, message, in) 307 | } 308 | return m.decodeRequestBody(ctx, header, message, in) 309 | } 310 | 311 | if header.Status != dubbo_spec.StatusOK { 312 | return m.decodeExceptionBody(ctx, header, message, in) 313 | } 314 | return m.decodeResponseBody(ctx, header, message, in) 315 | } 316 | 317 | func (m *DubboCodec) decodeEventBody(ctx context.Context, header *dubbo_spec.DubboHeader, message remote.Message, in remote.ByteBuffer) error { 318 | body, err := readBody(header, in) 319 | if err != nil { 320 | return err 321 | } 322 | 323 | // entire body equals to BC_NULL determines that this request is a heartbeat 324 | if len(body) == 1 && body[0] == hessian2.NULL { 325 | message.SetMessageType(remote.Heartbeat) 326 | } 327 | // todo(DMwangnima): there are other events(READONLY_EVENT, WRITABLE_EVENT) in dubbo-java that we are planning to implement currently 328 | 329 | return nil 330 | } 331 | 332 | func (m *DubboCodec) decodeRequestBody(ctx context.Context, header *dubbo_spec.DubboHeader, message remote.Message, in remote.ByteBuffer) error { 333 | body, err := readBody(header, in) 334 | if err != nil { 335 | return err 336 | } 337 | 338 | decoder := hessian2.NewDecoder(body) 339 | service := new(dubbo_spec.Service) 340 | if err := service.Decode(decoder); err != nil { 341 | return err 342 | } 343 | 344 | if name := m.opt.JavaClassName; service.Path != name { 345 | return fmt.Errorf("dubbo requested Path: %s, kitex service specified JavaClassName: %s", service.Path, name) 346 | } 347 | 348 | // decode payload 349 | if types, err := decoder.Decode(); err != nil { 350 | return err 351 | } else if method, exists := m.opt.MethodNames[service.Method+types.(string)]; exists { 352 | service.Method = method 353 | } 354 | if err := codec.NewDataIfNeeded(service.Method, message); err != nil { 355 | return err 356 | } 357 | arg, ok := message.Data().(iface.Message) 358 | if !ok { 359 | return fmt.Errorf("invalid data: not hessian2.MessageReader") 360 | } 361 | if err := arg.Decode(decoder); err != nil { 362 | return err 363 | } 364 | if err := codec.SetOrCheckMethodName(service.Method, message); err != nil { 365 | return err 366 | } 367 | 368 | if err := processAttachments(decoder, message); err != nil { 369 | return err 370 | } 371 | 372 | return nil 373 | } 374 | 375 | // decodeExceptionBody is responsible for processing exception in the outer layer which means business logic 376 | // in the remoting service has not been invoked. (eg. wrong request with non-exist InterfaceName) 377 | func (m *DubboCodec) decodeExceptionBody(ctx context.Context, header *dubbo_spec.DubboHeader, message remote.Message, in remote.ByteBuffer) error { 378 | body, err := readBody(header, in) 379 | if err != nil { 380 | return err 381 | } 382 | 383 | decoder := hessian2.NewDecoder(body) 384 | exception, err := decoder.Decode() 385 | if err != nil { 386 | return err 387 | } 388 | exceptionStr, ok := exception.(string) 389 | if !ok { 390 | return fmt.Errorf("exception %v is not of string", exception) 391 | } 392 | return fmt.Errorf("dubbo side exception: %s", exceptionStr) 393 | } 394 | 395 | func (m *DubboCodec) decodeResponseBody(ctx context.Context, header *dubbo_spec.DubboHeader, message remote.Message, in remote.ByteBuffer) error { 396 | body, err := readBody(header, in) 397 | if err != nil { 398 | return err 399 | } 400 | 401 | decoder := hessian2.NewDecoder(body) 402 | payloadType, err := dubbo_spec.DecodePayloadType(decoder) 403 | if err != nil { 404 | return err 405 | } 406 | switch payloadType { 407 | case dubbo_spec.RESPONSE_VALUE, dubbo_spec.RESPONSE_VALUE_WITH_ATTACHMENTS: 408 | msg, ok := message.Data().(iface.Message) 409 | if !ok { 410 | return fmt.Errorf("invalid data %v: not hessian2.MessageReader", msg) 411 | } 412 | if err := msg.Decode(decoder); err != nil { 413 | return err 414 | } 415 | if dubbo_spec.IsAttachmentsPayloadType(payloadType) { 416 | if err := processAttachments(decoder, message); err != nil { 417 | return err 418 | } 419 | } 420 | // business logic exception 421 | case dubbo_spec.RESPONSE_WITH_EXCEPTION, dubbo_spec.RESPONSE_WITH_EXCEPTION_WITH_ATTACHMENTS: 422 | exception, err := decoder.Decode() 423 | if err != nil { 424 | return err 425 | } 426 | if dubbo_spec.IsAttachmentsPayloadType(payloadType) { 427 | if err := processAttachments(decoder, message); err != nil { 428 | return err 429 | } 430 | } 431 | if exceptionErr, ok := exception.(error); ok { 432 | return exceptionErr 433 | } 434 | return fmt.Errorf("dubbo side exception: %v", exception) 435 | case dubbo_spec.RESPONSE_NULL_VALUE, dubbo_spec.RESPONSE_NULL_VALUE_WITH_ATTACHMENTS: 436 | if dubbo_spec.IsAttachmentsPayloadType(payloadType) { 437 | if err := processAttachments(decoder, message); err != nil { 438 | return err 439 | } 440 | } 441 | default: 442 | return fmt.Errorf("unsupported payloadType: %v", payloadType) 443 | } 444 | return nil 445 | } 446 | 447 | func processAttachments(decoder iface.Decoder, message remote.Message) error { 448 | // decode attachments 449 | attachmentsRaw, err := decoder.Decode() 450 | if err != nil { 451 | return err 452 | } 453 | 454 | if attachments, ok := attachmentsRaw.(map[interface{}]interface{}); ok { 455 | transStrMap := map[string]string{} 456 | for keyRaw, val := range attachments { 457 | if key, ok := keyRaw.(string); ok { 458 | message.Tags()[key] = val 459 | if v, ok := val.(string); ok { 460 | // The prefix needs to be added 461 | transStrMap[metainfo.PrefixTransient+key] = v 462 | } 463 | } 464 | } 465 | 466 | message.TransInfo().PutTransStrInfo(transStrMap) 467 | return nil 468 | } 469 | 470 | return fmt.Errorf("unsupported attachments: %v", attachmentsRaw) 471 | } 472 | 473 | func readBody(header *dubbo_spec.DubboHeader, in remote.ByteBuffer) ([]byte, error) { 474 | length := int(header.DataLength) 475 | return in.Next(length) 476 | } 477 | --------------------------------------------------------------------------------