Revert "Bump google.golang.org/grpc from 1.36.1 to 1.44.0" (#5981)

Reverts letsencrypt/boulder#5963

Turns out the tests are still flaky -- using the `grpc.WaitForReady(true)`
connection option results in sometimes seeing 9 entries added to the
purger queue, and sometimes 10 entries. Reverting because flakiness
on main should not be tolerated.
This commit is contained in:
Aaron Gable 2022-03-08 10:32:30 -08:00 committed by GitHub
parent 2ec03b377b
commit 32973392de
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
77 changed files with 1586 additions and 2574 deletions

2
go.mod
View File

@ -27,7 +27,7 @@ require (
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2
golang.org/x/net v0.0.0-20211029224645-99673261e6eb golang.org/x/net v0.0.0-20211029224645-99673261e6eb
golang.org/x/text v0.3.6 golang.org/x/text v0.3.6
google.golang.org/grpc v1.44.0 google.golang.org/grpc v1.36.1
google.golang.org/protobuf v1.27.1 google.golang.org/protobuf v1.27.1
gopkg.in/square/go-jose.v2 v2.4.1 gopkg.in/square/go-jose.v2 v2.4.1
gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v2 v2.4.0

23
go.sum
View File

@ -1,5 +1,4 @@
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/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/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ= github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ=
@ -10,7 +9,6 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy
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-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
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/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
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/beeker1121/goque v1.0.3-0.20191103205551-d618510128af h1:XbgLdZvVbWsK9HAhAYOp6rksTAdOVYDBQtGSVOLlJrw= github.com/beeker1121/goque v1.0.3-0.20191103205551-d618510128af h1:XbgLdZvVbWsK9HAhAYOp6rksTAdOVYDBQtGSVOLlJrw=
github.com/beeker1121/goque v1.0.3-0.20191103205551-d618510128af/go.mod h1:84CWnaDz4g1tEVnFLnuBigmGK15oPohy0RfvSN8d4eg= github.com/beeker1121/goque v1.0.3-0.20191103205551-d618510128af/go.mod h1:84CWnaDz4g1tEVnFLnuBigmGK15oPohy0RfvSN8d4eg=
@ -25,12 +23,7 @@ github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2/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-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/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
@ -52,9 +45,7 @@ github.com/eggsampler/acme/v3 v3.2.1 h1:Lfsrg3M2zt00QRnizOFzdpSfsS9oDvPsGrodXS/w
github.com/eggsampler/acme/v3 v3.2.1/go.mod h1:/qh0rKC/Dh7Jj+p4So7DbWmFNzC4dpcpK53r226Fhuo= github.com/eggsampler/acme/v3 v3.2.1/go.mod h1:/qh0rKC/Dh7Jj+p4So7DbWmFNzC4dpcpK53r226Fhuo=
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.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.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/facebookgo/clock v0.0.0-20150410010913-600d898af40a h1:yDWHCSQ40h88yih2JAcL6Ls/kVkSE8GFACTGVnMPruw= github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a h1:yDWHCSQ40h88yih2JAcL6Ls/kVkSE8GFACTGVnMPruw=
github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a/go.mod h1:7Ga40egUymuWXxAe151lTNnCv97MddSOVsjpPPkityA= github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a/go.mod h1:7Ga40egUymuWXxAe151lTNnCv97MddSOVsjpPPkityA=
@ -139,7 +130,6 @@ github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:W
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/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
@ -165,7 +155,6 @@ github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmg
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho=
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.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/honeycombio/beeline-go v1.1.1 h1:sU8r4ae34uEL3/CguSl8Mr+Asz9DL1nfH9Wwk85Pc7U= github.com/honeycombio/beeline-go v1.1.1 h1:sU8r4ae34uEL3/CguSl8Mr+Asz9DL1nfH9Wwk85Pc7U=
github.com/honeycombio/beeline-go v1.1.1/go.mod h1:kN0cfUGBMfA87DyCYbiiLoSzWsnw3bluZvNEWtatHxk= github.com/honeycombio/beeline-go v1.1.1/go.mod h1:kN0cfUGBMfA87DyCYbiiLoSzWsnw3bluZvNEWtatHxk=
@ -329,7 +318,6 @@ github.com/prometheus/procfs v0.1.3 h1:F0+tqvhOksq22sc6iCHF5WGlWjdwj92p0udFh1VFB
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
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.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.3.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.5.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.5.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
@ -408,7 +396,6 @@ go.opentelemetry.io/otel/oteltest v0.19.0 h1:YVfA0ByROYqTwOxqHVZYZExzEpfZor+MU1r
go.opentelemetry.io/otel/oteltest v0.19.0/go.mod h1:tI4yxwh8U21v7JD6R3BcA/2+RBoTKFexE/PJ/nSO7IA= go.opentelemetry.io/otel/oteltest v0.19.0/go.mod h1:tI4yxwh8U21v7JD6R3BcA/2+RBoTKFexE/PJ/nSO7IA=
go.opentelemetry.io/otel/trace v0.19.0 h1:1ucYlenXIDA1OlHVLDZKX0ObXV5RLaq06DtUKz5e5zc= go.opentelemetry.io/otel/trace v0.19.0 h1:1ucYlenXIDA1OlHVLDZKX0ObXV5RLaq06DtUKz5e5zc=
go.opentelemetry.io/otel/trace v0.19.0/go.mod h1:4IXiNextNOpPnRlI4ryK69mn5iC84bjBWZQA5DXz/qg= go.opentelemetry.io/otel/trace v0.19.0/go.mod h1:4IXiNextNOpPnRlI4ryK69mn5iC84bjBWZQA5DXz/qg=
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/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
@ -441,7 +428,6 @@ golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73r
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/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/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
@ -455,7 +441,6 @@ 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-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
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=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
@ -465,7 +450,6 @@ golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qx
golang.org/x/net v0.0.0-20211029224645-99673261e6eb h1:pirldcYWx7rx7kE5r+9WsOXPXK0+WH5+uZ7uPmJ44uM= golang.org/x/net v0.0.0-20211029224645-99673261e6eb h1:pirldcYWx7rx7kE5r+9WsOXPXK0+WH5+uZ7uPmJ44uM=
golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
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/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-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -543,7 +527,6 @@ google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpC
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
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=
@ -551,11 +534,8 @@ google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ij
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.1 h1:cmUfbeGKnz9+2DD/UYsMQXeqbHZqZDs4eQwW0sFOpBY=
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
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=
@ -589,7 +569,6 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWD
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

View File

@ -5,6 +5,7 @@ package integration
import ( import (
"bytes" "bytes"
"context" "context"
"fmt"
"os" "os"
"os/exec" "os/exec"
"strings" "strings"
@ -16,6 +17,7 @@ import (
"github.com/letsencrypt/boulder/cmd" "github.com/letsencrypt/boulder/cmd"
bcreds "github.com/letsencrypt/boulder/grpc/creds" bcreds "github.com/letsencrypt/boulder/grpc/creds"
"google.golang.org/grpc" "google.golang.org/grpc"
"google.golang.org/grpc/connectivity"
) )
func setup() (*exec.Cmd, *bytes.Buffer, akamaipb.AkamaiPurgerClient, error) { func setup() (*exec.Cmd, *bytes.Buffer, akamaipb.AkamaiPurgerClient, error) {
@ -47,12 +49,21 @@ func setup() (*exec.Cmd, *bytes.Buffer, akamaipb.AkamaiPurgerClient, error) {
conn, err := grpc.Dial( conn, err := grpc.Dial(
"dns:///akamai-purger.boulder:9199", "dns:///akamai-purger.boulder:9199",
grpc.WithTransportCredentials(creds), grpc.WithTransportCredentials(creds),
grpc.WithDefaultCallOptions(grpc.WaitForReady(true)),
) )
if err != nil { if err != nil {
sigterm() sigterm()
return nil, nil, nil, err return nil, nil, nil, err
} }
for i := 0; ; i++ {
if conn.GetState() == connectivity.Ready {
break
}
if i > 40 {
sigterm()
return nil, nil, nil, fmt.Errorf("timed out waiting for akamai-purger to come up")
}
time.Sleep(50 * time.Millisecond)
}
purgerClient := akamaipb.NewAkamaiPurgerClient(conn) purgerClient := akamaipb.NewAkamaiPurgerClient(conn)
return purgerCmd, &outputBuffer, purgerClient, nil return purgerCmd, &outputBuffer, purgerClient, nil
} }

42
vendor/google.golang.org/grpc/.travis.yml generated vendored Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -75,26 +75,24 @@ func Get(name string) Builder {
return nil return nil
} }
// A SubConn represents a single connection to a gRPC backend service. // SubConn represents a gRPC sub connection.
// 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.
// //
// Each SubConn contains a list of addresses. // The reconnect backoff will be applied on the list, not a single address.
// For example, try_on_all_addresses -> backoff -> try_on_all_addresses.
// //
// All SubConns start in IDLE, and will not try to connect. To trigger the // All SubConns start in IDLE, and will not try to connect. To trigger
// connecting, Balancers must call Connect. If a connection re-enters IDLE, // the connecting, Balancers must call Connect.
// Balancers must call Connect again to trigger a new connection attempt. // When the connection encounters an error, it will reconnect immediately.
// When the connection becomes IDLE, it will not reconnect unless Connect is
// called.
// //
// gRPC will try to connect to the addresses in sequence, and stop trying the // This interface is to be implemented by gRPC. Users should not need a
// remainder once the first connection is successful. If an attempt to connect // brand new implementation of this interface. For the situations like
// to all addresses encounters an error, the SubConn will enter // testing, the new implementation should embed this interface. This allows
// TRANSIENT_FAILURE for a backoff period, and then transition to IDLE. // gRPC to add new methods to this interface.
//
// 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.
@ -103,9 +101,6 @@ type SubConn interface {
// a new connection will be created. // a new connection will be created.
// //
// This will trigger a state transition for the SubConn. // This will trigger a state transition for the SubConn.
//
// Deprecated: This method is now part of the ClientConn interface and will
// eventually be removed from here.
UpdateAddresses([]resolver.Address) UpdateAddresses([]resolver.Address)
// Connect starts the connecting for this SubConn. // Connect starts the connecting for this SubConn.
Connect() Connect()
@ -148,13 +143,6 @@ type ClientConn interface {
// RemoveSubConn removes the SubConn from ClientConn. // RemoveSubConn removes the SubConn from ClientConn.
// The SubConn will be shutdown. // The SubConn will be shutdown.
RemoveSubConn(SubConn) RemoveSubConn(SubConn)
// UpdateAddresses updates the addresses used in the passed in SubConn.
// gRPC checks if the currently connected address is still in the new list.
// If so, the connection will be kept. Else, the connection will be
// gracefully closed, and a new connection will be created.
//
// This will trigger a state transition for the SubConn.
UpdateAddresses(SubConn, []resolver.Address)
// UpdateState notifies gRPC that the balancer's internal state has // UpdateState notifies gRPC that the balancer's internal state has
// changed. // changed.
@ -174,32 +162,25 @@ 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 credentials to use when communicating with a // DialCreds is the transport credential the Balancer implementation can
// remote load balancer server. Balancer implementations which do not // use to dial to a remote load balancer server. The Balancer implementations
// communicate with a remote load balancer server can ignore this field. // can ignore this if it does not need to talk to another party securely.
DialCreds credentials.TransportCredentials DialCreds credentials.TransportCredentials
// CredsBundle is the credentials bundle to use when communicating with a // CredsBundle is the credentials bundle that the Balancer can use.
// 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 to use when communicating with a remote load // Dialer is the custom dialer the Balancer implementation can use to dial
// balancer server. Balancer implementations which do not communicate with a // to a remote load balancer server. The Balancer implementations
// remote load balancer server can ignore this field. // can ignore this if it doesn't need to talk to remote balancer.
Dialer func(context.Context, string) (net.Conn, error) Dialer func(context.Context, string) (net.Conn, error)
// Authority is the server name to use as part of the authentication // ChannelzParentID is the entity parent's channelz unique identification number.
// 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 // Target contains the parsed address info of the dial target. It is the same resolver.Target as
// same resolver.Target as passed to the resolver. See the documentation for // passed to the resolver.
// the resolver.Target type for details about what it contains. // See the documentation for the resolver.Target type for details about what it contains.
Target resolver.Target Target resolver.Target
} }
@ -335,20 +316,6 @@ 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.
@ -378,8 +345,6 @@ 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
@ -387,11 +352,9 @@ 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 if at least one SubConn is TransientFailure, the aggregated state is Transient Failure; // - Else the aggregated state is TransientFailure.
// - 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
// //
// Shutdown is not considered. // Idle and Shutdown are 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} {
@ -401,10 +364,6 @@ 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
} }
} }
@ -415,11 +374,5 @@ func (cse *ConnectivityStateEvaluator) RecordTransition(oldState, newState conne
if cse.numConnecting > 0 { if cse.numConnecting > 0 {
return connectivity.Connecting return connectivity.Connecting
} }
if cse.numTransientFailure > 0 {
return connectivity.TransientFailure
}
if cse.numIdle > 0 {
return connectivity.Idle
}
return connectivity.TransientFailure return connectivity.TransientFailure
} }

View File

