mirror of https://github.com/docker/cli.git
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:
parent
0e3c9b282e
commit
b2cef834fb
|
@ -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
|
||||||
)
|
)
|
||||||
|
|
19
vendor.sum
19
vendor.sum
|
@ -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=
|
||||||
|
|
|
@ -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
|
|
|
@ -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
|
||||||
|
|
|
@ -41,8 +41,6 @@ vetdeps:
|
||||||
clean \
|
clean \
|
||||||
proto \
|
proto \
|
||||||
test \
|
test \
|
||||||
testappengine \
|
|
||||||
testappenginedeps \
|
|
||||||
testrace \
|
testrace \
|
||||||
vet \
|
vet \
|
||||||
vetdeps
|
vetdeps
|
||||||
|
|
|
@ -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.
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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.
|
||||||
//
|
//
|
||||||
|
|
|
@ -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"
|
|
||||||
}
|
|
|
@ -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"
|
||||||
|
}
|
|
@ -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",
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
)
|
)
|
||||||
|
|
|
@ -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=
|
||||||
|
|
|
@ -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{})
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"
|
|
|
@ -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{}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
// +build !appengine
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
*
|
*
|
||||||
* Copyright 2018 gRPC authors.
|
* Copyright 2018 gRPC authors.
|
||||||
|
|
|
@ -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")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
// +build linux,!appengine
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
*
|
*
|
||||||
* Copyright 2018 gRPC authors.
|
* Copyright 2018 gRPC authors.
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
// +build !linux appengine
|
//go:build !linux
|
||||||
|
// +build !linux
|
||||||
|
|
||||||
/*
|
/*
|
||||||
*
|
*
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
// +build !appengine
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
*
|
*
|
||||||
* Copyright 2020 gRPC authors.
|
* Copyright 2020 gRPC authors.
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -1,5 +1,3 @@
|
||||||
// +build !appengine
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
*
|
*
|
||||||
* Copyright 2018 gRPC authors.
|
* Copyright 2018 gRPC authors.
|
||||||
|
|
|
@ -18,7 +18,9 @@
|
||||||
|
|
||||||
package credentials
|
package credentials
|
||||||
|
|
||||||
import "crypto/tls"
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
)
|
||||||
|
|
||||||
const alpnProtoStrH2 = "h2"
|
const alpnProtoStrH2 = "h2"
|
||||||
|
|
||||||
|
|
|
@ -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")
|
||||||
)
|
)
|
||||||
|
|
|
@ -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)
|
||||||
|
)
|
|
@ -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{})
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -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)
|
||||||
}
|
}
|
|
@ -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
|
|
||||||
}
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
// +build !appengine
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
*
|
*
|
||||||
* Copyright 2018 gRPC authors.
|
* Copyright 2018 gRPC authors.
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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())},
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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) }
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)))
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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())
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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:
|
}
|
||||||
|
if errors.Is(err, context.DeadlineExceeded) {
|
||||||
return New(codes.DeadlineExceeded, err.Error())
|
return New(codes.DeadlineExceeded, err.Error())
|
||||||
case context.Canceled:
|
}
|
||||||
|
if errors.Is(err, context.Canceled) {
|
||||||
return New(codes.Canceled, err.Error())
|
return New(codes.Canceled, err.Error())
|
||||||
default:
|
}
|
||||||
return New(codes.Unknown, err.Error())
|
return New(codes.Unknown, err.Error())
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue