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);