@ -41,7 +41,7 @@ func (bb *baseBuilder) Build(cc balancer.ClientConn, opt balancer.BuildOptions)
cc: cc, cc: cc,
pickerBuilder: bb.pickerBuilder, pickerBuilder: bb.pickerBuilder,
subConns: resolver.NewAddressMap(), subConns: make(map[resolver.Address]balancer.SubConn),
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,
@ -64,7 +64,7 @@ type baseBalancer struct {
csEvltr *balancer.ConnectivityStateEvaluator csEvltr *balancer.ConnectivityStateEvaluator
state connectivity.State state connectivity.State
subConns *resolver.AddressMap subConns map[resolver.Address]balancer.SubConn // `attributes` is stripped from the keys of this map (the addresses)
scStates map[balancer.SubConn]connectivity.State scStates map[balancer.SubConn]connectivity.State
picker balancer.Picker picker balancer.Picker
config Config config Config
@ -75,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 b.subConns.Len() == 0 { if len(b.subConns) == 0 {
b.state = connectivity.TransientFailure b.state = connectivity.TransientFailure
} }
@ -99,29 +99,50 @@ 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 := resolver.NewAddressMap() addrsSet := make(map[resolver.Address]struct{})
for _, a := range s.ResolverState.Addresses { for _, a := range s.ResolverState.Addresses {
addrsSet.Set(a, nil) // Strip attributes from addresses before using them as map keys. So
if _, ok := b.subConns.Get(a); !ok { // that when two addresses only differ in attributes pointers (but with
// 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 sc, 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.Set(a, sc) b.subConns[aNoAttrs] = 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).
sc.UpdateAddresses([]resolver.Address{a})
} }
} }
for _, a := range b.subConns.Keys() { for a, sc := range b.subConns {
sci, _ := b.subConns.Get(a)
sc := sci.(balancer.SubConn)
// a was removed by resolver. // a was removed by resolver.
if _, ok := addrsSet.Get(a); !ok { if _, ok := addrsSet[a]; !ok {
b.cc.RemoveSubConn(sc) b.cc.RemoveSubConn(sc)
b.subConns.Delete(a) delete(b.subConns, 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.
} }
@ -163,9 +184,7 @@ 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 := range b.subConns.Keys() { for addr, sc := range b.subConns {
sci, _ := b.subConns.Get(addr)
sc := sci.(balancer.SubConn)
if st, ok := b.scStates[sc]; ok && st == connectivity.Ready { if st, ok := b.scStates[sc]; ok && st == connectivity.Ready {
readySCs[sc] = SubConnInfo{Address: addr} readySCs[sc] = SubConnInfo{Address: addr}
} }
@ -185,14 +204,10 @@ func (b *baseBalancer) UpdateSubConnState(sc balancer.SubConn, state balancer.Su
} }
return return
} }
if oldS == connectivity.TransientFailure && if oldS == connectivity.TransientFailure && s == connectivity.Connecting {
(s == connectivity.Connecting || s == connectivity.Idle) { // Once a subconn enters TRANSIENT_FAILURE, ignore subsequent
// 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
@ -218,6 +233,7 @@ 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})
} }
@ -226,11 +242,6 @@ func (b *baseBalancer) UpdateSubConnState(sc balancer.SubConn, state balancer.Su
func (b *baseBalancer) Close() { func (b *baseBalancer) Close() {
} }
// ExitIdle is a nop because the base balancer attempts to stay connected to
// all SubConns at all times.
func (b *baseBalancer) ExitIdle() {
}
// NewErrPicker returns a Picker that always returns err on Pick(). // NewErrPicker returns a Picker that always returns err on Pick().
func NewErrPicker(err error) balancer.Picker { func NewErrPicker(err error) balancer.Picker {
return &errPicker{err: err} return &errPicker{err: err}

View File

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

View File

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

View File

@ -37,19 +37,13 @@ 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
hasExitIdle bool scBuffer *buffer.Unbounded
updateCh *buffer.Unbounded
closed *grpcsync.Event
done *grpcsync.Event done *grpcsync.Event
mu sync.Mutex mu sync.Mutex
@ -59,14 +53,12 @@ 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,
updateCh: buffer.NewUnbounded(), scBuffer: 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
} }
@ -75,72 +67,35 @@ 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.updateCh.Get(): case t := <-ccb.scBuffer.Get():
ccb.updateCh.Load() ccb.scBuffer.Load()
if ccb.closed.HasFired() { if ccb.done.HasFired() {
break break
} }
switch u := t.(type) {
case *scStateUpdate:
ccb.balancerMu.Lock() ccb.balancerMu.Lock()
ccb.balancer.UpdateSubConnState(u.sc, balancer.SubConnState{ConnectivityState: u.state, ConnectionError: u.err}) su := t.(*scStateUpdate)
ccb.balancer.UpdateSubConnState(su.sc, balancer.SubConnState{ConnectivityState: su.state, ConnectionError: su.err})
ccb.balancerMu.Unlock() ccb.balancerMu.Unlock()
case *acBalancerWrapper: case <-ccb.done.Done():
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.closed.HasFired() { if ccb.done.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.closed.Fire() ccb.done.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) {
@ -154,7 +109,7 @@ func (ccb *ccBalancerWrapper) handleSubConnStateChange(sc balancer.SubConn, s co
if sc == nil { if sc == nil {
return return
} }
ccb.updateCh.Put(&scStateUpdate{ ccb.scBuffer.Put(&scStateUpdate{
sc: sc, sc: sc,
state: s, state: s,
err: err, err: err,
@ -169,8 +124,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) {
@ -195,18 +150,17 @@ func (ccb *ccBalancerWrapper) NewSubConn(addrs []resolver.Address, opts balancer
} }
func (ccb *ccBalancerWrapper) RemoveSubConn(sc balancer.SubConn) { func (ccb *ccBalancerWrapper) RemoveSubConn(sc balancer.SubConn) {
// The RemoveSubConn() is handled in the run() goroutine, to avoid deadlock
// during switchBalancer() if the old balancer calls RemoveSubConn() in its
// Close().
ccb.updateCh.Put(sc)
}
func (ccb *ccBalancerWrapper) UpdateAddresses(sc balancer.SubConn, addrs []resolver.Address) {
acbw, ok := sc.(*acBalancerWrapper) acbw, ok := sc.(*acBalancerWrapper)
if !ok { if !ok {
return return
} }
acbw.UpdateAddresses(addrs) 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) UpdateState(s balancer.State) { func (ccb *ccBalancerWrapper) UpdateState(s balancer.State) {
@ -243,7 +197,7 @@ func (acbw *acBalancerWrapper) UpdateAddresses(addrs []resolver.Address) {
acbw.mu.Lock() acbw.mu.Lock()
defer acbw.mu.Unlock() defer acbw.mu.Unlock()
if len(addrs) <= 0 { if len(addrs) <= 0 {
acbw.ac.cc.removeAddrConn(acbw.ac, errConnDrain) acbw.ac.tearDown(errConnDrain)
return return
} }
if !acbw.ac.tryUpdateAddrs(addrs) { if !acbw.ac.tryUpdateAddrs(addrs) {
@ -258,23 +212,23 @@ func (acbw *acBalancerWrapper) UpdateAddresses(addrs []resolver.Address) {
acbw.ac.acbw = nil acbw.ac.acbw = nil
acbw.ac.mu.Unlock() acbw.ac.mu.Unlock()
acState := acbw.ac.getState() acState := acbw.ac.getState()
acbw.ac.cc.removeAddrConn(acbw.ac, errConnDrain) acbw.ac.tearDown(errConnDrain)
if acState == connectivity.Shutdown { if acState == connectivity.Shutdown {
return return
} }
newAC, err := cc.newAddrConn(addrs, opts) ac, 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 = newAC acbw.ac = ac
newAC.mu.Lock() ac.mu.Lock()
newAC.acbw = acbw ac.acbw = acbw
newAC.mu.Unlock() ac.mu.Unlock()
if acState != connectivity.Idle { if acState != connectivity.Idle {
go newAC.connect() ac.connect()
} }
} }
} }
@ -282,7 +236,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()
go acbw.ac.connect() acbw.ac.connect()
} }
func (acbw *acBalancerWrapper) getAddrConn() *addrConn { func (acbw *acBalancerWrapper) getAddrConn() *addrConn {

View File

@ -23,7 +23,6 @@ import (
"errors" "errors"
"fmt" "fmt"
"math" "math"
"net/url"
"reflect" "reflect"
"strings" "strings"
"sync" "sync"
@ -38,6 +37,7 @@ 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")
// errNoTransportCredsInBundle indicated that the configured creds bundle // errTransportCredentialsMissing indicates that users want to transmit security
// returned a transport credentials which was nil. // information (e.g., OAuth2 token) which requires secure connection on an insecure
errNoTransportCredsInBundle = errors.New("grpc: credentials.Bundle must return non-nil transport credentials") // connection.
// 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 (
@ -143,7 +143,6 @@ func DialContext(ctx context.Context, target string, opts ...DialOption) (conn *
firstResolveEvent: grpcsync.NewEvent(), firstResolveEvent: grpcsync.NewEvent(),
} }
cc.retryThrottler.Store((*retryThrottler)(nil)) cc.retryThrottler.Store((*retryThrottler)(nil))
cc.safeConfigSelector.UpdateConfigSelector(&defaultConfigSelector{nil})
cc.ctx, cc.cancel = context.WithCancel(context.Background()) cc.ctx, cc.cancel = context.WithCancel(context.Background())
for _, opt := range opts { for _, opt := range opts {
@ -177,20 +176,17 @@ 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
} }
if cc.dopts.copts.CredsBundle != nil && cc.dopts.copts.CredsBundle.TransportCredentials() == nil { } else {
return nil, errNoTransportCredsInBundle if cc.dopts.copts.TransportCredentials != nil || cc.dopts.copts.CredsBundle != nil {
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
@ -251,15 +247,38 @@ func DialContext(ctx context.Context, target string, opts ...DialOption) (conn *
} }
// Determine the resolver to use. // Determine the resolver to use.
resolverBuilder, err := cc.parseTargetAndFindResolver() cc.parsedTarget = grpcutil.ParseTarget(cc.target, cc.dopts.copts.Dialer != nil)
if err != nil { channelz.Infof(logger, cc.channelzID, "parsed scheme: %q", cc.parsedTarget.Scheme)
return nil, err resolverBuilder := cc.getResolver(cc.parsedTarget.Scheme)
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,
} }
cc.authority, err = determineAuthority(cc.parsedTarget.Endpoint, cc.target, cc.dopts) resolverBuilder = cc.getResolver(cc.parsedTarget.Scheme)
if err != nil { if resolverBuilder == nil {
return nil, err return nil, fmt.Errorf("could not get resolver for default scheme: %q", cc.parsedTarget.Scheme)
}
}
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.
@ -285,7 +304,6 @@ 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,
@ -303,7 +321,6 @@ 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
@ -521,31 +538,12 @@ func (cc *ClientConn) WaitForStateChange(ctx context.Context, sourceState connec
// //
// Experimental // Experimental
// //
// Notice: This API is EXPERIMENTAL and may be changed or removed in a later // Notice: This API is EXPERIMENTAL and may be changed or removed in a
// release. // later 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 {
@ -633,10 +631,7 @@ func (cc *ClientConn) updateResolverState(s resolver.State, err error) error {
} }
var ret error var ret error
if cc.dopts.disableServiceConfig { if cc.dopts.disableServiceConfig || s.ServiceConfig == nil {
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?
@ -715,12 +710,7 @@ 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)
@ -849,7 +839,8 @@ func (ac *addrConn) connect() error {
ac.updateConnectivityState(connectivity.Connecting, nil) ac.updateConnectivityState(connectivity.Connecting, nil)
ac.mu.Unlock() ac.mu.Unlock()
ac.resetTransport() // Start a goroutine connecting to the server asynchronously.
go ac.resetTransport()
return nil return nil
} }
@ -886,7 +877,6 @@ 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
@ -900,26 +890,6 @@ 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{}
@ -1075,12 +1045,12 @@ func (cc *ClientConn) Close() error {
cc.blockingpicker.close() cc.blockingpicker.close()
if bWrapper != nil {
bWrapper.close()
}
if rWrapper != nil { if rWrapper != nil {
rWrapper.close() rWrapper.close()
} }
if bWrapper != nil {
bWrapper.close()
}
for ac := range conns { for ac := range conns {
ac.tearDown(ErrClientConnClosing) ac.tearDown(ErrClientConnClosing)
@ -1159,6 +1129,11 @@ 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()
@ -1186,10 +1161,11 @@ 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()
if err := ac.tryAllAddrs(addrs, connectDeadline); err != nil { newTr, addr, reconnect, err := ac.tryAllAddrs(addrs, connectDeadline)
ac.cc.resolveNow(resolver.ResolveNowOptions{}) if err != nil {
// After exhausting all addresses, the addrConn enters // After exhausting all addresses, the addrConn enters
// TRANSIENT_FAILURE. // TRANSIENT_FAILURE.
ac.mu.Lock() ac.mu.Lock()
@ -1215,30 +1191,50 @@ 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()
return return
} }
// Success; reset backoff. ac.curAddr = addr
ac.mu.Lock() ac.transport = newTr
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 // tryAllAddrs tries to creates a connection to the addresses, and stop when at the
// the first successful one. It returns an error if no address was successfully // first successful one. It returns the transport, the address and a Event in
// connected, or updates ac appropriately with the new transport. // the successful case. The Event fires when the returned transport disconnects.
func (ac *addrConn) tryAllAddrs(addrs []resolver.Address, connectDeadline time.Time) error { func (ac *addrConn) tryAllAddrs(addrs []resolver.Address, connectDeadline time.Time) (transport.ClientTransport, resolver.Address, *grpcsync.Event, 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 errConnClosing return nil, resolver.Address{}, nil, errConnClosing
} }
ac.cc.mu.RLock() ac.cc.mu.RLock()
@ -1253,9 +1249,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)
err := ac.createTransport(addr, copts, connectDeadline) newTr, reconnect, err := ac.createTransport(addr, copts, connectDeadline)
if err == nil { if err == nil {
return nil return newTr, addr, reconnect, nil
} }
if firstConnErr == nil { if firstConnErr == nil {
firstConnErr = err firstConnErr = err
@ -1264,50 +1260,57 @@ func (ac *addrConn) tryAllAddrs(addrs []resolver.Address, connectDeadline time.T
} }
// Couldn't connect to any address. // Couldn't connect to any address.
return firstConnErr return nil, resolver.Address{}, nil, firstConnErr
} }
// createTransport creates a connection to addr. It returns an error if the // createTransport creates a connection to addr. It returns the transport and a
// address was not successfully connected, or updates ac appropriately with the // Event in the successful case. The Event fires when the returned transport
// new transport. // disconnects.
func (ac *addrConn) createTransport(addr resolver.Address, copts transport.ConnectOptions, connectDeadline time.Time) error { func (ac *addrConn) createTransport(addr resolver.Address, copts transport.ConnectOptions, connectDeadline time.Time) (transport.ClientTransport, *grpcsync.Event, error) {
// TODO: Delete prefaceReceived and move the logic to wait for it into the prefaceReceived := make(chan struct{})
// transport. onCloseCalled := make(chan struct{})
prefaceReceived := grpcsync.NewEvent() reconnect := grpcsync.NewEvent()
connClosed := grpcsync.NewEvent()
addr.ServerName = ac.cc.getServerName(addr) // addr.ServerName takes precedent over ClientConn authority, if present.
hctx, hcancel := context.WithCancel(ac.ctx) if addr.ServerName == "" {
hcStarted := false // protected by ac.mu addr.ServerName = ac.cc.authority
onClose := func() {
ac.mu.Lock()
defer ac.mu.Unlock()
defer connClosed.Fire()
if !hcStarted || hctx.Err() != nil {
// We didn't start the health check or set the state to READY, so
// no need to do anything else here.
//
// OR, we have already cancelled the health check context, meaning
// 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)
}
} }
once := sync.Once{}
onGoAway := func(r transport.GoAwayReason) { onGoAway := func(r transport.GoAwayReason) {
ac.mu.Lock() ac.mu.Lock()
ac.adjustParams(r) 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() ac.mu.Unlock()
onClose() reconnect.Fire()
}
onClose := func() {
ac.mu.Lock()
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()
close(onCloseCalled)
reconnect.Fire()
}
onPrefaceReceipt := func() {
close(prefaceReceived)
} }
connectCtx, cancel := context.WithDeadline(ac.ctx, connectDeadline) connectCtx, cancel := context.WithDeadline(ac.ctx, connectDeadline)
@ -1316,67 +1319,27 @@ 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, func() { prefaceReceived.Fire() }, onGoAway, onClose) newTr, err := transport.NewClientTransport(connectCtx, ac.cc.ctx, addr, copts, onPrefaceReceipt, 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", addr, err) channelz.Warningf(logger, ac.channelzID, "grpc: addrConn.createTransport failed to connect to %v. Err: %v. Reconnecting...", addr, err)
return err return nil, nil, err
} }
select { select {
case <-connectCtx.Done(): case <-time.After(time.Until(connectDeadline)):
// We didn't get the preface in time. // We didn't get the preface in time.
// The error we pass to Close() is immaterial since there are no open newTr.Close()
// streams at this point, so no trailers with error details will be sent channelz.Warningf(logger, ac.channelzID, "grpc: addrConn.createTransport failed to connect to %v: didn't receive server preface in time. Reconnecting...", addr)
// out. We just need to pass a non-nil error. return nil, nil, errors.New("timed out waiting for server handshake")
newTr.Close(transport.ErrConnClosing) case <-prefaceReceived:
if connectCtx.Err() == context.DeadlineExceeded {
err := errors.New("failed to receive server preface within timeout")
channelz.Warningf(logger, ac.channelzID, "grpc: addrConn.createTransport failed to connect to %v: %v", addr, err)
return err
}
return nil
case <-prefaceReceived.Done():
// We got the preface - huzzah! things are good. // We got the preface - huzzah! things are good.
ac.mu.Lock() case <-onCloseCalled:
defer ac.mu.Unlock() // The transport has already closed - noop.
if connClosed.HasFired() { return nil, nil, errors.New("connection closed")
// onClose called first; go idle but do nothing else. // TODO(deklerk) this should bail on ac.ctx.Done(). Add a test and fix.
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
@ -1460,20 +1423,33 @@ func (ac *addrConn) resetConnectBackoff() {
ac.mu.Unlock() ac.mu.Unlock()
} }
// getReadyTransport returns the transport if ac's state is READY or nil if not. // getReadyTransport returns the transport if ac's state is READY.
func (ac *addrConn) getReadyTransport() transport.ClientTransport { // Otherwise it returns nil, false.
// 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()
defer ac.mu.Unlock() if ac.state == connectivity.Ready && ac.transport != nil {
if ac.state == connectivity.Ready { t := ac.transport
return ac.transport ac.mu.Unlock()
return t, true
} }
return nil var idle bool
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.
// // TODO(zhaoq): Make this synchronous to avoid unbounded memory consumption in
// Note that tearDown doesn't remove ac from ac.cc.conns, so the addrConn struct // some edge cases (e.g., the caller opens and closes many addrConn's in a
// will leak. In most cases, call cc.removeAddrConn() instead. // tight loop.
// tearDown doesn't remove ac from ac.cc.conns.
func (ac *addrConn) tearDown(err error) { func (ac *addrConn) tearDown(err error) {
ac.mu.Lock() ac.mu.Lock()
if ac.state == connectivity.Shutdown { if ac.state == connectivity.Shutdown {
@ -1618,114 +1594,3 @@ func (cc *ClientConn) connectionError() error {
defer cc.lceMu.Unlock() defer cc.lceMu.Unlock()
return cc.lastConnectionError return cc.lastConnectionError
} }
func (cc *ClientConn) parseTargetAndFindResolver() (resolver.Builder, error) {
channelz.Infof(logger, cc.channelzID, "original dial target is: %q", cc.target)
var rb resolver.Builder
parsedTarget, err := parseTarget(cc.target)
if err != nil {
channelz.Infof(logger, cc.channelzID, "dial target %q parse failed: %v", cc.target, err)
} else {
channelz.Infof(logger, cc.channelzID, "parsed dial target is: %+v", parsedTarget)
rb = cc.getResolver(parsedTarget.Scheme)
if rb != nil {
cc.parsedTarget = parsedTarget
return rb, nil
}
}
// We are here because the user's dial target did not contain a scheme or
// specified an unregistered scheme. We should fallback to the default
// scheme, except when a custom dialer is specified in which case, we should
// always use passthrough scheme.
defScheme := resolver.GetDefaultScheme()
channelz.Infof(logger, cc.channelzID, "fallback to scheme %q", defScheme)
canonicalTarget := defScheme + ":///" + cc.target
parsedTarget, err = parseTarget(canonicalTarget)
if err != nil {
channelz.Infof(logger, cc.channelzID, "dial target %q parse failed: %v", canonicalTarget, err)
return nil, err
}
channelz.Infof(logger, cc.channelzID, "parsed dial target is: %+v", parsedTarget)
rb = cc.getResolver(parsedTarget.Scheme)
if rb == nil {
return nil, fmt.Errorf("could not get resolver for default scheme: %q", parsedTarget.Scheme)
}
cc.parsedTarget = parsedTarget
return rb, nil
}
// parseTarget uses RFC 3986 semantics to parse the given target into a
// resolver.Target struct containing scheme, authority and endpoint. Query
// params are stripped from the endpoint.
func parseTarget(target string) (resolver.Target, error) {
u, err := url.Parse(target)
if err != nil {
return resolver.Target{}, err
}
// For targets of the form "[scheme]://[authority]/endpoint, the endpoint
// value returned from url.Parse() contains a leading "/". Although this is
// in accordance with RFC 3986, we do not want to break existing resolver
// implementations which expect the endpoint without the leading "/". So, we
// end up stripping the leading "/" here. But this will result in an
// incorrect parsing for something like "unix:///path/to/socket". Since we
// own the "unix" resolver, we can workaround in the unix resolver by using
// the `URL` field instead of the `Endpoint` field.
endpoint := u.Path
if endpoint == "" {
endpoint = u.Opaque
}
endpoint = strings.TrimPrefix(endpoint, "/")
return resolver.Target{
Scheme: u.Scheme,
Authority: u.Host,
Endpoint: endpoint,
URL: *u,
}, nil
}
// Determine channel authority. The order of precedence is as follows:
// - user specified authority override using `WithAuthority` dial option
// - creds' notion of server name for the authentication handshake
// - endpoint from dial target of the form "scheme://[authority]/endpoint"
func determineAuthority(endpoint, target string, dopts dialOptions) (string, error) {
// Historically, we had two options for users to specify the serverName or
// authority for a channel. One was through the transport credentials
// (either in its constructor, or through the OverrideServerName() method).
// The other option (for cases where WithInsecure() dial option was used)
// was to use the WithAuthority() dial option.
//
// A few things have changed since:
// - `insecure` package with an implementation of the `TransportCredentials`
// interface for the insecure case
// - WithAuthority() dial option support for secure credentials
authorityFromCreds := ""
if creds := dopts.copts.TransportCredentials; creds != nil && creds.Info().ServerName != "" {
authorityFromCreds = creds.Info().ServerName
}
authorityFromDialOption := dopts.authority
if (authorityFromCreds != "" && authorityFromDialOption != "") && authorityFromCreds != authorityFromDialOption {
return "", fmt.Errorf("ClientConn's authority from transport creds %q and dial option %q don't match", authorityFromCreds, authorityFromDialOption)
}
switch {
case authorityFromDialOption != "":
return authorityFromDialOption, nil
case authorityFromCreds != "":
return authorityFromCreds, nil
case strings.HasPrefix(target, "unix:") || strings.HasPrefix(target, "unix-abstract:"):
// TODO: remove when the unix resolver implements optional interface to
// return channel authority.
return "localhost", nil
case strings.HasPrefix(endpoint, ":"):
return "localhost" + endpoint, nil
default:
// TODO: Define an optional interface on the resolver builder to return
// the channel authority given the user's dial target. For resolvers
// which don't implement this interface, we will use the endpoint from
// "scheme://authority/endpoint" as the default authority.
return endpoint, nil
}
}

View File

@ -18,6 +18,7 @@
// 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 (
@ -44,7 +45,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"
} }
} }
@ -60,35 +61,3 @@ const (
// Shutdown indicates the ClientConn has started shutting down. // Shutdown indicates the ClientConn has started shutting down.
Shutdown Shutdown
) )
// ServingMode indicates the current mode of operation of the server.
//
// Only xDS enabled gRPC servers currently report their serving mode.
type ServingMode int
const (
// ServingModeStarting indicates that the server is starting up.
ServingModeStarting ServingMode = iota
// ServingModeServing indicates that the server contains all required
// configuration and is serving RPCs.
ServingModeServing
// ServingModeNotServing indicates that the server is not accepting new
// connections. Existing connections will be closed gracefully, allowing
// in-progress RPCs to complete. A server enters this mode when it does not
// contain the required configuration to serve RPCs.
ServingModeNotServing
)
func (s ServingMode) String() string {
switch s {
case ServingModeStarting:
return "STARTING"
case ServingModeServing:
return "SERVING"
case ServingModeNotServing:
return "NOT_SERVING"
default:
logger.Errorf("unknown serving mode: %d", s)
return "INVALID_MODE"
}
}

View File

@ -30,7 +30,7 @@ import (
"github.com/golang/protobuf/proto" "github.com/golang/protobuf/proto"
"google.golang.org/grpc/attributes" "google.golang.org/grpc/attributes"
icredentials "google.golang.org/grpc/internal/credentials" "google.golang.org/grpc/internal"
) )
// PerRPCCredentials defines the common interface for the credentials which need to // PerRPCCredentials defines the common interface for the credentials which need to
@ -140,11 +140,6 @@ 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
@ -158,13 +153,9 @@ 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 specifies the value used for the following: // OverrideServerName overrides the server name used to verify the hostname on the returned certificates from the server.
// - verifying the hostname on the returned certificates // gRPC internals also use it to override the virtual hosting name if it is set.
// - as SNI in the client's handshake to support virtual hosting // It must be called before dialing. Currently, this is only used by grpclb.
// - 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
} }
@ -178,18 +169,8 @@ 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.
// //
@ -207,12 +188,15 @@ type RequestInfo struct {
AuthInfo AuthInfo AuthInfo AuthInfo
} }
// requestInfoKey is a struct to be used as the key when attaching a RequestInfo to a context object.
type requestInfoKey struct{}
// RequestInfoFromContext extracts the RequestInfo from the context if it exists. // RequestInfoFromContext extracts the RequestInfo from the context if it exists.
// //
// This API is experimental. // This API is experimental.
func RequestInfoFromContext(ctx context.Context) (ri RequestInfo, ok bool) { func RequestInfoFromContext(ctx context.Context) (ri RequestInfo, ok bool) {
ri, ok = icredentials.RequestInfoFromContext(ctx).(RequestInfo) ri, ok = ctx.Value(requestInfoKey{}).(RequestInfo)
return ri, ok return
} }
// ClientHandshakeInfo holds data to be passed to ClientHandshake. This makes // ClientHandshakeInfo holds data to be passed to ClientHandshake. This makes
@ -227,12 +211,16 @@ type ClientHandshakeInfo struct {
Attributes *attributes.Attributes Attributes *attributes.Attributes
} }
// clientHandshakeInfoKey is a struct used as the key to store
// ClientHandshakeInfo in a context.
type clientHandshakeInfoKey struct{}
// ClientHandshakeInfoFromContext returns the ClientHandshakeInfo struct stored // ClientHandshakeInfoFromContext returns the ClientHandshakeInfo struct stored
// in ctx. // in ctx.
// //
// This API is experimental. // This API is experimental.
func ClientHandshakeInfoFromContext(ctx context.Context) ClientHandshakeInfo { func ClientHandshakeInfoFromContext(ctx context.Context) ClientHandshakeInfo {
chi, _ := icredentials.ClientHandshakeInfoFromContext(ctx).(ClientHandshakeInfo) chi, _ := ctx.Value(clientHandshakeInfoKey{}).(ClientHandshakeInfo)
return chi return chi
} }
@ -261,6 +249,15 @@ func CheckSecurityLevel(ai AuthInfo, level SecurityLevel) error {
return nil return nil
} }
func init() {
internal.NewRequestInfoContext = func(ctx context.Context, ri RequestInfo) context.Context {
return context.WithValue(ctx, requestInfoKey{}, ri)
}
internal.NewClientHandshakeInfoContext = func(ctx context.Context, chi ClientHandshakeInfo) context.Context {
return context.WithValue(ctx, clientHandshakeInfoKey{}, chi)
}
}
// ChannelzSecurityInfo defines the interface that security protocols should implement // ChannelzSecurityInfo defines the interface that security protocols should implement
// in order to provide security info to channelz. // in order to provide security info to channelz.
// //

30
vendor/google.golang.org/grpc/credentials/go12.go generated vendored Normal file
View File

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

View File

@ -1,72 +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 insecure provides an implementation of the
// credentials.TransportCredentials interface which disables transport security.
package insecure
import (
"context"
"net"
"google.golang.org/grpc/credentials"
)
// NewCredentials returns a credentials which disables transport security.
//
// Note that using this credentials with per-RPC credentials which require
// transport security is incompatible and will cause grpc.Dial() to fail.
func NewCredentials() credentials.TransportCredentials {
return insecureTC{}
}
// insecureTC implements the insecure transport credentials. The handshake
// methods simply return the passed in net.Conn and set the security level to
// NoSecurity.
type insecureTC struct{}
func (insecureTC) ClientHandshake(ctx context.Context, _ string, conn net.Conn) (net.Conn, credentials.AuthInfo, error) {
return conn, info{credentials.CommonAuthInfo{SecurityLevel: credentials.NoSecurity}}, nil
}
func (insecureTC) ServerHandshake(conn net.Conn) (net.Conn, credentials.AuthInfo, error) {
return conn, info{credentials.CommonAuthInfo{SecurityLevel: credentials.NoSecurity}}, nil
}
func (insecureTC) Info() credentials.ProtocolInfo {
return credentials.ProtocolInfo{SecurityProtocol: "insecure"}
}
func (insecureTC) Clone() credentials.TransportCredentials {
return insecureTC{}
}
func (insecureTC) OverrideServerName(string) error {
return nil
}
// info contains the auth information for an insecure connection.
// It implements the AuthInfo interface.
type info struct {
credentials.CommonAuthInfo
}
// AuthType returns the type of info as a string.
func (info) AuthType() string {
return "insecure"
}

View File

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

View File

@ -27,9 +27,9 @@ import (
"google.golang.org/grpc/backoff" "google.golang.org/grpc/backoff"
"google.golang.org/grpc/balancer" "google.golang.org/grpc/balancer"
"google.golang.org/grpc/credentials" "google.golang.org/grpc/credentials"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/internal" "google.golang.org/grpc/internal"
internalbackoff "google.golang.org/grpc/internal/backoff" internalbackoff "google.golang.org/grpc/internal/backoff"
"google.golang.org/grpc/internal/envconfig"
"google.golang.org/grpc/internal/transport" "google.golang.org/grpc/internal/transport"
"google.golang.org/grpc/keepalive" "google.golang.org/grpc/keepalive"
"google.golang.org/grpc/resolver" "google.golang.org/grpc/resolver"
@ -50,6 +50,7 @@ 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
@ -65,6 +66,10 @@ type dialOptions struct {
minConnectTimeout func() time.Duration minConnectTimeout func() time.Duration
defaultServiceConfig *ServiceConfig // defaultServiceConfig is parsed from defaultServiceConfigRawJSON. defaultServiceConfig *ServiceConfig // defaultServiceConfig is parsed from defaultServiceConfigRawJSON.
defaultServiceConfigRawJSON *string defaultServiceConfigRawJSON *string
// This is used by ccResolverWrapper to backoff between successive calls to
// resolver.ResolveNow(). The user will have no need to configure this, but
// we need to be able to configure this in tests.
resolveNowBackoff func(int) time.Duration
resolvers []resolver.Builder resolvers []resolver.Builder
} }
@ -227,14 +232,18 @@ func WithServiceConfig(c <-chan ServiceConfig) DialOption {
}) })
} }
// WithConnectParams configures the ClientConn to use the provided ConnectParams // WithConnectParams configures the dialer 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}
@ -272,7 +281,7 @@ func withBackoff(bs internalbackoff.Strategy) DialOption {
}) })
} }
// WithBlock returns a DialOption which makes callers of Dial block until the // WithBlock returns a DialOption which makes caller of Dial blocks 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 {
@ -298,17 +307,11 @@ func WithReturnConnectionError() DialOption {
} }
// WithInsecure returns a DialOption which disables transport security for this // WithInsecure returns a DialOption which disables transport security for this
// ClientConn. Under the hood, it uses insecure.NewCredentials(). // ClientConn. Note that transport security is required unless WithInsecure is
// // 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.copts.TransportCredentials = insecure.NewCredentials() o.insecure = true
}) })
} }
@ -483,7 +486,8 @@ 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 and as the server name in authentication handshake. // :authority pseudo-header. This value only works with WithInsecure and has no
// 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,16 +523,14 @@ 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, or // 1. WithDisableServiceConfig is also used.
// // 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.
// //
// The parameter s is the JSON representation of the default service config. // Experimental
// For more information about service configs, see: //
// https://github.com/grpc/grpc/blob/master/doc/service_config.md // Notice: This API is EXPERIMENTAL and may be changed or removed in a
// For a simple example of usage, see: // later release.
// 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
@ -540,8 +542,14 @@ 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 enabled by default, but may be disabled by // Retry support is currently disabled by default, but will be enabled by
// setting the environment variable "GRPC_GO_RETRY" to "off". // default in the future. Until then, it may be enabled by setting the
// 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
@ -581,12 +589,14 @@ 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,
ReadBufferSize: defaultReadBufSize, ReadBufferSize: defaultReadBufSize,
UseProxy: true, UseProxy: true,
}, },
resolveNowBackoff: internalbackoff.DefaultExponential.Backoff,
} }
} }
@ -601,6 +611,16 @@ func withMinConnectDeadline(f func() time.Duration) DialOption {
}) })
} }
// withResolveNowBackoff specifies the function that clientconn uses to backoff
// between successive calls to resolver.ResolveNow().
//
// For testing purpose only.
func withResolveNowBackoff(f func(int) time.Duration) DialOption {
return newFuncDialOption(func(o *dialOptions) {
o.resolveNowBackoff = f
})
}
// WithResolvers allows a list of resolver implementations to be registered // WithResolvers allows a list of resolver implementations to be registered
// locally with the ClientConn without needing to be globally registered via // locally with the ClientConn without needing to be globally registered via
// resolver.Register. They will be matched against the scheme used for the // resolver.Register. They will be matched against the scheme used for the

