vendor: google.golang.org/grpc v1.44.0

full diff: https://github.com/grpc/grpc-go/compare/v1.38.0...v1.44.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
Sebastiaan van Stijn 2022-03-24 13:21:41 +01:00
parent 0e3c9b282e
commit b2cef834fb
No known key found for this signature in database
GPG Key ID: 76698F39D527CE8C
74 changed files with 2090 additions and 1390 deletions

View File

@ -47,7 +47,7 @@ require (
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b
golang.org/x/text v0.3.7 golang.org/x/text v0.3.7
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect
google.golang.org/grpc v1.38.0 // indirect google.golang.org/grpc v1.44.0 // indirect
gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v2 v2.4.0
gotest.tools/v3 v3.1.0 gotest.tools/v3 v3.1.0
) )

View File

@ -69,6 +69,7 @@ github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:H
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0= github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
@ -119,6 +120,10 @@ github.com/cloudflare/cfssl v0.0.0-20180223231731-4e2dcbde5004 h1:lkAMpLVBDaj17e
github.com/cloudflare/cfssl v0.0.0-20180223231731-4e2dcbde5004/go.mod h1:yMWuSON2oQp+43nFtAV/uvKQIFpSPerB57DCt9t8sSA= github.com/cloudflare/cfssl v0.0.0-20180223231731-4e2dcbde5004/go.mod h1:yMWuSON2oQp+43nFtAV/uvKQIFpSPerB57DCt9t8sSA=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo= github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo=
github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA= github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA=
@ -292,7 +297,7 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0= github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0=
github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
@ -421,6 +426,7 @@ github.com/grpc-ecosystem/go-grpc-middleware v1.2.0/go.mod h1:mJzapYve32yjrKlk9G
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645/go.mod h1:6iZfnjpejD4L/4DwD7NryNaJyCQdzwWwH2MWhCA90Kw= github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645/go.mod h1:6iZfnjpejD4L/4DwD7NryNaJyCQdzwWwH2MWhCA90Kw=
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8= github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8=
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4= github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4=
@ -646,6 +652,7 @@ github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB8
github.com/prometheus/procfs v0.0.11 h1:DhHlBtkHWPYi8O2y31JkK0TF+DGM+51OopZjH/Ia5qI= github.com/prometheus/procfs v0.0.11 h1:DhHlBtkHWPYi8O2y31JkK0TF+DGM+51OopZjH/Ia5qI=
github.com/prometheus/procfs v0.0.11/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.0.11/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
@ -765,6 +772,7 @@ go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
@ -850,6 +858,7 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
@ -1046,6 +1055,7 @@ google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a h1:pOwg4OoaRYScjmR4LlLgdtnyoHYTSAVhhqe5uPdpII8= google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a h1:pOwg4OoaRYScjmR4LlLgdtnyoHYTSAVhhqe5uPdpII8=
google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
@ -1063,10 +1073,12 @@ google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.38.0 h1:/9BgsAsa5nWe26HqOlvlgJnqBuktYOLCgjCPqsa56W0= google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.44.0 h1:weqSxi/TMs1SqFRMHCtBgXRs8k3X39QIDEZ0pRcttUg=
google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@ -1109,6 +1121,7 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWD
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

View File

@ -1,42 +0,0 @@
language: go
matrix:
include:
- go: 1.14.x
env: VET=1 GO111MODULE=on
- go: 1.14.x
env: RACE=1 GO111MODULE=on
- go: 1.14.x
env: RUN386=1
- go: 1.14.x
env: GRPC_GO_RETRY=on
- go: 1.14.x
env: TESTEXTRAS=1
- go: 1.13.x
env: GO111MODULE=on
- go: 1.12.x
env: GO111MODULE=on
- go: 1.11.x # Keep until interop tests no longer require Go1.11
env: GO111MODULE=on
go_import_path: google.golang.org/grpc
before_install:
- if [[ "${GO111MODULE}" = "on" ]]; then mkdir "${HOME}/go"; export GOPATH="${HOME}/go"; fi
- if [[ -n "${RUN386}" ]]; then export GOARCH=386; fi
- if [[ "${TRAVIS_EVENT_TYPE}" = "cron" && -z "${RUN386}" ]]; then RACE=1; fi
- if [[ "${TRAVIS_EVENT_TYPE}" != "cron" ]]; then export VET_SKIP_PROTO=1; fi
install:
- try3() { eval "$*" || eval "$*" || eval "$*"; }
- try3 'if [[ "${GO111MODULE}" = "on" ]]; then go mod download; else make testdeps; fi'
- if [[ -n "${GAE}" ]]; then source ./install_gae.sh; make testappenginedeps; fi
- if [[ -n "${VET}" ]]; then ./vet.sh -install; fi
script:
- set -e
- if [[ -n "${TESTEXTRAS}" ]]; then examples/examples_test.sh; security/advancedtls/examples/examples_test.sh; interop/interop_test.sh; make testsubmodule; exit 0; fi
- if [[ -n "${VET}" ]]; then ./vet.sh; fi
- if [[ -n "${GAE}" ]]; then make testappengine; exit 0; fi
- if [[ -n "${RACE}" ]]; then make testrace; exit 0; fi
- make test

View File

