From 2f41909aeb6a01ae1a0b5ebabe9a9afc1360744a Mon Sep 17 00:00:00 2001 From: Valery Piashchynski Date: Tue, 7 Sep 2021 18:12:26 +0300 Subject: Initial commit for the GRPC plugin Signed-off-by: Valery Piashchynski --- .golangci.yml | 1 + Makefile | 4 + go.mod | 15 +- go.sum | 35 ++-- plugins/grpc/codec.go | 35 ++++ plugins/grpc/codec_test.go | 79 +++++++++ plugins/grpc/plugin.go | 11 ++ plugins/grpc/protoc-gen-php-grpc/main.go | 68 ++++++++ plugins/grpc/protoc-gen-php-grpc/php/generate.go | 57 +++++++ plugins/grpc/protoc-gen-php-grpc/php/keywords.go | 139 ++++++++++++++++ plugins/grpc/protoc-gen-php-grpc/php/ns.go | 103 ++++++++++++ plugins/grpc/protoc-gen-php-grpc/php/template.go | 103 ++++++++++++ tests/plugins/grpc/grpc_plugin_test.go | 7 + tests/plugins/grpc/plugin_test.go | 178 +++++++++++++++++++++ .../testdata/import/Import/ServiceInterface.php | 32 ++++ tests/plugins/grpc/testdata/import/service.proto | 17 ++ .../plugins/grpc/testdata/import/sub/message.proto | 7 + .../Test/CustomImport/ServiceInterface.php | 32 ++++ .../grpc/testdata/import_custom/service.proto | 19 +++ .../grpc/testdata/import_custom/sub/message.proto | 14 ++ .../Test/CustomNamespace/ServiceInterface.php | 22 +++ .../grpc/testdata/php_namespace/service.proto | 15 ++ .../simple/TestSimple/SimpleServiceInterface.php | 22 +++ tests/plugins/grpc/testdata/simple/simple.proto | 13 ++ .../testdata/use_empty/Test/ServiceInterface.php | 23 +++ .../plugins/grpc/testdata/use_empty/service.proto | 10 ++ tests/plugins/grpcplug | Bin 0 -> 6024075 bytes tests/plugins/grpcplugin | Bin 0 -> 6024075 bytes 28 files changed, 1042 insertions(+), 19 deletions(-) create mode 100644 plugins/grpc/codec.go create mode 100644 plugins/grpc/codec_test.go create mode 100644 plugins/grpc/plugin.go create mode 100644 plugins/grpc/protoc-gen-php-grpc/main.go create mode 100644 plugins/grpc/protoc-gen-php-grpc/php/generate.go create mode 100644 plugins/grpc/protoc-gen-php-grpc/php/keywords.go create mode 100644 plugins/grpc/protoc-gen-php-grpc/php/ns.go create mode 100644 plugins/grpc/protoc-gen-php-grpc/php/template.go create mode 100644 tests/plugins/grpc/grpc_plugin_test.go create mode 100644 tests/plugins/grpc/plugin_test.go create mode 100644 tests/plugins/grpc/testdata/import/Import/ServiceInterface.php create mode 100644 tests/plugins/grpc/testdata/import/service.proto create mode 100644 tests/plugins/grpc/testdata/import/sub/message.proto create mode 100644 tests/plugins/grpc/testdata/import_custom/Test/CustomImport/ServiceInterface.php create mode 100644 tests/plugins/grpc/testdata/import_custom/service.proto create mode 100644 tests/plugins/grpc/testdata/import_custom/sub/message.proto create mode 100644 tests/plugins/grpc/testdata/php_namespace/Test/CustomNamespace/ServiceInterface.php create mode 100644 tests/plugins/grpc/testdata/php_namespace/service.proto create mode 100644 tests/plugins/grpc/testdata/simple/TestSimple/SimpleServiceInterface.php create mode 100644 tests/plugins/grpc/testdata/simple/simple.proto create mode 100644 tests/plugins/grpc/testdata/use_empty/Test/ServiceInterface.php create mode 100644 tests/plugins/grpc/testdata/use_empty/service.proto create mode 100755 tests/plugins/grpcplug create mode 100755 tests/plugins/grpcplugin diff --git a/.golangci.yml b/.golangci.yml index f6ead63e..f623ed70 100755 --- a/.golangci.yml +++ b/.golangci.yml @@ -91,3 +91,4 @@ issues: - noctx - gosimple - revive + - gochecknoinits diff --git a/Makefile b/Makefile index 8390e910..d0a695ec 100755 --- a/Makefile +++ b/Makefile @@ -20,8 +20,10 @@ test_coverage: go test -v -race -cover -tags=debug -coverpkg=./... -coverprofile=./coverage-ci/server_cmd.txt -covermode=atomic ./plugins/server go test -v -race -cover -tags=debug -coverpkg=./... -coverprofile=./coverage-ci/struct_jobs.txt -covermode=atomic ./plugins/jobs/job go test -v -race -cover -tags=debug -coverpkg=./... -coverprofile=./coverage-ci/pipeline_jobs.txt -covermode=atomic ./plugins/jobs/pipeline + go test -v -race -cover -tags=debug -coverpkg=./... -coverprofile=./coverage-ci/grpc_plugin.txt -covermode=atomic ./plugins/grpc go test -v -race -cover -tags=debug -coverpkg=./... -coverprofile=./coverage-ci/jobs_core.txt -covermode=atomic ./tests/plugins/jobs go test -v -race -cover -tags=debug -coverpkg=./... -coverprofile=./coverage-ci/kv_plugin.txt -covermode=atomic ./tests/plugins/kv + go test -v -race -cover -tags=debug -coverpkg=./... -coverprofile=./coverage-ci/grpc_plugin.txt -covermode=atomic ./tests/plugins/grpc go test -v -race -cover -tags=debug -coverpkg=./... -coverprofile=./coverage-ci/broadcast_plugin.txt -covermode=atomic ./tests/plugins/broadcast go test -v -race -cover -tags=debug -coverpkg=./... -coverprofile=./coverage-ci/websockets.txt -covermode=atomic ./tests/plugins/websockets go test -v -race -cover -tags=debug -coverpkg=./... -coverprofile=./coverage-ci/http.txt -covermode=atomic ./tests/plugins/http @@ -53,11 +55,13 @@ test: ## Run application tests go test -v -race -tags=debug ./plugins/http/config go test -v -race -tags=debug ./plugins/server go test -v -race -tags=debug ./plugins/jobs/job + go test -v -race -tags=debug ./tests/plugins/grpc go test -v -race -tags=debug ./tests/plugins/jobs go test -v -race -tags=debug ./tests/plugins/kv go test -v -race -tags=debug ./tests/plugins/broadcast go test -v -race -tags=debug ./tests/plugins/websockets go test -v -race -tags=debug ./plugins/websockets + go test -v -race -tags=debug ./plugins/grpc go test -v -race -tags=debug ./tests/plugins/http go test -v -race -tags=debug ./tests/plugins/informer go test -v -race -tags=debug ./tests/plugins/reload diff --git a/go.mod b/go.mod index 85421a96..97e2bdc6 100644 --- a/go.mod +++ b/go.mod @@ -6,9 +6,9 @@ require ( github.com/Shopify/toxiproxy v2.1.4+incompatible // ========= AWS SDK v2 github.com/aws/aws-sdk-go-v2 v1.9.0 - github.com/aws/aws-sdk-go-v2/config v1.7.0 + github.com/aws/aws-sdk-go-v2/config v1.8.0 github.com/aws/aws-sdk-go-v2/credentials v1.4.0 - github.com/aws/aws-sdk-go-v2/service/sqs v1.8.0 + github.com/aws/aws-sdk-go-v2/service/sqs v1.9.0 github.com/aws/smithy-go v1.8.0 // ===================== github.com/beanstalkd/go-beanstalk v0.1.0 @@ -37,9 +37,10 @@ require ( go.etcd.io/bbolt v1.3.6 go.uber.org/multierr v1.7.0 go.uber.org/zap v1.19.0 - golang.org/x/net v0.0.0-20210825183410-e898025ed96a + golang.org/x/net v0.0.0-20210903162142-ad29c8ab022f golang.org/x/sync v0.0.0-20210220032951-036812b2e83c - golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e + golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34 + google.golang.org/grpc v1.40.0 google.golang.org/protobuf v1.27.1 gopkg.in/natefinch/lumberjack.v2 v2.0.0 ) @@ -62,12 +63,12 @@ require ( github.com/hashicorp/hcl v1.0.0 // indirect github.com/magiconair/properties v1.8.5 // indirect github.com/mattn/go-colorable v0.1.8 // indirect - github.com/mattn/go-isatty v0.0.13 // indirect + github.com/mattn/go-isatty v0.0.14 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect github.com/mitchellh/mapstructure v1.4.1 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.1 // indirect - github.com/pelletier/go-toml v1.9.3 // indirect + github.com/pelletier/go-toml v1.9.4 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_model v0.2.0 // indirect github.com/prometheus/common v0.30.0 // indirect @@ -87,7 +88,7 @@ require ( go.uber.org/atomic v1.9.0 // indirect golang.org/x/text v0.3.7 // indirect golang.org/x/tools v0.1.5 // indirect - gopkg.in/ini.v1 v1.62.0 // indirect + gopkg.in/ini.v1 v1.63.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect ) diff --git a/go.sum b/go.sum index e144019b..6096347d 100644 --- a/go.sum +++ b/go.sum @@ -40,6 +40,7 @@ dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7 github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/Shopify/toxiproxy v2.1.4+incompatible h1:TKdv8HiTLgE5wdJuEML90aBgNWsokNbMijUGhmcoBJc= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA= @@ -59,8 +60,8 @@ github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmV github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/aws/aws-sdk-go-v2 v1.9.0 h1:+S+dSqQCN3MSU5vJRu1HqHrq00cJn6heIMU7X9hcsoo= github.com/aws/aws-sdk-go-v2 v1.9.0/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4= -github.com/aws/aws-sdk-go-v2/config v1.7.0 h1:J2cZ7qe+3IpqBEXnHUrFrOjoB9BlsXg7j53vxcl5IVg= -github.com/aws/aws-sdk-go-v2/config v1.7.0/go.mod h1:w9+nMZ7soXCe5nT46Ri354SNhXDQ6v+V5wqDjnZE+GY= +github.com/aws/aws-sdk-go-v2/config v1.8.0 h1:O8EMFBOl6tue5gdJJV6U3Ikyl3lqgx6WrulCYrcy2SQ= +github.com/aws/aws-sdk-go-v2/config v1.8.0/go.mod h1:w9+nMZ7soXCe5nT46Ri354SNhXDQ6v+V5wqDjnZE+GY= github.com/aws/aws-sdk-go-v2/credentials v1.4.0 h1:kmvesfjY861FzlCU9mvAfe01D9aeXcG2ZuC+k9F2YLM= github.com/aws/aws-sdk-go-v2/credentials v1.4.0/go.mod h1:dgGR+Qq7Wjcd4AOAW5Rf5Tnv3+x7ed6kETXyS9WCuAY= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.5.0 h1:OxTAgH8Y4BXHD6PGCJ8DHx2kaZPCQfSTqmDsdRZFezE= @@ -69,8 +70,8 @@ github.com/aws/aws-sdk-go-v2/internal/ini v1.2.2 h1:d95cddM3yTm4qffj3P6EnP+TzX1S github.com/aws/aws-sdk-go-v2/internal/ini v1.2.2/go.mod h1:BQV0agm+JEhqR+2RT5e1XTFIDcAAV0eW6z2trp+iduw= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.3.0 h1:VNJ5NLBteVXEwE2F1zEXVmyIH58mZ6kIQGJoC7C+vkg= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.3.0/go.mod h1:R1KK+vY8AfalhG1AOu5e35pOD2SdoPKQCFLTvnxiohk= -github.com/aws/aws-sdk-go-v2/service/sqs v1.8.0 h1:BI05Jbkaqp5IDxiobr3B59mX07lfpLJDv5NwAEx3wSs= -github.com/aws/aws-sdk-go-v2/service/sqs v1.8.0/go.mod h1:BXA1CVaEd9TBOQ8G2ke7lMWdVggAeh35+h2HDO50z7s= +github.com/aws/aws-sdk-go-v2/service/sqs v1.9.0 h1:g6EHC3RFpgbRR8/Yk6BTbzfPn+E3o6J3zWPrcjvVJTw= +github.com/aws/aws-sdk-go-v2/service/sqs v1.9.0/go.mod h1:BXA1CVaEd9TBOQ8G2ke7lMWdVggAeh35+h2HDO50z7s= github.com/aws/aws-sdk-go-v2/service/sso v1.4.0 h1:sHXMIKYS6YiLPzmKSvDpPmOpJDHxmAUgbiF49YNVztg= github.com/aws/aws-sdk-go-v2/service/sso v1.4.0/go.mod h1:+1fpWnL96DL23aXPpMGbsmKe8jLTEfbjuQoA4WS1VaA= github.com/aws/aws-sdk-go-v2/service/sts v1.7.0 h1:1at4e5P+lvHNl2nUktdM2/v+rpICg/QSEr9TO/uW9vU= @@ -92,6 +93,8 @@ github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b/go.mod h1:H0wQ github.com/cenkalti/backoff/v4 v4.1.1 h1:G2HAfAmvm/GcKan2oOQpBXOd2tT2G57ZnZGWa1PxPBQ= github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -102,6 +105,7 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -115,6 +119,7 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fasthttp/websocket v1.4.3 h1:qjhRJ/rTy4KB8oBxljEC00SDt6HUY9jLRfM601SUdS4= github.com/fasthttp/websocket v1.4.3/go.mod h1:5r4oKssgS7W6Zn6mPWap3NWzNPJNzUUh3baWTOhcYQk= @@ -280,8 +285,8 @@ github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.13 h1:qdl+GuBjcsKKDco5BsxPJlId98mSWNKqYA+Co0SC1yA= -github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= @@ -314,8 +319,9 @@ github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1y github.com/onsi/gomega v1.15.0 h1:WjP/FQ/sk43MRmnEcT+MlDw2TFvkrXlprrPST/IudjU= github.com/onsi/gomega v1.15.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pelletier/go-toml v1.9.3 h1:zeC5b1GviRUyKYd6OJPvBU/mcVDVoL1OhT17FCt5dSQ= github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/pelletier/go-toml v1.9.4 h1:tjENF6MfZAg8e4ZmZTeWaWiT2vXtsoO6+iuOjFhECwM= +github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -364,6 +370,7 @@ github.com/smartystreets/assertions v1.1.1 h1:T/YLemO5Yp7KPzS+lVtu+WsHn8yoSwTfIt github.com/smartystreets/assertions v1.1.1/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY= github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= @@ -428,6 +435,7 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= @@ -527,8 +535,8 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210825183410-e898025ed96a h1:bRuuGXV8wwSdGTB+CtJf+FjgO1APK1CoO39T4BN/XBw= -golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210903162142-ad29c8ab022f h1:w6wWR0H+nyVpbSAQbzVEIACVyr/h8l/BEkY6Sokc7Eg= +golang.org/x/net v0.0.0-20210903162142-ad29c8ab022f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -614,8 +622,8 @@ golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e h1:XMgFehsDnnLGtjvjOfqWSUzt0alpTR1RSEuznObga2c= -golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34 h1:GkvMjFtXUmahfDtashnc1mnrCtuBVcwse5QV2lUk/tI= +golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -782,6 +790,8 @@ google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAG google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.40.0 h1:AGJ0Ih4mHjSeibYkFGh1dD9KJ/eOtZ93I6hoHhukQ5Q= +google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -804,8 +814,9 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/ini.v1 v1.38.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.63.0 h1:2t0h8NA59dpVQpa5Yh8cIcR6nHAeBIEk0zlLVqfw4N4= +gopkg.in/ini.v1 v1.63.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= diff --git a/plugins/grpc/codec.go b/plugins/grpc/codec.go new file mode 100644 index 00000000..5938e238 --- /dev/null +++ b/plugins/grpc/codec.go @@ -0,0 +1,35 @@ +package grpc + +import "google.golang.org/grpc/encoding" + +type rawMessage []byte + +func (r rawMessage) Reset() {} +func (rawMessage) ProtoMessage() {} +func (rawMessage) String() string { return "rawMessage" } + +type codec struct{ base encoding.Codec } + +// Marshal returns the wire format of v. rawMessages would be returned without encoding. +func (c *codec) Marshal(v interface{}) ([]byte, error) { + if raw, ok := v.(rawMessage); ok { + return raw, nil + } + + return c.base.Marshal(v) +} + +// Unmarshal parses the wire format into v. rawMessages would not be unmarshalled. +func (c *codec) Unmarshal(data []byte, v interface{}) error { + if raw, ok := v.(*rawMessage); ok { + *raw = data + return nil + } + + return c.base.Unmarshal(data, v) +} + +// String return codec name. +func (c *codec) String() string { + return "raw:" + c.base.Name() +} diff --git a/plugins/grpc/codec_test.go b/plugins/grpc/codec_test.go new file mode 100644 index 00000000..5f94b745 --- /dev/null +++ b/plugins/grpc/codec_test.go @@ -0,0 +1,79 @@ +package grpc + +import ( + "testing" + + json "github.com/json-iterator/go" + "github.com/stretchr/testify/assert" +) + +type jsonCodec struct{} + +func (jsonCodec) Marshal(v interface{}) ([]byte, error) { + return json.Marshal(v) +} + +func (jsonCodec) Unmarshal(data []byte, v interface{}) error { + return json.Unmarshal(data, v) +} + +func (jsonCodec) Name() string { + return "json" +} + +func TestCodec_String(t *testing.T) { + c := codec{jsonCodec{}} + + assert.Equal(t, "raw:json", c.String()) + + r := rawMessage{} + r.Reset() + r.ProtoMessage() + assert.Equal(t, "rawMessage", r.String()) +} + +func TestCodec_Unmarshal_ByPass(t *testing.T) { + c := codec{jsonCodec{}} + + s := struct { + Name string + }{} + + assert.NoError(t, c.Unmarshal([]byte(`{"name":"name"}`), &s)) + assert.Equal(t, "name", s.Name) +} + +func TestCodec_Marshal_ByPass(t *testing.T) { + c := codec{jsonCodec{}} + + s := struct { + Name string + }{ + Name: "name", + } + + d, err := c.Marshal(s) + assert.NoError(t, err) + + assert.Equal(t, `{"Name":"name"}`, string(d)) +} + +func TestCodec_Unmarshal_Raw(t *testing.T) { + c := codec{jsonCodec{}} + + s := rawMessage{} + + assert.NoError(t, c.Unmarshal([]byte(`{"name":"name"}`), &s)) + assert.Equal(t, `{"name":"name"}`, string(s)) +} + +func TestCodec_Marshal_Raw(t *testing.T) { + c := codec{jsonCodec{}} + + s := rawMessage(`{"Name":"name"}`) + + d, err := c.Marshal(s) + assert.NoError(t, err) + + assert.Equal(t, `{"Name":"name"}`, string(d)) +} diff --git a/plugins/grpc/plugin.go b/plugins/grpc/plugin.go new file mode 100644 index 00000000..5da60d75 --- /dev/null +++ b/plugins/grpc/plugin.go @@ -0,0 +1,11 @@ +package grpc + +import "github.com/spiral/errors" + +type Plugin struct { +} + +func (p *Plugin) Init() error { + const op = errors.Op("grpc_plugin_init") + return nil +} diff --git a/plugins/grpc/protoc-gen-php-grpc/main.go b/plugins/grpc/protoc-gen-php-grpc/main.go new file mode 100644 index 00000000..c9c4a573 --- /dev/null +++ b/plugins/grpc/protoc-gen-php-grpc/main.go @@ -0,0 +1,68 @@ +// MIT License +// +// Copyright (c) 2018 SpiralScout +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +package main + +import ( + "io" + "io/ioutil" + "os" + + "github.com/spiral/roadrunner/v2/plugins/grpc/protoc-gen-php-grpc/php" + "google.golang.org/protobuf/proto" + plugin "google.golang.org/protobuf/types/pluginpb" +) + +func main() { + req, err := readRequest(os.Stdin) + if err != nil { + panic(err) + } + + if err = writeResponse(os.Stdout, php.Generate(req)); err != nil { + panic(err) + } +} + +func readRequest(in io.Reader) (*plugin.CodeGeneratorRequest, error) { + data, err := ioutil.ReadAll(in) + if err != nil { + return nil, err + } + + req := new(plugin.CodeGeneratorRequest) + if err = proto.Unmarshal(data, req); err != nil { + return nil, err + } + + return req, nil +} + +func writeResponse(out io.Writer, resp *plugin.CodeGeneratorResponse) error { + data, err := proto.Marshal(resp) + if err != nil { + return err + } + + _, err = out.Write(data) + return err +} diff --git a/plugins/grpc/protoc-gen-php-grpc/php/generate.go b/plugins/grpc/protoc-gen-php-grpc/php/generate.go new file mode 100644 index 00000000..03c48ac8 --- /dev/null +++ b/plugins/grpc/protoc-gen-php-grpc/php/generate.go @@ -0,0 +1,57 @@ +// MIT License +// +// Copyright (c) 2018 SpiralScout +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +package php + +import ( + desc "google.golang.org/protobuf/types/descriptorpb" + plugin "google.golang.org/protobuf/types/pluginpb" +) + +// Generate generates needed service classes +func Generate(req *plugin.CodeGeneratorRequest) *plugin.CodeGeneratorResponse { + resp := &plugin.CodeGeneratorResponse{} + + for _, file := range req.ProtoFile { + for _, service := range file.Service { + resp.File = append(resp.File, generate(req, file, service)) + } + } + + return resp +} + +func generate( + req *plugin.CodeGeneratorRequest, + file *desc.FileDescriptorProto, + service *desc.ServiceDescriptorProto, +) *plugin.CodeGeneratorResponse_File { + return &plugin.CodeGeneratorResponse_File{ + Name: str(filename(file, service.Name)), + Content: str(body(req, file, service)), + } +} + +// helper to convert string into string pointer +func str(str string) *string { + return &str +} diff --git a/plugins/grpc/protoc-gen-php-grpc/php/keywords.go b/plugins/grpc/protoc-gen-php-grpc/php/keywords.go new file mode 100644 index 00000000..32579e33 --- /dev/null +++ b/plugins/grpc/protoc-gen-php-grpc/php/keywords.go @@ -0,0 +1,139 @@ +// MIT License +// +// Copyright (c) 2018 SpiralScout +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +package php + +import ( + "bytes" + "strings" + "unicode" +) + +// @see https://github.com/protocolbuffers/protobuf/blob/master/php/ext/google/protobuf/protobuf.c#L168 +var reservedKeywords = []string{ + "abstract", "and", "array", "as", "break", + "callable", "case", "catch", "class", "clone", + "const", "continue", "declare", "default", "die", + "do", "echo", "else", "elseif", "empty", + "enddeclare", "endfor", "endforeach", "endif", "endswitch", + "endwhile", "eval", "exit", "extends", "final", + "for", "foreach", "function", "global", "goto", + "if", "implements", "include", "include_once", "instanceof", + "insteadof", "interface", "isset", "list", "namespace", + "new", "or", "print", "private", "protected", + "public", "require", "require_once", "return", "static", + "switch", "throw", "trait", "try", "unset", + "use", "var", "while", "xor", "int", + "float", "bool", "string", "true", "false", + "null", "void", "iterable", +} + +// Check if given name/keyword is reserved by php. +func isReserved(name string) bool { + name = strings.ToLower(name) + for _, k := range reservedKeywords { + if name == k { + return true + } + } + + return false +} + +// generate php namespace or path +func namespace(pkg *string, sep string) string { + if pkg == nil { + return "" + } + + result := bytes.NewBuffer(nil) + for _, p := range strings.Split(*pkg, ".") { + result.WriteString(identifier(p, "")) + result.WriteString(sep) + } + + return strings.Trim(result.String(), sep) +} + +// create php identifier for class or message +func identifier(name string, suffix string) string { + name = Camelize(name) + if suffix != "" { + return name + Camelize(suffix) + } + + return name +} + +func resolveReserved(identifier string, pkg string) string { + if isReserved(strings.ToLower(identifier)) { + if pkg == ".google.protobuf" { + return "GPB" + identifier + } + return "PB" + identifier + } + + return identifier +} + +// Camelize "dino_party" -> "DinoParty" +func Camelize(word string) string { + words := splitAtCaseChangeWithTitlecase(word) + return strings.Join(words, "") +} + +func splitAtCaseChangeWithTitlecase(s string) []string { + words := make([]string, 0) + word := make([]rune, 0) + for _, c := range s { + spacer := isSpacerChar(c) + if len(word) > 0 { + if unicode.IsUpper(c) || spacer { + words = append(words, string(word)) + word = make([]rune, 0) + } + } + if !spacer { + if len(word) > 0 { + word = append(word, unicode.ToLower(c)) + } else { + word = append(word, unicode.ToUpper(c)) + } + } + } + words = append(words, string(word)) + return words +} + +func isSpacerChar(c rune) bool { + switch { + case c == rune("_"[0]): + return true + case c == rune(" "[0]): + return true + case c == rune(":"[0]): + return true + case c == rune("-"[0]): + return true + } + return false +} diff --git a/plugins/grpc/protoc-gen-php-grpc/php/ns.go b/plugins/grpc/protoc-gen-php-grpc/php/ns.go new file mode 100644 index 00000000..c1dc3898 --- /dev/null +++ b/plugins/grpc/protoc-gen-php-grpc/php/ns.go @@ -0,0 +1,103 @@ +package php + +import ( + "bytes" + "fmt" + "strings" + + desc "google.golang.org/protobuf/types/descriptorpb" + plugin "google.golang.org/protobuf/types/pluginpb" +) + +// manages internal name representation of the package +type ns struct { + // Package defines file package. + Package string + + // Root namespace of the package + Namespace string + + // Import declares what namespaces to be imported + Import map[string]string +} + +// newNamespace creates new work namespace. +func newNamespace(req *plugin.CodeGeneratorRequest, file *desc.FileDescriptorProto, service *desc.ServiceDescriptorProto) *ns { + ns := &ns{ + Package: *file.Package, + Namespace: namespace(file.Package, "\\"), + Import: make(map[string]string), + } + + if file.Options != nil && file.Options.PhpNamespace != nil { + ns.Namespace = *file.Options.PhpNamespace + } + + for k := range service.Method { + ns.importMessage(req, service.Method[k].InputType) + ns.importMessage(req, service.Method[k].OutputType) + } + + return ns +} + +// importMessage registers new import message namespace (only the namespace). +func (ns *ns) importMessage(req *plugin.CodeGeneratorRequest, msg *string) { + if msg == nil { + return + } + + chunks := strings.Split(*msg, ".") + pkg := strings.Join(chunks[:len(chunks)-1], ".") + + result := bytes.NewBuffer(nil) + for _, p := range chunks[:len(chunks)-1] { + result.WriteString(identifier(p, "")) + result.WriteString(`\`) + } + + if pkg == "."+ns.Package { + // root package + return + } + + for _, f := range req.ProtoFile { + if pkg == "."+*f.Package { + if f.Options != nil && f.Options.PhpNamespace != nil { + // custom imported namespace + ns.Import[pkg] = *f.Options.PhpNamespace + return + } + } + } + + ns.Import[pkg] = strings.Trim(result.String(), `\`) +} + +// resolve message alias +func (ns *ns) resolve(msg *string) string { + chunks := strings.Split(*msg, ".") + pkg := strings.Join(chunks[:len(chunks)-1], ".") + + if pkg == "."+ns.Package { + // root message + return identifier(chunks[len(chunks)-1], "") + } + + for iPkg, ns := range ns.Import { + if pkg == iPkg { + // use last namespace chunk + nsChunks := strings.Split(ns, `\`) + identifier := identifier(chunks[len(chunks)-1], "") + + return fmt.Sprintf( + `%s\%s`, + nsChunks[len(nsChunks)-1], + resolveReserved(identifier, pkg), + ) + } + } + + // fully clarified name (fallback) + return "\\" + namespace(msg, "\\") +} diff --git a/plugins/grpc/protoc-gen-php-grpc/php/template.go b/plugins/grpc/protoc-gen-php-grpc/php/template.go new file mode 100644 index 00000000..e00c6fdd --- /dev/null +++ b/plugins/grpc/protoc-gen-php-grpc/php/template.go @@ -0,0 +1,103 @@ +// MIT License +// +// Copyright (c) 2018 SpiralScout +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +package php + +import ( + "bytes" + "fmt" + "strings" + "text/template" + + desc "google.golang.org/protobuf/types/descriptorpb" + plugin "google.golang.org/protobuf/types/pluginpb" +) + +const phpBody = ` 0 || err != nil { + t.Log("RUNNING: ", strings.Join(cmd.Args, " ")) + } + + if len(out) > 0 { + t.Log(string(out)) + } + + if err != nil { + t.Fatalf("protoc: %v", err) + } +} + +func Test_Simple(t *testing.T) { + workdir, _ := os.Getwd() + tmpdir, err := ioutil.TempDir("", "proto-test") + require.NoError(t, err) + + defer func() { + assert.NoError(t, os.RemoveAll(tmpdir)) + }() + + args := []string{ + "-Itestdata", + "--php-grpc_out=" + tmpdir, + "simple/simple.proto", + } + + protoc(t, args) + + assertEqualFiles( + t, + workdir+"/testdata/simple/TestSimple/SimpleServiceInterface.php", + tmpdir+"/TestSimple/SimpleServiceInterface.php", + ) +} + +func Test_PhpNamespaceOption(t *testing.T) { + workdir, _ := os.Getwd() + tmpdir, err := ioutil.TempDir("", "proto-test") + require.NoError(t, err) + + defer func() { + assert.NoError(t, os.RemoveAll(tmpdir)) + }() + + args := []string{ + "-Itestdata", + "--php-grpc_out=" + tmpdir, + "php_namespace/service.proto", + } + protoc(t, args) + + assertEqualFiles( + t, + workdir+"/testdata/php_namespace/Test/CustomNamespace/ServiceInterface.php", + tmpdir+"/Test/CustomNamespace/ServiceInterface.php", + ) +} + +func Test_UseImportedMessage(t *testing.T) { + workdir, _ := os.Getwd() + tmpdir, err := ioutil.TempDir("", "proto-test") + require.NoError(t, err) + + defer func() { + assert.NoError(t, os.RemoveAll(tmpdir)) + }() + + args := []string{ + "-Itestdata", + "--php-grpc_out=" + tmpdir, + "import/service.proto", + } + protoc(t, args) + + assertEqualFiles( + t, + workdir+"/testdata/import/Import/ServiceInterface.php", + tmpdir+"/Import/ServiceInterface.php", + ) +} + +func Test_PhpNamespaceOptionInUse(t *testing.T) { + workdir, _ := os.Getwd() + tmpdir, err := ioutil.TempDir("", "proto-test") + require.NoError(t, err) + + defer func() { + assert.NoError(t, os.RemoveAll(tmpdir)) + }() + + args := []string{ + "-Itestdata", + "--php-grpc_out=" + tmpdir, + "import_custom/service.proto", + } + protoc(t, args) + + assertEqualFiles( + t, + workdir+"/testdata/import_custom/Test/CustomImport/ServiceInterface.php", + tmpdir+"/Test/CustomImport/ServiceInterface.php", + ) +} + +func Test_UseOfGoogleEmptyMessage(t *testing.T) { + workdir, _ := os.Getwd() + tmpdir, err := ioutil.TempDir("", "proto-test") + require.NoError(t, err) + + defer func() { + assert.NoError(t, os.RemoveAll(tmpdir)) + }() + + args := []string{ + "-Itestdata", + "--php-grpc_out=" + tmpdir, + "use_empty/service.proto", + } + protoc(t, args) + + assertEqualFiles( + t, + workdir+"/testdata/use_empty/Test/ServiceInterface.php", + tmpdir+"/Test/ServiceInterface.php", + ) + + assert.NoError(t, os.RemoveAll("plugin")) +} + +func assertEqualFiles(t *testing.T, original, generated string) { + assert.FileExists(t, generated) + + originalData, err := ioutil.ReadFile(original) + if err != nil { + t.Fatal("Can't find original file for comparison") + } + + generatedData, err := ioutil.ReadFile(generated) + if err != nil { + t.Fatal("Can't find generated file for comparison") + } + + // every OS has a special boy + r := strings.NewReplacer("\r\n", "", "\n", "") + assert.Equal(t, r.Replace(string(originalData)), r.Replace(string(generatedData))) +} diff --git a/tests/plugins/grpc/testdata/import/Import/ServiceInterface.php b/tests/plugins/grpc/testdata/import/Import/ServiceInterface.php new file mode 100644 index 00000000..13e58daf --- /dev/null +++ b/tests/plugins/grpc/testdata/import/Import/ServiceInterface.php @@ -0,0 +1,32 @@ + Date: Tue, 7 Sep 2021 18:57:31 +0300 Subject: Add protoc installation to the GA Signed-off-by: Valery Piashchynski --- .github/workflows/linux.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 0224de69..b20ec4dd 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -64,6 +64,11 @@ jobs: - name: Install Go dependencies run: go mod download + - name: Install protoc + uses: arduino/setup-protoc@v1 + with: + version: '3.17.3' + - name: Run golang tests with coverage run: make test_coverage -- cgit v1.2.3 From a42beba78e183adefe30533088d6da1e15f86ace Mon Sep 17 00:00:00 2001 From: Valery Piashchynski Date: Wed, 8 Sep 2021 11:26:45 +0300 Subject: Implement Endure interfaces Signed-off-by: Valery Piashchynski --- plugins/grpc/codec.go | 4 +++- plugins/grpc/config.go | 34 +++++++++++++++++++++++++++ plugins/grpc/plugin.go | 32 +++++++++++++++++++++++-- tests/plugins/grpc/configs/.rr-grpc-init.yaml | 0 4 files changed, 67 insertions(+), 3 deletions(-) create mode 100644 plugins/grpc/config.go create mode 100644 tests/plugins/grpc/configs/.rr-grpc-init.yaml diff --git a/plugins/grpc/codec.go b/plugins/grpc/codec.go index 5938e238..dd299601 100644 --- a/plugins/grpc/codec.go +++ b/plugins/grpc/codec.go @@ -4,9 +4,11 @@ import "google.golang.org/grpc/encoding" type rawMessage []byte +const rm string = "rawMessage" + func (r rawMessage) Reset() {} func (rawMessage) ProtoMessage() {} -func (rawMessage) String() string { return "rawMessage" } +func (rawMessage) String() string { return rm } type codec struct{ base encoding.Codec } diff --git a/plugins/grpc/config.go b/plugins/grpc/config.go new file mode 100644 index 00000000..9a9f8c2c --- /dev/null +++ b/plugins/grpc/config.go @@ -0,0 +1,34 @@ +package grpc + +import ( + "time" + + "github.com/spiral/roadrunner/v2/pkg/pool" +) + +type Config struct { + Listen string `mapstructure:"listen"` + Proto string `mapstructure:"proto"` + + TLS *TLS + + grpcPool pool.Pool + MaxSendMsgSize int64 `mapstructure:"max_send_msg_size"` + MaxRecvMsgSize int64 `mapstructure:"max_recv_msg_size"` + MaxConnectionIdle time.Duration `mapstructure:"max_connection_idle"` + MaxConnectionAge time.Duration `mapstructure:"max_connection_age"` + MaxConnectionAgeGrace time.Duration `mapstructure:"max_connection_age_grace"` + MaxConcurrentStreams int64 `mapstructure:"max_concurrent_streams"` + PingTime time.Duration `mapstructure:"ping_time"` + Timeout time.Duration `mapstructure:"timeout"` +} + +type TLS struct { + Key string + Cert string + RootCA string +} + +func (c *Config) InitDefaults() { + +} diff --git a/plugins/grpc/plugin.go b/plugins/grpc/plugin.go index 5da60d75..fe88dca5 100644 --- a/plugins/grpc/plugin.go +++ b/plugins/grpc/plugin.go @@ -1,11 +1,39 @@ package grpc -import "github.com/spiral/errors" +import ( + "github.com/spiral/errors" + "github.com/spiral/roadrunner/v2/plugins/config" + "github.com/spiral/roadrunner/v2/plugins/logger" +) + +const ( + name string = "grpc" +) type Plugin struct { + cfg config.Configurer + log logger.Logger } -func (p *Plugin) Init() error { +func (p *Plugin) Init(cfg config.Configurer, log logger.Logger) error { const op = errors.Op("grpc_plugin_init") + + return nil +} + +func (p *Plugin) Serve() chan error { + const op = errors.Op("grpc_plugin_serve") + errCh := make(chan error, 1) + + return errCh +} + +func (p *Plugin) Stop() error { return nil } + +func (p *Plugin) Available() {} + +func (p *Plugin) Name() string { + return name +} \ No newline at end of file diff --git a/tests/plugins/grpc/configs/.rr-grpc-init.yaml b/tests/plugins/grpc/configs/.rr-grpc-init.yaml new file mode 100644 index 00000000..e69de29b -- cgit v1.2.3 From b72643e64e116e51a245bc9331e25c3f73175030 Mon Sep 17 00:00:00 2001 From: Valery Piashchynski Date: Wed, 8 Sep 2021 11:30:20 +0300 Subject: gofmt-ed Signed-off-by: Valery Piashchynski --- bors.toml | 4 ++-- plugins/grpc/plugin.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bors.toml b/bors.toml index 00416762..697075af 100755 --- a/bors.toml +++ b/bors.toml @@ -1,6 +1,6 @@ status = [ - 'Linux / Build (Go 1.17, PHP 7.4, OS ubuntu-20.04)', - 'Linux / Build (Go 1.17, PHP 8.0, OS ubuntu-20.04)', + 'Linux / Build (Go 1.17, PHP 7.4, OS ubuntu-latest)', + 'Linux / Build (Go 1.17, PHP 8.0, OS ubuntu-latest)', 'Linters / Golang-CI (lint) ', ] required_approvals = 0 diff --git a/plugins/grpc/plugin.go b/plugins/grpc/plugin.go index fe88dca5..4871a8a7 100644 --- a/plugins/grpc/plugin.go +++ b/plugins/grpc/plugin.go @@ -36,4 +36,4 @@ func (p *Plugin) Available() {} func (p *Plugin) Name() string { return name -} \ No newline at end of file +} -- cgit v1.2.3 From f855d878c281285bd8f468af0dba2521e4f211db Mon Sep 17 00:00:00 2001 From: Valery Piashchynski Date: Wed, 8 Sep 2021 20:55:25 +0300 Subject: Update protoc plugin, Update serverOptions, Update codec. Signed-off-by: Valery Piashchynski --- go.mod | 1 + go.sum | 1 + plugins/grpc/codec.go | 5 + plugins/grpc/config.go | 8 ++ plugins/grpc/plugin.go | 8 ++ plugins/grpc/protoc-gen-php-grpc/main.go | 68 ---------- plugins/grpc/protoc-gen-php-grpc/php/generate.go | 57 --------- plugins/grpc/protoc-gen-php-grpc/php/keywords.go | 139 --------------------- plugins/grpc/protoc-gen-php-grpc/php/ns.go | 103 --------------- plugins/grpc/protoc-gen-php-grpc/php/template.go | 103 --------------- .../protoc_plugins/protoc-gen-php-grpc/main.go | 68 ++++++++++ .../protoc-gen-php-grpc/php/generate.go | 57 +++++++++ .../protoc-gen-php-grpc/php/keywords.go | 139 +++++++++++++++++++++ .../protoc_plugins/protoc-gen-php-grpc/php/ns.go | 103 +++++++++++++++ .../protoc-gen-php-grpc/php/template.go | 103 +++++++++++++++ plugins/grpc/server.go | 119 ++++++++++++++++++ tests/plugins/grpc/configs/.rr-grpc-init.yaml | 50 ++++++++ tests/plugins/grpc/plugin_test.go | 2 +- tests/plugins/logger/logger_test.go | 24 ++-- 19 files changed, 675 insertions(+), 483 deletions(-) delete mode 100644 plugins/grpc/protoc-gen-php-grpc/main.go delete mode 100644 plugins/grpc/protoc-gen-php-grpc/php/generate.go delete mode 100644 plugins/grpc/protoc-gen-php-grpc/php/keywords.go delete mode 100644 plugins/grpc/protoc-gen-php-grpc/php/ns.go delete mode 100644 plugins/grpc/protoc-gen-php-grpc/php/template.go create mode 100644 plugins/grpc/protoc_plugins/protoc-gen-php-grpc/main.go create mode 100644 plugins/grpc/protoc_plugins/protoc-gen-php-grpc/php/generate.go create mode 100644 plugins/grpc/protoc_plugins/protoc-gen-php-grpc/php/keywords.go create mode 100644 plugins/grpc/protoc_plugins/protoc-gen-php-grpc/php/ns.go create mode 100644 plugins/grpc/protoc_plugins/protoc-gen-php-grpc/php/template.go create mode 100644 plugins/grpc/server.go diff --git a/go.mod b/go.mod index 97e2bdc6..b7141c4a 100644 --- a/go.mod +++ b/go.mod @@ -88,6 +88,7 @@ require ( go.uber.org/atomic v1.9.0 // indirect golang.org/x/text v0.3.7 // indirect golang.org/x/tools v0.1.5 // indirect + google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c // indirect gopkg.in/ini.v1 v1.63.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect diff --git a/go.sum b/go.sum index 6096347d..7dec39f0 100644 --- a/go.sum +++ b/go.sum @@ -769,6 +769,7 @@ google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c h1:wtujag7C+4D6KMoulW9YauvK2lgdvCMS260jsqqBXr0= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= diff --git a/plugins/grpc/codec.go b/plugins/grpc/codec.go index dd299601..aeb373b9 100644 --- a/plugins/grpc/codec.go +++ b/plugins/grpc/codec.go @@ -4,6 +4,7 @@ import "google.golang.org/grpc/encoding" type rawMessage []byte +const cName string = "proto" const rm string = "rawMessage" func (r rawMessage) Reset() {} @@ -31,6 +32,10 @@ func (c *codec) Unmarshal(data []byte, v interface{}) error { return c.base.Unmarshal(data, v) } +func (c *codec) Name() string { + return cName +} + // String return codec name. func (c *codec) String() string { return "raw:" + c.base.Name() diff --git a/plugins/grpc/config.go b/plugins/grpc/config.go index 9a9f8c2c..53c50e22 100644 --- a/plugins/grpc/config.go +++ b/plugins/grpc/config.go @@ -32,3 +32,11 @@ type TLS struct { func (c *Config) InitDefaults() { } + +func (c *Config) EnableTLS() bool { + if c.TLS != nil { + return (c.TLS.RootCA != "" && c.TLS.Key != "" && c.TLS.Cert != "") || (c.TLS.Key != "" && c.TLS.Cert != "") + } + + return false +} diff --git a/plugins/grpc/plugin.go b/plugins/grpc/plugin.go index 4871a8a7..b424c5d0 100644 --- a/plugins/grpc/plugin.go +++ b/plugins/grpc/plugin.go @@ -4,6 +4,8 @@ import ( "github.com/spiral/errors" "github.com/spiral/roadrunner/v2/plugins/config" "github.com/spiral/roadrunner/v2/plugins/logger" + "google.golang.org/grpc" + "google.golang.org/grpc/encoding" ) const ( @@ -11,6 +13,9 @@ const ( ) type Plugin struct { + config *Config + opts []grpc.ServerOption + cfg config.Configurer log logger.Logger } @@ -18,6 +23,9 @@ type Plugin struct { func (p *Plugin) Init(cfg config.Configurer, log logger.Logger) error { const op = errors.Op("grpc_plugin_init") + // register the codec + encoding.RegisterCodec(&codec{}) + return nil } diff --git a/plugins/grpc/protoc-gen-php-grpc/main.go b/plugins/grpc/protoc-gen-php-grpc/main.go deleted file mode 100644 index c9c4a573..00000000 --- a/plugins/grpc/protoc-gen-php-grpc/main.go +++ /dev/null @@ -1,68 +0,0 @@ -// MIT License -// -// Copyright (c) 2018 SpiralScout -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -package main - -import ( - "io" - "io/ioutil" - "os" - - "github.com/spiral/roadrunner/v2/plugins/grpc/protoc-gen-php-grpc/php" - "google.golang.org/protobuf/proto" - plugin "google.golang.org/protobuf/types/pluginpb" -) - -func main() { - req, err := readRequest(os.Stdin) - if err != nil { - panic(err) - } - - if err = writeResponse(os.Stdout, php.Generate(req)); err != nil { - panic(err) - } -} - -func readRequest(in io.Reader) (*plugin.CodeGeneratorRequest, error) { - data, err := ioutil.ReadAll(in) - if err != nil { - return nil, err - } - - req := new(plugin.CodeGeneratorRequest) - if err = proto.Unmarshal(data, req); err != nil { - return nil, err - } - - return req, nil -} - -func writeResponse(out io.Writer, resp *plugin.CodeGeneratorResponse) error { - data, err := proto.Marshal(resp) - if err != nil { - return err - } - - _, err = out.Write(data) - return err -} diff --git a/plugins/grpc/protoc-gen-php-grpc/php/generate.go b/plugins/grpc/protoc-gen-php-grpc/php/generate.go deleted file mode 100644 index 03c48ac8..00000000 --- a/plugins/grpc/protoc-gen-php-grpc/php/generate.go +++ /dev/null @@ -1,57 +0,0 @@ -// MIT License -// -// Copyright (c) 2018 SpiralScout -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -package php - -import ( - desc "google.golang.org/protobuf/types/descriptorpb" - plugin "google.golang.org/protobuf/types/pluginpb" -) - -// Generate generates needed service classes -func Generate(req *plugin.CodeGeneratorRequest) *plugin.CodeGeneratorResponse { - resp := &plugin.CodeGeneratorResponse{} - - for _, file := range req.ProtoFile { - for _, service := range file.Service { - resp.File = append(resp.File, generate(req, file, service)) - } - } - - return resp -} - -func generate( - req *plugin.CodeGeneratorRequest, - file *desc.FileDescriptorProto, - service *desc.ServiceDescriptorProto, -) *plugin.CodeGeneratorResponse_File { - return &plugin.CodeGeneratorResponse_File{ - Name: str(filename(file, service.Name)), - Content: str(body(req, file, service)), - } -} - -// helper to convert string into string pointer -func str(str string) *string { - return &str -} diff --git a/plugins/grpc/protoc-gen-php-grpc/php/keywords.go b/plugins/grpc/protoc-gen-php-grpc/php/keywords.go deleted file mode 100644 index 32579e33..00000000 --- a/plugins/grpc/protoc-gen-php-grpc/php/keywords.go +++ /dev/null @@ -1,139 +0,0 @@ -// MIT License -// -// Copyright (c) 2018 SpiralScout -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -package php - -import ( - "bytes" - "strings" - "unicode" -) - -// @see https://github.com/protocolbuffers/protobuf/blob/master/php/ext/google/protobuf/protobuf.c#L168 -var reservedKeywords = []string{ - "abstract", "and", "array", "as", "break", - "callable", "case", "catch", "class", "clone", - "const", "continue", "declare", "default", "die", - "do", "echo", "else", "elseif", "empty", - "enddeclare", "endfor", "endforeach", "endif", "endswitch", - "endwhile", "eval", "exit", "extends", "final", - "for", "foreach", "function", "global", "goto", - "if", "implements", "include", "include_once", "instanceof", - "insteadof", "interface", "isset", "list", "namespace", - "new", "or", "print", "private", "protected", - "public", "require", "require_once", "return", "static", - "switch", "throw", "trait", "try", "unset", - "use", "var", "while", "xor", "int", - "float", "bool", "string", "true", "false", - "null", "void", "iterable", -} - -// Check if given name/keyword is reserved by php. -func isReserved(name string) bool { - name = strings.ToLower(name) - for _, k := range reservedKeywords { - if name == k { - return true - } - } - - return false -} - -// generate php namespace or path -func namespace(pkg *string, sep string) string { - if pkg == nil { - return "" - } - - result := bytes.NewBuffer(nil) - for _, p := range strings.Split(*pkg, ".") { - result.WriteString(identifier(p, "")) - result.WriteString(sep) - } - - return strings.Trim(result.String(), sep) -} - -// create php identifier for class or message -func identifier(name string, suffix string) string { - name = Camelize(name) - if suffix != "" { - return name + Camelize(suffix) - } - - return name -} - -func resolveReserved(identifier string, pkg string) string { - if isReserved(strings.ToLower(identifier)) { - if pkg == ".google.protobuf" { - return "GPB" + identifier - } - return "PB" + identifier - } - - return identifier -} - -// Camelize "dino_party" -> "DinoParty" -func Camelize(word string) string { - words := splitAtCaseChangeWithTitlecase(word) - return strings.Join(words, "") -} - -func splitAtCaseChangeWithTitlecase(s string) []string { - words := make([]string, 0) - word := make([]rune, 0) - for _, c := range s { - spacer := isSpacerChar(c) - if len(word) > 0 { - if unicode.IsUpper(c) || spacer { - words = append(words, string(word)) - word = make([]rune, 0) - } - } - if !spacer { - if len(word) > 0 { - word = append(word, unicode.ToLower(c)) - } else { - word = append(word, unicode.ToUpper(c)) - } - } - } - words = append(words, string(word)) - return words -} - -func isSpacerChar(c rune) bool { - switch { - case c == rune("_"[0]): - return true - case c == rune(" "[0]): - return true - case c == rune(":"[0]): - return true - case c == rune("-"[0]): - return true - } - return false -} diff --git a/plugins/grpc/protoc-gen-php-grpc/php/ns.go b/plugins/grpc/protoc-gen-php-grpc/php/ns.go deleted file mode 100644 index c1dc3898..00000000 --- a/plugins/grpc/protoc-gen-php-grpc/php/ns.go +++ /dev/null @@ -1,103 +0,0 @@ -package php - -import ( - "bytes" - "fmt" - "strings" - - desc "google.golang.org/protobuf/types/descriptorpb" - plugin "google.golang.org/protobuf/types/pluginpb" -) - -// manages internal name representation of the package -type ns struct { - // Package defines file package. - Package string - - // Root namespace of the package - Namespace string - - // Import declares what namespaces to be imported - Import map[string]string -} - -// newNamespace creates new work namespace. -func newNamespace(req *plugin.CodeGeneratorRequest, file *desc.FileDescriptorProto, service *desc.ServiceDescriptorProto) *ns { - ns := &ns{ - Package: *file.Package, - Namespace: namespace(file.Package, "\\"), - Import: make(map[string]string), - } - - if file.Options != nil && file.Options.PhpNamespace != nil { - ns.Namespace = *file.Options.PhpNamespace - } - - for k := range service.Method { - ns.importMessage(req, service.Method[k].InputType) - ns.importMessage(req, service.Method[k].OutputType) - } - - return ns -} - -// importMessage registers new import message namespace (only the namespace). -func (ns *ns) importMessage(req *plugin.CodeGeneratorRequest, msg *string) { - if msg == nil { - return - } - - chunks := strings.Split(*msg, ".") - pkg := strings.Join(chunks[:len(chunks)-1], ".") - - result := bytes.NewBuffer(nil) - for _, p := range chunks[:len(chunks)-1] { - result.WriteString(identifier(p, "")) - result.WriteString(`\`) - } - - if pkg == "."+ns.Package { - // root package - return - } - - for _, f := range req.ProtoFile { - if pkg == "."+*f.Package { - if f.Options != nil && f.Options.PhpNamespace != nil { - // custom imported namespace - ns.Import[pkg] = *f.Options.PhpNamespace - return - } - } - } - - ns.Import[pkg] = strings.Trim(result.String(), `\`) -} - -// resolve message alias -func (ns *ns) resolve(msg *string) string { - chunks := strings.Split(*msg, ".") - pkg := strings.Join(chunks[:len(chunks)-1], ".") - - if pkg == "."+ns.Package { - // root message - return identifier(chunks[len(chunks)-1], "") - } - - for iPkg, ns := range ns.Import { - if pkg == iPkg { - // use last namespace chunk - nsChunks := strings.Split(ns, `\`) - identifier := identifier(chunks[len(chunks)-1], "") - - return fmt.Sprintf( - `%s\%s`, - nsChunks[len(nsChunks)-1], - resolveReserved(identifier, pkg), - ) - } - } - - // fully clarified name (fallback) - return "\\" + namespace(msg, "\\") -} diff --git a/plugins/grpc/protoc-gen-php-grpc/php/template.go b/plugins/grpc/protoc-gen-php-grpc/php/template.go deleted file mode 100644 index e00c6fdd..00000000 --- a/plugins/grpc/protoc-gen-php-grpc/php/template.go +++ /dev/null @@ -1,103 +0,0 @@ -// MIT License -// -// Copyright (c) 2018 SpiralScout -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -package php - -import ( - "bytes" - "fmt" - "strings" - "text/template" - - desc "google.golang.org/protobuf/types/descriptorpb" - plugin "google.golang.org/protobuf/types/pluginpb" -) - -const phpBody = ` "DinoParty" +func Camelize(word string) string { + words := splitAtCaseChangeWithTitlecase(word) + return strings.Join(words, "") +} + +func splitAtCaseChangeWithTitlecase(s string) []string { + words := make([]string, 0) + word := make([]rune, 0) + for _, c := range s { + spacer := isSpacerChar(c) + if len(word) > 0 { + if unicode.IsUpper(c) || spacer { + words = append(words, string(word)) + word = make([]rune, 0) + } + } + if !spacer { + if len(word) > 0 { + word = append(word, unicode.ToLower(c)) + } else { + word = append(word, unicode.ToUpper(c)) + } + } + } + words = append(words, string(word)) + return words +} + +func isSpacerChar(c rune) bool { + switch { + case c == rune("_"[0]): + return true + case c == rune(" "[0]): + return true + case c == rune(":"[0]): + return true + case c == rune("-"[0]): + return true + } + return false +} diff --git a/plugins/grpc/protoc_plugins/protoc-gen-php-grpc/php/ns.go b/plugins/grpc/protoc_plugins/protoc-gen-php-grpc/php/ns.go new file mode 100644 index 00000000..c1dc3898 --- /dev/null +++ b/plugins/grpc/protoc_plugins/protoc-gen-php-grpc/php/ns.go @@ -0,0 +1,103 @@ +package php + +import ( + "bytes" + "fmt" + "strings" + + desc "google.golang.org/protobuf/types/descriptorpb" + plugin "google.golang.org/protobuf/types/pluginpb" +) + +// manages internal name representation of the package +type ns struct { + // Package defines file package. + Package string + + // Root namespace of the package + Namespace string + + // Import declares what namespaces to be imported + Import map[string]string +} + +// newNamespace creates new work namespace. +func newNamespace(req *plugin.CodeGeneratorRequest, file *desc.FileDescriptorProto, service *desc.ServiceDescriptorProto) *ns { + ns := &ns{ + Package: *file.Package, + Namespace: namespace(file.Package, "\\"), + Import: make(map[string]string), + } + + if file.Options != nil && file.Options.PhpNamespace != nil { + ns.Namespace = *file.Options.PhpNamespace + } + + for k := range service.Method { + ns.importMessage(req, service.Method[k].InputType) + ns.importMessage(req, service.Method[k].OutputType) + } + + return ns +} + +// importMessage registers new import message namespace (only the namespace). +func (ns *ns) importMessage(req *plugin.CodeGeneratorRequest, msg *string) { + if msg == nil { + return + } + + chunks := strings.Split(*msg, ".") + pkg := strings.Join(chunks[:len(chunks)-1], ".") + + result := bytes.NewBuffer(nil) + for _, p := range chunks[:len(chunks)-1] { + result.WriteString(identifier(p, "")) + result.WriteString(`\`) + } + + if pkg == "."+ns.Package { + // root package + return + } + + for _, f := range req.ProtoFile { + if pkg == "."+*f.Package { + if f.Options != nil && f.Options.PhpNamespace != nil { + // custom imported namespace + ns.Import[pkg] = *f.Options.PhpNamespace + return + } + } + } + + ns.Import[pkg] = strings.Trim(result.String(), `\`) +} + +// resolve message alias +func (ns *ns) resolve(msg *string) string { + chunks := strings.Split(*msg, ".") + pkg := strings.Join(chunks[:len(chunks)-1], ".") + + if pkg == "."+ns.Package { + // root message + return identifier(chunks[len(chunks)-1], "") + } + + for iPkg, ns := range ns.Import { + if pkg == iPkg { + // use last namespace chunk + nsChunks := strings.Split(ns, `\`) + identifier := identifier(chunks[len(chunks)-1], "") + + return fmt.Sprintf( + `%s\%s`, + nsChunks[len(nsChunks)-1], + resolveReserved(identifier, pkg), + ) + } + } + + // fully clarified name (fallback) + return "\\" + namespace(msg, "\\") +} diff --git a/plugins/grpc/protoc_plugins/protoc-gen-php-grpc/php/template.go b/plugins/grpc/protoc_plugins/protoc-gen-php-grpc/php/template.go new file mode 100644 index 00000000..e00c6fdd --- /dev/null +++ b/plugins/grpc/protoc_plugins/protoc-gen-php-grpc/php/template.go @@ -0,0 +1,103 @@ +// MIT License +// +// Copyright (c) 2018 SpiralScout +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +package php + +import ( + "bytes" + "fmt" + "strings" + "text/template" + + desc "google.golang.org/protobuf/types/descriptorpb" + plugin "google.golang.org/protobuf/types/pluginpb" +) + +const phpBody = ` Date: Fri, 10 Sep 2021 00:46:20 +0300 Subject: Fix issue with attributes.Init resets existing attributes Experiment with protoregistry Signed-off-by: Valery Piashchynski --- .vscode/settings.json | 15 ++++++ Makefile | 11 +++-- plugins/grpc/server.go | 11 +++++ plugins/http/attributes/attributes.go | 9 +++- proto/jobs/v1beta/jobs.pb.go | 2 +- proto/kv/v1beta/kv.pb.go | 5 +- proto/websockets/v1beta/websockets.pb.go | 5 +- tests/env/docker-compose.yaml | 74 ++++++++++++++-------------- tests/plugins/grpcplug | Bin 6024075 -> 0 bytes tests/plugins/grpcplugin | Bin 6024075 -> 0 bytes tests/plugins/jobs/jobs_with_toxics_test.go | 12 ++++- 11 files changed, 93 insertions(+), 51 deletions(-) create mode 100644 .vscode/settings.json delete mode 100755 tests/plugins/grpcplug delete mode 100755 tests/plugins/grpcplugin diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..831e5ac8 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,15 @@ +{ + "cSpell.words": [ + "AMQP", + "extendee", + "Idxs", + "Itestdata", + "jobsv", + "mdwr", + "prefetch", + "protobuf", + "protoimpl", + "tcreds", + "tmpdir" + ] +} diff --git a/Makefile b/Makefile index d0a695ec..a2844b59 100755 --- a/Makefile +++ b/Makefile @@ -16,16 +16,16 @@ test_coverage: go test -v -race -cover -tags=debug -coverpkg=./... -coverprofile=./coverage-ci/pq.txt -covermode=atomic ./pkg/priority_queue go test -v -race -cover -tags=debug -coverpkg=./... -coverprofile=./coverage-ci/worker_stack.txt -covermode=atomic ./pkg/worker_watcher go test -v -race -cover -tags=debug -coverpkg=./... -coverprofile=./coverage-ci/ws_origin.txt -covermode=atomic ./plugins/websockets + go test -v -race -cover -tags=debug -coverpkg=./... -coverprofile=./coverage-ci/struct_jobs.txt -covermode=atomic ./plugins/jobs/job go test -v -race -cover -tags=debug -coverpkg=./... -coverprofile=./coverage-ci/http_config.txt -covermode=atomic ./plugins/http/config go test -v -race -cover -tags=debug -coverpkg=./... -coverprofile=./coverage-ci/server_cmd.txt -covermode=atomic ./plugins/server - go test -v -race -cover -tags=debug -coverpkg=./... -coverprofile=./coverage-ci/struct_jobs.txt -covermode=atomic ./plugins/jobs/job go test -v -race -cover -tags=debug -coverpkg=./... -coverprofile=./coverage-ci/pipeline_jobs.txt -covermode=atomic ./plugins/jobs/pipeline go test -v -race -cover -tags=debug -coverpkg=./... -coverprofile=./coverage-ci/grpc_plugin.txt -covermode=atomic ./plugins/grpc - go test -v -race -cover -tags=debug -coverpkg=./... -coverprofile=./coverage-ci/jobs_core.txt -covermode=atomic ./tests/plugins/jobs go test -v -race -cover -tags=debug -coverpkg=./... -coverprofile=./coverage-ci/kv_plugin.txt -covermode=atomic ./tests/plugins/kv go test -v -race -cover -tags=debug -coverpkg=./... -coverprofile=./coverage-ci/grpc_plugin.txt -covermode=atomic ./tests/plugins/grpc go test -v -race -cover -tags=debug -coverpkg=./... -coverprofile=./coverage-ci/broadcast_plugin.txt -covermode=atomic ./tests/plugins/broadcast go test -v -race -cover -tags=debug -coverpkg=./... -coverprofile=./coverage-ci/websockets.txt -covermode=atomic ./tests/plugins/websockets + go test -v -race -cover -tags=debug -coverpkg=./... -coverprofile=./coverage-ci/jobs_core.txt -covermode=atomic ./tests/plugins/jobs go test -v -race -cover -tags=debug -coverpkg=./... -coverprofile=./coverage-ci/http.txt -covermode=atomic ./tests/plugins/http go test -v -race -cover -tags=debug -coverpkg=./... -coverprofile=./coverage-ci/informer.txt -covermode=atomic ./tests/plugins/informer go test -v -race -cover -tags=debug -coverpkg=./... -coverprofile=./coverage-ci/reload.txt -covermode=atomic ./tests/plugins/reload @@ -56,10 +56,10 @@ test: ## Run application tests go test -v -race -tags=debug ./plugins/server go test -v -race -tags=debug ./plugins/jobs/job go test -v -race -tags=debug ./tests/plugins/grpc - go test -v -race -tags=debug ./tests/plugins/jobs go test -v -race -tags=debug ./tests/plugins/kv go test -v -race -tags=debug ./tests/plugins/broadcast go test -v -race -tags=debug ./tests/plugins/websockets + go test -v -race -tags=debug ./tests/plugins/jobs go test -v -race -tags=debug ./plugins/websockets go test -v -race -tags=debug ./plugins/grpc go test -v -race -tags=debug ./tests/plugins/http @@ -76,3 +76,8 @@ test: ## Run application tests go test -v -race -tags=debug ./tests/plugins/resetter go test -v -race -tags=debug ./tests/plugins/rpc docker-compose -f tests/env/docker-compose.yaml down + +generate-proto: + protoc --proto_path=./proto/jobs/v1beta --go_out=./proto/jobs/v1beta jobs.proto + protoc --proto_path=./proto/kv/v1beta --go_out=./proto/kv/v1beta kv.proto + protoc --proto_path=./proto/websockets/v1beta --go_out=./proto/websockets/v1beta websockets.proto \ No newline at end of file diff --git a/plugins/grpc/server.go b/plugins/grpc/server.go index 53b0b2ae..735d53e9 100644 --- a/plugins/grpc/server.go +++ b/plugins/grpc/server.go @@ -10,6 +10,8 @@ import ( "google.golang.org/grpc" "google.golang.org/grpc/credentials" "google.golang.org/grpc/keepalive" + "google.golang.org/grpc/reflection" + "google.golang.org/protobuf/reflect/protoregistry" ) func (p *Plugin) createGRPCserver() (*grpc.Server, error) { @@ -20,6 +22,14 @@ func (p *Plugin) createGRPCserver() (*grpc.Server, error) { } server := grpc.NewServer(opts...) + reflection.Register(server) + + fd, err := protoregistry.GlobalFiles.FindFileByPath("file") + if err != nil { + return nil, errors.E(op, err) + } + + _ = fd /* proto descriptors parser @@ -114,6 +124,7 @@ func (p *Plugin) serverOptions() ([]grpc.ServerOption, error) { return append( opts, grpc.UnaryInterceptor(p.interceptor), + // TODO(rustatian): check deprecation // grpc.CustomCodec(&codec{encoding.GetCodec(encCodec)}), ), nil } diff --git a/plugins/http/attributes/attributes.go b/plugins/http/attributes/attributes.go index 81d9f01d..5df80da4 100644 --- a/plugins/http/attributes/attributes.go +++ b/plugins/http/attributes/attributes.go @@ -37,9 +37,14 @@ func (v attrs) del(key string) { delete(v, key) } -// Init returns request with new context and attribute bag. +// Init is idempotent returns request with new context and attribute bag. func Init(r *http.Request) *http.Request { - return r.WithContext(context.WithValue(r.Context(), PsrContextKey, attrs{})) + // do not overwrite the PsrContextKey payload + if val := r.Context().Value(PsrContextKey); val == nil { + return r.WithContext(context.WithValue(r.Context(), PsrContextKey, attrs{})) + } + + return r } // All returns all context attributes. diff --git a/proto/jobs/v1beta/jobs.pb.go b/proto/jobs/v1beta/jobs.pb.go index bd8e3b43..4c237ffa 100644 --- a/proto/jobs/v1beta/jobs.pb.go +++ b/proto/jobs/v1beta/jobs.pb.go @@ -487,7 +487,7 @@ func (x *Stats) GetStats() []*Stat { return nil } -// Stats used as a response for the Stats RPC call +// Stat used as a response for the Stats RPC call type Stat struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache diff --git a/proto/kv/v1beta/kv.pb.go b/proto/kv/v1beta/kv.pb.go index 1e38fe12..19621735 100644 --- a/proto/kv/v1beta/kv.pb.go +++ b/proto/kv/v1beta/kv.pb.go @@ -7,11 +7,10 @@ package kvv1beta import ( - reflect "reflect" - sync "sync" - protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" ) const ( diff --git a/proto/websockets/v1beta/websockets.pb.go b/proto/websockets/v1beta/websockets.pb.go index b07c271e..188dcf08 100644 --- a/proto/websockets/v1beta/websockets.pb.go +++ b/proto/websockets/v1beta/websockets.pb.go @@ -7,11 +7,10 @@ package websocketsv1beta import ( - reflect "reflect" - sync "sync" - protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" ) const ( diff --git a/tests/env/docker-compose.yaml b/tests/env/docker-compose.yaml index d93bc5af..8ef2a99b 100644 --- a/tests/env/docker-compose.yaml +++ b/tests/env/docker-compose.yaml @@ -1,44 +1,44 @@ -version: '3' +version: "3" services: - memcached: - image: memcached:latest - ports: - - "127.0.0.1:11211:11211" - redis: - image: redis:6 - ports: - - "127.0.0.1:6379:6379" - redis2: - image: redis:6 - ports: - - "127.0.0.1:6378:6379" + memcached: + image: memcached:latest + ports: + - "127.0.0.1:11211:11211" + redis: + image: redis:6 + ports: + - "127.0.0.1:6379:6379" + redis2: + image: redis:6 + ports: + - "127.0.0.1:6378:6379" - toxicproxy: - image: shopify/toxiproxy - network_mode: "host" + toxicproxy: + image: ghcr.io/shopify/toxiproxy:latest + network_mode: host - beanstalk: - build: - context: . - dockerfile: Dockerfile-beanstalkd.yaml - ports: - - "127.0.0.1:11300:11300" + beanstalk: + build: + context: . + dockerfile: Dockerfile-beanstalkd.yaml + ports: + - "127.0.0.1:11300:11300" - sqs: - build: - context: . - dockerfile: Dockerfile-elastic-mq.yaml - ports: - - "127.0.0.1:9324:9324" + sqs: + build: + context: . + dockerfile: Dockerfile-elastic-mq.yaml + ports: + - "127.0.0.1:9324:9324" - rabbitmq: - image: rabbitmq:3-management - ports: - - "127.0.0.1:15672:15672" - - "127.0.0.1:5672:5672" + rabbitmq: + image: rabbitmq:3-management + ports: + - "127.0.0.1:15672:15672" + - "127.0.0.1:5672:5672" - prometheus: - image: prom/prometheus - ports: - - "9090:9090" + prometheus: + image: prom/prometheus + ports: + - "9090:9090" \ No newline at end of file diff --git a/tests/plugins/grpcplug b/tests/plugins/grpcplug deleted file mode 100755 index 21785aeb..00000000 Binary files a/tests/plugins/grpcplug and /dev/null differ diff --git a/tests/plugins/grpcplugin b/tests/plugins/grpcplugin deleted file mode 100755 index 21785aeb..00000000 Binary files a/tests/plugins/grpcplugin and /dev/null differ diff --git a/tests/plugins/jobs/jobs_with_toxics_test.go b/tests/plugins/jobs/jobs_with_toxics_test.go index 80fed8eb..84fbec48 100644 --- a/tests/plugins/jobs/jobs_with_toxics_test.go +++ b/tests/plugins/jobs/jobs_with_toxics_test.go @@ -27,10 +27,18 @@ import ( func TestDurabilityAMQP(t *testing.T) { client := toxiproxy.NewClient("127.0.0.1:8474") + proxies, err := client.Proxies() + require.NoError(t, err) + + for p := range proxies { + _ = proxies[p].Delete() + } - _, err := client.CreateProxy("redial", "127.0.0.1:23679", "127.0.0.1:5672") + proxy, err := client.CreateProxy("redial", "127.0.0.1:23679", "127.0.0.1:5672") require.NoError(t, err) - defer deleteProxy("redial", t) + defer func() { + _ = proxy.Delete() + }() cont, err := endure.NewContainer(nil, endure.SetLogLevel(endure.ErrorLevel)) require.NoError(t, err) -- cgit v1.2.3 From d9c463468985e0b5d814c54d7d1a1880ac18a177 Mon Sep 17 00:00:00 2001 From: Valery Piashchynski Date: Fri, 10 Sep 2021 13:20:11 +0300 Subject: Update .vscode Signed-off-by: Valery Piashchynski --- .vscode/settings.json | 14 ++++++++++++-- tests/env/Dockerfile-elastic-mq.yaml | 4 ++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 831e5ac8..03fa9b74 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,15 +1,25 @@ { "cSpell.words": [ + "addrs", "AMQP", "extendee", + "Goridge", "Idxs", "Itestdata", "jobsv", "mdwr", + "Nyholm", "prefetch", "protobuf", + "protoc", "protoimpl", + "stretchr", "tcreds", - "tmpdir" - ] + "tmpdir", + "websockets", + "websocketsv" + ], + "files.associations": { + "Dockerfile-*.yaml": "dockerfile" + } } diff --git a/tests/env/Dockerfile-elastic-mq.yaml b/tests/env/Dockerfile-elastic-mq.yaml index e1513450..75d8a8ff 100644 --- a/tests/env/Dockerfile-elastic-mq.yaml +++ b/tests/env/Dockerfile-elastic-mq.yaml @@ -1,8 +1,8 @@ FROM openjdk:16 -ADD https://s3-eu-west-1.amazonaws.com/softwaremill-public/elasticmq-server-1.2.0.jar / +ADD https://s3-eu-west-1.amazonaws.com/softwaremill-public/elasticmq-server-1.2.1.jar / COPY custom.conf / -ENTRYPOINT ["java", "-Dconfig.file=custom.conf", "-jar", "/elasticmq-server-1.2.0.jar"] +ENTRYPOINT ["java", "-Dconfig.file=custom.conf", "-jar", "/elasticmq-server-1.2.1.jar"] EXPOSE 9324 -- cgit v1.2.3 From fc67803c6860942e6ac88907e11c94431ccce86a Mon Sep 17 00:00:00 2001 From: Valery Piashchynski Date: Tue, 14 Sep 2021 18:00:00 +0300 Subject: Add parser, codec, proxy Refactor old code, update proto libs Signed-off-by: Valery Piashchynski --- .vscode/settings.json | 2 + Makefile | 15 +- go.mod | 8 +- go.sum | 14 +- plugins/grpc/codec.go | 42 ----- plugins/grpc/codec/codec.go | 42 +++++ plugins/grpc/codec/codec_test.go | 79 +++++++++ plugins/grpc/codec_test.go | 79 --------- plugins/grpc/config.go | 2 +- plugins/grpc/parser/message.proto | 7 + plugins/grpc/parser/parse.go | 114 ++++++++++++ plugins/grpc/parser/parse_test.go | 71 ++++++++ plugins/grpc/parser/pong.proto | 10 ++ plugins/grpc/parser/test.proto | 20 +++ plugins/grpc/parser/test_import.proto | 12 ++ plugins/grpc/parser/test_nested/message.proto | 7 + plugins/grpc/parser/test_nested/pong.proto | 10 ++ plugins/grpc/parser/test_nested/test_import.proto | 12 ++ plugins/grpc/plugin.go | 10 +- plugins/grpc/proxy/proxy.go | 203 ++++++++++++++++++++++ plugins/grpc/proxy/proxy_test.go | 134 ++++++++++++++ plugins/grpc/server.go | 34 ++-- 22 files changed, 776 insertions(+), 151 deletions(-) delete mode 100644 plugins/grpc/codec.go create mode 100644 plugins/grpc/codec/codec.go create mode 100644 plugins/grpc/codec/codec_test.go delete mode 100644 plugins/grpc/codec_test.go create mode 100644 plugins/grpc/parser/message.proto create mode 100644 plugins/grpc/parser/parse.go create mode 100644 plugins/grpc/parser/parse_test.go create mode 100644 plugins/grpc/parser/pong.proto create mode 100644 plugins/grpc/parser/test.proto create mode 100644 plugins/grpc/parser/test_import.proto create mode 100644 plugins/grpc/parser/test_nested/message.proto create mode 100644 plugins/grpc/parser/test_nested/pong.proto create mode 100644 plugins/grpc/parser/test_nested/test_import.proto create mode 100644 plugins/grpc/proxy/proxy.go create mode 100644 plugins/grpc/proxy/proxy_test.go diff --git a/.vscode/settings.json b/.vscode/settings.json index 44b782ad..50bc942d 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,8 +3,10 @@ "addrs", "amqp", "amqpjobs", + "anypb", "boltdb", "codecov", + "Conv", "golangci", "gomemcache", "goridge", diff --git a/Makefile b/Makefile index 40b0a6de..109c963e 100644 --- a/Makefile +++ b/Makefile @@ -17,11 +17,15 @@ test_coverage: go test -v -race -cover -tags=debug -coverpkg=./... -coverprofile=./coverage-ci/worker_stack.out -covermode=atomic ./pkg/worker_watcher go test -v -race -cover -tags=debug -coverpkg=./... -coverprofile=./coverage-ci/ws_origin.out -covermode=atomic ./plugins/websockets go test -v -race -cover -tags=debug -coverpkg=./... -coverprofile=./coverage-ci/http_config.out -covermode=atomic ./plugins/http/config + go test -v -race -cover -tags=debug -coverpkg=./... -coverprofile=./coverage-ci/grpc_codec.out -covermode=atomic ./plugins/grpc/codec + go test -v -race -cover -tags=debug -coverpkg=./... -coverprofile=./coverage-ci/grpc_parser.out -covermode=atomic ./plugins/grpc/parser + go test -v -race -cover -tags=debug -coverpkg=./... -coverprofile=./coverage-ci/grpc_proxy.out -covermode=atomic ./plugins/grpc/proxy go test -v -race -cover -tags=debug -coverpkg=./... -coverprofile=./coverage-ci/server_cmd.out -covermode=atomic ./plugins/server go test -v -race -cover -tags=debug -coverpkg=./... -coverprofile=./coverage-ci/struct_jobs.out -covermode=atomic ./plugins/jobs/job go test -v -race -cover -tags=debug -coverpkg=./... -coverprofile=./coverage-ci/pipeline_jobs.out -covermode=atomic ./plugins/jobs/pipeline go test -v -race -cover -tags=debug -coverpkg=./... -coverprofile=./coverage-ci/jobs_core.out -covermode=atomic ./tests/plugins/jobs go test -v -race -cover -tags=debug -coverpkg=./... -coverprofile=./coverage-ci/kv_plugin.out -covermode=atomic ./tests/plugins/kv + go test -v -race -cover -tags=debug -coverpkg=./... -coverprofile=./coverage-ci/grpc_plugin.out -covermode=atomic ./tests/plugins/grpc go test -v -race -cover -tags=debug -coverpkg=./... -coverprofile=./coverage-ci/broadcast_plugin.out -covermode=atomic ./tests/plugins/broadcast go test -v -race -cover -tags=debug -coverpkg=./... -coverprofile=./coverage-ci/websockets.out -covermode=atomic ./tests/plugins/websockets go test -v -race -cover -tags=debug -coverpkg=./... -coverprofile=./coverage-ci/http.out -covermode=atomic ./tests/plugins/http @@ -54,14 +58,17 @@ test: ## Run application tests go test -v -race -tags=debug ./plugins/http/config go test -v -race -tags=debug ./plugins/server go test -v -race -tags=debug ./plugins/jobs/job - go test -v -race -tags=debug ./tests/plugins/grpc + go test -v -race -tags=debug ./plugins/websockets + go test -v -race -tags=debug ./plugins/grpc + go test -v -race -tags=debug ./plugins/grpc/codec + go test -v -race -tags=debug ./plugins/grpc/parser + go test -v -race -tags=debug ./plugins/grpc/proxy + go test -v -race -tags=debug ./tests/plugins/jobs go test -v -race -tags=debug ./tests/plugins/kv go test -v -race -tags=debug ./tests/plugins/broadcast go test -v -race -tags=debug ./tests/plugins/websockets - go test -v -race -tags=debug ./tests/plugins/jobs - go test -v -race -tags=debug ./plugins/websockets - go test -v -race -tags=debug ./plugins/grpc go test -v -race -tags=debug ./tests/plugins/http + go test -v -race -tags=debug ./tests/plugins/grpc go test -v -race -tags=debug ./tests/plugins/informer go test -v -race -tags=debug ./tests/plugins/reload go test -v -race -tags=debug ./tests/plugins/server diff --git a/go.mod b/go.mod index b7141c4a..6c1616f5 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,7 @@ require ( github.com/beanstalkd/go-beanstalk v0.1.0 github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b github.com/cenkalti/backoff/v4 v4.1.1 - github.com/fasthttp/websocket v1.4.3 + github.com/fasthttp/websocket v1.4.3-rc.8 github.com/fatih/color v1.12.0 github.com/go-redis/redis/v8 v8.11.3 github.com/gofiber/fiber/v2 v2.18.0 @@ -57,9 +57,9 @@ require ( github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect + github.com/emicklei/proto v1.9.1 github.com/fsnotify/fsnotify v1.5.1 // indirect github.com/go-ole/go-ole v1.2.5 // indirect - github.com/golang/protobuf v1.5.2 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/magiconair/properties v1.8.5 // indirect github.com/mattn/go-colorable v0.1.8 // indirect @@ -73,7 +73,7 @@ require ( github.com/prometheus/client_model v0.2.0 // indirect github.com/prometheus/common v0.30.0 // indirect github.com/prometheus/procfs v0.7.3 // indirect - github.com/savsgio/gotils v0.0.0-20210617111740-97865ed5a873 // indirect + github.com/savsgio/gotils v0.0.0-20210907153846-c06938798b52 // indirect github.com/spf13/afero v1.6.0 // indirect github.com/spf13/cast v1.4.1 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect @@ -93,3 +93,5 @@ require ( gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect ) + +require github.com/golang/protobuf v1.5.2 // indirect diff --git a/go.sum b/go.sum index 7dec39f0..07e30edd 100644 --- a/go.sum +++ b/go.sum @@ -50,7 +50,6 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/andybalholm/brotli v1.0.0/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= github.com/andybalholm/brotli v1.0.2/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= github.com/andybalholm/brotli v1.0.3 h1:fpcw+r1N1h0Poc1F/pHbW40cUm/lMEQslZtCkBQ0UnM= github.com/andybalholm/brotli v1.0.3/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= @@ -113,6 +112,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= +github.com/emicklei/proto v1.9.1 h1:MUgjFo5xlMwYv72TnF5xmmdKZ04u+dVbv6wdARv16D8= +github.com/emicklei/proto v1.9.1/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= @@ -121,8 +122,8 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/fasthttp/websocket v1.4.3 h1:qjhRJ/rTy4KB8oBxljEC00SDt6HUY9jLRfM601SUdS4= -github.com/fasthttp/websocket v1.4.3/go.mod h1:5r4oKssgS7W6Zn6mPWap3NWzNPJNzUUh3baWTOhcYQk= +github.com/fasthttp/websocket v1.4.3-rc.8 h1:6P/+ejKdkLC9UhkY7GlShGWYMDBiWQtIECLBTDZ/2LU= +github.com/fasthttp/websocket v1.4.3-rc.8/go.mod h1:4m/MeZnTBQR2coy0HDUpyBXDkgtl2SxO+GZng0EKr6k= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc= github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= @@ -265,7 +266,6 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.10.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= github.com/klauspost/compress v1.13.5 h1:9O69jUPDcsT9fEm74W92rZL9FQY7rCdaXVneq+yyzl4= github.com/klauspost/compress v1.13.5/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= @@ -356,9 +356,9 @@ github.com/rabbitmq/amqp091-go v0.0.0-20210823000215-c428a6150891/go.mod h1:ogQD github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/savsgio/gotils v0.0.0-20200608150037-a5f6f5aef16c/go.mod h1:TWNAOTaVzGOXq8RbEvHnhzA/A2sLZzgn0m6URjnukY8= -github.com/savsgio/gotils v0.0.0-20210617111740-97865ed5a873 h1:N3Af8f13ooDKcIhsmFT7Z05CStZWu4C7Md0uDEy4q6o= github.com/savsgio/gotils v0.0.0-20210617111740-97865ed5a873/go.mod h1:dmPawKuiAeG/aFYVs2i+Dyosoo7FNcm+Pi8iK6ZUrX8= +github.com/savsgio/gotils v0.0.0-20210907153846-c06938798b52 h1:FODZE/jDkENIpW3JiMA9sXBQfNklTfClUNhR9k37dPY= +github.com/savsgio/gotils v0.0.0-20210907153846-c06938798b52/go.mod h1:oejLrk1Y/5zOF+c/aHtXqn3TFlzzbAgPWg8zBiAHDas= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/shirou/gopsutil v3.21.8+incompatible h1:sh0foI8tMRlCidUJR+KzqWYWxrkuuPIGiO6Vp+KXdCU= github.com/shirou/gopsutil v3.21.8+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= @@ -405,10 +405,8 @@ github.com/tklauser/numcpus v0.3.0 h1:ILuRUQBtssgnxw0XXIjKUC56fgnOrFoQQ/4+DeU2bi github.com/tklauser/numcpus v0.3.0/go.mod h1:yFGUr7TUHQRAhyqBcEg0Ge34zDBAsIvJJcyE6boqnA8= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasthttp v1.14.0/go.mod h1:ol1PCaL0dX20wC0htZ7sYCsvCYmrouYra0zHzaclZhE= github.com/valyala/fasthttp v1.29.0 h1:F5GKpytwFk5OhCuRh6H+d4vZAcEeNAwPTdwQnm6IERY= github.com/valyala/fasthttp v1.29.0/go.mod h1:2rsYD01CKFrjjsvFxx75KlEUNpWNBY9JWD3K/7o2Cus= -github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= github.com/vmihailenco/msgpack/v5 v5.3.4 h1:qMKAwOV+meBw2Y8k9cVwAy7qErtYCwBzZ2ellBfvnqc= diff --git a/plugins/grpc/codec.go b/plugins/grpc/codec.go deleted file mode 100644 index aeb373b9..00000000 --- a/plugins/grpc/codec.go +++ /dev/null @@ -1,42 +0,0 @@ -package grpc - -import "google.golang.org/grpc/encoding" - -type rawMessage []byte - -const cName string = "proto" -const rm string = "rawMessage" - -func (r rawMessage) Reset() {} -func (rawMessage) ProtoMessage() {} -func (rawMessage) String() string { return rm } - -type codec struct{ base encoding.Codec } - -// Marshal returns the wire format of v. rawMessages would be returned without encoding. -func (c *codec) Marshal(v interface{}) ([]byte, error) { - if raw, ok := v.(rawMessage); ok { - return raw, nil - } - - return c.base.Marshal(v) -} - -// Unmarshal parses the wire format into v. rawMessages would not be unmarshalled. -func (c *codec) Unmarshal(data []byte, v interface{}) error { - if raw, ok := v.(*rawMessage); ok { - *raw = data - return nil - } - - return c.base.Unmarshal(data, v) -} - -func (c *codec) Name() string { - return cName -} - -// String return codec name. -func (c *codec) String() string { - return "raw:" + c.base.Name() -} diff --git a/plugins/grpc/codec/codec.go b/plugins/grpc/codec/codec.go new file mode 100644 index 00000000..d34cedf1 --- /dev/null +++ b/plugins/grpc/codec/codec.go @@ -0,0 +1,42 @@ +package codec + +import "google.golang.org/grpc/encoding" + +type RawMessage []byte + +const cName string = "proto" +const rm string = "rawMessage" + +func (r RawMessage) Reset() {} +func (RawMessage) ProtoMessage() {} +func (RawMessage) String() string { return rm } + +type Codec struct{ base encoding.Codec } + +// Marshal returns the wire format of v. rawMessages would be returned without encoding. +func (c *Codec) Marshal(v interface{}) ([]byte, error) { + if raw, ok := v.(RawMessage); ok { + return raw, nil + } + + return c.base.Marshal(v) +} + +// Unmarshal parses the wire format into v. rawMessages would not be unmarshalled. +func (c *Codec) Unmarshal(data []byte, v interface{}) error { + if raw, ok := v.(*RawMessage); ok { + *raw = data + return nil + } + + return c.base.Unmarshal(data, v) +} + +func (c *Codec) Name() string { + return cName +} + +// String return codec name. +func (c *Codec) String() string { + return "raw:" + c.base.Name() +} diff --git a/plugins/grpc/codec/codec_test.go b/plugins/grpc/codec/codec_test.go new file mode 100644 index 00000000..60efb072 --- /dev/null +++ b/plugins/grpc/codec/codec_test.go @@ -0,0 +1,79 @@ +package codec + +import ( + "testing" + + json "github.com/json-iterator/go" + "github.com/stretchr/testify/assert" +) + +type jsonCodec struct{} + +func (jsonCodec) Marshal(v interface{}) ([]byte, error) { + return json.Marshal(v) +} + +func (jsonCodec) Unmarshal(data []byte, v interface{}) error { + return json.Unmarshal(data, v) +} + +func (jsonCodec) Name() string { + return "json" +} + +func TestCodec_String(t *testing.T) { + c := Codec{jsonCodec{}} + + assert.Equal(t, "raw:json", c.String()) + + r := RawMessage{} + r.Reset() + r.ProtoMessage() + assert.Equal(t, "rawMessage", r.String()) +} + +func TestCodec_Unmarshal_ByPass(t *testing.T) { + c := Codec{jsonCodec{}} + + s := struct { + Name string + }{} + + assert.NoError(t, c.Unmarshal([]byte(`{"name":"name"}`), &s)) + assert.Equal(t, "name", s.Name) +} + +func TestCodec_Marshal_ByPass(t *testing.T) { + c := Codec{jsonCodec{}} + + s := struct { + Name string + }{ + Name: "name", + } + + d, err := c.Marshal(s) + assert.NoError(t, err) + + assert.Equal(t, `{"Name":"name"}`, string(d)) +} + +func TestCodec_Unmarshal_Raw(t *testing.T) { + c := Codec{jsonCodec{}} + + s := RawMessage{} + + assert.NoError(t, c.Unmarshal([]byte(`{"name":"name"}`), &s)) + assert.Equal(t, `{"name":"name"}`, string(s)) +} + +func TestCodec_Marshal_Raw(t *testing.T) { + c := Codec{jsonCodec{}} + + s := RawMessage(`{"Name":"name"}`) + + d, err := c.Marshal(s) + assert.NoError(t, err) + + assert.Equal(t, `{"Name":"name"}`, string(d)) +} diff --git a/plugins/grpc/codec_test.go b/plugins/grpc/codec_test.go deleted file mode 100644 index 5f94b745..00000000 --- a/plugins/grpc/codec_test.go +++ /dev/null @@ -1,79 +0,0 @@ -package grpc - -import ( - "testing" - - json "github.com/json-iterator/go" - "github.com/stretchr/testify/assert" -) - -type jsonCodec struct{} - -func (jsonCodec) Marshal(v interface{}) ([]byte, error) { - return json.Marshal(v) -} - -func (jsonCodec) Unmarshal(data []byte, v interface{}) error { - return json.Unmarshal(data, v) -} - -func (jsonCodec) Name() string { - return "json" -} - -func TestCodec_String(t *testing.T) { - c := codec{jsonCodec{}} - - assert.Equal(t, "raw:json", c.String()) - - r := rawMessage{} - r.Reset() - r.ProtoMessage() - assert.Equal(t, "rawMessage", r.String()) -} - -func TestCodec_Unmarshal_ByPass(t *testing.T) { - c := codec{jsonCodec{}} - - s := struct { - Name string - }{} - - assert.NoError(t, c.Unmarshal([]byte(`{"name":"name"}`), &s)) - assert.Equal(t, "name", s.Name) -} - -func TestCodec_Marshal_ByPass(t *testing.T) { - c := codec{jsonCodec{}} - - s := struct { - Name string - }{ - Name: "name", - } - - d, err := c.Marshal(s) - assert.NoError(t, err) - - assert.Equal(t, `{"Name":"name"}`, string(d)) -} - -func TestCodec_Unmarshal_Raw(t *testing.T) { - c := codec{jsonCodec{}} - - s := rawMessage{} - - assert.NoError(t, c.Unmarshal([]byte(`{"name":"name"}`), &s)) - assert.Equal(t, `{"name":"name"}`, string(s)) -} - -func TestCodec_Marshal_Raw(t *testing.T) { - c := codec{jsonCodec{}} - - s := rawMessage(`{"Name":"name"}`) - - d, err := c.Marshal(s) - assert.NoError(t, err) - - assert.Equal(t, `{"Name":"name"}`, string(d)) -} diff --git a/plugins/grpc/config.go b/plugins/grpc/config.go index 53c50e22..8a3af6a2 100644 --- a/plugins/grpc/config.go +++ b/plugins/grpc/config.go @@ -12,7 +12,7 @@ type Config struct { TLS *TLS - grpcPool pool.Pool + grpcPool pool.Config MaxSendMsgSize int64 `mapstructure:"max_send_msg_size"` MaxRecvMsgSize int64 `mapstructure:"max_recv_msg_size"` MaxConnectionIdle time.Duration `mapstructure:"max_connection_idle"` diff --git a/plugins/grpc/parser/message.proto b/plugins/grpc/parser/message.proto new file mode 100644 index 00000000..a4012010 --- /dev/null +++ b/plugins/grpc/parser/message.proto @@ -0,0 +1,7 @@ +syntax = "proto3"; +package app.namespace; + +message Message { + string msg = 1; + int64 value = 2; +} \ No newline at end of file diff --git a/plugins/grpc/parser/parse.go b/plugins/grpc/parser/parse.go new file mode 100644 index 00000000..d59b0927 --- /dev/null +++ b/plugins/grpc/parser/parse.go @@ -0,0 +1,114 @@ +package parser + +import ( + "bytes" + "io" + "os" + + pp "github.com/emicklei/proto" +) + +// Service contains information about singular GRPC service. +type Service struct { + // Package defines service namespace. + Package string + + // Name defines service name. + Name string + + // Methods list. + Methods []Method +} + +// Method describes singular RPC method. +type Method struct { + // Name is method name. + Name string + + // StreamsRequest defines if method accept stream input. + StreamsRequest bool + + // RequestType defines message name (from the same package) of method input. + RequestType string + + // StreamsReturns defines if method streams result. + StreamsReturns bool + + // ReturnsType defines message name (from the same package) of method return value. + ReturnsType string +} + +// File parses given proto file or returns error. +func File(file string, importPath string) ([]Service, error) { + reader, _ := os.Open(file) + defer reader.Close() + + return parse(reader, importPath) +} + +// Bytes parses string into proto definition. +func Bytes(data []byte) ([]Service, error) { + return parse(bytes.NewBuffer(data), "") +} + +func parse(reader io.Reader, importPath string) ([]Service, error) { + proto, err := pp.NewParser(reader).Parse() + if err != nil { + return nil, err + } + + return parseServices( + proto, + parsePackage(proto), + importPath, + ) +} + +func parsePackage(proto *pp.Proto) string { + for _, e := range proto.Elements { + if p, ok := e.(*pp.Package); ok { + return p.Name + } + } + + return "" +} + +func parseServices(proto *pp.Proto, pkg string, importPath string) ([]Service, error) { + services := make([]Service, 0) + + pp.Walk(proto, pp.WithService(func(service *pp.Service) { + services = append(services, Service{ + Package: pkg, + Name: service.Name, + Methods: parseMethods(service), + }) + })) + + pp.Walk(proto, func(v pp.Visitee) { + if i, ok := v.(*pp.Import); ok { + if im, err := File(importPath+"/"+i.Filename, importPath); err == nil { + services = append(services, im...) + } + } + }) + + return services, nil +} + +func parseMethods(s *pp.Service) []Method { + methods := make([]Method, 0) + for _, e := range s.Elements { + if m, ok := e.(*pp.RPC); ok { + methods = append(methods, Method{ + Name: m.Name, + StreamsRequest: m.StreamsRequest, + RequestType: m.RequestType, + StreamsReturns: m.StreamsReturns, + ReturnsType: m.ReturnsType, + }) + } + } + + return methods +} diff --git a/plugins/grpc/parser/parse_test.go b/plugins/grpc/parser/parse_test.go new file mode 100644 index 00000000..b71c133d --- /dev/null +++ b/plugins/grpc/parser/parse_test.go @@ -0,0 +1,71 @@ +package parser + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestParseFile(t *testing.T) { + services, err := File("test.proto", "") + assert.NoError(t, err) + assert.Len(t, services, 2) + + assert.Equal(t, "app.namespace", services[0].Package) +} + +func TestParseFileWithImportsNestedFolder(t *testing.T) { + services, err := File("./test_nested/test_import.proto", "./test_nested") + assert.NoError(t, err) + assert.Len(t, services, 2) + + assert.Equal(t, "app.namespace", services[0].Package) +} + +func TestParseFileWithImports(t *testing.T) { + services, err := File("test_import.proto", ".") + assert.NoError(t, err) + assert.Len(t, services, 2) + + assert.Equal(t, "app.namespace", services[0].Package) +} + +func TestParseNotFound(t *testing.T) { + _, err := File("test2.proto", "") + assert.Error(t, err) +} + +func TestParseBytes(t *testing.T) { + services, err := Bytes([]byte{}) + assert.NoError(t, err) + assert.Len(t, services, 0) +} + +func TestParseString(t *testing.T) { + services, err := Bytes([]byte(` +syntax = "proto3"; +package app.namespace; + +// Ping Service. +service PingService { + // Ping Method. + rpc Ping (Message) returns (Message) { + } +} + +// Pong service. +service PongService { + rpc Pong (stream Message) returns (stream Message) { + } +} + +message Message { + string msg = 1; + int64 value = 2; +} +`)) + assert.NoError(t, err) + assert.Len(t, services, 2) + + assert.Equal(t, "app.namespace", services[0].Package) +} diff --git a/plugins/grpc/parser/pong.proto b/plugins/grpc/parser/pong.proto new file mode 100644 index 00000000..9756fabe --- /dev/null +++ b/plugins/grpc/parser/pong.proto @@ -0,0 +1,10 @@ +syntax = "proto3"; +package app.namespace; + +import "message.proto"; + +// Pong service. +service PongService { + rpc Pong (stream Message) returns (stream Message) { + } +} \ No newline at end of file diff --git a/plugins/grpc/parser/test.proto b/plugins/grpc/parser/test.proto new file mode 100644 index 00000000..e2230954 --- /dev/null +++ b/plugins/grpc/parser/test.proto @@ -0,0 +1,20 @@ +syntax = "proto3"; +package app.namespace; + +// Ping Service. +service PingService { + // Ping Method. + rpc Ping (Message) returns (Message) { + } +} + +// Pong service. +service PongService { + rpc Pong (stream Message) returns (stream Message) { + } +} + +message Message { + string msg = 1; + int64 value = 2; +} \ No newline at end of file diff --git a/plugins/grpc/parser/test_import.proto b/plugins/grpc/parser/test_import.proto new file mode 100644 index 00000000..1b954fc1 --- /dev/null +++ b/plugins/grpc/parser/test_import.proto @@ -0,0 +1,12 @@ +syntax = "proto3"; +package app.namespace; + +import "message.proto"; +import "pong.proto"; + +// Ping Service. +service PingService { + // Ping Method. + rpc Ping (Message) returns (Message) { + } +} \ No newline at end of file diff --git a/plugins/grpc/parser/test_nested/message.proto b/plugins/grpc/parser/test_nested/message.proto new file mode 100644 index 00000000..a4012010 --- /dev/null +++ b/plugins/grpc/parser/test_nested/message.proto @@ -0,0 +1,7 @@ +syntax = "proto3"; +package app.namespace; + +message Message { + string msg = 1; + int64 value = 2; +} \ No newline at end of file diff --git a/plugins/grpc/parser/test_nested/pong.proto b/plugins/grpc/parser/test_nested/pong.proto new file mode 100644 index 00000000..9756fabe --- /dev/null +++ b/plugins/grpc/parser/test_nested/pong.proto @@ -0,0 +1,10 @@ +syntax = "proto3"; +package app.namespace; + +import "message.proto"; + +// Pong service. +service PongService { + rpc Pong (stream Message) returns (stream Message) { + } +} \ No newline at end of file diff --git a/plugins/grpc/parser/test_nested/test_import.proto b/plugins/grpc/parser/test_nested/test_import.proto new file mode 100644 index 00000000..a3a476ba --- /dev/null +++ b/plugins/grpc/parser/test_nested/test_import.proto @@ -0,0 +1,12 @@ +syntax = "proto3"; +package app.namespace; + +import "message.proto"; +import "pong.proto"; + +// Ping Service. +service PingService { + // Ping Method. + rpc Ping (Message) returns (Message) { + } +} diff --git a/plugins/grpc/plugin.go b/plugins/grpc/plugin.go index b424c5d0..9285afe5 100644 --- a/plugins/grpc/plugin.go +++ b/plugins/grpc/plugin.go @@ -2,7 +2,9 @@ package grpc import ( "github.com/spiral/errors" + "github.com/spiral/roadrunner/v2/pkg/pool" "github.com/spiral/roadrunner/v2/plugins/config" + "github.com/spiral/roadrunner/v2/plugins/grpc/codec" "github.com/spiral/roadrunner/v2/plugins/logger" "google.golang.org/grpc" "google.golang.org/grpc/encoding" @@ -13,8 +15,10 @@ const ( ) type Plugin struct { - config *Config - opts []grpc.ServerOption + config *Config + gPool pool.Pool + opts []grpc.ServerOption + services []func(server *grpc.Server) cfg config.Configurer log logger.Logger @@ -24,7 +28,7 @@ func (p *Plugin) Init(cfg config.Configurer, log logger.Logger) error { const op = errors.Op("grpc_plugin_init") // register the codec - encoding.RegisterCodec(&codec{}) + encoding.RegisterCodec(&codec.Codec{}) return nil } diff --git a/plugins/grpc/proxy/proxy.go b/plugins/grpc/proxy/proxy.go new file mode 100644 index 00000000..7a8eaa1f --- /dev/null +++ b/plugins/grpc/proxy/proxy.go @@ -0,0 +1,203 @@ +package proxy + +import ( + "encoding/json" + "fmt" + "strconv" + "strings" + + "github.com/spiral/roadrunner/v2/pkg/payload" + "github.com/spiral/roadrunner/v2/pkg/pool" + "github.com/spiral/roadrunner/v2/plugins/grpc/codec" + "golang.org/x/net/context" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/metadata" + "google.golang.org/grpc/peer" + "google.golang.org/grpc/status" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/anypb" +) + +// base interface for Proxy class +type proxyService interface { + // RegisterMethod registers new RPC method. + RegisterMethod(method string) + + // ServiceDesc returns service description for the proxy. + ServiceDesc() *grpc.ServiceDesc +} + +// carry details about service, method and RPC context to PHP process +type rpcContext struct { + Service string `json:"service"` + Method string `json:"method"` + Context map[string][]string `json:"context"` +} + +// Proxy manages GRPC/RoadRunner bridge. +type Proxy struct { + grpcPool pool.Pool + name string + metadata string + methods []string +} + +// NewProxy creates new service proxy object. +func NewProxy(name string, metadata string, grpcPool pool.Pool) *Proxy { + return &Proxy{ + grpcPool: grpcPool, + name: name, + metadata: metadata, + methods: make([]string, 0), + } +} + +// RegisterMethod registers new RPC method. +func (p *Proxy) RegisterMethod(method string) { + p.methods = append(p.methods, method) +} + +// ServiceDesc returns service description for the proxy. +func (p *Proxy) ServiceDesc() *grpc.ServiceDesc { + desc := &grpc.ServiceDesc{ + ServiceName: p.name, + Metadata: p.metadata, + HandlerType: (*proxyService)(nil), + Methods: []grpc.MethodDesc{}, + Streams: []grpc.StreamDesc{}, + } + + // Registering methods + for _, m := range p.methods { + desc.Methods = append(desc.Methods, grpc.MethodDesc{ + MethodName: m, + Handler: p.methodHandler(m), + }) + } + + return desc +} + +// Generate method handler proxy. +func (p *Proxy) methodHandler(method string) func(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + return func(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := codec.RawMessage{} + if err := dec(&in); err != nil { + return nil, wrapError(err) + } + + if interceptor == nil { + return p.invoke(ctx, method, in) + } + + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: fmt.Sprintf("/%s/%s", p.name, method), + } + + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return p.invoke(ctx, method, req.(codec.RawMessage)) + } + + return interceptor(ctx, in, info, handler) + } +} + +func (p *Proxy) invoke(ctx context.Context, method string, in codec.RawMessage) (interface{}, error) { + payload, err := p.makePayload(ctx, method, in) + if err != nil { + return nil, err + } + + resp, err := p.grpcPool.Exec(payload) + + if err != nil { + return nil, wrapError(err) + } + + md, err := p.responseMetadata(resp) + if err != nil { + return nil, err + } + ctx = metadata.NewIncomingContext(ctx, md) + err = grpc.SetHeader(ctx, md) + if err != nil { + return nil, err + } + + return codec.RawMessage(resp.Body), nil +} + +// responseMetadata extracts metadata from roadrunner response Payload.Context and converts it to metadata.MD +func (p *Proxy) responseMetadata(resp *payload.Payload) (metadata.MD, error) { + var md metadata.MD + if resp == nil || len(resp.Context) == 0 { + return md, nil + } + + var rpcMetadata map[string]string + err := json.Unmarshal(resp.Context, &rpcMetadata) + if err != nil { + return md, err + } + + if len(rpcMetadata) > 0 { + md = metadata.New(rpcMetadata) + } + + return md, nil +} + +// makePayload generates RoadRunner compatible payload based on GRPC message. todo: return error +func (p *Proxy) makePayload(ctx context.Context, method string, body codec.RawMessage) (*payload.Payload, error) { + ctxMD := make(map[string][]string) + + if md, ok := metadata.FromIncomingContext(ctx); ok { + for k, v := range md { + ctxMD[k] = v + } + } + + if pr, ok := peer.FromContext(ctx); ok { + ctxMD[":peer.address"] = []string{pr.Addr.String()} + if pr.AuthInfo != nil { + ctxMD[":peer.auth-type"] = []string{pr.AuthInfo.AuthType()} + } + } + + ctxData, err := json.Marshal(rpcContext{Service: p.name, Method: method, Context: ctxMD}) + + if err != nil { + return nil, err + } + + return &payload.Payload{Context: ctxData, Body: body}, nil +} + +// mounts proper error code for the error +func wrapError(err error) error { + // internal agreement + if strings.Contains(err.Error(), "|:|") { + chunks := strings.Split(err.Error(), "|:|") + code := codes.Internal + + if phpCode, errConv := strconv.Atoi(chunks[0]); errConv == nil { + code = codes.Code(phpCode) + } + + st := status.New(code, chunks[1]).Proto() + + for _, detailsMessage := range chunks[2:] { + anyDetailsMessage := anypb.Any{} + errP := proto.Unmarshal([]byte(detailsMessage), &anyDetailsMessage) + if errP == nil { + st.Details = append(st.Details, &anyDetailsMessage) + } + } + + return status.ErrorProto(st) + } + + return status.Error(codes.Internal, err.Error()) +} diff --git a/plugins/grpc/proxy/proxy_test.go b/plugins/grpc/proxy/proxy_test.go new file mode 100644 index 00000000..2c024ee3 --- /dev/null +++ b/plugins/grpc/proxy/proxy_test.go @@ -0,0 +1,134 @@ +package proxy + +// import ( +// "testing" +// "time" + +// "github.com/sirupsen/logrus" +// "github.com/sirupsen/logrus/hooks/test" +// "github.com/stretchr/testify/assert" +// "golang.org/x/net/context" +// "google.golang.org/grpc" +// "google.golang.org/grpc/codes" +// "google.golang.org/grpc/metadata" +// "google.golang.org/grpc/status" +// ) + +// const addr = "localhost:9080" + +// func Test_Proxy_Error(t *testing.T) { +// logger, _ := test.NewNullLogger() +// logger.SetLevel(logrus.DebugLevel) + +// c := service.NewContainer(logger) +// c.Register(ID, &Service{}) + +// assert.NoError(t, c.Init(&testCfg{ +// grpcCfg: `{ +// "listen": "tcp://:9080", +// "tls": { +// "key": "tests/server.key", +// "cert": "tests/server.crt" +// }, +// "proto": "tests/test.proto", +// "workers":{ +// "command": "php tests/worker.php", +// "relay": "pipes", +// "pool": { +// "numWorkers": 1, +// "allocateTimeout": 10, +// "destroyTimeout": 10 +// } +// } +// }`, +// })) + +// s, st := c.Get(ID) +// assert.NotNil(t, s) +// assert.Equal(t, service.StatusOK, st) + +// // should do nothing +// s.(*Service).Stop() + +// go func() { assert.NoError(t, c.Serve()) }() +// time.Sleep(time.Millisecond * 100) +// defer c.Stop() + +// cl, cn := getClient(addr) +// defer cn.Close() + +// _, err := cl.Throw(context.Background(), &tests.Message{Msg: "notFound"}) + +// assert.Error(t, err) +// se, _ := status.FromError(err) +// assert.Equal(t, "nothing here", se.Message()) +// assert.Equal(t, codes.NotFound, se.Code()) + +// _, errWithDetails := cl.Throw(context.Background(), &tests.Message{Msg: "withDetails"}) + +// assert.Error(t, errWithDetails) +// statusWithDetails, _ := status.FromError(errWithDetails) +// assert.Equal(t, "main exception message", statusWithDetails.Message()) +// assert.Equal(t, codes.InvalidArgument, statusWithDetails.Code()) + +// details := statusWithDetails.Details() + +// detailsMessageForException := details[0].(*tests.DetailsMessageForException) + +// assert.Equal(t, detailsMessageForException.Code, uint64(1)) +// assert.Equal(t, detailsMessageForException.Message, "details message") +// } + +// func Test_Proxy_Metadata(t *testing.T) { +// logger, _ := test.NewNullLogger() +// logger.SetLevel(logrus.DebugLevel) + +// c := service.NewContainer(logger) +// c.Register(ID, &Service{}) + +// assert.NoError(t, c.Init(&testCfg{ +// grpcCfg: `{ +// "listen": "tcp://:9080", +// "tls": { +// "key": "tests/server.key", +// "cert": "tests/server.crt" +// }, +// "proto": "tests/test.proto", +// "workers":{ +// "command": "php tests/worker.php", +// "relay": "pipes", +// "pool": { +// "numWorkers": 1, +// "allocateTimeout": 10, +// "destroyTimeout": 10 +// } +// } +// }`, +// })) + +// s, st := c.Get(ID) +// assert.NotNil(t, s) +// assert.Equal(t, service.StatusOK, st) + +// // should do nothing +// s.(*Service).Stop() + +// go func() { assert.NoError(t, c.Serve()) }() +// time.Sleep(time.Millisecond * 100) +// defer c.Stop() + +// cl, cn := getClient(addr) +// defer cn.Close() + +// ctx := metadata.AppendToOutgoingContext(context.Background(), "key", "proxy-value") +// var header metadata.MD +// out, err := cl.Info( +// ctx, +// &tests.Message{Msg: "MD"}, +// grpc.Header(&header), +// grpc.WaitForReady(true), +// ) +// assert.Equal(t, []string{"bar"}, header.Get("foo")) +// assert.NoError(t, err) +// assert.Equal(t, `["proxy-value"]`, out.Msg) +// } diff --git a/plugins/grpc/server.go b/plugins/grpc/server.go index 735d53e9..24759fba 100644 --- a/plugins/grpc/server.go +++ b/plugins/grpc/server.go @@ -4,14 +4,16 @@ import ( "context" "crypto/tls" "crypto/x509" + "fmt" "os" + "path" "github.com/spiral/errors" + "github.com/spiral/roadrunner/v2/plugins/grpc/parser" + "github.com/spiral/roadrunner/v2/plugins/grpc/proxy" "google.golang.org/grpc" "google.golang.org/grpc/credentials" "google.golang.org/grpc/keepalive" - "google.golang.org/grpc/reflection" - "google.golang.org/protobuf/reflect/protoregistry" ) func (p *Plugin) createGRPCserver() (*grpc.Server, error) { @@ -22,18 +24,28 @@ func (p *Plugin) createGRPCserver() (*grpc.Server, error) { } server := grpc.NewServer(opts...) - reflection.Register(server) - fd, err := protoregistry.GlobalFiles.FindFileByPath("file") - if err != nil { - return nil, errors.E(op, err) - } + if p.config.Proto != "" { + // php proxy services + services, err := parser.File(p.config.Proto, path.Dir(p.config.Proto)) + if err != nil { + return nil, err + } - _ = fd + for _, service := range services { + p := proxy.NewProxy(fmt.Sprintf("%s.%s", service.Package, service.Name), p.config.Proto, p.gPool) + for _, m := range service.Methods { + p.RegisterMethod(m.Name) + } - /* - proto descriptors parser - */ + server.RegisterService(p.ServiceDesc(), p) + } + } + + // external and native services + for _, r := range p.services { + r(server) + } return server, nil } -- cgit v1.2.3 From 6846141b15ed69036cb180307fde34913e8c1b77 Mon Sep 17 00:00:00 2001 From: Valery Piashchynski Date: Wed, 15 Sep 2021 00:00:07 +0300 Subject: Atoi -> ParseUint(..,10, 32). Int in the string might be bigger, that uint32, we should parse it properly. Signed-off-by: Valery Piashchynski --- plugins/grpc/proxy/proxy.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/grpc/proxy/proxy.go b/plugins/grpc/proxy/proxy.go index 7a8eaa1f..14655e0d 100644 --- a/plugins/grpc/proxy/proxy.go +++ b/plugins/grpc/proxy/proxy.go @@ -182,7 +182,7 @@ func wrapError(err error) error { chunks := strings.Split(err.Error(), "|:|") code := codes.Internal - if phpCode, errConv := strconv.Atoi(chunks[0]); errConv == nil { + if phpCode, errConv := strconv.ParseUint(chunks[0], 10, 32); errConv == nil { code = codes.Code(phpCode) } -- cgit v1.2.3 From e4c84c703c2c798e4fa8ff8cf97e5e59e81ef4ed Mon Sep 17 00:00:00 2001 From: Valery Piashchynski Date: Wed, 15 Sep 2021 07:52:14 +0300 Subject: Check the len of the grpc chunks. Signed-off-by: Valery Piashchynski --- plugins/grpc/proxy/proxy.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/plugins/grpc/proxy/proxy.go b/plugins/grpc/proxy/proxy.go index 14655e0d..9e406bbf 100644 --- a/plugins/grpc/proxy/proxy.go +++ b/plugins/grpc/proxy/proxy.go @@ -182,6 +182,11 @@ func wrapError(err error) error { chunks := strings.Split(err.Error(), "|:|") code := codes.Internal + // protect the slice access + if len(chunks) < 2 { + return err + } + if phpCode, errConv := strconv.ParseUint(chunks[0], 10, 32); errConv == nil { code = codes.Code(phpCode) } -- cgit v1.2.3 From d445f59b5f9c55719dec16eca96722a913f9e839 Mon Sep 17 00:00:00 2001 From: Valery Piashchynski Date: Wed, 15 Sep 2021 17:13:10 +0300 Subject: Finish GRPC plugin Signed-off-by: Valery Piashchynski --- .vscode/settings.json | 1 + pkg/events/grpc_event.go | 39 ++++++++++++ plugins/grpc/config.go | 5 +- plugins/grpc/plugin.go | 143 ++++++++++++++++++++++++++++++++++++++++++-- plugins/grpc/proxy/proxy.go | 21 +++++-- plugins/grpc/server.go | 36 +++++++---- tests/psr-worker-bench.php | 1 + 7 files changed, 224 insertions(+), 22 deletions(-) create mode 100644 pkg/events/grpc_event.go diff --git a/.vscode/settings.json b/.vscode/settings.json index 50bc942d..5fba80db 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -19,6 +19,7 @@ "mget", "prefetch", "proto", + "protobuf", "SETEX", "shivammathur", "srem", diff --git a/pkg/events/grpc_event.go b/pkg/events/grpc_event.go new file mode 100644 index 00000000..31ff4957 --- /dev/null +++ b/pkg/events/grpc_event.go @@ -0,0 +1,39 @@ +package events + +import ( + "time" + + "google.golang.org/grpc" +) + +const ( + // EventUnaryCallOk represents success unary call response + EventUnaryCallOk G = iota + 13000 + + // EventUnaryCallErr raised when unary call ended with error + EventUnaryCallErr +) + +type G int64 + +func (ev G) String() string { + switch ev { + case EventUnaryCallOk: + return "EventUnaryCallOk" + case EventUnaryCallErr: + return "EventUnaryCallErr" + } + return UnknownEventType +} + +// JobEvent represent job event. +type GRPCEvent struct { + Event G + // Info contains unary call info. + Info *grpc.UnaryServerInfo + // Error associated with event. + Error error + // event timings + Start time.Time + Elapsed time.Duration +} diff --git a/plugins/grpc/config.go b/plugins/grpc/config.go index 8a3af6a2..87bbf7ae 100644 --- a/plugins/grpc/config.go +++ b/plugins/grpc/config.go @@ -12,7 +12,10 @@ type Config struct { TLS *TLS - grpcPool pool.Config + // Env is environment variables passed to the http pool + Env map[string]string + + GrpcPool pool.Config MaxSendMsgSize int64 `mapstructure:"max_send_msg_size"` MaxRecvMsgSize int64 `mapstructure:"max_recv_msg_size"` MaxConnectionIdle time.Duration `mapstructure:"max_connection_idle"` diff --git a/plugins/grpc/plugin.go b/plugins/grpc/plugin.go index 9285afe5..579a00a4 100644 --- a/plugins/grpc/plugin.go +++ b/plugins/grpc/plugin.go @@ -1,35 +1,66 @@ package grpc import ( + "context" + "sync" + "github.com/spiral/errors" + "github.com/spiral/roadrunner/v2/pkg/events" "github.com/spiral/roadrunner/v2/pkg/pool" + "github.com/spiral/roadrunner/v2/pkg/state/process" "github.com/spiral/roadrunner/v2/plugins/config" "github.com/spiral/roadrunner/v2/plugins/grpc/codec" "github.com/spiral/roadrunner/v2/plugins/logger" + "github.com/spiral/roadrunner/v2/plugins/server" + "github.com/spiral/roadrunner/v2/utils" "google.golang.org/grpc" "google.golang.org/grpc/encoding" ) const ( - name string = "grpc" + name string = "grpc" + RrGrpc string = "RR_GRPC" ) type Plugin struct { + mu *sync.RWMutex config *Config gPool pool.Pool opts []grpc.ServerOption services []func(server *grpc.Server) + server *grpc.Server + rrServer server.Server - cfg config.Configurer - log logger.Logger + // events handler + events events.Handler + log logger.Logger } -func (p *Plugin) Init(cfg config.Configurer, log logger.Logger) error { +func (p *Plugin) Init(cfg config.Configurer, log logger.Logger, server server.Server) error { const op = errors.Op("grpc_plugin_init") + if !cfg.Has(name) { + return errors.E(errors.Disabled) + } // register the codec encoding.RegisterCodec(&codec.Codec{}) + err := cfg.UnmarshalKey(name, p.config) + if err != nil { + return errors.E(op, err) + } + + p.opts = make([]grpc.ServerOption, 0) + p.services = make([]func(server *grpc.Server), 0) + p.events = events.NewEventsHandler() + p.events.AddListener(p.collectGRPCEvents) + p.rrServer = server + + // worker's GRPC mode + p.config.Env[RrGrpc] = "true" + + p.log = log + return nil } @@ -37,10 +68,61 @@ func (p *Plugin) Serve() chan error { const op = errors.Op("grpc_plugin_serve") errCh := make(chan error, 1) + var err error + p.gPool, err = p.rrServer.NewWorkerPool(context.Background(), &pool.Config{ + Debug: p.config.GrpcPool.Debug, + NumWorkers: p.config.GrpcPool.NumWorkers, + MaxJobs: p.config.GrpcPool.MaxJobs, + AllocateTimeout: p.config.GrpcPool.AllocateTimeout, + DestroyTimeout: p.config.GrpcPool.DestroyTimeout, + Supervisor: p.config.GrpcPool.Supervisor, + }, p.config.Env, p.collectGRPCEvents) + if err != nil { + errCh <- errors.E(op, err) + return errCh + } + + go func() { + var err error + p.mu.Lock() + defer p.mu.Unlock() + p.server, err = p.createGRPCserver() + if err != nil { + p.log.Error("create grpc server", "error", err) + errCh <- errors.E(op, err) + return + } + + l, err := utils.CreateListener(p.config.Listen) + if err != nil { + p.log.Error("create grpc listener", "error", err) + errCh <- errors.E(op, err) + } + + // protect serve + err = p.server.Serve(l) + if err != nil { + // skip errors when stopping the server + if err == grpc.ErrServerStopped { + return + } + + p.log.Error("grpc server stopped", "error", err) + errCh <- errors.E(op, err) + return + } + }() + return errCh } func (p *Plugin) Stop() error { + p.mu.Lock() + defer p.mu.Unlock() + + if p.server != nil { + p.server.GracefulStop() + } return nil } @@ -49,3 +131,56 @@ func (p *Plugin) Available() {} func (p *Plugin) Name() string { return name } + +func (p *Plugin) Reset() error { + p.mu.Lock() + defer p.mu.Unlock() + const op = errors.Op("grpc_plugin_reset") + + // destroy old pool + p.gPool.Destroy(context.Background()) + + var err error + p.gPool, err = p.rrServer.NewWorkerPool(context.Background(), &pool.Config{ + Debug: p.config.GrpcPool.Debug, + NumWorkers: p.config.GrpcPool.NumWorkers, + MaxJobs: p.config.GrpcPool.MaxJobs, + AllocateTimeout: p.config.GrpcPool.AllocateTimeout, + DestroyTimeout: p.config.GrpcPool.DestroyTimeout, + Supervisor: p.config.GrpcPool.Supervisor, + }, p.config.Env, p.collectGRPCEvents) + if err != nil { + return errors.E(op, err) + } + + return nil +} + +func (p *Plugin) Workers() []*process.State { + p.mu.RLock() + defer p.mu.RUnlock() + + workers := p.gPool.Workers() + + ps := make([]*process.State, 0, len(workers)) + for i := 0; i < len(workers); i++ { + state, err := process.WorkerProcessState(workers[i]) + if err != nil { + return nil + } + ps = append(ps, state) + } + + return ps +} + +func (p *Plugin) collectGRPCEvents(event interface{}) { + if gev, ok := event.(events.GRPCEvent); ok { + switch gev.Event { + case events.EventUnaryCallOk: + p.log.Info("method called", "method", gev.Info.FullMethod, "started", gev.Start, "elapsed", gev.Elapsed) + case events.EventUnaryCallErr: + p.log.Info("method call finished with error", "error", gev.Error, "method", gev.Info.FullMethod, "started", gev.Start, "elapsed", gev.Elapsed) + } + } +} diff --git a/plugins/grpc/proxy/proxy.go b/plugins/grpc/proxy/proxy.go index 9e406bbf..074aac85 100644 --- a/plugins/grpc/proxy/proxy.go +++ b/plugins/grpc/proxy/proxy.go @@ -5,6 +5,7 @@ import ( "fmt" "strconv" "strings" + "sync" "github.com/spiral/roadrunner/v2/pkg/payload" "github.com/spiral/roadrunner/v2/pkg/pool" @@ -19,6 +20,12 @@ import ( "google.golang.org/protobuf/types/known/anypb" ) +const ( + peerAddr string = ":peer.address" + peerAuthType string = ":peer.auth-type" + delimiter string = "|:|" +) + // base interface for Proxy class type proxyService interface { // RegisterMethod registers new RPC method. @@ -37,6 +44,7 @@ type rpcContext struct { // Proxy manages GRPC/RoadRunner bridge. type Proxy struct { + mu *sync.RWMutex grpcPool pool.Pool name string metadata string @@ -44,8 +52,9 @@ type Proxy struct { } // NewProxy creates new service proxy object. -func NewProxy(name string, metadata string, grpcPool pool.Pool) *Proxy { +func NewProxy(name string, metadata string, grpcPool pool.Pool, mu *sync.RWMutex) *Proxy { return &Proxy{ + mu: mu, grpcPool: grpcPool, name: name, metadata: metadata, @@ -110,7 +119,9 @@ func (p *Proxy) invoke(ctx context.Context, method string, in codec.RawMessage) return nil, err } + p.mu.RLock() resp, err := p.grpcPool.Exec(payload) + p.mu.RUnlock() if err != nil { return nil, wrapError(err) @@ -160,9 +171,9 @@ func (p *Proxy) makePayload(ctx context.Context, method string, body codec.RawMe } if pr, ok := peer.FromContext(ctx); ok { - ctxMD[":peer.address"] = []string{pr.Addr.String()} + ctxMD[peerAddr] = []string{pr.Addr.String()} if pr.AuthInfo != nil { - ctxMD[":peer.auth-type"] = []string{pr.AuthInfo.AuthType()} + ctxMD[peerAuthType] = []string{pr.AuthInfo.AuthType()} } } @@ -178,8 +189,8 @@ func (p *Proxy) makePayload(ctx context.Context, method string, body codec.RawMe // mounts proper error code for the error func wrapError(err error) error { // internal agreement - if strings.Contains(err.Error(), "|:|") { - chunks := strings.Split(err.Error(), "|:|") + if strings.Contains(err.Error(), delimiter) { + chunks := strings.Split(err.Error(), delimiter) code := codes.Internal // protect the slice access diff --git a/plugins/grpc/server.go b/plugins/grpc/server.go index 24759fba..323f73a0 100644 --- a/plugins/grpc/server.go +++ b/plugins/grpc/server.go @@ -7,8 +7,10 @@ import ( "fmt" "os" "path" + "time" "github.com/spiral/errors" + "github.com/spiral/roadrunner/v2/pkg/events" "github.com/spiral/roadrunner/v2/plugins/grpc/parser" "github.com/spiral/roadrunner/v2/plugins/grpc/proxy" "google.golang.org/grpc" @@ -33,7 +35,7 @@ func (p *Plugin) createGRPCserver() (*grpc.Server, error) { } for _, service := range services { - p := proxy.NewProxy(fmt.Sprintf("%s.%s", service.Package, service.Name), p.config.Proto, p.gPool) + p := proxy.NewProxy(fmt.Sprintf("%s.%s", service.Package, service.Name), p.config.Proto, p.gPool, p.mu) for _, m := range service.Methods { p.RegisterMethod(m.Name) } @@ -50,19 +52,29 @@ func (p *Plugin) createGRPCserver() (*grpc.Server, error) { return server, nil } -func (p *Plugin) interceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) { - // start := time.Now() - resp, err = handler(ctx, req) +func (p *Plugin) interceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { + start := time.Now() + resp, err := handler(ctx, req) + if err != nil { + p.events.Push(events.GRPCEvent{ + Event: events.EventUnaryCallErr, + Info: info, + Error: err, + Start: start, + Elapsed: time.Since(start), + }) + + return nil, err + } - // svc.throw(EventUnaryCall, &UnaryCallEvent{ - // Info: info, - // Context: ctx, - // Error: err, - // start: start, - // elapsed: time.Since(start), - // }) + p.events.Push(events.GRPCEvent{ + Event: events.EventUnaryCallOk, + Info: info, + Start: start, + Elapsed: time.Since(start), + }) - return resp, err + return resp, nil } func (p *Plugin) serverOptions() ([]grpc.ServerOption, error) { diff --git a/tests/psr-worker-bench.php b/tests/psr-worker-bench.php index 80fc435c..f8b1b47c 100644 --- a/tests/psr-worker-bench.php +++ b/tests/psr-worker-bench.php @@ -18,6 +18,7 @@ $psr7 = new RoadRunner\Http\PSR7Worker( while ($req = $psr7->waitRequest()) { try { + sleep(3); $resp = new \Nyholm\Psr7\Response(); $resp->getBody()->write("hello world"); -- cgit v1.2.3 From aa7af56539177a1e1c0c115ea7e5e2f6c65215f1 Mon Sep 17 00:00:00 2001 From: Valery Piashchynski Date: Wed, 15 Sep 2021 17:42:34 +0300 Subject: properly initialize configuration Add Init test Regenerate protofiles Signed-off-by: Valery Piashchynski --- .vscode/settings.json | 3 + plugins/grpc/config.go | 89 +++++++- plugins/grpc/plugin.go | 15 +- tests/plugins/grpc/configs/.rr-grpc-init.yaml | 20 +- tests/plugins/grpc/configs/external.proto | 19 ++ tests/plugins/grpc/configs/server.crt | 15 ++ tests/plugins/grpc/configs/server.key | 9 + tests/plugins/grpc/configs/test.pb.go | 291 ++++++++++++++++++++++++++ tests/plugins/grpc/configs/test.proto | 33 +++ tests/plugins/grpc/grpc_plugin_test.go | 86 +++++++- tests/plugins/grpc/plugin | Bin 0 -> 6022049 bytes 11 files changed, 566 insertions(+), 14 deletions(-) create mode 100644 tests/plugins/grpc/configs/external.proto create mode 100644 tests/plugins/grpc/configs/server.crt create mode 100644 tests/plugins/grpc/configs/server.key create mode 100644 tests/plugins/grpc/configs/test.pb.go create mode 100644 tests/plugins/grpc/configs/test.proto create mode 100755 tests/plugins/grpc/plugin diff --git a/.vscode/settings.json b/.vscode/settings.json index 5fba80db..e7762292 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -13,6 +13,7 @@ "hget", "hset", "INMEMORY", + "Itestdata", "memcachedkv", "memorykv", "mexpire", @@ -20,10 +21,12 @@ "prefetch", "proto", "protobuf", + "protoc", "SETEX", "shivammathur", "srem", "stretchr", + "tmpdir", "unsub", "Upgrader", "websockets", diff --git a/plugins/grpc/config.go b/plugins/grpc/config.go index 87bbf7ae..fedd4998 100644 --- a/plugins/grpc/config.go +++ b/plugins/grpc/config.go @@ -1,8 +1,12 @@ package grpc import ( + "math" + "os" + "strings" "time" + "github.com/spiral/errors" "github.com/spiral/roadrunner/v2/pkg/pool" ) @@ -13,9 +17,9 @@ type Config struct { TLS *TLS // Env is environment variables passed to the http pool - Env map[string]string + Env map[string]string `mapstructure:"env"` - GrpcPool pool.Config + GrpcPool *pool.Config `mapstructure:"pool"` MaxSendMsgSize int64 `mapstructure:"max_send_msg_size"` MaxRecvMsgSize int64 `mapstructure:"max_recv_msg_size"` MaxConnectionIdle time.Duration `mapstructure:"max_connection_idle"` @@ -32,8 +36,87 @@ type TLS struct { RootCA string } -func (c *Config) InitDefaults() { +func (c *Config) InitDefaults() error { //nolint:gocognit + const op = errors.Op("grpc_plugin_config") + if c.GrpcPool == nil { + c.GrpcPool = &pool.Config{} + } + + c.GrpcPool.InitDefaults() + + if !strings.Contains(c.Listen, ":") { + return errors.E(op, errors.Errorf("mailformed grpc grpc address, provided: %s", c.Listen)) + } + + if c.EnableTLS() { + if _, err := os.Stat(c.TLS.Key); err != nil { + if os.IsNotExist(err) { + return errors.E(op, errors.Errorf("key file '%s' does not exists", c.TLS.Key)) + } + + return errors.E(op, err) + } + + if _, err := os.Stat(c.TLS.Cert); err != nil { + if os.IsNotExist(err) { + return errors.E(op, errors.Errorf("cert file '%s' does not exists", c.TLS.Cert)) + } + + return errors.E(op, err) + } + + // RootCA is optional, but if provided - check it + if c.TLS.RootCA != "" { + if _, err := os.Stat(c.TLS.RootCA); err != nil { + if os.IsNotExist(err) { + return errors.E(op, errors.Errorf("root ca path provided, but key file '%s' does not exists", c.TLS.RootCA)) + } + return errors.E(op, err) + } + } + } + + // used to set max time + infinity := time.Duration(math.MaxInt64) + + if c.PingTime == 0 { + c.PingTime = time.Hour * 2 + } + + if c.Timeout == 0 { + c.Timeout = time.Second * 20 + } + + if c.MaxConcurrentStreams == 0 { + c.MaxConcurrentStreams = 10 + } + // set default + if c.MaxConnectionAge == 0 { + c.MaxConnectionAge = infinity + } + + // set default + if c.MaxConnectionIdle == 0 { + c.MaxConnectionIdle = infinity + } + + if c.MaxConnectionAgeGrace == 0 { + c.MaxConnectionAgeGrace = infinity + } + + if c.MaxRecvMsgSize == 0 { + c.MaxRecvMsgSize = 1024 * 1024 * 50 + } else { + c.MaxRecvMsgSize = 1024 * 1024 * c.MaxRecvMsgSize + } + + if c.MaxSendMsgSize == 0 { + c.MaxSendMsgSize = 1024 * 1024 * 50 + } else { + c.MaxSendMsgSize = 1024 * 1024 * c.MaxSendMsgSize + } + return nil } func (c *Config) EnableTLS() bool { diff --git a/plugins/grpc/plugin.go b/plugins/grpc/plugin.go index 579a00a4..7518d352 100644 --- a/plugins/grpc/plugin.go +++ b/plugins/grpc/plugin.go @@ -45,7 +45,12 @@ func (p *Plugin) Init(cfg config.Configurer, log logger.Logger, server server.Se // register the codec encoding.RegisterCodec(&codec.Codec{}) - err := cfg.UnmarshalKey(name, p.config) + err := cfg.UnmarshalKey(name, &p.config) + if err != nil { + return errors.E(op, err) + } + + err = p.config.InitDefaults() if err != nil { return errors.E(op, err) } @@ -57,9 +62,13 @@ func (p *Plugin) Init(cfg config.Configurer, log logger.Logger, server server.Se p.rrServer = server // worker's GRPC mode + if p.config.Env == nil { + p.config.Env = make(map[string]string) + } p.config.Env[RrGrpc] = "true" p.log = log + p.mu = &sync.RWMutex{} return nil } @@ -85,7 +94,6 @@ func (p *Plugin) Serve() chan error { go func() { var err error p.mu.Lock() - defer p.mu.Unlock() p.server, err = p.createGRPCserver() if err != nil { p.log.Error("create grpc server", "error", err) @@ -100,6 +108,7 @@ func (p *Plugin) Serve() chan error { } // protect serve + p.mu.Unlock() err = p.server.Serve(l) if err != nil { // skip errors when stopping the server @@ -121,7 +130,7 @@ func (p *Plugin) Stop() error { defer p.mu.Unlock() if p.server != nil { - p.server.GracefulStop() + p.server.Stop() } return nil } diff --git a/tests/plugins/grpc/configs/.rr-grpc-init.yaml b/tests/plugins/grpc/configs/.rr-grpc-init.yaml index 010e904e..b743a766 100644 --- a/tests/plugins/grpc/configs/.rr-grpc-init.yaml +++ b/tests/plugins/grpc/configs/.rr-grpc-init.yaml @@ -1,10 +1,18 @@ +rpc: + listen: "tcp://127.0.0.1:6001" + +server: + command: "php ../../psr-worker-bench.php" + relay: "pipes" + relay_timeout: "20s" + # GRPC service configuration grpc: # socket to listen listen: "tcp://localhost:9001" # proto root file - proto: "test.proto" + proto: "configs/test.proto" # max send limit (MB) max_send_msg_size: 50 @@ -36,7 +44,7 @@ grpc: # pings the client to see if the transport is still alive. # If set below 1s, a minimum value of 1s will be used instead. ping_time: 1s - + # After having pinged for keepalive check, the server waits for a duration # of Timeout and if no activity is seen even after that the connection is # closed. @@ -44,7 +52,7 @@ grpc: # Usual workers pool configuration pool: - num_workers: 2 - max_jobs: 0 - allocate_timeout: 60s - destroy_timeout: 60 + num_workers: 2 + max_jobs: 0 + allocate_timeout: 60s + destroy_timeout: 60 diff --git a/tests/plugins/grpc/configs/external.proto b/tests/plugins/grpc/configs/external.proto new file mode 100644 index 00000000..2bbe806e --- /dev/null +++ b/tests/plugins/grpc/configs/external.proto @@ -0,0 +1,19 @@ +syntax = "proto3"; +package tests; + +service External { + rpc Echo (Ping) returns (Pong) { + } + + rpc Empty (EmptyMessage) returns (EmptyMessage) { + + } +} + +message Ping { + int64 value = 1; +} + +message Pong { + int64 value = 1; +} \ No newline at end of file diff --git a/tests/plugins/grpc/configs/server.crt b/tests/plugins/grpc/configs/server.crt new file mode 100644 index 00000000..24d67fd7 --- /dev/null +++ b/tests/plugins/grpc/configs/server.crt @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE----- +MIICTTCCAdOgAwIBAgIJAOKyUd+llTRKMAoGCCqGSM49BAMCMGMxCzAJBgNVBAYT +AlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2Nv +MRMwEQYDVQQKDApSb2FkUnVubmVyMRIwEAYDVQQDDAlsb2NhbGhvc3QwHhcNMTgw +OTMwMTMzNDUzWhcNMjgwOTI3MTMzNDUzWjBjMQswCQYDVQQGEwJVUzETMBEGA1UE +CAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzETMBEGA1UECgwK +Um9hZFJ1bm5lcjESMBAGA1UEAwwJbG9jYWxob3N0MHYwEAYHKoZIzj0CAQYFK4EE +ACIDYgAEVnbShsM+l5RR3wfWWmGhzuFGwNzKCk7i9xyobDIyBUxG/UUSfj7KKlUX +puDnDEtF5xXcepl744CyIAYFLOXHb5WqI4jCOzG0o9f/00QQ4bQudJOdbqV910QF +C2vb7Fxro1MwUTAdBgNVHQ4EFgQU9xUexnbB6ORKayA7Pfjzs33otsAwHwYDVR0j +BBgwFoAU9xUexnbB6ORKayA7Pfjzs33otsAwDwYDVR0TAQH/BAUwAwEB/zAKBggq +hkjOPQQDAgNoADBlAjEAue3HhR/MUhxoa9tSDBtOJT3FYbDQswrsdqBTz97CGKst +e7XeZ3HMEvEXy0hGGEMhAjAqcD/4k9vViVppgWFtkk6+NFbm+Kw/QeeAiH5FgFSj +8xQcb+b7nPwNLp3JOkXkVd4= +-----END CERTIFICATE----- diff --git a/tests/plugins/grpc/configs/server.key b/tests/plugins/grpc/configs/server.key new file mode 100644 index 00000000..7501dd46 --- /dev/null +++ b/tests/plugins/grpc/configs/server.key @@ -0,0 +1,9 @@ +-----BEGIN EC PARAMETERS----- +BgUrgQQAIg== +-----END EC PARAMETERS----- +-----BEGIN EC PRIVATE KEY----- +MIGkAgEBBDCQP8utxNbHR6xZOLAJgUhn88r6IrPqmN0MsgGJM/jePB+T9UhkmIU8 +PMm2HeScbcugBwYFK4EEACKhZANiAARWdtKGwz6XlFHfB9ZaYaHO4UbA3MoKTuL3 +HKhsMjIFTEb9RRJ+PsoqVRem4OcMS0XnFdx6mXvjgLIgBgUs5cdvlaojiMI7MbSj +1//TRBDhtC50k51upX3XRAULa9vsXGs= +-----END EC PRIVATE KEY----- diff --git a/tests/plugins/grpc/configs/test.pb.go b/tests/plugins/grpc/configs/test.pb.go new file mode 100644 index 00000000..5f30ceb6 --- /dev/null +++ b/tests/plugins/grpc/configs/test.pb.go @@ -0,0 +1,291 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc v3.17.3 +// source: test.proto + +package __ + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type Message struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Msg string `protobuf:"bytes,1,opt,name=msg,proto3" json:"msg,omitempty"` +} + +func (x *Message) Reset() { + *x = Message{} + if protoimpl.UnsafeEnabled { + mi := &file_test_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Message) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Message) ProtoMessage() {} + +func (x *Message) ProtoReflect() protoreflect.Message { + mi := &file_test_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Message.ProtoReflect.Descriptor instead. +func (*Message) Descriptor() ([]byte, []int) { + return file_test_proto_rawDescGZIP(), []int{0} +} + +func (x *Message) GetMsg() string { + if x != nil { + return x.Msg + } + return "" +} + +type EmptyMessage struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *EmptyMessage) Reset() { + *x = EmptyMessage{} + if protoimpl.UnsafeEnabled { + mi := &file_test_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *EmptyMessage) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*EmptyMessage) ProtoMessage() {} + +func (x *EmptyMessage) ProtoReflect() protoreflect.Message { + mi := &file_test_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use EmptyMessage.ProtoReflect.Descriptor instead. +func (*EmptyMessage) Descriptor() ([]byte, []int) { + return file_test_proto_rawDescGZIP(), []int{1} +} + +type DetailsMessageForException struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Code uint64 `protobuf:"varint,1,opt,name=code,proto3" json:"code,omitempty"` + Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` +} + +func (x *DetailsMessageForException) Reset() { + *x = DetailsMessageForException{} + if protoimpl.UnsafeEnabled { + mi := &file_test_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DetailsMessageForException) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DetailsMessageForException) ProtoMessage() {} + +func (x *DetailsMessageForException) ProtoReflect() protoreflect.Message { + mi := &file_test_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DetailsMessageForException.ProtoReflect.Descriptor instead. +func (*DetailsMessageForException) Descriptor() ([]byte, []int) { + return file_test_proto_rawDescGZIP(), []int{2} +} + +func (x *DetailsMessageForException) GetCode() uint64 { + if x != nil { + return x.Code + } + return 0 +} + +func (x *DetailsMessageForException) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + +var File_test_proto protoreflect.FileDescriptor + +var file_test_proto_rawDesc = []byte{ + 0x0a, 0x0a, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x07, 0x73, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x22, 0x1b, 0x0a, 0x07, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x73, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6d, + 0x73, 0x67, 0x22, 0x0e, 0x0a, 0x0c, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x4d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x22, 0x4a, 0x0a, 0x1a, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x4d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x46, 0x6f, 0x72, 0x45, 0x78, 0x63, 0x65, 0x70, 0x74, 0x69, 0x6f, 0x6e, + 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, + 0x63, 0x6f, 0x64, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x32, 0xf6, + 0x01, 0x0a, 0x04, 0x54, 0x65, 0x73, 0x74, 0x12, 0x2c, 0x0a, 0x04, 0x45, 0x63, 0x68, 0x6f, 0x12, + 0x10, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x1a, 0x10, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x4d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x22, 0x00, 0x12, 0x2d, 0x0a, 0x05, 0x54, 0x68, 0x72, 0x6f, 0x77, 0x12, 0x10, + 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x1a, 0x10, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x22, 0x00, 0x12, 0x2b, 0x0a, 0x03, 0x44, 0x69, 0x65, 0x12, 0x10, 0x2e, 0x73, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x10, 0x2e, + 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, + 0x00, 0x12, 0x2c, 0x0a, 0x04, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x10, 0x2e, 0x73, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x10, 0x2e, 0x73, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x12, + 0x36, 0x0a, 0x04, 0x50, 0x69, 0x6e, 0x67, 0x12, 0x15, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x15, + 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x4d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x42, 0x05, 0x5a, 0x03, 0x2e, 0x2f, 0x3b, 0x62, 0x06, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_test_proto_rawDescOnce sync.Once + file_test_proto_rawDescData = file_test_proto_rawDesc +) + +func file_test_proto_rawDescGZIP() []byte { + file_test_proto_rawDescOnce.Do(func() { + file_test_proto_rawDescData = protoimpl.X.CompressGZIP(file_test_proto_rawDescData) + }) + return file_test_proto_rawDescData +} + +var file_test_proto_msgTypes = make([]protoimpl.MessageInfo, 3) +var file_test_proto_goTypes = []interface{}{ + (*Message)(nil), // 0: service.Message + (*EmptyMessage)(nil), // 1: service.EmptyMessage + (*DetailsMessageForException)(nil), // 2: service.DetailsMessageForException +} +var file_test_proto_depIdxs = []int32{ + 0, // 0: service.Test.Echo:input_type -> service.Message + 0, // 1: service.Test.Throw:input_type -> service.Message + 0, // 2: service.Test.Die:input_type -> service.Message + 0, // 3: service.Test.Info:input_type -> service.Message + 1, // 4: service.Test.Ping:input_type -> service.EmptyMessage + 0, // 5: service.Test.Echo:output_type -> service.Message + 0, // 6: service.Test.Throw:output_type -> service.Message + 0, // 7: service.Test.Die:output_type -> service.Message + 0, // 8: service.Test.Info:output_type -> service.Message + 1, // 9: service.Test.Ping:output_type -> service.EmptyMessage + 5, // [5:10] is the sub-list for method output_type + 0, // [0:5] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_test_proto_init() } +func file_test_proto_init() { + if File_test_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_test_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Message); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_test_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*EmptyMessage); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_test_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DetailsMessageForException); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_test_proto_rawDesc, + NumEnums: 0, + NumMessages: 3, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_test_proto_goTypes, + DependencyIndexes: file_test_proto_depIdxs, + MessageInfos: file_test_proto_msgTypes, + }.Build() + File_test_proto = out.File + file_test_proto_rawDesc = nil + file_test_proto_goTypes = nil + file_test_proto_depIdxs = nil +} diff --git a/tests/plugins/grpc/configs/test.proto b/tests/plugins/grpc/configs/test.proto new file mode 100644 index 00000000..2e1c90a9 --- /dev/null +++ b/tests/plugins/grpc/configs/test.proto @@ -0,0 +1,33 @@ +syntax = "proto3"; + +package service; +option go_package = "./;"; + +service Test { + rpc Echo (Message) returns (Message) { + } + + rpc Throw (Message) returns (Message) { + } + + rpc Die (Message) returns (Message) { + } + + rpc Info (Message) returns (Message) { + } + + rpc Ping (EmptyMessage) returns (EmptyMessage) { + } +} + +message Message { + string msg = 1; +} + +message EmptyMessage { +} + +message DetailsMessageForException { + uint64 code = 1; + string message = 2; +} \ No newline at end of file diff --git a/tests/plugins/grpc/grpc_plugin_test.go b/tests/plugins/grpc/grpc_plugin_test.go index 85dd5723..b92282f7 100644 --- a/tests/plugins/grpc/grpc_plugin_test.go +++ b/tests/plugins/grpc/grpc_plugin_test.go @@ -1,7 +1,89 @@ package grpc_test -import "testing" +import ( + "os" + "os/signal" + "sync" + "syscall" + "testing" + "time" -func GrpcInit(t *testing.T) { + endure "github.com/spiral/endure/pkg/container" + "github.com/spiral/roadrunner/v2/plugins/config" + "github.com/spiral/roadrunner/v2/plugins/grpc" + "github.com/spiral/roadrunner/v2/plugins/informer" + "github.com/spiral/roadrunner/v2/plugins/logger" + "github.com/spiral/roadrunner/v2/plugins/resetter" + rpcPlugin "github.com/spiral/roadrunner/v2/plugins/rpc" + "github.com/spiral/roadrunner/v2/plugins/server" + "github.com/stretchr/testify/assert" +) +func TestGrpcInit(t *testing.T) { + cont, err := endure.NewContainer(nil, endure.SetLogLevel(endure.ErrorLevel)) + assert.NoError(t, err) + + cfg := &config.Viper{ + Path: "configs/.rr-grpc-init.yaml", + Prefix: "rr", + } + + err = cont.RegisterAll( + cfg, + &grpc.Plugin{}, + &rpcPlugin.Plugin{}, + &logger.ZapLogger{}, + &server.Plugin{}, + &informer.Plugin{}, + &resetter.Plugin{}, + ) + assert.NoError(t, err) + + err = cont.Init() + if err != nil { + t.Fatal(err) + } + + ch, err := cont.Serve() + assert.NoError(t, err) + + sig := make(chan os.Signal, 1) + signal.Notify(sig, os.Interrupt, syscall.SIGINT, syscall.SIGTERM) + + wg := &sync.WaitGroup{} + wg.Add(1) + + stopCh := make(chan struct{}, 1) + + go func() { + defer wg.Done() + for { + select { + case e := <-ch: + assert.Fail(t, "error", e.Error.Error()) + err = cont.Stop() + if err != nil { + assert.FailNow(t, "error", err.Error()) + } + case <-sig: + err = cont.Stop() + if err != nil { + assert.FailNow(t, "error", err.Error()) + } + return + case <-stopCh: + // timeout + err = cont.Stop() + if err != nil { + assert.FailNow(t, "error", err.Error()) + } + return + } + } + }() + + time.Sleep(time.Second * 5) + stopCh <- struct{}{} + + wg.Wait() } diff --git a/tests/plugins/grpc/plugin b/tests/plugins/grpc/plugin new file mode 100755 index 00000000..48f92edb Binary files /dev/null and b/tests/plugins/grpc/plugin differ -- cgit v1.2.3 From 2aa0b99c588dd9c6a921904966ad435d485e23bb Mon Sep 17 00:00:00 2001 From: Valery Piashchynski Date: Wed, 15 Sep 2021 18:50:12 +0300 Subject: Fix websocket_plugin_test, remove sleep from the test worker Signed-off-by: Valery Piashchynski --- tests/plugins/grpc/plugin | Bin 6022049 -> 0 bytes tests/plugins/websockets/websocket_plugin_test.go | 2 +- tests/psr-worker-bench.php | 3 ++- 3 files changed, 3 insertions(+), 2 deletions(-) delete mode 100755 tests/plugins/grpc/plugin diff --git a/tests/plugins/grpc/plugin b/tests/plugins/grpc/plugin deleted file mode 100755 index 48f92edb..00000000 Binary files a/tests/plugins/grpc/plugin and /dev/null differ diff --git a/tests/plugins/websockets/websocket_plugin_test.go b/tests/plugins/websockets/websocket_plugin_test.go index bfdc980b..3e74ca59 100644 --- a/tests/plugins/websockets/websocket_plugin_test.go +++ b/tests/plugins/websockets/websocket_plugin_test.go @@ -660,7 +660,7 @@ func RPCWsPubAsync(port string) func(t *testing.T) { }() go func() { - messagesToVerify := make([]string, 0, 10) + messagesToVerify := make([]string, 0, 4) messagesToVerify = append(messagesToVerify, `{"topic":"@join","payload":["foo","foo2"]}`) messagesToVerify = append(messagesToVerify, `{"topic":"foo","payload":"hello, PHP"}`) messagesToVerify = append(messagesToVerify, `{"topic":"@leave","payload":["foo"]}`) diff --git a/tests/psr-worker-bench.php b/tests/psr-worker-bench.php index f8b1b47c..e809f380 100644 --- a/tests/psr-worker-bench.php +++ b/tests/psr-worker-bench.php @@ -1,7 +1,9 @@ waitRequest()) { try { - sleep(3); $resp = new \Nyholm\Psr7\Response(); $resp->getBody()->write("hello world"); -- cgit v1.2.3 From b24b4e066e32aa9be5617f409b3a01a358273cbb Mon Sep 17 00:00:00 2001 From: Valery Piashchynski Date: Thu, 16 Sep 2021 12:14:09 +0300 Subject: Add test PHP-GRPC server Signed-off-by: Valery Piashchynski --- plugins/grpc/codec/codec.go | 2 + tests/plugins/grpc/configs/server.crt | 15 ------ tests/plugins/grpc/configs/server.key | 9 ---- tests/plugins/grpc/php_server/.rr.yaml | 22 ++++++++ tests/plugins/grpc/php_server/composer.json | 23 +++++++++ tests/plugins/grpc/php_server/server.crt | 15 ++++++ tests/plugins/grpc/php_server/server.key | 9 ++++ tests/plugins/grpc/php_server/service.proto | 11 ++++ tests/plugins/grpc/php_server/src/EchoService.php | 17 +++++++ .../grpc/php_server/src/GPBMetadata/Service.php | 27 ++++++++++ .../grpc/php_server/src/Service/EchoInterface.php | 22 ++++++++ .../grpc/php_server/src/Service/Message.php | 58 ++++++++++++++++++++++ tests/plugins/grpc/php_server/worker.php | 26 ++++++++++ 13 files changed, 232 insertions(+), 24 deletions(-) delete mode 100644 tests/plugins/grpc/configs/server.crt delete mode 100644 tests/plugins/grpc/configs/server.key create mode 100644 tests/plugins/grpc/php_server/.rr.yaml create mode 100644 tests/plugins/grpc/php_server/composer.json create mode 100644 tests/plugins/grpc/php_server/server.crt create mode 100644 tests/plugins/grpc/php_server/server.key create mode 100644 tests/plugins/grpc/php_server/service.proto create mode 100644 tests/plugins/grpc/php_server/src/EchoService.php create mode 100644 tests/plugins/grpc/php_server/src/GPBMetadata/Service.php create mode 100644 tests/plugins/grpc/php_server/src/Service/EchoInterface.php create mode 100644 tests/plugins/grpc/php_server/src/Service/Message.php create mode 100644 tests/plugins/grpc/php_server/worker.php diff --git a/plugins/grpc/codec/codec.go b/plugins/grpc/codec/codec.go index d34cedf1..a9d89ac5 100644 --- a/plugins/grpc/codec/codec.go +++ b/plugins/grpc/codec/codec.go @@ -4,6 +4,8 @@ import "google.golang.org/grpc/encoding" type RawMessage []byte +// By default, gRPC registers and uses the "proto" codec, so it is not necessary to do this in your own code to send and receive proto messages. +// https://github.com/grpc/grpc-go/blob/master/Documentation/encoding.md#using-a-codec const cName string = "proto" const rm string = "rawMessage" diff --git a/tests/plugins/grpc/configs/server.crt b/tests/plugins/grpc/configs/server.crt deleted file mode 100644 index 24d67fd7..00000000 --- a/tests/plugins/grpc/configs/server.crt +++ /dev/null @@ -1,15 +0,0 @@ ------BEGIN CERTIFICATE----- -MIICTTCCAdOgAwIBAgIJAOKyUd+llTRKMAoGCCqGSM49BAMCMGMxCzAJBgNVBAYT -AlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2Nv -MRMwEQYDVQQKDApSb2FkUnVubmVyMRIwEAYDVQQDDAlsb2NhbGhvc3QwHhcNMTgw -OTMwMTMzNDUzWhcNMjgwOTI3MTMzNDUzWjBjMQswCQYDVQQGEwJVUzETMBEGA1UE -CAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzETMBEGA1UECgwK -Um9hZFJ1bm5lcjESMBAGA1UEAwwJbG9jYWxob3N0MHYwEAYHKoZIzj0CAQYFK4EE -ACIDYgAEVnbShsM+l5RR3wfWWmGhzuFGwNzKCk7i9xyobDIyBUxG/UUSfj7KKlUX -puDnDEtF5xXcepl744CyIAYFLOXHb5WqI4jCOzG0o9f/00QQ4bQudJOdbqV910QF -C2vb7Fxro1MwUTAdBgNVHQ4EFgQU9xUexnbB6ORKayA7Pfjzs33otsAwHwYDVR0j -BBgwFoAU9xUexnbB6ORKayA7Pfjzs33otsAwDwYDVR0TAQH/BAUwAwEB/zAKBggq -hkjOPQQDAgNoADBlAjEAue3HhR/MUhxoa9tSDBtOJT3FYbDQswrsdqBTz97CGKst -e7XeZ3HMEvEXy0hGGEMhAjAqcD/4k9vViVppgWFtkk6+NFbm+Kw/QeeAiH5FgFSj -8xQcb+b7nPwNLp3JOkXkVd4= ------END CERTIFICATE----- diff --git a/tests/plugins/grpc/configs/server.key b/tests/plugins/grpc/configs/server.key deleted file mode 100644 index 7501dd46..00000000 --- a/tests/plugins/grpc/configs/server.key +++ /dev/null @@ -1,9 +0,0 @@ ------BEGIN EC PARAMETERS----- -BgUrgQQAIg== ------END EC PARAMETERS----- ------BEGIN EC PRIVATE KEY----- -MIGkAgEBBDCQP8utxNbHR6xZOLAJgUhn88r6IrPqmN0MsgGJM/jePB+T9UhkmIU8 -PMm2HeScbcugBwYFK4EEACKhZANiAARWdtKGwz6XlFHfB9ZaYaHO4UbA3MoKTuL3 -HKhsMjIFTEb9RRJ+PsoqVRem4OcMS0XnFdx6mXvjgLIgBgUs5cdvlaojiMI7MbSj -1//TRBDhtC50k51upX3XRAULa9vsXGs= ------END EC PRIVATE KEY----- diff --git a/tests/plugins/grpc/php_server/.rr.yaml b/tests/plugins/grpc/php_server/.rr.yaml new file mode 100644 index 00000000..cc4a9300 --- /dev/null +++ b/tests/plugins/grpc/php_server/.rr.yaml @@ -0,0 +1,22 @@ +grpc: + listen: "tcp://:9001" + proto: "service.proto" + tls: + key: "server.key" + cert: "server.crt" + workers: + command: "php worker.php" + pool: + numWorkers: 4 + +metrics: + address: localhost:2112 + +limit: + interval: 1 + services: + grpc: + maxMemory: 100 + TTL: 0 + idleTTL: 0 + execTTL: 60 \ No newline at end of file diff --git a/tests/plugins/grpc/php_server/composer.json b/tests/plugins/grpc/php_server/composer.json new file mode 100644 index 00000000..b6303291 --- /dev/null +++ b/tests/plugins/grpc/php_server/composer.json @@ -0,0 +1,23 @@ +{ + "name": "app/example-grpc-server", + "description": "Example GRPC Server", + "repositories": [ + { + "type": "path", + "url": "../.." + } + ], + "require": { + "spiral/php-grpc": "*" + }, + "require-dev": { + "grpc/grpc": "^1.36" + }, + "autoload": { + "psr-4": { + "": "src" + } + }, + "minimum-stability": "dev", + "prefer-stable": true +} diff --git a/tests/plugins/grpc/php_server/server.crt b/tests/plugins/grpc/php_server/server.crt new file mode 100644 index 00000000..24d67fd7 --- /dev/null +++ b/tests/plugins/grpc/php_server/server.crt @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE----- +MIICTTCCAdOgAwIBAgIJAOKyUd+llTRKMAoGCCqGSM49BAMCMGMxCzAJBgNVBAYT +AlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2Nv +MRMwEQYDVQQKDApSb2FkUnVubmVyMRIwEAYDVQQDDAlsb2NhbGhvc3QwHhcNMTgw +OTMwMTMzNDUzWhcNMjgwOTI3MTMzNDUzWjBjMQswCQYDVQQGEwJVUzETMBEGA1UE +CAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzETMBEGA1UECgwK +Um9hZFJ1bm5lcjESMBAGA1UEAwwJbG9jYWxob3N0MHYwEAYHKoZIzj0CAQYFK4EE +ACIDYgAEVnbShsM+l5RR3wfWWmGhzuFGwNzKCk7i9xyobDIyBUxG/UUSfj7KKlUX +puDnDEtF5xXcepl744CyIAYFLOXHb5WqI4jCOzG0o9f/00QQ4bQudJOdbqV910QF +C2vb7Fxro1MwUTAdBgNVHQ4EFgQU9xUexnbB6ORKayA7Pfjzs33otsAwHwYDVR0j +BBgwFoAU9xUexnbB6ORKayA7Pfjzs33otsAwDwYDVR0TAQH/BAUwAwEB/zAKBggq +hkjOPQQDAgNoADBlAjEAue3HhR/MUhxoa9tSDBtOJT3FYbDQswrsdqBTz97CGKst +e7XeZ3HMEvEXy0hGGEMhAjAqcD/4k9vViVppgWFtkk6+NFbm+Kw/QeeAiH5FgFSj +8xQcb+b7nPwNLp3JOkXkVd4= +-----END CERTIFICATE----- diff --git a/tests/plugins/grpc/php_server/server.key b/tests/plugins/grpc/php_server/server.key new file mode 100644 index 00000000..7501dd46 --- /dev/null +++ b/tests/plugins/grpc/php_server/server.key @@ -0,0 +1,9 @@ +-----BEGIN EC PARAMETERS----- +BgUrgQQAIg== +-----END EC PARAMETERS----- +-----BEGIN EC PRIVATE KEY----- +MIGkAgEBBDCQP8utxNbHR6xZOLAJgUhn88r6IrPqmN0MsgGJM/jePB+T9UhkmIU8 +PMm2HeScbcugBwYFK4EEACKhZANiAARWdtKGwz6XlFHfB9ZaYaHO4UbA3MoKTuL3 +HKhsMjIFTEb9RRJ+PsoqVRem4OcMS0XnFdx6mXvjgLIgBgUs5cdvlaojiMI7MbSj +1//TRBDhtC50k51upX3XRAULa9vsXGs= +-----END EC PRIVATE KEY----- diff --git a/tests/plugins/grpc/php_server/service.proto b/tests/plugins/grpc/php_server/service.proto new file mode 100644 index 00000000..60ff84a9 --- /dev/null +++ b/tests/plugins/grpc/php_server/service.proto @@ -0,0 +1,11 @@ +syntax = "proto3"; +package service; + +service Echo { + rpc Ping (Message) returns (Message) { + } +} + +message Message { + string msg = 1; +} \ No newline at end of file diff --git a/tests/plugins/grpc/php_server/src/EchoService.php b/tests/plugins/grpc/php_server/src/EchoService.php new file mode 100644 index 00000000..c2707811 --- /dev/null +++ b/tests/plugins/grpc/php_server/src/EchoService.php @@ -0,0 +1,17 @@ +setMsg(strtoupper($in->getMsg())); + } +} \ No newline at end of file diff --git a/tests/plugins/grpc/php_server/src/GPBMetadata/Service.php b/tests/plugins/grpc/php_server/src/GPBMetadata/Service.php new file mode 100644 index 00000000..c1b65b21 --- /dev/null +++ b/tests/plugins/grpc/php_server/src/GPBMetadata/Service.php @@ -0,0 +1,27 @@ +internalAddGeneratedFile(hex2bin( + "0a6e0a0d736572766963652e70726f746f12077365727669636522160a07" . + "4d657373616765120b0a036d736718012001280932340a044563686f122c" . + "0a0450696e6712102e736572766963652e4d6573736167651a102e736572" . + "766963652e4d6573736167652200620670726f746f33" + )); + + static::$is_initialized = true; + } +} + diff --git a/tests/plugins/grpc/php_server/src/Service/EchoInterface.php b/tests/plugins/grpc/php_server/src/Service/EchoInterface.php new file mode 100644 index 00000000..5f336ace --- /dev/null +++ b/tests/plugins/grpc/php_server/src/Service/EchoInterface.php @@ -0,0 +1,22 @@ +service.Message + */ +class Message extends \Google\Protobuf\Internal\Message +{ + /** + * Generated from protobuf field string msg = 1; + */ + private $msg = ''; + + /** + * Constructor. + * + * @param array $data { + * Optional. Data for populating the Message object. + * + * @type string $msg + * } + */ + public function __construct($data = NULL) { + \GPBMetadata\Service::initOnce(); + parent::__construct($data); + } + + /** + * Generated from protobuf field string msg = 1; + * @return string + */ + public function getMsg() + { + return $this->msg; + } + + /** + * Generated from protobuf field string msg = 1; + * @param string $var + * @return $this + */ + public function setMsg($var) + { + GPBUtil::checkString($var, True); + $this->msg = $var; + + return $this; + } + +} + diff --git a/tests/plugins/grpc/php_server/worker.php b/tests/plugins/grpc/php_server/worker.php new file mode 100644 index 00000000..683a2341 --- /dev/null +++ b/tests/plugins/grpc/php_server/worker.php @@ -0,0 +1,26 @@ + false, // optional (default: false) +]); + +$server->registerService(EchoInterface::class, new EchoService()); + +$worker = \method_exists(Worker::class, 'create') + // RoadRunner >= 2.x + ? Worker::create() + // RoadRunner 1.x + : new Worker(new StreamRelay(STDIN, STDOUT)) +; + +$server->serve($worker); -- cgit v1.2.3 From cc56349f3ad19aa54ae7900c50e018d757305804 Mon Sep 17 00:00:00 2001 From: Valery Piashchynski Date: Thu, 16 Sep 2021 20:33:22 +0300 Subject: Update php worker Signed-off-by: Valery Piashchynski --- tests/plugins/grpc/php_server/worker-grpc.php | 26 ++++++++++++++++++++++++++ tests/plugins/grpc/php_server/worker.php | 26 -------------------------- 2 files changed, 26 insertions(+), 26 deletions(-) create mode 100644 tests/plugins/grpc/php_server/worker-grpc.php delete mode 100644 tests/plugins/grpc/php_server/worker.php diff --git a/tests/plugins/grpc/php_server/worker-grpc.php b/tests/plugins/grpc/php_server/worker-grpc.php new file mode 100644 index 00000000..683a2341 --- /dev/null +++ b/tests/plugins/grpc/php_server/worker-grpc.php @@ -0,0 +1,26 @@ + false, // optional (default: false) +]); + +$server->registerService(EchoInterface::class, new EchoService()); + +$worker = \method_exists(Worker::class, 'create') + // RoadRunner >= 2.x + ? Worker::create() + // RoadRunner 1.x + : new Worker(new StreamRelay(STDIN, STDOUT)) +; + +$server->serve($worker); diff --git a/tests/plugins/grpc/php_server/worker.php b/tests/plugins/grpc/php_server/worker.php deleted file mode 100644 index 683a2341..00000000 --- a/tests/plugins/grpc/php_server/worker.php +++ /dev/null @@ -1,26 +0,0 @@ - false, // optional (default: false) -]); - -$server->registerService(EchoInterface::class, new EchoService()); - -$worker = \method_exists(Worker::class, 'create') - // RoadRunner >= 2.x - ? Worker::create() - // RoadRunner 1.x - : new Worker(new StreamRelay(STDIN, STDOUT)) -; - -$server->serve($worker); -- cgit v1.2.3