View File

@ -19,14 +19,11 @@
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"
) )
@ -100,7 +97,6 @@ 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.
@ -109,32 +105,19 @@ 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 newLoggerV2WithConfig(infoW, warningW, errorW, loggerV2Config{}) return NewLoggerV2WithVerbosity(infoW, warningW, errorW, 0)
} }
// 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
flag := log.LstdFlags m = append(m, log.New(infoW, severityName[infoLog]+": ", log.LstdFlags))
if c.jsonFormat { m = append(m, log.New(io.MultiWriter(infoW, warningW), severityName[warningLog]+": ", log.LstdFlags))
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, "", flag)) 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))
return &loggerT{m: m, v: c.verbose, jsonFormat: c.jsonFormat} return &loggerT{m: m, v: v}
} }
// newLoggerV2 creates a loggerV2 to be used as default logger. // newLoggerV2 creates a loggerV2 to be used as default logger.
@ -159,79 +142,58 @@ 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.output(infoLog, fmt.Sprint(args...)) g.m[infoLog].Print(args...)
} }
func (g *loggerT) Infoln(args ...interface{}) { func (g *loggerT) Infoln(args ...interface{}) {
g.output(infoLog, fmt.Sprintln(args...)) g.m[infoLog].Println(args...)
} }
func (g *loggerT) Infof(format string, args ...interface{}) { func (g *loggerT) Infof(format string, args ...interface{}) {
g.output(infoLog, fmt.Sprintf(format, args...)) g.m[infoLog].Printf(format, args...)
} }
func (g *loggerT) Warning(args ...interface{}) { func (g *loggerT) Warning(args ...interface{}) {
g.output(warningLog, fmt.Sprint(args...)) g.m[warningLog].Print(args...)
} }
func (g *loggerT) Warningln(args ...interface{}) { func (g *loggerT) Warningln(args ...interface{}) {
g.output(warningLog, fmt.Sprintln(args...)) g.m[warningLog].Println(args...)
} }
func (g *loggerT) Warningf(format string, args ...interface{}) { func (g *loggerT) Warningf(format string, args ...interface{}) {
g.output(warningLog, fmt.Sprintf(format, args...)) g.m[warningLog].Printf(format, args...)
} }
func (g *loggerT) Error(args ...interface{}) { func (g *loggerT) Error(args ...interface{}) {
g.output(errorLog, fmt.Sprint(args...)) g.m[errorLog].Print(args...)
} }
func (g *loggerT) Errorln(args ...interface{}) { func (g *loggerT) Errorln(args ...interface{}) {
g.output(errorLog, fmt.Sprintln(args...)) g.m[errorLog].Println(args...)
} }
func (g *loggerT) Errorf(format string, args ...interface{}) { func (g *loggerT) Errorf(format string, args ...interface{}) {
g.output(errorLog, fmt.Sprintf(format, args...)) g.m[errorLog].Printf(format, args...)
} }
func (g *loggerT) Fatal(args ...interface{}) { func (g *loggerT) Fatal(args ...interface{}) {
g.output(fatalLog, fmt.Sprint(args...)) g.m[fatalLog].Fatal(args...)
os.Exit(1) // No need to call os.Exit() again because log.Logger.Fatal() calls os.Exit().
} }
func (g *loggerT) Fatalln(args ...interface{}) { func (g *loggerT) Fatalln(args ...interface{}) {
g.output(fatalLog, fmt.Sprintln(args...)) g.m[fatalLog].Fatalln(args...)
os.Exit(1) // No need to call os.Exit() again because log.Logger.Fatal() calls os.Exit().
} }
func (g *loggerT) Fatalf(format string, args ...interface{}) { func (g *loggerT) Fatalf(format string, args ...interface{}) {
g.output(fatalLog, fmt.Sprintf(format, args...)) g.m[fatalLog].Fatalf(format, args...)
os.Exit(1) // No need to call os.Exit() again because log.Logger.Fatal() calls os.Exit().
} }
func (g *loggerT) V(l int) bool { func (g *loggerT) V(l int) bool {
@ -248,12 +210,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.Println. // InfoDepth logs to INFO log at the specified depth. Arguments are handled in the manner of fmt.Print.
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.Println. // WarningDepth logs to WARNING log at the specified depth. Arguments are handled in the manner of fmt.Print.
WarningDepth(depth int, args ...interface{}) WarningDepth(depth int, args ...interface{})
// ErrorDepth logs to ERROR log at the specified depth. Arguments are handled in the manner of fmt.Println. // ErrorDetph logs to ERROR log at the specified depth. Arguments are handled in the manner of fmt.Print.
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.Println. // FatalDepth logs to FATAL log at the specified depth. Arguments are handled in the manner of fmt.Print.
FatalDepth(depth int, args ...interface{}) FatalDepth(depth int, args ...interface{})
} }

View File

@ -1,8 +1,4 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT. // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.2.0
// - protoc v3.14.0
// source: grpc/health/v1/health.proto
package grpc_health_v1 package grpc_health_v1

6
vendor/google.golang.org/grpc/install_gae.sh generated vendored Normal file
View File

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

View File

@ -69,8 +69,7 @@ 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.Errorf("binary logging: failed to marshal proto message: %v", err) grpclogLogger.Infof("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)))
@ -90,23 +89,20 @@ 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 {
fs.mu.Lock()
defer fs.mu.Unlock()
if !fs.flusherStarted {
// Start the write loop when Write is called. // Start the write loop when Write is called.
fs.startFlushGoroutine() fs.writeStartOnce.Do(fs.startFlushGoroutine)
fs.flusherStarted = true fs.mu.Lock()
}
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
} }
@ -117,12 +113,7 @@ const (
func (fs *bufferedSink) startFlushGoroutine() { func (fs *bufferedSink) startFlushGoroutine() {
fs.writeTicker = time.NewTicker(bufFlushDuration) fs.writeTicker = time.NewTicker(bufFlushDuration)
go func() { go func() {
for { for range fs.writeTicker.C {
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)
@ -133,12 +124,10 @@ 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()
} }
close(fs.done) 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)
} }
@ -148,6 +137,7 @@ 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
} }
@ -165,6 +155,5 @@ func NewBufferedSink(o io.WriteCloser) Sink {
closer: o, closer: o,
out: newWriterSink(bufW), out: newWriterSink(bufW),
buf: bufW, buf: bufW,
done: make(chan struct{}),
} }
} }

View File

@ -204,9 +204,9 @@ func RegisterChannel(c Channel, pid int64, ref string) int64 {
trace: &channelTrace{createdTime: time.Now(), events: make([]*TraceEvent, 0, getMaxTraceEntry())}, trace: &channelTrace{createdTime: time.Now(), events: make([]*TraceEvent, 0, getMaxTraceEntry())},
} }
if pid == 0 { if pid == 0 {
db.get().addChannel(id, cn, true, pid) db.get().addChannel(id, cn, true, pid, ref)
} else { } else {
db.get().addChannel(id, cn, false, pid) db.get().addChannel(id, cn, false, pid, ref)
} }
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) db.get().addSubChannel(id, sc, pid, ref)
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) db.get().addListenSocket(id, ls, pid, ref)
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) db.get().addNormalSocket(id, ns, pid, ref)
return id return id
} }
// RemoveEntry removes an entry with unique channelz tracking id to be id from // RemoveEntry removes an entry with unique channelz trakcing 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) { func (c *channelMap) addChannel(id int64, cn *channel, isTopChannel bool, pid int64, ref string) {
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) { func (c *channelMap) addSubChannel(id int64, sc *subChannel, pid int64, ref string) {
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) {
c.mu.Unlock() c.mu.Unlock()
} }
func (c *channelMap) addListenSocket(id int64, ls *listenSocket, pid int64) { func (c *channelMap) addListenSocket(id int64, ls *listenSocket, pid int64, ref string) {
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) {
c.mu.Unlock() c.mu.Unlock()
} }
func (c *channelMap) addNormalSocket(id int64, ns *normalSocket, pid int64) { func (c *channelMap) addNormalSocket(id int64, ns *normalSocket, pid int64, ref string) {
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
} }
s := make([]*SocketMetric, 0, len(sks)) var s []*SocketMetric
for _, ns := range sks { for _, ns := range sks {
sm := &SocketMetric{} sm := &SocketMetric{}
sm.SocketData = ns.s.ChannelzMetric() sm.SocketData = ns.s.ChannelzMetric()

View File

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

View File

@ -1,5 +1,4 @@
//go:build !linux // +build !linux appengine
// +build !linux
/* /*
* *
@ -38,6 +37,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 environments") logger.Warning("Channelz: socket options are not supported on non-linux os and appengine.")
}) })
} }

View File

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

View File

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

View File

@ -1,49 +0,0 @@
/*
* 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 credentials
import (
"context"
)
// requestInfoKey is a struct to be used as the key to store RequestInfo in a
// context.
type requestInfoKey struct{}
// NewRequestInfoContext creates a context with ri.
func NewRequestInfoContext(ctx context.Context, ri interface{}) context.Context {
return context.WithValue(ctx, requestInfoKey{}, ri)
}
// RequestInfoFromContext extracts the RequestInfo from ctx.
func RequestInfoFromContext(ctx context.Context) interface{} {
return ctx.Value(requestInfoKey{})
}
// clientHandshakeInfoKey is a struct used as the key to store
// ClientHandshakeInfo in a context.
type clientHandshakeInfoKey struct{}
// ClientHandshakeInfoFromContext extracts the ClientHandshakeInfo from ctx.
func ClientHandshakeInfoFromContext(ctx context.Context) interface{} {
return ctx.Value(clientHandshakeInfoKey{})
}
// NewClientHandshakeInfoContext creates a context with chi.
func NewClientHandshakeInfoContext(ctx context.Context, chi interface{}) context.Context {
return context.WithValue(ctx, clientHandshakeInfoKey{}, chi)
}

View File

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

View File

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

View File

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

View File

@ -1,6 +1,8 @@
// +build appengine
/* /*
* *
* Copyright 2021 gRPC authors. * Copyright 2018 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.
@ -16,5 +18,13 @@
* *
*/ */
// Package grpcutil provides utility functions used across the gRPC codebase. package credentials
package grpcutil
import (
"net"
)
// WrapSyscallConn returns newConn on appengine.
func WrapSyscallConn(rawConn, newConn net.Conn) net.Conn {
return newConn
}

View File

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

View File

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

View File

@ -1,97 +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 envconfig
import (
"os"
"strings"
)
const (
// XDSBootstrapFileNameEnv is the env variable to set bootstrap file name.
// Do not use this and read from env directly. Its value is read and kept in
// variable BootstrapFileName.
//
// When both bootstrap FileName and FileContent are set, FileName is used.
XDSBootstrapFileNameEnv = "GRPC_XDS_BOOTSTRAP"
// XDSBootstrapFileContentEnv is the env variable to set bootstrapp file
// content. Do not use this and read from env directly. Its value is read
// and kept in variable BootstrapFileName.
//
// When both bootstrap FileName and FileContent are set, FileName is used.
XDSBootstrapFileContentEnv = "GRPC_XDS_BOOTSTRAP_CONFIG"
ringHashSupportEnv = "GRPC_XDS_EXPERIMENTAL_ENABLE_RING_HASH"
clientSideSecuritySupportEnv = "GRPC_XDS_EXPERIMENTAL_SECURITY_SUPPORT"
aggregateAndDNSSupportEnv = "GRPC_XDS_EXPERIMENTAL_ENABLE_AGGREGATE_AND_LOGICAL_DNS_CLUSTER"
rbacSupportEnv = "GRPC_XDS_EXPERIMENTAL_RBAC"
federationEnv = "GRPC_EXPERIMENTAL_XDS_FEDERATION"
rlsInXDSEnv = "GRPC_EXPERIMENTAL_XDS_RLS_LB"
c2pResolverTestOnlyTrafficDirectorURIEnv = "GRPC_TEST_ONLY_GOOGLE_C2P_RESOLVER_TRAFFIC_DIRECTOR_URI"
)
var (
// XDSBootstrapFileName holds the name of the file which contains xDS
// bootstrap configuration. Users can specify the location of the bootstrap
// file by setting the environment variable "GRPC_XDS_BOOTSTRAP".
//
// When both bootstrap FileName and FileContent are set, FileName is used.
XDSBootstrapFileName = os.Getenv(XDSBootstrapFileNameEnv)
// XDSBootstrapFileContent holds the content of the xDS bootstrap
// configuration. Users can specify the bootstrap config by setting the
// environment variable "GRPC_XDS_BOOTSTRAP_CONFIG".
//
// When both bootstrap FileName and FileContent are set, FileName is used.
XDSBootstrapFileContent = os.Getenv(XDSBootstrapFileContentEnv)
// XDSRingHash indicates whether ring hash support is enabled, which can be
// disabled by setting the environment variable
// "GRPC_XDS_EXPERIMENTAL_ENABLE_RING_HASH" to "false".
XDSRingHash = !strings.EqualFold(os.Getenv(ringHashSupportEnv), "false")
// XDSClientSideSecurity is used to control processing of security
// configuration on the client-side.
//
// Note that there is no env var protection for the server-side because we
// have a brand new API on the server-side and users explicitly need to use
// the new API to get security integration on the server.
XDSClientSideSecurity = !strings.EqualFold(os.Getenv(clientSideSecuritySupportEnv), "false")
// XDSAggregateAndDNS indicates whether processing of aggregated cluster
// and DNS cluster is enabled, which can be enabled by setting the
// environment variable
// "GRPC_XDS_EXPERIMENTAL_ENABLE_AGGREGATE_AND_LOGICAL_DNS_CLUSTER" to
// "true".
XDSAggregateAndDNS = strings.EqualFold(os.Getenv(aggregateAndDNSSupportEnv), "true")
// XDSRBAC indicates whether xDS configured RBAC HTTP Filter is enabled,
// which can be disabled by setting the environment variable
// "GRPC_XDS_EXPERIMENTAL_RBAC" to "false".
XDSRBAC = !strings.EqualFold(os.Getenv(rbacSupportEnv), "false")
// XDSFederation indicates whether federation support is enabled.
XDSFederation = strings.EqualFold(os.Getenv(federationEnv), "true")
// XDSRLS indicates whether processing of Cluster Specifier plugins and
// support for the RLS CLuster Specifier is enabled, which can be enabled by
// setting the environment variable "GRPC_EXPERIMENTAL_XDS_RLS_LB" to
// "true".
XDSRLS = strings.EqualFold(os.Getenv(rlsInXDSEnv), "true")
// C2PResolverTestOnlyTrafficDirectorURI is the TD URI for testing.
C2PResolverTestOnlyTrafficDirectorURI = os.Getenv(c2pResolverTestOnlyTrafficDirectorURIEnv)
)

View File

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

View File

@ -31,37 +31,26 @@ 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()
defer mu.Unlock() res := r.Int63n(n)
return r.Int63n(n) mu.Unlock()
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()
defer mu.Unlock() res := r.Intn(n)
return r.Intn(n) mu.Unlock()
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()
defer mu.Unlock() res := r.Float64()
return r.Float64() mu.Unlock()
} return res
// Uint64 implements rand.Uint64 on the grpcrand global source.
func Uint64() uint64 {
mu.Lock()
defer mu.Unlock()
return r.Uint64()
} }