@ -8,17 +8,18 @@ See [CONTRIBUTING.md](https://github.com/grpc/grpc-community/blob/master/CONTRIB
for general contribution guidelines. for general contribution guidelines.
## Maintainers (in alphabetical order) ## Maintainers (in alphabetical order)
- [canguler](https://github.com/canguler), Google LLC
- [cesarghali](https://github.com/cesarghali), Google LLC - [cesarghali](https://github.com/cesarghali), Google LLC
- [dfawley](https://github.com/dfawley), Google LLC - [dfawley](https://github.com/dfawley), Google LLC
- [easwars](https://github.com/easwars), Google LLC - [easwars](https://github.com/easwars), Google LLC
- [jadekler](https://github.com/jadekler), Google LLC
- [menghanl](https://github.com/menghanl), Google LLC - [menghanl](https://github.com/menghanl), Google LLC
- [srini100](https://github.com/srini100), Google LLC - [srini100](https://github.com/srini100), Google LLC
## Emeritus Maintainers (in alphabetical order) ## Emeritus Maintainers (in alphabetical order)
- [adelez](https://github.com/adelez), Google LLC - [adelez](https://github.com/adelez), Google LLC
- [canguler](https://github.com/canguler), Google LLC
- [iamqizhao](https://github.com/iamqizhao), Google LLC - [iamqizhao](https://github.com/iamqizhao), Google LLC
- [jadekler](https://github.com/jadekler), Google LLC
- [jtattermusch](https://github.com/jtattermusch), Google LLC - [jtattermusch](https://github.com/jtattermusch), Google LLC
- [lyuxuan](https://github.com/lyuxuan), Google LLC - [lyuxuan](https://github.com/lyuxuan), Google LLC
- [makmukhi](https://github.com/makmukhi), Google LLC - [makmukhi](https://github.com/makmukhi), Google LLC

View File

@ -41,8 +41,6 @@ vetdeps:
clean \ clean \
proto \ proto \
test \ test \
testappengine \
testappenginedeps \
testrace \ testrace \
vet \ vet \
vetdeps vetdeps

13
vendor/google.golang.org/grpc/NOTICE.txt generated vendored Normal file
View File

@ -0,0 +1,13 @@
Copyright 2014 gRPC authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -136,6 +136,6 @@ errors.
[Go module]: https://github.com/golang/go/wiki/Modules [Go module]: https://github.com/golang/go/wiki/Modules
[gRPC]: https://grpc.io [gRPC]: https://grpc.io
[Go gRPC docs]: https://grpc.io/docs/languages/go [Go gRPC docs]: https://grpc.io/docs/languages/go
[Performance benchmark]: https://performance-dot-grpc-testing.appspot.com/explore?dashboard=5652536396611584&widget=490377658&container=1286539696 [Performance benchmark]: https://performance-dot-grpc-testing.appspot.com/explore?dashboard=5180705743044608
[quick start]: https://grpc.io/docs/languages/go/quickstart [quick start]: https://grpc.io/docs/languages/go/quickstart
[go-releases]: https://golang.org/doc/devel/release.html [go-releases]: https://golang.org/doc/devel/release.html

View File

@ -25,55 +25,77 @@
// later release. // later release.
package attributes package attributes
import "fmt"
// Attributes is an immutable struct for storing and retrieving generic // Attributes is an immutable struct for storing and retrieving generic
// key/value pairs. Keys must be hashable, and users should define their own // key/value pairs. Keys must be hashable, and users should define their own
// types for keys. // types for keys. Values should not be modified after they are added to an
// Attributes or if they were received from one. If values implement 'Equal(o
// interface{}) bool', it will be called by (*Attributes).Equal to determine
// whether two values with the same key should be considered equal.
type Attributes struct { type Attributes struct {
m map[interface{}]interface{} m map[interface{}]interface{}
} }
// New returns a new Attributes containing all key/value pairs in kvs. If the // New returns a new Attributes containing the key/value pair.
// same key appears multiple times, the last value overwrites all previous func New(key, value interface{}) *Attributes {
// values for that key. Panics if len(kvs) is not even. return &Attributes{m: map[interface{}]interface{}{key: value}}
func New(kvs ...interface{}) *Attributes {
if len(kvs)%2 != 0 {
panic(fmt.Sprintf("attributes.New called with unexpected input: len(kvs) = %v", len(kvs)))
}
a := &Attributes{m: make(map[interface{}]interface{}, len(kvs)/2)}
for i := 0; i < len(kvs)/2; i++ {
a.m[kvs[i*2]] = kvs[i*2+1]
}
return a
} }
// WithValues returns a new Attributes containing all key/value pairs in a and // WithValue returns a new Attributes containing the previous keys and values
// kvs. Panics if len(kvs) is not even. If the same key appears multiple // and the new key/value pair. If the same key appears multiple times, the
// times, the last value overwrites all previous values for that key. To // last value overwrites all previous values for that key. To remove an
// remove an existing key, use a nil value. // existing key, use a nil value. value should not be modified later.
func (a *Attributes) WithValues(kvs ...interface{}) *Attributes { func (a *Attributes) WithValue(key, value interface{}) *Attributes {
if a == nil { if a == nil {
return New(kvs...) return New(key, value)
} }
if len(kvs)%2 != 0 { n := &Attributes{m: make(map[interface{}]interface{}, len(a.m)+1)}
panic(fmt.Sprintf("attributes.New called with unexpected input: len(kvs) = %v", len(kvs)))
}
n := &Attributes{m: make(map[interface{}]interface{}, len(a.m)+len(kvs)/2)}
for k, v := range a.m { for k, v := range a.m {
n.m[k] = v n.m[k] = v
} }
for i := 0; i < len(kvs)/2; i++ { n.m[key] = value
n.m[kvs[i*2]] = kvs[i*2+1]
}
return n return n
} }
// Value returns the value associated with these attributes for key, or nil if // Value returns the value associated with these attributes for key, or nil if
// no value is associated with key. // no value is associated with key. The returned value should not be modified.
func (a *Attributes) Value(key interface{}) interface{} { func (a *Attributes) Value(key interface{}) interface{} {
if a == nil { if a == nil {
return nil return nil
} }
return a.m[key] return a.m[key]
} }
// Equal returns whether a and o are equivalent. If 'Equal(o interface{})
// bool' is implemented for a value in the attributes, it is called to
// determine if the value matches the one stored in the other attributes. If
// Equal is not implemented, standard equality is used to determine if the two
// values are equal. Note that some types (e.g. maps) aren't comparable by
// default, so they must be wrapped in a struct, or in an alias type, with Equal
// defined.
func (a *Attributes) Equal(o *Attributes) bool {
if a == nil && o == nil {
return true
}
if a == nil || o == nil {
return false
}
if len(a.m) != len(o.m) {
return false
}
for k, v := range a.m {
ov, ok := o.m[k]
if !ok {
// o missing element of a
return false
}
if eq, ok := v.(interface{ Equal(o interface{}) bool }); ok {
if !eq.Equal(ov) {
return false
}
} else if v != ov {
// Fallback to a standard equality check if Value is unimplemented.
return false
}
}
return true
}

View File

@ -75,24 +75,26 @@ func Get(name string) Builder {
return nil return nil
} }
// SubConn represents a gRPC sub connection. // A SubConn represents a single connection to a gRPC backend service.
// Each sub connection contains a list of addresses. gRPC will
// try to connect to them (in sequence), and stop trying the
// remainder once one connection is successful.
// //
// The reconnect backoff will be applied on the list, not a single address. // Each SubConn contains a list of addresses.
// For example, try_on_all_addresses -> backoff -> try_on_all_addresses.
// //
// All SubConns start in IDLE, and will not try to connect. To trigger // All SubConns start in IDLE, and will not try to connect. To trigger the
// the connecting, Balancers must call Connect. // connecting, Balancers must call Connect. If a connection re-enters IDLE,
// When the connection encounters an error, it will reconnect immediately. // Balancers must call Connect again to trigger a new connection attempt.
// When the connection becomes IDLE, it will not reconnect unless Connect is
// called.
// //
// This interface is to be implemented by gRPC. Users should not need a // gRPC will try to connect to the addresses in sequence, and stop trying the
// brand new implementation of this interface. For the situations like // remainder once the first connection is successful. If an attempt to connect
// testing, the new implementation should embed this interface. This allows // to all addresses encounters an error, the SubConn will enter
// gRPC to add new methods to this interface. // TRANSIENT_FAILURE for a backoff period, and then transition to IDLE.
//
// Once established, if a connection is lost, the SubConn will transition
// directly to IDLE.
//
// This interface is to be implemented by gRPC. Users should not need their own
// implementation of this interface. For situations like testing, any
// implementations should embed this interface. This allows gRPC to add new
// methods to this interface.
type SubConn interface { type SubConn interface {
// UpdateAddresses updates the addresses used in this SubConn. // UpdateAddresses updates the addresses used in this SubConn.
// gRPC checks if currently-connected address is still in the new list. // gRPC checks if currently-connected address is still in the new list.
@ -172,25 +174,32 @@ type ClientConn interface {
// BuildOptions contains additional information for Build. // BuildOptions contains additional information for Build.
type BuildOptions struct { type BuildOptions struct {
// DialCreds is the transport credential the Balancer implementation can // DialCreds is the transport credentials to use when communicating with a
// use to dial to a remote load balancer server. The Balancer implementations // remote load balancer server. Balancer implementations which do not
// can ignore this if it does not need to talk to another party securely. // communicate with a remote load balancer server can ignore this field.
DialCreds credentials.TransportCredentials DialCreds credentials.TransportCredentials
// CredsBundle is the credentials bundle that the Balancer can use. // CredsBundle is the credentials bundle to use when communicating with a
// remote load balancer server. Balancer implementations which do not
// communicate with a remote load balancer server can ignore this field.
CredsBundle credentials.Bundle CredsBundle credentials.Bundle
// Dialer is the custom dialer the Balancer implementation can use to dial // Dialer is the custom dialer to use when communicating with a remote load
// to a remote load balancer server. The Balancer implementations // balancer server. Balancer implementations which do not communicate with a
// can ignore this if it doesn't need to talk to remote balancer. // remote load balancer server can ignore this field.
Dialer func(context.Context, string) (net.Conn, error) Dialer func(context.Context, string) (net.Conn, error)
// ChannelzParentID is the entity parent's channelz unique identification number. // Authority is the server name to use as part of the authentication
// handshake when communicating with a remote load balancer server. Balancer
// implementations which do not communicate with a remote load balancer
// server can ignore this field.
Authority string
// ChannelzParentID is the parent ClientConn's channelz ID.
ChannelzParentID int64 ChannelzParentID int64
// CustomUserAgent is the custom user agent set on the parent ClientConn. // CustomUserAgent is the custom user agent set on the parent ClientConn.
// The balancer should set the same custom user agent if it creates a // The balancer should set the same custom user agent if it creates a
// ClientConn. // ClientConn.
CustomUserAgent string CustomUserAgent string
// Target contains the parsed address info of the dial target. It is the same resolver.Target as // Target contains the parsed address info of the dial target. It is the
// passed to the resolver. // same resolver.Target as passed to the resolver. See the documentation for
// See the documentation for the resolver.Target type for details about what it contains. // the resolver.Target type for details about what it contains.
Target resolver.Target Target resolver.Target
} }
@ -326,6 +335,20 @@ type Balancer interface {
Close() Close()
} }
// ExitIdler is an optional interface for balancers to implement. If
// implemented, ExitIdle will be called when ClientConn.Connect is called, if
// the ClientConn is idle. If unimplemented, ClientConn.Connect will cause
// all SubConns to connect.
//
// Notice: it will be required for all balancers to implement this in a future
// release.
type ExitIdler interface {
// ExitIdle instructs the LB policy to reconnect to backends / exit the
// IDLE state, if appropriate and possible. Note that SubConns that enter
// the IDLE state will not reconnect until SubConn.Connect is called.
ExitIdle()
}
// SubConnState describes the state of a SubConn. // SubConnState describes the state of a SubConn.
type SubConnState struct { type SubConnState struct {
// ConnectivityState is the connectivity state of the SubConn. // ConnectivityState is the connectivity state of the SubConn.
@ -355,6 +378,8 @@ var ErrBadResolverState = errors.New("bad resolver state")
type ConnectivityStateEvaluator struct { type ConnectivityStateEvaluator struct {
numReady uint64 // Number of addrConns in ready state. numReady uint64 // Number of addrConns in ready state.
numConnecting uint64 // Number of addrConns in connecting state. numConnecting uint64 // Number of addrConns in connecting state.
numTransientFailure uint64 // Number of addrConns in transient failure state.
numIdle uint64 // Number of addrConns in idle state.
} }
// RecordTransition records state change happening in subConn and based on that // RecordTransition records state change happening in subConn and based on that
@ -362,9 +387,11 @@ type ConnectivityStateEvaluator struct {
// //
// - If at least one SubConn in Ready, the aggregated state is Ready; // - If at least one SubConn in Ready, the aggregated state is Ready;
// - Else if at least one SubConn in Connecting, the aggregated state is Connecting; // - Else if at least one SubConn in Connecting, the aggregated state is Connecting;
// - Else the aggregated state is TransientFailure. // - Else if at least one SubConn is TransientFailure, the aggregated state is Transient Failure;
// - Else if at least one SubConn is Idle, the aggregated state is Idle;
// - Else there are no subconns and the aggregated state is Transient Failure
// //
// Idle and Shutdown are not considered. // Shutdown is not considered.
func (cse *ConnectivityStateEvaluator) RecordTransition(oldState, newState connectivity.State) connectivity.State { func (cse *ConnectivityStateEvaluator) RecordTransition(oldState, newState connectivity.State) connectivity.State {
// Update counters. // Update counters.
for idx, state := range []connectivity.State{oldState, newState} { for idx, state := range []connectivity.State{oldState, newState} {
@ -374,6 +401,10 @@ func (cse *ConnectivityStateEvaluator) RecordTransition(oldState, newState conne
cse.numReady += updateVal cse.numReady += updateVal
case connectivity.Connecting: case connectivity.Connecting:
cse.numConnecting += updateVal cse.numConnecting += updateVal
case connectivity.TransientFailure:
cse.numTransientFailure += updateVal
case connectivity.Idle:
cse.numIdle += updateVal
} }
} }
@ -384,5 +415,11 @@ func (cse *ConnectivityStateEvaluator) RecordTransition(oldState, newState conne
if cse.numConnecting > 0 { if cse.numConnecting > 0 {
return connectivity.Connecting return connectivity.Connecting
} }
if cse.numTransientFailure > 0 {
return connectivity.TransientFailure
}
if cse.numIdle > 0 {
return connectivity.Idle
}
return connectivity.TransientFailure return connectivity.TransientFailure
} }

View File

@ -22,7 +22,6 @@ import (
"errors" "errors"
"fmt" "fmt"
"google.golang.org/grpc/attributes"
"google.golang.org/grpc/balancer" "google.golang.org/grpc/balancer"
"google.golang.org/grpc/connectivity" "google.golang.org/grpc/connectivity"
"google.golang.org/grpc/grpclog" "google.golang.org/grpc/grpclog"
@ -42,7 +41,7 @@ func (bb *baseBuilder) Build(cc balancer.ClientConn, opt balancer.BuildOptions)
cc: cc, cc: cc,
pickerBuilder: bb.pickerBuilder, pickerBuilder: bb.pickerBuilder,
subConns: make(map[resolver.Address]subConnInfo), subConns: resolver.NewAddressMap(),
scStates: make(map[balancer.SubConn]connectivity.State), scStates: make(map[balancer.SubConn]connectivity.State),
csEvltr: &balancer.ConnectivityStateEvaluator{}, csEvltr: &balancer.ConnectivityStateEvaluator{},
config: bb.config, config: bb.config,
@ -58,11 +57,6 @@ func (bb *baseBuilder) Name() string {
return bb.name return bb.name
} }
type subConnInfo struct {
subConn balancer.SubConn
attrs *attributes.Attributes
}
type baseBalancer struct { type baseBalancer struct {
cc balancer.ClientConn cc balancer.ClientConn
pickerBuilder PickerBuilder pickerBuilder PickerBuilder
@ -70,7 +64,7 @@ type baseBalancer struct {
csEvltr *balancer.ConnectivityStateEvaluator csEvltr *balancer.ConnectivityStateEvaluator
state connectivity.State state connectivity.State
subConns map[resolver.Address]subConnInfo // `attributes` is stripped from the keys of this map (the addresses) subConns *resolver.AddressMap
scStates map[balancer.SubConn]connectivity.State scStates map[balancer.SubConn]connectivity.State
picker balancer.Picker picker balancer.Picker
config Config config Config
@ -81,7 +75,7 @@ type baseBalancer struct {
func (b *baseBalancer) ResolverError(err error) { func (b *baseBalancer) ResolverError(err error) {
b.resolverErr = err b.resolverErr = err
if len(b.subConns) == 0 { if b.subConns.Len() == 0 {
b.state = connectivity.TransientFailure b.state = connectivity.TransientFailure
} }
@ -105,52 +99,29 @@ func (b *baseBalancer) UpdateClientConnState(s balancer.ClientConnState) error {
// Successful resolution; clear resolver error and ensure we return nil. // Successful resolution; clear resolver error and ensure we return nil.
b.resolverErr = nil b.resolverErr = nil
// addrsSet is the set converted from addrs, it's used for quick lookup of an address. // addrsSet is the set converted from addrs, it's used for quick lookup of an address.
addrsSet := make(map[resolver.Address]struct{}) addrsSet := resolver.NewAddressMap()
for _, a := range s.ResolverState.Addresses { for _, a := range s.ResolverState.Addresses {
// Strip attributes from addresses before using them as map keys. So addrsSet.Set(a, nil)
// that when two addresses only differ in attributes pointers (but with if _, ok := b.subConns.Get(a); !ok {
// the same attribute content), they are considered the same address.
//
// Note that this doesn't handle the case where the attribute content is
// different. So if users want to set different attributes to create
// duplicate connections to the same backend, it doesn't work. This is
// fine for now, because duplicate is done by setting Metadata today.
//
// TODO: read attributes to handle duplicate connections.
aNoAttrs := a
aNoAttrs.Attributes = nil
addrsSet[aNoAttrs] = struct{}{}
if scInfo, ok := b.subConns[aNoAttrs]; !ok {
// a is a new address (not existing in b.subConns). // a is a new address (not existing in b.subConns).
//
// When creating SubConn, the original address with attributes is
// passed through. So that connection configurations in attributes
// (like creds) will be used.
sc, err := b.cc.NewSubConn([]resolver.Address{a}, balancer.NewSubConnOptions{HealthCheckEnabled: b.config.HealthCheck}) sc, err := b.cc.NewSubConn([]resolver.Address{a}, balancer.NewSubConnOptions{HealthCheckEnabled: b.config.HealthCheck})
if err != nil { if err != nil {
logger.Warningf("base.baseBalancer: failed to create new SubConn: %v", err) logger.Warningf("base.baseBalancer: failed to create new SubConn: %v", err)
continue continue
} }
b.subConns[aNoAttrs] = subConnInfo{subConn: sc, attrs: a.Attributes} b.subConns.Set(a, sc)
b.scStates[sc] = connectivity.Idle b.scStates[sc] = connectivity.Idle
b.csEvltr.RecordTransition(connectivity.Shutdown, connectivity.Idle)
sc.Connect() sc.Connect()
} else {
// Always update the subconn's address in case the attributes
// changed.
//
// The SubConn does a reflect.DeepEqual of the new and old
// addresses. So this is a noop if the current address is the same
// as the old one (including attributes).
scInfo.attrs = a.Attributes
b.subConns[aNoAttrs] = scInfo
b.cc.UpdateAddresses(scInfo.subConn, []resolver.Address{a})
} }
} }
for a, scInfo := range b.subConns { for _, a := range b.subConns.Keys() {
sci, _ := b.subConns.Get(a)
sc := sci.(balancer.SubConn)
// a was removed by resolver. // a was removed by resolver.
if _, ok := addrsSet[a]; !ok { if _, ok := addrsSet.Get(a); !ok {
b.cc.RemoveSubConn(scInfo.subConn) b.cc.RemoveSubConn(sc)
delete(b.subConns, a) b.subConns.Delete(a)
// Keep the state of this sc in b.scStates until sc's state becomes Shutdown. // Keep the state of this sc in b.scStates until sc's state becomes Shutdown.
// The entry will be deleted in UpdateSubConnState. // The entry will be deleted in UpdateSubConnState.
} }
@ -192,10 +163,11 @@ func (b *baseBalancer) regeneratePicker() {
readySCs := make(map[balancer.SubConn]SubConnInfo) readySCs := make(map[balancer.SubConn]SubConnInfo)
// Filter out all ready SCs from full subConn map. // Filter out all ready SCs from full subConn map.
for addr, scInfo := range b.subConns { for _, addr := range b.subConns.Keys() {
if st, ok := b.scStates[scInfo.subConn]; ok && st == connectivity.Ready { sci, _ := b.subConns.Get(addr)
addr.Attributes = scInfo.attrs sc := sci.(balancer.SubConn)
readySCs[scInfo.subConn] = SubConnInfo{Address: addr} if st, ok := b.scStates[sc]; ok && st == connectivity.Ready {
readySCs[sc] = SubConnInfo{Address: addr}
} }
} }
b.picker = b.pickerBuilder.Build(PickerBuildInfo{ReadySCs: readySCs}) b.picker = b.pickerBuilder.Build(PickerBuildInfo{ReadySCs: readySCs})
@ -213,10 +185,14 @@ func (b *baseBalancer) UpdateSubConnState(sc balancer.SubConn, state balancer.Su
} }
return return
} }
if oldS == connectivity.TransientFailure && s == connectivity.Connecting { if oldS == connectivity.TransientFailure &&
// Once a subconn enters TRANSIENT_FAILURE, ignore subsequent (s == connectivity.Connecting || s == connectivity.Idle) {
// Once a subconn enters TRANSIENT_FAILURE, ignore subsequent IDLE or
// CONNECTING transitions to prevent the aggregated state from being // CONNECTING transitions to prevent the aggregated state from being
// always CONNECTING when many backends exist but are all down. // always CONNECTING when many backends exist but are all down.
if s == connectivity.Idle {
sc.Connect()
}
return return
} }
b.scStates[sc] = s b.scStates[sc] = s
@ -242,7 +218,6 @@ func (b *baseBalancer) UpdateSubConnState(sc balancer.SubConn, state balancer.Su
b.state == connectivity.TransientFailure { b.state == connectivity.TransientFailure {
b.regeneratePicker() b.regeneratePicker()
} }
b.cc.UpdateState(balancer.State{ConnectivityState: b.state, Picker: b.picker}) b.cc.UpdateState(balancer.State{ConnectivityState: b.state, Picker: b.picker})
} }
@ -251,6 +226,11 @@ func (b *baseBalancer) UpdateSubConnState(sc balancer.SubConn, state balancer.Su
func (b *baseBalancer) Close() { func (b *baseBalancer) Close() {
} }
// ExitIdle is a nop because the base balancer attempts to stay connected to
// all SubConns at all times.
func (b *baseBalancer) ExitIdle() {
}
// NewErrPicker returns a Picker that always returns err on Pick(). // NewErrPicker returns a Picker that always returns err on Pick().
func NewErrPicker(err error) balancer.Picker { func NewErrPicker(err error) balancer.Picker {
return &errPicker{err: err} return &errPicker{err: err}

View File

@ -39,7 +39,7 @@ type State struct {
// Set returns a copy of the provided state with attributes containing s. s's // Set returns a copy of the provided state with attributes containing s. s's
// data should not be mutated after calling Set. // data should not be mutated after calling Set.
func Set(state resolver.State, s *State) resolver.State { func Set(state resolver.State, s *State) resolver.State {
state.Attributes = state.Attributes.WithValues(key, s) state.Attributes = state.Attributes.WithValue(key, s)
return state return state
} }

View File

@ -47,11 +47,11 @@ func init() {
type rrPickerBuilder struct{} type rrPickerBuilder struct{}
func (*rrPickerBuilder) Build(info base.PickerBuildInfo) balancer.Picker { func (*rrPickerBuilder) Build(info base.PickerBuildInfo) balancer.Picker {
logger.Infof("roundrobinPicker: newPicker called with info: %v", info) logger.Infof("roundrobinPicker: Build called with info: %v", info)
if len(info.ReadySCs) == 0 { if len(info.ReadySCs) == 0 {
return base.NewErrPicker(balancer.ErrNoSubConnAvailable) return base.NewErrPicker(balancer.ErrNoSubConnAvailable)
} }
var scs []balancer.SubConn scs := make([]balancer.SubConn, 0, len(info.ReadySCs))
for sc := range info.ReadySCs { for sc := range info.ReadySCs {
scs = append(scs, sc) scs = append(scs, sc)
} }

View File

@ -37,13 +37,19 @@ type scStateUpdate struct {
err error err error
} }
// exitIdle contains no data and is just a signal sent on the updateCh in
// ccBalancerWrapper to instruct the balancer to exit idle.
type exitIdle struct{}
// ccBalancerWrapper is a wrapper on top of cc for balancers. // ccBalancerWrapper is a wrapper on top of cc for balancers.
// It implements balancer.ClientConn interface. // It implements balancer.ClientConn interface.
type ccBalancerWrapper struct { type ccBalancerWrapper struct {
cc *ClientConn cc *ClientConn
balancerMu sync.Mutex // synchronizes calls to the balancer balancerMu sync.Mutex // synchronizes calls to the balancer
balancer balancer.Balancer balancer balancer.Balancer
scBuffer *buffer.Unbounded hasExitIdle bool
updateCh *buffer.Unbounded
closed *grpcsync.Event
done *grpcsync.Event done *grpcsync.Event
mu sync.Mutex mu sync.Mutex
@ -53,12 +59,14 @@ type ccBalancerWrapper struct {
func newCCBalancerWrapper(cc *ClientConn, b balancer.Builder, bopts balancer.BuildOptions) *ccBalancerWrapper { func newCCBalancerWrapper(cc *ClientConn, b balancer.Builder, bopts balancer.BuildOptions) *ccBalancerWrapper {
ccb := &ccBalancerWrapper{ ccb := &ccBalancerWrapper{
cc: cc, cc: cc,
scBuffer: buffer.NewUnbounded(), updateCh: buffer.NewUnbounded(),
closed: grpcsync.NewEvent(),
done: grpcsync.NewEvent(), done: grpcsync.NewEvent(),
subConns: make(map[*acBalancerWrapper]struct{}), subConns: make(map[*acBalancerWrapper]struct{}),
} }
go ccb.watcher() go ccb.watcher()
ccb.balancer = b.Build(ccb, bopts) ccb.balancer = b.Build(ccb, bopts)
_, ccb.hasExitIdle = ccb.balancer.(balancer.ExitIdler)
return ccb return ccb
} }
@ -67,35 +75,72 @@ func newCCBalancerWrapper(cc *ClientConn, b balancer.Builder, bopts balancer.Bui
func (ccb *ccBalancerWrapper) watcher() { func (ccb *ccBalancerWrapper) watcher() {
for { for {
select { select {
case t := <-ccb.scBuffer.Get(): case t := <-ccb.updateCh.Get():
ccb.scBuffer.Load() ccb.updateCh.Load()
if ccb.done.HasFired() { if ccb.closed.HasFired() {
break break
} }
switch u := t.(type) {
case *scStateUpdate:
ccb.balancerMu.Lock() ccb.balancerMu.Lock()
su := t.(*scStateUpdate) ccb.balancer.UpdateSubConnState(u.sc, balancer.SubConnState{ConnectivityState: u.state, ConnectionError: u.err})
ccb.balancer.UpdateSubConnState(su.sc, balancer.SubConnState{ConnectivityState: su.state, ConnectionError: su.err})
ccb.balancerMu.Unlock() ccb.balancerMu.Unlock()
case <-ccb.done.Done(): case *acBalancerWrapper:
ccb.mu.Lock()
if ccb.subConns != nil {
delete(ccb.subConns, u)
ccb.cc.removeAddrConn(u.getAddrConn(), errConnDrain)
}
ccb.mu.Unlock()
case exitIdle:
if ccb.cc.GetState() == connectivity.Idle {
if ei, ok := ccb.balancer.(balancer.ExitIdler); ok {
// We already checked that the balancer implements
// ExitIdle before pushing the event to updateCh, but
// check conditionally again as defensive programming.
ccb.balancerMu.Lock()
ei.ExitIdle()
ccb.balancerMu.Unlock()
}
}
default:
logger.Errorf("ccBalancerWrapper.watcher: unknown update %+v, type %T", t, t)
}
case <-ccb.closed.Done():
} }
if ccb.done.HasFired() { if ccb.closed.HasFired() {
ccb.balancerMu.Lock()
ccb.balancer.Close() ccb.balancer.Close()
ccb.balancerMu.Unlock()
ccb.mu.Lock() ccb.mu.Lock()
scs := ccb.subConns scs := ccb.subConns
ccb.subConns = nil ccb.subConns = nil
ccb.mu.Unlock() ccb.mu.Unlock()
ccb.UpdateState(balancer.State{ConnectivityState: connectivity.Connecting, Picker: nil})
ccb.done.Fire()
// Fire done before removing the addr conns. We can safely unblock
// ccb.close and allow the removeAddrConns to happen
// asynchronously.
for acbw := range scs { for acbw := range scs {
ccb.cc.removeAddrConn(acbw.getAddrConn(), errConnDrain) ccb.cc.removeAddrConn(acbw.getAddrConn(), errConnDrain)
} }
ccb.UpdateState(balancer.State{ConnectivityState: connectivity.Connecting, Picker: nil})
return return
} }
} }
} }
func (ccb *ccBalancerWrapper) close() { func (ccb *ccBalancerWrapper) close() {
ccb.done.Fire() ccb.closed.Fire()
<-ccb.done.Done()
}
func (ccb *ccBalancerWrapper) exitIdle() bool {
if !ccb.hasExitIdle {
return false
}
ccb.updateCh.Put(exitIdle{})
return true
} }
func (ccb *ccBalancerWrapper) handleSubConnStateChange(sc balancer.SubConn, s connectivity.State, err error) { func (ccb *ccBalancerWrapper) handleSubConnStateChange(sc balancer.SubConn, s connectivity.State, err error) {
@ -109,7 +154,7 @@ func (ccb *ccBalancerWrapper) handleSubConnStateChange(sc balancer.SubConn, s co
if sc == nil { if sc == nil {
return return
} }
ccb.scBuffer.Put(&scStateUpdate{ ccb.updateCh.Put(&scStateUpdate{
sc: sc, sc: sc,
state: s, state: s,
err: err, err: err,
@ -124,8 +169,8 @@ func (ccb *ccBalancerWrapper) updateClientConnState(ccs *balancer.ClientConnStat
func (ccb *ccBalancerWrapper) resolverError(err error) { func (ccb *ccBalancerWrapper) resolverError(err error) {
ccb.balancerMu.Lock() ccb.balancerMu.Lock()
defer ccb.balancerMu.Unlock()
ccb.balancer.ResolverError(err) ccb.balancer.ResolverError(err)
ccb.balancerMu.Unlock()
} }
func (ccb *ccBalancerWrapper) NewSubConn(addrs []resolver.Address, opts balancer.NewSubConnOptions) (balancer.SubConn, error) { func (ccb *ccBalancerWrapper) NewSubConn(addrs []resolver.Address, opts balancer.NewSubConnOptions) (balancer.SubConn, error) {
@ -150,17 +195,10 @@ func (ccb *ccBalancerWrapper) NewSubConn(addrs []resolver.Address, opts balancer
} }
func (ccb *ccBalancerWrapper) RemoveSubConn(sc balancer.SubConn) { func (ccb *ccBalancerWrapper) RemoveSubConn(sc balancer.SubConn) {
acbw, ok := sc.(*acBalancerWrapper) // The RemoveSubConn() is handled in the run() goroutine, to avoid deadlock
if !ok { // during switchBalancer() if the old balancer calls RemoveSubConn() in its
return // Close().
} ccb.updateCh.Put(sc)
ccb.mu.Lock()
defer ccb.mu.Unlock()
if ccb.subConns == nil {
return
}
delete(ccb.subConns, acbw)
ccb.cc.removeAddrConn(acbw.getAddrConn(), errConnDrain)
} }
func (ccb *ccBalancerWrapper) UpdateAddresses(sc balancer.SubConn, addrs []resolver.Address) { func (ccb *ccBalancerWrapper) UpdateAddresses(sc balancer.SubConn, addrs []resolver.Address) {
@ -226,17 +264,17 @@ func (acbw *acBalancerWrapper) UpdateAddresses(addrs []resolver.Address) {
return return
} }
ac, err := cc.newAddrConn(addrs, opts) newAC, err := cc.newAddrConn(addrs, opts)
if err != nil { if err != nil {
channelz.Warningf(logger, acbw.ac.channelzID, "acBalancerWrapper: UpdateAddresses: failed to newAddrConn: %v", err) channelz.Warningf(logger, acbw.ac.channelzID, "acBalancerWrapper: UpdateAddresses: failed to newAddrConn: %v", err)
return return
} }
acbw.ac = ac acbw.ac = newAC
ac.mu.Lock() newAC.mu.Lock()
ac.acbw = acbw newAC.acbw = acbw
ac.mu.Unlock() newAC.mu.Unlock()
if acState != connectivity.Idle { if acState != connectivity.Idle {
ac.connect() go newAC.connect()
} }
} }
} }
@ -244,7 +282,7 @@ func (acbw *acBalancerWrapper) UpdateAddresses(addrs []resolver.Address) {
func (acbw *acBalancerWrapper) Connect() { func (acbw *acBalancerWrapper) Connect() {
acbw.mu.Lock() acbw.mu.Lock()
defer acbw.mu.Unlock() defer acbw.mu.Unlock()
acbw.ac.connect() go acbw.ac.connect()
} }
func (acbw *acBalancerWrapper) getAddrConn() *addrConn { func (acbw *acBalancerWrapper) getAddrConn() *addrConn {

View File

@ -23,6 +23,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"math" "math"
"net/url"
"reflect" "reflect"
"strings" "strings"
"sync" "sync"
@ -37,7 +38,6 @@ import (
"google.golang.org/grpc/internal/backoff" "google.golang.org/grpc/internal/backoff"
"google.golang.org/grpc/internal/channelz" "google.golang.org/grpc/internal/channelz"
"google.golang.org/grpc/internal/grpcsync" "google.golang.org/grpc/internal/grpcsync"
"google.golang.org/grpc/internal/grpcutil"
iresolver "google.golang.org/grpc/internal/resolver" iresolver "google.golang.org/grpc/internal/resolver"
"google.golang.org/grpc/internal/transport" "google.golang.org/grpc/internal/transport"
"google.golang.org/grpc/keepalive" "google.golang.org/grpc/keepalive"
@ -83,13 +83,13 @@ var (
// errTransportCredsAndBundle indicates that creds bundle is used together // errTransportCredsAndBundle indicates that creds bundle is used together
// with other individual Transport Credentials. // with other individual Transport Credentials.
errTransportCredsAndBundle = errors.New("grpc: credentials.Bundle may not be used with individual TransportCredentials") errTransportCredsAndBundle = errors.New("grpc: credentials.Bundle may not be used with individual TransportCredentials")
// errTransportCredentialsMissing indicates that users want to transmit security // errNoTransportCredsInBundle indicated that the configured creds bundle
// information (e.g., OAuth2 token) which requires secure connection on an insecure // returned a transport credentials which was nil.
// connection. errNoTransportCredsInBundle = errors.New("grpc: credentials.Bundle must return non-nil transport credentials")
// errTransportCredentialsMissing indicates that users want to transmit
// security information (e.g., OAuth2 token) which requires secure
// connection on an insecure connection.
errTransportCredentialsMissing = errors.New("grpc: the credentials require transport level security (use grpc.WithTransportCredentials() to set)") errTransportCredentialsMissing = errors.New("grpc: the credentials require transport level security (use grpc.WithTransportCredentials() to set)")
// errCredentialsConflict indicates that grpc.WithTransportCredentials()
// and grpc.WithInsecure() are both called for a connection.
errCredentialsConflict = errors.New("grpc: transport credentials are set for an insecure connection (grpc.WithTransportCredentials() and grpc.WithInsecure() are both called)")
) )
const ( const (
@ -177,17 +177,20 @@ func DialContext(ctx context.Context, target string, opts ...DialOption) (conn *
cc.csMgr.channelzID = cc.channelzID cc.csMgr.channelzID = cc.channelzID
} }
if !cc.dopts.insecure {
if cc.dopts.copts.TransportCredentials == nil && cc.dopts.copts.CredsBundle == nil { if cc.dopts.copts.TransportCredentials == nil && cc.dopts.copts.CredsBundle == nil {
return nil, errNoTransportSecurity return nil, errNoTransportSecurity
} }
if cc.dopts.copts.TransportCredentials != nil && cc.dopts.copts.CredsBundle != nil { if cc.dopts.copts.TransportCredentials != nil && cc.dopts.copts.CredsBundle != nil {
return nil, errTransportCredsAndBundle return nil, errTransportCredsAndBundle
} }
} else { if cc.dopts.copts.CredsBundle != nil && cc.dopts.copts.CredsBundle.TransportCredentials() == nil {
if cc.dopts.copts.TransportCredentials != nil || cc.dopts.copts.CredsBundle != nil { return nil, errNoTransportCredsInBundle
return nil, errCredentialsConflict
} }
transportCreds := cc.dopts.copts.TransportCredentials
if transportCreds == nil {
transportCreds = cc.dopts.copts.CredsBundle.TransportCredentials()
}
if transportCreds.Info().SecurityProtocol == "insecure" {
for _, cd := range cc.dopts.copts.PerRPCCredentials { for _, cd := range cc.dopts.copts.PerRPCCredentials {
if cd.RequireTransportSecurity() { if cd.RequireTransportSecurity() {
return nil, errTransportCredentialsMissing return nil, errTransportCredentialsMissing
@ -248,38 +251,15 @@ func DialContext(ctx context.Context, target string, opts ...DialOption) (conn *
} }
// Determine the resolver to use. // Determine the resolver to use.
cc.parsedTarget = grpcutil.ParseTarget(cc.target, cc.dopts.copts.Dialer != nil) resolverBuilder, err := cc.parseTargetAndFindResolver()
channelz.Infof(logger, cc.channelzID, "parsed scheme: %q", cc.parsedTarget.Scheme) if err != nil {
resolverBuilder := cc.getResolver(cc.parsedTarget.Scheme) return nil, err
if resolverBuilder == nil {
// If resolver builder is still nil, the parsed target's scheme is
// not registered. Fallback to default resolver and set Endpoint to
// the original target.
channelz.Infof(logger, cc.channelzID, "scheme %q not registered, fallback to default scheme", cc.parsedTarget.Scheme)
cc.parsedTarget = resolver.Target{
Scheme: resolver.GetDefaultScheme(),
Endpoint: target,
} }
resolverBuilder = cc.getResolver(cc.parsedTarget.Scheme) cc.authority, err = determineAuthority(cc.parsedTarget.Endpoint, cc.target, cc.dopts)
if resolverBuilder == nil { if err != nil {
return nil, fmt.Errorf("could not get resolver for default scheme: %q", cc.parsedTarget.Scheme) return nil, err
}
}
creds := cc.dopts.copts.TransportCredentials
if creds != nil && creds.Info().ServerName != "" {
cc.authority = creds.Info().ServerName
} else if cc.dopts.insecure && cc.dopts.authority != "" {
cc.authority = cc.dopts.authority
} else if strings.HasPrefix(cc.target, "unix:") || strings.HasPrefix(cc.target, "unix-abstract:") {
cc.authority = "localhost"
} else if strings.HasPrefix(cc.parsedTarget.Endpoint, ":") {
cc.authority = "localhost" + cc.parsedTarget.Endpoint
} else {
// Use endpoint from "scheme://authority/endpoint" as the default
// authority for ClientConn.
cc.authority = cc.parsedTarget.Endpoint
} }
channelz.Infof(logger, cc.channelzID, "Channel authority set to %q", cc.authority)
if cc.dopts.scChan != nil && !scSet { if cc.dopts.scChan != nil && !scSet {
// Blocking wait for the initial service config. // Blocking wait for the initial service config.
@ -305,6 +285,7 @@ func DialContext(ctx context.Context, target string, opts ...DialOption) (conn *
DialCreds: credsClone, DialCreds: credsClone,
CredsBundle: cc.dopts.copts.CredsBundle, CredsBundle: cc.dopts.copts.CredsBundle,
Dialer: cc.dopts.copts.Dialer, Dialer: cc.dopts.copts.Dialer,
Authority: cc.authority,
CustomUserAgent: cc.dopts.copts.UserAgent, CustomUserAgent: cc.dopts.copts.UserAgent,
ChannelzParentID: cc.channelzID, ChannelzParentID: cc.channelzID,
Target: cc.parsedTarget, Target: cc.parsedTarget,
@ -322,6 +303,7 @@ func DialContext(ctx context.Context, target string, opts ...DialOption) (conn *
// A blocking dial blocks until the clientConn is ready. // A blocking dial blocks until the clientConn is ready.
if cc.dopts.block { if cc.dopts.block {
for { for {
cc.Connect()
s := cc.GetState() s := cc.GetState()
if s == connectivity.Ready { if s == connectivity.Ready {
break break
@ -539,12 +521,31 @@ func (cc *ClientConn) WaitForStateChange(ctx context.Context, sourceState connec
// //
// Experimental // Experimental
// //
// Notice: This API is EXPERIMENTAL and may be changed or removed in a // Notice: This API is EXPERIMENTAL and may be changed or removed in a later
// later release. // release.
func (cc *ClientConn) GetState() connectivity.State { func (cc *ClientConn) GetState() connectivity.State {
return cc.csMgr.getState() return cc.csMgr.getState()
} }
// Connect causes all subchannels in the ClientConn to attempt to connect if
// the channel is idle. Does not wait for the connection attempts to begin
// before returning.
//
// Experimental
//
// Notice: This API is EXPERIMENTAL and may be changed or removed in a later
// release.
func (cc *ClientConn) Connect() {
cc.mu.Lock()
defer cc.mu.Unlock()
if cc.balancerWrapper != nil && cc.balancerWrapper.exitIdle() {
return
}
for ac := range cc.conns {
go ac.connect()
}
}
func (cc *ClientConn) scWatcher() { func (cc *ClientConn) scWatcher() {
for { for {
select { select {
@ -632,7 +633,10 @@ func (cc *ClientConn) updateResolverState(s resolver.State, err error) error {
} }
var ret error var ret error
if cc.dopts.disableServiceConfig || s.ServiceConfig == nil { if cc.dopts.disableServiceConfig {
channelz.Infof(logger, cc.channelzID, "ignoring service config from resolver (%v) and applying the default because service config is disabled", s.ServiceConfig)
cc.maybeApplyDefaultServiceConfig(s.Addresses)
} else if s.ServiceConfig == nil {
cc.maybeApplyDefaultServiceConfig(s.Addresses) cc.maybeApplyDefaultServiceConfig(s.Addresses)
// TODO: do we need to apply a failing LB policy if there is no // TODO: do we need to apply a failing LB policy if there is no
// default, per the error handling design? // default, per the error handling design?
@ -711,7 +715,12 @@ func (cc *ClientConn) switchBalancer(name string) {
return return
} }
if cc.balancerWrapper != nil { if cc.balancerWrapper != nil {
// Don't hold cc.mu while closing the balancers. The balancers may call
// methods that require cc.mu (e.g. cc.NewSubConn()). Holding the mutex
// would cause a deadlock in that case.
cc.mu.Unlock()
cc.balancerWrapper.close() cc.balancerWrapper.close()
cc.mu.Lock()
} }
builder := balancer.Get(name) builder := balancer.Get(name)
@ -840,8 +849,7 @@ func (ac *addrConn) connect() error {
ac.updateConnectivityState(connectivity.Connecting, nil) ac.updateConnectivityState(connectivity.Connecting, nil)
ac.mu.Unlock() ac.mu.Unlock()
// Start a goroutine connecting to the server asynchronously. ac.resetTransport()
go ac.resetTransport()
return nil return nil
} }
@ -878,6 +886,7 @@ func (ac *addrConn) tryUpdateAddrs(addrs []resolver.Address) bool {
// ac.state is Ready, try to find the connected address. // ac.state is Ready, try to find the connected address.
var curAddrFound bool var curAddrFound bool
for _, a := range addrs { for _, a := range addrs {
a.ServerName = ac.cc.getServerName(a)
if reflect.DeepEqual(ac.curAddr, a) { if reflect.DeepEqual(ac.curAddr, a) {
curAddrFound = true curAddrFound = true
break break
@ -891,6 +900,26 @@ func (ac *addrConn) tryUpdateAddrs(addrs []resolver.Address) bool {
return curAddrFound return curAddrFound
} }
// getServerName determines the serverName to be used in the connection
// handshake. The default value for the serverName is the authority on the
// ClientConn, which either comes from the user's dial target or through an
// authority override specified using the WithAuthority dial option. Name
// resolvers can specify a per-address override for the serverName through the
// resolver.Address.ServerName field which is used only if the WithAuthority
// dial option was not used. The rationale is that per-address authority
// overrides specified by the name resolver can represent a security risk, while
// an override specified by the user is more dependable since they probably know
// what they are doing.
func (cc *ClientConn) getServerName(addr resolver.Address) string {
if cc.dopts.authority != "" {
return cc.dopts.authority
}
if addr.ServerName != "" {
return addr.ServerName
}
return cc.authority
}
func getMethodConfig(sc *ServiceConfig, method string) MethodConfig { func getMethodConfig(sc *ServiceConfig, method string) MethodConfig {
if sc == nil { if sc == nil {
return MethodConfig{} return MethodConfig{}
@ -1046,12 +1075,12 @@ func (cc *ClientConn) Close() error {
cc.blockingpicker.close() cc.blockingpicker.close()
if rWrapper != nil {
rWrapper.close()
}
if bWrapper != nil { if bWrapper != nil {
bWrapper.close() bWrapper.close()
} }
if rWrapper != nil {
rWrapper.close()
}
for ac := range conns { for ac := range conns {
ac.tearDown(ErrClientConnClosing) ac.tearDown(ErrClientConnClosing)
@ -1130,11 +1159,6 @@ func (ac *addrConn) adjustParams(r transport.GoAwayReason) {
} }
func (ac *addrConn) resetTransport() { func (ac *addrConn) resetTransport() {
for i := 0; ; i++ {
if i > 0 {
ac.cc.resolveNow(resolver.ResolveNowOptions{})
}
ac.mu.Lock() ac.mu.Lock()
if ac.state == connectivity.Shutdown { if ac.state == connectivity.Shutdown {
ac.mu.Unlock() ac.mu.Unlock()
@ -1162,11 +1186,10 @@ func (ac *addrConn) resetTransport() {
connectDeadline := time.Now().Add(dialDuration) connectDeadline := time.Now().Add(dialDuration)
ac.updateConnectivityState(connectivity.Connecting, nil) ac.updateConnectivityState(connectivity.Connecting, nil)
ac.transport = nil
ac.mu.Unlock() ac.mu.Unlock()
newTr, addr, reconnect, err := ac.tryAllAddrs(addrs, connectDeadline) if err := ac.tryAllAddrs(addrs, connectDeadline); err != nil {
if err != nil { ac.cc.resolveNow(resolver.ResolveNowOptions{})
// After exhausting all addresses, the addrConn enters // After exhausting all addresses, the addrConn enters
// TRANSIENT_FAILURE. // TRANSIENT_FAILURE.
ac.mu.Lock() ac.mu.Lock()
@ -1192,50 +1215,30 @@ func (ac *addrConn) resetTransport() {
timer.Stop() timer.Stop()
return return
} }
continue
}
ac.mu.Lock() ac.mu.Lock()
if ac.state == connectivity.Shutdown { if ac.state != connectivity.Shutdown {
ac.updateConnectivityState(connectivity.Idle, err)
}
ac.mu.Unlock() ac.mu.Unlock()
newTr.Close(fmt.Errorf("reached connectivity state: SHUTDOWN"))
return return
} }
ac.curAddr = addr // Success; reset backoff.
ac.transport = newTr ac.mu.Lock()
ac.backoffIdx = 0 ac.backoffIdx = 0
hctx, hcancel := context.WithCancel(ac.ctx)
ac.startHealthCheck(hctx)
ac.mu.Unlock() ac.mu.Unlock()
// Block until the created transport is down. And when this happens,
// we restart from the top of the addr list.
<-reconnect.Done()
hcancel()
// restart connecting - the top of the loop will set state to
// CONNECTING. This is against the current connectivity semantics doc,
// however it allows for graceful behavior for RPCs not yet dispatched
// - unfortunate timing would otherwise lead to the RPC failing even
// though the TRANSIENT_FAILURE state (called for by the doc) would be
// instantaneous.
//
// Ideally we should transition to Idle here and block until there is
// RPC activity that leads to the balancer requesting a reconnect of
// the associated SubConn.
}
} }
// tryAllAddrs tries to creates a connection to the addresses, and stop when at the // tryAllAddrs tries to creates a connection to the addresses, and stop when at
// first successful one. It returns the transport, the address and a Event in // the first successful one. It returns an error if no address was successfully
// the successful case. The Event fires when the returned transport disconnects. // connected, or updates ac appropriately with the new transport.
func (ac *addrConn) tryAllAddrs(addrs []resolver.Address, connectDeadline time.Time) (transport.ClientTransport, resolver.Address, *grpcsync.Event, error) { func (ac *addrConn) tryAllAddrs(addrs []resolver.Address, connectDeadline time.Time) error {
var firstConnErr error var firstConnErr error
for _, addr := range addrs { for _, addr := range addrs {
ac.mu.Lock() ac.mu.Lock()
if ac.state == connectivity.Shutdown { if ac.state == connectivity.Shutdown {
ac.mu.Unlock() ac.mu.Unlock()
return nil, resolver.Address{}, nil, errConnClosing return errConnClosing
} }
ac.cc.mu.RLock() ac.cc.mu.RLock()
@ -1250,9 +1253,9 @@ func (ac *addrConn) tryAllAddrs(addrs []resolver.Address, connectDeadline time.T
channelz.Infof(logger, ac.channelzID, "Subchannel picks a new address %q to connect", addr.Addr) channelz.Infof(logger, ac.channelzID, "Subchannel picks a new address %q to connect", addr.Addr)
newTr, reconnect, err := ac.createTransport(addr, copts, connectDeadline) err := ac.createTransport(addr, copts, connectDeadline)
if err == nil { if err == nil {
return newTr, addr, reconnect, nil return nil
} }
if firstConnErr == nil { if firstConnErr == nil {
firstConnErr = err firstConnErr = err
@ -1261,57 +1264,50 @@ func (ac *addrConn) tryAllAddrs(addrs []resolver.Address, connectDeadline time.T
} }
// Couldn't connect to any address. // Couldn't connect to any address.
return nil, resolver.Address{}, nil, firstConnErr return firstConnErr
} }
// createTransport creates a connection to addr. It returns the transport and a // createTransport creates a connection to addr. It returns an error if the
// Event in the successful case. The Event fires when the returned transport // address was not successfully connected, or updates ac appropriately with the
// disconnects. // new transport.
func (ac *addrConn) createTransport(addr resolver.Address, copts transport.ConnectOptions, connectDeadline time.Time) (transport.ClientTransport, *grpcsync.Event, error) { func (ac *addrConn) createTransport(addr resolver.Address, copts transport.ConnectOptions, connectDeadline time.Time) error {
prefaceReceived := make(chan struct{}) // TODO: Delete prefaceReceived and move the logic to wait for it into the
onCloseCalled := make(chan struct{}) // transport.
reconnect := grpcsync.NewEvent() prefaceReceived := grpcsync.NewEvent()
connClosed := grpcsync.NewEvent()
// addr.ServerName takes precedent over ClientConn authority, if present. addr.ServerName = ac.cc.getServerName(addr)
if addr.ServerName == "" { hctx, hcancel := context.WithCancel(ac.ctx)
addr.ServerName = ac.cc.authority hcStarted := false // protected by ac.mu
}
once := sync.Once{}
onGoAway := func(r transport.GoAwayReason) {
ac.mu.Lock()
ac.adjustParams(r)
once.Do(func() {
if ac.state == connectivity.Ready {
// Prevent this SubConn from being used for new RPCs by setting its
// state to Connecting.
//
// TODO: this should be Idle when grpc-go properly supports it.
ac.updateConnectivityState(connectivity.Connecting, nil)
}
})
ac.mu.Unlock()
reconnect.Fire()
}
onClose := func() { onClose := func() {
ac.mu.Lock() ac.mu.Lock()
once.Do(func() { defer ac.mu.Unlock()
if ac.state == connectivity.Ready { defer connClosed.Fire()
// Prevent this SubConn from being used for new RPCs by setting its if !hcStarted || hctx.Err() != nil {
// state to Connecting. // We didn't start the health check or set the state to READY, so
// no need to do anything else here.
// //
// TODO: this should be Idle when grpc-go properly supports it. // OR, we have already cancelled the health check context, meaning
ac.updateConnectivityState(connectivity.Connecting, nil) // we have already called onClose once for this transport. In this
// case it would be dangerous to clear the transport and update the
// state, since there may be a new transport in this addrConn.
return
}
hcancel()
ac.transport = nil
// Refresh the name resolver
ac.cc.resolveNow(resolver.ResolveNowOptions{})
if ac.state != connectivity.Shutdown {
ac.updateConnectivityState(connectivity.Idle, nil)
} }
})
ac.mu.Unlock()
close(onCloseCalled)
reconnect.Fire()
} }
onPrefaceReceipt := func() { onGoAway := func(r transport.GoAwayReason) {
close(prefaceReceived) ac.mu.Lock()
ac.adjustParams(r)
ac.mu.Unlock()
onClose()
} }
connectCtx, cancel := context.WithDeadline(ac.ctx, connectDeadline) connectCtx, cancel := context.WithDeadline(ac.ctx, connectDeadline)
@ -1320,27 +1316,67 @@ func (ac *addrConn) createTransport(addr resolver.Address, copts transport.Conne
copts.ChannelzParentID = ac.channelzID copts.ChannelzParentID = ac.channelzID
} }
newTr, err := transport.NewClientTransport(connectCtx, ac.cc.ctx, addr, copts, onPrefaceReceipt, onGoAway, onClose) newTr, err := transport.NewClientTransport(connectCtx, ac.cc.ctx, addr, copts, func() { prefaceReceived.Fire() }, onGoAway, onClose)
if err != nil { if err != nil {
// newTr is either nil, or closed. // newTr is either nil, or closed.
channelz.Warningf(logger, ac.channelzID, "grpc: addrConn.createTransport failed to connect to %v. Err: %v. Reconnecting...", addr, err) channelz.Warningf(logger, ac.channelzID, "grpc: addrConn.createTransport failed to connect to %v. Err: %v", addr, err)
return nil, nil, err return err
} }
select { select {
case <-time.After(time.Until(connectDeadline)): case <-connectCtx.Done():
// We didn't get the preface in time. // We didn't get the preface in time.
newTr.Close(fmt.Errorf("failed to receive server preface within timeout")) // The error we pass to Close() is immaterial since there are no open
channelz.Warningf(logger, ac.channelzID, "grpc: addrConn.createTransport failed to connect to %v: didn't receive server preface in time. Reconnecting...", addr) // streams at this point, so no trailers with error details will be sent
return nil, nil, errors.New("timed out waiting for server handshake") // out. We just need to pass a non-nil error.
case <-prefaceReceived: newTr.Close(transport.ErrConnClosing)
// We got the preface - huzzah! things are good. if connectCtx.Err() == context.DeadlineExceeded {
case <-onCloseCalled: err := errors.New("failed to receive server preface within timeout")
// The transport has already closed - noop. channelz.Warningf(logger, ac.channelzID, "grpc: addrConn.createTransport failed to connect to %v: %v", addr, err)
return nil, nil, errors.New("connection closed") return err
// TODO(deklerk) this should bail on ac.ctx.Done(). Add a test and fix. }
return nil
case <-prefaceReceived.Done():
// We got the preface - huzzah! things are good.
ac.mu.Lock()
defer ac.mu.Unlock()
if connClosed.HasFired() {
// onClose called first; go idle but do nothing else.
if ac.state != connectivity.Shutdown {
ac.updateConnectivityState(connectivity.Idle, nil)
}
return nil
}
if ac.state == connectivity.Shutdown {
// This can happen if the subConn was removed while in `Connecting`
// state. tearDown() would have set the state to `Shutdown`, but
// would not have closed the transport since ac.transport would not
// been set at that point.
//
// We run this in a goroutine because newTr.Close() calls onClose()
// inline, which requires locking ac.mu.
//
// The error we pass to Close() is immaterial since there are no open
// streams at this point, so no trailers with error details will be sent
// out. We just need to pass a non-nil error.
go newTr.Close(transport.ErrConnClosing)
return nil
}
ac.curAddr = addr
ac.transport = newTr
hcStarted = true
ac.startHealthCheck(hctx) // Will set state to READY if appropriate.
return nil
case <-connClosed.Done():
// The transport has already closed. If we received the preface, too,
// this is not an error.
select {
case <-prefaceReceived.Done():
return nil
default:
return errors.New("connection closed before server preface received")
}
} }
return newTr, reconnect, nil
} }
// startHealthCheck starts the health checking stream (RPC) to watch the health // startHealthCheck starts the health checking stream (RPC) to watch the health
@ -1424,26 +1460,14 @@ func (ac *addrConn) resetConnectBackoff() {
ac.mu.Unlock() ac.mu.Unlock()
} }
// getReadyTransport returns the transport if ac's state is READY. // getReadyTransport returns the transport if ac's state is READY or nil if not.
// Otherwise it returns nil, false. func (ac *addrConn) getReadyTransport() transport.ClientTransport {
// If ac's state is IDLE, it will trigger ac to connect.
func (ac *addrConn) getReadyTransport() (transport.ClientTransport, bool) {
ac.mu.Lock() ac.mu.Lock()
if ac.state == connectivity.Ready && ac.transport != nil { defer ac.mu.Unlock()
t := ac.transport if ac.state == connectivity.Ready {
ac.mu.Unlock() return ac.transport
return t, true
} }
var idle bool return nil
if ac.state == connectivity.Idle {
idle = true
}
ac.mu.Unlock()
// Trigger idle ac to connect.
if idle {
ac.connect()
}
return nil, false
} }
// tearDown starts to tear down the addrConn. // tearDown starts to tear down the addrConn.
@ -1594,3 +1618,114 @@ func (cc *ClientConn) connectionError() error {
defer cc.lceMu.Unlock() defer cc.lceMu.Unlock()
return cc.lastConnectionError return cc.lastConnectionError
} }
func (cc *ClientConn) parseTargetAndFindResolver() (resolver.Builder, error) {
channelz.Infof(logger, cc.channelzID, "original dial target is: %q", cc.target)
var rb resolver.Builder
parsedTarget, err := parseTarget(cc.target)
if err != nil {
channelz.Infof(logger, cc.channelzID, "dial target %q parse failed: %v", cc.target, err)
} else {
channelz.Infof(logger, cc.channelzID, "parsed dial target is: %+v", parsedTarget)
rb = cc.getResolver(parsedTarget.Scheme)
if rb != nil {
cc.parsedTarget = parsedTarget
return rb, nil
}
}
// We are here because the user's dial target did not contain a scheme or
// specified an unregistered scheme. We should fallback to the default
// scheme, except when a custom dialer is specified in which case, we should
// always use passthrough scheme.
defScheme := resolver.GetDefaultScheme()
channelz.Infof(logger, cc.channelzID, "fallback to scheme %q", defScheme)
canonicalTarget := defScheme + ":///" + cc.target
parsedTarget, err = parseTarget(canonicalTarget)
if err != nil {
channelz.Infof(logger, cc.channelzID, "dial target %q parse failed: %v", canonicalTarget, err)
return nil, err
}
channelz.Infof(logger, cc.channelzID, "parsed dial target is: %+v", parsedTarget)
rb = cc.getResolver(parsedTarget.Scheme)
if rb == nil {
return nil, fmt.Errorf("could not get resolver for default scheme: %q", parsedTarget.Scheme)
}
cc.parsedTarget = parsedTarget
return rb, nil
}
// parseTarget uses RFC 3986 semantics to parse the given target into a
// resolver.Target struct containing scheme, authority and endpoint. Query
// params are stripped from the endpoint.
func parseTarget(target string) (resolver.Target, error) {
u, err := url.Parse(target)
if err != nil {
return resolver.Target{}, err
}
// For targets of the form "[scheme]://[authority]/endpoint, the endpoint
// value returned from url.Parse() contains a leading "/". Although this is
// in accordance with RFC 3986, we do not want to break existing resolver
// implementations which expect the endpoint without the leading "/". So, we
// end up stripping the leading "/" here. But this will result in an
// incorrect parsing for something like "unix:///path/to/socket". Since we
// own the "unix" resolver, we can workaround in the unix resolver by using
// the `URL` field instead of the `Endpoint` field.
endpoint := u.Path
if endpoint == "" {
endpoint = u.Opaque
}
endpoint = strings.TrimPrefix(endpoint, "/")
return resolver.Target{
Scheme: u.Scheme,
Authority: u.Host,
Endpoint: endpoint,
URL: *u,
}, nil
}
// Determine channel authority. The order of precedence is as follows:
// - user specified authority override using `WithAuthority` dial option
// - creds' notion of server name for the authentication handshake
// - endpoint from dial target of the form "scheme://[authority]/endpoint"
func determineAuthority(endpoint, target string, dopts dialOptions) (string, error) {
// Historically, we had two options for users to specify the serverName or
// authority for a channel. One was through the transport credentials
// (either in its constructor, or through the OverrideServerName() method).
// The other option (for cases where WithInsecure() dial option was used)
// was to use the WithAuthority() dial option.
//
// A few things have changed since:
// - `insecure` package with an implementation of the `TransportCredentials`
// interface for the insecure case
// - WithAuthority() dial option support for secure credentials
authorityFromCreds := ""
if creds := dopts.copts.TransportCredentials; creds != nil && creds.Info().ServerName != "" {
authorityFromCreds = creds.Info().ServerName
}
authorityFromDialOption := dopts.authority
if (authorityFromCreds != "" && authorityFromDialOption != "") && authorityFromCreds != authorityFromDialOption {
return "", fmt.Errorf("ClientConn's authority from transport creds %q and dial option %q don't match", authorityFromCreds, authorityFromDialOption)
}
switch {
case authorityFromDialOption != "":
return authorityFromDialOption, nil
case authorityFromCreds != "":
return authorityFromCreds, nil
case strings.HasPrefix(target, "unix:") || strings.HasPrefix(target, "unix-abstract:"):
// TODO: remove when the unix resolver implements optional interface to
// return channel authority.
return "localhost", nil
case strings.HasPrefix(endpoint, ":"):
return "localhost" + endpoint, nil
default:
// TODO: Define an optional interface on the resolver builder to return
// the channel authority given the user's dial target. For resolvers
// which don't implement this interface, we will use the endpoint from
// "scheme://authority/endpoint" as the default authority.
return endpoint, nil
}
}

View File

@ -18,7 +18,6 @@
// Package connectivity defines connectivity semantics. // Package connectivity defines connectivity semantics.
// For details, see https://github.com/grpc/grpc/blob/master/doc/connectivity-semantics-and-api.md. // For details, see https://github.com/grpc/grpc/blob/master/doc/connectivity-semantics-and-api.md.
// All APIs in this package are experimental.
package connectivity package connectivity
import ( import (
@ -45,7 +44,7 @@ func (s State) String() string {
return "SHUTDOWN" return "SHUTDOWN"
default: default:
logger.Errorf("unknown connectivity state: %d", s) logger.Errorf("unknown connectivity state: %d", s)
return "Invalid-State" return "INVALID_STATE"
} }
} }
@ -61,3 +60,35 @@ const (
// Shutdown indicates the ClientConn has started shutting down. // Shutdown indicates the ClientConn has started shutting down.
Shutdown Shutdown
) )
// ServingMode indicates the current mode of operation of the server.
//
// Only xDS enabled gRPC servers currently report their serving mode.
type ServingMode int
const (
// ServingModeStarting indicates that the server is starting up.
ServingModeStarting ServingMode = iota
// ServingModeServing indicates that the server contains all required
// configuration and is serving RPCs.
ServingModeServing
// ServingModeNotServing indicates that the server is not accepting new
// connections. Existing connections will be closed gracefully, allowing
// in-progress RPCs to complete. A server enters this mode when it does not
// contain the required configuration to serve RPCs.
ServingModeNotServing
)
func (s ServingMode) String() string {
switch s {
case ServingModeStarting:
return "STARTING"
case ServingModeServing:
return "SERVING"
case ServingModeNotServing:
return "NOT_SERVING"
default:
logger.Errorf("unknown serving mode: %d", s)
return "INVALID_MODE"
}
}

View File

@ -140,6 +140,11 @@ type TransportCredentials interface {
// Additionally, ClientHandshakeInfo data will be available via the context // Additionally, ClientHandshakeInfo data will be available via the context
// passed to this call. // passed to this call.
// //
// The second argument to this method is the `:authority` header value used
// while creating new streams on this connection after authentication
// succeeds. Implementations must use this as the server name during the
// authentication handshake.
//
// If the returned net.Conn is closed, it MUST close the net.Conn provided. // If the returned net.Conn is closed, it MUST close the net.Conn provided.
ClientHandshake(context.Context, string, net.Conn) (net.Conn, AuthInfo, error) ClientHandshake(context.Context, string, net.Conn) (net.Conn, AuthInfo, error)
// ServerHandshake does the authentication handshake for servers. It returns // ServerHandshake does the authentication handshake for servers. It returns
@ -153,9 +158,13 @@ type TransportCredentials interface {
Info() ProtocolInfo Info() ProtocolInfo
// Clone makes a copy of this TransportCredentials. // Clone makes a copy of this TransportCredentials.
Clone() TransportCredentials Clone() TransportCredentials
// OverrideServerName overrides the server name used to verify the hostname on the returned certificates from the server. // OverrideServerName specifies the value used for the following:
// gRPC internals also use it to override the virtual hosting name if it is set. // - verifying the hostname on the returned certificates
// It must be called before dialing. Currently, this is only used by grpclb. // - as SNI in the client's handshake to support virtual hosting
// - as the value for `:authority` header at stream creation time
//
// Deprecated: use grpc.WithAuthority instead. Will be supported
// throughout 1.x.
OverrideServerName(string) error OverrideServerName(string) error
} }
@ -169,8 +178,18 @@ type TransportCredentials interface {
// //
// This API is experimental. // This API is experimental.
type Bundle interface { type Bundle interface {
// TransportCredentials returns the transport credentials from the Bundle.
//
// Implementations must return non-nil transport credentials. If transport
// security is not needed by the Bundle, implementations may choose to
// return insecure.NewCredentials().
TransportCredentials() TransportCredentials TransportCredentials() TransportCredentials
// PerRPCCredentials returns the per-RPC credentials from the Bundle.
//
// May be nil if per-RPC credentials are not needed.
PerRPCCredentials() PerRPCCredentials PerRPCCredentials() PerRPCCredentials
// NewWithMode should make a copy of Bundle, and switch mode. Modifying the // NewWithMode should make a copy of Bundle, and switch mode. Modifying the
// existing Bundle may cause races. // existing Bundle may cause races.
// //

View File

@ -1,30 +0,0 @@
// +build go1.12
/*
*
* Copyright 2019 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package credentials
import "crypto/tls"
// This init function adds cipher suite constants only defined in Go 1.12.
func init() {
cipherSuiteLookup[tls.TLS_AES_128_GCM_SHA256] = "TLS_AES_128_GCM_SHA256"
cipherSuiteLookup[tls.TLS_AES_256_GCM_SHA384] = "TLS_AES_256_GCM_SHA384"
cipherSuiteLookup[tls.TLS_CHACHA20_POLY1305_SHA256] = "TLS_CHACHA20_POLY1305_SHA256"
}

View File

@ -0,0 +1,72 @@
/*
*
* Copyright 2020 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
// Package insecure provides an implementation of the
// credentials.TransportCredentials interface which disables transport security.
package insecure
import (
"context"
"net"
"google.golang.org/grpc/credentials"
)
// NewCredentials returns a credentials which disables transport security.
//
// Note that using this credentials with per-RPC credentials which require
// transport security is incompatible and will cause grpc.Dial() to fail.
func NewCredentials() credentials.TransportCredentials {
return insecureTC{}
}
// insecureTC implements the insecure transport credentials. The handshake
// methods simply return the passed in net.Conn and set the security level to
// NoSecurity.
type insecureTC struct{}
func (insecureTC) ClientHandshake(ctx context.Context, _ string, conn net.Conn) (net.Conn, credentials.AuthInfo, error) {
return conn, info{credentials.CommonAuthInfo{SecurityLevel: credentials.NoSecurity}}, nil
}
func (insecureTC) ServerHandshake(conn net.Conn) (net.Conn, credentials.AuthInfo, error) {
return conn, info{credentials.CommonAuthInfo{SecurityLevel: credentials.NoSecurity}}, nil
}
func (insecureTC) Info() credentials.ProtocolInfo {
return credentials.ProtocolInfo{SecurityProtocol: "insecure"}
}
func (insecureTC) Clone() credentials.TransportCredentials {
return insecureTC{}
}
func (insecureTC) OverrideServerName(string) error {
return nil
}
// info contains the auth information for an insecure connection.
// It implements the AuthInfo interface.
type info struct {
credentials.CommonAuthInfo
}
// AuthType returns the type of info as a string.
func (info) AuthType() string {
return "insecure"
}

View File

@ -230,4 +230,7 @@ var cipherSuiteLookup = map[uint16]string{
tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256: "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256: "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305: "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305", tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305: "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305",
tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305: "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305", tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305: "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305",
tls.TLS_AES_128_GCM_SHA256: "TLS_AES_128_GCM_SHA256",
tls.TLS_AES_256_GCM_SHA384: "TLS_AES_256_GCM_SHA384",
tls.TLS_CHACHA20_POLY1305_SHA256: "TLS_CHACHA20_POLY1305_SHA256",
} }

View File

@ -27,9 +27,9 @@ import (
"google.golang.org/grpc/backoff" "google.golang.org/grpc/backoff"
"google.golang.org/grpc/balancer" "google.golang.org/grpc/balancer"
"google.golang.org/grpc/credentials" "google.golang.org/grpc/credentials"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/internal" "google.golang.org/grpc/internal"
internalbackoff "google.golang.org/grpc/internal/backoff" internalbackoff "google.golang.org/grpc/internal/backoff"
"google.golang.org/grpc/internal/envconfig"
"google.golang.org/grpc/internal/transport" "google.golang.org/grpc/internal/transport"
"google.golang.org/grpc/keepalive" "google.golang.org/grpc/keepalive"
"google.golang.org/grpc/resolver" "google.golang.org/grpc/resolver"
@ -50,7 +50,6 @@ type dialOptions struct {
bs internalbackoff.Strategy bs internalbackoff.Strategy
block bool block bool
returnLastError bool returnLastError bool
insecure bool
timeout time.Duration timeout time.Duration
scChan <-chan ServiceConfig scChan <-chan ServiceConfig
authority string authority string
@ -228,18 +227,14 @@ func WithServiceConfig(c <-chan ServiceConfig) DialOption {
}) })
} }
// WithConnectParams configures the dialer to use the provided ConnectParams. // WithConnectParams configures the ClientConn to use the provided ConnectParams
// for creating and maintaining connections to servers.
// //
// The backoff configuration specified as part of the ConnectParams overrides // The backoff configuration specified as part of the ConnectParams overrides
// all defaults specified in // all defaults specified in
// https://github.com/grpc/grpc/blob/master/doc/connection-backoff.md. Consider // https://github.com/grpc/grpc/blob/master/doc/connection-backoff.md. Consider
// using the backoff.DefaultConfig as a base, in cases where you want to // using the backoff.DefaultConfig as a base, in cases where you want to
// override only a subset of the backoff configuration. // override only a subset of the backoff configuration.
//
// Experimental
//
// Notice: This API is EXPERIMENTAL and may be changed or removed in a
// later release.
func WithConnectParams(p ConnectParams) DialOption { func WithConnectParams(p ConnectParams) DialOption {
return newFuncDialOption(func(o *dialOptions) { return newFuncDialOption(func(o *dialOptions) {
o.bs = internalbackoff.Exponential{Config: p.Backoff} o.bs = internalbackoff.Exponential{Config: p.Backoff}
@ -277,7 +272,7 @@ func withBackoff(bs internalbackoff.Strategy) DialOption {
}) })
} }
// WithBlock returns a DialOption which makes caller of Dial blocks until the // WithBlock returns a DialOption which makes callers of Dial block until the
// underlying connection is up. Without this, Dial returns immediately and // underlying connection is up. Without this, Dial returns immediately and
// connecting the server happens in background. // connecting the server happens in background.
func WithBlock() DialOption { func WithBlock() DialOption {
@ -303,11 +298,17 @@ func WithReturnConnectionError() DialOption {
} }
// WithInsecure returns a DialOption which disables transport security for this // WithInsecure returns a DialOption which disables transport security for this
// ClientConn. Note that transport security is required unless WithInsecure is // ClientConn. Under the hood, it uses insecure.NewCredentials().
// set. //
// Note that using this DialOption with per-RPC credentials (through
// WithCredentialsBundle or WithPerRPCCredentials) which require transport
// security is incompatible and will cause grpc.Dial() to fail.
//
// Deprecated: use WithTransportCredentials and insecure.NewCredentials() instead.
// Will be supported throughout 1.x.
func WithInsecure() DialOption { func WithInsecure() DialOption {
return newFuncDialOption(func(o *dialOptions) { return newFuncDialOption(func(o *dialOptions) {
o.insecure = true o.copts.TransportCredentials = insecure.NewCredentials()
}) })
} }
@ -482,8 +483,7 @@ func WithChainStreamInterceptor(interceptors ...StreamClientInterceptor) DialOpt
} }
// WithAuthority returns a DialOption that specifies the value to be used as the // WithAuthority returns a DialOption that specifies the value to be used as the
// :authority pseudo-header. This value only works with WithInsecure and has no // :authority pseudo-header and as the server name in authentication handshake.
// effect if TransportCredentials are present.
func WithAuthority(a string) DialOption { func WithAuthority(a string) DialOption {
return newFuncDialOption(func(o *dialOptions) { return newFuncDialOption(func(o *dialOptions) {
o.authority = a o.authority = a
@ -519,14 +519,16 @@ func WithDisableServiceConfig() DialOption {
// WithDefaultServiceConfig returns a DialOption that configures the default // WithDefaultServiceConfig returns a DialOption that configures the default
// service config, which will be used in cases where: // service config, which will be used in cases where:
// //
// 1. WithDisableServiceConfig is also used. // 1. WithDisableServiceConfig is also used, or
// 2. Resolver does not return a service config or if the resolver returns an //
// 2. The name resolver does not provide a service config or provides an
// invalid service config. // invalid service config.
// //
// Experimental // The parameter s is the JSON representation of the default service config.
// // For more information about service configs, see:
// Notice: This API is EXPERIMENTAL and may be changed or removed in a // https://github.com/grpc/grpc/blob/master/doc/service_config.md
// later release. // For a simple example of usage, see:
// examples/features/load_balancing/client/main.go
func WithDefaultServiceConfig(s string) DialOption { func WithDefaultServiceConfig(s string) DialOption {
return newFuncDialOption(func(o *dialOptions) { return newFuncDialOption(func(o *dialOptions) {
o.defaultServiceConfigRawJSON = &s o.defaultServiceConfigRawJSON = &s
@ -538,14 +540,8 @@ func WithDefaultServiceConfig(s string) DialOption {
// will happen automatically if no data is written to the wire or if the RPC is // will happen automatically if no data is written to the wire or if the RPC is
// unprocessed by the remote server. // unprocessed by the remote server.
// //
// Retry support is currently disabled by default, but will be enabled by // Retry support is currently enabled by default, but may be disabled by
// default in the future. Until then, it may be enabled by setting the // setting the environment variable "GRPC_GO_RETRY" to "off".
// environment variable "GRPC_GO_RETRY" to "on".
//
// Experimental
//
// Notice: This API is EXPERIMENTAL and may be changed or removed in a
// later release.
func WithDisableRetry() DialOption { func WithDisableRetry() DialOption {
return newFuncDialOption(func(o *dialOptions) { return newFuncDialOption(func(o *dialOptions) {
o.disableRetry = true o.disableRetry = true
@ -585,7 +581,6 @@ func withHealthCheckFunc(f internal.HealthChecker) DialOption {
func defaultDialOptions() dialOptions { func defaultDialOptions() dialOptions {
return dialOptions{ return dialOptions{
disableRetry: !envconfig.Retry,
healthCheckFunc: internal.HealthCheckFunc, healthCheckFunc: internal.HealthCheckFunc,
copts: transport.ConnectOptions{ copts: transport.ConnectOptions{
WriteBufferSize: defaultWriteBufSize, WriteBufferSize: defaultWriteBufSize,

16
vendor/google.golang.org/grpc/go.mod generated vendored
View File

@ -1,17 +1,19 @@
module google.golang.org/grpc module google.golang.org/grpc
go 1.11 go 1.14
require ( require (
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403 github.com/cespare/xxhash/v2 v2.1.1
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b
github.com/golang/protobuf v1.4.2 github.com/golang/protobuf v1.4.3
github.com/google/go-cmp v0.5.0 github.com/google/go-cmp v0.5.0
github.com/google/uuid v1.1.2 github.com/google/uuid v1.1.2
golang.org/x/net v0.0.0-20190311183353-d8887717615a golang.org/x/net v0.0.0-20200822124328-c89045814202
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013
google.golang.org/protobuf v1.25.0 google.golang.org/protobuf v1.25.0
) )

55
vendor/google.golang.org/grpc/go.sum generated vendored
View File

@ -1,32 +1,47 @@
cloud.google.com/go v0.26.0 h1:e0WKqKTd5BnrG8aKH3J3h+QvEIQtSUcf2n5UZ5ZgLtQ=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0 h1:eOI3/cP2VTU6uZLDYAoic+eyzzB9YyGmJ7eIjl8rOPg=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/census-instrumentation/opencensus-proto v0.2.1 h1:glEXhBS5PSLLv4IXzLA5yPRVX4bilULVyxxbrfOtDAk= github.com/census-instrumentation/opencensus-proto v0.2.1 h1:glEXhBS5PSLLv4IXzLA5yPRVX4bilULVyxxbrfOtDAk=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403 h1:cqQfy1jclcSy/FwLjemeg3SR1yaINm74aQyupQ0Bl8M= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4 h1:hzAQntlaYRkVSFEfj9OTWlVV1H155FMD8BTKktLv0QI=
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1 h1:zH8ljVhhq7yC0MIeUL/IviMtY8hx2mK8cN9wEYb8ggw=
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d h1:QyzYnTnPE15SQyUeqU6qLbWxMkwyAyu+vGksa0b7j00= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021 h1:fP+fF0up6oPY49OrjPrhIJ8yQfdIM85NXMLkMg1EXVs=
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
github.com/envoyproxy/protoc-gen-validate v0.1.0 h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A= github.com/envoyproxy/protoc-gen-validate v0.1.0 h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
@ -35,49 +50,65 @@ github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be h1:vEDujvNQGv4jgYKudGeI/+DAX4Jffq6hpD55MmoEvKs= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508= google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@ -90,7 +121,9 @@ google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

View File

@ -19,11 +19,14 @@
package grpclog package grpclog
import ( import (
"encoding/json"
"fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"log" "log"
"os" "os"
"strconv" "strconv"
"strings"
"google.golang.org/grpc/internal/grpclog" "google.golang.org/grpc/internal/grpclog"
) )
@ -97,6 +100,7 @@ var severityName = []string{
type loggerT struct { type loggerT struct {
m []*log.Logger m []*log.Logger
v int v int
jsonFormat bool
} }
// NewLoggerV2 creates a loggerV2 with the provided writers. // NewLoggerV2 creates a loggerV2 with the provided writers.
@ -105,19 +109,32 @@ type loggerT struct {
// Warning logs will be written to warningW and infoW. // Warning logs will be written to warningW and infoW.
// Info logs will be written to infoW. // Info logs will be written to infoW.
func NewLoggerV2(infoW, warningW, errorW io.Writer) LoggerV2 { func NewLoggerV2(infoW, warningW, errorW io.Writer) LoggerV2 {
return NewLoggerV2WithVerbosity(infoW, warningW, errorW, 0) return newLoggerV2WithConfig(infoW, warningW, errorW, loggerV2Config{})
} }
// NewLoggerV2WithVerbosity creates a loggerV2 with the provided writers and // NewLoggerV2WithVerbosity creates a loggerV2 with the provided writers and
// verbosity level. // verbosity level.
func NewLoggerV2WithVerbosity(infoW, warningW, errorW io.Writer, v int) LoggerV2 { func NewLoggerV2WithVerbosity(infoW, warningW, errorW io.Writer, v int) LoggerV2 {
return newLoggerV2WithConfig(infoW, warningW, errorW, loggerV2Config{verbose: v})
}
type loggerV2Config struct {
verbose int
jsonFormat bool
}
func newLoggerV2WithConfig(infoW, warningW, errorW io.Writer, c loggerV2Config) LoggerV2 {
var m []*log.Logger var m []*log.Logger
m = append(m, log.New(infoW, severityName[infoLog]+": ", log.LstdFlags)) flag := log.LstdFlags
m = append(m, log.New(io.MultiWriter(infoW, warningW), severityName[warningLog]+": ", log.LstdFlags)) if c.jsonFormat {
flag = 0
}
m = append(m, log.New(infoW, "", flag))
m = append(m, log.New(io.MultiWriter(infoW, warningW), "", flag))
ew := io.MultiWriter(infoW, warningW, errorW) // ew will be used for error and fatal. ew := io.MultiWriter(infoW, warningW, errorW) // ew will be used for error and fatal.
m = append(m, log.New(ew, severityName[errorLog]+": ", log.LstdFlags)) m = append(m, log.New(ew, "", flag))
m = append(m, log.New(ew, severityName[fatalLog]+": ", log.LstdFlags)) m = append(m, log.New(ew, "", flag))
return &loggerT{m: m, v: v} return &loggerT{m: m, v: c.verbose, jsonFormat: c.jsonFormat}
} }
// newLoggerV2 creates a loggerV2 to be used as default logger. // newLoggerV2 creates a loggerV2 to be used as default logger.
@ -142,58 +159,79 @@ func newLoggerV2() LoggerV2 {
if vl, err := strconv.Atoi(vLevel); err == nil { if vl, err := strconv.Atoi(vLevel); err == nil {
v = vl v = vl
} }
return NewLoggerV2WithVerbosity(infoW, warningW, errorW, v)
jsonFormat := strings.EqualFold(os.Getenv("GRPC_GO_LOG_FORMATTER"), "json")
return newLoggerV2WithConfig(infoW, warningW, errorW, loggerV2Config{
verbose: v,
jsonFormat: jsonFormat,
})
}
func (g *loggerT) output(severity int, s string) {
sevStr := severityName[severity]
if !g.jsonFormat {
g.m[severity].Output(2, fmt.Sprintf("%v: %v", sevStr, s))
return
}
// TODO: we can also include the logging component, but that needs more
// (API) changes.
b, _ := json.Marshal(map[string]string{
"severity": sevStr,
"message": s,
})
g.m[severity].Output(2, string(b))
} }
func (g *loggerT) Info(args ...interface{}) { func (g *loggerT) Info(args ...interface{}) {
g.m[infoLog].Print(args...) g.output(infoLog, fmt.Sprint(args...))
} }
func (g *loggerT) Infoln(args ...interface{}) { func (g *loggerT) Infoln(args ...interface{}) {
g.m[infoLog].Println(args...) g.output(infoLog, fmt.Sprintln(args...))
} }
func (g *loggerT) Infof(format string, args ...interface{}) { func (g *loggerT) Infof(format string, args ...interface{}) {
g.m[infoLog].Printf(format, args...) g.output(infoLog, fmt.Sprintf(format, args...))
} }
func (g *loggerT) Warning(args ...interface{}) { func (g *loggerT) Warning(args ...interface{}) {
g.m[warningLog].Print(args...) g.output(warningLog, fmt.Sprint(args...))
} }
func (g *loggerT) Warningln(args ...interface{}) { func (g *loggerT) Warningln(args ...interface{}) {
g.m[warningLog].Println(args...) g.output(warningLog, fmt.Sprintln(args...))
} }
func (g *loggerT) Warningf(format string, args ...interface{}) { func (g *loggerT) Warningf(format string, args ...interface{}) {
g.m[warningLog].Printf(format, args...) g.output(warningLog, fmt.Sprintf(format, args...))
} }
func (g *loggerT) Error(args ...interface{}) { func (g *loggerT) Error(args ...interface{}) {
g.m[errorLog].Print(args...) g.output(errorLog, fmt.Sprint(args...))
} }
func (g *loggerT) Errorln(args ...interface{}) { func (g *loggerT) Errorln(args ...interface{}) {
g.m[errorLog].Println(args...) g.output(errorLog, fmt.Sprintln(args...))
} }
func (g *loggerT) Errorf(format string, args ...interface{}) { func (g *loggerT) Errorf(format string, args ...interface{}) {
g.m[errorLog].Printf(format, args...) g.output(errorLog, fmt.Sprintf(format, args...))
} }
func (g *loggerT) Fatal(args ...interface{}) { func (g *loggerT) Fatal(args ...interface{}) {
g.m[fatalLog].Fatal(args...) g.output(fatalLog, fmt.Sprint(args...))
// No need to call os.Exit() again because log.Logger.Fatal() calls os.Exit(). os.Exit(1)
} }
func (g *loggerT) Fatalln(args ...interface{}) { func (g *loggerT) Fatalln(args ...interface{}) {
g.m[fatalLog].Fatalln(args...) g.output(fatalLog, fmt.Sprintln(args...))
// No need to call os.Exit() again because log.Logger.Fatal() calls os.Exit(). os.Exit(1)
} }
func (g *loggerT) Fatalf(format string, args ...interface{}) { func (g *loggerT) Fatalf(format string, args ...interface{}) {
g.m[fatalLog].Fatalf(format, args...) g.output(fatalLog, fmt.Sprintf(format, args...))
// No need to call os.Exit() again because log.Logger.Fatal() calls os.Exit(). os.Exit(1)
} }
func (g *loggerT) V(l int) bool { func (g *loggerT) V(l int) bool {
@ -210,12 +248,12 @@ func (g *loggerT) V(l int) bool {
// later release. // later release.
type DepthLoggerV2 interface { type DepthLoggerV2 interface {
LoggerV2 LoggerV2
// InfoDepth logs to INFO log at the specified depth. Arguments are handled in the manner of fmt.Print. // InfoDepth logs to INFO log at the specified depth. Arguments are handled in the manner of fmt.Println.
InfoDepth(depth int, args ...interface{}) InfoDepth(depth int, args ...interface{})
// WarningDepth logs to WARNING log at the specified depth. Arguments are handled in the manner of fmt.Print. // WarningDepth logs to WARNING log at the specified depth. Arguments are handled in the manner of fmt.Println.
WarningDepth(depth int, args ...interface{}) WarningDepth(depth int, args ...interface{})
// ErrorDetph logs to ERROR log at the specified depth. Arguments are handled in the manner of fmt.Print. // ErrorDepth logs to ERROR log at the specified depth. Arguments are handled in the manner of fmt.Println.
ErrorDepth(depth int, args ...interface{}) ErrorDepth(depth int, args ...interface{})
// FatalDepth logs to FATAL log at the specified depth. Arguments are handled in the manner of fmt.Print. // FatalDepth logs to FATAL log at the specified depth. Arguments are handled in the manner of fmt.Println.
FatalDepth(depth int, args ...interface{}) FatalDepth(depth int, args ...interface{})
} }

View File

@ -1,6 +0,0 @@
#!/bin/bash
TMP=$(mktemp -d /tmp/sdk.XXX) \
&& curl -o $TMP.zip "https://storage.googleapis.com/appengine-sdks/featured/go_appengine_sdk_linux_amd64-1.9.68.zip" \
&& unzip -q $TMP.zip -d $TMP \
&& export PATH="$PATH:$TMP/go_appengine"

View File

@ -69,7 +69,8 @@ type writerSink struct {
func (ws *writerSink) Write(e *pb.GrpcLogEntry) error { func (ws *writerSink) Write(e *pb.GrpcLogEntry) error {
b, err := proto.Marshal(e) b, err := proto.Marshal(e)
if err != nil { if err != nil {
grpclogLogger.Infof("binary logging: failed to marshal proto message: %v", err) grpclogLogger.Errorf("binary logging: failed to marshal proto message: %v", err)
return err
} }
hdr := make([]byte, 4) hdr := make([]byte, 4)
binary.BigEndian.PutUint32(hdr, uint32(len(b))) binary.BigEndian.PutUint32(hdr, uint32(len(b)))
@ -89,20 +90,23 @@ type bufferedSink struct {
closer io.Closer closer io.Closer
out Sink // out is built on buf. out Sink // out is built on buf.
buf *bufio.Writer // buf is kept for flush. buf *bufio.Writer // buf is kept for flush.
flusherStarted bool
writeStartOnce sync.Once
writeTicker *time.Ticker writeTicker *time.Ticker
done chan struct{}
} }
func (fs *bufferedSink) Write(e *pb.GrpcLogEntry) error { func (fs *bufferedSink) Write(e *pb.GrpcLogEntry) error {
// Start the write loop when Write is called.
fs.writeStartOnce.Do(fs.startFlushGoroutine)
fs.mu.Lock() fs.mu.Lock()
defer fs.mu.Unlock()
if !fs.flusherStarted {
// Start the write loop when Write is called.
fs.startFlushGoroutine()
fs.flusherStarted = true
}
if err := fs.out.Write(e); err != nil { if err := fs.out.Write(e); err != nil {
fs.mu.Unlock()
return err return err
} }
fs.mu.Unlock()
return nil return nil
} }
@ -113,7 +117,12 @@ const (
func (fs *bufferedSink) startFlushGoroutine() { func (fs *bufferedSink) startFlushGoroutine() {
fs.writeTicker = time.NewTicker(bufFlushDuration) fs.writeTicker = time.NewTicker(bufFlushDuration)
go func() { go func() {
for range fs.writeTicker.C { for {
select {
case <-fs.done:
return
case <-fs.writeTicker.C:
}
fs.mu.Lock() fs.mu.Lock()
if err := fs.buf.Flush(); err != nil { if err := fs.buf.Flush(); err != nil {
grpclogLogger.Warningf("failed to flush to Sink: %v", err) grpclogLogger.Warningf("failed to flush to Sink: %v", err)
@ -124,10 +133,12 @@ func (fs *bufferedSink) startFlushGoroutine() {
} }
func (fs *bufferedSink) Close() error { func (fs *bufferedSink) Close() error {
fs.mu.Lock()
defer fs.mu.Unlock()
if fs.writeTicker != nil { if fs.writeTicker != nil {
fs.writeTicker.Stop() fs.writeTicker.Stop()
} }
fs.mu.Lock() close(fs.done)
if err := fs.buf.Flush(); err != nil { if err := fs.buf.Flush(); err != nil {
grpclogLogger.Warningf("failed to flush to Sink: %v", err) grpclogLogger.Warningf("failed to flush to Sink: %v", err)
} }
@ -137,7 +148,6 @@ func (fs *bufferedSink) Close() error {
if err := fs.out.Close(); err != nil { if err := fs.out.Close(); err != nil {
grpclogLogger.Warningf("failed to close the Sink: %v", err) grpclogLogger.Warningf("failed to close the Sink: %v", err)
} }
fs.mu.Unlock()
return nil return nil
} }
@ -155,5 +165,6 @@ func NewBufferedSink(o io.WriteCloser) Sink {
closer: o, closer: o,
out: newWriterSink(bufW), out: newWriterSink(bufW),
buf: bufW, buf: bufW,
done: make(chan struct{}),
} }
} }

View File

@ -204,9 +204,9 @@ func RegisterChannel(c Channel, pid int64, ref string) int64 {
trace: &channelTrace{createdTime: time.Now(), events: make([]*TraceEvent, 0, getMaxTraceEntry())}, trace: &channelTrace{createdTime: time.Now(), events: make([]*TraceEvent, 0, getMaxTraceEntry())},
} }
if pid == 0 { if pid == 0 {
db.get().addChannel(id, cn, true, pid, ref) db.get().addChannel(id, cn, true, pid)
} else { } else {
db.get().addChannel(id, cn, false, pid, ref) db.get().addChannel(id, cn, false, pid)
} }
return id return id
} }
@ -228,7 +228,7 @@ func RegisterSubChannel(c Channel, pid int64, ref string) int64 {
pid: pid, pid: pid,
trace: &channelTrace{createdTime: time.Now(), events: make([]*TraceEvent, 0, getMaxTraceEntry())}, trace: &channelTrace{createdTime: time.Now(), events: make([]*TraceEvent, 0, getMaxTraceEntry())},
} }
db.get().addSubChannel(id, sc, pid, ref) db.get().addSubChannel(id, sc, pid)
return id return id
} }
@ -258,7 +258,7 @@ func RegisterListenSocket(s Socket, pid int64, ref string) int64 {
} }
id := idGen.genID() id := idGen.genID()
ls := &listenSocket{refName: ref, s: s, id: id, pid: pid} ls := &listenSocket{refName: ref, s: s, id: id, pid: pid}
db.get().addListenSocket(id, ls, pid, ref) db.get().addListenSocket(id, ls, pid)
return id return id
} }
@ -273,11 +273,11 @@ func RegisterNormalSocket(s Socket, pid int64, ref string) int64 {
} }
id := idGen.genID() id := idGen.genID()
ns := &normalSocket{refName: ref, s: s, id: id, pid: pid} ns := &normalSocket{refName: ref, s: s, id: id, pid: pid}
db.get().addNormalSocket(id, ns, pid, ref) db.get().addNormalSocket(id, ns, pid)
return id return id
} }
// RemoveEntry removes an entry with unique channelz trakcing id to be id from // RemoveEntry removes an entry with unique channelz tracking id to be id from
// channelz database. // channelz database.
func RemoveEntry(id int64) { func RemoveEntry(id int64) {
db.get().removeEntry(id) db.get().removeEntry(id)
@ -333,7 +333,7 @@ func (c *channelMap) addServer(id int64, s *server) {
c.mu.Unlock() c.mu.Unlock()
} }
func (c *channelMap) addChannel(id int64, cn *channel, isTopChannel bool, pid int64, ref string) { func (c *channelMap) addChannel(id int64, cn *channel, isTopChannel bool, pid int64) {
c.mu.Lock() c.mu.Lock()
cn.cm = c cn.cm = c
cn.trace.cm = c cn.trace.cm = c
@ -346,7 +346,7 @@ func (c *channelMap) addChannel(id int64, cn *channel, isTopChannel bool, pid in
c.mu.Unlock() c.mu.Unlock()
} }
func (c *channelMap) addSubChannel(id int64, sc *subChannel, pid int64, ref string) { func (c *channelMap) addSubChannel(id int64, sc *subChannel, pid int64) {
c.mu.Lock() c.mu.Lock()
sc.cm = c sc.cm = c
sc.trace.cm = c sc.trace.cm = c
@ -355,7 +355,7 @@ func (c *channelMap) addSubChannel(id int64, sc *subChannel, pid int64, ref stri
c.mu.Unlock() c.mu.Unlock()
} }
func (c *channelMap) addListenSocket(id int64, ls *listenSocket, pid int64, ref string) { func (c *channelMap) addListenSocket(id int64, ls *listenSocket, pid int64) {
c.mu.Lock() c.mu.Lock()
ls.cm = c ls.cm = c
c.listenSockets[id] = ls c.listenSockets[id] = ls
@ -363,7 +363,7 @@ func (c *channelMap) addListenSocket(id int64, ls *listenSocket, pid int64, ref
c.mu.Unlock() c.mu.Unlock()
} }
func (c *channelMap) addNormalSocket(id int64, ns *normalSocket, pid int64, ref string) { func (c *channelMap) addNormalSocket(id int64, ns *normalSocket, pid int64) {
c.mu.Lock() c.mu.Lock()
ns.cm = c ns.cm = c
c.normalSockets[id] = ns c.normalSockets[id] = ns
@ -630,7 +630,7 @@ func (c *channelMap) GetServerSockets(id int64, startID int64, maxResults int64)
if count == 0 { if count == 0 {
end = true end = true
} }
var s []*SocketMetric s := make([]*SocketMetric, 0, len(sks))
for _, ns := range sks { for _, ns := range sks {
sm := &SocketMetric{} sm := &SocketMetric{}
sm.SocketData = ns.s.ChannelzMetric() sm.SocketData = ns.s.ChannelzMetric()

View File

@ -1,5 +1,3 @@
// +build !appengine
/* /*
* *
* Copyright 2018 gRPC authors. * Copyright 2018 gRPC authors.

View File

@ -1,4 +1,5 @@
// +build !linux appengine //go:build !linux
// +build !linux
/* /*
* *
@ -37,6 +38,6 @@ type SocketOptionData struct {
// Windows OS doesn't support Socket Option // Windows OS doesn't support Socket Option
func (s *SocketOptionData) Getsockopt(fd uintptr) { func (s *SocketOptionData) Getsockopt(fd uintptr) {
once.Do(func() { once.Do(func() {
logger.Warning("Channelz: socket options are not supported on non-linux os and appengine.") logger.Warning("Channelz: socket options are not supported on non-linux environments")
}) })
} }

View File

@ -1,5 +1,3 @@
// +build linux,!appengine
/* /*
* *
* Copyright 2018 gRPC authors. * Copyright 2018 gRPC authors.

View File

@ -1,4 +1,5 @@
// +build !linux appengine //go:build !linux
// +build !linux
/* /*
* *

View File

@ -1,5 +1,3 @@
// +build !appengine
/* /*
* *
* Copyright 2020 gRPC authors. * Copyright 2020 gRPC authors.

View File

@ -1,31 +0,0 @@
// +build appengine
/*
*
* Copyright 2020 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package credentials
import (
"crypto/tls"
"net/url"
)
// SPIFFEIDFromState is a no-op for appengine builds.
func SPIFFEIDFromState(state tls.ConnectionState) *url.URL {
return nil
}

View File

@ -1,5 +1,3 @@
// +build !appengine
/* /*
* *
* Copyright 2018 gRPC authors. * Copyright 2018 gRPC authors.

View File

@ -18,7 +18,9 @@
package credentials package credentials
import "crypto/tls" import (
"crypto/tls"
)
const alpnProtoStrH2 = "h2" const alpnProtoStrH2 = "h2"

View File

@ -26,13 +26,10 @@ import (
const ( const (
prefix = "GRPC_GO_" prefix = "GRPC_GO_"
retryStr = prefix + "RETRY"
txtErrIgnoreStr = prefix + "IGNORE_TXT_ERRORS" txtErrIgnoreStr = prefix + "IGNORE_TXT_ERRORS"
) )
var ( var (
// Retry is set if retry is explicitly enabled via "GRPC_GO_RETRY=on".
Retry = strings.EqualFold(os.Getenv(retryStr), "on")
// TXTErrIgnore is set if TXT errors should be ignored ("GRPC_GO_IGNORE_TXT_ERRORS" is not "false"). // TXTErrIgnore is set if TXT errors should be ignored ("GRPC_GO_IGNORE_TXT_ERRORS" is not "false").
TXTErrIgnore = !strings.EqualFold(os.Getenv(txtErrIgnoreStr), "false") TXTErrIgnore = !strings.EqualFold(os.Getenv(txtErrIgnoreStr), "false")
) )

View File

@ -0,0 +1,97 @@
/*
*
* Copyright 2020 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package envconfig
import (
"os"
"strings"
)
const (
// XDSBootstrapFileNameEnv is the env variable to set bootstrap file name.
// Do not use this and read from env directly. Its value is read and kept in
// variable BootstrapFileName.
//
// When both bootstrap FileName and FileContent are set, FileName is used.
XDSBootstrapFileNameEnv = "GRPC_XDS_BOOTSTRAP"
// XDSBootstrapFileContentEnv is the env variable to set bootstrapp file
// content. Do not use this and read from env directly. Its value is read
// and kept in variable BootstrapFileName.
//
// When both bootstrap FileName and FileContent are set, FileName is used.
XDSBootstrapFileContentEnv = "GRPC_XDS_BOOTSTRAP_CONFIG"
ringHashSupportEnv = "GRPC_XDS_EXPERIMENTAL_ENABLE_RING_HASH"
clientSideSecuritySupportEnv = "GRPC_XDS_EXPERIMENTAL_SECURITY_SUPPORT"
aggregateAndDNSSupportEnv = "GRPC_XDS_EXPERIMENTAL_ENABLE_AGGREGATE_AND_LOGICAL_DNS_CLUSTER"
rbacSupportEnv = "GRPC_XDS_EXPERIMENTAL_RBAC"
federationEnv = "GRPC_EXPERIMENTAL_XDS_FEDERATION"
rlsInXDSEnv = "GRPC_EXPERIMENTAL_XDS_RLS_LB"
c2pResolverTestOnlyTrafficDirectorURIEnv = "GRPC_TEST_ONLY_GOOGLE_C2P_RESOLVER_TRAFFIC_DIRECTOR_URI"
)
var (
// XDSBootstrapFileName holds the name of the file which contains xDS
// bootstrap configuration. Users can specify the location of the bootstrap
// file by setting the environment variable "GRPC_XDS_BOOTSTRAP".
//
// When both bootstrap FileName and FileContent are set, FileName is used.
XDSBootstrapFileName = os.Getenv(XDSBootstrapFileNameEnv)
// XDSBootstrapFileContent holds the content of the xDS bootstrap
// configuration. Users can specify the bootstrap config by setting the
// environment variable "GRPC_XDS_BOOTSTRAP_CONFIG".
//
// When both bootstrap FileName and FileContent are set, FileName is used.
XDSBootstrapFileContent = os.Getenv(XDSBootstrapFileContentEnv)
// XDSRingHash indicates whether ring hash support is enabled, which can be
// disabled by setting the environment variable
// "GRPC_XDS_EXPERIMENTAL_ENABLE_RING_HASH" to "false".
XDSRingHash = !strings.EqualFold(os.Getenv(ringHashSupportEnv), "false")
// XDSClientSideSecurity is used to control processing of security
// configuration on the client-side.
//
// Note that there is no env var protection for the server-side because we
// have a brand new API on the server-side and users explicitly need to use
// the new API to get security integration on the server.
XDSClientSideSecurity = !strings.EqualFold(os.Getenv(clientSideSecuritySupportEnv), "false")
// XDSAggregateAndDNS indicates whether processing of aggregated cluster
// and DNS cluster is enabled, which can be enabled by setting the
// environment variable
// "GRPC_XDS_EXPERIMENTAL_ENABLE_AGGREGATE_AND_LOGICAL_DNS_CLUSTER" to
// "true".
XDSAggregateAndDNS = strings.EqualFold(os.Getenv(aggregateAndDNSSupportEnv), "true")
// XDSRBAC indicates whether xDS configured RBAC HTTP Filter is enabled,
// which can be disabled by setting the environment variable
// "GRPC_XDS_EXPERIMENTAL_RBAC" to "false".
XDSRBAC = !strings.EqualFold(os.Getenv(rbacSupportEnv), "false")
// XDSFederation indicates whether federation support is enabled.
XDSFederation = strings.EqualFold(os.Getenv(federationEnv), "true")
// XDSRLS indicates whether processing of Cluster Specifier plugins and
// support for the RLS CLuster Specifier is enabled, which can be enabled by
// setting the environment variable "GRPC_EXPERIMENTAL_XDS_RLS_LB" to
// "true".
XDSRLS = strings.EqualFold(os.Getenv(rlsInXDSEnv), "true")
// C2PResolverTestOnlyTrafficDirectorURI is the TD URI for testing.
C2PResolverTestOnlyTrafficDirectorURI = os.Getenv(c2pResolverTestOnlyTrafficDirectorURIEnv)
)

View File

@ -115,12 +115,12 @@ type LoggerV2 interface {
// Notice: This type is EXPERIMENTAL and may be changed or removed in a // Notice: This type is EXPERIMENTAL and may be changed or removed in a
// later release. // later release.
type DepthLoggerV2 interface { type DepthLoggerV2 interface {
// InfoDepth logs to INFO log at the specified depth. Arguments are handled in the manner of fmt.Print. // InfoDepth logs to INFO log at the specified depth. Arguments are handled in the manner of fmt.Println.
InfoDepth(depth int, args ...interface{}) InfoDepth(depth int, args ...interface{})
// WarningDepth logs to WARNING log at the specified depth. Arguments are handled in the manner of fmt.Print. // WarningDepth logs to WARNING log at the specified depth. Arguments are handled in the manner of fmt.Println.
WarningDepth(depth int, args ...interface{}) WarningDepth(depth int, args ...interface{})
// ErrorDetph logs to ERROR log at the specified depth. Arguments are handled in the manner of fmt.Print. // ErrorDepth logs to ERROR log at the specified depth. Arguments are handled in the manner of fmt.Println.
ErrorDepth(depth int, args ...interface{}) ErrorDepth(depth int, args ...interface{})
// FatalDepth logs to FATAL log at the specified depth. Arguments are handled in the manner of fmt.Print. // FatalDepth logs to FATAL log at the specified depth. Arguments are handled in the manner of fmt.Println.
FatalDepth(depth int, args ...interface{}) FatalDepth(depth int, args ...interface{})
} }

View File

@ -31,26 +31,37 @@ var (
mu sync.Mutex mu sync.Mutex
) )
// Int implements rand.Int on the grpcrand global source.
func Int() int {
mu.Lock()
defer mu.Unlock()
return r.Int()
}
// Int63n implements rand.Int63n on the grpcrand global source. // Int63n implements rand.Int63n on the grpcrand global source.
func Int63n(n int64) int64 { func Int63n(n int64) int64 {
mu.Lock() mu.Lock()
res := r.Int63n(n) defer mu.Unlock()
mu.Unlock() return r.Int63n(n)
return res
} }
// Intn implements rand.Intn on the grpcrand global source. // Intn implements rand.Intn on the grpcrand global source.
func Intn(n int) int { func Intn(n int) int {
mu.Lock() mu.Lock()
res := r.Intn(n) defer mu.Unlock()
mu.Unlock() return r.Intn(n)
return res
} }
// Float64 implements rand.Float64 on the grpcrand global source. // Float64 implements rand.Float64 on the grpcrand global source.
func Float64() float64 { func Float64() float64 {
mu.Lock() mu.Lock()
res := r.Float64() defer mu.Unlock()
mu.Unlock() return r.Float64()
return res }
// Uint64 implements rand.Uint64 on the grpcrand global source.
func Uint64() uint64 {
mu.Lock()
defer mu.Unlock()
return r.Uint64()
} }

View File

@ -1,8 +1,6 @@
// +build appengine
/* /*
* *
* Copyright 2018 gRPC authors. * Copyright 2021 gRPC authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -18,13 +16,5 @@
* *
*/ */
package credentials // Package grpcutil provides utility functions used across the gRPC codebase.
package grpcutil
import (
"net"
)
// WrapSyscallConn returns newConn on appengine.
func WrapSyscallConn(rawConn, newConn net.Conn) net.Conn {
return newConn
}

View File

@ -1,8 +1,6 @@
// +build go1.13
/* /*
* *
* Copyright 2019 gRPC authors. * Copyright 2021 gRPC authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -18,16 +16,16 @@
* *
*/ */
package dns package grpcutil
import "net" import "regexp"
func init() { // FullMatchWithRegex returns whether the full text matches the regex provided.
filterError = func(err error) error { func FullMatchWithRegex(re *regexp.Regexp, text string) bool {
if dnsErr, ok := err.(*net.DNSError); ok && dnsErr.IsNotFound { if len(text) == 0 {
// The name does not exist; not an error. return re.MatchString(text)
return nil
}
return err
} }
re.Longest()
rem := re.FindString(text)
return len(rem) == len(text)
} }

View File

@ -1,89 +0,0 @@
/*
*
* Copyright 2020 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
// Package grpcutil provides a bunch of utility functions to be used across the
// gRPC codebase.
package grpcutil
import (
"strings"
"google.golang.org/grpc/resolver"
)
// split2 returns the values from strings.SplitN(s, sep, 2).
// If sep is not found, it returns ("", "", false) instead.
func split2(s, sep string) (string, string, bool) {
spl := strings.SplitN(s, sep, 2)
if len(spl) < 2 {
return "", "", false
}
return spl[0], spl[1], true
}
// ParseTarget splits target into a resolver.Target struct containing scheme,
// authority and endpoint. skipUnixColonParsing indicates that the parse should
// not parse "unix:[path]" cases. This should be true in cases where a custom
// dialer is present, to prevent a behavior change.
//
// If target is not a valid scheme://authority/endpoint as specified in
// https://github.com/grpc/grpc/blob/master/doc/naming.md,
// it returns {Endpoint: target}.
func ParseTarget(target string, skipUnixColonParsing bool) (ret resolver.Target) {
var ok bool
if strings.HasPrefix(target, "unix-abstract:") {
if strings.HasPrefix(target, "unix-abstract://") {
// Maybe, with Authority specified, try to parse it
var remain string
ret.Scheme, remain, _ = split2(target, "://")
ret.Authority, ret.Endpoint, ok = split2(remain, "/")
if !ok {
// No Authority, add the "//" back
ret.Endpoint = "//" + remain
} else {
// Found Authority, add the "/" back
ret.Endpoint = "/" + ret.Endpoint
}
} else {
// Without Authority specified, split target on ":"
ret.Scheme, ret.Endpoint, _ = split2(target, ":")
}
return ret
}
ret.Scheme, ret.Endpoint, ok = split2(target, "://")
if !ok {
if strings.HasPrefix(target, "unix:") && !skipUnixColonParsing {
// Handle the "unix:[local/path]" and "unix:[/absolute/path]" cases,
// because splitting on :// only handles the
// "unix://[/absolute/path]" case. Only handle if the dialer is nil,
// to avoid a behavior change with custom dialers.
return resolver.Target{Scheme: "unix", Endpoint: target[len("unix:"):]}
}
return resolver.Target{Endpoint: target}
}
ret.Authority, ret.Endpoint, ok = split2(ret.Endpoint, "/")
if !ok {
return resolver.Target{Endpoint: target}
}
if ret.Scheme == "unix" {
// Add the "/" back in the unix case, so the unix resolver receives the
// actual endpoint in the "unix://[/absolute/path]" case.
ret.Endpoint = "/" + ret.Endpoint
}
return ret
}

View File

@ -30,14 +30,38 @@ type mdKeyType string
const mdKey = mdKeyType("grpc.internal.address.metadata") const mdKey = mdKeyType("grpc.internal.address.metadata")
type mdValue metadata.MD
func (m mdValue) Equal(o interface{}) bool {
om, ok := o.(mdValue)
if !ok {
return false
}
if len(m) != len(om) {
return false
}
for k, v := range m {
ov := om[k]
if len(ov) != len(v) {
return false
}
for i, ve := range v {
if ov[i] != ve {
return false
}
}
}
return true
}
// Get returns the metadata of addr. // Get returns the metadata of addr.
func Get(addr resolver.Address) metadata.MD { func Get(addr resolver.Address) metadata.MD {
attrs := addr.Attributes attrs := addr.Attributes
if attrs == nil { if attrs == nil {
return nil return nil
} }
md, _ := attrs.Value(mdKey).(metadata.MD) md, _ := attrs.Value(mdKey).(mdValue)
return md return metadata.MD(md)
} }
// Set sets (overrides) the metadata in addr. // Set sets (overrides) the metadata in addr.
@ -45,6 +69,6 @@ func Get(addr resolver.Address) metadata.MD {
// When a SubConn is created with this address, the RPCs sent on it will all // When a SubConn is created with this address, the RPCs sent on it will all
// have this metadata. // have this metadata.
func Set(addr resolver.Address, md metadata.MD) resolver.Address { func Set(addr resolver.Address, md metadata.MD) resolver.Address {
addr.Attributes = addr.Attributes.WithValues(mdKey, md) addr.Attributes = addr.Attributes.WithValue(mdKey, mdValue(md))
return addr return addr
} }

View File

@ -117,9 +117,12 @@ type ClientInterceptor interface {
NewStream(ctx context.Context, ri RPCInfo, done func(), newStream func(ctx context.Context, done func()) (ClientStream, error)) (ClientStream, error) NewStream(ctx context.Context, ri RPCInfo, done func(), newStream func(ctx context.Context, done func()) (ClientStream, error)) (ClientStream, error)
} }
// ServerInterceptor is unimplementable; do not use. // ServerInterceptor is an interceptor for incoming RPC's on gRPC server side.
type ServerInterceptor interface { type ServerInterceptor interface {
notDefined() // AllowRPC checks if an incoming RPC is allowed to proceed based on
// information about connection RPC was received on, and HTTP Headers. This
// information will be piped into context.
AllowRPC(ctx context.Context) error // TODO: Make this a real interceptor for filters such as rate limiting.
} }
type csKeyType string type csKeyType string
@ -129,7 +132,7 @@ const csKey = csKeyType("grpc.internal.resolver.configSelector")
// SetConfigSelector sets the config selector in state and returns the new // SetConfigSelector sets the config selector in state and returns the new
// state. // state.
func SetConfigSelector(state resolver.State, cs ConfigSelector) resolver.State { func SetConfigSelector(state resolver.State, cs ConfigSelector) resolver.State {
state.Attributes = state.Attributes.WithValues(csKey, cs) state.Attributes = state.Attributes.WithValue(csKey, cs)
return state return state
} }

View File

@ -277,18 +277,13 @@ func (d *dnsResolver) lookupSRV() ([]resolver.Address, error) {
return newAddrs, nil return newAddrs, nil
} }
var filterError = func(err error) error { func handleDNSError(err error, lookupType string) error {
if dnsErr, ok := err.(*net.DNSError); ok && !dnsErr.IsTimeout && !dnsErr.IsTemporary { if dnsErr, ok := err.(*net.DNSError); ok && !dnsErr.IsTimeout && !dnsErr.IsTemporary {
// Timeouts and temporary errors should be communicated to gRPC to // Timeouts and temporary errors should be communicated to gRPC to
// attempt another DNS query (with backoff). Other errors should be // attempt another DNS query (with backoff). Other errors should be
// suppressed (they may represent the absence of a TXT record). // suppressed (they may represent the absence of a TXT record).
return nil return nil
} }
return err
}
func handleDNSError(err error, lookupType string) error {
err = filterError(err)
if err != nil { if err != nil {
err = fmt.Errorf("dns: %v record lookup error: %v", lookupType, err) err = fmt.Errorf("dns: %v record lookup error: %v", lookupType, err)
logger.Info(err) logger.Info(err)
@ -323,12 +318,12 @@ func (d *dnsResolver) lookupTXT() *serviceconfig.ParseResult {
} }
func (d *dnsResolver) lookupHost() ([]resolver.Address, error) { func (d *dnsResolver) lookupHost() ([]resolver.Address, error) {
var newAddrs []resolver.Address
addrs, err := d.resolver.LookupHost(d.ctx, d.host) addrs, err := d.resolver.LookupHost(d.ctx, d.host)
if err != nil { if err != nil {
err = handleDNSError(err, "A") err = handleDNSError(err, "A")
return nil, err return nil, err
} }
newAddrs := make([]resolver.Address, 0, len(addrs))
for _, a := range addrs { for _, a := range addrs {
ip, ok := formatIP(a) ip, ok := formatIP(a)
if !ok { if !ok {

View File

@ -37,7 +37,17 @@ func (b *builder) Build(target resolver.Target, cc resolver.ClientConn, _ resolv
if target.Authority != "" { if target.Authority != "" {
return nil, fmt.Errorf("invalid (non-empty) authority: %v", target.Authority) return nil, fmt.Errorf("invalid (non-empty) authority: %v", target.Authority)
} }
addr := resolver.Address{Addr: target.Endpoint}
// gRPC was parsing the dial target manually before PR #4817, and we
// switched to using url.Parse() in that PR. To avoid breaking existing
// resolver implementations we ended up stripping the leading "/" from the
// endpoint. This obviously does not work for the "unix" scheme. Hence we
// end up using the parsed URL instead.
endpoint := target.URL.Path
if endpoint == "" {
endpoint = target.URL.Opaque
}
addr := resolver.Address{Addr: endpoint}
if b.scheme == unixAbstractScheme { if b.scheme == unixAbstractScheme {
// prepend "\x00" to address for unix-abstract // prepend "\x00" to address for unix-abstract
addr.Addr = "\x00" + addr.Addr addr.Addr = "\x00" + addr.Addr

View File

@ -78,6 +78,7 @@ func (bc *BalancerConfig) UnmarshalJSON(b []byte) error {
return err return err
} }
var names []string
for i, lbcfg := range ir { for i, lbcfg := range ir {
if len(lbcfg) != 1 { if len(lbcfg) != 1 {
return fmt.Errorf("invalid loadBalancingConfig: entry %v does not contain exactly 1 policy/config pair: %q", i, lbcfg) return fmt.Errorf("invalid loadBalancingConfig: entry %v does not contain exactly 1 policy/config pair: %q", i, lbcfg)
@ -92,6 +93,7 @@ func (bc *BalancerConfig) UnmarshalJSON(b []byte) error {
for name, jsonCfg = range lbcfg { for name, jsonCfg = range lbcfg {
} }
names = append(names, name)
builder := balancer.Get(name) builder := balancer.Get(name)
if builder == nil { if builder == nil {
// If the balancer is not registered, move on to the next config. // If the balancer is not registered, move on to the next config.
@ -120,7 +122,7 @@ func (bc *BalancerConfig) UnmarshalJSON(b []byte) error {
// return. This means we had a loadBalancingConfig slice but did not // return. This means we had a loadBalancingConfig slice but did not
// encounter a registered policy. The config is considered invalid in this // encounter a registered policy. The config is considered invalid in this
// case. // case.
return fmt.Errorf("invalid loadBalancingConfig: no supported policies found") return fmt.Errorf("invalid loadBalancingConfig: no supported policies found in %v", names)
} }
// MethodConfig defines the configuration recommended by the service providers for a // MethodConfig defines the configuration recommended by the service providers for a

View File

@ -97,7 +97,7 @@ func (s *Status) Err() error {
if s.Code() == codes.OK { if s.Code() == codes.OK {
return nil return nil
} }
return &Error{e: s.Proto()} return &Error{s: s}
} }
// WithDetails returns a new status with the provided details messages appended to the status. // WithDetails returns a new status with the provided details messages appended to the status.
@ -136,19 +136,23 @@ func (s *Status) Details() []interface{} {
return details return details
} }
func (s *Status) String() string {
return fmt.Sprintf("rpc error: code = %s desc = %s", s.Code(), s.Message())
}
// Error wraps a pointer of a status proto. It implements error and Status, // Error wraps a pointer of a status proto. It implements error and Status,
// and a nil *Error should never be returned by this package. // and a nil *Error should never be returned by this package.
type Error struct { type Error struct {
e *spb.Status s *Status
} }
func (e *Error) Error() string { func (e *Error) Error() string {
return fmt.Sprintf("rpc error: code = %s desc = %s", codes.Code(e.e.GetCode()), e.e.GetMessage()) return e.s.String()
} }
// GRPCStatus returns the Status represented by se. // GRPCStatus returns the Status represented by se.
func (e *Error) GRPCStatus() *Status { func (e *Error) GRPCStatus() *Status {
return FromProto(e.e) return e.s
} }
// Is implements future error.Is functionality. // Is implements future error.Is functionality.
@ -158,5 +162,5 @@ func (e *Error) Is(target error) bool {
if !ok { if !ok {
return false return false
} }
return proto.Equal(e.e, tse.e) return proto.Equal(e.s.s, tse.s.s)
} }

View File

@ -1,5 +1,3 @@
// +build !appengine
/* /*
* *
* Copyright 2018 gRPC authors. * Copyright 2018 gRPC authors.

View File

@ -1,4 +1,5 @@
// +build !linux appengine //go:build !linux
// +build !linux
/* /*
* *
@ -35,41 +36,41 @@ var logger = grpclog.Component("core")
func log() { func log() {
once.Do(func() { once.Do(func() {
logger.Info("CPU time info is unavailable on non-linux or appengine environment.") logger.Info("CPU time info is unavailable on non-linux environments.")
}) })
} }
// GetCPUTime returns the how much CPU time has passed since the start of this process. // GetCPUTime returns the how much CPU time has passed since the start of this
// It always returns 0 under non-linux or appengine environment. // process. It always returns 0 under non-linux environments.
func GetCPUTime() int64 { func GetCPUTime() int64 {
log() log()
return 0 return 0
} }
// Rusage is an empty struct under non-linux or appengine environment. // Rusage is an empty struct under non-linux environments.
type Rusage struct{} type Rusage struct{}
// GetRusage is a no-op function under non-linux or appengine environment. // GetRusage is a no-op function under non-linux environments.
func GetRusage() *Rusage { func GetRusage() *Rusage {
log() log()
return nil return nil
} }
// CPUTimeDiff returns the differences of user CPU time and system CPU time used // CPUTimeDiff returns the differences of user CPU time and system CPU time used
// between two Rusage structs. It a no-op function for non-linux or appengine environment. // between two Rusage structs. It a no-op function for non-linux environments.
func CPUTimeDiff(first *Rusage, latest *Rusage) (float64, float64) { func CPUTimeDiff(first *Rusage, latest *Rusage) (float64, float64) {
log() log()
return 0, 0 return 0, 0
} }
// SetTCPUserTimeout is a no-op function under non-linux or appengine environments // SetTCPUserTimeout is a no-op function under non-linux environments.
func SetTCPUserTimeout(conn net.Conn, timeout time.Duration) error { func SetTCPUserTimeout(conn net.Conn, timeout time.Duration) error {
log() log()
return nil return nil
} }
// GetTCPUserTimeout is a no-op function under non-linux or appengine environments // GetTCPUserTimeout is a no-op function under non-linux environments.
// a negative return value indicates the operation is not supported // A negative return value indicates the operation is not supported
func GetTCPUserTimeout(conn net.Conn) (int, error) { func GetTCPUserTimeout(conn net.Conn) (int, error) {
log() log()
return -1, nil return -1, nil

View File

@ -133,6 +133,7 @@ type cleanupStream struct {
func (c *cleanupStream) isTransportResponseFrame() bool { return c.rst } // Results in a RST_STREAM func (c *cleanupStream) isTransportResponseFrame() bool { return c.rst } // Results in a RST_STREAM
type earlyAbortStream struct { type earlyAbortStream struct {
httpStatus uint32
streamID uint32 streamID uint32
contentSubtype string contentSubtype string
status *status.Status status *status.Status
@ -296,7 +297,7 @@ type controlBuffer struct {
// closed and nilled when transportResponseFrames drops below the // closed and nilled when transportResponseFrames drops below the
// threshold. Both fields are protected by mu. // threshold. Both fields are protected by mu.
transportResponseFrames int transportResponseFrames int
trfChan atomic.Value // *chan struct{} trfChan atomic.Value // chan struct{}
} }
func newControlBuffer(done <-chan struct{}) *controlBuffer { func newControlBuffer(done <-chan struct{}) *controlBuffer {
@ -310,10 +311,10 @@ func newControlBuffer(done <-chan struct{}) *controlBuffer {
// throttle blocks if there are too many incomingSettings/cleanupStreams in the // throttle blocks if there are too many incomingSettings/cleanupStreams in the
// controlbuf. // controlbuf.
func (c *controlBuffer) throttle() { func (c *controlBuffer) throttle() {
ch, _ := c.trfChan.Load().(*chan struct{}) ch, _ := c.trfChan.Load().(chan struct{})
if ch != nil { if ch != nil {
select { select {
case <-*ch: case <-ch:
case <-c.done: case <-c.done:
} }
} }
@ -347,8 +348,7 @@ func (c *controlBuffer) executeAndPut(f func(it interface{}) bool, it cbItem) (b
if c.transportResponseFrames == maxQueuedTransportResponseFrames { if c.transportResponseFrames == maxQueuedTransportResponseFrames {
// We are adding the frame that puts us over the threshold; create // We are adding the frame that puts us over the threshold; create
// a throttling channel. // a throttling channel.
ch := make(chan struct{}) c.trfChan.Store(make(chan struct{}))
c.trfChan.Store(&ch)
} }
} }
c.mu.Unlock() c.mu.Unlock()
@ -389,9 +389,9 @@ func (c *controlBuffer) get(block bool) (interface{}, error) {
if c.transportResponseFrames == maxQueuedTransportResponseFrames { if c.transportResponseFrames == maxQueuedTransportResponseFrames {
// We are removing the frame that put us over the // We are removing the frame that put us over the
// threshold; close and clear the throttling channel. // threshold; close and clear the throttling channel.
ch := c.trfChan.Load().(*chan struct{}) ch := c.trfChan.Load().(chan struct{})
close(*ch) close(ch)
c.trfChan.Store((*chan struct{})(nil)) c.trfChan.Store((chan struct{})(nil))
} }
c.transportResponseFrames-- c.transportResponseFrames--
} }
@ -407,7 +407,6 @@ func (c *controlBuffer) get(block bool) (interface{}, error) {
select { select {
case <-c.ch: case <-c.ch:
case <-c.done: case <-c.done:
c.finish()
return nil, ErrConnClosing return nil, ErrConnClosing
} }
} }
@ -432,6 +431,14 @@ func (c *controlBuffer) finish() {
hdr.onOrphaned(ErrConnClosing) hdr.onOrphaned(ErrConnClosing)
} }
} }
// In case throttle() is currently in flight, it needs to be unblocked.
// Otherwise, the transport may not close, since the transport is closed by
// the reader encountering the connection error.
ch, _ := c.trfChan.Load().(chan struct{})
if ch != nil {
close(ch)
}
c.trfChan.Store((chan struct{})(nil))
c.mu.Unlock() c.mu.Unlock()
} }
@ -765,9 +772,12 @@ func (l *loopyWriter) earlyAbortStreamHandler(eas *earlyAbortStream) error {
if l.side == clientSide { if l.side == clientSide {
return errors.New("earlyAbortStream not handled on client") return errors.New("earlyAbortStream not handled on client")
} }
// In case the caller forgets to set the http status, default to 200.
if eas.httpStatus == 0 {
eas.httpStatus = 200
}
headerFields := []hpack.HeaderField{ headerFields := []hpack.HeaderField{
{Name: ":status", Value: "200"}, {Name: ":status", Value: strconv.Itoa(int(eas.httpStatus))},
{Name: "content-type", Value: grpcutil.ContentType(eas.contentSubtype)}, {Name: "content-type", Value: grpcutil.ContentType(eas.contentSubtype)},
{Name: "grpc-status", Value: strconv.Itoa(int(eas.status.Code()))}, {Name: "grpc-status", Value: strconv.Itoa(int(eas.status.Code()))},
{Name: "grpc-message", Value: encodeGrpcMessage(eas.status.Message())}, {Name: "grpc-message", Value: encodeGrpcMessage(eas.status.Message())},

View File

@ -136,12 +136,10 @@ type inFlow struct {
// newLimit updates the inflow window to a new value n. // newLimit updates the inflow window to a new value n.
// It assumes that n is always greater than the old limit. // It assumes that n is always greater than the old limit.
func (f *inFlow) newLimit(n uint32) uint32 { func (f *inFlow) newLimit(n uint32) {
f.mu.Lock() f.mu.Lock()
d := n - f.limit
f.limit = n f.limit = n
f.mu.Unlock() f.mu.Unlock()
return d
} }
func (f *inFlow) maybeAdjust(n uint32) uint32 { func (f *inFlow) maybeAdjust(n uint32) uint32 {

View File

@ -141,9 +141,8 @@ type serverHandlerTransport struct {
stats stats.Handler stats stats.Handler
} }
func (ht *serverHandlerTransport) Close() error { func (ht *serverHandlerTransport) Close() {
ht.closeOnce.Do(ht.closeCloseChanOnce) ht.closeOnce.Do(ht.closeCloseChanOnce)
return nil
} }
func (ht *serverHandlerTransport) closeCloseChanOnce() { close(ht.closedCh) } func (ht *serverHandlerTransport) closeCloseChanOnce() { close(ht.closedCh) }

View File

@ -24,6 +24,8 @@ import (
"io" "io"
"math" "math"
"net" "net"
"net/http"
"path/filepath"
"strconv" "strconv"
"strings" "strings"
"sync" "sync"
@ -145,14 +147,21 @@ func dial(ctx context.Context, fn func(context.Context, string) (net.Conn, error
address := addr.Addr address := addr.Addr
networkType, ok := networktype.Get(addr) networkType, ok := networktype.Get(addr)
if fn != nil { if fn != nil {
// Special handling for unix scheme with custom dialer. Back in the day,
// we did not have a unix resolver and therefore targets with a unix
// scheme would end up using the passthrough resolver. So, user's used a
// custom dialer in this case and expected the original dial target to
// be passed to the custom dialer. Now, we have a unix resolver. But if
// a custom dialer is specified, we want to retain the old behavior in
// terms of the address being passed to the custom dialer.
if networkType == "unix" && !strings.HasPrefix(address, "\x00") { if networkType == "unix" && !strings.HasPrefix(address, "\x00") {
// For backward compatibility, if the user dialed "unix:///path", // Supported unix targets are either "unix://absolute-path" or
// the passthrough resolver would be used and the user's custom // "unix:relative-path".
// dialer would see "unix:///path". Since the unix resolver is used if filepath.IsAbs(address) {
// and the address is now "/path", prepend "unix://" so the user's
// custom dialer sees the same address.
return fn(ctx, "unix://"+address) return fn(ctx, "unix://"+address)
} }
return fn(ctx, "unix:"+address)
}
return fn(ctx, address) return fn(ctx, address)
} }
if !ok { if !ok {
@ -192,6 +201,12 @@ func newHTTP2Client(connectCtx, ctx context.Context, addr resolver.Address, opts
} }
}() }()
// gRPC, resolver, balancer etc. can specify arbitrary data in the
// Attributes field of resolver.Address, which is shoved into connectCtx
// and passed to the dialer and credential handshaker. This makes it possible for
// address specific arbitrary data to reach custom dialers and credential handshakers.
connectCtx = icredentials.NewClientHandshakeInfoContext(connectCtx, credentials.ClientHandshakeInfo{Attributes: addr.Attributes})
conn, err := dial(connectCtx, opts.Dialer, addr, opts.UseProxy, opts.UserAgent) conn, err := dial(connectCtx, opts.Dialer, addr, opts.UseProxy, opts.UserAgent)
if err != nil { if err != nil {
if opts.FailOnNonTempDialError { if opts.FailOnNonTempDialError {
@ -236,12 +251,15 @@ func newHTTP2Client(connectCtx, ctx context.Context, addr resolver.Address, opts
} }
} }
if transportCreds != nil { if transportCreds != nil {
// gRPC, resolver, balancer etc. can specify arbitrary data in the rawConn := conn
// Attributes field of resolver.Address, which is shoved into connectCtx // Pull the deadline from the connectCtx, which will be used for
// and passed to the credential handshaker. This makes it possible for // timeouts in the authentication protocol handshake. Can ignore the
// address specific arbitrary data to reach the credential handshaker. // boolean as the deadline will return the zero value, which will make
connectCtx = icredentials.NewClientHandshakeInfoContext(connectCtx, credentials.ClientHandshakeInfo{Attributes: addr.Attributes}) // the conn not timeout on I/O operations.
conn, authInfo, err = transportCreds.ClientHandshake(connectCtx, addr.ServerName, conn) deadline, _ := connectCtx.Deadline()
rawConn.SetDeadline(deadline)
conn, authInfo, err = transportCreds.ClientHandshake(connectCtx, addr.ServerName, rawConn)
rawConn.SetDeadline(time.Time{})
if err != nil { if err != nil {
return nil, connectionErrorf(isTemporary(err), err, "transport: authentication handshake failed: %v", err) return nil, connectionErrorf(isTemporary(err), err, "transport: authentication handshake failed: %v", err)
} }
@ -399,11 +417,10 @@ func newHTTP2Client(connectCtx, ctx context.Context, addr resolver.Address, opts
logger.Errorf("transport: loopyWriter.run returning. Err: %v", err) logger.Errorf("transport: loopyWriter.run returning. Err: %v", err)
} }
} }
// If it's a connection error, let reader goroutine handle it // Do not close the transport. Let reader goroutine handle it since
// since there might be data in the buffers. // there might be data in the buffers.
if _, ok := err.(net.Error); !ok {
t.conn.Close() t.conn.Close()
} t.controlBuf.finish()
close(t.writerDone) close(t.writerDone)
}() }()
return t, nil return t, nil
@ -571,7 +588,7 @@ func (t *http2Client) getTrAuthData(ctx context.Context, audience string) (map[s
return nil, err return nil, err
} }
return nil, status.Errorf(codes.Unauthenticated, "transport: %v", err) return nil, status.Errorf(codes.Unauthenticated, "transport: per-RPC creds failed due to error: %v", err)
} }
for k, v := range data { for k, v := range data {
// Capital header names are illegal in HTTP/2. // Capital header names are illegal in HTTP/2.
@ -608,26 +625,35 @@ func (t *http2Client) getCallAuthData(ctx context.Context, audience string, call
return callAuthData, nil return callAuthData, nil
} }
// PerformedIOError wraps an error to indicate IO may have been performed // NewStreamError wraps an error and reports additional information. Typically
// before the error occurred. // NewStream errors result in transparent retry, as they mean nothing went onto
type PerformedIOError struct { // the wire. However, there are two notable exceptions:
//
// 1. If the stream headers violate the max header list size allowed by the
// server. In this case there is no reason to retry at all, as it is
// assumed the RPC would continue to fail on subsequent attempts.
// 2. If the credentials errored when requesting their headers. In this case,
// it's possible a retry can fix the problem, but indefinitely transparently
// retrying is not appropriate as it is likely the credentials, if they can
// eventually succeed, would need I/O to do so.
type NewStreamError struct {
Err error Err error
DoNotRetry bool
DoNotTransparentRetry bool
} }
// Error implements error. func (e NewStreamError) Error() string {
func (p PerformedIOError) Error() string { return e.Err.Error()
return p.Err.Error()
} }
// NewStream creates a stream and registers it into the transport as "active" // NewStream creates a stream and registers it into the transport as "active"
// streams. // streams. All non-nil errors returned will be *NewStreamError.
func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr) (_ *Stream, err error) { func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr) (_ *Stream, err error) {
ctx = peer.NewContext(ctx, t.getPeer()) ctx = peer.NewContext(ctx, t.getPeer())
headerFields, err := t.createHeaderFields(ctx, callHdr) headerFields, err := t.createHeaderFields(ctx, callHdr)
if err != nil { if err != nil {
// We may have performed I/O in the per-RPC creds callback, so do not return nil, &NewStreamError{Err: err, DoNotTransparentRetry: true}
// allow transparent retry.
return nil, PerformedIOError{err}
} }
s := t.newStream(ctx, callHdr) s := t.newStream(ctx, callHdr)
cleanup := func(err error) { cleanup := func(err error) {
@ -727,23 +753,23 @@ func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr) (_ *Strea
return true return true
}, hdr) }, hdr)
if err != nil { if err != nil {
return nil, err return nil, &NewStreamError{Err: err}
} }
if success { if success {
break break
} }
if hdrListSizeErr != nil { if hdrListSizeErr != nil {
return nil, hdrListSizeErr return nil, &NewStreamError{Err: hdrListSizeErr, DoNotRetry: true}
} }
firstTry = false firstTry = false
select { select {
case <-ch: case <-ch:
case <-s.ctx.Done(): case <-ctx.Done():
return nil, ContextErr(s.ctx.Err()) return nil, &NewStreamError{Err: ContextErr(ctx.Err())}
case <-t.goAway: case <-t.goAway:
return nil, errStreamDrain return nil, &NewStreamError{Err: errStreamDrain}
case <-t.ctx.Done(): case <-t.ctx.Done():
return nil, ErrConnClosing return nil, &NewStreamError{Err: ErrConnClosing}
} }
} }
if t.statsHandler != nil { if t.statsHandler != nil {
@ -878,12 +904,18 @@ func (t *http2Client) Close(err error) {
// Append info about previous goaways if there were any, since this may be important // Append info about previous goaways if there were any, since this may be important
// for understanding the root cause for this connection to be closed. // for understanding the root cause for this connection to be closed.
_, goAwayDebugMessage := t.GetGoAwayReason() _, goAwayDebugMessage := t.GetGoAwayReason()
var st *status.Status
if len(goAwayDebugMessage) > 0 { if len(goAwayDebugMessage) > 0 {
err = fmt.Errorf("closing transport due to: %v, received prior goaway: %v", err, goAwayDebugMessage) st = status.Newf(codes.Unavailable, "closing transport due to: %v, received prior goaway: %v", err, goAwayDebugMessage)
err = st.Err()
} else {
st = status.New(codes.Unavailable, err.Error())
} }
// Notify all active streams. // Notify all active streams.
for _, s := range streams { for _, s := range streams {
t.closeStream(s, err, false, http2.ErrCodeNo, status.New(codes.Unavailable, err.Error()), nil, false) t.closeStream(s, err, false, http2.ErrCodeNo, st, nil, false)
} }
if t.statsHandler != nil { if t.statsHandler != nil {
connEnd := &stats.ConnEnd{ connEnd := &stats.ConnEnd{
@ -1050,7 +1082,7 @@ func (t *http2Client) handleData(f *http2.DataFrame) {
} }
// The server has closed the stream without sending trailers. Record that // The server has closed the stream without sending trailers. Record that
// the read direction is closed, and set the status appropriately. // the read direction is closed, and set the status appropriately.
if f.FrameHeader.Flags.Has(http2.FlagDataEndStream) { if f.StreamEnded() {
t.closeStream(s, io.EOF, false, http2.ErrCodeNo, status.New(codes.Internal, "server closed the stream without sending trailers"), nil, true) t.closeStream(s, io.EOF, false, http2.ErrCodeNo, status.New(codes.Internal, "server closed the stream without sending trailers"), nil, true)
} }
} }
@ -1221,7 +1253,11 @@ func (t *http2Client) setGoAwayReason(f *http2.GoAwayFrame) {
t.goAwayReason = GoAwayTooManyPings t.goAwayReason = GoAwayTooManyPings
} }
} }
t.goAwayDebugMessage = fmt.Sprintf("code: %s, debug data: %v", f.ErrCode, string(f.DebugData())) if len(f.DebugData()) == 0 {
t.goAwayDebugMessage = fmt.Sprintf("code: %s", f.ErrCode)
} else {
t.goAwayDebugMessage = fmt.Sprintf("code: %s, debug data: %q", f.ErrCode, string(f.DebugData()))
}
} }
func (t *http2Client) GetGoAwayReason() (GoAwayReason, string) { func (t *http2Client) GetGoAwayReason() (GoAwayReason, string) {
@ -1254,35 +1290,128 @@ func (t *http2Client) operateHeaders(frame *http2.MetaHeadersFrame) {
return return
} }
state := &decodeState{} // frame.Truncated is set to true when framer detects that the current header
// Initialize isGRPC value to be !initialHeader, since if a gRPC Response-Headers has already been received, then it means that the peer is speaking gRPC and we are in gRPC mode. // list size hits MaxHeaderListSize limit.
state.data.isGRPC = !initialHeader if frame.Truncated {
if h2code, err := state.decodeHeader(frame); err != nil { se := status.New(codes.Internal, "peer header list size exceeded limit")
t.closeStream(s, err, true, h2code, status.Convert(err), nil, endStream) t.closeStream(s, se.Err(), true, http2.ErrCodeFrameSize, se, nil, endStream)
return
}
var (
// If a gRPC Response-Headers has already been received, then it means
// that the peer is speaking gRPC and we are in gRPC mode.
isGRPC = !initialHeader
mdata = make(map[string][]string)
contentTypeErr = "malformed header: missing HTTP content-type"
grpcMessage string
statusGen *status.Status
recvCompress string
httpStatusCode *int
httpStatusErr string
rawStatusCode = codes.Unknown
// headerError is set if an error is encountered while parsing the headers
headerError string
)
if initialHeader {
httpStatusErr = "malformed header: missing HTTP status"
}
for _, hf := range frame.Fields {
switch hf.Name {
case "content-type":
if _, validContentType := grpcutil.ContentSubtype(hf.Value); !validContentType {
contentTypeErr = fmt.Sprintf("transport: received unexpected content-type %q", hf.Value)
break
}
contentTypeErr = ""
mdata[hf.Name] = append(mdata[hf.Name], hf.Value)
isGRPC = true
case "grpc-encoding":
recvCompress = hf.Value
case "grpc-status":
code, err := strconv.ParseInt(hf.Value, 10, 32)
if err != nil {
se := status.New(codes.Internal, fmt.Sprintf("transport: malformed grpc-status: %v", err))
t.closeStream(s, se.Err(), true, http2.ErrCodeProtocol, se, nil, endStream)
return
}
rawStatusCode = codes.Code(uint32(code))
case "grpc-message":
grpcMessage = decodeGrpcMessage(hf.Value)
case "grpc-status-details-bin":
var err error
statusGen, err = decodeGRPCStatusDetails(hf.Value)
if err != nil {
headerError = fmt.Sprintf("transport: malformed grpc-status-details-bin: %v", err)
}
case ":status":
if hf.Value == "200" {
httpStatusErr = ""
statusCode := 200
httpStatusCode = &statusCode
break
}
c, err := strconv.ParseInt(hf.Value, 10, 32)
if err != nil {
se := status.New(codes.Internal, fmt.Sprintf("transport: malformed http-status: %v", err))
t.closeStream(s, se.Err(), true, http2.ErrCodeProtocol, se, nil, endStream)
return
}
statusCode := int(c)
httpStatusCode = &statusCode
httpStatusErr = fmt.Sprintf(
"unexpected HTTP status code received from server: %d (%s)",
statusCode,
http.StatusText(statusCode),
)
default:
if isReservedHeader(hf.Name) && !isWhitelistedHeader(hf.Name) {
break
}
v, err := decodeMetadataHeader(hf.Name, hf.Value)
if err != nil {
headerError = fmt.Sprintf("transport: malformed %s: %v", hf.Name, err)
logger.Warningf("Failed to decode metadata header (%q, %q): %v", hf.Name, hf.Value, err)
break
}
mdata[hf.Name] = append(mdata[hf.Name], v)
}
}
if !isGRPC || httpStatusErr != "" {
var code = codes.Internal // when header does not include HTTP status, return INTERNAL
if httpStatusCode != nil {
var ok bool
code, ok = HTTPStatusConvTab[*httpStatusCode]
if !ok {
code = codes.Unknown
}
}
var errs []string
if httpStatusErr != "" {
errs = append(errs, httpStatusErr)
}
if contentTypeErr != "" {
errs = append(errs, contentTypeErr)
}
// Verify the HTTP response is a 200.
se := status.New(code, strings.Join(errs, "; "))
t.closeStream(s, se.Err(), true, http2.ErrCodeProtocol, se, nil, endStream)
return
}
if headerError != "" {
se := status.New(codes.Internal, headerError)
t.closeStream(s, se.Err(), true, http2.ErrCodeProtocol, se, nil, endStream)
return return
} }
isHeader := false isHeader := false
defer func() {
if t.statsHandler != nil {
if isHeader {
inHeader := &stats.InHeader{
Client: true,
WireLength: int(frame.Header().Length),
Header: s.header.Copy(),
Compression: s.recvCompress,
}
t.statsHandler.HandleRPC(s.ctx, inHeader)
} else {
inTrailer := &stats.InTrailer{
Client: true,
WireLength: int(frame.Header().Length),
Trailer: s.trailer.Copy(),
}
t.statsHandler.HandleRPC(s.ctx, inTrailer)
}
}
}()
// If headerChan hasn't been closed yet // If headerChan hasn't been closed yet
if atomic.CompareAndSwapUint32(&s.headerChanClosed, 0, 1) { if atomic.CompareAndSwapUint32(&s.headerChanClosed, 0, 1) {
@ -1293,9 +1422,9 @@ func (t *http2Client) operateHeaders(frame *http2.MetaHeadersFrame) {
// These values can be set without any synchronization because // These values can be set without any synchronization because
// stream goroutine will read it only after seeing a closed // stream goroutine will read it only after seeing a closed
// headerChan which we'll close after setting this. // headerChan which we'll close after setting this.
s.recvCompress = state.data.encoding s.recvCompress = recvCompress
if len(state.data.mdata) > 0 { if len(mdata) > 0 {
s.header = state.data.mdata s.header = mdata
} }
} else { } else {
// HEADERS frame block carries a Trailers-Only. // HEADERS frame block carries a Trailers-Only.
@ -1304,13 +1433,36 @@ func (t *http2Client) operateHeaders(frame *http2.MetaHeadersFrame) {
close(s.headerChan) close(s.headerChan)
} }
if t.statsHandler != nil {
if isHeader {
inHeader := &stats.InHeader{
Client: true,
WireLength: int(frame.Header().Length),
Header: metadata.MD(mdata).Copy(),
Compression: s.recvCompress,
}
t.statsHandler.HandleRPC(s.ctx, inHeader)
} else {
inTrailer := &stats.InTrailer{
Client: true,
WireLength: int(frame.Header().Length),
Trailer: metadata.MD(mdata).Copy(),
}
t.statsHandler.HandleRPC(s.ctx, inTrailer)
}
}
if !endStream { if !endStream {
return return
} }
if statusGen == nil {
statusGen = status.New(rawStatusCode, grpcMessage)
}
// if client received END_STREAM from server while stream was still active, send RST_STREAM // if client received END_STREAM from server while stream was still active, send RST_STREAM
rst := s.getState() == streamActive rst := s.getState() == streamActive
t.closeStream(s, io.EOF, rst, http2.ErrCodeNo, state.status(), state.data.mdata, true) t.closeStream(s, io.EOF, rst, http2.ErrCodeNo, statusGen, mdata, true)
} }
// reader runs as a separate goroutine in charge of reading data from network // reader runs as a separate goroutine in charge of reading data from network
@ -1405,7 +1557,7 @@ func minTime(a, b time.Duration) time.Duration {
return b return b
} }
// keepalive running in a separate goroutune makes sure the connection is alive by sending pings. // keepalive running in a separate goroutine makes sure the connection is alive by sending pings.
func (t *http2Client) keepalive() { func (t *http2Client) keepalive() {
p := &ping{data: [8]byte{}} p := &ping{data: [8]byte{}}
// True iff a ping has been sent, and no data has been received since then. // True iff a ping has been sent, and no data has been received since then.

View File

@ -73,7 +73,6 @@ type http2Server struct {
writerDone chan struct{} // sync point to enable testing. writerDone chan struct{} // sync point to enable testing.
remoteAddr net.Addr remoteAddr net.Addr
localAddr net.Addr localAddr net.Addr
maxStreamID uint32 // max stream ID ever seen
authInfo credentials.AuthInfo // auth info about the connection authInfo credentials.AuthInfo // auth info about the connection
inTapHandle tap.ServerInHandle inTapHandle tap.ServerInHandle
framer *framer framer *framer
@ -102,11 +101,11 @@ type http2Server struct {
mu sync.Mutex // guard the following mu sync.Mutex // guard the following
// drainChan is initialized when drain(...) is called the first time. // drainChan is initialized when Drain() is called the first time.
// After which the server writes out the first GoAway(with ID 2^31-1) frame. // After which the server writes out the first GoAway(with ID 2^31-1) frame.
// Then an independent goroutine will be launched to later send the second GoAway. // Then an independent goroutine will be launched to later send the second GoAway.
// During this time we don't want to write another first GoAway(with ID 2^31 -1) frame. // During this time we don't want to write another first GoAway(with ID 2^31 -1) frame.
// Thus call to drain(...) will be a no-op if drainChan is already initialized since draining is // Thus call to Drain() will be a no-op if drainChan is already initialized since draining is
// already underway. // already underway.
drainChan chan struct{} drainChan chan struct{}
state transportState state transportState
@ -123,11 +122,37 @@ type http2Server struct {
bufferPool *bufferPool bufferPool *bufferPool
connectionID uint64 connectionID uint64
// maxStreamMu guards the maximum stream ID
// This lock may not be taken if mu is already held.
maxStreamMu sync.Mutex
maxStreamID uint32 // max stream ID ever seen
} }
// newHTTP2Server constructs a ServerTransport based on HTTP2. ConnectionError is // NewServerTransport creates a http2 transport with conn and configuration
// returned if something goes wrong. // options from config.
func newHTTP2Server(conn net.Conn, config *ServerConfig) (_ ServerTransport, err error) { //
// It returns a non-nil transport and a nil error on success. On failure, it
// returns a nil transport and a non-nil error. For a special case where the
// underlying conn gets closed before the client preface could be read, it
// returns a nil transport and a nil error.
func NewServerTransport(conn net.Conn, config *ServerConfig) (_ ServerTransport, err error) {
var authInfo credentials.AuthInfo
rawConn := conn
if config.Credentials != nil {
var err error
conn, authInfo, err = config.Credentials.ServerHandshake(rawConn)
if err != nil {
// ErrConnDispatched means that the connection was dispatched away
// from gRPC; those connections should be left open. io.EOF means
// the connection was closed before handshaking completed, which can
// happen naturally from probers. Return these errors directly.
if err == credentials.ErrConnDispatched || err == io.EOF {
return nil, err
}
return nil, connectionErrorf(false, err, "ServerHandshake(%q) failed: %v", rawConn.RemoteAddr(), err)
}
}
writeBufSize := config.WriteBufferSize writeBufSize := config.WriteBufferSize
readBufSize := config.ReadBufferSize readBufSize := config.ReadBufferSize
maxHeaderListSize := defaultServerMaxHeaderListSize maxHeaderListSize := defaultServerMaxHeaderListSize
@ -210,14 +235,15 @@ func newHTTP2Server(conn net.Conn, config *ServerConfig) (_ ServerTransport, err
if kep.MinTime == 0 { if kep.MinTime == 0 {
kep.MinTime = defaultKeepalivePolicyMinTime kep.MinTime = defaultKeepalivePolicyMinTime
} }
done := make(chan struct{}) done := make(chan struct{})
t := &http2Server{ t := &http2Server{
ctx: context.Background(), ctx: setConnection(context.Background(), rawConn),
done: done, done: done,
conn: conn, conn: conn,
remoteAddr: conn.RemoteAddr(), remoteAddr: conn.RemoteAddr(),
localAddr: conn.LocalAddr(), localAddr: conn.LocalAddr(),
authInfo: config.AuthInfo, authInfo: authInfo,
framer: framer, framer: framer,
readerDone: make(chan struct{}), readerDone: make(chan struct{}),
writerDone: make(chan struct{}), writerDone: make(chan struct{}),
@ -266,6 +292,14 @@ func newHTTP2Server(conn net.Conn, config *ServerConfig) (_ ServerTransport, err
// Check the validity of client preface. // Check the validity of client preface.
preface := make([]byte, len(clientPreface)) preface := make([]byte, len(clientPreface))
if _, err := io.ReadFull(t.conn, preface); err != nil { if _, err := io.ReadFull(t.conn, preface); err != nil {
// In deployments where a gRPC server runs behind a cloud load balancer
// which performs regular TCP level health checks, the connection is
// closed immediately by the latter. Returning io.EOF here allows the
// grpc server implementation to recognize this scenario and suppress
// logging to reduce spam.
if err == io.EOF {
return nil, io.EOF
}
return nil, connectionErrorf(false, err, "transport: http2Server.HandleStreams failed to receive the preface from client: %v", err) return nil, connectionErrorf(false, err, "transport: http2Server.HandleStreams failed to receive the preface from client: %v", err)
} }
if !bytes.Equal(preface, clientPreface) { if !bytes.Equal(preface, clientPreface) {
@ -295,6 +329,7 @@ func newHTTP2Server(conn net.Conn, config *ServerConfig) (_ ServerTransport, err
} }
} }
t.conn.Close() t.conn.Close()
t.controlBuf.finish()
close(t.writerDone) close(t.writerDone)
}() }()
go t.keepalive() go t.keepalive()
@ -303,38 +338,144 @@ func newHTTP2Server(conn net.Conn, config *ServerConfig) (_ ServerTransport, err
// operateHeader takes action on the decoded headers. // operateHeader takes action on the decoded headers.
func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func(*Stream), traceCtx func(context.Context, string) context.Context) (fatal bool) { func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func(*Stream), traceCtx func(context.Context, string) context.Context) (fatal bool) {
// Acquire max stream ID lock for entire duration
t.maxStreamMu.Lock()
defer t.maxStreamMu.Unlock()
streamID := frame.Header().StreamID streamID := frame.Header().StreamID
state := &decodeState{
serverSide: true, // frame.Truncated is set to true when framer detects that the current header
} // list size hits MaxHeaderListSize limit.
if h2code, err := state.decodeHeader(frame); err != nil { if frame.Truncated {
if _, ok := status.FromError(err); ok {
t.controlBuf.put(&cleanupStream{ t.controlBuf.put(&cleanupStream{
streamID: streamID, streamID: streamID,
rst: true, rst: true,
rstCode: h2code, rstCode: http2.ErrCodeFrameSize,
onWrite: func() {}, onWrite: func() {},
}) })
}
return false return false
} }
if streamID%2 != 1 || streamID <= t.maxStreamID {
// illegal gRPC stream id.
if logger.V(logLevel) {
logger.Errorf("transport: http2Server.HandleStreams received an illegal stream id: %v", streamID)
}
return true
}
t.maxStreamID = streamID
buf := newRecvBuffer() buf := newRecvBuffer()
s := &Stream{ s := &Stream{
id: streamID, id: streamID,
st: t, st: t,
buf: buf, buf: buf,
fc: &inFlow{limit: uint32(t.initialWindowSize)}, fc: &inFlow{limit: uint32(t.initialWindowSize)},
recvCompress: state.data.encoding,
method: state.data.method,
contentSubtype: state.data.contentSubtype,
} }
var (
// If a gRPC Response-Headers has already been received, then it means
// that the peer is speaking gRPC and we are in gRPC mode.
isGRPC = false
mdata = make(map[string][]string)
httpMethod string
// headerError is set if an error is encountered while parsing the headers
headerError bool
timeoutSet bool
timeout time.Duration
)
for _, hf := range frame.Fields {
switch hf.Name {
case "content-type":
contentSubtype, validContentType := grpcutil.ContentSubtype(hf.Value)
if !validContentType {
break
}
mdata[hf.Name] = append(mdata[hf.Name], hf.Value)
s.contentSubtype = contentSubtype
isGRPC = true
case "grpc-encoding":
s.recvCompress = hf.Value
case ":method":
httpMethod = hf.Value
case ":path":
s.method = hf.Value
case "grpc-timeout":
timeoutSet = true
var err error
if timeout, err = decodeTimeout(hf.Value); err != nil {
headerError = true
}
// "Transports must consider requests containing the Connection header
// as malformed." - A41
case "connection":
if logger.V(logLevel) {
logger.Errorf("transport: http2Server.operateHeaders parsed a :connection header which makes a request malformed as per the HTTP/2 spec")
}
headerError = true
default:
if isReservedHeader(hf.Name) && !isWhitelistedHeader(hf.Name) {
break
}
v, err := decodeMetadataHeader(hf.Name, hf.Value)
if err != nil {
headerError = true
logger.Warningf("Failed to decode metadata header (%q, %q): %v", hf.Name, hf.Value, err)
break
}
mdata[hf.Name] = append(mdata[hf.Name], v)
}
}
// "If multiple Host headers or multiple :authority headers are present, the
// request must be rejected with an HTTP status code 400 as required by Host
// validation in RFC 7230 §5.4, gRPC status code INTERNAL, or RST_STREAM
// with HTTP/2 error code PROTOCOL_ERROR." - A41. Since this is a HTTP/2
// error, this takes precedence over a client not speaking gRPC.
if len(mdata[":authority"]) > 1 || len(mdata["host"]) > 1 {
errMsg := fmt.Sprintf("num values of :authority: %v, num values of host: %v, both must only have 1 value as per HTTP/2 spec", len(mdata[":authority"]), len(mdata["host"]))
if logger.V(logLevel) {
logger.Errorf("transport: %v", errMsg)
}
t.controlBuf.put(&earlyAbortStream{
httpStatus: 400,
streamID: streamID,
contentSubtype: s.contentSubtype,
status: status.New(codes.Internal, errMsg),
})
return false
}
if !isGRPC || headerError {
t.controlBuf.put(&cleanupStream{
streamID: streamID,
rst: true,
rstCode: http2.ErrCodeProtocol,
onWrite: func() {},
})
return false
}
// "If :authority is missing, Host must be renamed to :authority." - A41
if len(mdata[":authority"]) == 0 {
// No-op if host isn't present, no eventual :authority header is a valid
// RPC.
if host, ok := mdata["host"]; ok {
mdata[":authority"] = host
delete(mdata, "host")
}
} else {
// "If :authority is present, Host must be discarded" - A41
delete(mdata, "host")
}
if frame.StreamEnded() { if frame.StreamEnded() {
// s is just created by the caller. No lock needed. // s is just created by the caller. No lock needed.
s.state = streamReadDone s.state = streamReadDone
} }
if state.data.timeoutSet { if timeoutSet {
s.ctx, s.cancel = context.WithTimeout(t.ctx, state.data.timeout) s.ctx, s.cancel = context.WithTimeout(t.ctx, timeout)
} else { } else {
s.ctx, s.cancel = context.WithCancel(t.ctx) s.ctx, s.cancel = context.WithCancel(t.ctx)
} }
@ -347,14 +488,14 @@ func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func(
} }
s.ctx = peer.NewContext(s.ctx, pr) s.ctx = peer.NewContext(s.ctx, pr)
// Attach the received metadata to the context. // Attach the received metadata to the context.
if len(state.data.mdata) > 0 { if len(mdata) > 0 {
s.ctx = metadata.NewIncomingContext(s.ctx, state.data.mdata) s.ctx = metadata.NewIncomingContext(s.ctx, mdata)
if statsTags := mdata["grpc-tags-bin"]; len(statsTags) > 0 {
s.ctx = stats.SetIncomingTags(s.ctx, []byte(statsTags[len(statsTags)-1]))
} }
if state.data.statsTags != nil { if statsTrace := mdata["grpc-trace-bin"]; len(statsTrace) > 0 {
s.ctx = stats.SetIncomingTags(s.ctx, state.data.statsTags) s.ctx = stats.SetIncomingTrace(s.ctx, []byte(statsTrace[len(statsTrace)-1]))
} }
if state.data.statsTrace != nil {
s.ctx = stats.SetIncomingTrace(s.ctx, state.data.statsTrace)
} }
t.mu.Lock() t.mu.Lock()
if t.state != reachable { if t.state != reachable {
@ -373,20 +514,10 @@ func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func(
s.cancel() s.cancel()
return false return false
} }
if streamID%2 != 1 || streamID <= t.maxStreamID { if httpMethod != http.MethodPost {
t.mu.Unlock()
// illegal gRPC stream id.
if logger.V(logLevel) {
logger.Errorf("transport: http2Server.HandleStreams received an illegal stream id: %v", streamID)
}
s.cancel()
return true
}
t.maxStreamID = streamID
if state.data.httpMethod != http.MethodPost {
t.mu.Unlock() t.mu.Unlock()
if logger.V(logLevel) { if logger.V(logLevel) {
logger.Warningf("transport: http2Server.operateHeaders parsed a :method field: %v which should be POST", state.data.httpMethod) logger.Infof("transport: http2Server.operateHeaders parsed a :method field: %v which should be POST", httpMethod)
} }
t.controlBuf.put(&cleanupStream{ t.controlBuf.put(&cleanupStream{
streamID: streamID, streamID: streamID,
@ -399,7 +530,7 @@ func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func(
} }
if t.inTapHandle != nil { if t.inTapHandle != nil {
var err error var err error
if s.ctx, err = t.inTapHandle(s.ctx, &tap.Info{FullMethodName: state.data.method}); err != nil { if s.ctx, err = t.inTapHandle(s.ctx, &tap.Info{FullMethodName: s.method}); err != nil {
t.mu.Unlock() t.mu.Unlock()
if logger.V(logLevel) { if logger.V(logLevel) {
logger.Infof("transport: http2Server.operateHeaders got an error from InTapHandle: %v", err) logger.Infof("transport: http2Server.operateHeaders got an error from InTapHandle: %v", err)
@ -409,6 +540,7 @@ func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func(
stat = status.New(codes.PermissionDenied, err.Error()) stat = status.New(codes.PermissionDenied, err.Error())
} }
t.controlBuf.put(&earlyAbortStream{ t.controlBuf.put(&earlyAbortStream{
httpStatus: 200,
streamID: s.id, streamID: s.id,
contentSubtype: s.contentSubtype, contentSubtype: s.contentSubtype,
status: stat, status: stat,
@ -437,7 +569,7 @@ func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func(
LocalAddr: t.localAddr, LocalAddr: t.localAddr,
Compression: s.recvCompress, Compression: s.recvCompress,
WireLength: int(frame.Header().Length), WireLength: int(frame.Header().Length),
Header: metadata.MD(state.data.mdata).Copy(), Header: metadata.MD(mdata).Copy(),
} }
t.stats.HandleRPC(s.ctx, inHeader) t.stats.HandleRPC(s.ctx, inHeader)
} }
@ -649,7 +781,7 @@ func (t *http2Server) handleData(f *http2.DataFrame) {
s.write(recvMsg{buffer: buffer}) s.write(recvMsg{buffer: buffer})
} }
} }
if f.Header().Flags.Has(http2.FlagDataEndStream) { if f.StreamEnded() {
// Received the end of stream from the client. // Received the end of stream from the client.
s.compareAndSwapState(streamActive, streamReadDone) s.compareAndSwapState(streamActive, streamReadDone)
s.write(recvMsg{err: io.EOF}) s.write(recvMsg{err: io.EOF})
@ -1004,12 +1136,12 @@ func (t *http2Server) keepalive() {
if val <= 0 { if val <= 0 {
// The connection has been idle for a duration of keepalive.MaxConnectionIdle or more. // The connection has been idle for a duration of keepalive.MaxConnectionIdle or more.
// Gracefully close the connection. // Gracefully close the connection.
t.drain(http2.ErrCodeNo, []byte{}) t.Drain()
return return
} }
idleTimer.Reset(val) idleTimer.Reset(val)
case <-ageTimer.C: case <-ageTimer.C:
t.drain(http2.ErrCodeNo, []byte{}) t.Drain()
ageTimer.Reset(t.kp.MaxConnectionAgeGrace) ageTimer.Reset(t.kp.MaxConnectionAgeGrace)
select { select {
case <-ageTimer.C: case <-ageTimer.C:
@ -1063,11 +1195,11 @@ func (t *http2Server) keepalive() {
// Close starts shutting down the http2Server transport. // Close starts shutting down the http2Server transport.
// TODO(zhaoq): Now the destruction is not blocked on any pending streams. This // TODO(zhaoq): Now the destruction is not blocked on any pending streams. This
// could cause some resource issue. Revisit this later. // could cause some resource issue. Revisit this later.
func (t *http2Server) Close() error { func (t *http2Server) Close() {
t.mu.Lock() t.mu.Lock()
if t.state == closing { if t.state == closing {
t.mu.Unlock() t.mu.Unlock()
return errors.New("transport: Close() was already called") return
} }
t.state = closing t.state = closing
streams := t.activeStreams streams := t.activeStreams
@ -1075,7 +1207,9 @@ func (t *http2Server) Close() error {
t.mu.Unlock() t.mu.Unlock()
t.controlBuf.finish() t.controlBuf.finish()
close(t.done) close(t.done)
err := t.conn.Close() if err := t.conn.Close(); err != nil && logger.V(logLevel) {
logger.Infof("transport: error closing conn during Close: %v", err)
}
if channelz.IsOn() { if channelz.IsOn() {
channelz.RemoveEntry(t.channelzID) channelz.RemoveEntry(t.channelzID)
} }
@ -1087,7 +1221,6 @@ func (t *http2Server) Close() error {
connEnd := &stats.ConnEnd{} connEnd := &stats.ConnEnd{}
t.stats.HandleConn(t.ctx, connEnd) t.stats.HandleConn(t.ctx, connEnd)
} }
return err
} }
// deleteStream deletes the stream s from transport's active streams. // deleteStream deletes the stream s from transport's active streams.
@ -1152,17 +1285,13 @@ func (t *http2Server) RemoteAddr() net.Addr {
} }
func (t *http2Server) Drain() { func (t *http2Server) Drain() {
t.drain(http2.ErrCodeNo, []byte{})
}
func (t *http2Server) drain(code http2.ErrCode, debugData []byte) {
t.mu.Lock() t.mu.Lock()
defer t.mu.Unlock() defer t.mu.Unlock()
if t.drainChan != nil { if t.drainChan != nil {
return return
} }
t.drainChan = make(chan struct{}) t.drainChan = make(chan struct{})
t.controlBuf.put(&goAway{code: code, debugData: debugData, headsUp: true}) t.controlBuf.put(&goAway{code: http2.ErrCodeNo, debugData: []byte{}, headsUp: true})
} }
var goAwayPing = &ping{data: [8]byte{1, 6, 1, 8, 0, 3, 3, 9}} var goAwayPing = &ping{data: [8]byte{1, 6, 1, 8, 0, 3, 3, 9}}
@ -1170,20 +1299,23 @@ var goAwayPing = &ping{data: [8]byte{1, 6, 1, 8, 0, 3, 3, 9}}
// Handles outgoing GoAway and returns true if loopy needs to put itself // Handles outgoing GoAway and returns true if loopy needs to put itself
// in draining mode. // in draining mode.
func (t *http2Server) outgoingGoAwayHandler(g *goAway) (bool, error) { func (t *http2Server) outgoingGoAwayHandler(g *goAway) (bool, error) {
t.maxStreamMu.Lock()
t.mu.Lock() t.mu.Lock()
if t.state == closing { // TODO(mmukhi): This seems unnecessary. if t.state == closing { // TODO(mmukhi): This seems unnecessary.
t.mu.Unlock() t.mu.Unlock()
t.maxStreamMu.Unlock()
// The transport is closing. // The transport is closing.
return false, ErrConnClosing return false, ErrConnClosing
} }
sid := t.maxStreamID
if !g.headsUp { if !g.headsUp {
// Stop accepting more streams now. // Stop accepting more streams now.
t.state = draining t.state = draining
sid := t.maxStreamID
if len(t.activeStreams) == 0 { if len(t.activeStreams) == 0 {
g.closeConn = true g.closeConn = true
} }
t.mu.Unlock() t.mu.Unlock()
t.maxStreamMu.Unlock()
if err := t.framer.fr.WriteGoAway(sid, g.code, g.debugData); err != nil { if err := t.framer.fr.WriteGoAway(sid, g.code, g.debugData); err != nil {
return false, err return false, err
} }
@ -1196,6 +1328,7 @@ func (t *http2Server) outgoingGoAwayHandler(g *goAway) (bool, error) {
return true, nil return true, nil
} }
t.mu.Unlock() t.mu.Unlock()
t.maxStreamMu.Unlock()
// For a graceful close, send out a GoAway with stream ID of MaxUInt32, // For a graceful close, send out a GoAway with stream ID of MaxUInt32,
// Follow that with a ping and wait for the ack to come back or a timer // Follow that with a ping and wait for the ack to come back or a timer
// to expire. During this time accept new streams since they might have // to expire. During this time accept new streams since they might have
@ -1280,3 +1413,18 @@ func getJitter(v time.Duration) time.Duration {
j := grpcrand.Int63n(2*r) - r j := grpcrand.Int63n(2*r) - r
return time.Duration(j) return time.Duration(j)
} }
type connectionKey struct{}
// GetConnection gets the connection from the context.
func GetConnection(ctx context.Context) net.Conn {
conn, _ := ctx.Value(connectionKey{}).(net.Conn)
return conn
}
// SetConnection adds the connection to the context to be able to get
// information about the destination ip and port for an incoming RPC. This also
// allows any unary or streaming interceptors to see the connection.
func setConnection(ctx context.Context, conn net.Conn) context.Context {
return context.WithValue(ctx, connectionKey{}, conn)
}

View File

@ -39,7 +39,6 @@ import (
spb "google.golang.org/genproto/googleapis/rpc/status" spb "google.golang.org/genproto/googleapis/rpc/status"
"google.golang.org/grpc/codes" "google.golang.org/grpc/codes"
"google.golang.org/grpc/grpclog" "google.golang.org/grpc/grpclog"
"google.golang.org/grpc/internal/grpcutil"
"google.golang.org/grpc/status" "google.golang.org/grpc/status"
) )
@ -96,53 +95,6 @@ var (
logger = grpclog.Component("transport") logger = grpclog.Component("transport")
) )
type parsedHeaderData struct {
encoding string
// statusGen caches the stream status received from the trailer the server
// sent. Client side only. Do not access directly. After all trailers are
// parsed, use the status method to retrieve the status.
statusGen *status.Status
// rawStatusCode and rawStatusMsg are set from the raw trailer fields and are not
// intended for direct access outside of parsing.
rawStatusCode *int
rawStatusMsg string
httpStatus *int
// Server side only fields.
timeoutSet bool
timeout time.Duration
method string
httpMethod string
// key-value metadata map from the peer.
mdata map[string][]string
statsTags []byte
statsTrace []byte
contentSubtype string
// isGRPC field indicates whether the peer is speaking gRPC (otherwise HTTP).
//
// We are in gRPC mode (peer speaking gRPC) if:
// * We are client side and have already received a HEADER frame that indicates gRPC peer.
// * The header contains valid a content-type, i.e. a string starts with "application/grpc"
// And we should handle error specific to gRPC.
//
// Otherwise (i.e. a content-type string starts without "application/grpc", or does not exist), we
// are in HTTP fallback mode, and should handle error specific to HTTP.
isGRPC bool
grpcErr error
httpErr error
contentTypeErr string
}
// decodeState configures decoding criteria and records the decoded data.
type decodeState struct {
// whether decoding on server side or not
serverSide bool
// Records the states during HPACK decoding. It will be filled with info parsed from HTTP HEADERS
// frame once decodeHeader function has been invoked and returned.
data parsedHeaderData
}
// isReservedHeader checks whether hdr belongs to HTTP2 headers // isReservedHeader checks whether hdr belongs to HTTP2 headers
// reserved by gRPC protocol. Any other headers are classified as the // reserved by gRPC protocol. Any other headers are classified as the
// user-specified metadata. // user-specified metadata.
@ -180,14 +132,6 @@ func isWhitelistedHeader(hdr string) bool {
} }
} }
func (d *decodeState) status() *status.Status {
if d.data.statusGen == nil {
// No status-details were provided; generate status using code/msg.
d.data.statusGen = status.New(codes.Code(int32(*(d.data.rawStatusCode))), d.data.rawStatusMsg)
}
return d.data.statusGen
}
const binHdrSuffix = "-bin" const binHdrSuffix = "-bin"
func encodeBinHeader(v []byte) string { func encodeBinHeader(v []byte) string {
@ -217,168 +161,16 @@ func decodeMetadataHeader(k, v string) (string, error) {
return v, nil return v, nil
} }
func (d *decodeState) decodeHeader(frame *http2.MetaHeadersFrame) (http2.ErrCode, error) { func decodeGRPCStatusDetails(rawDetails string) (*status.Status, error) {
// frame.Truncated is set to true when framer detects that the current header v, err := decodeBinHeader(rawDetails)
// list size hits MaxHeaderListSize limit.
if frame.Truncated {
return http2.ErrCodeFrameSize, status.Error(codes.Internal, "peer header list size exceeded limit")
}
for _, hf := range frame.Fields {
d.processHeaderField(hf)
}
if d.data.isGRPC {
if d.data.grpcErr != nil {
return http2.ErrCodeProtocol, d.data.grpcErr
}
if d.serverSide {
return http2.ErrCodeNo, nil
}
if d.data.rawStatusCode == nil && d.data.statusGen == nil {
// gRPC status doesn't exist.
// Set rawStatusCode to be unknown and return nil error.
// So that, if the stream has ended this Unknown status
// will be propagated to the user.
// Otherwise, it will be ignored. In which case, status from
// a later trailer, that has StreamEnded flag set, is propagated.
code := int(codes.Unknown)
d.data.rawStatusCode = &code
}
return http2.ErrCodeNo, nil
}
// HTTP fallback mode
if d.data.httpErr != nil {
return http2.ErrCodeProtocol, d.data.httpErr
}
var (
code = codes.Internal // when header does not include HTTP status, return INTERNAL
ok bool
)
if d.data.httpStatus != nil {
code, ok = HTTPStatusConvTab[*(d.data.httpStatus)]
if !ok {
code = codes.Unknown
}
}
return http2.ErrCodeProtocol, status.Error(code, d.constructHTTPErrMsg())
}
// constructErrMsg constructs error message to be returned in HTTP fallback mode.
// Format: HTTP status code and its corresponding message + content-type error message.
func (d *decodeState) constructHTTPErrMsg() string {
var errMsgs []string
if d.data.httpStatus == nil {
errMsgs = append(errMsgs, "malformed header: missing HTTP status")
} else {
errMsgs = append(errMsgs, fmt.Sprintf("%s: HTTP status code %d", http.StatusText(*(d.data.httpStatus)), *d.data.httpStatus))
}
if d.data.contentTypeErr == "" {
errMsgs = append(errMsgs, "transport: missing content-type field")
} else {
errMsgs = append(errMsgs, d.data.contentTypeErr)
}
return strings.Join(errMsgs, "; ")
}
func (d *decodeState) addMetadata(k, v string) {
if d.data.mdata == nil {
d.data.mdata = make(map[string][]string)
}
d.data.mdata[k] = append(d.data.mdata[k], v)
}
func (d *decodeState) processHeaderField(f hpack.HeaderField) {
switch f.Name {
case "content-type":
contentSubtype, validContentType := grpcutil.ContentSubtype(f.Value)
if !validContentType {
d.data.contentTypeErr = fmt.Sprintf("transport: received the unexpected content-type %q", f.Value)
return
}
d.data.contentSubtype = contentSubtype
// TODO: do we want to propagate the whole content-type in the metadata,
// or come up with a way to just propagate the content-subtype if it was set?
// ie {"content-type": "application/grpc+proto"} or {"content-subtype": "proto"}
// in the metadata?
d.addMetadata(f.Name, f.Value)
d.data.isGRPC = true
case "grpc-encoding":
d.data.encoding = f.Value
case "grpc-status":
code, err := strconv.Atoi(f.Value)
if err != nil { if err != nil {
d.data.grpcErr = status.Errorf(codes.Internal, "transport: malformed grpc-status: %v", err) return nil, err
return
} }
d.data.rawStatusCode = &code st := &spb.Status{}
case "grpc-message": if err = proto.Unmarshal(v, st); err != nil {
d.data.rawStatusMsg = decodeGrpcMessage(f.Value) return nil, err
case "grpc-status-details-bin":
v, err := decodeBinHeader(f.Value)
if err != nil {
d.data.grpcErr = status.Errorf(codes.Internal, "transport: malformed grpc-status-details-bin: %v", err)
return
}
s := &spb.Status{}
if err := proto.Unmarshal(v, s); err != nil {
d.data.grpcErr = status.Errorf(codes.Internal, "transport: malformed grpc-status-details-bin: %v", err)
return
}
d.data.statusGen = status.FromProto(s)
case "grpc-timeout":
d.data.timeoutSet = true
var err error
if d.data.timeout, err = decodeTimeout(f.Value); err != nil {
d.data.grpcErr = status.Errorf(codes.Internal, "transport: malformed time-out: %v", err)
}
case ":path":
d.data.method = f.Value
case ":status":
code, err := strconv.Atoi(f.Value)
if err != nil {
d.data.httpErr = status.Errorf(codes.Internal, "transport: malformed http-status: %v", err)
return
}
d.data.httpStatus = &code
case "grpc-tags-bin":
v, err := decodeBinHeader(f.Value)
if err != nil {
d.data.grpcErr = status.Errorf(codes.Internal, "transport: malformed grpc-tags-bin: %v", err)
return
}
d.data.statsTags = v
d.addMetadata(f.Name, string(v))
case "grpc-trace-bin":
v, err := decodeBinHeader(f.Value)
if err != nil {
d.data.grpcErr = status.Errorf(codes.Internal, "transport: malformed grpc-trace-bin: %v", err)
return
}
d.data.statsTrace = v
d.addMetadata(f.Name, string(v))
case ":method":
d.data.httpMethod = f.Value
default:
if isReservedHeader(f.Name) && !isWhitelistedHeader(f.Name) {
break
}
v, err := decodeMetadataHeader(f.Name, f.Value)
if err != nil {
if logger.V(logLevel) {
logger.Errorf("Failed to decode metadata header (%q, %q): %v", f.Name, f.Value, err)
}
return
}
d.addMetadata(f.Name, v)
} }
return status.FromProto(st), nil
} }
type timeoutUnit uint8 type timeoutUnit uint8

View File

@ -17,7 +17,7 @@
*/ */
// Package networktype declares the network type to be used in the default // Package networktype declares the network type to be used in the default
// dailer. Attribute of a resolver.Address. // dialer. Attribute of a resolver.Address.
package networktype package networktype
import ( import (
@ -31,7 +31,7 @@ const key = keyType("grpc.internal.transport.networktype")
// Set returns a copy of the provided address with attributes containing networkType. // Set returns a copy of the provided address with attributes containing networkType.
func Set(address resolver.Address, networkType string) resolver.Address { func Set(address resolver.Address, networkType string) resolver.Address {
address.Attributes = address.Attributes.WithValues(key, networkType) address.Attributes = address.Attributes.WithValue(key, networkType)
return address return address
} }

View File

@ -37,7 +37,7 @@ var (
httpProxyFromEnvironment = http.ProxyFromEnvironment httpProxyFromEnvironment = http.ProxyFromEnvironment
) )
func mapAddress(ctx context.Context, address string) (*url.URL, error) { func mapAddress(address string) (*url.URL, error) {
req := &http.Request{ req := &http.Request{
URL: &url.URL{ URL: &url.URL{
Scheme: "https", Scheme: "https",
@ -114,7 +114,7 @@ func doHTTPConnectHandshake(ctx context.Context, conn net.Conn, backendAddr stri
// connection. // connection.
func proxyDial(ctx context.Context, addr string, grpcUA string) (conn net.Conn, err error) { func proxyDial(ctx context.Context, addr string, grpcUA string) (conn net.Conn, err error) {
newAddr := addr newAddr := addr
proxyURL, err := mapAddress(ctx, addr) proxyURL, err := mapAddress(addr)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -30,6 +30,7 @@ import (
"net" "net"
"sync" "sync"
"sync/atomic" "sync/atomic"
"time"
"google.golang.org/grpc/codes" "google.golang.org/grpc/codes"
"google.golang.org/grpc/credentials" "google.golang.org/grpc/credentials"
@ -518,7 +519,8 @@ const (
// ServerConfig consists of all the configurations to establish a server transport. // ServerConfig consists of all the configurations to establish a server transport.
type ServerConfig struct { type ServerConfig struct {
MaxStreams uint32 MaxStreams uint32
AuthInfo credentials.AuthInfo ConnectionTimeout time.Duration
Credentials credentials.TransportCredentials
InTapHandle tap.ServerInHandle InTapHandle tap.ServerInHandle
StatsHandler stats.Handler StatsHandler stats.Handler
KeepaliveParams keepalive.ServerParameters KeepaliveParams keepalive.ServerParameters
@ -532,12 +534,6 @@ type ServerConfig struct {
HeaderTableSize *uint32 HeaderTableSize *uint32
} }
// NewServerTransport creates a ServerTransport with conn or non-nil error
// if it fails.
func NewServerTransport(protocol string, conn net.Conn, config *ServerConfig) (ServerTransport, error) {
return newHTTP2Server(conn, config)
}
// ConnectOptions covers all relevant options for communicating with the server. // ConnectOptions covers all relevant options for communicating with the server.
type ConnectOptions struct { type ConnectOptions struct {
// UserAgent is the application user agent. // UserAgent is the application user agent.
@ -694,7 +690,7 @@ type ServerTransport interface {
// Close tears down the transport. Once it is called, the transport // Close tears down the transport. Once it is called, the transport
// should not be accessed any more. All the pending streams and their // should not be accessed any more. All the pending streams and their
// handlers will be terminated asynchronously. // handlers will be terminated asynchronously.
Close() error Close()
// RemoteAddr returns the remote network address. // RemoteAddr returns the remote network address.
RemoteAddr() net.Addr RemoteAddr() net.Addr

View File

@ -28,7 +28,7 @@ type handshakeClusterNameKey struct{}
// SetXDSHandshakeClusterName returns a copy of addr in which the Attributes field // SetXDSHandshakeClusterName returns a copy of addr in which the Attributes field
// is updated with the cluster name. // is updated with the cluster name.
func SetXDSHandshakeClusterName(addr resolver.Address, clusterName string) resolver.Address { func SetXDSHandshakeClusterName(addr resolver.Address, clusterName string) resolver.Address {
addr.Attributes = addr.Attributes.WithValues(handshakeClusterNameKey{}, clusterName) addr.Attributes = addr.Attributes.WithValue(handshakeClusterNameKey{}, clusterName)
return addr return addr
} }

View File

@ -93,12 +93,16 @@ func (md MD) Copy() MD {
} }
// Get obtains the values for a given key. // Get obtains the values for a given key.
//
// k is converted to lowercase before searching in md.
func (md MD) Get(k string) []string { func (md MD) Get(k string) []string {
k = strings.ToLower(k) k = strings.ToLower(k)
return md[k] return md[k]
} }
// Set sets the value of a given key with a slice of values. // Set sets the value of a given key with a slice of values.
//
// k is converted to lowercase before storing in md.
func (md MD) Set(k string, vals ...string) { func (md MD) Set(k string, vals ...string) {
if len(vals) == 0 { if len(vals) == 0 {
return return
@ -107,7 +111,10 @@ func (md MD) Set(k string, vals ...string) {
md[k] = vals md[k] = vals
} }
// Append adds the values to key k, not overwriting what was already stored at that key. // Append adds the values to key k, not overwriting what was already stored at
// that key.
//
// k is converted to lowercase before storing in md.
func (md MD) Append(k string, vals ...string) { func (md MD) Append(k string, vals ...string) {
if len(vals) == 0 { if len(vals) == 0 {
return return
@ -116,9 +123,17 @@ func (md MD) Append(k string, vals ...string) {
md[k] = append(md[k], vals...) md[k] = append(md[k], vals...)
} }
// Delete removes the values for a given key k which is converted to lowercase
// before removing it from md.
func (md MD) Delete(k string) {
k = strings.ToLower(k)
delete(md, k)
}
// Join joins any number of mds into a single MD. // Join joins any number of mds into a single MD.
// The order of values for each key is determined by the order in which //
// the mds containing those values are presented to Join. // The order of values for each key is determined by the order in which the mds
// containing those values are presented to Join.
func Join(mds ...MD) MD { func Join(mds ...MD) MD {
out := MD{} out := MD{}
for _, md := range mds { for _, md := range mds {
@ -145,8 +160,8 @@ func NewOutgoingContext(ctx context.Context, md MD) context.Context {
} }
// AppendToOutgoingContext returns a new context with the provided kv merged // AppendToOutgoingContext returns a new context with the provided kv merged
// with any existing metadata in the context. Please refer to the // with any existing metadata in the context. Please refer to the documentation
// documentation of Pairs for a description of kv. // of Pairs for a description of kv.
func AppendToOutgoingContext(ctx context.Context, kv ...string) context.Context { func AppendToOutgoingContext(ctx context.Context, kv ...string) context.Context {
if len(kv)%2 == 1 { if len(kv)%2 == 1 {
panic(fmt.Sprintf("metadata: AppendToOutgoingContext got an odd number of input pairs for metadata: %d", len(kv))) panic(fmt.Sprintf("metadata: AppendToOutgoingContext got an odd number of input pairs for metadata: %d", len(kv)))
@ -159,20 +174,34 @@ func AppendToOutgoingContext(ctx context.Context, kv ...string) context.Context
return context.WithValue(ctx, mdOutgoingKey{}, rawMD{md: md.md, added: added}) return context.WithValue(ctx, mdOutgoingKey{}, rawMD{md: md.md, added: added})
} }
// FromIncomingContext returns the incoming metadata in ctx if it exists. The // FromIncomingContext returns the incoming metadata in ctx if it exists.
// returned MD should not be modified. Writing to it may cause races. //
// Modification should be made to copies of the returned MD. // All keys in the returned MD are lowercase.
func FromIncomingContext(ctx context.Context) (md MD, ok bool) { func FromIncomingContext(ctx context.Context) (MD, bool) {
md, ok = ctx.Value(mdIncomingKey{}).(MD) md, ok := ctx.Value(mdIncomingKey{}).(MD)
return if !ok {
return nil, false
}
out := MD{}
for k, v := range md {
// We need to manually convert all keys to lower case, because MD is a
// map, and there's no guarantee that the MD attached to the context is
// created using our helper functions.
key := strings.ToLower(k)
out[key] = v
}
return out, true
} }
// FromOutgoingContextRaw returns the un-merged, intermediary contents // FromOutgoingContextRaw returns the un-merged, intermediary contents of rawMD.
// of rawMD. Remember to perform strings.ToLower on the keys. The returned
// MD should not be modified. Writing to it may cause races. Modification
// should be made to copies of the returned MD.
// //
// This is intended for gRPC-internal use ONLY. // Remember to perform strings.ToLower on the keys, for both the returned MD (MD
// is a map, there's no guarantee it's created using our helper functions) and
// the extra kv pairs (AppendToOutgoingContext doesn't turn them into
// lowercase).
//
// This is intended for gRPC-internal use ONLY. Users should use
// FromOutgoingContext instead.
func FromOutgoingContextRaw(ctx context.Context) (MD, [][]string, bool) { func FromOutgoingContextRaw(ctx context.Context) (MD, [][]string, bool) {
raw, ok := ctx.Value(mdOutgoingKey{}).(rawMD) raw, ok := ctx.Value(mdOutgoingKey{}).(rawMD)
if !ok { if !ok {
@ -182,16 +211,23 @@ func FromOutgoingContextRaw(ctx context.Context) (MD, [][]string, bool) {
return raw.md, raw.added, true return raw.md, raw.added, true
} }
// FromOutgoingContext returns the outgoing metadata in ctx if it exists. The // FromOutgoingContext returns the outgoing metadata in ctx if it exists.
// returned MD should not be modified. Writing to it may cause races. //
// Modification should be made to copies of the returned MD. // All keys in the returned MD are lowercase.
func FromOutgoingContext(ctx context.Context) (MD, bool) { func FromOutgoingContext(ctx context.Context) (MD, bool) {
raw, ok := ctx.Value(mdOutgoingKey{}).(rawMD) raw, ok := ctx.Value(mdOutgoingKey{}).(rawMD)
if !ok { if !ok {
return nil, false return nil, false
} }
out := raw.md.Copy() out := MD{}
for k, v := range raw.md {
// We need to manually convert all keys to lower case, because MD is a
// map, and there's no guarantee that the MD attached to the context is
// created using our helper functions.
key := strings.ToLower(k)
out[key] = v
}
for _, added := range raw.added { for _, added := range raw.added {
if len(added)%2 == 1 { if len(added)%2 == 1 {
panic(fmt.Sprintf("metadata: FromOutgoingContext got an odd number of input pairs for metadata: %d", len(added))) panic(fmt.Sprintf("metadata: FromOutgoingContext got an odd number of input pairs for metadata: %d", len(added)))

View File

@ -144,10 +144,10 @@ func (pw *pickerWrapper) pick(ctx context.Context, failfast bool, info balancer.
acw, ok := pickResult.SubConn.(*acBalancerWrapper) acw, ok := pickResult.SubConn.(*acBalancerWrapper)
if !ok { if !ok {
logger.Error("subconn returned from pick is not *acBalancerWrapper") logger.Errorf("subconn returned from pick is type %T, not *acBalancerWrapper", pickResult.SubConn)
continue continue
} }
if t, ok := acw.getAddrConn().getReadyTransport(); ok { if t := acw.getAddrConn().getReadyTransport(); t != nil {
if channelz.IsOn() { if channelz.IsOn() {
return t, doneChannelzWrapper(acw, pickResult.Done), nil return t, doneChannelzWrapper(acw, pickResult.Done), nil
} }

View File

@ -107,10 +107,12 @@ func (b *pickfirstBalancer) UpdateSubConnState(sc balancer.SubConn, s balancer.S
} }
switch s.ConnectivityState { switch s.ConnectivityState {
case connectivity.Ready, connectivity.Idle: case connectivity.Ready:
b.cc.UpdateState(balancer.State{ConnectivityState: s.ConnectivityState, Picker: &picker{result: balancer.PickResult{SubConn: sc}}}) b.cc.UpdateState(balancer.State{ConnectivityState: s.ConnectivityState, Picker: &picker{result: balancer.PickResult{SubConn: sc}}})
case connectivity.Connecting: case connectivity.Connecting:
b.cc.UpdateState(balancer.State{ConnectivityState: s.ConnectivityState, Picker: &picker{err: balancer.ErrNoSubConnAvailable}}) b.cc.UpdateState(balancer.State{ConnectivityState: s.ConnectivityState, Picker: &picker{err: balancer.ErrNoSubConnAvailable}})
case connectivity.Idle:
b.cc.UpdateState(balancer.State{ConnectivityState: s.ConnectivityState, Picker: &idlePicker{sc: sc}})
case connectivity.TransientFailure: case connectivity.TransientFailure:
b.cc.UpdateState(balancer.State{ b.cc.UpdateState(balancer.State{
ConnectivityState: s.ConnectivityState, ConnectivityState: s.ConnectivityState,
@ -122,6 +124,12 @@ func (b *pickfirstBalancer) UpdateSubConnState(sc balancer.SubConn, s balancer.S
func (b *pickfirstBalancer) Close() { func (b *pickfirstBalancer) Close() {
} }
func (b *pickfirstBalancer) ExitIdle() {
if b.sc != nil && b.state == connectivity.Idle {
b.sc.Connect()
}
}
type picker struct { type picker struct {
result balancer.PickResult result balancer.PickResult
err error err error
@ -131,6 +139,17 @@ func (p *picker) Pick(info balancer.PickInfo) (balancer.PickResult, error) {
return p.result, p.err return p.result, p.err
} }
// idlePicker is used when the SubConn is IDLE and kicks the SubConn into
// CONNECTING when Pick is called.
type idlePicker struct {
sc balancer.SubConn
}
func (i *idlePicker) Pick(info balancer.PickInfo) (balancer.PickResult, error) {
i.sc.Connect()
return balancer.PickResult{}, balancer.ErrNoSubConnAvailable
}
func init() { func init() {
balancer.Register(newPickfirstBuilder()) balancer.Register(newPickfirstBuilder())
} }

View File

@ -76,7 +76,21 @@ SOURCES=(
# These options of the form 'Mfoo.proto=bar' instruct the codegen to use an # These options of the form 'Mfoo.proto=bar' instruct the codegen to use an
# import path of 'bar' in the generated code when 'foo.proto' is imported in # import path of 'bar' in the generated code when 'foo.proto' is imported in
# one of the sources. # one of the sources.
OPTS=Mgrpc/service_config/service_config.proto=/internal/proto/grpc_service_config,Mgrpc/core/stats.proto=google.golang.org/grpc/interop/grpc_testing/core #
# Note that the protos listed here are all for testing purposes. All protos to
# be used externally should have a go_package option (and they don't need to be
# listed here).
OPTS=Mgrpc/service_config/service_config.proto=/internal/proto/grpc_service_config,\
Mgrpc/core/stats.proto=google.golang.org/grpc/interop/grpc_testing/core,\
Mgrpc/testing/benchmark_service.proto=google.golang.org/grpc/interop/grpc_testing,\
Mgrpc/testing/stats.proto=google.golang.org/grpc/interop/grpc_testing,\
Mgrpc/testing/report_qps_scenario_service.proto=google.golang.org/grpc/interop/grpc_testing,\
Mgrpc/testing/messages.proto=google.golang.org/grpc/interop/grpc_testing,\
Mgrpc/testing/worker_service.proto=google.golang.org/grpc/interop/grpc_testing,\
Mgrpc/testing/control.proto=google.golang.org/grpc/interop/grpc_testing,\
Mgrpc/testing/test.proto=google.golang.org/grpc/interop/grpc_testing,\
Mgrpc/testing/payloads.proto=google.golang.org/grpc/interop/grpc_testing,\
Mgrpc/testing/empty.proto=google.golang.org/grpc/interop/grpc_testing
for src in ${SOURCES[@]}; do for src in ${SOURCES[@]}; do
echo "protoc ${src}" echo "protoc ${src}"
@ -85,7 +99,6 @@ for src in ${SOURCES[@]}; do
-I${WORKDIR}/grpc-proto \ -I${WORKDIR}/grpc-proto \
-I${WORKDIR}/googleapis \ -I${WORKDIR}/googleapis \
-I${WORKDIR}/protobuf/src \ -I${WORKDIR}/protobuf/src \
-I${WORKDIR}/istio \
${src} ${src}
done done
@ -96,14 +109,13 @@ for src in ${LEGACY_SOURCES[@]}; do
-I${WORKDIR}/grpc-proto \ -I${WORKDIR}/grpc-proto \
-I${WORKDIR}/googleapis \ -I${WORKDIR}/googleapis \
-I${WORKDIR}/protobuf/src \ -I${WORKDIR}/protobuf/src \
-I${WORKDIR}/istio \
${src} ${src}
done done
# The go_package option in grpc/lookup/v1/rls.proto doesn't match the # The go_package option in grpc/lookup/v1/rls.proto doesn't match the
# current location. Move it into the right place. # current location. Move it into the right place.
mkdir -p ${WORKDIR}/out/google.golang.org/grpc/balancer/rls/internal/proto/grpc_lookup_v1 mkdir -p ${WORKDIR}/out/google.golang.org/grpc/internal/proto/grpc_lookup_v1
mv ${WORKDIR}/out/google.golang.org/grpc/lookup/grpc_lookup_v1/* ${WORKDIR}/out/google.golang.org/grpc/balancer/rls/internal/proto/grpc_lookup_v1 mv ${WORKDIR}/out/google.golang.org/grpc/lookup/grpc_lookup_v1/* ${WORKDIR}/out/google.golang.org/grpc/internal/proto/grpc_lookup_v1
# grpc_testingv3/testv3.pb.go is not re-generated because it was # grpc_testingv3/testv3.pb.go is not re-generated because it was
# intentionally generated by an older version of protoc-gen-go. # intentionally generated by an older version of protoc-gen-go.

109
vendor/google.golang.org/grpc/resolver/map.go generated vendored Normal file
View File

@ -0,0 +1,109 @@
/*
*
* Copyright 2021 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package resolver
type addressMapEntry struct {
addr Address
value interface{}
}
// AddressMap is a map of addresses to arbitrary values taking into account
// Attributes. BalancerAttributes are ignored, as are Metadata and Type.
// Multiple accesses may not be performed concurrently. Must be created via
// NewAddressMap; do not construct directly.
type AddressMap struct {
m map[string]addressMapEntryList
}
type addressMapEntryList []*addressMapEntry
// NewAddressMap creates a new AddressMap.
func NewAddressMap() *AddressMap {
return &AddressMap{m: make(map[string]addressMapEntryList)}
}
// find returns the index of addr in the addressMapEntry slice, or -1 if not
// present.
func (l addressMapEntryList) find(addr Address) int {
if len(l) == 0 {
return -1
}
for i, entry := range l {
if entry.addr.ServerName == addr.ServerName &&
entry.addr.Attributes.Equal(addr.Attributes) {
return i
}
}
return -1
}
// Get returns the value for the address in the map, if present.
func (a *AddressMap) Get(addr Address) (value interface{}, ok bool) {
entryList := a.m[addr.Addr]
if entry := entryList.find(addr); entry != -1 {
return entryList[entry].value, true
}
return nil, false
}
// Set updates or adds the value to the address in the map.
func (a *AddressMap) Set(addr Address, value interface{}) {
entryList := a.m[addr.Addr]
if entry := entryList.find(addr); entry != -1 {
a.m[addr.Addr][entry].value = value
return
}
a.m[addr.Addr] = append(a.m[addr.Addr], &addressMapEntry{addr: addr, value: value})
}
// Delete removes addr from the map.
func (a *AddressMap) Delete(addr Address) {
entryList := a.m[addr.Addr]
entry := entryList.find(addr)
if entry == -1 {
return
}
if len(entryList) == 1 {
entryList = nil
} else {
copy(entryList[entry:], entryList[entry+1:])
entryList = entryList[:len(entryList)-1]
}
a.m[addr.Addr] = entryList
}
// Len returns the number of entries in the map.
func (a *AddressMap) Len() int {
ret := 0
for _, entryList := range a.m {
ret += len(entryList)
}
return ret
}
// Keys returns a slice of all current map keys.
func (a *AddressMap) Keys() []Address {
ret := make([]Address, 0, a.Len())
for _, entryList := range a.m {
for _, entry := range entryList {
ret = append(ret, entry.addr)
}
}
return ret
}

View File

@ -23,6 +23,7 @@ package resolver
import ( import (
"context" "context"
"net" "net"
"net/url"
"google.golang.org/grpc/attributes" "google.golang.org/grpc/attributes"
"google.golang.org/grpc/credentials" "google.golang.org/grpc/credentials"
@ -116,9 +117,14 @@ type Address struct {
ServerName string ServerName string
// Attributes contains arbitrary data about this address intended for // Attributes contains arbitrary data about this address intended for
// consumption by the load balancing policy. // consumption by the SubConn.
Attributes *attributes.Attributes Attributes *attributes.Attributes
// BalancerAttributes contains arbitrary data about this address intended
// for consumption by the LB policy. These attribes do not affect SubConn
// creation, connection establishment, handshaking, etc.
BalancerAttributes *attributes.Attributes
// Type is the type of this address. // Type is the type of this address.
// //
// Deprecated: use Attributes instead. // Deprecated: use Attributes instead.
@ -131,6 +137,15 @@ type Address struct {
Metadata interface{} Metadata interface{}
} }
// Equal returns whether a and o are identical. Metadata is compared directly,
// not with any recursive introspection.
func (a *Address) Equal(o Address) bool {
return a.Addr == o.Addr && a.ServerName == o.ServerName &&
a.Attributes.Equal(o.Attributes) &&
a.BalancerAttributes.Equal(o.BalancerAttributes) &&
a.Type == o.Type && a.Metadata == o.Metadata
}
// BuildOptions includes additional information for the builder to create // BuildOptions includes additional information for the builder to create
// the resolver. // the resolver.
type BuildOptions struct { type BuildOptions struct {
@ -204,25 +219,36 @@ type ClientConn interface {
// Target represents a target for gRPC, as specified in: // Target represents a target for gRPC, as specified in:
// https://github.com/grpc/grpc/blob/master/doc/naming.md. // https://github.com/grpc/grpc/blob/master/doc/naming.md.
// It is parsed from the target string that gets passed into Dial or DialContext by the user. And // It is parsed from the target string that gets passed into Dial or DialContext
// grpc passes it to the resolver and the balancer. // by the user. And gRPC passes it to the resolver and the balancer.
// //
// If the target follows the naming spec, and the parsed scheme is registered with grpc, we will // If the target follows the naming spec, and the parsed scheme is registered
// parse the target string according to the spec. e.g. "dns://some_authority/foo.bar" will be parsed // with gRPC, we will parse the target string according to the spec. If the
// into &Target{Scheme: "dns", Authority: "some_authority", Endpoint: "foo.bar"} // target does not contain a scheme or if the parsed scheme is not registered
// (i.e. no corresponding resolver available to resolve the endpoint), we will
// apply the default scheme, and will attempt to reparse it.
// //
// If the target does not contain a scheme, we will apply the default scheme, and set the Target to // Examples:
// be the full target string. e.g. "foo.bar" will be parsed into
// &Target{Scheme: resolver.GetDefaultScheme(), Endpoint: "foo.bar"}.
// //
// If the parsed scheme is not registered (i.e. no corresponding resolver available to resolve the // - "dns://some_authority/foo.bar"
// endpoint), we set the Scheme to be the default scheme, and set the Endpoint to be the full target // Target{Scheme: "dns", Authority: "some_authority", Endpoint: "foo.bar"}
// string. e.g. target string "unknown_scheme://authority/endpoint" will be parsed into // - "foo.bar"
// &Target{Scheme: resolver.GetDefaultScheme(), Endpoint: "unknown_scheme://authority/endpoint"}. // Target{Scheme: resolver.GetDefaultScheme(), Endpoint: "foo.bar"}
// - "unknown_scheme://authority/endpoint"
// Target{Scheme: resolver.GetDefaultScheme(), Endpoint: "unknown_scheme://authority/endpoint"}
type Target struct { type Target struct {
// Deprecated: use URL.Scheme instead.
Scheme string Scheme string
// Deprecated: use URL.Host instead.
Authority string Authority string
// Deprecated: use URL.Path or URL.Opaque instead. The latter is set when
// the former is empty.
Endpoint string Endpoint string
// URL contains the parsed dial target with an optional default scheme added
// to it if the original dial target contained no scheme or contained an
// unregistered scheme. Any query params specified in the original dial
// target can be accessed from here.
URL url.URL
} }
// Builder creates a resolver that will be used to watch name resolution updates. // Builder creates a resolver that will be used to watch name resolution updates.

View File

@ -39,6 +39,8 @@ type ccResolverWrapper struct {
resolver resolver.Resolver resolver resolver.Resolver
done *grpcsync.Event done *grpcsync.Event
curState resolver.State curState resolver.State
incomingMu sync.Mutex // Synchronizes all the incoming calls.
} }
// newCCResolverWrapper uses the resolver.Builder to build a Resolver and // newCCResolverWrapper uses the resolver.Builder to build a Resolver and
@ -90,6 +92,8 @@ func (ccr *ccResolverWrapper) close() {
} }
func (ccr *ccResolverWrapper) UpdateState(s resolver.State) error { func (ccr *ccResolverWrapper) UpdateState(s resolver.State) error {
ccr.incomingMu.Lock()
defer ccr.incomingMu.Unlock()
if ccr.done.HasFired() { if ccr.done.HasFired() {
return nil return nil
} }
@ -105,6 +109,8 @@ func (ccr *ccResolverWrapper) UpdateState(s resolver.State) error {
} }
func (ccr *ccResolverWrapper) ReportError(err error) { func (ccr *ccResolverWrapper) ReportError(err error) {
ccr.incomingMu.Lock()
defer ccr.incomingMu.Unlock()
if ccr.done.HasFired() { if ccr.done.HasFired() {
return return
} }
@ -114,6 +120,8 @@ func (ccr *ccResolverWrapper) ReportError(err error) {
// NewAddress is called by the resolver implementation to send addresses to gRPC. // NewAddress is called by the resolver implementation to send addresses to gRPC.
func (ccr *ccResolverWrapper) NewAddress(addrs []resolver.Address) { func (ccr *ccResolverWrapper) NewAddress(addrs []resolver.Address) {
ccr.incomingMu.Lock()
defer ccr.incomingMu.Unlock()
if ccr.done.HasFired() { if ccr.done.HasFired() {
return return
} }
@ -128,6 +136,8 @@ func (ccr *ccResolverWrapper) NewAddress(addrs []resolver.Address) {
// NewServiceConfig is called by the resolver implementation to send service // NewServiceConfig is called by the resolver implementation to send service
// configs to gRPC. // configs to gRPC.
func (ccr *ccResolverWrapper) NewServiceConfig(sc string) { func (ccr *ccResolverWrapper) NewServiceConfig(sc string) {
ccr.incomingMu.Lock()
defer ccr.incomingMu.Unlock()
if ccr.done.HasFired() { if ccr.done.HasFired() {
return return
} }

View File

@ -258,7 +258,8 @@ func (o PeerCallOption) after(c *callInfo, attempt *csAttempt) {
} }
// WaitForReady configures the action to take when an RPC is attempted on broken // WaitForReady configures the action to take when an RPC is attempted on broken
// connections or unreachable servers. If waitForReady is false, the RPC will fail // connections or unreachable servers. If waitForReady is false and the
// connection is in the TRANSIENT_FAILURE state, the RPC will fail
// immediately. Otherwise, the RPC client will block the call until a // immediately. Otherwise, the RPC client will block the call until a
// connection is available (or the call is canceled or times out) and will // connection is available (or the call is canceled or times out) and will
// retry the call if it fails due to a transient error. gRPC will not retry if // retry the call if it fails due to a transient error. gRPC will not retry if
@ -711,13 +712,11 @@ func recvAndDecompress(p *parser, s *transport.Stream, dc Decompressor, maxRecei
if err != nil { if err != nil {
return nil, status.Errorf(codes.Internal, "grpc: failed to decompress the received message %v", err) return nil, status.Errorf(codes.Internal, "grpc: failed to decompress the received message %v", err)
} }
} else {
size = len(d)
}
if size > maxReceiveMessageSize { if size > maxReceiveMessageSize {
// TODO: Revisit the error code. Currently keep it consistent with java // TODO: Revisit the error code. Currently keep it consistent with java
// implementation. // implementation.
return nil, status.Errorf(codes.ResourceExhausted, "grpc: received message larger than max (%d vs. %d)", size, maxReceiveMessageSize) return nil, status.Errorf(codes.ResourceExhausted, "grpc: received message after decompression larger than max (%d vs. %d)", size, maxReceiveMessageSize)
}
} }
return d, nil return d, nil
} }
@ -828,26 +827,28 @@ func Errorf(c codes.Code, format string, a ...interface{}) error {
// toRPCErr converts an error into an error from the status package. // toRPCErr converts an error into an error from the status package.
func toRPCErr(err error) error { func toRPCErr(err error) error {
if err == nil || err == io.EOF {
return err
}
if err == io.ErrUnexpectedEOF {
return status.Error(codes.Internal, err.Error())
}
if _, ok := status.FromError(err); ok {
return err
}
switch e := err.(type) {
case transport.ConnectionError:
return status.Error(codes.Unavailable, e.Desc)
default:
switch err { switch err {
case nil, io.EOF:
return err
case context.DeadlineExceeded: case context.DeadlineExceeded:
return status.Error(codes.DeadlineExceeded, err.Error()) return status.Error(codes.DeadlineExceeded, err.Error())
case context.Canceled: case context.Canceled:
return status.Error(codes.Canceled, err.Error()) return status.Error(codes.Canceled, err.Error())
case io.ErrUnexpectedEOF:
return status.Error(codes.Internal, err.Error())
} }
switch e := err.(type) {
case transport.ConnectionError:
return status.Error(codes.Unavailable, e.Desc)
case *transport.NewStreamError:
return toRPCErr(e.Err)
} }
if _, ok := status.FromError(err); ok {
return err
}
return status.Error(codes.Unknown, err.Error()) return status.Error(codes.Unknown, err.Error())
} }

View File

@ -710,13 +710,6 @@ func (s *Server) GetServiceInfo() map[string]ServiceInfo {
// the server being stopped. // the server being stopped.
var ErrServerStopped = errors.New("grpc: the server has been stopped") var ErrServerStopped = errors.New("grpc: the server has been stopped")
func (s *Server) useTransportAuthenticator(rawConn net.Conn) (net.Conn, credentials.AuthInfo, error) {
if s.opts.creds == nil {
return rawConn, nil, nil
}
return s.opts.creds.ServerHandshake(rawConn)
}
type listenSocket struct { type listenSocket struct {
net.Listener net.Listener
channelzID int64 channelzID int64
@ -839,28 +832,14 @@ func (s *Server) handleRawConn(lisAddr string, rawConn net.Conn) {
return return
} }
rawConn.SetDeadline(time.Now().Add(s.opts.connectionTimeout)) rawConn.SetDeadline(time.Now().Add(s.opts.connectionTimeout))
conn, authInfo, err := s.useTransportAuthenticator(rawConn)
if err != nil {
// ErrConnDispatched means that the connection was dispatched away from
// gRPC; those connections should be left open.
if err != credentials.ErrConnDispatched {
s.mu.Lock()
s.errorf("ServerHandshake(%q) failed: %v", rawConn.RemoteAddr(), err)
s.mu.Unlock()
channelz.Warningf(logger, s.channelzID, "grpc: Server.Serve failed to complete security handshake from %q: %v", rawConn.RemoteAddr(), err)
rawConn.Close()
}
rawConn.SetDeadline(time.Time{})
return
}
// Finish handshaking (HTTP2) // Finish handshaking (HTTP2)
st := s.newHTTP2Transport(conn, authInfo) st := s.newHTTP2Transport(rawConn)
rawConn.SetDeadline(time.Time{})
if st == nil { if st == nil {
return return
} }
rawConn.SetDeadline(time.Time{})
if !s.addConn(lisAddr, st) { if !s.addConn(lisAddr, st) {
return return
} }
@ -881,10 +860,11 @@ func (s *Server) drainServerTransports(addr string) {
// newHTTP2Transport sets up a http/2 transport (using the // newHTTP2Transport sets up a http/2 transport (using the
// gRPC http2 server transport in transport/http2_server.go). // gRPC http2 server transport in transport/http2_server.go).
func (s *Server) newHTTP2Transport(c net.Conn, authInfo credentials.AuthInfo) transport.ServerTransport { func (s *Server) newHTTP2Transport(c net.Conn) transport.ServerTransport {
config := &transport.ServerConfig{ config := &transport.ServerConfig{
MaxStreams: s.opts.maxConcurrentStreams, MaxStreams: s.opts.maxConcurrentStreams,
AuthInfo: authInfo, ConnectionTimeout: s.opts.connectionTimeout,
Credentials: s.opts.creds,
InTapHandle: s.opts.inTapHandle, InTapHandle: s.opts.inTapHandle,
StatsHandler: s.opts.statsHandler, StatsHandler: s.opts.statsHandler,
KeepaliveParams: s.opts.keepaliveParams, KeepaliveParams: s.opts.keepaliveParams,
@ -897,13 +877,20 @@ func (s *Server) newHTTP2Transport(c net.Conn, authInfo credentials.AuthInfo) tr
MaxHeaderListSize: s.opts.maxHeaderListSize, MaxHeaderListSize: s.opts.maxHeaderListSize,
HeaderTableSize: s.opts.headerTableSize, HeaderTableSize: s.opts.headerTableSize,
} }
st, err := transport.NewServerTransport("http2", c, config) st, err := transport.NewServerTransport(c, config)
if err != nil { if err != nil {
s.mu.Lock() s.mu.Lock()
s.errorf("NewServerTransport(%q) failed: %v", c.RemoteAddr(), err) s.errorf("NewServerTransport(%q) failed: %v", c.RemoteAddr(), err)
s.mu.Unlock() s.mu.Unlock()
c.Close() // ErrConnDispatched means that the connection was dispatched away from
// gRPC; those connections should be left open.
if err != credentials.ErrConnDispatched {
// Don't log on ErrConnDispatched and io.EOF to prevent log spam.
if err != io.EOF {
channelz.Warning(logger, s.channelzID, "grpc: Server.Serve failed to create ServerTransport: ", err) channelz.Warning(logger, s.channelzID, "grpc: Server.Serve failed to create ServerTransport: ", err)
}
c.Close()
}
return nil return nil
} }
@ -1109,22 +1096,29 @@ func chainUnaryServerInterceptors(s *Server) {
} else if len(interceptors) == 1 { } else if len(interceptors) == 1 {
chainedInt = interceptors[0] chainedInt = interceptors[0]
} else { } else {
chainedInt = func(ctx context.Context, req interface{}, info *UnaryServerInfo, handler UnaryHandler) (interface{}, error) { chainedInt = chainUnaryInterceptors(interceptors)
return interceptors[0](ctx, req, info, getChainUnaryHandler(interceptors, 0, info, handler))
}
} }
s.opts.unaryInt = chainedInt s.opts.unaryInt = chainedInt
} }
// getChainUnaryHandler recursively generate the chained UnaryHandler func chainUnaryInterceptors(interceptors []UnaryServerInterceptor) UnaryServerInterceptor {
func getChainUnaryHandler(interceptors []UnaryServerInterceptor, curr int, info *UnaryServerInfo, finalHandler UnaryHandler) UnaryHandler { return func(ctx context.Context, req interface{}, info *UnaryServerInfo, handler UnaryHandler) (interface{}, error) {
if curr == len(interceptors)-1 { // the struct ensures the variables are allocated together, rather than separately, since we
return finalHandler // know they should be garbage collected together. This saves 1 allocation and decreases
// time/call by about 10% on the microbenchmark.
var state struct {
i int
next UnaryHandler
} }
state.next = func(ctx context.Context, req interface{}) (interface{}, error) {
return func(ctx context.Context, req interface{}) (interface{}, error) { if state.i == len(interceptors)-1 {
return interceptors[curr+1](ctx, req, info, getChainUnaryHandler(interceptors, curr+1, info, finalHandler)) return interceptors[state.i](ctx, req, info, handler)
}
state.i++
return interceptors[state.i-1](ctx, req, info, state.next)
}
return state.next(ctx, req)
} }
} }
@ -1139,6 +1133,8 @@ func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport.
beginTime := time.Now() beginTime := time.Now()
statsBegin = &stats.Begin{ statsBegin = &stats.Begin{
BeginTime: beginTime, BeginTime: beginTime,
IsClientStream: false,
IsServerStream: false,
} }
sh.HandleRPC(stream.Context(), statsBegin) sh.HandleRPC(stream.Context(), statsBegin)
} }
@ -1390,22 +1386,29 @@ func chainStreamServerInterceptors(s *Server) {
} else if len(interceptors) == 1 { } else if len(interceptors) == 1 {
chainedInt = interceptors[0] chainedInt = interceptors[0]
} else { } else {
chainedInt = func(srv interface{}, ss ServerStream, info *StreamServerInfo, handler StreamHandler) error { chainedInt = chainStreamInterceptors(interceptors)
return interceptors[0](srv, ss, info, getChainStreamHandler(interceptors, 0, info, handler))
}
} }
s.opts.streamInt = chainedInt s.opts.streamInt = chainedInt
} }
// getChainStreamHandler recursively generate the chained StreamHandler func chainStreamInterceptors(interceptors []StreamServerInterceptor) StreamServerInterceptor {
func getChainStreamHandler(interceptors []StreamServerInterceptor, curr int, info *StreamServerInfo, finalHandler StreamHandler) StreamHandler { return func(srv interface{}, ss ServerStream, info *StreamServerInfo, handler StreamHandler) error {
if curr == len(interceptors)-1 { // the struct ensures the variables are allocated together, rather than separately, since we
return finalHandler // know they should be garbage collected together. This saves 1 allocation and decreases
// time/call by about 10% on the microbenchmark.
var state struct {
i int
next StreamHandler
} }
state.next = func(srv interface{}, ss ServerStream) error {
return func(srv interface{}, ss ServerStream) error { if state.i == len(interceptors)-1 {
return interceptors[curr+1](srv, ss, info, getChainStreamHandler(interceptors, curr+1, info, finalHandler)) return interceptors[state.i](srv, ss, info, handler)
}
state.i++
return interceptors[state.i-1](srv, ss, info, state.next)
}
return state.next(srv, ss)
} }
} }
@ -1419,6 +1422,8 @@ func (s *Server) processStreamingRPC(t transport.ServerTransport, stream *transp
beginTime := time.Now() beginTime := time.Now()
statsBegin = &stats.Begin{ statsBegin = &stats.Begin{
BeginTime: beginTime, BeginTime: beginTime,
IsClientStream: sd.ClientStreams,
IsServerStream: sd.ServerStreams,
} }
sh.HandleRPC(stream.Context(), statsBegin) sh.HandleRPC(stream.Context(), statsBegin)
} }
@ -1521,6 +1526,8 @@ func (s *Server) processStreamingRPC(t transport.ServerTransport, stream *transp
} }
} }
ss.ctx = newContextWithRPCInfo(ss.ctx, false, ss.codec, ss.cp, ss.comp)
if trInfo != nil { if trInfo != nil {
trInfo.tr.LazyLog(&trInfo.firstLine, false) trInfo.tr.LazyLog(&trInfo.firstLine, false)
} }
@ -1588,7 +1595,7 @@ func (s *Server) handleStream(t transport.ServerTransport, stream *transport.Str
trInfo.tr.SetError() trInfo.tr.SetError()
} }
errDesc := fmt.Sprintf("malformed method name: %q", stream.Method()) errDesc := fmt.Sprintf("malformed method name: %q", stream.Method())
if err := t.WriteStatus(stream, status.New(codes.ResourceExhausted, errDesc)); err != nil { if err := t.WriteStatus(stream, status.New(codes.Unimplemented, errDesc)); err != nil {
if trInfo != nil { if trInfo != nil {
trInfo.tr.LazyLog(&fmtStringer{"%v", []interface{}{err}}, true) trInfo.tr.LazyLog(&fmtStringer{"%v", []interface{}{err}}, true)
trInfo.tr.SetError() trInfo.tr.SetError()

View File

@ -36,15 +36,22 @@ type RPCStats interface {
IsClient() bool IsClient() bool
} }
// Begin contains stats when an RPC begins. // Begin contains stats when an RPC attempt begins.
// FailFast is only valid if this Begin is from client side. // FailFast is only valid if this Begin is from client side.
type Begin struct { type Begin struct {
// Client is true if this Begin is from client side. // Client is true if this Begin is from client side.
Client bool Client bool
// BeginTime is the time when the RPC begins. // BeginTime is the time when the RPC attempt begins.
BeginTime time.Time BeginTime time.Time
// FailFast indicates if this RPC is failfast. // FailFast indicates if this RPC is failfast.
FailFast bool FailFast bool
// IsClientStream indicates whether the RPC is a client streaming RPC.
IsClientStream bool
// IsServerStream indicates whether the RPC is a server streaming RPC.
IsServerStream bool
// IsTransparentRetryAttempt indicates whether this attempt was initiated
// due to transparently retrying a previous attempt.
IsTransparentRetryAttempt bool
} }
// IsClient indicates if the stats information is from client side. // IsClient indicates if the stats information is from client side.

View File

@ -29,6 +29,7 @@ package status
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
spb "google.golang.org/genproto/googleapis/rpc/status" spb "google.golang.org/genproto/googleapis/rpc/status"
@ -73,11 +74,16 @@ func FromProto(s *spb.Status) *Status {
return status.FromProto(s) return status.FromProto(s)
} }
// FromError returns a Status representing err if it was produced by this // FromError returns a Status representation of err.
// package or has a method `GRPCStatus() *Status`. //
// If err is nil, a Status is returned with codes.OK and no message. // - If err was produced by this package or implements the method `GRPCStatus()
// Otherwise, ok is false and a Status is returned with codes.Unknown and // *Status`, the appropriate Status is returned.
// the original error message. //
// - If err is nil, a Status is returned with codes.OK and no message.
//
// - Otherwise, err is an error not compatible with this package. In this
// case, a Status is returned with codes.Unknown and err's Error() message,
// and ok is false.
func FromError(err error) (s *Status, ok bool) { func FromError(err error) (s *Status, ok bool) {
if err == nil { if err == nil {
return nil, true return nil, true
@ -112,18 +118,18 @@ func Code(err error) codes.Code {
return codes.Unknown return codes.Unknown
} }
// FromContextError converts a context error into a Status. It returns a // FromContextError converts a context error or wrapped context error into a
// Status with codes.OK if err is nil, or a Status with codes.Unknown if err is // Status. It returns a Status with codes.OK if err is nil, or a Status with
// non-nil and not a context error. // codes.Unknown if err is non-nil and not a context error.
func FromContextError(err error) *Status { func FromContextError(err error) *Status {
switch err { if err == nil {
case nil:
return nil return nil
case context.DeadlineExceeded:
return New(codes.DeadlineExceeded, err.Error())
case context.Canceled:
return New(codes.Canceled, err.Error())
default:
return New(codes.Unknown, err.Error())
} }
if errors.Is(err, context.DeadlineExceeded) {
return New(codes.DeadlineExceeded, err.Error())
}
if errors.Is(err, context.Canceled) {
return New(codes.Canceled, err.Error())
}
return New(codes.Unknown, err.Error())
} }

View File

@ -274,33 +274,6 @@ func newClientStreamWithParams(ctx context.Context, desc *StreamDesc, cc *Client
if c.creds != nil { if c.creds != nil {
callHdr.Creds = c.creds callHdr.Creds = c.creds
} }
var trInfo *traceInfo
if EnableTracing {
trInfo = &traceInfo{
tr: trace.New("grpc.Sent."+methodFamily(method), method),
firstLine: firstLine{
client: true,
},
}
if deadline, ok := ctx.Deadline(); ok {
trInfo.firstLine.deadline = time.Until(deadline)
}
trInfo.tr.LazyLog(&trInfo.firstLine, false)
ctx = trace.NewContext(ctx, trInfo.tr)
}
ctx = newContextWithRPCInfo(ctx, c.failFast, c.codec, cp, comp)
sh := cc.dopts.copts.StatsHandler
var beginTime time.Time
if sh != nil {
ctx = sh.TagRPC(ctx, &stats.RPCTagInfo{FullMethodName: method, FailFast: c.failFast})
beginTime = time.Now()
begin := &stats.Begin{
Client: true,
BeginTime: beginTime,
FailFast: c.failFast,
}
sh.HandleRPC(ctx, begin)
}
cs := &clientStream{ cs := &clientStream{
callHdr: callHdr, callHdr: callHdr,
@ -314,7 +287,6 @@ func newClientStreamWithParams(ctx context.Context, desc *StreamDesc, cc *Client
cp: cp, cp: cp,
comp: comp, comp: comp,
cancel: cancel, cancel: cancel,
beginTime: beginTime,
firstAttempt: true, firstAttempt: true,
onCommit: onCommit, onCommit: onCommit,
} }
@ -323,9 +295,7 @@ func newClientStreamWithParams(ctx context.Context, desc *StreamDesc, cc *Client
} }
cs.binlog = binarylog.GetMethodLogger(method) cs.binlog = binarylog.GetMethodLogger(method)
// Only this initial attempt has stats/tracing. if err := cs.newAttemptLocked(false /* isTransparent */); err != nil {
// TODO(dfawley): move to newAttempt when per-attempt stats are implemented.
if err := cs.newAttemptLocked(sh, trInfo); err != nil {
cs.finish(err) cs.finish(err)
return nil, err return nil, err
} }
@ -373,8 +343,43 @@ func newClientStreamWithParams(ctx context.Context, desc *StreamDesc, cc *Client
// newAttemptLocked creates a new attempt with a transport. // newAttemptLocked creates a new attempt with a transport.
// If it succeeds, then it replaces clientStream's attempt with this new attempt. // If it succeeds, then it replaces clientStream's attempt with this new attempt.
func (cs *clientStream) newAttemptLocked(sh stats.Handler, trInfo *traceInfo) (retErr error) { func (cs *clientStream) newAttemptLocked(isTransparent bool) (retErr error) {
ctx := newContextWithRPCInfo(cs.ctx, cs.callInfo.failFast, cs.callInfo.codec, cs.cp, cs.comp)
method := cs.callHdr.Method
sh := cs.cc.dopts.copts.StatsHandler
var beginTime time.Time
if sh != nil {
ctx = sh.TagRPC(ctx, &stats.RPCTagInfo{FullMethodName: method, FailFast: cs.callInfo.failFast})
beginTime = time.Now()
begin := &stats.Begin{
Client: true,
BeginTime: beginTime,
FailFast: cs.callInfo.failFast,
IsClientStream: cs.desc.ClientStreams,
IsServerStream: cs.desc.ServerStreams,
IsTransparentRetryAttempt: isTransparent,
}
sh.HandleRPC(ctx, begin)
}
var trInfo *traceInfo
if EnableTracing {
trInfo = &traceInfo{
tr: trace.New("grpc.Sent."+methodFamily(method), method),
firstLine: firstLine{
client: true,
},
}
if deadline, ok := ctx.Deadline(); ok {
trInfo.firstLine.deadline = time.Until(deadline)
}
trInfo.tr.LazyLog(&trInfo.firstLine, false)
ctx = trace.NewContext(ctx, trInfo.tr)
}
newAttempt := &csAttempt{ newAttempt := &csAttempt{
ctx: ctx,
beginTime: beginTime,
cs: cs, cs: cs,
dc: cs.cc.dopts.dc, dc: cs.cc.dopts.dc,
statsHandler: sh, statsHandler: sh,
@ -389,15 +394,14 @@ func (cs *clientStream) newAttemptLocked(sh stats.Handler, trInfo *traceInfo) (r
} }
}() }()
if err := cs.ctx.Err(); err != nil { if err := ctx.Err(); err != nil {
return toRPCErr(err) return toRPCErr(err)
} }
ctx := cs.ctx
if cs.cc.parsedTarget.Scheme == "xds" { if cs.cc.parsedTarget.Scheme == "xds" {
// Add extra metadata (metadata that will be added by transport) to context // Add extra metadata (metadata that will be added by transport) to context
// so the balancer can see them. // so the balancer can see them.
ctx = grpcutil.WithExtraMetadata(cs.ctx, metadata.Pairs( ctx = grpcutil.WithExtraMetadata(ctx, metadata.Pairs(
"content-type", grpcutil.ContentType(cs.callHdr.ContentSubtype), "content-type", grpcutil.ContentType(cs.callHdr.ContentSubtype),
)) ))
} }
@ -417,15 +421,12 @@ func (cs *clientStream) newAttemptLocked(sh stats.Handler, trInfo *traceInfo) (r
func (a *csAttempt) newStream() error { func (a *csAttempt) newStream() error {
cs := a.cs cs := a.cs
cs.callHdr.PreviousAttempts = cs.numRetries cs.callHdr.PreviousAttempts = cs.numRetries
s, err := a.t.NewStream(cs.ctx, cs.callHdr) s, err := a.t.NewStream(a.ctx, cs.callHdr)
if err != nil { if err != nil {
if _, ok := err.(transport.PerformedIOError); ok {
// Return without converting to an RPC error so retry code can // Return without converting to an RPC error so retry code can
// inspect. // inspect.
return err return err
} }
return toRPCErr(err)
}
cs.attempt.s = s cs.attempt.s = s
cs.attempt.p = &parser{r: s} cs.attempt.p = &parser{r: s}
return nil return nil
@ -446,7 +447,6 @@ type clientStream struct {
cancel context.CancelFunc // cancels all attempts cancel context.CancelFunc // cancels all attempts
sentLast bool // sent an end stream sentLast bool // sent an end stream
beginTime time.Time
methodConfig *MethodConfig methodConfig *MethodConfig
@ -486,6 +486,7 @@ type clientStream struct {
// csAttempt implements a single transport stream attempt within a // csAttempt implements a single transport stream attempt within a
// clientStream. // clientStream.
type csAttempt struct { type csAttempt struct {
ctx context.Context
cs *clientStream cs *clientStream
t transport.ClientTransport t transport.ClientTransport
s *transport.Stream s *transport.Stream
@ -504,6 +505,7 @@ type csAttempt struct {
trInfo *traceInfo trInfo *traceInfo
statsHandler stats.Handler statsHandler stats.Handler
beginTime time.Time
} }
func (cs *clientStream) commitAttemptLocked() { func (cs *clientStream) commitAttemptLocked() {
@ -521,46 +523,57 @@ func (cs *clientStream) commitAttempt() {
} }
// shouldRetry returns nil if the RPC should be retried; otherwise it returns // shouldRetry returns nil if the RPC should be retried; otherwise it returns
// the error that should be returned by the operation. // the error that should be returned by the operation. If the RPC should be
func (cs *clientStream) shouldRetry(err error) error { // retried, the bool indicates whether it is being retried transparently.
unprocessed := false func (cs *clientStream) shouldRetry(err error) (bool, error) {
if cs.attempt.s == nil { if cs.attempt.s == nil {
pioErr, ok := err.(transport.PerformedIOError) // Error from NewClientStream.
if ok { nse, ok := err.(*transport.NewStreamError)
// Unwrap error. if !ok {
err = toRPCErr(pioErr.Err) // Unexpected, but assume no I/O was performed and the RPC is not
} else { // fatal, so retry indefinitely.
unprocessed = true return true, nil
} }
if !ok && !cs.callInfo.failFast {
// In the event of a non-IO operation error from NewStream, we // Unwrap and convert error.
// never attempted to write anything to the wire, so we can retry err = toRPCErr(nse.Err)
// indefinitely for non-fail-fast RPCs.
return nil // Never retry DoNotRetry errors, which indicate the RPC should not be
// retried due to max header list size violation, etc.
if nse.DoNotRetry {
return false, err
}
// In the event of a non-IO operation error from NewStream, we never
// attempted to write anything to the wire, so we can retry
// indefinitely.
if !nse.DoNotTransparentRetry {
return true, nil
} }
} }
if cs.finished || cs.committed { if cs.finished || cs.committed {
// RPC is finished or committed; cannot retry. // RPC is finished or committed; cannot retry.
return err return false, err
} }
// Wait for the trailers. // Wait for the trailers.
unprocessed := false
if cs.attempt.s != nil { if cs.attempt.s != nil {
<-cs.attempt.s.Done() <-cs.attempt.s.Done()
unprocessed = cs.attempt.s.Unprocessed() unprocessed = cs.attempt.s.Unprocessed()
} }
if cs.firstAttempt && unprocessed { if cs.firstAttempt && unprocessed {
// First attempt, stream unprocessed: transparently retry. // First attempt, stream unprocessed: transparently retry.
return nil return true, nil
} }
if cs.cc.dopts.disableRetry { if cs.cc.dopts.disableRetry {
return err return false, err
} }
pushback := 0 pushback := 0
hasPushback := false hasPushback := false
if cs.attempt.s != nil { if cs.attempt.s != nil {
if !cs.attempt.s.TrailersOnly() { if !cs.attempt.s.TrailersOnly() {
return err return false, err
} }
// TODO(retry): Move down if the spec changes to not check server pushback // TODO(retry): Move down if the spec changes to not check server pushback
@ -571,13 +584,13 @@ func (cs *clientStream) shouldRetry(err error) error {
if pushback, e = strconv.Atoi(sps[0]); e != nil || pushback < 0 { if pushback, e = strconv.Atoi(sps[0]); e != nil || pushback < 0 {
channelz.Infof(logger, cs.cc.channelzID, "Server retry pushback specified to abort (%q).", sps[0]) channelz.Infof(logger, cs.cc.channelzID, "Server retry pushback specified to abort (%q).", sps[0])
cs.retryThrottler.throttle() // This counts as a failure for throttling. cs.retryThrottler.throttle() // This counts as a failure for throttling.
return err return false, err
} }
hasPushback = true hasPushback = true
} else if len(sps) > 1 { } else if len(sps) > 1 {
channelz.Warningf(logger, cs.cc.channelzID, "Server retry pushback specified multiple values (%q); not retrying.", sps) channelz.Warningf(logger, cs.cc.channelzID, "Server retry pushback specified multiple values (%q); not retrying.", sps)
cs.retryThrottler.throttle() // This counts as a failure for throttling. cs.retryThrottler.throttle() // This counts as a failure for throttling.
return err return false, err
} }
} }
@ -590,16 +603,16 @@ func (cs *clientStream) shouldRetry(err error) error {
rp := cs.methodConfig.RetryPolicy rp := cs.methodConfig.RetryPolicy
if rp == nil || !rp.RetryableStatusCodes[code] { if rp == nil || !rp.RetryableStatusCodes[code] {
return err return false, err
} }
// Note: the ordering here is important; we count this as a failure // Note: the ordering here is important; we count this as a failure
// only if the code matched a retryable code. // only if the code matched a retryable code.
if cs.retryThrottler.throttle() { if cs.retryThrottler.throttle() {
return err return false, err
} }
if cs.numRetries+1 >= rp.MaxAttempts { if cs.numRetries+1 >= rp.MaxAttempts {
return err return false, err
} }
var dur time.Duration var dur time.Duration
@ -622,23 +635,24 @@ func (cs *clientStream) shouldRetry(err error) error {
select { select {
case <-t.C: case <-t.C:
cs.numRetries++ cs.numRetries++
return nil return false, nil
case <-cs.ctx.Done(): case <-cs.ctx.Done():
t.Stop() t.Stop()
return status.FromContextError(cs.ctx.Err()).Err() return false, status.FromContextError(cs.ctx.Err()).Err()
} }
} }
// Returns nil if a retry was performed and succeeded; error otherwise. // Returns nil if a retry was performed and succeeded; error otherwise.
func (cs *clientStream) retryLocked(lastErr error) error { func (cs *clientStream) retryLocked(lastErr error) error {
for { for {
cs.attempt.finish(lastErr) cs.attempt.finish(toRPCErr(lastErr))
if err := cs.shouldRetry(lastErr); err != nil { isTransparent, err := cs.shouldRetry(lastErr)
if err != nil {
cs.commitAttemptLocked() cs.commitAttemptLocked()
return err return err
} }
cs.firstAttempt = false cs.firstAttempt = false
if err := cs.newAttemptLocked(nil, nil); err != nil { if err := cs.newAttemptLocked(isTransparent); err != nil {
return err return err
} }
if lastErr = cs.replayBufferLocked(); lastErr == nil { if lastErr = cs.replayBufferLocked(); lastErr == nil {
@ -659,7 +673,11 @@ func (cs *clientStream) withRetry(op func(a *csAttempt) error, onSuccess func())
for { for {
if cs.committed { if cs.committed {
cs.mu.Unlock() cs.mu.Unlock()
return op(cs.attempt) // toRPCErr is used in case the error from the attempt comes from
// NewClientStream, which intentionally doesn't return a status
// error to allow for further inspection; all other errors should
// already be status errors.
return toRPCErr(op(cs.attempt))
} }
a := cs.attempt a := cs.attempt
cs.mu.Unlock() cs.mu.Unlock()
@ -924,7 +942,7 @@ func (a *csAttempt) sendMsg(m interface{}, hdr, payld, data []byte) error {
return io.EOF return io.EOF
} }
if a.statsHandler != nil { if a.statsHandler != nil {
a.statsHandler.HandleRPC(cs.ctx, outPayload(true, m, data, payld, time.Now())) a.statsHandler.HandleRPC(a.ctx, outPayload(true, m, data, payld, time.Now()))
} }
if channelz.IsOn() { if channelz.IsOn() {
a.t.IncrMsgSent() a.t.IncrMsgSent()
@ -972,7 +990,7 @@ func (a *csAttempt) recvMsg(m interface{}, payInfo *payloadInfo) (err error) {
a.mu.Unlock() a.mu.Unlock()
} }
if a.statsHandler != nil { if a.statsHandler != nil {
a.statsHandler.HandleRPC(cs.ctx, &stats.InPayload{ a.statsHandler.HandleRPC(a.ctx, &stats.InPayload{
Client: true, Client: true,
RecvTime: time.Now(), RecvTime: time.Now(),
Payload: m, Payload: m,
@ -1034,12 +1052,12 @@ func (a *csAttempt) finish(err error) {
if a.statsHandler != nil { if a.statsHandler != nil {
end := &stats.End{ end := &stats.End{
Client: true, Client: true,
BeginTime: a.cs.beginTime, BeginTime: a.beginTime,
EndTime: time.Now(), EndTime: time.Now(),
Trailer: tr, Trailer: tr,
Error: err, Error: err,
} }
a.statsHandler.HandleRPC(a.cs.ctx, end) a.statsHandler.HandleRPC(a.ctx, end)
} }
if a.trInfo != nil && a.trInfo.tr != nil { if a.trInfo != nil && a.trInfo.tr != nil {
if err == nil { if err == nil {

View File

@ -19,4 +19,4 @@
package grpc package grpc
// Version is the current grpc version. // Version is the current grpc version.
const Version = "1.38.0" const Version = "1.44.1-dev"

16
vendor/google.golang.org/grpc/vet.sh generated vendored
View File

@ -32,8 +32,6 @@ PATH="${HOME}/go/bin:${GOROOT}/bin:${PATH}"
go version go version
if [[ "$1" = "-install" ]]; then if [[ "$1" = "-install" ]]; then
# Check for module support
if go help mod >& /dev/null; then
# Install the pinned versions as defined in module tools. # Install the pinned versions as defined in module tools.
pushd ./test/tools pushd ./test/tools
go install \ go install \
@ -42,16 +40,6 @@ if [[ "$1" = "-install" ]]; then
honnef.co/go/tools/cmd/staticcheck \ honnef.co/go/tools/cmd/staticcheck \
github.com/client9/misspell/cmd/misspell github.com/client9/misspell/cmd/misspell
popd popd
else
# Ye olde `go get` incantation.
# Note: this gets the latest version of all tools (vs. the pinned versions
# with Go modules).
go get -u \
golang.org/x/lint/golint \
golang.org/x/tools/cmd/goimports \
honnef.co/go/tools/cmd/staticcheck \
github.com/client9/misspell/cmd/misspell
fi
if [[ -z "${VET_SKIP_PROTO}" ]]; then if [[ -z "${VET_SKIP_PROTO}" ]]; then
if [[ "${TRAVIS}" = "true" ]]; then if [[ "${TRAVIS}" = "true" ]]; then
PROTOBUF_VERSION=3.14.0 PROTOBUF_VERSION=3.14.0
@ -101,10 +89,6 @@ not git grep "\(import \|^\s*\)\"github.com/golang/protobuf/ptypes/" -- "*.go"
# - Ensure all xds proto imports are renamed to *pb or *grpc. # - Ensure all xds proto imports are renamed to *pb or *grpc.
git grep '"github.com/envoyproxy/go-control-plane/envoy' -- '*.go' ':(exclude)*.pb.go' | not grep -v 'pb "\|grpc "' git grep '"github.com/envoyproxy/go-control-plane/envoy' -- '*.go' ':(exclude)*.pb.go' | not grep -v 'pb "\|grpc "'
# - Check imports that are illegal in appengine (until Go 1.11).
# TODO: Remove when we drop Go 1.10 support
go list -f {{.Dir}} ./... | xargs go run test/go_vet/vet.go
misspell -error . misspell -error .
# - Check that generated proto files are up to date. # - Check that generated proto files are up to date.

3
vendor/modules.txt vendored
View File

@ -271,7 +271,7 @@ golang.org/x/xerrors
golang.org/x/xerrors/internal golang.org/x/xerrors/internal
# google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a # google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a
google.golang.org/genproto/googleapis/rpc/status google.golang.org/genproto/googleapis/rpc/status
# google.golang.org/grpc v1.38.0 # google.golang.org/grpc v1.44.0
## explicit ## explicit
google.golang.org/grpc google.golang.org/grpc
google.golang.org/grpc/attributes google.golang.org/grpc/attributes
@ -284,6 +284,7 @@ google.golang.org/grpc/binarylog/grpc_binarylog_v1
google.golang.org/grpc/codes google.golang.org/grpc/codes
google.golang.org/grpc/connectivity google.golang.org/grpc/connectivity
google.golang.org/grpc/credentials google.golang.org/grpc/credentials
google.golang.org/grpc/credentials/insecure
google.golang.org/grpc/encoding google.golang.org/grpc/encoding
google.golang.org/grpc/encoding/proto google.golang.org/grpc/encoding/proto
google.golang.org/grpc/grpclog google.golang.org/grpc/grpclog