diff --git a/.github/workflows/grpc-interceptors.yml b/.github/workflows/grpc-interceptors.yml new file mode 100644 index 0000000..70ddd53 --- /dev/null +++ b/.github/workflows/grpc-interceptors.yml @@ -0,0 +1,66 @@ +name: grpc-interceptors + +on: + push: + branches: + - master + - stable + pull_request: + branches: + - master + - stable + +jobs: + grpc_interceptors_test: + name: gRPC interceptors (Go ${{ matrix.go }}, PHP ${{ matrix.php }}, OS ${{matrix.os}}) + runs-on: ${{ matrix.os }} + timeout-minutes: 60 + strategy: + matrix: + php: ["8.5"] + go: [stable] + os: ["ubuntu-latest"] + + steps: + - name: Set up Go ${{ matrix.go }} + uses: actions/setup-go@v5 # 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 + + - name: Check out code + uses: actions/checkout@v4 + + - name: Get Composer Cache Directory + id: composer-cache + run: | + cd tests/php_test_files + echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT + + - name: Init Composer Cache # Docs: + uses: actions/cache@v4 + 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: cd tests/php_test_files && composer update --prefer-dist --no-progress --ansi + + - name: Init Go modules Cache # Docs: + uses: actions/cache@v4 + 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: Run interceptors e2e test + run: cd tests && go test -timeout 20m -v -race -tags=debug grpc_interceptors_test.go diff --git a/config.go b/config.go index 055d074..52344e4 100644 --- a/config.go +++ b/config.go @@ -29,7 +29,8 @@ type Config struct { TLS *TLS `mapstructure:"tls"` // Env is environment variables passed to the http pool - Env map[string]string `mapstructure:"env"` + Env map[string]string `mapstructure:"env"` + Interceptors []string `mapstructure:"interceptors"` GrpcPool *pool.Config `mapstructure:"pool"` MaxSendMsgSize int64 `mapstructure:"max_send_msg_size"` diff --git a/go.work.sum b/go.work.sum index daeaf03..e4c63d5 100644 --- a/go.work.sum +++ b/go.work.sum @@ -11,6 +11,7 @@ cel.dev/expr v0.19.2/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw= cel.dev/expr v0.20.0/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw= cel.dev/expr v0.24.0 h1:56OvJKSH3hDGL0ml5uSxZmz3/3Pq4tJ+fb1unVLAFcY= cel.dev/expr v0.24.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw= +cel.dev/expr v0.25.1/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4= cloud.google.com/go v0.26.0 h1:e0WKqKTd5BnrG8aKH3J3h+QvEIQtSUcf2n5UZ5ZgLtQ= cloud.google.com/go v0.110.0 h1:Zc8gqp3+a9/Eyph2KDmcGaPtbKRIoqq4YTlL4NMD0Ys= cloud.google.com/go v0.110.8/go.mod h1:Iz8AkXJf1qmxC3Oxoep8R1T36w8B92yU29PcBhHO5fk= @@ -73,6 +74,7 @@ cloud.google.com/go/aiplatform v1.94.0 h1:rzofdDuPWRI955J6mQdL2K5z8X614dasPvIIqV cloud.google.com/go/aiplatform v1.94.0/go.mod h1:4lKl6YhQz5V+zewJ+RsqQ91H7hjy2AmDy+93ZhwSgiE= cloud.google.com/go/aiplatform v1.109.0/go.mod h1:4rwKOMdubQOND81AlO3EckcskvEFCYSzXKfn42GMm8k= cloud.google.com/go/aiplatform v1.114.0/go.mod h1:W5yMrpIuHG/CSK8iF7XnwIfCJu6dcLRQ0cTqGR5vwwE= +cloud.google.com/go/aiplatform v1.115.0/go.mod h1:DwPJAxebOTy6BajSMjF7ah3QvlYO4jf2gpJw6/1z9gU= cloud.google.com/go/analytics v0.21.3 h1:TFBC1ZAqX9/jL56GEXdLrVe5vT3I22bDVWyDwZX4IEg= cloud.google.com/go/analytics v0.21.4 h1:SScWR8i/M8h7h3lFKtOYcj0r4272aL+KvRRrsu39Vec= cloud.google.com/go/analytics v0.21.4/go.mod h1:zZgNCxLCy8b2rKKVfC1YkC2vTrpfZmeRCySM3aUbskA= @@ -271,6 +273,7 @@ cloud.google.com/go/bigquery v1.67.0/go.mod h1:HQeP1AHFuAz0Y55heDSb0cjZIhnEkuwFR cloud.google.com/go/bigquery v1.69.0 h1:rZvHnjSUs5sHK3F9awiuFk2PeOaB8suqNuim21GbaTc= cloud.google.com/go/bigquery v1.69.0/go.mod h1:TdGLquA3h/mGg+McX+GsqG9afAzTAcldMjqhdjHTLew= cloud.google.com/go/bigquery v1.72.0/go.mod h1:GUbRtmeCckOE85endLherHD9RsujY+gS7i++c1CqssQ= +cloud.google.com/go/bigquery v1.73.1/go.mod h1:KSLx1mKP/yGiA8U+ohSrqZM1WknUnjZAxHAQZ51/b1k= cloud.google.com/go/bigtable v1.35.0 h1:UEacPwaejN2mNbz67i1Iy3G812rxtgcs6ePj1TAg7dw= cloud.google.com/go/bigtable v1.35.0/go.mod h1:EabtwwmTcOJFXp+oMZAT/jZkyDIjNwrv53TrS4DGrrM= cloud.google.com/go/bigtable v1.37.0/go.mod h1:HXqddP6hduwzrtiTCqZPpj9ij4hGZb4Zy1WF/dT+yaU= @@ -278,6 +281,7 @@ cloud.google.com/go/bigtable v1.38.0 h1:L/PnUXRtAzFfa7qMULJHt4cXa/O2dqPJEkzYNGA4 cloud.google.com/go/bigtable v1.38.0/go.mod h1:o/lntJarF3Y5C0XYLMJLjLYwxaRbcrtM0BiV57ymXbI= cloud.google.com/go/bigtable v1.40.1/go.mod h1:LtPzCcrAFaGRZ82Hs8xMueUeYW9Jw12AmNdUTMfDnh4= cloud.google.com/go/bigtable v1.41.0/go.mod h1:JlaltP06LEFXaxQdZiarGR9tKsX/II0IkNAKMDrWspI= +cloud.google.com/go/bigtable v1.42.0/go.mod h1:oZ30nofVB6/UYGg7lBwGLWSea7NZUvw/WvBBgLY07xU= cloud.google.com/go/billing v1.17.0 h1:CpagWXb/+QNye+vouomndbc4Gsr0uo+AGR24V16uk8Q= cloud.google.com/go/billing v1.17.0/go.mod h1:Z9+vZXEq+HwH7bhJkyI4OQcR6TSbeMrjlpEjO2vzY64= cloud.google.com/go/billing v1.17.2 h1:ozS/MNj6KKz8Reuw7tIG8Ycucq/YpSf3u3XCqrupbcg= @@ -444,6 +448,7 @@ cloud.google.com/go/container v1.42.4/go.mod h1:wf9lKc3ayWVbbV/IxKIDzT7E+1KQgzkz cloud.google.com/go/container v1.44.0 h1:JEHeW535svvNwJrjrlQ/cdjd15LCWrPKnHsulrufd3A= cloud.google.com/go/container v1.44.0/go.mod h1:tVK2o4UZUTkg9WpBcgj4qRzwGA1dSFdWA3mil3YkLIQ= cloud.google.com/go/container v1.45.0/go.mod h1:eB6jUfJLjne9VsTDGcH7mnj6JyZK+KOUIA6KZnYE/ds= +cloud.google.com/go/container v1.46.0/go.mod h1:A7gMqdQduTk46+zssWDTKbGS2z46UsJNXfKqvMI1ZO4= cloud.google.com/go/containeranalysis v0.11.0 h1:/EsoP+UTIjvl4yqrLA4WgUG83kwQhqZmbXEfqirT2LM= cloud.google.com/go/containeranalysis v0.11.0/go.mod h1:4n2e99ZwpGxpNcz+YsFT1dfOHPQFGcAC8FN2M2/ne/U= cloud.google.com/go/containeranalysis v0.11.1 h1:PHh4KTcMpCjYgxfV+TzvP24wolTGP9lGbqh9sBNHxjs= @@ -583,6 +588,7 @@ cloud.google.com/go/datastore v1.15.0/go.mod h1:GAeStMBIt9bPS7jMJA85kgkpsMkvseWW cloud.google.com/go/datastore v1.20.0 h1:NNpXoyEqIJmZFc0ACcwBEaXnmscUpcG4NkKnbCePmiM= cloud.google.com/go/datastore v1.20.0/go.mod h1:uFo3e+aEpRfHgtp5pp0+6M0o147KoPaYNaPAKpfh8Ew= cloud.google.com/go/datastore v1.21.0/go.mod h1:9l+KyAHO+YVVcdBbNQZJu8svF17Nw5sMKuFR0LYf1nY= +cloud.google.com/go/datastore v1.22.0/go.mod h1:aopSX+Whx0lHspWWBj+AjWt68/zjYsPfDe3LjWtqZg8= cloud.google.com/go/datastream v1.10.0 h1:ra/+jMv36zTAGPfi8TRne1hXme+UsKtdcK4j6bnqQiw= cloud.google.com/go/datastream v1.10.1 h1:XWiXV1hzs8oAd54//wcb1L15Jl7MnZ/cY2B8XCmu0xE= cloud.google.com/go/datastream v1.10.1/go.mod h1:7ngSYwnw95YFyTd5tOGBxHlOZiL+OtpjheqU7t2/s/c= @@ -635,6 +641,7 @@ cloud.google.com/go/dialogflow v1.69.0 h1:nW3vH/ysZWBdjQJ4rIh3PC5Do/Brz8KEp3OeDy cloud.google.com/go/dialogflow v1.69.0/go.mod h1:+2drAzrguQ8vltf6qn6foBPHrT/fFa1S3FQ40byV2WU= cloud.google.com/go/dialogflow v1.71.0/go.mod h1:mP4XrpgDvPYBP+cdLxFC1WJJlkwuy0H8L1Lada9No/M= cloud.google.com/go/dialogflow v1.74.0/go.mod h1:jlKHmd3/KdvWWhGZjoCnWQAQNOMHOhDK6DQ430p3T1I= +cloud.google.com/go/dialogflow v1.75.0/go.mod h1:z1W1ZogmigYVtP5YmyeUh+D219VCjdd3VJqY76PG3gA= cloud.google.com/go/dlp v1.10.1 h1:tF3wsJ2QulRhRLWPzWVkeDz3FkOGVoMl6cmDUHtfYxw= cloud.google.com/go/dlp v1.10.2 h1:sWOATigjZOKmA2rVOSjIcKLCtL2ifdawaukx+H9iffk= cloud.google.com/go/dlp v1.10.2/go.mod h1:ZbdKIhcnyhILgccwVDzkwqybthh7+MplGC3kZVZsIOQ= @@ -669,6 +676,7 @@ cloud.google.com/go/documentai v1.36.1/go.mod h1:6+IBOdk6FUZ8c0df91ZPtF2muF+eikA cloud.google.com/go/documentai v1.37.0 h1:7fla8GcarupO15eatRTUveXCob6DOSW1Wa+1i63CM3Q= cloud.google.com/go/documentai v1.37.0/go.mod h1:qAf3ewuIUJgvSHQmmUWvM3Ogsr5A16U2WPHmiJldvLA= cloud.google.com/go/documentai v1.39.0/go.mod h1:KmlLO93F7GRU8dENXRxvt+7V8o7eCG6Y6WDitKbcYJs= +cloud.google.com/go/documentai v1.40.0/go.mod h1:oDTm0aoG8ldKucW/yzRrLbaTO0NvtgGAWm5KPAT5iNY= cloud.google.com/go/domains v0.9.1 h1:rqz6KY7mEg7Zs/69U6m6LMbB7PxFDWmT3QWNXIqhHm0= cloud.google.com/go/domains v0.9.2 h1:SjpTtaTNRPPajrGiZEtxz9dpElO4PxuDWFvU4JpV1gk= cloud.google.com/go/domains v0.9.2/go.mod h1:3YvXGYzZG1Temjbk7EyGCuGGiXHJwVNmwIf+E/cUp5I= @@ -936,6 +944,7 @@ cloud.google.com/go/logging v1.9.0/go.mod h1:1Io0vnZv4onoUnsVUQY3HZ3Igb1nBchky0A cloud.google.com/go/logging v1.13.0 h1:7j0HgAp0B94o1YRDqiqm26w4q1rDMH7XNRU34lJXHYc= cloud.google.com/go/logging v1.13.0/go.mod h1:36CoKh6KA/M0PbhPKMq6/qety2DCAErbhXT62TuXALA= cloud.google.com/go/logging v1.13.1/go.mod h1:XAQkfkMBxQRjQek96WLPNze7vsOmay9H5PqfsNYDqvw= +cloud.google.com/go/logging v1.13.2/go.mod h1:zaybliM3yun1J8mU2dVQ1/qDzjbOqEijZCn6hSBtKak= cloud.google.com/go/longrunning v0.4.1 h1:v+yFJOfKC3yZdY6ZUI933pIYdhyhV8S3NpWrXWmg7jM= cloud.google.com/go/longrunning v0.5.1 h1:Fr7TXftcqTudoyRJa113hyaqlGdiBQkp0Gq7tErFDWI= cloud.google.com/go/longrunning v0.5.2 h1:u+oFqfEwwU7F9dIELigxbe0XVnBAo9wqMuQLA50CZ5k= @@ -1064,6 +1073,7 @@ cloud.google.com/go/networkmanagement v1.18.0/go.mod h1:yTxpAFuvQOOKgL3W7+k2Rp1b cloud.google.com/go/networkmanagement v1.19.1 h1:ecukgArkYCVcK5w2h7WDDd+nHgmBAp9Bst7ClmVKz5A= cloud.google.com/go/networkmanagement v1.19.1/go.mod h1:icgk265dNnilxQzpr6rO9WuAuuCmUOqq9H6WBeM2Af4= cloud.google.com/go/networkmanagement v1.21.0/go.mod h1:clG/5Yt0wQ57qSH6Yh7oehQYlobHw3F6nb3Pn4ig5hU= +cloud.google.com/go/networkmanagement v1.22.0/go.mod h1:RGR62aLOlm72C7DT/3yaMUK43oill6hj9wqktUQ8h6Q= cloud.google.com/go/networksecurity v0.9.1 h1:TBLEkMp3AE+6IV/wbIGRNTxnqLXHCTEQWoxRVC18TzY= cloud.google.com/go/networksecurity v0.9.2 h1:fA73AX//KWaqNKOvuQ00WUD3Z/XMhiMhHSFTEl2Wxec= cloud.google.com/go/networksecurity v0.9.2/go.mod h1:jG0SeAttWzPMUILEHDUvFYdQTl8L/E/KC8iZDj85lEI= @@ -1143,6 +1153,7 @@ cloud.google.com/go/osconfig v1.14.5/go.mod h1:XH+NjBVat41I/+xgQzKOJEhuC4xI7lX2I cloud.google.com/go/osconfig v1.14.6 h1:4uJrA1obzMBp1I+DF15y/MvsXKIODevuANpq3QhvX30= cloud.google.com/go/osconfig v1.14.6/go.mod h1:LS39HDBH0IJDFgOUkhSZUHFQzmcWaCpYXLrc3A4CVzI= cloud.google.com/go/osconfig v1.15.1/go.mod h1:NegylQQl0+5m+I+4Ey/g3HGeQxKkncQ1q+Il4DZ8PME= +cloud.google.com/go/osconfig v1.16.0/go.mod h1:PRmLgZ1loD1hGaqnTBww1nETbqcqAvmTQOLYiIZ7Nvk= cloud.google.com/go/oslogin v1.10.1 h1:LdSuG3xBYu2Sgr3jTUULL1XCl5QBx6xwzGqzoDUw1j0= cloud.google.com/go/oslogin v1.11.1 h1:r3JYeLf004krfXhRMDfYKlBdMgDDc2q2PM1bomb5Luw= cloud.google.com/go/oslogin v1.11.1/go.mod h1:OhD2icArCVNUxKqtK0mcSmKL7lgr0LVlQz+v9s1ujTg= @@ -1301,6 +1312,7 @@ cloud.google.com/go/retail v1.20.0/go.mod h1:1CXWDZDJTOsK6lPjkv67gValP9+h1TMadTC cloud.google.com/go/retail v1.22.0 h1:4Nvso8WG3J2T8F78rs61g1INwpd3feX8WPI/gS71w8s= cloud.google.com/go/retail v1.22.0/go.mod h1:INfBxkiT1UcK+ohGFP9hsCM2UVECK3EzjfCut4lgDf8= cloud.google.com/go/retail v1.25.1/go.mod h1:J75G8pd+DH0SHueL9IJw7Y5d2VhTsjFsk+F1t9f8jXc= +cloud.google.com/go/retail v1.26.0/go.mod h1:gMfh6s174Mvy1rK4g50J9TH5sRim8px+Krml25kdrqo= cloud.google.com/go/run v1.2.0 h1:kHeIG8q+N6Zv0nDkBjSOYfK2eWqa5FnaiDPH/7/HirE= cloud.google.com/go/run v1.3.1 h1:xc46W9kxJI2De9hmpqHEBSSLJhP3bSZl86LdlJa5zm8= cloud.google.com/go/run v1.3.1/go.mod h1:cymddtZOzdwLIAsmS6s+Asl4JoXIDm/K1cpZTxV4Q5s= @@ -1766,6 +1778,7 @@ github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42/go.mod h1:W+zGtBO5Y1Ig github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443 h1:aQ3y1lwWyqYPiWZThqv1aFbZMiM9vblcSArJRf2Irls= github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= github.com/cncf/xds/go v0.0.0-20251022180443-0feb69152e9f/go.mod h1:HlzOvOjVBOfTGSRXRyY0OiCS/3J1akRGQQpRO/7zyF4= +github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5/go.mod h1:KdCmV+x/BuvyMxRnYBlmVaq4OLiKW6iRQfvC62cvdkI= github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI= @@ -1806,9 +1819,11 @@ github.com/envoyproxy/go-control-plane v0.13.1/go.mod h1:X45hY0mufo6Fd0KW3rqsGvQ github.com/envoyproxy/go-control-plane v0.13.4 h1:zEqyPVyku6IvWCFwux4x9RxkLOMUL+1vC9xUFv5l2/M= github.com/envoyproxy/go-control-plane v0.13.4/go.mod h1:kDfuBlDVsSj2MjrLEtRWtHlsWIFcGyB2RMO44Dc5GZA= github.com/envoyproxy/go-control-plane v0.13.5-0.20251024222203-75eaa193e329/go.mod h1:Alz8LEClvR7xKsrq3qzoc4N0guvVNSS8KmSChGYr9hs= +github.com/envoyproxy/go-control-plane v0.14.0/go.mod h1:NcS5X47pLl/hfqxU70yPwL9ZMkUlwlKxtAohpi2wBEU= github.com/envoyproxy/go-control-plane/envoy v1.32.4 h1:jb83lalDRZSpPWW2Z7Mck/8kXZ5CQAFYVjQcdVIr83A= github.com/envoyproxy/go-control-plane/envoy v1.32.4/go.mod h1:Gzjc5k8JcJswLjAx1Zm+wSYE20UrLtt7JZMWiWQXQEw= github.com/envoyproxy/go-control-plane/envoy v1.35.0/go.mod h1:09qwbGVuSWWAyN5t/b3iyVfz5+z8QWGrzkoqm/8SbEs= +github.com/envoyproxy/go-control-plane/envoy v1.36.0/go.mod h1:ty89S1YCCVruQAm9OtKeEkQLTb+Lkz0k8v9W0Oxsv98= github.com/envoyproxy/go-control-plane/ratelimit v0.1.0 h1:/G9QYbddjL25KvtKTv3an9lx6VBE2cnb8wp1vEGNYGI= github.com/envoyproxy/go-control-plane/ratelimit v0.1.0/go.mod h1:Wk+tMFAFbCXaJPzVVHnPgRKdUdwW/KdbRt94AzgRee4= github.com/envoyproxy/protoc-gen-validate v0.10.1 h1:c0g45+xCJhdgFGw7a5QAfdS4byAbud7miNWJ1WwEVf8= @@ -1817,6 +1832,7 @@ github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZ github.com/envoyproxy/protoc-gen-validate v1.1.0/go.mod h1:sXRDRVmzEbkM7CVcM06s9shE/m23dg3wzjl0UWqJ2q4= github.com/envoyproxy/protoc-gen-validate v1.2.1 h1:DEo3O99U8j4hBFwbJfrz9VtgcDfUKS7KJ7spH3d86P8= github.com/envoyproxy/protoc-gen-validate v1.2.1/go.mod h1:d/C80l/jxXLdfEIhX1W2TmLfsJ31lvEjwamM4DxlWXU= +github.com/envoyproxy/protoc-gen-validate v1.3.0/go.mod h1:HvYl7zwPa5mffgyeTUHA9zHIH36nmrm7oCbo4YKoSWA= github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8= @@ -2283,6 +2299,7 @@ go.opentelemetry.io/contrib/detectors/gcp v1.35.0/go.mod h1:qGWP8/+ILwMRIUf9uIVL go.opentelemetry.io/contrib/detectors/gcp v1.36.0 h1:F7q2tNlCaHY9nMKHR6XH9/qkp8FktLnIcy6jJNyOCQw= go.opentelemetry.io/contrib/detectors/gcp v1.36.0/go.mod h1:IbBN8uAIIx734PTonTPxAxnjc2pQTxWNkwfstZ+6H2k= go.opentelemetry.io/contrib/detectors/gcp v1.38.0/go.mod h1:SU+iU7nu5ud4oCb3LQOhIZ3nRLj6FNVrKgtflbaf2ts= +go.opentelemetry.io/contrib/detectors/gcp v1.39.0/go.mod h1:t/OGqzHBa5v6RHZwrDBJ2OirWc+4q/w2fTbLZwAKjTk= go.opentelemetry.io/contrib/instrumentation/github.com/bradfitz/gomemcache/memcache/otelmemcache v0.43.0 h1:UBMZi0bClix43Z5bGClUstfycTv5/GwGEpeXrkVCILw= go.opentelemetry.io/contrib/instrumentation/github.com/bradfitz/gomemcache/memcache/otelmemcache v0.43.0/go.mod h1:Y4/69ywKyaWZ5jN/NypeJyGLuoFjSpdyKuWVLClgDgM= go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI= @@ -2336,6 +2353,7 @@ golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM= golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY= golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0= golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A= +golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6 h1:QE6XYQK6naiK1EPAe1g/ILLxN5RBoH5xkJk3CqlMI/Y= golang.org/x/exp v0.0.0-20220827204233-334a2380cb91 h1:tnebWN09GYg9OLPss1KXj8txwZc6X6uMr6VFdcGNbHw= golang.org/x/exp v0.0.0-20230809150735-7b3493d9a819 h1:EDuYyU/MkFXllv9QF9819VlI9a4tzGuCbhG0ExK9o1U= @@ -2377,6 +2395,7 @@ golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w= golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc= golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg= +golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU= golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ= golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= @@ -2412,6 +2431,7 @@ golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/oauth2 v0.32.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= +golang.org/x/oauth2 v0.35.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= @@ -2465,6 +2485,7 @@ golang.org/x/term v0.33.0 h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg= golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0= golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg= golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww= +golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM= golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= @@ -2509,6 +2530,7 @@ golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs= golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ= golang.org/x/tools v0.40.0/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc= +golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= diff --git a/protoc_plugins/Dockerfile b/protoc_plugins/Dockerfile index d976914..106d44c 100644 --- a/protoc_plugins/Dockerfile +++ b/protoc_plugins/Dockerfile @@ -1,17 +1,26 @@ -FROM --platform=${TARGETPLATFORM:-linux/amd64} golang:1.22-alpine as builder +FROM golang:1.26-alpine AS builder + +ARG TARGETPLATFORM ENV CGO_ENABLED=0 WORKDIR /src -COPY protoc_plugins/ . +# https://buf.build/docs/bsr/remote-plugins/custom-plugins/#build-a-docker-image +RUN test "${TARGETPLATFORM}" = "linux/amd64" || (echo "buf plugin image must be built for linux/amd64" && exit 1) + +COPY protoc_plugins/go.mod protoc_plugins/go.sum ./ RUN go mod download -RUN go mod tidy -RUN go build -trimpath -ldflags "-s" -o protoc-gen-php-grpc-plugin protoc-gen-php-grpc/main.go +COPY protoc_plugins/ . + +RUN go build -trimpath -ldflags "-s -w" -o /out/protoc-gen-php-grpc-plugin ./protoc-gen-php-grpc FROM scratch -ARG APP_VERSION="" +# Supply real metadata at build time, for example: +# docker build --platform linux/amd64 --build-arg BUILD_TIME="$(date -u +%Y-%m-%dT%H:%M:%SZ)" --build-arg APP_VERSION="vX.Y.Z" -f protoc_plugins/Dockerfile . +ARG APP_VERSION="unknown" +ARG BUILD_TIME="unknown" # Runtime dependencies LABEL org.opencontainers.image.title="protoc-gen-php-grpc" @@ -19,10 +28,12 @@ LABEL org.opencontainers.image.description="protoc plugin for generating PHP gRP LABEL org.opencontainers.image.url="https://roadrunner.dev" LABEL org.opencontainers.image.source="https://github.com/roadrunner-server/grpc" LABEL org.opencontainers.image.vendor="SpiralScout" -LABEL org.opencontainers.image.version="$APP_VERSION" -LABEL org.opencontainers.image.created="$BUILD_TIME" +LABEL org.opencontainers.image.version="${APP_VERSION}" +LABEL org.opencontainers.image.created="${BUILD_TIME}" LABEL org.opencontainers.image.licenses="MIT" -COPY --from=builder /src/protoc-gen-php-grpc-plugin / +COPY --from=builder --chown=65532:65532 /out/protoc-gen-php-grpc-plugin /protoc-gen-php-grpc-plugin + +USER 65532:65532 ENTRYPOINT ["/protoc-gen-php-grpc-plugin"] diff --git a/protoc_plugins/go.mod b/protoc_plugins/go.mod index 18513a0..39f7d0f 100644 --- a/protoc_plugins/go.mod +++ b/protoc_plugins/go.mod @@ -1,8 +1,6 @@ module github.com/roadrunner-server/grpc/protoc_plugins/v5 -go 1.23 - -toolchain go1.23.1 +go 1.26 require ( github.com/stretchr/testify v1.11.1 diff --git a/protoc_plugins/go.sum b/protoc_plugins/go.sum index 50e526c..08827df 100644 --- a/protoc_plugins/go.sum +++ b/protoc_plugins/go.sum @@ -15,8 +15,11 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= diff --git a/schema.json b/schema.json index 9f1a0af..bc630d7 100644 --- a/schema.json +++ b/schema.json @@ -34,6 +34,24 @@ ] } }, + "interceptors": { + "description": "List of registered unary gRPC interceptor plugin names.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "minLength": 1 + }, + "examples": [ + [ + "sample-grpc-interceptor" + ], + [ + "auth-interceptor", + "ratelimit-interceptor" + ] + ] + }, "tls": { "description": "GRPC TLS configuration", "type": "object", diff --git a/server.go b/server.go index 2d9358b..86a12e7 100644 --- a/server.go +++ b/server.go @@ -33,11 +33,21 @@ func (p *Plugin) createGRPCserver(interceptors map[string]api.Interceptor) (*grp grpc.UnaryServerInterceptor(p.interceptor), } - for _, interceptor := range interceptors { - unaryInterceptors = append( - unaryInterceptors, - interceptor.UnaryServerInterceptor(), - ) + // if we have interceptors in the config, we need to chain them with our interceptor, and add them to the server options + if len(p.config.Interceptors) > 0 { + // apply interceptors in the same order as they are configured + for i := 0; i < len(p.config.Interceptors); i++ { + name := p.config.Interceptors[i] + if _, ok := interceptors[name]; !ok { + // we should raise an error here, since we may silently ignore let's say auth interceptor, which is critical for security + return nil, errors.E(op, errors.Str(fmt.Sprintf("interceptor %s is not registered", name))) + } + + unaryInterceptors = append( + unaryInterceptors, + interceptors[name].UnaryServerInterceptor(), + ) + } } opts = append( diff --git a/tests/configs/.rr-grpc-rq-interceptors.yaml b/tests/configs/.rr-grpc-rq-interceptors.yaml new file mode 100644 index 0000000..543016b --- /dev/null +++ b/tests/configs/.rr-grpc-rq-interceptors.yaml @@ -0,0 +1,30 @@ +version: '3' +rpc: + listen: "tcp://127.0.0.1:6011" +server: + command: "php php_test_files/worker-grpc.php" + relay: "pipes" + relay_timeout: "20s" +logs: + mode: development + level: error +grpc: + listen: "tcp://127.0.0.1:9011" + proto: + - "proto/service/service.proto" + interceptors: + - "interceptor1" + - "interceptor2" + max_send_msg_size: 50 + max_recv_msg_size: 50 + max_connection_idle: 0s + max_connection_age: 0s + max_connection_age_grace: 0s + max_concurrent_streams: 10 + ping_time: 1s + timeout: 200s + pool: + num_workers: 2 + max_jobs: 0 + allocate_timeout: 60s + destroy_timeout: 60s diff --git a/tests/doc.go b/tests/doc.go new file mode 100644 index 0000000..fe96ac3 --- /dev/null +++ b/tests/doc.go @@ -0,0 +1,2 @@ +// Package grpc_test contains end-to-end tests for the gRPC plugin. +package grpc_test diff --git a/tests/grpc_interceptors_test.go b/tests/grpc_interceptors_test.go new file mode 100644 index 0000000..c6f955e --- /dev/null +++ b/tests/grpc_interceptors_test.go @@ -0,0 +1,117 @@ +package grpc_test + +import ( + "context" + "log/slog" + "os" + "os/signal" + "strings" + "sync" + "syscall" + "testing" + "time" + + "tests/interceptor1" + "tests/interceptor2" + mocklogger "tests/mock" + "tests/proto/service" + + "github.com/roadrunner-server/config/v5" + "github.com/roadrunner-server/endure/v2" + grpcPlugin "github.com/roadrunner-server/grpc/v5" + rpcPlugin "github.com/roadrunner-server/rpc/v5" + "github.com/roadrunner-server/server/v5" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.uber.org/zap" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" +) + +func TestGrpcInterceptors(t *testing.T) { + cont := endure.New(slog.LevelDebug) + + cfg := &config.Plugin{ + Version: "2023.3.0", + Path: "configs/.rr-grpc-rq-interceptors.yaml", + } + + l, observed := mocklogger.ZapTestLogger(zap.DebugLevel) + err := cont.RegisterAll( + cfg, + &interceptor1.Plugin{}, + &interceptor2.Plugin{}, + &grpcPlugin.Plugin{}, + &rpcPlugin.Plugin{}, + &server.Plugin{}, + l, + ) + assert.NoError(t, err) + + err = cont.Init() + if err != nil { + t.Fatal(err) + } + + ch, err := cont.Serve() + assert.NoError(t, err) + + sig := make(chan os.Signal, 1) + signal.Notify(sig, os.Interrupt, syscall.SIGINT, syscall.SIGTERM) + + wg := &sync.WaitGroup{} + wg.Add(1) + + stopCh := make(chan struct{}, 1) + + go func() { + defer wg.Done() + for { + select { + case e := <-ch: + assert.Fail(t, "error", e.Error.Error()) + err = cont.Stop() + if err != nil { + assert.FailNow(t, "error", err.Error()) + } + case <-sig: + err = cont.Stop() + if err != nil { + assert.FailNow(t, "error", err.Error()) + } + return + case <-stopCh: + err = cont.Stop() + if err != nil { + assert.FailNow(t, "error", err.Error()) + } + return + } + } + }() + + time.Sleep(time.Second) + + conn, err := grpc.NewClient("127.0.0.1:9011", grpc.WithTransportCredentials(insecure.NewCredentials())) + require.NoError(t, err) + require.NotNil(t, conn) + + client := service.NewEchoClient(conn) + resp, err := client.Ping(context.Background(), &service.Message{Msg: "hello"}) + require.NoError(t, err) + require.Equal(t, "HELLO", resp.Msg) + _ = conn.Close() + + stopCh <- struct{}{} + wg.Wait() + + interceptor1Log := observed.FilterMessageSnippet("interceptor1 created marker") + require.Equal(t, 1, interceptor1Log.Len()) + + interceptor2Log := observed.FilterMessageSnippet("interceptor2 received marker") + require.Equal(t, 1, interceptor2Log.Len()) + + marker, ok := interceptor2Log.All()[0].ContextMap()["marker"].(string) + require.True(t, ok) + require.True(t, strings.HasPrefix(marker, interceptor1.MarkerPrefix)) +} diff --git a/tests/grpc_plugin_test.go b/tests/grpc_plugin_test.go index 18286e7..dbd4ac4 100644 --- a/tests/grpc_plugin_test.go +++ b/tests/grpc_plugin_test.go @@ -1074,7 +1074,8 @@ func TestGRPCMetrics(t *testing.T) { func sendReset(address string) func(t *testing.T) { return func(t *testing.T) { - conn, err := net.Dial("tcp", address) + var d net.Dialer + conn, err := d.DialContext(context.Background(), "tcp", address) assert.NoError(t, err) client := rpc.NewClientWithCodec(goridgeRpc.NewClientCodec(conn)) // WorkerList contains list of workers. diff --git a/tests/interceptor1/doc.go b/tests/interceptor1/doc.go new file mode 100644 index 0000000..b445172 --- /dev/null +++ b/tests/interceptor1/doc.go @@ -0,0 +1,2 @@ +// Package interceptor1 provides a simple test interceptor used in e2e tests. +package interceptor1 diff --git a/tests/interceptor1/interceptor1.go b/tests/interceptor1/interceptor1.go new file mode 100644 index 0000000..45c4e9d --- /dev/null +++ b/tests/interceptor1/interceptor1.go @@ -0,0 +1,61 @@ +package interceptor1 + +import ( + "context" + "fmt" + "time" + + "github.com/roadrunner-server/grpc/v5/api" + "go.uber.org/zap" + "google.golang.org/grpc" +) + +const ( + name = "interceptor1" + MarkerPrefix = "interceptor1-marker-" +) + +type Logger interface { + NamedLogger(name string) *zap.Logger +} + +type Plugin struct { + log *zap.Logger +} + +var _ api.Interceptor = (*Plugin)(nil) + +func (p *Plugin) Init(logger Logger) error { + p.log = logger.NamedLogger(name) + return nil +} + +type markerKey struct{} + +var markerCtxKey = markerKey{} //nolint:gochecknoglobals + +func MarkerFromContext(ctx context.Context) (string, bool) { + marker, ok := ctx.Value(markerCtxKey).(string) + return marker, ok +} + +func (p *Plugin) UnaryServerInterceptor() grpc.UnaryServerInterceptor { + return func(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) { + marker := fmt.Sprintf("%s%d", MarkerPrefix, time.Now().UnixNano()) + p.logger().Info("interceptor1 created marker", zap.String("method", info.FullMethod), zap.String("marker", marker)) + + return handler(context.WithValue(ctx, markerCtxKey, marker), req) + } +} + +func (p *Plugin) Name() string { + return name +} + +func (p *Plugin) logger() *zap.Logger { + if p.log == nil { + return zap.NewNop() + } + + return p.log +} diff --git a/tests/interceptor2/doc.go b/tests/interceptor2/doc.go new file mode 100644 index 0000000..bf825d9 --- /dev/null +++ b/tests/interceptor2/doc.go @@ -0,0 +1,2 @@ +// Package interceptor2 provides a simple test interceptor used in e2e tests. +package interceptor2 diff --git a/tests/interceptor2/interceptor2.go b/tests/interceptor2/interceptor2.go new file mode 100644 index 0000000..1185053 --- /dev/null +++ b/tests/interceptor2/interceptor2.go @@ -0,0 +1,53 @@ +package interceptor2 + +import ( + "context" + + "tests/interceptor1" + + "github.com/roadrunner-server/grpc/v5/api" + "go.uber.org/zap" + "google.golang.org/grpc" +) + +const name = "interceptor2" + +type Logger interface { + NamedLogger(name string) *zap.Logger +} + +type Plugin struct { + log *zap.Logger +} + +var _ api.Interceptor = (*Plugin)(nil) + +func (p *Plugin) Init(logger Logger) error { + p.log = logger.NamedLogger(name) + return nil +} + +func (p *Plugin) UnaryServerInterceptor() grpc.UnaryServerInterceptor { + return func(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) { + marker, ok := interceptor1.MarkerFromContext(ctx) + if ok { + p.logger().Info("interceptor2 received marker", zap.String("method", info.FullMethod), zap.String("marker", marker)) + } else { + panic("interceptor2 did not receive marker from interceptor1, context is not properly propagated") + } + + return handler(ctx, req) + } +} + +func (p *Plugin) Name() string { + return name +} + +func (p *Plugin) logger() *zap.Logger { + if p.log == nil { + return zap.NewNop() + } + + return p.log +} diff --git a/tests/mock/doc.go b/tests/mock/doc.go new file mode 100644 index 0000000..15dbcdb --- /dev/null +++ b/tests/mock/doc.go @@ -0,0 +1,2 @@ +// Package mocklogger contains test logger helpers and observed log utilities. +package mocklogger