View File

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

View File

@ -38,6 +38,12 @@ var (
// KeepaliveMinPingTime is the minimum ping interval. This must be 10s by // KeepaliveMinPingTime is the minimum ping interval. This must be 10s by
// default, but tests may wish to set it lower for convenience. // default, but tests may wish to set it lower for convenience.
KeepaliveMinPingTime = 10 * time.Second KeepaliveMinPingTime = 10 * time.Second
// NewRequestInfoContext creates a new context based on the argument context attaching
// the passed in RequestInfo to the new context.
NewRequestInfoContext interface{} // func(context.Context, credentials.RequestInfo) context.Context
// NewClientHandshakeInfoContext returns a copy of the input context with
// the passed in ClientHandshakeInfo struct added to it.
NewClientHandshakeInfoContext interface{} // func(context.Context, credentials.ClientHandshakeInfo) context.Context
// ParseServiceConfigForTesting is for creating a fake // ParseServiceConfigForTesting is for creating a fake
// ClientConn for resolver testing only // ClientConn for resolver testing only
ParseServiceConfigForTesting interface{} // func(string) *serviceconfig.ParseResult ParseServiceConfigForTesting interface{} // func(string) *serviceconfig.ParseResult
@ -59,11 +65,6 @@ var (
// gRPC server. An xDS-enabled server needs to know what type of credentials // gRPC server. An xDS-enabled server needs to know what type of credentials
// is configured on the underlying gRPC server. This is set by server.go. // is configured on the underlying gRPC server. This is set by server.go.
GetServerCredentials interface{} // func (*grpc.Server) credentials.TransportCredentials GetServerCredentials interface{} // func (*grpc.Server) credentials.TransportCredentials
// DrainServerTransports initiates a graceful close of existing connections
// on a gRPC server accepted on the provided listener address. An
// xDS-enabled server invokes this method on a grpc.Server when a particular
// listener moves to "not-serving" mode.
DrainServerTransports interface{} // func(*grpc.Server, string)
) )
// HealthChecker defines the signature of the client-side LB channel health checking function. // HealthChecker defines the signature of the client-side LB channel health checking function.

View File

@ -30,38 +30,14 @@ 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).(mdValue) md, _ := attrs.Value(mdKey).(metadata.MD)
return metadata.MD(md) return md
} }
// Set sets (overrides) the metadata in addr. // Set sets (overrides) the metadata in addr.
@ -69,6 +45,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.WithValue(mdKey, mdValue(md)) addr.Attributes = addr.Attributes.WithValues(mdKey, md)
return addr return addr
} }

View File

@ -24,7 +24,6 @@ import (
"sync" "sync"
"google.golang.org/grpc/internal/serviceconfig" "google.golang.org/grpc/internal/serviceconfig"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/resolver" "google.golang.org/grpc/resolver"
) )
@ -52,77 +51,6 @@ type RPCConfig struct {
Context context.Context Context context.Context
MethodConfig serviceconfig.MethodConfig // configuration to use for this RPC MethodConfig serviceconfig.MethodConfig // configuration to use for this RPC
OnCommitted func() // Called when the RPC has been committed (retries no longer possible) OnCommitted func() // Called when the RPC has been committed (retries no longer possible)
Interceptor ClientInterceptor
}
// ClientStream is the same as grpc.ClientStream, but defined here for circular
// dependency reasons.
type ClientStream interface {
// Header returns the header metadata received from the server if there
// is any. It blocks if the metadata is not ready to read.
Header() (metadata.MD, error)
// Trailer returns the trailer metadata from the server, if there is any.
// It must only be called after stream.CloseAndRecv has returned, or
// stream.Recv has returned a non-nil error (including io.EOF).
Trailer() metadata.MD
// CloseSend closes the send direction of the stream. It closes the stream
// when non-nil error is met. It is also not safe to call CloseSend
// concurrently with SendMsg.
CloseSend() error
// Context returns the context for this stream.
//
// It should not be called until after Header or RecvMsg has returned. Once
// called, subsequent client-side retries are disabled.
Context() context.Context
// SendMsg is generally called by generated code. On error, SendMsg aborts
// the stream. If the error was generated by the client, the status is
// returned directly; otherwise, io.EOF is returned and the status of
// the stream may be discovered using RecvMsg.
//
// SendMsg blocks until:
// - There is sufficient flow control to schedule m with the transport, or
// - The stream is done, or
// - The stream breaks.
//
// SendMsg does not wait until the message is received by the server. An
// untimely stream closure may result in lost messages. To ensure delivery,
// users should ensure the RPC completed successfully using RecvMsg.
//
// It is safe to have a goroutine calling SendMsg and another goroutine
// calling RecvMsg on the same stream at the same time, but it is not safe
// to call SendMsg on the same stream in different goroutines. It is also
// not safe to call CloseSend concurrently with SendMsg.
SendMsg(m interface{}) error
// RecvMsg blocks until it receives a message into m or the stream is
// done. It returns io.EOF when the stream completes successfully. On
// any other error, the stream is aborted and the error contains the RPC
// status.
//
// It is safe to have a goroutine calling SendMsg and another goroutine
// calling RecvMsg on the same stream at the same time, but it is not
// safe to call RecvMsg on the same stream in different goroutines.
RecvMsg(m interface{}) error
}
// ClientInterceptor is an interceptor for gRPC client streams.
type ClientInterceptor interface {
// NewStream produces a ClientStream for an RPC which may optionally use
// the provided function to produce a stream for delegation. Note:
// RPCInfo.Context should not be used (will be nil).
//
// done is invoked when the RPC is finished using its connection, or could
// not be assigned a connection. RPC operations may still occur on
// ClientStream after done is called, since the interceptor is invoked by
// application-layer operations. done must never be nil when called.
NewStream(ctx context.Context, ri RPCInfo, done func(), newStream func(ctx context.Context, done func()) (ClientStream, error)) (ClientStream, error)
}
// ServerInterceptor is an interceptor for incoming RPC's on gRPC server side.
type ServerInterceptor interface {
// 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
@ -132,7 +60,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.WithValue(csKey, cs) state.Attributes = state.Attributes.WithValues(csKey, cs)
return state return state
} }

View File

@ -34,7 +34,6 @@ import (
grpclbstate "google.golang.org/grpc/balancer/grpclb/state" grpclbstate "google.golang.org/grpc/balancer/grpclb/state"
"google.golang.org/grpc/grpclog" "google.golang.org/grpc/grpclog"
"google.golang.org/grpc/internal/backoff"
"google.golang.org/grpc/internal/envconfig" "google.golang.org/grpc/internal/envconfig"
"google.golang.org/grpc/internal/grpcrand" "google.golang.org/grpc/internal/grpcrand"
"google.golang.org/grpc/resolver" "google.golang.org/grpc/resolver"
@ -47,13 +46,6 @@ var EnableSRVLookups = false
var logger = grpclog.Component("dns") var logger = grpclog.Component("dns")
// Globals to stub out in tests. TODO: Perhaps these two can be combined into a
// single variable for testing the resolver?
var (
newTimer = time.NewTimer
newTimerDNSResRate = time.NewTimer
)
func init() { func init() {
resolver.Register(NewBuilder()) resolver.Register(NewBuilder())
} }
@ -151,6 +143,7 @@ func (b *dnsBuilder) Build(target resolver.Target, cc resolver.ClientConn, opts
d.wg.Add(1) d.wg.Add(1)
go d.watcher() go d.watcher()
d.ResolveNow(resolver.ResolveNowOptions{})
return d, nil return d, nil
} }
@ -208,38 +201,28 @@ func (d *dnsResolver) Close() {
func (d *dnsResolver) watcher() { func (d *dnsResolver) watcher() {
defer d.wg.Done() defer d.wg.Done()
backoffIndex := 1
for { for {
state, err := d.lookup()
if err != nil {
// Report error to the underlying grpc.ClientConn.
d.cc.ReportError(err)
} else {
err = d.cc.UpdateState(*state)
}
var timer *time.Timer
if err == nil {
// Success resolving, wait for the next ResolveNow. However, also wait 30 seconds at the very least
// to prevent constantly re-resolving.
backoffIndex = 1
timer = newTimerDNSResRate(minDNSResRate)
select { select {
case <-d.ctx.Done(): case <-d.ctx.Done():
timer.Stop()
return return
case <-d.rn: case <-d.rn:
} }
state, err := d.lookup()
if err != nil {
d.cc.ReportError(err)
} else { } else {
// Poll on an error found in DNS Resolver or an error received from ClientConn. d.cc.UpdateState(*state)
timer = newTimer(backoff.DefaultExponential.Backoff(backoffIndex))
backoffIndex++
} }
// Sleep to prevent excessive re-resolutions. Incoming resolution requests
// will be queued in d.rn.
t := time.NewTimer(minDNSResRate)
select { select {
case <-t.C:
case <-d.ctx.Done(): case <-d.ctx.Done():
timer.Stop() t.Stop()
return return
case <-timer.C:
} }
} }
} }
@ -277,13 +260,18 @@ func (d *dnsResolver) lookupSRV() ([]resolver.Address, error) {
return newAddrs, nil return newAddrs, nil
} }
func handleDNSError(err error, lookupType string) error { var filterError = func(err error) 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)
@ -318,12 +306,12 @@ func (d *dnsResolver) lookupTXT() *serviceconfig.ParseResult {
} }
func (d *dnsResolver) lookupHost() ([]resolver.Address, error) { func (d *dnsResolver) lookupHost() ([]resolver.Address, error) {
var newAddrs []resolver.Address
addrs, err := d.resolver.LookupHost(d.ctx, d.host) addrs, err := d.resolver.LookupHost(d.ctx, d.host)
if err != nil { if err != nil {
err = handleDNSError(err, "A") err = handleDNSError(err, "A")
return nil, err return nil, err
} }
newAddrs := make([]resolver.Address, 0, len(addrs))
for _, a := range addrs { for _, a := range addrs {
ip, ok := formatIP(a) ip, ok := formatIP(a)
if !ok { if !ok {

View File

@ -1,6 +1,8 @@
// +build go1.13
/* /*
* *
* Copyright 2021 gRPC authors. * Copyright 2019 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.
@ -16,16 +18,16 @@
* *
*/ */
package grpcutil package dns
import "regexp" import "net"
// FullMatchWithRegex returns whether the full text matches the regex provided. func init() {
func FullMatchWithRegex(re *regexp.Regexp, text string) bool { filterError = func(err error) error {
if len(text) == 0 { if dnsErr, ok := err.(*net.DNSError); ok && dnsErr.IsNotFound {
return re.MatchString(text) // The name does not exist; not an error.
return nil
}
return err
} }
re.Longest()
rem := re.FindString(text)
return len(rem) == len(text)
} }

View File

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

View File

@ -46,22 +46,6 @@ type BalancerConfig struct {
type intermediateBalancerConfig []map[string]json.RawMessage type intermediateBalancerConfig []map[string]json.RawMessage
// MarshalJSON implements the json.Marshaler interface.
//
// It marshals the balancer and config into a length-1 slice
// ([]map[string]config).
func (bc *BalancerConfig) MarshalJSON() ([]byte, error) {
if bc.Config == nil {
// If config is nil, return empty config `{}`.
return []byte(fmt.Sprintf(`[{%q: %v}]`, bc.Name, "{}")), nil
}
c, err := json.Marshal(bc.Config)
if err != nil {
return nil, err
}
return []byte(fmt.Sprintf(`[{%q: %s}]`, bc.Name, c)), nil
}
// UnmarshalJSON implements the json.Unmarshaler interface. // UnmarshalJSON implements the json.Unmarshaler interface.
// //
// ServiceConfig contains a list of loadBalancingConfigs, each with a name and // ServiceConfig contains a list of loadBalancingConfigs, each with a name and
@ -78,7 +62,6 @@ 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)
@ -93,7 +76,6 @@ 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.
@ -122,7 +104,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 in %v", names) return fmt.Errorf("invalid loadBalancingConfig: no supported policies found")
} }
// MethodConfig defines the configuration recommended by the service providers for a // MethodConfig defines the configuration recommended by the service providers for a

View File

@ -97,7 +97,7 @@ func (s *Status) Err() error {
if s.Code() == codes.OK { if s.Code() == codes.OK {
return nil return nil
} }
return &Error{s: s} return &Error{e: s.Proto()}
} }
// 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,23 +136,19 @@ 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 {
s *Status e *spb.Status
} }
func (e *Error) Error() string { func (e *Error) Error() string {
return e.s.String() return fmt.Sprintf("rpc error: code = %s desc = %s", codes.Code(e.e.GetCode()), e.e.GetMessage())
} }
// 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 e.s return FromProto(e.e)
} }
// Is implements future error.Is functionality. // Is implements future error.Is functionality.
@ -162,5 +158,5 @@ func (e *Error) Is(target error) bool {
if !ok { if !ok {
return false return false
} }
return proto.Equal(e.s.s, tse.s.s) return proto.Equal(e.e, tse.e)
} }

View File

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

View File

