diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100755 index 0000000..ae68d57 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,40 @@ +--- +name: Bug report +about: Create a report to help us improve +title: "[BUG]" +labels: B-bug, F-need-verification +assignees: rustatian + +--- + +--- +name: Bug Report +about: Issue in HTTP module +labels: A-network +--- + + +I tried this code: + +```go + +``` + +I expected to see this happen: *explanation* + +Instead, this happened: *explanation* + +The version of RR used: *explanation* + +My `.rr.yaml` configuration is: *config* + +Errortrace, Backtrace or Panictrace +``` + +``` + +

+ diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100755 index 0000000..31649fd --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: "[FEATURE REQUEST]" +labels: C-feature-request +assignees: rustatian + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..dac8ce1 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,16 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + +version: 2 +updates: + - package-ecosystem: gomod # See documentation for possible values + directory: "/" # Location of package manifests + schedule: + interval: daily + + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: daily diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..c346785 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,24 @@ +# Reason for This PR + +`[Author TODO: add issue # or explain reasoning.]` + +## Description of Changes + +`[Author TODO: add description of changes.]` + +## License Acceptance + +By submitting this pull request, I confirm that my contribution is made under +the terms of the MIT license. + +## PR Checklist + +`[Author TODO: Meet these criteria.]` +`[Reviewer TODO: Verify that these criteria are met. Request changes if not]` + +- [ ] All commits in this PR are signed (`git commit -s`). +- [ ] The reason for this PR is clearly provided (issue no. or explanation). +- [ ] The description of changes is clear and encompassing. +- [ ] Any required documentation changes (code and docs) are included in this PR. +- [ ] Any user-facing changes are mentioned in `CHANGELOG.md`. +- [ ] All added/changed functionality is tested. diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 7e8ead3..05ca036 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -7,12 +7,12 @@ name: "CodeQL" on: push: - branches: [master] + branches: [ master ] pull_request: # The branches below must be a subset of the branches above - branches: [master] + branches: [ master ] schedule: - - cron: '0 12 * * 6' + - cron: '0 15 * * 6' jobs: analyze: @@ -24,48 +24,43 @@ jobs: matrix: # Override automatic language detection by changing the below list # Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python'] - language: ['go'] + language: [ 'go' ] # Learn more... # https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection steps: - - name: Checkout repository - uses: actions/checkout@v2 - with: - # We must fetch at least the immediate parents so that if this is - # a pull request then we can checkout the head. - fetch-depth: 2 + - name: Checkout repository + uses: actions/checkout@v2 + with: + # We must fetch at least the immediate parents so that if this is + # a pull request then we can checkout the head. + fetch-depth: 2 - # If this run was triggered by a pull request event, then checkout - # the head of the pull request instead of the merge commit. - - run: git checkout HEAD^2 - if: ${{ github.event_name == 'pull_request' }} + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v1 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + # queries: ./path/to/local/query, your-org/your-repo/queries@main - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v1 - with: - languages: ${{ matrix.language }} - # If you wish to specify custom queries, you can do so here or in a config file. - # By default, queries listed here will override any specified in a config file. - # Prefix the list here with "+" to use these queries and those in the config file. - # queries: ./path/to/local/query, your-org/your-repo/queries@main + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v1 - # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). - # If this step fails, then you should remove it and run the build manually (see below) - - name: Autobuild - uses: github/codeql-action/autobuild@v1 + # ℹī¸ Command-line programs to run using the OS shell. + # 📚 https://git.io/JvXDl - # ℹī¸ Command-line programs to run using the OS shell. - # 📚 https://git.io/JvXDl + # ✏ī¸ If the Autobuild fails above, remove it and uncomment the following three lines + # and modify them (or add more) to build your code if your project + # uses a compiled language - # ✏ī¸ If the Autobuild fails above, remove it and uncomment the following three lines - # and modify them (or add more) to build your code if your project - # uses a compiled language + #- run: | + # make bootstrap + # make release - #- run: | - # make bootstrap - # make release - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v1 diff --git a/.github/workflows/linters.yml b/.github/workflows/linters.yml new file mode 100644 index 0000000..0c7620d --- /dev/null +++ b/.github/workflows/linters.yml @@ -0,0 +1,18 @@ +name: Linters + +on: [push, pull_request] + +jobs: + golangci-lint: + name: Golang-CI (lint) + runs-on: ubuntu-latest + steps: + - name: Check out code + uses: actions/checkout@v2 + + - name: Run linter + uses: golangci/golangci-lint-action@v2 # Action page: + with: + version: v1.43 # without patch version + only-new-issues: false # show only new issues if it's a pull request + args: --timeout=10m diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml new file mode 100644 index 0000000..01cffa1 --- /dev/null +++ b/.github/workflows/linux.yml @@ -0,0 +1,85 @@ +name: Linux + +on: + push: + branches: + - master + - beta + - stable + tags-ignore: + - "**" + paths-ignore: + - "**.md" + - "**.yaml" + - "**.yml" + pull_request: + paths-ignore: + - "**.md" + - "**.yaml" + - "**.yml" + +jobs: + golang: + name: Build (Go ${{ matrix.go }}, PHP ${{ matrix.php }}, OS ${{matrix.os}}) + runs-on: ${{ matrix.os }} + timeout-minutes: 60 + strategy: + fail-fast: true + matrix: + php: ["7.4", "8.0", "8.1"] + go: ["1.17.4"] + os: ["ubuntu-latest"] + steps: + - name: Set up Go ${{ matrix.go }} + uses: actions/setup-go@v2 # action page: + with: + go-version: ${{ matrix.go }} + + - name: Set up PHP ${{ matrix.php }} + uses: shivammathur/setup-php@v2 # action page: + with: + php-version: ${{ matrix.php }} + extensions: sockets, grpc + + - name: Check out code + uses: actions/checkout@v2 + + - name: Get Composer Cache Directory + id: composer-cache + run: echo "::set-output name=dir::$(composer config cache-files-dir)" + + - name: Init Composer Cache # Docs: + uses: actions/cache@v2 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ matrix.php }}-${{ hashFiles('**/composer.json') }} + restore-keys: ${{ runner.os }}-composer- + + - name: Install Composer dependencies + run: composer update --prefer-dist --no-progress --ansi + + - name: Init Go modules Cache # Docs: + uses: actions/cache@v2 + with: + path: ~/go/pkg/mod + key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} + restore-keys: ${{ runner.os }}-go- + + - name: Install Go dependencies + run: go mod download + + - name: Go mod tidy + run: go mod tidy + + - name: Install protoc + uses: arduino/setup-protoc@v1 + with: + version: '3.x' + + - name: Run golang tests with coverage + run: make test + + - uses: codecov/codecov-action@v2 # Docs: + with: + file: ./coverage-ci/summary.txt + fail_ci_if_error: false diff --git a/.gitignore b/.gitignore index bd558f8..39e94d9 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,5 @@ example/vendor/ go.sum builds/ .phpunit.result.cache +vendor_php +coverage-ci diff --git a/.golangci.yml b/.golangci.yml index 4e7e916..37b764c 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,49 +1,87 @@ -linters: - # please, do not use `enable-all`: it's deprecated and will be removed soon. - # inverted configuration with `enable-all` and `disable` is not scalable during updates of golangci-lint +# Documentation: + +run: + timeout: 1m + skip-dirs: + - .github + - .git + - tests + - src + - vendor_php + skip-files: + - condec_test.go + - config_test.go + - proxy_test.go + - rpc_test.go + - service_test.go + modules-download-mode: readonly + allow-parallel-runners: true + +output: + format: colored-line-number # colored-line-number|line-number|json|tab|checkstyle|code-climate + +linters-settings: + govet: + check-shadowing: true + revive: + confidence: 0.8 + errorCode: 0 + warningCode: 0 + gocyclo: + min-complexity: 15 + godot: + scope: declarations + capital: true + dupl: + threshold: 100 + goconst: + min-len: 2 + min-occurrences: 3 + misspell: + locale: US + lll: + line-length: 120 + prealloc: + simple: true + range-loops: true + for-loops: true + nolintlint: + allow-leading-space: false + require-specific: true + +linters: # All available linters list: disable-all: true enable: - - bodyclose - - depguard - - dogsled - - dupl - - gochecknoinits - - goconst - - gocritic - - gocyclo - - gofmt - - goimports - # - golint - - goprintffuncname - # - gosec - # - gosimple - - govet - - ineffassign - - interfacer - - misspell - - nakedret - - nolintlint - - rowserrcheck - - scopelint - - staticcheck - - structcheck - - stylecheck - - typecheck - - unconvert - - unparam - # - unused - - varcheck - - whitespace - - # don't enable: - # - asciicheck - # - gochecknoglobals - # - gocognit - # - godot - # - godox - # - goerr113 - # - maligned - # - nestif - # - prealloc - # - testpackage - # - wsl + - asciicheck # Simple linter to check that your code does not contain non-ASCII identifiers + - bodyclose # Checks whether HTTP response body is closed successfully + - deadcode # Finds unused code + - depguard # Go linter that checks if package imports are in a list of acceptable packages + - dogsled # Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f()) + - errcheck # Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases + - exhaustive # check exhaustiveness of enum switch statements + - exportloopref # checks for pointers to enclosing loop variables + - goconst # Finds repeated strings that could be replaced by a constant + - gocritic # The most opinionated Go source code linter + - gofmt # Gofmt checks whether code was gofmt-ed. By default this tool runs with -s option to check for code simplification + - goimports # Goimports does everything that gofmt does. Additionally it checks unused imports + - revive # Golint differs from gofmt. Gofmt reformats Go source code, whereas golint prints out style mistakes + - goprintffuncname # Checks that printf-like functions are named with `f` at the end + - gosec # Inspects source code for security problems + - gosimple # Linter for Go source code that specializes in simplifying a code + - govet # Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string + - ineffassign # Detects when assignments to existing variables are not used + - misspell # Finds commonly misspelled English words in comments + - nakedret # Finds naked returns in functions greater than a specified function length + - noctx # finds sending http request without context.Context + - nolintlint # Reports ill-formed or insufficient nolint directives + - prealloc # Finds slice declarations that could potentially be preallocated + - rowserrcheck # Checks whether Err of rows is checked successfully + - staticcheck # Staticcheck is a go vet on steroids, applying a ton of static analysis checks + - structcheck # Finds unused struct fields + - stylecheck # Stylecheck is a replacement for golint + - tparallel # detects inappropriate usage of t.Parallel() method in your Go test codes + - unconvert # Remove unnecessary type conversions + - unused # Checks Go code for unused constants, variables, functions and types + - varcheck # Finds unused global variables and constants + - gochecknoglobals + - whitespace # Tool for detection of leading and trailing whitespace diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index b63f434..0000000 --- a/.travis.yml +++ /dev/null @@ -1,72 +0,0 @@ -dist: xenial -language: go -sudo: required - -go: - - "1.12.x" - -install: - - PROTOBUF_VERSION=3.7.0 - - PROTOC_FILENAME=protoc-${PROTOBUF_VERSION}-linux-x86_64.zip - - pushd /home/travis - - wget https://github.com/google/protobuf/releases/download/v${PROTOBUF_VERSION}/${PROTOC_FILENAME} - - unzip ${PROTOC_FILENAME} - - bin/protoc --version - - popd - - export GO111MODULE=on - - go mod download - - composer install --no-interaction --prefer-source - - go get - -script: - - go test -race -v -coverprofile=grpc.txt -covermode=atomic - - go test ./parser -race -v -coverprofile=parser.txt -covermode=atomic - - go test -race -v -coverprofile=protoc-gen-php-grpc.txt ./cmd/protoc-gen-php-grpc -covermode=atomic - - vendor/bin/spiral-cs check src tests/GRPC - - vendor/bin/phpunit --coverage-clover=coverage.xml - - go build cmd/protoc-gen-php-grpc/main.go - - go build cmd/rr-grpc/main.go - -after_success: - - bash <(curl -s https://codecov.io/bash) -f grpc.txt - - bash <(curl -s https://codecov.io/bash) -f parser.txt - - bash <(curl -s https://codecov.io/bash) -f coverage.xml - -jobs: - include: - - stage: Test - env: "PHP=7.2" - before_install: - - sudo add-apt-repository -y ppa:ondrej/php - - sudo apt-get update - - sudo apt-get install -y php7.2-cli php7.2-xml php7.2-xdebug - - sudo cp `which php7.2` `which php` - - php -v - - composer self-update - - stage: Test - env: "PHP=7.3" - before_install: - - sudo add-apt-repository -y ppa:ondrej/php - - sudo apt-get update - - sudo apt-get install -y php7.3-cli php7.3-xml php7.3-xdebug - - sudo cp `which php7.3` `which php` - - php -v - - composer self-update - - stage: Test - env: "PHP=7.4" - before_install: - - sudo add-apt-repository -y ppa:ondrej/php - - sudo apt-get update - - sudo apt-get install -y php7.4-cli php7.4-xml php7.4-xdebug - - sudo cp `which php7.4` `which php` - - php -v - - composer self-update - - stage: Test - env: "PHP=8.0" - before_install: - - sudo add-apt-repository -y ppa:ondrej/php - - sudo apt-get update - - sudo apt-get install -y php8.0-cli php8.0-xml php8.0-xdebug - - sudo cp `which php8.0` `which php` - - php -v - - composer self-update diff --git a/Makefile b/Makefile index 1df2eb4..c86ba2b 100644 --- a/Makefile +++ b/Makefile @@ -12,11 +12,17 @@ uninstall: rm -f /usr/local/bin/protoc-gen-php-grpc rm -f /usr/local/bin/rr-grpc test: - composer update - go test -v -race -cover - go test -v -race -cover ./parser - go test -v -race -cover ./cmd/protoc-gen-php-grpc - vendor/bin/phpunit + rm -rf coverage-ci + mkdir ./coverage-ci + + go test -v -race -cover -tags=debug -coverpkg=./... -failfast -coverprofile=./coverage-ci/root.out -covermode=atomic . + go test -v -race -cover -tags=debug -coverpkg=./... -failfast -coverprofile=./coverage-ci/parser.out -covermode=atomic ./parser + go test -v -race -cover -tags=debug -coverpkg=./... -failfast -coverprofile=./coverage-ci/gen.out -covermode=atomic ./cmd/protoc-gen-php-grpc + echo 'mode: atomic' > ./coverage-ci/summary.txt + tail -q -n +2 ./coverage-ci/*.out >> ./coverage-ci/summary.txt + + vendor_php/bin/phpunit + lint: go fmt ./... golint ./... \ No newline at end of file diff --git a/README.md b/README.md index 124c270..00478fd 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,8 @@ PHP-GRPC ================================= [![Latest Stable Version](https://poser.pugx.org/spiral/php-grpc/version)](https://packagist.org/packages/spiral/php-grpc) [![GoDoc](https://godoc.org/github.com/spiral/php-grpc?status.svg)](https://godoc.org/github.com/spiral/php-grpc) -[![Build Status](https://travis-ci.org/spiral/php-grpc.svg?branch=master)](https://travis-ci.org/spiral/php-grpc) +[![Tests](https://github.com/spiral/roadrunner-plugins/actions)](https://github.com/spiral/roadrunner-plugins/workflows/Linux/badge.svg) +[![Linters](https://github.com/spiral/roadrunner-plugins/actions)](https://github.com/spiral/roadrunner-plugins/workflows/Linters/badge.svg) [![Go Report Card](https://goreportcard.com/badge/github.com/spiral/php-grpc)](https://goreportcard.com/report/github.com/spiral/php-grpc) [![Total alerts](https://img.shields.io/lgtm/alerts/g/spiral/php-grpc.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/spiral/php-grpc/alerts/) [![Codecov](https://codecov.io/gh/spiral/php-grpc/branch/master/graph/badge.svg)](https://codecov.io/gh/spiral/php-grpc/) @@ -10,6 +11,10 @@ PHP-GRPC PHP-GRPC is an open-source (MIT) high-performance PHP [GRPC](https://grpc.io/) server build at top of [RoadRunner](https://github.com/spiral/roadrunner). Server support both PHP and Golang services running within one application. +Note: +------- +For the RoadRunner v2, please use the [RR-GRPC](https://github.com/spiral/roadrunner-grpc) library. + Documentation: -------- * [Installation and Configuration](https://spiral.dev/docs/grpc-configuration) diff --git a/build.sh b/build.sh index f7bed77..cfff2bc 100755 --- a/build.sh +++ b/build.sh @@ -2,7 +2,7 @@ cd $(dirname "${BASH_SOURCE[0]}") OD="$(pwd)" # Pushes application version into the build information. -RR_VERSION=1.4.1 +RR_VERSION=1.6.0 # Hardcode some values to the core package LDFLAGS="$LDFLAGS -X github.com/spiral/roadrunner/cmd/rr/cmd.Version=${RR_VERSION}" diff --git a/cmd/protoc-gen-php-grpc/main.go b/cmd/protoc-gen-php-grpc/main.go index 881646f..4f048fc 100644 --- a/cmd/protoc-gen-php-grpc/main.go +++ b/cmd/protoc-gen-php-grpc/main.go @@ -27,9 +27,9 @@ import ( "io/ioutil" "os" - plugin "github.com/golang/protobuf/protoc-gen-go/plugin" "github.com/spiral/php-grpc/cmd/protoc-gen-php-grpc/php" "google.golang.org/protobuf/proto" + plugin "google.golang.org/protobuf/types/pluginpb" ) func main() { diff --git a/cmd/protoc-gen-php-grpc/php/generate.go b/cmd/protoc-gen-php-grpc/php/generate.go index 608b275..03c48ac 100644 --- a/cmd/protoc-gen-php-grpc/php/generate.go +++ b/cmd/protoc-gen-php-grpc/php/generate.go @@ -23,8 +23,8 @@ package php import ( - "github.com/golang/protobuf/protoc-gen-go/descriptor" - plugin "github.com/golang/protobuf/protoc-gen-go/plugin" + desc "google.golang.org/protobuf/types/descriptorpb" + plugin "google.golang.org/protobuf/types/pluginpb" ) // Generate generates needed service classes @@ -42,8 +42,8 @@ func Generate(req *plugin.CodeGeneratorRequest) *plugin.CodeGeneratorResponse { func generate( req *plugin.CodeGeneratorRequest, - file *descriptor.FileDescriptorProto, - service *descriptor.ServiceDescriptorProto, + file *desc.FileDescriptorProto, + service *desc.ServiceDescriptorProto, ) *plugin.CodeGeneratorResponse_File { return &plugin.CodeGeneratorResponse_File{ Name: str(filename(file, service.Name)), diff --git a/cmd/protoc-gen-php-grpc/php/keywords.go b/cmd/protoc-gen-php-grpc/php/keywords.go index d4a00c0..011b331 100644 --- a/cmd/protoc-gen-php-grpc/php/keywords.go +++ b/cmd/protoc-gen-php-grpc/php/keywords.go @@ -25,12 +25,12 @@ package php import ( "bytes" "strings" - - "github.com/c9s/inflect" + "unicode" ) // @see https://github.com/protocolbuffers/protobuf/blob/master/php/ext/google/protobuf/protobuf.c#L168 -var reservedKeywords = []string{ +// immutable +var reservedKeywords = []string{ //nolint:gochecknoglobals "abstract", "and", "array", "as", "break", "callable", "case", "catch", "class", "clone", "const", "continue", "declare", "default", "die", @@ -77,9 +77,9 @@ func namespace(pkg *string, sep string) string { // create php identifier for class or message func identifier(name string, suffix string) string { - name = inflect.Camelize(name) + name = Camelize(name) if suffix != "" { - return name + inflect.Camelize(suffix) + return name + Camelize(suffix) } return name @@ -95,3 +95,46 @@ func resolveReserved(identifier string, pkg string) string { 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/cmd/protoc-gen-php-grpc/php/ns.go b/cmd/protoc-gen-php-grpc/php/ns.go index 75c6964..c1dc389 100644 --- a/cmd/protoc-gen-php-grpc/php/ns.go +++ b/cmd/protoc-gen-php-grpc/php/ns.go @@ -5,8 +5,8 @@ import ( "fmt" "strings" - "github.com/golang/protobuf/protoc-gen-go/descriptor" - plugin "github.com/golang/protobuf/protoc-gen-go/plugin" + desc "google.golang.org/protobuf/types/descriptorpb" + plugin "google.golang.org/protobuf/types/pluginpb" ) // manages internal name representation of the package @@ -22,11 +22,7 @@ type ns struct { } // newNamespace creates new work namespace. -func newNamespace( - req *plugin.CodeGeneratorRequest, - file *descriptor.FileDescriptorProto, - service *descriptor.ServiceDescriptorProto, -) *ns { +func newNamespace(req *plugin.CodeGeneratorRequest, file *desc.FileDescriptorProto, service *desc.ServiceDescriptorProto) *ns { ns := &ns{ Package: *file.Package, Namespace: namespace(file.Package, "\\"), @@ -37,9 +33,9 @@ func newNamespace( ns.Namespace = *file.Options.PhpNamespace } - for _, m := range service.Method { - ns.importMessage(req, m.InputType) - ns.importMessage(req, m.OutputType) + for k := range service.Method { + ns.importMessage(req, service.Method[k].InputType) + ns.importMessage(req, service.Method[k].OutputType) } return ns diff --git a/cmd/protoc-gen-php-grpc/php/template.go b/cmd/protoc-gen-php-grpc/php/template.go index 70c2f97..e00c6fd 100644 --- a/cmd/protoc-gen-php-grpc/php/template.go +++ b/cmd/protoc-gen-php-grpc/php/template.go @@ -28,8 +28,8 @@ import ( "strings" "text/template" - "github.com/golang/protobuf/protoc-gen-go/descriptor" - plugin "github.com/golang/protobuf/protoc-gen-go/plugin" + desc "google.golang.org/protobuf/types/descriptorpb" + plugin "google.golang.org/protobuf/types/pluginpb" ) const phpBody = `%s", st.Code().String()) + default: + return util.Sprintf("%s", st.Code().String()) } - - return util.Sprintf("%s", st.Code().String()) } func getPeer(ctx context.Context) string { diff --git a/cmd/rr-grpc/grpc/workers.go b/cmd/rr-grpc/grpc/workers.go index 1e6179d..f221e46 100644 --- a/cmd/rr-grpc/grpc/workers.go +++ b/cmd/rr-grpc/grpc/workers.go @@ -35,8 +35,8 @@ import ( ) var ( - interactive bool - stopSignal = make(chan os.Signal, 1) + interactive bool //nolint:gochecknoglobals + stopSignal = make(chan os.Signal, 1) //nolint:gochecknoglobals ) func init() { diff --git a/cmd/rr-grpc/main.go b/cmd/rr-grpc/main.go index 4523cc9..31afb28 100644 --- a/cmd/rr-grpc/main.go +++ b/cmd/rr-grpc/main.go @@ -6,7 +6,7 @@ import ( "github.com/spiral/roadrunner/service/metrics" "github.com/spiral/roadrunner/service/rpc" - "github.com/spiral/php-grpc" + grpc "github.com/spiral/php-grpc" // grpc specific commands _ "github.com/spiral/php-grpc/cmd/rr-grpc/grpc" diff --git a/codec.go b/codec.go index b8b8cb0..1112600 100644 --- a/codec.go +++ b/codec.go @@ -6,32 +6,38 @@ import ( type rawMessage []byte +const CodecName string = "proto" + func (r rawMessage) Reset() {} func (rawMessage) ProtoMessage() {} func (rawMessage) String() string { return "rawMessage" } -type codec struct{ base encoding.Codec } +type Codec struct{ Base encoding.Codec } + +func (c *Codec) Name() string { + return CodecName +} // Marshal returns the wire format of v. rawMessages would be returned without encoding. -func (c *codec) Marshal(v interface{}) ([]byte, error) { +func (c *Codec) Marshal(v interface{}) ([]byte, error) { if raw, ok := v.(rawMessage); ok { return raw, nil } - return c.base.Marshal(v) + 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 { +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) + return c.Base.Unmarshal(data, v) } -// String return codec name. -func (c *codec) String() string { - return "raw:" + c.base.Name() +// String return Codec name. +func (c *Codec) String() string { + return "raw:" + c.Base.Name() } diff --git a/codec_test.go b/codec_test.go index 50b5f98..08bc030 100644 --- a/codec_test.go +++ b/codec_test.go @@ -22,7 +22,7 @@ func (jsonCodec) Name() string { } func TestCodec_String(t *testing.T) { - c := codec{jsonCodec{}} + c := Codec{jsonCodec{}} assert.Equal(t, "raw:json", c.String()) @@ -33,7 +33,7 @@ func TestCodec_String(t *testing.T) { } func TestCodec_Unmarshal_ByPass(t *testing.T) { - c := codec{jsonCodec{}} + c := Codec{jsonCodec{}} s := struct { Name string @@ -44,7 +44,7 @@ func TestCodec_Unmarshal_ByPass(t *testing.T) { } func TestCodec_Marshal_ByPass(t *testing.T) { - c := codec{jsonCodec{}} + c := Codec{jsonCodec{}} s := struct { Name string @@ -59,7 +59,7 @@ func TestCodec_Marshal_ByPass(t *testing.T) { } func TestCodec_Unmarshal_Raw(t *testing.T) { - c := codec{jsonCodec{}} + c := Codec{jsonCodec{}} s := rawMessage{} @@ -68,7 +68,7 @@ func TestCodec_Unmarshal_Raw(t *testing.T) { } func TestCodec_Marshal_Raw(t *testing.T) { - c := codec{jsonCodec{}} + c := Codec{jsonCodec{}} s := rawMessage(`{"Name":"name"}`) diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 0000000..36572c8 --- /dev/null +++ b/codecov.yml @@ -0,0 +1,27 @@ +coverage: + status: + project: + default: + target: auto + threshold: 50% + informational: true + patch: + default: + target: auto + threshold: 50% + informational: true + +# do not include tests folders +ignore: + - ".github" + - "tests" + - "codec_test.go" + - "config_test.go" + - "proxy_test.go" + - "rpc_test.go" + - "service_test.go" + - "vendor_php" + - "src" + - "parser/parse_test.go" + - "example" + - "cmd/protoc-gen-php-grpc/plugin_test.go" diff --git a/composer.json b/composer.json index cb350cd..81ebd63 100644 --- a/composer.json +++ b/composer.json @@ -48,7 +48,8 @@ } }, "config": { - "sort-packages": true + "sort-packages": true, + "vendor-dir": "vendor_php" }, "minimum-stability": "dev", "prefer-stable": true diff --git a/config.go b/config.go index a9935dd..6a74677 100644 --- a/config.go +++ b/config.go @@ -19,8 +19,8 @@ type Config struct { // Address to listen. Listen string - // Proto file associated with the service. - Proto string + // Proto files associated with the service. + Proto []string // TLS defined authentication method (TLS for now). TLS TLS @@ -54,7 +54,10 @@ type TLS struct { // Hydrate the config and validate it's values. func (c *Config) Hydrate(cfg service.Config) error { c.Workers = &roadrunner.ServerConfig{} - c.Workers.InitDefaults() + err := c.Workers.InitDefaults() + if err != nil { + return err + } if err := cfg.Unmarshal(c); err != nil { return err @@ -66,15 +69,15 @@ func (c *Config) Hydrate(cfg service.Config) error { // Valid validates the configuration. func (c *Config) Valid() error { - if c.Proto == "" && c.Workers.Command != "" { + if len(c.Proto) == 0 && c.Workers.Command != "" { // only when rr server is set - return errors.New("proto file is required") + return errors.New("at least one proto file is required") } - if c.Proto != "" { - if _, err := os.Stat(c.Proto); err != nil { + for _, proto := range c.Proto { + if _, err := os.Stat(proto); err != nil { if os.IsNotExist(err) { - return fmt.Errorf("proto file '%s' does not exists", c.Proto) + return fmt.Errorf("proto file '%s' does not exists", proto) } return err @@ -170,7 +173,7 @@ func (c *Config) Listener() (net.Listener, error) { } if dsn[0] == "unix" { - syscall.Unlink(dsn[1]) + _ = syscall.Unlink(dsn[1]) } return net.Listen(dsn[0], dsn[1]) diff --git a/config_test.go b/config_test.go index 9a529a0..6bc4776 100644 --- a/config_test.go +++ b/config_test.go @@ -31,7 +31,7 @@ func Test_Config_Valid_TLS(t *testing.T) { Cert: "tests/server.crt", RootCA: "tests/server.crt", }, - Proto: "tests/test.proto", + Proto: []string{"tests/test.proto"}, Workers: &roadrunner.ServerConfig{ Command: "php tests/worker.php", Relay: "pipes", @@ -54,7 +54,73 @@ func Test_Config_No_Proto(t *testing.T) { Key: "tests/server.key", Cert: "tests/server.crt", }, - Proto: "tests/test2.proto", + Proto: []string{}, + Workers: &roadrunner.ServerConfig{ + Command: "php tests/worker.php", + Relay: "pipes", + Pool: &roadrunner.Config{ + NumWorkers: 1, + AllocateTimeout: time.Second, + DestroyTimeout: time.Second, + }, + }, + } + + assert.Error(t, cfg.Valid()) +} + +func Test_Config_Nil_Proto(t *testing.T) { + cfg := &Config{ + Listen: "tcp://:8080", + TLS: TLS{ + Key: "tests/server.key", + Cert: "tests/server.crt", + }, + Proto: nil, + Workers: &roadrunner.ServerConfig{ + Command: "php tests/worker.php", + Relay: "pipes", + Pool: &roadrunner.Config{ + NumWorkers: 1, + AllocateTimeout: time.Second, + DestroyTimeout: time.Second, + }, + }, + } + + assert.Error(t, cfg.Valid()) +} + +func Test_Config_Missing_Proto(t *testing.T) { + cfg := &Config{ + Listen: "tcp://:8080", + TLS: TLS{ + Key: "tests/server.key", + Cert: "tests/server.crt", + }, + Proto: []string{"tests/missing.proto"}, + Workers: &roadrunner.ServerConfig{ + Command: "php tests/worker.php", + Relay: "pipes", + Pool: &roadrunner.Config{ + NumWorkers: 1, + AllocateTimeout: time.Second, + DestroyTimeout: time.Second, + }, + }, + } + + assert.Error(t, cfg.Valid()) +} + +func Test_Config_Multiple_Missing_Proto(t *testing.T) { + cfg := &Config{ + Listen: "tcp://:8080", + TLS: TLS{ + Key: "tests/server.key", + Cert: "tests/server.crt", + }, + Proto: []string{"tests/test.proto", "tests/missing.proto"}, Workers: &roadrunner.ServerConfig{ Command: "php tests/worker.php", Relay: "pipes", @@ -76,7 +142,7 @@ func Test_Config_BadAddress(t *testing.T) { Key: "tests/server.key", Cert: "tests/server.crt", }, - Proto: "tests/test.proto", + Proto: []string{"tests/test.proto"}, Workers: &roadrunner.ServerConfig{ Command: "php tests/worker.php", Relay: "pipes", @@ -98,7 +164,7 @@ func Test_Config_BadListener(t *testing.T) { Key: "tests/server.key", Cert: "tests/server.crt", }, - Proto: "tests/test.proto", + Proto: []string{"tests/test.proto"}, Workers: &roadrunner.ServerConfig{ Command: "php tests/worker.php", Relay: "pipes", @@ -125,7 +191,7 @@ func Test_Config_UnixListener(t *testing.T) { Key: "tests/server.key", Cert: "tests/server.crt", }, - Proto: "tests/test2.proto", + Proto: []string{"tests/test.proto"}, Workers: &roadrunner.ServerConfig{ Command: "php tests/worker.php", Relay: "pipes", @@ -150,7 +216,7 @@ func Test_Config_InvalidWorkerPool(t *testing.T) { Key: "tests/server.key", Cert: "tests/server.crt", }, - Proto: "tests/test.proto", + Proto: []string{"tests/test.proto"}, Workers: &roadrunner.ServerConfig{ Command: "php tests/worker.php", Relay: "pipes", @@ -169,7 +235,7 @@ func Test_Config_TLS_No_key(t *testing.T) { TLS: TLS{ Cert: "tests/server.crt", }, - Proto: "tests/test.proto", + Proto: []string{"tests/test.proto"}, Workers: &roadrunner.ServerConfig{ Command: "php tests/worker.php", Relay: "pipes", @@ -192,7 +258,7 @@ func Test_Config_TLS_WrongKeyPath(t *testing.T) { Cert: "testss/server.crt", Key: "testss/server.key", }, - Proto: "tests/test.proto", + Proto: []string{"tests/test.proto"}, Workers: &roadrunner.ServerConfig{ Command: "php tests/worker.php", Relay: "pipes", @@ -215,7 +281,7 @@ func Test_Config_TLS_WrongRootCAPath(t *testing.T) { Key: "tests/server.key", RootCA: "testss/server.crt", }, - Proto: "tests/test.proto", + Proto: []string{"tests/test.proto"}, Workers: &roadrunner.ServerConfig{ Command: "php tests/worker.php", Relay: "pipes", @@ -236,7 +302,7 @@ func Test_Config_TLS_No_Cert(t *testing.T) { TLS: TLS{ Key: "tests/server.key", }, - Proto: "tests/test.proto", + Proto: []string{"tests/test.proto"}, Workers: &roadrunner.ServerConfig{ Command: "php tests/worker.php", Relay: "pipes", diff --git a/example/client/proto/service.pb.go b/example/client/proto/service.pb.go index 9a234d6..f0baae5 100644 --- a/example/client/proto/service.pb.go +++ b/example/client/proto/service.pb.go @@ -5,10 +5,11 @@ package proto import ( fmt "fmt" + math "math" + proto "github.com/golang/protobuf/proto" context "golang.org/x/net/context" grpc "google.golang.org/grpc" - math "math" ) // Reference imports to suppress errors if they are not otherwise used. diff --git a/go.mod b/go.mod index 55cd06a..c7ddac7 100644 --- a/go.mod +++ b/go.mod @@ -1,18 +1,65 @@ module github.com/spiral/php-grpc -go 1.15 +go 1.17 require ( - github.com/buger/goterm v0.0.0-20200322175922-2f3e71b85129 - github.com/c9s/inflect v0.0.0-20130402162822-006c50878f3f - github.com/emicklei/proto v1.9.0 - github.com/golang/protobuf v1.4.2 - github.com/prometheus/client_golang v1.6.0 - github.com/sirupsen/logrus v1.6.0 - github.com/spf13/cobra v1.0.0 - github.com/spiral/roadrunner v1.8.3 - github.com/stretchr/testify v1.5.1 - golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2 - google.golang.org/grpc v1.29.1 - google.golang.org/protobuf v1.23.0 + github.com/buger/goterm v1.0.1 + github.com/emicklei/proto v1.9.1 + github.com/golang/protobuf v1.5.2 + github.com/prometheus/client_golang v1.11.0 + github.com/sirupsen/logrus v1.8.1 + github.com/spf13/cobra v1.2.1 + github.com/spiral/roadrunner v1.9.2 + github.com/stretchr/testify v1.7.0 + golang.org/x/net v0.0.0-20211209124913-491a49abca63 + google.golang.org/grpc v1.42.0 + google.golang.org/protobuf v1.27.1 +) + +require ( + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.1.2 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/dustin/go-humanize v1.0.0 // indirect + github.com/fsnotify/fsnotify v1.5.1 // indirect + github.com/go-ole/go-ole v1.2.6 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect + github.com/inconshreveable/mousetrap v1.0.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/kr/pretty v0.2.0 // indirect + github.com/magiconair/properties v1.8.5 // indirect + github.com/mattn/go-colorable v0.1.12 // indirect + github.com/mattn/go-isatty v0.0.14 // indirect + github.com/mattn/go-runewidth v0.0.13 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect + github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect + github.com/mitchellh/mapstructure v1.4.3 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/olekukonko/tablewriter v0.0.5 // indirect + github.com/pelletier/go-toml v1.9.4 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/prometheus/client_model v0.2.0 // indirect + github.com/prometheus/common v0.32.1 // indirect + github.com/prometheus/procfs v0.7.3 // indirect + github.com/rivo/uniseg v0.2.0 // indirect + github.com/shirou/gopsutil v3.21.11+incompatible // 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 + github.com/spf13/pflag v1.0.5 // indirect + github.com/spf13/viper v1.10.0 // indirect + github.com/spiral/goridge/v2 v2.4.6 // indirect + github.com/subosito/gotenv v1.2.0 // indirect + github.com/tklauser/go-sysconf v0.3.9 // indirect + github.com/tklauser/numcpus v0.3.0 // indirect + github.com/valyala/tcplisten v1.0.0 // indirect + github.com/yusufpapurcu/wmi v1.2.2 // indirect + golang.org/x/sys v0.0.0-20211210111614-af8b64212486 // indirect + golang.org/x/text v0.3.7 // indirect + google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa // indirect + gopkg.in/ini.v1 v1.66.2 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect ) diff --git a/parser/parse.go b/parser/parse.go index d59b092..36e6a2a 100644 --- a/parser/parse.go +++ b/parser/parse.go @@ -41,7 +41,9 @@ type Method struct { // File parses given proto file or returns error. func File(file string, importPath string) ([]Service, error) { reader, _ := os.Open(file) - defer reader.Close() + defer func() { + _ = reader.Close() + }() return parse(reader, importPath) } diff --git a/proxy.go b/proxy.go index ddf87e8..9c1ea46 100644 --- a/proxy.go +++ b/proxy.go @@ -3,6 +3,7 @@ package grpc import ( "encoding/json" "fmt" + "math" "strconv" "strings" @@ -124,7 +125,10 @@ func (p *Proxy) invoke(ctx context.Context, method string, in rawMessage) (inter return nil, err } ctx = metadata.NewIncomingContext(ctx, md) - grpc.SetHeader(ctx, md) + err = grpc.SetHeader(ctx, md) + if err != nil { + return nil, err + } return rawMessage(resp.Body), nil } @@ -182,7 +186,17 @@ func wrapError(err error) error { chunks := strings.Split(err.Error(), "|:|") code := codes.Internal - if phpCode, err := strconv.Atoi(chunks[0]); err == nil { + // protect the slice access + if len(chunks) < 2 { + return err + } + + phpCode, errConv := strconv.ParseUint(chunks[0], 10, 32) + if errConv != nil { + return err + } + + if phpCode > 0 && phpCode < math.MaxUint32 { code = codes.Code(phpCode) } @@ -190,7 +204,7 @@ func wrapError(err error) error { for _, detailsMessage := range chunks[2:] { anyDetailsMessage := any.Any{} - err := proto.Unmarshal([]byte(detailsMessage), &anyDetailsMessage) + err = proto.Unmarshal([]byte(detailsMessage), &anyDetailsMessage) if err == nil { st.Details = append(st.Details, &anyDetailsMessage) } diff --git a/proxy_test.go b/proxy_test.go index d3e69c8..e1460ad 100644 --- a/proxy_test.go +++ b/proxy_test.go @@ -16,8 +16,6 @@ import ( "google.golang.org/grpc/status" ) -const addr = "localhost:9080" - func Test_Proxy_Error(t *testing.T) { logger, _ := test.NewNullLogger() logger.SetLevel(logrus.DebugLevel) @@ -27,12 +25,12 @@ func Test_Proxy_Error(t *testing.T) { assert.NoError(t, c.Init(&testCfg{ grpcCfg: `{ - "listen": "tcp://:9080", + "listen": "tcp://:9093", "tls": { "key": "tests/server.key", "cert": "tests/server.crt" }, - "proto": "tests/test.proto", + "proto": ["tests/test.proto"], "workers":{ "command": "php tests/worker.php", "relay": "pipes", @@ -56,7 +54,7 @@ func Test_Proxy_Error(t *testing.T) { time.Sleep(time.Millisecond * 100) defer c.Stop() - cl, cn := getClient(addr) + cl, cn := getClient("127.0.0.1:9093") defer cn.Close() _, err := cl.Throw(context.Background(), &tests.Message{Msg: "notFound"}) @@ -90,12 +88,12 @@ func Test_Proxy_Metadata(t *testing.T) { assert.NoError(t, c.Init(&testCfg{ grpcCfg: `{ - "listen": "tcp://:9080", + "listen": "tcp://:9094", "tls": { "key": "tests/server.key", "cert": "tests/server.crt" }, - "proto": "tests/test.proto", + "proto": ["tests/test.proto"], "workers":{ "command": "php tests/worker.php", "relay": "pipes", @@ -119,7 +117,7 @@ func Test_Proxy_Metadata(t *testing.T) { time.Sleep(time.Millisecond * 100) defer c.Stop() - cl, cn := getClient(addr) + cl, cn := getClient("127.0.0.1:9094") defer cn.Close() ctx := metadata.AppendToOutgoingContext(context.Background(), "key", "proxy-value") diff --git a/rpc_test.go b/rpc_test.go index a20b247..3f6f955 100644 --- a/rpc_test.go +++ b/rpc_test.go @@ -25,12 +25,12 @@ func Test_RPC(t *testing.T) { assert.NoError(t, c.Init(&testCfg{ rpcCfg: `{"enable":true, "listen":"tcp://:5004"}`, grpcCfg: `{ - "listen": "tcp://:9080", + "listen": "tcp://:9095", "tls": { "key": "tests/server.key", "cert": "tests/server.crt" }, - "proto": "tests/test.proto", + "proto": ["tests/test.proto"], "workers":{ "command": "php tests/worker.php", "relay": "pipes", @@ -53,7 +53,7 @@ func Test_RPC(t *testing.T) { time.Sleep(time.Millisecond * 100) defer c.Stop() - cl, cn := getClient(addr) + cl, cn := getClient("127.0.0.1:9095") defer cn.Close() rcl, err := rs.Client() @@ -87,12 +87,12 @@ func Test_Workers(t *testing.T) { assert.NoError(t, c.Init(&testCfg{ rpcCfg: `{"enable":true, "listen":"tcp://:5004"}`, grpcCfg: `{ - "listen": "tcp://:9080", + "listen": "tcp://:9096", "tls": { "key": "tests/server.key", "cert": "tests/server.crt" }, - "proto": "tests/test.proto", + "proto": ["tests/test.proto"], "workers":{ "command": "php tests/worker.php", "relay": "pipes", @@ -115,7 +115,7 @@ func Test_Workers(t *testing.T) { time.Sleep(time.Millisecond * 100) defer c.Stop() - cl, cn := getClient(addr) + cl, cn := getClient("127.0.0.1:9096") defer cn.Close() rcl, err := rs.Client() diff --git a/service.go b/service.go index 6d30003..06318b6 100644 --- a/service.go +++ b/service.go @@ -75,6 +75,11 @@ func (svc *Service) Init(cfg *Config, r *rpc.Service, e env.Environment) (ok boo svc.rr = roadrunner.NewServer(svc.cfg.Workers) } + // register the Codec + encoding.RegisterCodec(&Codec{ + Base: encoding.GetCodec(CodecName), + }) + return true, nil } @@ -183,20 +188,22 @@ func (svc *Service) createGPRCServer() (*grpc.Server, error) { server := grpc.NewServer(opts...) - if svc.cfg.Proto != "" && svc.rr != nil { - // php proxy services - services, err := parser.File(svc.cfg.Proto, path.Dir(svc.cfg.Proto)) - if err != nil { - return nil, err - } - - for _, service := range services { - p := NewProxy(fmt.Sprintf("%s.%s", service.Package, service.Name), svc.cfg.Proto, svc.rr) - for _, m := range service.Methods { - p.RegisterMethod(m.Name) + if len(svc.cfg.Proto) > 0 && svc.rr != nil { + for _, proto := range svc.cfg.Proto { + // php proxy services + services, err := parser.File(proto, path.Dir(proto)) + if err != nil { + return nil, err } - server.RegisterService(p.ServiceDesc(), p) + for _, service := range services { + p := NewProxy(fmt.Sprintf("%s.%s", service.Package, service.Name), proto, svc.rr) + for _, m := range service.Methods { + p.RegisterMethod(m.Name) + } + + server.RegisterService(p.ServiceDesc(), p) + } } } @@ -209,24 +216,29 @@ func (svc *Service) createGPRCServer() (*grpc.Server, error) { } // server options -func (svc *Service) serverOptions() (opts []grpc.ServerOption, err error) { +func (svc *Service) serverOptions() ([]grpc.ServerOption, error) { var tcreds credentials.TransportCredentials + var opts []grpc.ServerOption + var cert tls.Certificate + var certPool *x509.CertPool + var rca []byte + var err error if svc.cfg.EnableTLS() { // if client CA is not empty we combine it with Cert and Key if svc.cfg.TLS.RootCA != "" { - cert, err := tls.LoadX509KeyPair(svc.cfg.TLS.Cert, svc.cfg.TLS.Key) + cert, err = tls.LoadX509KeyPair(svc.cfg.TLS.Cert, svc.cfg.TLS.Key) if err != nil { return nil, err } - certPool, err := x509.SystemCertPool() + certPool, err = x509.SystemCertPool() if err != nil { return nil, err } if certPool == nil { certPool = x509.NewCertPool() } - rca, err := ioutil.ReadFile(svc.cfg.TLS.RootCA) + rca, err = ioutil.ReadFile(svc.cfg.TLS.RootCA) if err != nil { return nil, err } @@ -236,11 +248,14 @@ func (svc *Service) serverOptions() (opts []grpc.ServerOption, err error) { } tcreds = credentials.NewTLS(&tls.Config{ + MinVersion: tls.VersionTLS12, ClientAuth: tls.RequireAndVerifyClientCert, Certificates: []tls.Certificate{cert}, ClientCAs: certPool, + RootCAs: certPool, }) } else { + var err error tcreds, err = credentials.NewServerTLSFromFile(svc.cfg.TLS.Cert, svc.cfg.TLS.Key) if err != nil { return nil, err @@ -266,10 +281,10 @@ func (svc *Service) serverOptions() (opts []grpc.ServerOption, err error) { opts = append(opts, svc.opts...) - // custom codec is required to bypass protobuf, common interceptor used for debug and stats + // custom Codec is required to bypass protobuf, common interceptor used for debug and stats + // custom Codec is required to bypass protobuf, common interceptor used for debug and stats return append( opts, grpc.UnaryInterceptor(svc.interceptor), - grpc.CustomCodec(&codec{encoding.GetCodec("proto")}), ), nil } diff --git a/service_test.go b/service_test.go index 9b4a338..bf9a1cf 100644 --- a/service_test.go +++ b/service_test.go @@ -73,12 +73,12 @@ func Test_Service_Configure_Enable(t *testing.T) { assert.NoError(t, c.Init(&testCfg{ grpcCfg: `{ - "listen": "tcp://:9080", + "listen": "tcp://:9081", "tls": { "key": "tests/server.key", "cert": "tests/server.crt" }, - "proto": "tests/test.proto", + "proto": ["tests/test.proto"], "workers":{ "command": "php tests/worker.php", "relay": "pipes", @@ -105,12 +105,12 @@ func Test_Service_Dead(t *testing.T) { assert.NoError(t, c.Init(&testCfg{ grpcCfg: `{ - "listen": "tcp://:9080", + "listen": "tcp://:9082", "tls": { "key": "tests/server.key", "cert": "tests/server.crt" }, - "proto": "tests/test.proto", + "proto": ["tests/test.proto"], "workers":{ "command": "php tests/worker-bad.php", "relay": "pipes", @@ -142,12 +142,12 @@ func Test_Service_Invalid_TLS(t *testing.T) { assert.NoError(t, c.Init(&testCfg{ grpcCfg: `{ - "listen": "tcp://:9080", + "listen": "tcp://:9083", "tls": { "key": "tests/server.key", "cert": "tests/test.proto" }, - "proto": "tests/test.proto", + "proto": ["tests/test.proto"], "workers":{ "command": "php tests/worker.php", "relay": "pipes", @@ -179,12 +179,49 @@ func Test_Service_Invalid_Proto(t *testing.T) { assert.NoError(t, c.Init(&testCfg{ grpcCfg: `{ - "listen": "tcp://:9080", + "listen": "tcp://:9084", "tls": { "key": "tests/server.key", "cert": "tests/server.crt" }, - "proto": "tests/server.key", + "proto": ["tests/server.key"], + "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() + + assert.Error(t, c.Serve()) +} + +func Test_Service_Multiple_Invalid_Proto(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://:9085", + "tls": { + "key": "tests/server.key", + "cert": "tests/server.crt" + }, + "proto": ["tests/health.proto", "tests/server.key"], "workers":{ "command": "php tests/worker.php", "relay": "pipes", @@ -216,12 +253,59 @@ func Test_Service_Echo(t *testing.T) { assert.NoError(t, c.Init(&testCfg{ grpcCfg: `{ - "listen": "tcp://:9080", + "listen": "tcp://:9086", + "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("localhost:9086") + defer cn.Close() + + out, err := cl.Echo(context.Background(), &tests.Message{Msg: "ping"}) + + assert.NoError(t, err) + assert.Equal(t, "ping", out.Msg) +} + +func Test_Service_Multiple_Echo(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://:9087", "tls": { "key": "tests/server.key", "cert": "tests/server.crt" }, - "proto": "tests/test.proto", + "proto": ["tests/test.proto", "tests/health.proto"], "workers":{ "command": "php tests/worker.php", "relay": "pipes", @@ -245,7 +329,7 @@ func Test_Service_Echo(t *testing.T) { time.Sleep(time.Millisecond * 100) defer c.Stop() - cl, cn := getClient(addr) + cl, cn := getClient("127.0.0.1:9087") defer cn.Close() out, err := cl.Echo(context.Background(), &tests.Message{Msg: "ping"}) @@ -263,12 +347,12 @@ func Test_Service_Empty(t *testing.T) { assert.NoError(t, c.Init(&testCfg{ grpcCfg: `{ - "listen": "tcp://:9080", + "listen": "tcp://:9088", "tls": { "key": "tests/server.key", "cert": "tests/server.crt" }, - "proto": "tests/test.proto", + "proto": ["tests/test.proto"], "workers":{ "command": "php tests/worker.php", "relay": "pipes", @@ -292,7 +376,7 @@ func Test_Service_Empty(t *testing.T) { time.Sleep(time.Millisecond * 100) defer c.Stop() - cl, cn := getClient(addr) + cl, cn := getClient("127.0.0.1:9088") defer cn.Close() _, err := cl.Ping(context.Background(), &tests.EmptyMessage{}) @@ -309,12 +393,12 @@ func Test_Service_ErrorBuffer(t *testing.T) { assert.NoError(t, c.Init(&testCfg{ grpcCfg: `{ - "listen": "tcp://:9080", + "listen": "tcp://:9089", "tls": { "key": "tests/server.key", "cert": "tests/server.crt" }, - "proto": "tests/test.proto", + "proto": ["tests/test.proto"], "workers":{ "command": "php tests/worker.php", "relay": "pipes", @@ -347,7 +431,7 @@ func Test_Service_ErrorBuffer(t *testing.T) { time.Sleep(time.Millisecond * 100) defer c.Stop() - cl, cn := getClient(addr) + cl, cn := getClient("127.0.0.1:9089") defer cn.Close() out, err := cl.Die(context.Background(), &tests.Message{Msg: "WORLD"}) @@ -367,12 +451,12 @@ func Test_Service_Env(t *testing.T) { assert.NoError(t, c.Init(&testCfg{ grpcCfg: `{ - "listen": "tcp://:9080", + "listen": "tcp://:9090", "tls": { "key": "tests/server.key", "cert": "tests/server.crt" }, - "proto": "tests/test.proto", + "proto": ["tests/test.proto"], "workers":{ "command": "php tests/worker.php", "relay": "pipes", @@ -400,7 +484,7 @@ func Test_Service_Env(t *testing.T) { time.Sleep(time.Millisecond * 100) defer c.Stop() - cl, cn := getClient(addr) + cl, cn := getClient("127.0.0.1:9090") defer cn.Close() out, err := cl.Info(context.Background(), &tests.Message{Msg: "RR_GRPC"}) @@ -422,12 +506,12 @@ func Test_Service_External_Service_Test(t *testing.T) { c.Register(ID, &Service{}) assert.NoError(t, c.Init(&testCfg{grpcCfg: `{ - "listen": "tcp://:9080", + "listen": "tcp://:9091", "tls": { "key": "tests/server.key", "cert": "tests/server.crt" }, - "proto": "tests/test.proto", + "proto": ["tests/test.proto"], "workers":{ "command": "php tests/worker.php", "relay": "pipes", @@ -452,7 +536,7 @@ func Test_Service_External_Service_Test(t *testing.T) { time.Sleep(time.Millisecond * 100) defer c.Stop() - cl, cn := getExternalClient("localhost:9080") + cl, cn := getExternalClient("localhost:9091") defer cn.Close() out, err := cl.Echo(context.Background(), &ext.Ping{Value: 9}) @@ -469,12 +553,12 @@ func Test_Service_Kill(t *testing.T) { c.Register(ID, &Service{}) assert.NoError(t, c.Init(&testCfg{grpcCfg: `{ - "listen": "tcp://:9080", + "listen": "tcp://:9092", "tls": { "key": "tests/server.key", "cert": "tests/server.crt" }, - "proto": "tests/test.proto", + "proto": ["tests/test.proto"], "workers":{ "command": "php tests/worker.php", "relay": "pipes", diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 9d86e88..1500f7c 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -9,4 +9,4 @@ ini_set('display_errors', true); //Composer -require dirname(__DIR__) . '/vendor/autoload.php'; \ No newline at end of file +require dirname(__DIR__) . '/vendor_php/autoload.php'; \ No newline at end of file diff --git a/tests/ext/external.pb.go b/tests/ext/external.pb.go index 76cf487..7b8a08e 100644 --- a/tests/ext/external.pb.go +++ b/tests/ext/external.pb.go @@ -3,12 +3,15 @@ package ext -import proto "github.com/golang/protobuf/proto" -import fmt "fmt" -import math "math" - import ( + fmt "fmt" + + proto "github.com/golang/protobuf/proto" + + math "math" + context "golang.org/x/net/context" + grpc "google.golang.org/grpc" ) diff --git a/tests/health.proto b/tests/health.proto new file mode 100644 index 0000000..0dce348 --- /dev/null +++ b/tests/health.proto @@ -0,0 +1,27 @@ +syntax = "proto3"; + +package grpc.health.v1; +option go_package = "./;tests"; +option php_namespace = "Health"; +option php_metadata_namespace = "GPBMetadata"; + +message HealthCheckRequest { + string service = 1; +} + +// https://github.com/grpc/grpc/blob/master/doc/health-checking.md +message HealthCheckResponse { + enum ServingStatus { + UNKNOWN = 0; + SERVING = 1; + NOT_SERVING = 2; + SERVICE_UNKNOWN = 3; // Used only by the Watch method. + } + ServingStatus status = 1; +} + +service Health { + rpc Check(HealthCheckRequest) returns (HealthCheckResponse); + + rpc Watch(HealthCheckRequest) returns (stream HealthCheckResponse); +} diff --git a/tests/server.crt b/tests/server.crt index 24d67fd..61b0456 100644 --- a/tests/server.crt +++ b/tests/server.crt @@ -1,15 +1,30 @@ -----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= +MIIFLjCCAxagAwIBAgIUHtW+ZMN/jSyCxDXrmiyDpkTbW8swDQYJKoZIhvcNAQEL +BQAwLTELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAk5KMREwDwYDVQQKDAhDQSwgSW5j +LjAeFw0yMTA5MTkxNzIxMTFaFw0zMTA5MTcxNzIxMTFaMEMxCzAJBgNVBAYTAlVT +MQswCQYDVQQIDAJOSjETMBEGA1UECgwKVGVzdCwgSW5jLjESMBAGA1UEAwwJbG9j +YWxob3N0MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA3i0/FD88LtSf +yTJesK7b1O8VttpxEww7b9HXvw4eL2omrDVt8rFbe8XR2AXYiQ7ffea3ZwOq3aAi +qMQylDaJAwTf04EBSNn+8P7piOegABspdw6mj5E0SzoRW+rRYzJAAoHT642tX74Q +TT1vIzd3g+MMsa/P06R31P38axdgo+BRUApwnLx52HWmJj18T4gy+B6drQMT3lML +/ltT3Hh7zFluHifo6wDt3gZMYw8NgsnSeTSZL3o/VJ8RrCllRzFhJQIG63atc34w +i+w9BCnHnxJYrpWKZtgz7WlkVOEoMxBlwfatbdSketIe34svPiCWnKEfJgxbc59+ +B3qB7sxXK5KUg8IkSFlvmFAWeUyRiBwEnmlIBTnTCLvZgvY1qXBm1BgKvM7DQb7j +wAiebm91DToCB4IGEoUR5ROlOuPuXUnnEChZblHgXXpD3DlhNa27h+d15Lyd7akm +CODGmdbk84uUFXbMGA/VWGgtZz8/X5RWA2pl4PTtk3CfNvdpzZJR0zfROf4BiN1m +NQo6Tm6YiEVP7xMUzcqcJNGrRUvc/j8IHPWqSrvWv4zzJOvMNCsV3Y+65CsXT2Ey +qRXdtJFkZ2oZFse3tef1pNQyOkHDTvKwl12TuSAyNPrPB2Wwu4v6TVM/+aXww6+3 +WSv2f9e1qkWke/L+7JzCOh/lkBWMS90CAwEAAaMwMC4wLAYDVR0RBCUwI4IJbG9j +YWxob3N0hxAAAAAAAAAAAAAAAAAAAAABhwR/AAABMA0GCSqGSIb3DQEBCwUAA4IC +AQA9Pp5MTFsBO+0rXZyAZm/yKfJfquzdBTQqN+BEeeBG7qxetcl5PfB9k2+cfc/L ++q/VvN15r0+5HBfGt0HLKbW5NbU2AU9Le6mub3BW/lUOYPhWl1jkj4q1fZCkaKsF +6cnljbyY6d32QJ0c1SVLIRoM6rW9KiVMBsavyJQf8+u4Vk8LwsCv2m6CpWqGEvgN +dFhW0PBi0/GIECR1tI4iMHnMWzEr26/LnXkzUtIOgIIwE1a4agangfexDqc7svUZ +DX6CoAie5vJjvCH3iNpUZpN95OAUMXLuhb06IkFwpyEL35DqpKDQdFjFNR2KIL5a +u52mW/hckBu7kcNRoJzamzh+lyAB4HilroTDBdRZr9QJkkJBf5xcIjP86OYJQLH4 +cnxIGmFqIML+JR8tGtGAITq+DnhTv9LaoaPT8XbC561bkquZHS0PvYKiVu/sVXUR +rgAjaW9XG9HWZbMg74mZN5BpIoFxS1WNnbTCBbDuwViF0dLfpsVob9BU8XqFFtEF +QirObch1dDLF+yhQ8ndyPsA75b9fJSDKev4Gg83IiCBVKbQ3ZPJ3+FySOGTnsWi3 +Kg6DFPxISrM50UjEwI479EV5AvCvfImyhUgXvP2rwMJb0jCO6Lwt4g33/0gv7rvH +scL4G/PfQ6ZDDc/70N3ot4KSbkq50y9IRaiS1YPRqj6BHw== -----END CERTIFICATE----- diff --git a/tests/server.key b/tests/server.key index 7501dd4..9bd9efa 100644 --- a/tests/server.key +++ b/tests/server.key @@ -1,9 +1,51 @@ ------BEGIN EC PARAMETERS----- -BgUrgQQAIg== ------END EC PARAMETERS----- ------BEGIN EC PRIVATE KEY----- -MIGkAgEBBDCQP8utxNbHR6xZOLAJgUhn88r6IrPqmN0MsgGJM/jePB+T9UhkmIU8 -PMm2HeScbcugBwYFK4EEACKhZANiAARWdtKGwz6XlFHfB9ZaYaHO4UbA3MoKTuL3 -HKhsMjIFTEb9RRJ+PsoqVRem4OcMS0XnFdx6mXvjgLIgBgUs5cdvlaojiMI7MbSj -1//TRBDhtC50k51upX3XRAULa9vsXGs= ------END EC PRIVATE KEY----- +-----BEGIN RSA PRIVATE KEY----- +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 +-----END RSA PRIVATE KEY----- diff --git a/tests/src/GPBMetadata/Health.php b/tests/src/GPBMetadata/Health.php new file mode 100644 index 0000000..8d62465 Binary files /dev/null and b/tests/src/GPBMetadata/Health.php differ diff --git a/tests/src/Health/HealthCheckRequest.php b/tests/src/Health/HealthCheckRequest.php new file mode 100644 index 0000000..94489af --- /dev/null +++ b/tests/src/Health/HealthCheckRequest.php @@ -0,0 +1,58 @@ +grpc.health.v1.HealthCheckRequest
+ */ +class HealthCheckRequest extends \Google\Protobuf\Internal\Message +{ + /** + * Generated from protobuf field string service = 1; + */ + protected $service = ''; + + /** + * Constructor. + * + * @param array $data { + * Optional. Data for populating the Message object. + * + * @type string $service + * } + */ + public function __construct($data = NULL) { + \GPBMetadata\Health::initOnce(); + parent::__construct($data); + } + + /** + * Generated from protobuf field string service = 1; + * @return string + */ + public function getService() + { + return $this->service; + } + + /** + * Generated from protobuf field string service = 1; + * @param string $var + * @return $this + */ + public function setService($var) + { + GPBUtil::checkString($var, True); + $this->service = $var; + + return $this; + } + +} + diff --git a/tests/src/Health/HealthCheckResponse.php b/tests/src/Health/HealthCheckResponse.php new file mode 100644 index 0000000..2c02b07 --- /dev/null +++ b/tests/src/Health/HealthCheckResponse.php @@ -0,0 +1,58 @@ +grpc.health.v1.HealthCheckResponse + */ +class HealthCheckResponse extends \Google\Protobuf\Internal\Message +{ + /** + * Generated from protobuf field .grpc.health.v1.HealthCheckResponse.ServingStatus status = 1; + */ + protected $status = 0; + + /** + * Constructor. + * + * @param array $data { + * Optional. Data for populating the Message object. + * + * @type int $status + * } + */ + public function __construct($data = NULL) { + \GPBMetadata\Health::initOnce(); + parent::__construct($data); + } + + /** + * Generated from protobuf field .grpc.health.v1.HealthCheckResponse.ServingStatus status = 1; + * @return int + */ + public function getStatus() + { + return $this->status; + } + + /** + * Generated from protobuf field .grpc.health.v1.HealthCheckResponse.ServingStatus status = 1; + * @param int $var + * @return $this + */ + public function setStatus($var) + { + GPBUtil::checkEnum($var, \Health\HealthCheckResponse\ServingStatus::class); + $this->status = $var; + + return $this; + } + +} + diff --git a/tests/src/Health/HealthCheckResponse/ServingStatus.php b/tests/src/Health/HealthCheckResponse/ServingStatus.php new file mode 100644 index 0000000..e13fe2f --- /dev/null +++ b/tests/src/Health/HealthCheckResponse/ServingStatus.php @@ -0,0 +1,63 @@ +grpc.health.v1.HealthCheckResponse.ServingStatus + */ +class ServingStatus +{ + /** + * Generated from protobuf enum UNKNOWN = 0; + */ + const UNKNOWN = 0; + /** + * Generated from protobuf enum SERVING = 1; + */ + const SERVING = 1; + /** + * Generated from protobuf enum NOT_SERVING = 2; + */ + const NOT_SERVING = 2; + /** + * Used only by the Watch method. + * + * Generated from protobuf enum SERVICE_UNKNOWN = 3; + */ + const SERVICE_UNKNOWN = 3; + + private static $valueToName = [ + self::UNKNOWN => 'UNKNOWN', + self::SERVING => 'SERVING', + self::NOT_SERVING => 'NOT_SERVING', + self::SERVICE_UNKNOWN => 'SERVICE_UNKNOWN', + ]; + + public static function name($value) + { + if (!isset(self::$valueToName[$value])) { + throw new UnexpectedValueException(sprintf( + 'Enum %s has no name defined for value %s', __CLASS__, $value)); + } + return self::$valueToName[$value]; + } + + + public static function value($name) + { + $const = __CLASS__ . '::' . strtoupper($name); + if (!defined($const)) { + throw new UnexpectedValueException(sprintf( + 'Enum %s has no value defined for name %s', __CLASS__, $name)); + } + return constant($const); + } +} + +// Adding a class alias for backwards compatibility with the previous class name. +class_alias(ServingStatus::class, \Health\HealthCheckResponse_ServingStatus::class); + diff --git a/tests/src/Health/HealthCheckResponse_ServingStatus.php b/tests/src/Health/HealthCheckResponse_ServingStatus.php new file mode 100644 index 0000000..57f393b --- /dev/null +++ b/tests/src/Health/HealthCheckResponse_ServingStatus.php @@ -0,0 +1,16 @@ +setStatus(HealthCheckResponse\ServingStatus::SERVING); + return $out; + } + + public function Watch(ContextInterface $ctx, HealthCheckRequest $in): HealthCheckResponse + { + $out = new HealthCheckResponse(); + $out->setStatus(HealthCheckResponse\ServingStatus::SERVING); + return $out; + } +} diff --git a/tests/test.pb.go b/tests/test.pb.go index 080669e..0355ff0 100644 --- a/tests/test.pb.go +++ b/tests/test.pb.go @@ -6,11 +6,12 @@ package tests import ( context "context" fmt "fmt" + math "math" + proto "github.com/golang/protobuf/proto" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" - math "math" ) // Reference imports to suppress errors if they are not otherwise used. diff --git a/tests/worker.php b/tests/worker.php index 7a80416..4432347 100644 --- a/tests/worker.php +++ b/tests/worker.php @@ -7,10 +7,11 @@ use Spiral\RoadRunner; ini_set('display_errors', 'stderr'); -require __DIR__ . '/../vendor/autoload.php'; +require __DIR__ . '/../vendor_php/autoload.php'; $server = new \Spiral\GRPC\Server(); $server->registerService(\Service\TestInterface::class, new \Test\TestService()); +$server->registerService(\Health\HealthInterface::class, new \HealthImpl\HealthService()); $w = new RoadRunner\Worker(new Goridge\StreamRelay(STDIN, STDOUT)); $server->serve($w);