@ -1,5 +1,4 @@
//go:build !linux // +build !linux appengine
// +build !linux
/* /*
* *
@ -36,41 +35,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 environments.") logger.Info("CPU time info is unavailable on non-linux or appengine environment.")
}) })
} }
// GetCPUTime returns the how much CPU time has passed since the start of this // GetCPUTime returns the how much CPU time has passed since the start of this process.
// process. It always returns 0 under non-linux environments. // It always returns 0 under non-linux or appengine environment.
func GetCPUTime() int64 { func GetCPUTime() int64 {
log() log()
return 0 return 0
} }
// Rusage is an empty struct under non-linux environments. // Rusage is an empty struct under non-linux or appengine environment.
type Rusage struct{} type Rusage struct{}
// GetRusage is a no-op function under non-linux environments. // GetRusage is a no-op function under non-linux or appengine environment.
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 environments. // between two Rusage structs. It a no-op function for non-linux or appengine environment.
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 environments. // SetTCPUserTimeout is a no-op function under non-linux or appengine 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 environments. // GetTCPUserTimeout is a no-op function under non-linux or appengine environments
// A negative return value indicates the operation is not supported // a negative return value indicates the operation is not supported
func GetTCPUserTimeout(conn net.Conn) (int, error) { func GetTCPUserTimeout(conn net.Conn) (int, error) {
log() log()
return -1, nil return -1, nil

View File

@ -20,17 +20,13 @@ package transport
import ( import (
"bytes" "bytes"
"errors"
"fmt" "fmt"
"runtime" "runtime"
"strconv"
"sync" "sync"
"sync/atomic" "sync/atomic"
"golang.org/x/net/http2" "golang.org/x/net/http2"
"golang.org/x/net/http2/hpack" "golang.org/x/net/http2/hpack"
"google.golang.org/grpc/internal/grpcutil"
"google.golang.org/grpc/status"
) )
var updateHeaderTblSize = func(e *hpack.Encoder, v uint32) { var updateHeaderTblSize = func(e *hpack.Encoder, v uint32) {
@ -132,15 +128,6 @@ 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 {
httpStatus uint32
streamID uint32
contentSubtype string
status *status.Status
}
func (*earlyAbortStream) isTransportResponseFrame() bool { return false }
type dataFrame struct { type dataFrame struct {
streamID uint32 streamID uint32
endStream bool endStream bool
@ -297,7 +284,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 {
@ -311,10 +298,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:
} }
} }
@ -348,7 +335,8 @@ 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.
c.trfChan.Store(make(chan struct{})) ch := make(chan struct{})
c.trfChan.Store(&ch)
} }
} }
c.mu.Unlock() c.mu.Unlock()
@ -389,9 +377,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,6 +395,7 @@ 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
} }
} }
@ -431,14 +420,6 @@ 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()
} }
@ -768,27 +749,6 @@ func (l *loopyWriter) cleanupStreamHandler(c *cleanupStream) error {
return nil return nil
} }
func (l *loopyWriter) earlyAbortStreamHandler(eas *earlyAbortStream) error {
if l.side == clientSide {
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{
{Name: ":status", Value: strconv.Itoa(int(eas.httpStatus))},
{Name: "content-type", Value: grpcutil.ContentType(eas.contentSubtype)},
{Name: "grpc-status", Value: strconv.Itoa(int(eas.status.Code()))},
{Name: "grpc-message", Value: encodeGrpcMessage(eas.status.Message())},
}
if err := l.writeHeader(eas.streamID, true, headerFields, nil); err != nil {
return err
}
return nil
}
func (l *loopyWriter) incomingGoAwayHandler(*incomingGoAway) error { func (l *loopyWriter) incomingGoAwayHandler(*incomingGoAway) error {
if l.side == clientSide { if l.side == clientSide {
l.draining = true l.draining = true
@ -827,8 +787,6 @@ func (l *loopyWriter) handle(i interface{}) error {
return l.registerStreamHandler(i) return l.registerStreamHandler(i)
case *cleanupStream: case *cleanupStream:
return l.cleanupStreamHandler(i) return l.cleanupStreamHandler(i)
case *earlyAbortStream:
return l.earlyAbortStreamHandler(i)
case *incomingGoAway: case *incomingGoAway:
return l.incomingGoAwayHandler(i) return l.incomingGoAwayHandler(i)
case *dataFrame: case *dataFrame:

View File

@ -136,10 +136,12 @@ 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) { func (f *inFlow) newLimit(n uint32) uint32 {
f.mu.Lock() f.mu.Lock()
d := n - f.limit
f.limit = n f.limit = n
f.mu.Unlock() f.mu.Unlock()
return d
} }
func (f *inFlow) maybeAdjust(n uint32) uint32 { func (f *inFlow) maybeAdjust(n uint32) uint32 {

View File

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

View File

@ -24,8 +24,6 @@ import (
"io" "io"
"math" "math"
"net" "net"
"net/http"
"path/filepath"
"strconv" "strconv"
"strings" "strings"
"sync" "sync"
@ -34,14 +32,15 @@ import (
"golang.org/x/net/http2" "golang.org/x/net/http2"
"golang.org/x/net/http2/hpack" "golang.org/x/net/http2/hpack"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/internal/channelz"
icredentials "google.golang.org/grpc/internal/credentials"
"google.golang.org/grpc/internal/grpcutil" "google.golang.org/grpc/internal/grpcutil"
imetadata "google.golang.org/grpc/internal/metadata" imetadata "google.golang.org/grpc/internal/metadata"
"google.golang.org/grpc/internal/syscall"
"google.golang.org/grpc/internal/transport/networktype" "google.golang.org/grpc/internal/transport/networktype"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/internal"
"google.golang.org/grpc/internal/channelz"
"google.golang.org/grpc/internal/syscall"
"google.golang.org/grpc/keepalive" "google.golang.org/grpc/keepalive"
"google.golang.org/grpc/metadata" "google.golang.org/grpc/metadata"
"google.golang.org/grpc/peer" "google.golang.org/grpc/peer"
@ -117,9 +116,6 @@ type http2Client struct {
// goAwayReason records the http2.ErrCode and debug data received with the // goAwayReason records the http2.ErrCode and debug data received with the
// GoAway frame. // GoAway frame.
goAwayReason GoAwayReason goAwayReason GoAwayReason
// goAwayDebugMessage contains a detailed human readable string about a
// GoAway frame, useful for error messages.
goAwayDebugMessage string
// A condition variable used to signal when the keepalive goroutine should // A condition variable used to signal when the keepalive goroutine should
// go dormant. The condition for dormancy is based on the number of active // go dormant. The condition for dormancy is based on the number of active
// streams and the `PermitWithoutStream` keepalive client parameter. And // streams and the `PermitWithoutStream` keepalive client parameter. And
@ -147,21 +143,14 @@ 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") {
// Supported unix targets are either "unix://absolute-path" or // For backward compatibility, if the user dialed "unix:///path",
// "unix:relative-path". // the passthrough resolver would be used and the user's custom
if filepath.IsAbs(address) { // dialer would see "unix:///path". Since the unix resolver is used
// 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 {
@ -201,12 +190,6 @@ 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 {
@ -251,15 +234,13 @@ func newHTTP2Client(connectCtx, ctx context.Context, addr resolver.Address, opts
} }
} }
if transportCreds != nil { if transportCreds != nil {
rawConn := conn // gRPC, resolver, balancer etc. can specify arbitrary data in the
// Pull the deadline from the connectCtx, which will be used for // Attributes field of resolver.Address, which is shoved into connectCtx
// timeouts in the authentication protocol handshake. Can ignore the // and passed to the credential handshaker. This makes it possible for
// boolean as the deadline will return the zero value, which will make // address specific arbitrary data to reach the credential handshaker.
// the conn not timeout on I/O operations. contextWithHandshakeInfo := internal.NewClientHandshakeInfoContext.(func(context.Context, credentials.ClientHandshakeInfo) context.Context)
deadline, _ := connectCtx.Deadline() connectCtx = contextWithHandshakeInfo(connectCtx, credentials.ClientHandshakeInfo{Attributes: addr.Attributes})
rawConn.SetDeadline(deadline) conn, authInfo, err = transportCreds.ClientHandshake(connectCtx, addr.ServerName, conn)
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)
} }
@ -366,14 +347,12 @@ func newHTTP2Client(connectCtx, ctx context.Context, addr resolver.Address, opts
// Send connection preface to server. // Send connection preface to server.
n, err := t.conn.Write(clientPreface) n, err := t.conn.Write(clientPreface)
if err != nil { if err != nil {
err = connectionErrorf(true, err, "transport: failed to write client preface: %v", err) t.Close()
t.Close(err) return nil, connectionErrorf(true, err, "transport: failed to write client preface: %v", err)
return nil, err
} }
if n != len(clientPreface) { if n != len(clientPreface) {
err = connectionErrorf(true, nil, "transport: preface mismatch, wrote %d bytes; want %d", n, len(clientPreface)) t.Close()
t.Close(err) return nil, connectionErrorf(true, err, "transport: preface mismatch, wrote %d bytes; want %d", n, len(clientPreface))
return nil, err
} }
var ss []http2.Setting var ss []http2.Setting
@ -391,16 +370,14 @@ func newHTTP2Client(connectCtx, ctx context.Context, addr resolver.Address, opts
} }
err = t.framer.fr.WriteSettings(ss...) err = t.framer.fr.WriteSettings(ss...)
if err != nil { if err != nil {
err = connectionErrorf(true, err, "transport: failed to write initial settings frame: %v", err) t.Close()
t.Close(err) return nil, connectionErrorf(true, err, "transport: failed to write initial settings frame: %v", err)
return nil, err
} }
// Adjust the connection flow control window if needed. // Adjust the connection flow control window if needed.
if delta := uint32(icwz - defaultWindowSize); delta > 0 { if delta := uint32(icwz - defaultWindowSize); delta > 0 {
if err := t.framer.fr.WriteWindowUpdate(0, delta); err != nil { if err := t.framer.fr.WriteWindowUpdate(0, delta); err != nil {
err = connectionErrorf(true, err, "transport: failed to write window update: %v", err) t.Close()
t.Close(err) return nil, connectionErrorf(true, err, "transport: failed to write window update: %v", err)
return nil, err
} }
} }
@ -417,10 +394,11 @@ 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)
} }
} }
// Do not close the transport. Let reader goroutine handle it since // If it's a connection error, let reader goroutine handle it
// there might be data in the buffers. // since 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
@ -436,7 +414,6 @@ func (t *http2Client) newStream(ctx context.Context, callHdr *CallHdr) *Stream {
buf: newRecvBuffer(), buf: newRecvBuffer(),
headerChan: make(chan struct{}), headerChan: make(chan struct{}),
contentSubtype: callHdr.ContentSubtype, contentSubtype: callHdr.ContentSubtype,
doneFunc: callHdr.DoneFunc,
} }
s.wq = newWriteQuota(defaultWriteQuota, s.done) s.wq = newWriteQuota(defaultWriteQuota, s.done)
s.requestRead = func(n int) { s.requestRead = func(n int) {
@ -476,7 +453,7 @@ func (t *http2Client) createHeaderFields(ctx context.Context, callHdr *CallHdr)
Method: callHdr.Method, Method: callHdr.Method,
AuthInfo: t.authInfo, AuthInfo: t.authInfo,
} }
ctxWithRequestInfo := icredentials.NewRequestInfoContext(ctx, ri) ctxWithRequestInfo := internal.NewRequestInfoContext.(func(context.Context, credentials.RequestInfo) context.Context)(ctx, ri)
authData, err := t.getTrAuthData(ctxWithRequestInfo, aud) authData, err := t.getTrAuthData(ctxWithRequestInfo, aud)
if err != nil { if err != nil {
return nil, err return nil, err
@ -588,7 +565,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: per-RPC creds failed due to error: %v", err) return nil, status.Errorf(codes.Unauthenticated, "transport: %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.
@ -625,35 +602,26 @@ func (t *http2Client) getCallAuthData(ctx context.Context, audience string, call
return callAuthData, nil return callAuthData, nil
} }
// NewStreamError wraps an error and reports additional information. Typically // PerformedIOError wraps an error to indicate IO may have been performed
// NewStream errors result in transparent retry, as they mean nothing went onto // before the error occurred.
// the wire. However, there are two notable exceptions: type PerformedIOError struct {
//
// 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
} }
func (e NewStreamError) Error() string { // Error implements error.
return e.Err.Error() func (p PerformedIOError) Error() string {
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. All non-nil errors returned will be *NewStreamError. // streams.
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 {
return nil, &NewStreamError{Err: err, DoNotTransparentRetry: true} // We may have performed I/O in the per-RPC creds callback, so do not
// 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) {
@ -753,23 +721,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, &NewStreamError{Err: err} return nil, err
} }
if success { if success {
break break
} }
if hdrListSizeErr != nil { if hdrListSizeErr != nil {
return nil, &NewStreamError{Err: hdrListSizeErr, DoNotRetry: true} return nil, hdrListSizeErr
} }
firstTry = false firstTry = false
select { select {
case <-ch: case <-ch:
case <-ctx.Done(): case <-s.ctx.Done():
return nil, &NewStreamError{Err: ContextErr(ctx.Err())} return nil, ContextErr(s.ctx.Err())
case <-t.goAway: case <-t.goAway:
return nil, &NewStreamError{Err: errStreamDrain} return nil, errStreamDrain
case <-t.ctx.Done(): case <-t.ctx.Done():
return nil, &NewStreamError{Err: ErrConnClosing} return nil, ErrConnClosing
} }
} }
if t.statsHandler != nil { if t.statsHandler != nil {
@ -864,9 +832,6 @@ func (t *http2Client) closeStream(s *Stream, err error, rst bool, rstCode http2.
t.controlBuf.executeAndPut(addBackStreamQuota, cleanup) t.controlBuf.executeAndPut(addBackStreamQuota, cleanup)
// This will unblock write. // This will unblock write.
close(s.done) close(s.done)
if s.doneFunc != nil {
s.doneFunc()
}
} }
// Close kicks off the shutdown process of the transport. This should be called // Close kicks off the shutdown process of the transport. This should be called
@ -876,12 +841,12 @@ func (t *http2Client) closeStream(s *Stream, err error, rst bool, rstCode http2.
// This method blocks until the addrConn that initiated this transport is // This method blocks until the addrConn that initiated this transport is
// re-connected. This happens because t.onClose() begins reconnect logic at the // re-connected. This happens because t.onClose() begins reconnect logic at the
// addrConn level and blocks until the addrConn is successfully connected. // addrConn level and blocks until the addrConn is successfully connected.
func (t *http2Client) Close(err error) { func (t *http2Client) Close() error {
t.mu.Lock() t.mu.Lock()
// Make sure we only Close once. // Make sure we only Close once.
if t.state == closing { if t.state == closing {
t.mu.Unlock() t.mu.Unlock()
return return nil
} }
// Call t.onClose before setting the state to closing to prevent the client // Call t.onClose before setting the state to closing to prevent the client
// from attempting to create new streams ASAP. // from attempting to create new streams ASAP.
@ -897,25 +862,13 @@ func (t *http2Client) Close(err error) {
t.mu.Unlock() t.mu.Unlock()
t.controlBuf.finish() t.controlBuf.finish()
t.cancel() t.cancel()
t.conn.Close() err := t.conn.Close()
if channelz.IsOn() { if channelz.IsOn() {
channelz.RemoveEntry(t.channelzID) channelz.RemoveEntry(t.channelzID)
} }
// 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.
_, goAwayDebugMessage := t.GetGoAwayReason()
var st *status.Status
if len(goAwayDebugMessage) > 0 {
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, st, nil, false) t.closeStream(s, ErrConnClosing, false, http2.ErrCodeNo, status.New(codes.Unavailable, ErrConnClosing.Desc), nil, false)
} }
if t.statsHandler != nil { if t.statsHandler != nil {
connEnd := &stats.ConnEnd{ connEnd := &stats.ConnEnd{
@ -923,6 +876,7 @@ func (t *http2Client) Close(err error) {
} }
t.statsHandler.HandleConn(t.ctx, connEnd) t.statsHandler.HandleConn(t.ctx, connEnd)
} }
return err
} }
// GracefulClose sets the state to draining, which prevents new streams from // GracefulClose sets the state to draining, which prevents new streams from
@ -941,7 +895,7 @@ func (t *http2Client) GracefulClose() {
active := len(t.activeStreams) active := len(t.activeStreams)
t.mu.Unlock() t.mu.Unlock()
if active == 0 { if active == 0 {
t.Close(ErrConnClosing) t.Close()
return return
} }
t.controlBuf.put(&incomingGoAway{}) t.controlBuf.put(&incomingGoAway{})
@ -1082,7 +1036,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.StreamEnded() { if f.FrameHeader.Flags.Has(http2.FlagDataEndStream) {
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)
} }
} }
@ -1187,9 +1141,9 @@ func (t *http2Client) handleGoAway(f *http2.GoAwayFrame) {
} }
} }
id := f.LastStreamID id := f.LastStreamID
if id > 0 && id%2 == 0 { if id > 0 && id%2 != 1 {
t.mu.Unlock() t.mu.Unlock()
t.Close(connectionErrorf(true, nil, "received goaway with non-zero even-numbered numbered stream id: %v", id)) t.Close()
return return
} }
// A client can receive multiple GoAways from the server (see // A client can receive multiple GoAways from the server (see
@ -1207,7 +1161,7 @@ func (t *http2Client) handleGoAway(f *http2.GoAwayFrame) {
// If there are multiple GoAways the first one should always have an ID greater than the following ones. // If there are multiple GoAways the first one should always have an ID greater than the following ones.
if id > t.prevGoAwayID { if id > t.prevGoAwayID {
t.mu.Unlock() t.mu.Unlock()
t.Close(connectionErrorf(true, nil, "received goaway with stream id: %v, which exceeds stream id of previous goaway: %v", id, t.prevGoAwayID)) t.Close()
return return
} }
default: default:
@ -1237,7 +1191,7 @@ func (t *http2Client) handleGoAway(f *http2.GoAwayFrame) {
active := len(t.activeStreams) active := len(t.activeStreams)
t.mu.Unlock() t.mu.Unlock()
if active == 0 { if active == 0 {
t.Close(connectionErrorf(true, nil, "received goaway and there are no active streams")) t.Close()
} }
} }
@ -1253,17 +1207,12 @@ func (t *http2Client) setGoAwayReason(f *http2.GoAwayFrame) {
t.goAwayReason = GoAwayTooManyPings t.goAwayReason = GoAwayTooManyPings
} }
} }
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 {
t.mu.Lock() t.mu.Lock()
defer t.mu.Unlock() defer t.mu.Unlock()
return t.goAwayReason, t.goAwayDebugMessage return t.goAwayReason
} }
func (t *http2Client) handleWindowUpdate(f *http2.WindowUpdateFrame) { func (t *http2Client) handleWindowUpdate(f *http2.WindowUpdateFrame) {
@ -1290,128 +1239,35 @@ func (t *http2Client) operateHeaders(frame *http2.MetaHeadersFrame) {
return return
} }
// frame.Truncated is set to true when framer detects that the current header state := &decodeState{}
// list size hits MaxHeaderListSize limit. // 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.
if frame.Truncated { state.data.isGRPC = !initialHeader
se := status.New(codes.Internal, "peer header list size exceeded limit") if h2code, err := state.decodeHeader(frame); err != nil {
t.closeStream(s, se.Err(), true, http2.ErrCodeFrameSize, se, nil, endStream) t.closeStream(s, err, true, h2code, status.Convert(err), 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) {
@ -1422,9 +1278,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 = recvCompress s.recvCompress = state.data.encoding
if len(mdata) > 0 { if len(state.data.mdata) > 0 {
s.header = mdata s.header = state.data.mdata
} }
} else { } else {
// HEADERS frame block carries a Trailers-Only. // HEADERS frame block carries a Trailers-Only.
@ -1433,36 +1289,13 @@ 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, statusGen, mdata, true) t.closeStream(s, io.EOF, rst, http2.ErrCodeNo, state.status(), state.data.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
@ -1476,8 +1309,7 @@ func (t *http2Client) reader() {
// Check the validity of server preface. // Check the validity of server preface.
frame, err := t.framer.fr.ReadFrame() frame, err := t.framer.fr.ReadFrame()
if err != nil { if err != nil {
err = connectionErrorf(true, err, "error reading server preface: %v", err) t.Close() // this kicks off resetTransport, so must be last before return
t.Close(err) // this kicks off resetTransport, so must be last before return
return return
} }
t.conn.SetReadDeadline(time.Time{}) // reset deadline once we get the settings frame (we didn't time out, yay!) t.conn.SetReadDeadline(time.Time{}) // reset deadline once we get the settings frame (we didn't time out, yay!)
@ -1486,8 +1318,7 @@ func (t *http2Client) reader() {
} }
sf, ok := frame.(*http2.SettingsFrame) sf, ok := frame.(*http2.SettingsFrame)
if !ok { if !ok {
// this kicks off resetTransport, so must be last before return t.Close() // this kicks off resetTransport, so must be last before return
t.Close(connectionErrorf(true, nil, "initial http2 frame from server is not a settings frame: %T", frame))
return return
} }
t.onPrefaceReceipt() t.onPrefaceReceipt()
@ -1523,7 +1354,7 @@ func (t *http2Client) reader() {
continue continue
} else { } else {
// Transport error. // Transport error.
t.Close(connectionErrorf(true, err, "error reading from server: %v", err)) t.Close()
return return
} }
} }
@ -1557,7 +1388,7 @@ func minTime(a, b time.Duration) time.Duration {
return b return b
} }
// keepalive running in a separate goroutine makes sure the connection is alive by sending pings. // keepalive running in a separate goroutune 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.
@ -1582,7 +1413,7 @@ func (t *http2Client) keepalive() {
continue continue
} }
if outstandingPing && timeoutLeft <= 0 { if outstandingPing && timeoutLeft <= 0 {
t.Close(connectionErrorf(true, nil, "keepalive ping failed to receive ACK within timeout")) t.Close()
return return
} }
t.mu.Lock() t.mu.Lock()

View File

@ -26,7 +26,6 @@ import (
"io" "io"
"math" "math"
"net" "net"
"net/http"
"strconv" "strconv"
"sync" "sync"
"sync/atomic" "sync/atomic"
@ -73,6 +72,7 @@ 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
@ -101,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
@ -122,37 +122,11 @@ 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
} }
// NewServerTransport creates a http2 transport with conn and configuration // newHTTP2Server constructs a ServerTransport based on HTTP2. ConnectionError is
// options from config. // returned if something goes wrong.
// 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
@ -235,15 +209,14 @@ func NewServerTransport(conn net.Conn, config *ServerConfig) (_ ServerTransport,
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: setConnection(context.Background(), rawConn), ctx: context.Background(),
done: done, done: done,
conn: conn, conn: conn,
remoteAddr: conn.RemoteAddr(), remoteAddr: conn.RemoteAddr(),
localAddr: conn.LocalAddr(), localAddr: conn.LocalAddr(),
authInfo: authInfo, authInfo: config.AuthInfo,
framer: framer, framer: framer,
readerDone: make(chan struct{}), readerDone: make(chan struct{}),
writerDone: make(chan struct{}), writerDone: make(chan struct{}),
@ -292,14 +265,6 @@ func NewServerTransport(conn net.Conn, config *ServerConfig) (_ ServerTransport,
// 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) {
@ -329,7 +294,6 @@ func NewServerTransport(conn net.Conn, config *ServerConfig) (_ ServerTransport,
} }
} }
t.conn.Close() t.conn.Close()
t.controlBuf.finish()
close(t.writerDone) close(t.writerDone)
}() }()
go t.keepalive() go t.keepalive()
@ -338,144 +302,38 @@ func NewServerTransport(conn net.Conn, config *ServerConfig) (_ ServerTransport,
// 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{
// frame.Truncated is set to true when framer detects that the current header serverSide: true,
// list size hits MaxHeaderListSize limit. }
if frame.Truncated { if h2code, err := state.decodeHeader(frame); err != nil {
if _, ok := status.FromError(err); ok {
t.controlBuf.put(&cleanupStream{ t.controlBuf.put(&cleanupStream{
streamID: streamID, streamID: streamID,
rst: true, rst: true,
rstCode: http2.ErrCodeFrameSize, rstCode: h2code,
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 timeoutSet { if state.data.timeoutSet {
s.ctx, s.cancel = context.WithTimeout(t.ctx, timeout) s.ctx, s.cancel = context.WithTimeout(t.ctx, state.data.timeout)
} else { } else {
s.ctx, s.cancel = context.WithCancel(t.ctx) s.ctx, s.cancel = context.WithCancel(t.ctx)
} }
@ -488,13 +346,33 @@ 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(mdata) > 0 { if len(state.data.mdata) > 0 {
s.ctx = metadata.NewIncomingContext(s.ctx, mdata) s.ctx = metadata.NewIncomingContext(s.ctx, state.data.mdata)
if statsTags := mdata["grpc-tags-bin"]; len(statsTags) > 0 {
s.ctx = stats.SetIncomingTags(s.ctx, []byte(statsTags[len(statsTags)-1]))
} }
if statsTrace := mdata["grpc-trace-bin"]; len(statsTrace) > 0 { if state.data.statsTags != nil {
s.ctx = stats.SetIncomingTrace(s.ctx, []byte(statsTrace[len(statsTrace)-1])) s.ctx = stats.SetIncomingTags(s.ctx, state.data.statsTags)
}
if state.data.statsTrace != nil {
s.ctx = stats.SetIncomingTrace(s.ctx, state.data.statsTrace)
}
if t.inTapHandle != nil {
var err error
info := &tap.Info{
FullMethodName: state.data.method,
}
s.ctx, err = t.inTapHandle(s.ctx, info)
if err != nil {
if logger.V(logLevel) {
logger.Warningf("transport: http2Server.operateHeaders got an error from InTapHandle: %v", err)
}
t.controlBuf.put(&cleanupStream{
streamID: s.id,
rst: true,
rstCode: http2.ErrCodeRefusedStream,
onWrite: func() {},
})
s.cancel()
return false
} }
} }
t.mu.Lock() t.mu.Lock()
@ -514,40 +392,16 @@ func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func(
s.cancel() s.cancel()
return false return false
} }
if httpMethod != http.MethodPost { if streamID%2 != 1 || streamID <= t.maxStreamID {
t.mu.Unlock() t.mu.Unlock()
// illegal gRPC stream id.
if logger.V(logLevel) { if logger.V(logLevel) {
logger.Infof("transport: http2Server.operateHeaders parsed a :method field: %v which should be POST", httpMethod) logger.Errorf("transport: http2Server.HandleStreams received an illegal stream id: %v", streamID)
} }
t.controlBuf.put(&cleanupStream{
streamID: streamID,
rst: true,
rstCode: http2.ErrCodeProtocol,
onWrite: func() {},
})
s.cancel() s.cancel()
return false return true
}
if t.inTapHandle != nil {
var err error
if s.ctx, err = t.inTapHandle(s.ctx, &tap.Info{FullMethodName: s.method}); err != nil {
t.mu.Unlock()
if logger.V(logLevel) {
logger.Infof("transport: http2Server.operateHeaders got an error from InTapHandle: %v", err)
}
stat, ok := status.FromError(err)
if !ok {
stat = status.New(codes.PermissionDenied, err.Error())
}
t.controlBuf.put(&earlyAbortStream{
httpStatus: 200,
streamID: s.id,
contentSubtype: s.contentSubtype,
status: stat,
})
return false
}
} }
t.maxStreamID = streamID
t.activeStreams[streamID] = s t.activeStreams[streamID] = s
if len(t.activeStreams) == 1 { if len(t.activeStreams) == 1 {
t.idle = time.Time{} t.idle = time.Time{}
@ -569,7 +423,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(mdata).Copy(), Header: metadata.MD(state.data.mdata).Copy(),
} }
t.stats.HandleRPC(s.ctx, inHeader) t.stats.HandleRPC(s.ctx, inHeader)
} }
@ -781,7 +635,7 @@ func (t *http2Server) handleData(f *http2.DataFrame) {
s.write(recvMsg{buffer: buffer}) s.write(recvMsg{buffer: buffer})
} }
} }
if f.StreamEnded() { if f.Header().Flags.Has(http2.FlagDataEndStream) {
// 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})
@ -1136,12 +990,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() t.drain(http2.ErrCodeNo, []byte{})
return return
} }
idleTimer.Reset(val) idleTimer.Reset(val)
case <-ageTimer.C: case <-ageTimer.C:
t.Drain() t.drain(http2.ErrCodeNo, []byte{})
ageTimer.Reset(t.kp.MaxConnectionAgeGrace) ageTimer.Reset(t.kp.MaxConnectionAgeGrace)
select { select {
case <-ageTimer.C: case <-ageTimer.C:
@ -1195,11 +1049,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() { func (t *http2Server) Close() error {
t.mu.Lock() t.mu.Lock()
if t.state == closing { if t.state == closing {
t.mu.Unlock() t.mu.Unlock()
return return errors.New("transport: Close() was already called")
} }
t.state = closing t.state = closing
streams := t.activeStreams streams := t.activeStreams
@ -1207,9 +1061,7 @@ func (t *http2Server) Close() {
t.mu.Unlock() t.mu.Unlock()
t.controlBuf.finish() t.controlBuf.finish()
close(t.done) close(t.done)
if err := t.conn.Close(); err != nil && logger.V(logLevel) { err := t.conn.Close()
logger.Infof("transport: error closing conn during Close: %v", err)
}
if channelz.IsOn() { if channelz.IsOn() {
channelz.RemoveEntry(t.channelzID) channelz.RemoveEntry(t.channelzID)
} }
@ -1221,6 +1073,7 @@ func (t *http2Server) Close() {
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.
@ -1285,13 +1138,17 @@ 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: http2.ErrCodeNo, debugData: []byte{}, headsUp: true}) t.controlBuf.put(&goAway{code: code, debugData: debugData, 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}}
@ -1299,23 +1156,20 @@ 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
} }
@ -1328,7 +1182,6 @@ 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
@ -1413,18 +1266,3 @@ func getJitter(v time.Duration) time.Duration {
j := grpcrand.Int63n(2*r) - r j := grpcrand.Int63n(2*r) - r
return time.Duration(j) return time.Duration(j)
} }
type connectionKey struct{}
// GetConnection gets the connection from the context.
func GetConnection(ctx context.Context) net.Conn {
conn, _ := ctx.Value(connectionKey{}).(net.Conn)
return conn
}
// SetConnection adds the connection to the context to be able to get
// information about the destination ip and port for an incoming RPC. This also
// allows any unary or streaming interceptors to see the connection.
func setConnection(ctx context.Context, conn net.Conn) context.Context {
return context.WithValue(ctx, connectionKey{}, conn)
}

View File

@ -39,6 +39,7 @@ 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"
) )
@ -95,6 +96,52 @@ 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
// 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.
@ -132,6 +179,14 @@ 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 {
@ -161,16 +216,166 @@ func decodeMetadataHeader(k, v string) (string, error) {
return v, nil return v, nil
} }
func decodeGRPCStatusDetails(rawDetails string) (*status.Status, error) { func (d *decodeState) decodeHeader(frame *http2.MetaHeadersFrame) (http2.ErrCode, error) {
v, err := decodeBinHeader(rawDetails) // frame.Truncated is set to true when framer detects that the current header
// 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 {
return nil, err d.data.grpcErr = status.Errorf(codes.Internal, "transport: malformed grpc-status: %v", err)
return
} }
st := &spb.Status{} d.data.rawStatusCode = &code
if err = proto.Unmarshal(v, st); err != nil { case "grpc-message":
return nil, err d.data.rawStatusMsg = decodeGrpcMessage(f.Value)
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))
default:
if isReservedHeader(f.Name) && !isWhitelistedHeader(f.Name) {
break
}
v, err := decodeMetadataHeader(f.Name, f.Value)
if err != nil {
if logger.V(logLevel) {
logger.Errorf("Failed to decode metadata header (%q, %q): %v", f.Name, f.Value, err)
}
return
}
d.addMetadata(f.Name, v)
} }
return status.FromProto(st), nil
} }
type timeoutUnit uint8 type timeoutUnit uint8

View File

@ -17,7 +17,7 @@
*/ */
// Package networktype declares the network type to be used in the default // Package networktype declares the network type to be used in the default
// dialer. Attribute of a resolver.Address. // dailer. 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.WithValue(key, networkType) address.Attributes = address.Attributes.WithValues(key, networkType)
return address return address
} }

View File

@ -37,7 +37,7 @@ var (
httpProxyFromEnvironment = http.ProxyFromEnvironment httpProxyFromEnvironment = http.ProxyFromEnvironment
) )
func mapAddress(address string) (*url.URL, error) { func mapAddress(ctx context.Context, 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(addr) proxyURL, err := mapAddress(ctx, addr)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -30,7 +30,6 @@ 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"
@ -242,7 +241,6 @@ type Stream struct {
ctx context.Context // the associated context of the stream ctx context.Context // the associated context of the stream
cancel context.CancelFunc // always nil for client side Stream cancel context.CancelFunc // always nil for client side Stream
done chan struct{} // closed at the end of stream to unblock writers. On the client side. done chan struct{} // closed at the end of stream to unblock writers. On the client side.
doneFunc func() // invoked at the end of stream on client side.
ctxDone <-chan struct{} // same as done chan but for server side. Cache of ctx.Done() (for performance) ctxDone <-chan struct{} // same as done chan but for server side. Cache of ctx.Done() (for performance)
method string // the associated RPC method of the stream method string // the associated RPC method of the stream
recvCompress string recvCompress string
@ -519,8 +517,7 @@ 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
ConnectionTimeout time.Duration AuthInfo credentials.AuthInfo
Credentials credentials.TransportCredentials
InTapHandle tap.ServerInHandle InTapHandle tap.ServerInHandle
StatsHandler stats.Handler StatsHandler stats.Handler
KeepaliveParams keepalive.ServerParameters KeepaliveParams keepalive.ServerParameters
@ -534,6 +531,12 @@ 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.
@ -608,8 +611,6 @@ type CallHdr struct {
ContentSubtype string ContentSubtype string
PreviousAttempts int // value of grpc-previous-rpc-attempts header to set PreviousAttempts int // value of grpc-previous-rpc-attempts header to set
DoneFunc func() // called when the stream is finished
} }
// ClientTransport is the common interface for all gRPC client-side transport // ClientTransport is the common interface for all gRPC client-side transport
@ -618,7 +619,7 @@ type ClientTransport interface {
// Close tears down this transport. Once it returns, the transport // Close tears down this transport. Once it returns, the transport
// should not be accessed any more. The caller must make sure this // should not be accessed any more. The caller must make sure this
// is called only once. // is called only once.
Close(err error) Close() error
// GracefulClose starts to tear down the transport: the transport will stop // GracefulClose starts to tear down the transport: the transport will stop
// accepting new RPCs and NewStream will return error. Once all streams are // accepting new RPCs and NewStream will return error. Once all streams are
@ -652,9 +653,8 @@ type ClientTransport interface {
// HTTP/2). // HTTP/2).
GoAway() <-chan struct{} GoAway() <-chan struct{}
// GetGoAwayReason returns the reason why GoAway frame was received, along // GetGoAwayReason returns the reason why GoAway frame was received.
// with a human readable string with debug info. GetGoAwayReason() GoAwayReason
GetGoAwayReason() (GoAwayReason, string)
// RemoteAddr returns the remote network address. // RemoteAddr returns the remote network address.
RemoteAddr() net.Addr RemoteAddr() net.Addr
@ -690,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() Close() error
// RemoteAddr returns the remote network address. // RemoteAddr returns the remote network address.
RemoteAddr() net.Addr RemoteAddr() net.Addr

View File

@ -1,40 +0,0 @@
/*
* 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 internal
import (
"google.golang.org/grpc/attributes"
"google.golang.org/grpc/resolver"
)
// handshakeClusterNameKey is the type used as the key to store cluster name in
// the Attributes field of resolver.Address.
type handshakeClusterNameKey struct{}
// SetXDSHandshakeClusterName returns a copy of addr in which the Attributes field
// is updated with the cluster name.
func SetXDSHandshakeClusterName(addr resolver.Address, clusterName string) resolver.Address {
addr.Attributes = addr.Attributes.WithValue(handshakeClusterNameKey{}, clusterName)
return addr
}
// GetXDSHandshakeClusterName returns cluster name stored in attr.
func GetXDSHandshakeClusterName(attr *attributes.Attributes) (string, bool) {
v := attr.Value(handshakeClusterNameKey{})
name, ok := v.(string)
return name, ok
}

View File

@ -75,9 +75,13 @@ func Pairs(kv ...string) MD {
panic(fmt.Sprintf("metadata: Pairs got the odd number of input pairs for metadata: %d", len(kv))) panic(fmt.Sprintf("metadata: Pairs got the odd number of input pairs for metadata: %d", len(kv)))
} }
md := MD{} md := MD{}
for i := 0; i < len(kv); i += 2 { var key string
key := strings.ToLower(kv[i]) for i, s := range kv {
md[key] = append(md[key], kv[i+1]) if i%2 == 0 {
key = strings.ToLower(s)
continue
}
md[key] = append(md[key], s)
} }
return md return md
} }
@ -93,16 +97,12 @@ 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
@ -111,10 +111,7 @@ 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 // Append adds the values to key k, not overwriting what was already stored at that key.
// 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
@ -123,17 +120,9 @@ 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 order of values for each key is determined by the order in which the mds // the mds containing those values are presented to Join.
// 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 {
@ -160,8 +149,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 documentation // with any existing metadata in the context. Please refer to the
// of Pairs for a description of kv. // documentation 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)))
@ -174,34 +163,20 @@ 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. // FromIncomingContext returns the incoming metadata in ctx if it exists. The
// // returned MD should not be modified. Writing to it may cause races.
// All keys in the returned MD are lowercase. // Modification should be made to copies of the returned MD.
func FromIncomingContext(ctx context.Context) (MD, bool) { func FromIncomingContext(ctx context.Context) (md MD, ok bool) {
md, ok := ctx.Value(mdIncomingKey{}).(MD) md, ok = ctx.Value(mdIncomingKey{}).(MD)
if !ok { return
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 of rawMD. // FromOutgoingContextRaw returns the un-merged, intermediary contents
// 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.
// //
// Remember to perform strings.ToLower on the keys, for both the returned MD (MD // This is intended for gRPC-internal use ONLY.
// 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 {
@ -211,34 +186,21 @@ 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. // FromOutgoingContext returns the outgoing metadata in ctx if it exists. The
// // returned MD should not be modified. Writing to it may cause races.
// All keys in the returned MD are lowercase. // Modification should be made to copies of the returned MD.
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 := MD{} mds := make([]MD, 0, len(raw.added)+1)
for k, v := range raw.md { mds = append(mds, raw.md)
// We need to manually convert all keys to lower case, because MD is a for _, vv := range raw.added {
// map, and there's no guarantee that the MD attached to the context is mds = append(mds, Pairs(vv...))
// created using our helper functions.
key := strings.ToLower(k)
out[key] = v
} }
for _, added := range raw.added { return Join(mds...), ok
if len(added)%2 == 1 {
panic(fmt.Sprintf("metadata: FromOutgoingContext got an odd number of input pairs for metadata: %d", len(added)))
}
for i := 0; i < len(added); i += 2 {
key := strings.ToLower(added[i])
out[key] = append(out[key], added[i+1])
}
}
return out, ok
} }
type rawMD struct { type rawMD struct {

View File

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

View File

@ -84,7 +84,7 @@ func (b *pickfirstBalancer) UpdateClientConnState(cs balancer.ClientConnState) e
b.cc.UpdateState(balancer.State{ConnectivityState: connectivity.Idle, Picker: &picker{result: balancer.PickResult{SubConn: b.sc}}}) b.cc.UpdateState(balancer.State{ConnectivityState: connectivity.Idle, Picker: &picker{result: balancer.PickResult{SubConn: b.sc}}})
b.sc.Connect() b.sc.Connect()
} else { } else {
b.cc.UpdateAddresses(b.sc, cs.ResolverState.Addresses) b.sc.UpdateAddresses(cs.ResolverState.Addresses)
b.sc.Connect() b.sc.Connect()
} }
return nil return nil
@ -107,12 +107,10 @@ func (b *pickfirstBalancer) UpdateSubConnState(sc balancer.SubConn, s balancer.S
} }
switch s.ConnectivityState { switch s.ConnectivityState {
case connectivity.Ready: case connectivity.Ready, connectivity.Idle:
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,
@ -124,12 +122,6 @@ 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
@ -139,17 +131,6 @@ func (p *picker) Pick(info balancer.PickInfo) (balancer.PickResult, error) {
return p.result, p.err return p.result, p.err
} }
// idlePicker is used when the SubConn is IDLE and kicks the SubConn into
// CONNECTING when Pick is called.
type idlePicker struct {
sc balancer.SubConn
}
func (i *idlePicker) Pick(info balancer.PickInfo) (balancer.PickResult, error) {
i.sc.Connect()
return balancer.PickResult{}, balancer.ErrNoSubConnAvailable
}
func init() { func init() {
balancer.Register(newPickfirstBuilder()) balancer.Register(newPickfirstBuilder())
} }

View File

@ -48,6 +48,11 @@ mkdir -p ${WORKDIR}/googleapis/google/rpc
echo "curl https://raw.githubusercontent.com/googleapis/googleapis/master/google/rpc/code.proto" echo "curl https://raw.githubusercontent.com/googleapis/googleapis/master/google/rpc/code.proto"
curl --silent https://raw.githubusercontent.com/googleapis/googleapis/master/google/rpc/code.proto > ${WORKDIR}/googleapis/google/rpc/code.proto curl --silent https://raw.githubusercontent.com/googleapis/googleapis/master/google/rpc/code.proto > ${WORKDIR}/googleapis/google/rpc/code.proto
# Pull in the MeshCA service proto.
mkdir -p ${WORKDIR}/istio/istio/google/security/meshca/v1
echo "curl https://raw.githubusercontent.com/istio/istio/master/security/proto/providers/google/meshca.proto"
curl --silent https://raw.githubusercontent.com/istio/istio/master/security/proto/providers/google/meshca.proto > ${WORKDIR}/istio/istio/google/security/meshca/v1/meshca.proto
mkdir -p ${WORKDIR}/out mkdir -p ${WORKDIR}/out
# Generates sources without the embed requirement # Generates sources without the embed requirement
@ -71,26 +76,13 @@ SOURCES=(
${WORKDIR}/grpc-proto/grpc/service_config/service_config.proto ${WORKDIR}/grpc-proto/grpc/service_config/service_config.proto
${WORKDIR}/grpc-proto/grpc/testing/*.proto ${WORKDIR}/grpc-proto/grpc/testing/*.proto
${WORKDIR}/grpc-proto/grpc/core/*.proto ${WORKDIR}/grpc-proto/grpc/core/*.proto
${WORKDIR}/istio/istio/google/security/meshca/v1/meshca.proto
) )
# 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}"
@ -99,6 +91,7 @@ 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
@ -109,13 +102,14 @@ 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/internal/proto/grpc_lookup_v1 mkdir -p ${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 mv ${WORKDIR}/out/google.golang.org/grpc/lookup/grpc_lookup_v1/* ${WORKDIR}/out/google.golang.org/grpc/balancer/rls/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.
@ -128,4 +122,8 @@ mv ${WORKDIR}/out/grpc/service_config/service_config.pb.go internal/proto/grpc_s
mv ${WORKDIR}/out/grpc/testing/*.pb.go interop/grpc_testing/ mv ${WORKDIR}/out/grpc/testing/*.pb.go interop/grpc_testing/
mv ${WORKDIR}/out/grpc/core/*.pb.go interop/grpc_testing/core/ mv ${WORKDIR}/out/grpc/core/*.pb.go interop/grpc_testing/core/
# istio/google/security/meshca/v1/meshca.proto does not have a go_package option.
mkdir -p ${WORKDIR}/out/google.golang.org/grpc/credentials/tls/certprovider/meshca/internal/v1/
mv ${WORKDIR}/out/istio/google/security/meshca/v1/* ${WORKDIR}/out/google.golang.org/grpc/credentials/tls/certprovider/meshca/internal/v1/
cp -R ${WORKDIR}/out/google.golang.org/grpc/* . cp -R ${WORKDIR}/out/google.golang.org/grpc/* .

View File

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

View File

@ -23,7 +23,6 @@ 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"
@ -117,14 +116,9 @@ 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 SubConn. // consumption by the load balancing policy.
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.
@ -137,15 +131,6 @@ 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 {
@ -196,7 +181,7 @@ type State struct {
// gRPC to add new methods to this interface. // gRPC to add new methods to this interface.
type ClientConn interface { type ClientConn interface {
// UpdateState updates the state of the ClientConn appropriately. // UpdateState updates the state of the ClientConn appropriately.
UpdateState(State) error UpdateState(State)
// ReportError notifies the ClientConn that the Resolver encountered an // ReportError notifies the ClientConn that the Resolver encountered an
// error. The ClientConn will notify the load balancer and begin calling // error. The ClientConn will notify the load balancer and begin calling
// ResolveNow on the Resolver with exponential backoff. // ResolveNow on the Resolver with exponential backoff.
@ -219,36 +204,25 @@ 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 // It is parsed from the target string that gets passed into Dial or DialContext by the user. And
// by the user. And gRPC passes it to the resolver and the balancer. // grpc passes it to the resolver and the balancer.
// //
// If the target follows the naming spec, and the parsed scheme is registered // If the target follows the naming spec, and the parsed scheme is registered with grpc, we will
// with gRPC, we will parse the target string according to the spec. If the // parse the target string according to the spec. e.g. "dns://some_authority/foo.bar" will be parsed
// target does not contain a scheme or if the parsed scheme is not registered // into &Target{Scheme: "dns", Authority: "some_authority", Endpoint: "foo.bar"}
// (i.e. no corresponding resolver available to resolve the endpoint), we will
// apply the default scheme, and will attempt to reparse it.
// //
// Examples: // If the target does not contain a scheme, we will apply the default scheme, and set the Target to
// be the full target string. e.g. "foo.bar" will be parsed into
// &Target{Scheme: resolver.GetDefaultScheme(), Endpoint: "foo.bar"}.
// //
// - "dns://some_authority/foo.bar" // If the parsed scheme is not registered (i.e. no corresponding resolver available to resolve the
// Target{Scheme: "dns", Authority: "some_authority", Endpoint: "foo.bar"} // endpoint), we set the Scheme to be the default scheme, and set the Endpoint to be the full target
// - "foo.bar" // string. e.g. target string "unknown_scheme://authority/endpoint" will be parsed into
// Target{Scheme: resolver.GetDefaultScheme(), Endpoint: "foo.bar"} // &Target{Scheme: resolver.GetDefaultScheme(), Endpoint: "unknown_scheme://authority/endpoint"}.
// - "unknown_scheme://authority/endpoint"
// Target{Scheme: resolver.GetDefaultScheme(), Endpoint: "unknown_scheme://authority/endpoint"}
type Target struct { type Target struct {
// Deprecated: use URL.Scheme instead.
Scheme string Scheme string
// Deprecated: use URL.Host instead.
Authority string Authority string
// Deprecated: use URL.Path or URL.Opaque instead. The latter is set when
// the former is empty.
Endpoint string Endpoint string
// URL contains the parsed dial target with an optional default scheme added
// to it if the original dial target contained no scheme or contained an
// unregistered scheme. Any query params specified in the original dial
// target can be accessed from here.
URL url.URL
} }
// Builder creates a resolver that will be used to watch name resolution updates. // Builder creates a resolver that will be used to watch name resolution updates.

View File

@ -22,6 +22,7 @@ import (
"fmt" "fmt"
"strings" "strings"
"sync" "sync"
"time"
"google.golang.org/grpc/balancer" "google.golang.org/grpc/balancer"
"google.golang.org/grpc/credentials" "google.golang.org/grpc/credentials"
@ -40,7 +41,8 @@ type ccResolverWrapper struct {
done *grpcsync.Event done *grpcsync.Event
curState resolver.State curState resolver.State
incomingMu sync.Mutex // Synchronizes all the incoming calls. pollingMu sync.Mutex
polling chan struct{}
} }
// newCCResolverWrapper uses the resolver.Builder to build a Resolver and // newCCResolverWrapper uses the resolver.Builder to build a Resolver and
@ -91,37 +93,71 @@ func (ccr *ccResolverWrapper) close() {
ccr.resolverMu.Unlock() ccr.resolverMu.Unlock()
} }
func (ccr *ccResolverWrapper) UpdateState(s resolver.State) error { // poll begins or ends asynchronous polling of the resolver based on whether
ccr.incomingMu.Lock() // err is ErrBadResolverState.
defer ccr.incomingMu.Unlock() func (ccr *ccResolverWrapper) poll(err error) {
ccr.pollingMu.Lock()
defer ccr.pollingMu.Unlock()
if err != balancer.ErrBadResolverState {
// stop polling
if ccr.polling != nil {
close(ccr.polling)
ccr.polling = nil
}
return
}
if ccr.polling != nil {
// already polling
return
}
p := make(chan struct{})
ccr.polling = p
go func() {
for i := 0; ; i++ {
ccr.resolveNow(resolver.ResolveNowOptions{})
t := time.NewTimer(ccr.cc.dopts.resolveNowBackoff(i))
select {
case <-p:
t.Stop()
return
case <-ccr.done.Done():
// Resolver has been closed.
t.Stop()
return
case <-t.C:
select {
case <-p:
return
default:
}
// Timer expired; re-resolve.
}
}
}()
}
func (ccr *ccResolverWrapper) UpdateState(s resolver.State) {
if ccr.done.HasFired() { if ccr.done.HasFired() {
return nil return
} }
channelz.Infof(logger, ccr.cc.channelzID, "ccResolverWrapper: sending update to cc: %v", s) channelz.Infof(logger, ccr.cc.channelzID, "ccResolverWrapper: sending update to cc: %v", s)
if channelz.IsOn() { if channelz.IsOn() {
ccr.addChannelzTraceEvent(s) ccr.addChannelzTraceEvent(s)
} }
ccr.curState = s ccr.curState = s
if err := ccr.cc.updateResolverState(ccr.curState, nil); err == balancer.ErrBadResolverState { ccr.poll(ccr.cc.updateResolverState(ccr.curState, nil))
return balancer.ErrBadResolverState
}
return nil
} }
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
} }
channelz.Warningf(logger, ccr.cc.channelzID, "ccResolverWrapper: reporting error to cc: %v", err) channelz.Warningf(logger, ccr.cc.channelzID, "ccResolverWrapper: reporting error to cc: %v", err)
ccr.cc.updateResolverState(resolver.State{}, err) ccr.poll(ccr.cc.updateResolverState(resolver.State{}, err))
} }
// 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
} }
@ -130,14 +166,12 @@ func (ccr *ccResolverWrapper) NewAddress(addrs []resolver.Address) {
ccr.addChannelzTraceEvent(resolver.State{Addresses: addrs, ServiceConfig: ccr.curState.ServiceConfig}) ccr.addChannelzTraceEvent(resolver.State{Addresses: addrs, ServiceConfig: ccr.curState.ServiceConfig})
} }
ccr.curState.Addresses = addrs ccr.curState.Addresses = addrs
ccr.cc.updateResolverState(ccr.curState, nil) ccr.poll(ccr.cc.updateResolverState(ccr.curState, nil))
} }
// 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
} }
@ -149,13 +183,14 @@ func (ccr *ccResolverWrapper) NewServiceConfig(sc string) {
scpr := parseServiceConfig(sc) scpr := parseServiceConfig(sc)
if scpr.Err != nil { if scpr.Err != nil {
channelz.Warningf(logger, ccr.cc.channelzID, "ccResolverWrapper: error parsing service config: %v", scpr.Err) channelz.Warningf(logger, ccr.cc.channelzID, "ccResolverWrapper: error parsing service config: %v", scpr.Err)
ccr.poll(balancer.ErrBadResolverState)
return return
} }
if channelz.IsOn() { if channelz.IsOn() {
ccr.addChannelzTraceEvent(resolver.State{Addresses: ccr.curState.Addresses, ServiceConfig: scpr}) ccr.addChannelzTraceEvent(resolver.State{Addresses: ccr.curState.Addresses, ServiceConfig: scpr})
} }
ccr.curState.ServiceConfig = scpr ccr.curState.ServiceConfig = scpr
ccr.cc.updateResolverState(ccr.curState, nil) ccr.poll(ccr.cc.updateResolverState(ccr.curState, nil))
} }
func (ccr *ccResolverWrapper) ParseServiceConfig(scJSON string) *serviceconfig.ParseResult { func (ccr *ccResolverWrapper) ParseServiceConfig(scJSON string) *serviceconfig.ParseResult {

View File

@ -258,8 +258,7 @@ 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 and the // connections or unreachable servers. If waitForReady is false, the RPC will fail
// 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
@ -430,10 +429,9 @@ func (o ContentSubtypeCallOption) before(c *callInfo) error {
} }
func (o ContentSubtypeCallOption) after(c *callInfo, attempt *csAttempt) {} func (o ContentSubtypeCallOption) after(c *callInfo, attempt *csAttempt) {}
// ForceCodec returns a CallOption that will set codec to be used for all // ForceCodec returns a CallOption that will set the given Codec to be
// request and response messages for a call. The result of calling Name() will // used for all request and response messages for a call. The result of calling
// be used as the content-subtype after converting to lowercase, unless // String() will be used as the content-subtype in a case-insensitive manner.
// CallContentSubtype is also used.
// //
// See Content-Type on // See Content-Type on
// https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md#requests for // https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md#requests for
@ -712,11 +710,13 @@ 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 after decompression larger than max (%d vs. %d)", size, maxReceiveMessageSize) return nil, status.Errorf(codes.ResourceExhausted, "grpc: received message larger than max (%d vs. %d)", size, maxReceiveMessageSize)
}
} }
return d, nil return d, nil
} }
@ -827,45 +827,33 @@ 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 {
switch err { if err == nil || err == io.EOF {
case nil, io.EOF:
return err 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 {
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())
} }
// setCallInfoCodec should only be called after CallOptions have been applied. // setCallInfoCodec should only be called after CallOptions have been applied.
func setCallInfoCodec(c *callInfo) error { func setCallInfoCodec(c *callInfo) error {
if c.codec != nil { if c.codec != nil {
// codec was already set by a CallOption; use it, but set the content // codec was already set by a CallOption; use it.
// subtype if it is not set.
if c.contentSubtype == "" {
// c.codec is a baseCodec to hide the difference between grpc.Codec and
// encoding.Codec (Name vs. String method name). We only support
// setting content subtype from encoding.Codec to avoid a behavior
// change with the deprecated version.
if ec, ok := c.codec.(encoding.Codec); ok {
c.contentSubtype = strings.ToLower(ec.Name())
}
}
return nil return nil
} }

View File

@ -57,22 +57,12 @@ import (
const ( const (
defaultServerMaxReceiveMessageSize = 1024 * 1024 * 4 defaultServerMaxReceiveMessageSize = 1024 * 1024 * 4
defaultServerMaxSendMessageSize = math.MaxInt32 defaultServerMaxSendMessageSize = math.MaxInt32
// Server transports are tracked in a map which is keyed on listener
// address. For regular gRPC traffic, connections are accepted in Serve()
// through a call to Accept(), and we use the actual listener address as key
// when we add it to the map. But for connections received through
// ServeHTTP(), we do not have a listener and hence use this dummy value.
listenerAddressForServeHTTP = "listenerAddressForServeHTTP"
) )
func init() { func init() {
internal.GetServerCredentials = func(srv *Server) credentials.TransportCredentials { internal.GetServerCredentials = func(srv *Server) credentials.TransportCredentials {
return srv.opts.creds return srv.opts.creds
} }
internal.DrainServerTransports = func(srv *Server, addr string) {
srv.drainServerTransports(addr)
}
} }
var statusOK = status.New(codes.OK, "") var statusOK = status.New(codes.OK, "")
@ -119,10 +109,7 @@ type Server struct {
mu sync.Mutex // guards following mu sync.Mutex // guards following
lis map[net.Listener]bool lis map[net.Listener]bool
// conns contains all active server transports. It is a map keyed on a conns map[transport.ServerTransport]bool
// listener address with the value being the set of active transports
// belonging to that listener.
conns map[string]map[transport.ServerTransport]bool
serve bool serve bool
drain bool drain bool
cv *sync.Cond // signaled when connections close for GracefulStop cv *sync.Cond // signaled when connections close for GracefulStop
@ -279,35 +266,6 @@ func CustomCodec(codec Codec) ServerOption {
}) })
} }
// ForceServerCodec returns a ServerOption that sets a codec for message
// marshaling and unmarshaling.
//
// This will override any lookups by content-subtype for Codecs registered
// with RegisterCodec.
//
// See Content-Type on
// https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md#requests for
// more details. Also see the documentation on RegisterCodec and
// CallContentSubtype for more details on the interaction between encoding.Codec
// and content-subtype.
//
// This function is provided for advanced users; prefer to register codecs
// using encoding.RegisterCodec.
// The server will automatically use registered codecs based on the incoming
// requests' headers. See also
// https://github.com/grpc/grpc-go/blob/master/Documentation/encoding.md#using-a-codec.
// Will be supported throughout 1.x.
//
// Experimental
//
// Notice: This API is EXPERIMENTAL and may be changed or removed in a
// later release.
func ForceServerCodec(codec encoding.Codec) ServerOption {
return newFuncServerOption(func(o *serverOptions) {
o.codec = codec
})
}
// RPCCompressor returns a ServerOption that sets a compressor for outbound // RPCCompressor returns a ServerOption that sets a compressor for outbound
// messages. For backward compatibility, all outbound messages will be sent // messages. For backward compatibility, all outbound messages will be sent
// using this compressor, regardless of incoming message compression. By // using this compressor, regardless of incoming message compression. By
@ -418,11 +376,6 @@ func ChainStreamInterceptor(interceptors ...StreamServerInterceptor) ServerOptio
// InTapHandle returns a ServerOption that sets the tap handle for all the server // InTapHandle returns a ServerOption that sets the tap handle for all the server
// transport to be created. Only one can be installed. // transport to be created. Only one can be installed.
//
// Experimental
//
// Notice: This API is EXPERIMENTAL and may be changed or removed in a
// later release.
func InTapHandle(h tap.ServerInHandle) ServerOption { func InTapHandle(h tap.ServerInHandle) ServerOption {
return newFuncServerOption(func(o *serverOptions) { return newFuncServerOption(func(o *serverOptions) {
if o.inTapHandle != nil { if o.inTapHandle != nil {
@ -566,7 +519,7 @@ func NewServer(opt ...ServerOption) *Server {
s := &Server{ s := &Server{
lis: make(map[net.Listener]bool), lis: make(map[net.Listener]bool),
opts: opts, opts: opts,
conns: make(map[string]map[transport.ServerTransport]bool), conns: make(map[transport.ServerTransport]bool),
services: make(map[string]*serviceInfo), services: make(map[string]*serviceInfo),
quit: grpcsync.NewEvent(), quit: grpcsync.NewEvent(),
done: grpcsync.NewEvent(), done: grpcsync.NewEvent(),
@ -710,6 +663,13 @@ 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
@ -818,7 +778,7 @@ func (s *Server) Serve(lis net.Listener) error {
// s.conns before this conn can be added. // s.conns before this conn can be added.
s.serveWG.Add(1) s.serveWG.Add(1)
go func() { go func() {
s.handleRawConn(lis.Addr().String(), rawConn) s.handleRawConn(rawConn)
s.serveWG.Done() s.serveWG.Done()
}() }()
} }
@ -826,45 +786,49 @@ func (s *Server) Serve(lis net.Listener) error {
// handleRawConn forks a goroutine to handle a just-accepted connection that // handleRawConn forks a goroutine to handle a just-accepted connection that
// has not had any I/O performed on it yet. // has not had any I/O performed on it yet.
func (s *Server) handleRawConn(lisAddr string, rawConn net.Conn) { func (s *Server) handleRawConn(rawConn net.Conn) {
if s.quit.HasFired() { if s.quit.HasFired() {
rawConn.Close() rawConn.Close()
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(rawConn) st := s.newHTTP2Transport(conn, authInfo)
rawConn.SetDeadline(time.Time{})
if st == nil { if st == nil {
return return
} }
if !s.addConn(lisAddr, st) { rawConn.SetDeadline(time.Time{})
if !s.addConn(st) {
return return
} }
go func() { go func() {
s.serveStreams(st) s.serveStreams(st)
s.removeConn(lisAddr, st) s.removeConn(st)
}() }()
} }
func (s *Server) drainServerTransports(addr string) {
s.mu.Lock()
conns := s.conns[addr]
for st := range conns {
st.Drain()
}
s.mu.Unlock()
}
// 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) transport.ServerTransport { func (s *Server) newHTTP2Transport(c net.Conn, authInfo credentials.AuthInfo) transport.ServerTransport {
config := &transport.ServerConfig{ config := &transport.ServerConfig{
MaxStreams: s.opts.maxConcurrentStreams, MaxStreams: s.opts.maxConcurrentStreams,
ConnectionTimeout: s.opts.connectionTimeout, AuthInfo: authInfo,
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,
@ -877,20 +841,13 @@ func (s *Server) newHTTP2Transport(c net.Conn) transport.ServerTransport {
MaxHeaderListSize: s.opts.maxHeaderListSize, MaxHeaderListSize: s.opts.maxHeaderListSize,
HeaderTableSize: s.opts.headerTableSize, HeaderTableSize: s.opts.headerTableSize,
} }
st, err := transport.NewServerTransport(c, config) st, err := transport.NewServerTransport("http2", 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()
// 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)
}
c.Close() c.Close()
} channelz.Warning(logger, s.channelzID, "grpc: Server.Serve failed to create ServerTransport: ", err)
return nil return nil
} }
@ -967,10 +924,10 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)
return return
} }
if !s.addConn(listenerAddressForServeHTTP, st) { if !s.addConn(st) {
return return
} }
defer s.removeConn(listenerAddressForServeHTTP, st) defer s.removeConn(st)
s.serveStreams(st) s.serveStreams(st)
} }
@ -998,7 +955,7 @@ func (s *Server) traceInfo(st transport.ServerTransport, stream *transport.Strea
return trInfo return trInfo
} }
func (s *Server) addConn(addr string, st transport.ServerTransport) bool { func (s *Server) addConn(st transport.ServerTransport) bool {
s.mu.Lock() s.mu.Lock()
defer s.mu.Unlock() defer s.mu.Unlock()
if s.conns == nil { if s.conns == nil {
@ -1010,28 +967,15 @@ func (s *Server) addConn(addr string, st transport.ServerTransport) bool {
// immediately. // immediately.
st.Drain() st.Drain()
} }
s.conns[st] = true
if s.conns[addr] == nil {
// Create a map entry if this is the first connection on this listener.
s.conns[addr] = make(map[transport.ServerTransport]bool)
}
s.conns[addr][st] = true
return true return true
} }
func (s *Server) removeConn(addr string, st transport.ServerTransport) { func (s *Server) removeConn(st transport.ServerTransport) {
s.mu.Lock() s.mu.Lock()
defer s.mu.Unlock() defer s.mu.Unlock()
if s.conns != nil {
conns := s.conns[addr] delete(s.conns, st)
if conns != nil {
delete(conns, st)
if len(conns) == 0 {
// If the last connection for this address is being removed, also
// remove the map entry corresponding to the address. This is used
// in GracefulStop() when waiting for all connections to be closed.
delete(s.conns, addr)
}
s.cv.Broadcast() s.cv.Broadcast()
} }
} }
@ -1096,29 +1040,22 @@ func chainUnaryServerInterceptors(s *Server) {
} else if len(interceptors) == 1 { } else if len(interceptors) == 1 {
chainedInt = interceptors[0] chainedInt = interceptors[0]
} else { } else {
chainedInt = chainUnaryInterceptors(interceptors) chainedInt = func(ctx context.Context, req interface{}, info *UnaryServerInfo, handler UnaryHandler) (interface{}, error) {
return interceptors[0](ctx, req, info, getChainUnaryHandler(interceptors, 0, info, handler))
}
} }
s.opts.unaryInt = chainedInt s.opts.unaryInt = chainedInt
} }
func chainUnaryInterceptors(interceptors []UnaryServerInterceptor) UnaryServerInterceptor { // getChainUnaryHandler recursively generate the chained UnaryHandler
return func(ctx context.Context, req interface{}, info *UnaryServerInfo, handler UnaryHandler) (interface{}, error) { func getChainUnaryHandler(interceptors []UnaryServerInterceptor, curr int, info *UnaryServerInfo, finalHandler UnaryHandler) UnaryHandler {
// the struct ensures the variables are allocated together, rather than separately, since we if curr == len(interceptors)-1 {
// know they should be garbage collected together. This saves 1 allocation and decreases return finalHandler
// 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) {
if state.i == len(interceptors)-1 { return func(ctx context.Context, req interface{}) (interface{}, error) {
return interceptors[state.i](ctx, req, info, handler) return interceptors[curr+1](ctx, req, info, getChainUnaryHandler(interceptors, curr+1, info, finalHandler))
}
state.i++
return interceptors[state.i-1](ctx, req, info, state.next)
}
return state.next(ctx, req)
} }
} }
@ -1133,8 +1070,6 @@ 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)
} }
@ -1386,29 +1321,22 @@ func chainStreamServerInterceptors(s *Server) {
} else if len(interceptors) == 1 { } else if len(interceptors) == 1 {
chainedInt = interceptors[0] chainedInt = interceptors[0]
} else { } else {
chainedInt = chainStreamInterceptors(interceptors) chainedInt = func(srv interface{}, ss ServerStream, info *StreamServerInfo, handler StreamHandler) error {
return interceptors[0](srv, ss, info, getChainStreamHandler(interceptors, 0, info, handler))
}
} }
s.opts.streamInt = chainedInt s.opts.streamInt = chainedInt
} }
func chainStreamInterceptors(interceptors []StreamServerInterceptor) StreamServerInterceptor { // getChainStreamHandler recursively generate the chained StreamHandler
return func(srv interface{}, ss ServerStream, info *StreamServerInfo, handler StreamHandler) error { func getChainStreamHandler(interceptors []StreamServerInterceptor, curr int, info *StreamServerInfo, finalHandler StreamHandler) StreamHandler {
// the struct ensures the variables are allocated together, rather than separately, since we if curr == len(interceptors)-1 {
// know they should be garbage collected together. This saves 1 allocation and decreases return finalHandler
// time/call by about 10% on the microbenchmark.
var state struct {
i int
next StreamHandler
} }
state.next = func(srv interface{}, ss ServerStream) error {
if state.i == len(interceptors)-1 { return func(srv interface{}, ss ServerStream) error {
return interceptors[state.i](srv, ss, info, handler) return interceptors[curr+1](srv, ss, info, getChainStreamHandler(interceptors, curr+1, info, finalHandler))
}
state.i++
return interceptors[state.i-1](srv, ss, info, state.next)
}
return state.next(srv, ss)
} }
} }
@ -1422,8 +1350,6 @@ 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)
} }
@ -1526,8 +1452,6 @@ 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)
} }
@ -1595,7 +1519,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.Unimplemented, errDesc)); err != nil { if err := t.WriteStatus(stream, status.New(codes.ResourceExhausted, 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()
@ -1715,7 +1639,7 @@ func (s *Server) Stop() {
s.mu.Lock() s.mu.Lock()
listeners := s.lis listeners := s.lis
s.lis = nil s.lis = nil
conns := s.conns st := s.conns
s.conns = nil s.conns = nil
// interrupt GracefulStop if Stop and GracefulStop are called concurrently. // interrupt GracefulStop if Stop and GracefulStop are called concurrently.
s.cv.Broadcast() s.cv.Broadcast()
@ -1724,10 +1648,8 @@ func (s *Server) Stop() {
for lis := range listeners { for lis := range listeners {
lis.Close() lis.Close()
} }
for _, cs := range conns { for c := range st {
for st := range cs { c.Close()
st.Close()
}
} }
if s.opts.numServerWorkers > 0 { if s.opts.numServerWorkers > 0 {
s.stopServerWorkers() s.stopServerWorkers()
@ -1764,11 +1686,9 @@ func (s *Server) GracefulStop() {
} }
s.lis = nil s.lis = nil
if !s.drain { if !s.drain {
for _, conns := range s.conns { for st := range s.conns {
for st := range conns {
st.Drain() st.Drain()
} }
}
s.drain = true s.drain = true
} }

View File

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

View File

@ -29,7 +29,6 @@ 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"
@ -74,16 +73,11 @@ func FromProto(s *spb.Status) *Status {
return status.FromProto(s) return status.FromProto(s)
} }
// FromError returns a Status representation of err. // FromError returns a Status representing err if it was produced by this
// // package or has a method `GRPCStatus() *Status`.
// - If err was produced by this package or implements the method `GRPCStatus() // If err is nil, a Status is returned with codes.OK and no message.
// *Status`, the appropriate Status is returned. // Otherwise, ok is false and a Status is returned with codes.Unknown and
// // 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
@ -118,18 +112,18 @@ func Code(err error) codes.Code {
return codes.Unknown return codes.Unknown
} }
// FromContextError converts a context error or wrapped context error into a // FromContextError converts a context error into a Status. It returns a
// Status. It returns a Status with codes.OK if err is nil, or a Status with // Status with codes.OK if err is nil, or a Status with codes.Unknown if err is
// codes.Unknown if err is non-nil and not a context error. // non-nil and not a context error.
func FromContextError(err error) *Status { func FromContextError(err error) *Status {
if err == nil { switch err {
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())
}
} }

View File

@ -52,20 +52,14 @@ import (
// of the RPC. // of the RPC.
type StreamHandler func(srv interface{}, stream ServerStream) error type StreamHandler func(srv interface{}, stream ServerStream) error
// StreamDesc represents a streaming RPC service's method specification. Used // StreamDesc represents a streaming RPC service's method specification.
// on the server when registering services and on the client when initiating
// new streams.
type StreamDesc struct { type StreamDesc struct {
// StreamName and Handler are only used when registering handlers on a StreamName string
// server. Handler StreamHandler
StreamName string // the name of the method excluding the service
Handler StreamHandler // the handler called for the method
// ServerStreams and ClientStreams are used for registering handlers on a // At least one of these is true.
// server as well as defining RPC behavior when passed to NewClientStream ServerStreams bool
// and ClientConn.NewStream. At least one must be true. ClientStreams bool
ServerStreams bool // indicates the server can perform streaming sends
ClientStreams bool // indicates the client can perform streaming sends
} }
// Stream defines the common interface a client or server stream has to satisfy. // Stream defines the common interface a client or server stream has to satisfy.
@ -172,6 +166,7 @@ func newClientStream(ctx context.Context, desc *StreamDesc, cc *ClientConn, meth
} }
}() }()
} }
c := defaultCallInfo()
// Provide an opportunity for the first RPC to see the first service config // Provide an opportunity for the first RPC to see the first service config
// provided by the resolver. // provided by the resolver.
if err := cc.waitForResolvedAddrs(ctx); err != nil { if err := cc.waitForResolvedAddrs(ctx); err != nil {
@ -180,40 +175,18 @@ func newClientStream(ctx context.Context, desc *StreamDesc, cc *ClientConn, meth
var mc serviceconfig.MethodConfig var mc serviceconfig.MethodConfig
var onCommit func() var onCommit func()
var newStream = func(ctx context.Context, done func()) (iresolver.ClientStream, error) { rpcConfig, err := cc.safeConfigSelector.SelectConfig(iresolver.RPCInfo{Context: ctx, Method: method})
return newClientStreamWithParams(ctx, desc, cc, method, mc, onCommit, done, opts...)
}
rpcInfo := iresolver.RPCInfo{Context: ctx, Method: method}
rpcConfig, err := cc.safeConfigSelector.SelectConfig(rpcInfo)
if err != nil { if err != nil {
return nil, toRPCErr(err) return nil, status.Convert(err).Err()
} }
if rpcConfig != nil { if rpcConfig != nil {
if rpcConfig.Context != nil { if rpcConfig.Context != nil {
ctx = rpcConfig.Context ctx = rpcConfig.Context
} }
mc = rpcConfig.MethodConfig mc = rpcConfig.MethodConfig
onCommit = rpcConfig.OnCommitted onCommit = rpcConfig.OnCommitted
if rpcConfig.Interceptor != nil {
rpcInfo.Context = nil
ns := newStream
newStream = func(ctx context.Context, done func()) (iresolver.ClientStream, error) {
cs, err := rpcConfig.Interceptor.NewStream(ctx, rpcInfo, done, ns)
if err != nil {
return nil, toRPCErr(err)
}
return cs, nil
}
}
} }
return newStream(ctx, func() {})
}
func newClientStreamWithParams(ctx context.Context, desc *StreamDesc, cc *ClientConn, method string, mc serviceconfig.MethodConfig, onCommit, doneFunc func(), opts ...CallOption) (_ iresolver.ClientStream, err error) {
c := defaultCallInfo()
if mc.WaitForReady != nil { if mc.WaitForReady != nil {
c.failFast = !*mc.WaitForReady c.failFast = !*mc.WaitForReady
} }
@ -250,7 +223,6 @@ func newClientStreamWithParams(ctx context.Context, desc *StreamDesc, cc *Client
Host: cc.authority, Host: cc.authority,
Method: method, Method: method,
ContentSubtype: c.contentSubtype, ContentSubtype: c.contentSubtype,
DoneFunc: doneFunc,
} }
// Set our outgoing compression according to the UseCompressor CallOption, if // Set our outgoing compression according to the UseCompressor CallOption, if
@ -274,6 +246,33 @@ 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,
@ -287,6 +286,7 @@ 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,
} }
@ -295,7 +295,9 @@ func newClientStreamWithParams(ctx context.Context, desc *StreamDesc, cc *Client
} }
cs.binlog = binarylog.GetMethodLogger(method) cs.binlog = binarylog.GetMethodLogger(method)
if err := cs.newAttemptLocked(false /* isTransparent */); err != nil { // Only this initial attempt has stats/tracing.
// 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
} }
@ -343,43 +345,8 @@ 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(isTransparent bool) (retErr error) { func (cs *clientStream) newAttemptLocked(sh stats.Handler, trInfo *traceInfo) (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,
@ -394,14 +361,15 @@ func (cs *clientStream) newAttemptLocked(isTransparent bool) (retErr error) {
} }
}() }()
if err := ctx.Err(); err != nil { if err := cs.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(ctx, metadata.Pairs( ctx = grpcutil.WithExtraMetadata(cs.ctx, metadata.Pairs(
"content-type", grpcutil.ContentType(cs.callHdr.ContentSubtype), "content-type", grpcutil.ContentType(cs.callHdr.ContentSubtype),
)) ))
} }
@ -421,12 +389,15 @@ func (cs *clientStream) newAttemptLocked(isTransparent bool) (retErr error) {
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(a.ctx, cs.callHdr) s, err := a.t.NewStream(cs.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
@ -447,6 +418,7 @@ 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,7 +458,6 @@ 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
@ -505,7 +476,6 @@ type csAttempt struct {
trInfo *traceInfo trInfo *traceInfo
statsHandler stats.Handler statsHandler stats.Handler
beginTime time.Time
} }
func (cs *clientStream) commitAttemptLocked() { func (cs *clientStream) commitAttemptLocked() {
@ -523,57 +493,46 @@ 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. If the RPC should be // the error that should be returned by the operation.
// retried, the bool indicates whether it is being retried transparently. func (cs *clientStream) shouldRetry(err error) error {
func (cs *clientStream) shouldRetry(err error) (bool, error) { unprocessed := false
if cs.attempt.s == nil { if cs.attempt.s == nil {
// Error from NewClientStream. pioErr, ok := err.(transport.PerformedIOError)
nse, ok := err.(*transport.NewStreamError) if ok {
if !ok { // Unwrap error.
// Unexpected, but assume no I/O was performed and the RPC is not err = toRPCErr(pioErr.Err)
// fatal, so retry indefinitely. } else {
return true, nil unprocessed = true
} }
if !ok && !cs.callInfo.failFast {
// Unwrap and convert error. // In the event of a non-IO operation error from NewStream, we
err = toRPCErr(nse.Err) // never attempted to write anything to the wire, so we can retry
// indefinitely for non-fail-fast RPCs.
// Never retry DoNotRetry errors, which indicate the RPC should not be return nil
// 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 false, err return 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 true, nil return nil
} }
if cs.cc.dopts.disableRetry { if cs.cc.dopts.disableRetry {
return false, err return 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 false, err return 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
@ -584,13 +543,13 @@ func (cs *clientStream) shouldRetry(err error) (bool, 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 false, err return 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 false, err return err
} }
} }
@ -603,16 +562,16 @@ func (cs *clientStream) shouldRetry(err error) (bool, error) {
rp := cs.methodConfig.RetryPolicy rp := cs.methodConfig.RetryPolicy
if rp == nil || !rp.RetryableStatusCodes[code] { if rp == nil || !rp.RetryableStatusCodes[code] {
return false, err return 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 false, err return err
} }
if cs.numRetries+1 >= rp.MaxAttempts { if cs.numRetries+1 >= rp.MaxAttempts {
return false, err return err
} }
var dur time.Duration var dur time.Duration
@ -635,24 +594,23 @@ func (cs *clientStream) shouldRetry(err error) (bool, error) {
select { select {
case <-t.C: case <-t.C:
cs.numRetries++ cs.numRetries++
return false, nil return nil
case <-cs.ctx.Done(): case <-cs.ctx.Done():
t.Stop() t.Stop()
return false, status.FromContextError(cs.ctx.Err()).Err() return 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(toRPCErr(lastErr)) cs.attempt.finish(lastErr)
isTransparent, err := cs.shouldRetry(lastErr) if err := cs.shouldRetry(lastErr); err != nil {
if err != nil {
cs.commitAttemptLocked() cs.commitAttemptLocked()
return err return err
} }
cs.firstAttempt = false cs.firstAttempt = false
if err := cs.newAttemptLocked(isTransparent); err != nil { if err := cs.newAttemptLocked(nil, nil); err != nil {
return err return err
} }
if lastErr = cs.replayBufferLocked(); lastErr == nil { if lastErr = cs.replayBufferLocked(); lastErr == nil {
@ -673,11 +631,7 @@ 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()
// toRPCErr is used in case the error from the attempt comes from return op(cs.attempt)
// 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()
@ -942,7 +896,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(a.ctx, outPayload(true, m, data, payld, time.Now())) a.statsHandler.HandleRPC(cs.ctx, outPayload(true, m, data, payld, time.Now()))
} }
if channelz.IsOn() { if channelz.IsOn() {
a.t.IncrMsgSent() a.t.IncrMsgSent()
@ -990,7 +944,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(a.ctx, &stats.InPayload{ a.statsHandler.HandleRPC(cs.ctx, &stats.InPayload{
Client: true, Client: true,
RecvTime: time.Now(), RecvTime: time.Now(),
Payload: m, Payload: m,
@ -1052,12 +1006,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.beginTime, BeginTime: a.cs.beginTime,
EndTime: time.Now(), EndTime: time.Now(),
Trailer: tr, Trailer: tr,
Error: err, Error: err,
} }
a.statsHandler.HandleRPC(a.ctx, end) a.statsHandler.HandleRPC(a.cs.ctx, end)
} }
if a.trInfo != nil && a.trInfo.tr != nil { if a.trInfo != nil && a.trInfo.tr != nil {
if err == nil { if err == nil {

View File

@ -37,16 +37,16 @@ type Info struct {
// TODO: More to be added. // TODO: More to be added.
} }
// ServerInHandle defines the function which runs before a new stream is // ServerInHandle defines the function which runs before a new stream is created
// created on the server side. If it returns a non-nil error, the stream will // on the server side. If it returns a non-nil error, the stream will not be
// not be created and an error will be returned to the client. If the error // created and a RST_STREAM will be sent back to the client with REFUSED_STREAM.
// returned is a status error, that status code and message will be used, // The client will receive an RPC error "code = Unavailable, desc = stream
// otherwise PermissionDenied will be the code and err.Error() will be the // terminated by RST_STREAM with error code: REFUSED_STREAM".
// message.
// //
// It's intended to be used in situations where you don't want to waste the // It's intended to be used in situations where you don't want to waste the
// resources to accept the new stream (e.g. rate-limiting). For other general // resources to accept the new stream (e.g. rate-limiting). And the content of
// usages, please use interceptors. // the error will be ignored and won't be sent back to the client. For other
// general usages, please use interceptors.
// //
// Note that it is executed in the per-connection I/O goroutine(s) instead of // Note that it is executed in the per-connection I/O goroutine(s) instead of
// per-RPC goroutine. Therefore, users should NOT have any // per-RPC goroutine. Therefore, users should NOT have any

View File

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

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

@ -28,10 +28,11 @@ cleanup() {
} }
trap cleanup EXIT trap cleanup EXIT
PATH="${HOME}/go/bin:${GOROOT}/bin:${PATH}" PATH="${GOPATH}/bin:${GOROOT}/bin:${PATH}"
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 \
@ -40,6 +41,16 @@ 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
@ -89,6 +100,16 @@ 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
# - gofmt, goimports, golint (with exceptions for generated code), go vet.
gofmt -s -d -l . 2>&1 | fail_on_output
goimports -l . 2>&1 | not grep -vE "\.pb\.go"
golint ./... 2>&1 | not grep -vE "\.pb\.go:"
go vet -all ./...
misspell -error . misspell -error .
# - Check that generated proto files are up to date. # - Check that generated proto files are up to date.
@ -98,22 +119,12 @@ if [[ -z "${VET_SKIP_PROTO}" ]]; then
(git status; git --no-pager diff; exit 1) (git status; git --no-pager diff; exit 1)
fi fi
# - gofmt, goimports, golint (with exceptions for generated code), go vet, # - Check that our modules are tidy.
# go mod tidy. if go help mod >& /dev/null; then
# Perform these checks on each module inside gRPC. find . -name 'go.mod' | xargs -IXXX bash -c 'cd $(dirname XXX); go mod tidy'
for MOD_FILE in $(find . -name 'go.mod'); do
MOD_DIR=$(dirname ${MOD_FILE})
pushd ${MOD_DIR}
go vet -all ./... | fail_on_output
gofmt -s -d -l . 2>&1 | fail_on_output
goimports -l . 2>&1 | not grep -vE "\.pb\.go"
golint ./... 2>&1 | not grep -vE "/testv3\.pb\.go:"
go mod tidy
git status --porcelain 2>&1 | fail_on_output || \ git status --porcelain 2>&1 | fail_on_output || \
(git status; git --no-pager diff; exit 1) (git status; git --no-pager diff; exit 1)
popd fi
done
# - Collection of static analysis checks # - Collection of static analysis checks
# #
@ -130,11 +141,8 @@ not grep -Fv '.CredsBundle
.NewAddress .NewAddress
.NewServiceConfig .NewServiceConfig
.Type is deprecated: use Attributes .Type is deprecated: use Attributes
BuildVersion is deprecated
balancer.ErrTransientFailure balancer.ErrTransientFailure
balancer.Picker balancer.Picker
extDesc.Filename is deprecated
github.com/golang/protobuf/jsonpb is deprecated
grpc.CallCustomCodec grpc.CallCustomCodec
grpc.Code grpc.Code
grpc.Compressor grpc.Compressor
@ -156,7 +164,13 @@ grpc.WithServiceConfig
grpc.WithTimeout grpc.WithTimeout
http.CloseNotifier http.CloseNotifier
info.SecurityVersion info.SecurityVersion
resolver.Backend
resolver.GRPCLB
extDesc.Filename is deprecated
BuildVersion is deprecated
github.com/golang/protobuf/jsonpb is deprecated
proto is deprecated proto is deprecated
xxx_messageInfo_
proto.InternalMessageInfo is deprecated proto.InternalMessageInfo is deprecated
proto.EnumName is deprecated proto.EnumName is deprecated
proto.ErrInternalBadWireType is deprecated proto.ErrInternalBadWireType is deprecated
@ -170,12 +184,7 @@ proto.RegisterExtension is deprecated
proto.RegisteredExtension is deprecated proto.RegisteredExtension is deprecated
proto.RegisteredExtensions is deprecated proto.RegisteredExtensions is deprecated
proto.RegisterMapType is deprecated proto.RegisterMapType is deprecated
proto.Unmarshaler is deprecated proto.Unmarshaler is deprecated' "${SC_OUT}"
resolver.Backend
resolver.GRPCLB
Target is deprecated: Use the Target field in the BuildOptions instead.
xxx_messageInfo_
' "${SC_OUT}"
# - special golint on package comments. # - special golint on package comments.
lint_package_comment_per_package() { lint_package_comment_per_package() {

5
vendor/modules.txt vendored
View File

@ -270,8 +270,8 @@ google.golang.org/appengine/internal/remote_api
# google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 # google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013
## explicit; go 1.11 ## explicit; go 1.11
google.golang.org/genproto/googleapis/rpc/status google.golang.org/genproto/googleapis/rpc/status
# google.golang.org/grpc v1.44.0 # google.golang.org/grpc v1.36.1
## explicit; go 1.14 ## explicit; go 1.11
google.golang.org/grpc google.golang.org/grpc
google.golang.org/grpc/attributes google.golang.org/grpc/attributes
google.golang.org/grpc/backoff google.golang.org/grpc/backoff
@ -283,7 +283,6 @@ 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