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:
parent
2ec03b377b
commit
32973392de
2
go.mod
2
go.mod
|
|
@ -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
23
go.sum
|
|
@ -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=
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,8 @@ vetdeps:
|
||||||
clean \
|
clean \
|
||||||
proto \
|
proto \
|
||||||
test \
|
test \
|
||||||
|
testappengine \
|
||||||
|
testappenginedeps \
|
||||||
testrace \
|
testrace \
|
||||||
vet \
|
vet \
|
||||||
vetdeps
|
vetdeps
|
||||||
|
|
|
||||||
|
|
@ -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.
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
@ -376,10 +343,8 @@ var ErrBadResolverState = errors.New("bad resolver state")
|
||||||
//
|
//
|
||||||
// It's not thread safe.
|
// It's not thread safe.
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -37,20 +37,14 @@ 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
|
done *grpcsync.Event
|
||||||
closed *grpcsync.Event
|
|
||||||
done *grpcsync.Event
|
|
||||||
|
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
subConns map[*acBalancerWrapper]struct{}
|
subConns map[*acBalancerWrapper]struct{}
|
||||||
|
|
@ -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) {
|
ccb.balancerMu.Lock()
|
||||||
case *scStateUpdate:
|
su := t.(*scStateUpdate)
|
||||||
ccb.balancerMu.Lock()
|
ccb.balancer.UpdateSubConnState(su.sc, balancer.SubConnState{ConnectivityState: su.state, ConnectionError: su.err})
|
||||||
ccb.balancer.UpdateSubConnState(u.sc, balancer.SubConnState{ConnectivityState: u.state, ConnectionError: u.err})
|
ccb.balancerMu.Unlock()
|
||||||
ccb.balancerMu.Unlock()
|
case <-ccb.done.Done():
|
||||||
case *acBalancerWrapper:
|
|
||||||
ccb.mu.Lock()
|
|
||||||
if ccb.subConns != nil {
|
|
||||||
delete(ccb.subConns, u)
|
|
||||||
ccb.cc.removeAddrConn(u.getAddrConn(), errConnDrain)
|
|
||||||
}
|
|
||||||
ccb.mu.Unlock()
|
|
||||||
case exitIdle:
|
|
||||||
if ccb.cc.GetState() == connectivity.Idle {
|
|
||||||
if ei, ok := ccb.balancer.(balancer.ExitIdler); ok {
|
|
||||||
// We already checked that the balancer implements
|
|
||||||
// ExitIdle before pushing the event to updateCh, but
|
|
||||||
// check conditionally again as defensive programming.
|
|
||||||
ccb.balancerMu.Lock()
|
|
||||||
ei.ExitIdle()
|
|
||||||
ccb.balancerMu.Unlock()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
logger.Errorf("ccBalancerWrapper.watcher: unknown update %+v, type %T", t, t)
|
|
||||||
}
|
|
||||||
case <-ccb.closed.Done():
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ccb.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 {
|
||||||
|
|
|
||||||
|
|
@ -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.copts.TransportCredentials == nil && cc.dopts.copts.CredsBundle == nil {
|
if !cc.dopts.insecure {
|
||||||
return nil, errNoTransportSecurity
|
if cc.dopts.copts.TransportCredentials == nil && cc.dopts.copts.CredsBundle == nil {
|
||||||
}
|
return nil, errNoTransportSecurity
|
||||||
if cc.dopts.copts.TransportCredentials != nil && cc.dopts.copts.CredsBundle != nil {
|
}
|
||||||
return nil, errTransportCredsAndBundle
|
if cc.dopts.copts.TransportCredentials != nil && cc.dopts.copts.CredsBundle != nil {
|
||||||
}
|
return nil, errTransportCredsAndBundle
|
||||||
if cc.dopts.copts.CredsBundle != nil && cc.dopts.copts.CredsBundle.TransportCredentials() == nil {
|
}
|
||||||
return nil, errNoTransportCredsInBundle
|
} else {
|
||||||
}
|
if cc.dopts.copts.TransportCredentials != nil || cc.dopts.copts.CredsBundle != nil {
|
||||||
transportCreds := cc.dopts.copts.TransportCredentials
|
return nil, errCredentialsConflict
|
||||||
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,
|
||||||
|
}
|
||||||
|
resolverBuilder = cc.getResolver(cc.parsedTarget.Scheme)
|
||||||
|
if resolverBuilder == nil {
|
||||||
|
return nil, fmt.Errorf("could not get resolver for default scheme: %q", cc.parsedTarget.Scheme)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
cc.authority, err = determineAuthority(cc.parsedTarget.Endpoint, cc.target, cc.dopts)
|
|
||||||
if err != nil {
|
creds := cc.dopts.copts.TransportCredentials
|
||||||
return nil, err
|
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,86 +1129,112 @@ func (ac *addrConn) adjustParams(r transport.GoAwayReason) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ac *addrConn) resetTransport() {
|
func (ac *addrConn) resetTransport() {
|
||||||
ac.mu.Lock()
|
for i := 0; ; i++ {
|
||||||
if ac.state == connectivity.Shutdown {
|
if i > 0 {
|
||||||
ac.mu.Unlock()
|
ac.cc.resolveNow(resolver.ResolveNowOptions{})
|
||||||
return
|
}
|
||||||
}
|
|
||||||
|
|
||||||
addrs := ac.addrs
|
|
||||||
backoffFor := ac.dopts.bs.Backoff(ac.backoffIdx)
|
|
||||||
// This will be the duration that dial gets to finish.
|
|
||||||
dialDuration := minConnectTimeout
|
|
||||||
if ac.dopts.minConnectTimeout != nil {
|
|
||||||
dialDuration = ac.dopts.minConnectTimeout()
|
|
||||||
}
|
|
||||||
|
|
||||||
if dialDuration < backoffFor {
|
|
||||||
// Give dial more time as we keep failing to connect.
|
|
||||||
dialDuration = backoffFor
|
|
||||||
}
|
|
||||||
// We can potentially spend all the time trying the first address, and
|
|
||||||
// if the server accepts the connection and then hangs, the following
|
|
||||||
// addresses will never be tried.
|
|
||||||
//
|
|
||||||
// The spec doesn't mention what should be done for multiple addresses.
|
|
||||||
// https://github.com/grpc/grpc/blob/master/doc/connection-backoff.md#proposed-backoff-algorithm
|
|
||||||
connectDeadline := time.Now().Add(dialDuration)
|
|
||||||
|
|
||||||
ac.updateConnectivityState(connectivity.Connecting, nil)
|
|
||||||
ac.mu.Unlock()
|
|
||||||
|
|
||||||
if err := ac.tryAllAddrs(addrs, connectDeadline); err != nil {
|
|
||||||
ac.cc.resolveNow(resolver.ResolveNowOptions{})
|
|
||||||
// After exhausting all addresses, the addrConn enters
|
|
||||||
// TRANSIENT_FAILURE.
|
|
||||||
ac.mu.Lock()
|
ac.mu.Lock()
|
||||||
if ac.state == connectivity.Shutdown {
|
if ac.state == connectivity.Shutdown {
|
||||||
ac.mu.Unlock()
|
ac.mu.Unlock()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ac.updateConnectivityState(connectivity.TransientFailure, err)
|
|
||||||
|
|
||||||
// Backoff.
|
addrs := ac.addrs
|
||||||
b := ac.resetBackoff
|
backoffFor := ac.dopts.bs.Backoff(ac.backoffIdx)
|
||||||
|
// This will be the duration that dial gets to finish.
|
||||||
|
dialDuration := minConnectTimeout
|
||||||
|
if ac.dopts.minConnectTimeout != nil {
|
||||||
|
dialDuration = ac.dopts.minConnectTimeout()
|
||||||
|
}
|
||||||
|
|
||||||
|
if dialDuration < backoffFor {
|
||||||
|
// Give dial more time as we keep failing to connect.
|
||||||
|
dialDuration = backoffFor
|
||||||
|
}
|
||||||
|
// We can potentially spend all the time trying the first address, and
|
||||||
|
// if the server accepts the connection and then hangs, the following
|
||||||
|
// addresses will never be tried.
|
||||||
|
//
|
||||||
|
// The spec doesn't mention what should be done for multiple addresses.
|
||||||
|
// https://github.com/grpc/grpc/blob/master/doc/connection-backoff.md#proposed-backoff-algorithm
|
||||||
|
connectDeadline := time.Now().Add(dialDuration)
|
||||||
|
|
||||||
|
ac.updateConnectivityState(connectivity.Connecting, nil)
|
||||||
|
ac.transport = nil
|
||||||
ac.mu.Unlock()
|
ac.mu.Unlock()
|
||||||
|
|
||||||
timer := time.NewTimer(backoffFor)
|
newTr, addr, reconnect, err := ac.tryAllAddrs(addrs, connectDeadline)
|
||||||
select {
|
if err != nil {
|
||||||
case <-timer.C:
|
// After exhausting all addresses, the addrConn enters
|
||||||
|
// TRANSIENT_FAILURE.
|
||||||
ac.mu.Lock()
|
ac.mu.Lock()
|
||||||
ac.backoffIdx++
|
if ac.state == connectivity.Shutdown {
|
||||||
|
ac.mu.Unlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ac.updateConnectivityState(connectivity.TransientFailure, err)
|
||||||
|
|
||||||
|
// Backoff.
|
||||||
|
b := ac.resetBackoff
|
||||||
ac.mu.Unlock()
|
ac.mu.Unlock()
|
||||||
case <-b:
|
|
||||||
timer.Stop()
|
timer := time.NewTimer(backoffFor)
|
||||||
case <-ac.ctx.Done():
|
select {
|
||||||
timer.Stop()
|
case <-timer.C:
|
||||||
return
|
ac.mu.Lock()
|
||||||
|
ac.backoffIdx++
|
||||||
|
ac.mu.Unlock()
|
||||||
|
case <-b:
|
||||||
|
timer.Stop()
|
||||||
|
case <-ac.ctx.Done():
|
||||||
|
timer.Stop()
|
||||||
|
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()
|
||||||
|
newTr.Close()
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
ac.curAddr = addr
|
||||||
|
ac.transport = newTr
|
||||||
|
ac.backoffIdx = 0
|
||||||
|
|
||||||
|
hctx, hcancel := context.WithCancel(ac.ctx)
|
||||||
|
ac.startHealthCheck(hctx)
|
||||||
ac.mu.Unlock()
|
ac.mu.Unlock()
|
||||||
return
|
|
||||||
|
// 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.
|
||||||
}
|
}
|
||||||
// Success; reset backoff.
|
|
||||||
ac.mu.Lock()
|
|
||||||
ac.backoffIdx = 0
|
|
||||||
ac.mu.Unlock()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
//
|
//
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
}
|
||||||
|
|
@ -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"
|
|
||||||
}
|
|
||||||
|
|
@ -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",
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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,7 +66,11 @@ 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
|
||||||
resolvers []resolver.Builder
|
// 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
|
||||||
}
|
}
|
||||||
|
|
||||||
// DialOption configures how we set up the connection.
|
// DialOption configures how we set up the connection.
|
||||||
|
|
@ -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
|
||||||
|
// invalid service config.
|
||||||
//
|
//
|
||||||
// 2. The name resolver does not provide a service config or provides an
|
// Experimental
|
||||||
// invalid service config.
|
|
||||||
//
|
//
|
||||||
// The parameter s is the JSON representation of the default service config.
|
// Notice: This API is EXPERIMENTAL and may be changed or removed in a
|
||||||
// For more information about service configs, see:
|
// later release.
|
||||||
// https://github.com/grpc/grpc/blob/master/doc/service_config.md
|
|
||||||
// For a simple example of usage, see:
|
|
||||||
// examples/features/load_balancing/client/main.go
|
|
||||||
func WithDefaultServiceConfig(s string) DialOption {
|
func WithDefaultServiceConfig(s string) DialOption {
|
||||||
return newFuncDialOption(func(o *dialOptions) {
|
return newFuncDialOption(func(o *dialOptions) {
|
||||||
o.defaultServiceConfigRawJSON = &s
|
o.defaultServiceConfigRawJSON = &s
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
@ -98,9 +95,8 @@ var severityName = []string{
|
||||||
|
|
||||||
// loggerT is the default logger used by grpclog.
|
// loggerT is the default logger used by grpclog.
|
||||||
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{})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
@ -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)))
|
||||||
|
|
@ -86,27 +85,24 @@ func (ws *writerSink) Write(e *pb.GrpcLogEntry) error {
|
||||||
func (ws *writerSink) Close() error { return nil }
|
func (ws *writerSink) Close() error { return nil }
|
||||||
|
|
||||||
type bufferedSink struct {
|
type bufferedSink struct {
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
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
|
|
||||||
|
|
||||||
writeTicker *time.Ticker
|
writeStartOnce sync.Once
|
||||||
done chan struct{}
|
writeTicker *time.Ticker
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fs *bufferedSink) Write(e *pb.GrpcLogEntry) error {
|
func (fs *bufferedSink) Write(e *pb.GrpcLogEntry) error {
|
||||||
|
// Start the write loop when Write is called.
|
||||||
|
fs.writeStartOnce.Do(fs.startFlushGoroutine)
|
||||||
fs.mu.Lock()
|
fs.mu.Lock()
|
||||||
defer fs.mu.Unlock()
|
|
||||||
if !fs.flusherStarted {
|
|
||||||
// Start the write loop when Write is called.
|
|
||||||
fs.startFlushGoroutine()
|
|
||||||
fs.flusherStarted = true
|
|
||||||
}
|
|
||||||
if err := fs.out.Write(e); err != nil {
|
if err := fs.out.Write(e); err != nil {
|
||||||
|
fs.mu.Unlock()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
fs.mu.Unlock()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -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{}),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
// +build !appengine
|
||||||
|
|
||||||
/*
|
/*
|
||||||
*
|
*
|
||||||
* Copyright 2018 gRPC authors.
|
* Copyright 2018 gRPC authors.
|
||||||
|
|
|
||||||
|
|
@ -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.")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
// +build linux,!appengine
|
||||||
|
|
||||||
/*
|
/*
|
||||||
*
|
*
|
||||||
* Copyright 2018 gRPC authors.
|
* Copyright 2018 gRPC authors.
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
//go:build !linux
|
// +build !linux appengine
|
||||||
// +build !linux
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -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)
|
|
||||||
}
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
// +build !appengine
|
||||||
|
|
||||||
/*
|
/*
|
||||||
*
|
*
|
||||||
* Copyright 2020 gRPC authors.
|
* Copyright 2020 gRPC authors.
|
||||||
|
|
|
||||||
31
vendor/google.golang.org/grpc/internal/credentials/spiffe_appengine.go
generated
vendored
Normal file
31
vendor/google.golang.org/grpc/internal/credentials/spiffe_appengine.go
generated
vendored
Normal 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
|
||||||
|
}
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
// +build !appengine
|
||||||
|
|
||||||
/*
|
/*
|
||||||
*
|
*
|
||||||
* Copyright 2018 gRPC authors.
|
* Copyright 2018 gRPC authors.
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
@ -18,9 +18,7 @@
|
||||||
|
|
||||||
package credentials
|
package credentials
|
||||||
|
|
||||||
import (
|
import "crypto/tls"
|
||||||
"crypto/tls"
|
|
||||||
)
|
|
||||||
|
|
||||||
const alpnProtoStrH2 = "h2"
|
const alpnProtoStrH2 = "h2"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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")
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -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)
|
|
||||||
)
|
|
||||||
|
|
@ -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{})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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()
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
|
||||||
case <-d.ctx.Done():
|
|
||||||
timer.Stop()
|
|
||||||
return
|
|
||||||
case <-d.rn:
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Poll on an error found in DNS Resolver or an error received from ClientConn.
|
|
||||||
timer = newTimer(backoff.DefaultExponential.Backoff(backoffIndex))
|
|
||||||
backoffIndex++
|
|
||||||
}
|
|
||||||
select {
|
select {
|
||||||
case <-d.ctx.Done():
|
case <-d.ctx.Done():
|
||||||
timer.Stop()
|
|
||||||
return
|
return
|
||||||
case <-timer.C:
|
case <-d.rn:
|
||||||
|
}
|
||||||
|
|
||||||
|
state, err := d.lookup()
|
||||||
|
if err != nil {
|
||||||
|
d.cc.ReportError(err)
|
||||||
|
} else {
|
||||||
|
d.cc.UpdateState(*state)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sleep to prevent excessive re-resolutions. Incoming resolution requests
|
||||||
|
// will be queued in d.rn.
|
||||||
|
t := time.NewTimer(minDNSResRate)
|
||||||
|
select {
|
||||||
|
case <-t.C:
|
||||||
|
case <-d.ctx.Done():
|
||||||
|
t.Stop()
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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)
|
|
||||||
}
|
}
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
// +build !appengine
|
||||||
|
|
||||||
/*
|
/*
|
||||||
*
|
*
|
||||||
* Copyright 2018 gRPC authors.
|
* Copyright 2018 gRPC authors.
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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) }
|
||||||
|
|
|
||||||
|
|
@ -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,20 +143,13 @@ 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
|
||||||
return fn(ctx, "unix://"+address)
|
// and the address is now "/path", prepend "unix://" so the user's
|
||||||
}
|
// custom dialer sees the same address.
|
||||||
return fn(ctx, "unix:"+address)
|
return fn(ctx, "unix://"+address)
|
||||||
}
|
}
|
||||||
return fn(ctx, address)
|
return fn(ctx, address)
|
||||||
}
|
}
|
||||||
|
|
@ -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.
|
||||||
t.conn.Close()
|
if _, ok := err.(net.Error); !ok {
|
||||||
t.controlBuf.finish()
|
t.conn.Close()
|
||||||
|
}
|
||||||
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()
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
t.controlBuf.put(&cleanupStream{
|
if _, ok := status.FromError(err); ok {
|
||||||
streamID: streamID,
|
t.controlBuf.put(&cleanupStream{
|
||||||
rst: true,
|
streamID: streamID,
|
||||||
rstCode: http2.ErrCodeFrameSize,
|
rst: true,
|
||||||
onWrite: func() {},
|
rstCode: h2code,
|
||||||
})
|
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 state.data.statsTags != nil {
|
||||||
|
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,
|
||||||
}
|
}
|
||||||
if statsTrace := mdata["grpc-trace-bin"]; len(statsTrace) > 0 {
|
s.ctx, err = t.inTapHandle(s.ctx, info)
|
||||||
s.ctx = stats.SetIncomingTrace(s.ctx, []byte(statsTrace[len(statsTrace)-1]))
|
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)
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
if err != nil {
|
// list size hits MaxHeaderListSize limit.
|
||||||
return nil, err
|
if frame.Truncated {
|
||||||
|
return http2.ErrCodeFrameSize, status.Error(codes.Internal, "peer header list size exceeded limit")
|
||||||
}
|
}
|
||||||
st := &spb.Status{}
|
|
||||||
if err = proto.Unmarshal(v, st); err != nil {
|
for _, hf := range frame.Fields {
|
||||||
return nil, err
|
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 {
|
||||||
|
d.data.grpcErr = status.Errorf(codes.Internal, "transport: malformed grpc-status: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
d.data.rawStatusCode = &code
|
||||||
|
case "grpc-message":
|
||||||
|
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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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/* .
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
Endpoint string
|
||||||
// the former is empty.
|
|
||||||
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.
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
if size > maxReceiveMessageSize {
|
} else {
|
||||||
// TODO: Revisit the error code. Currently keep it consistent with java
|
size = len(d)
|
||||||
// implementation.
|
}
|
||||||
return nil, status.Errorf(codes.ResourceExhausted, "grpc: received message after decompression larger than max (%d vs. %d)", size, maxReceiveMessageSize)
|
if size > maxReceiveMessageSize {
|
||||||
}
|
// TODO: Revisit the error code. Currently keep it consistent with java
|
||||||
|
// implementation.
|
||||||
|
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
|
||||||
case context.DeadlineExceeded:
|
}
|
||||||
return status.Error(codes.DeadlineExceeded, err.Error())
|
if err == io.ErrUnexpectedEOF {
|
||||||
case context.Canceled:
|
|
||||||
return status.Error(codes.Canceled, err.Error())
|
|
||||||
case io.ErrUnexpectedEOF:
|
|
||||||
return status.Error(codes.Internal, err.Error())
|
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 {
|
if _, ok := status.FromError(err); ok {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
switch e := err.(type) {
|
||||||
|
case transport.ConnectionError:
|
||||||
|
return status.Error(codes.Unavailable, e.Desc)
|
||||||
|
default:
|
||||||
|
switch err {
|
||||||
|
case context.DeadlineExceeded:
|
||||||
|
return status.Error(codes.DeadlineExceeded, err.Error())
|
||||||
|
case context.Canceled:
|
||||||
|
return status.Error(codes.Canceled, err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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, "")
|
||||||
|
|
@ -117,12 +107,9 @@ type serverWorkerData struct {
|
||||||
type Server struct {
|
type Server struct {
|
||||||
opts serverOptions
|
opts serverOptions
|
||||||
|
|
||||||
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
|
c.Close()
|
||||||
// gRPC; those connections should be left open.
|
channelz.Warning(logger, s.channelzID, "grpc: Server.Serve failed to create ServerTransport: ", err)
|
||||||
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()
|
|
||||||
}
|
|
||||||
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
|
return func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
next UnaryHandler
|
return interceptors[curr+1](ctx, req, info, getChainUnaryHandler(interceptors, curr+1, info, finalHandler))
|
||||||
}
|
|
||||||
state.next = func(ctx context.Context, req interface{}) (interface{}, error) {
|
|
||||||
if state.i == len(interceptors)-1 {
|
|
||||||
return interceptors[state.i](ctx, req, info, handler)
|
|
||||||
}
|
|
||||||
state.i++
|
|
||||||
return interceptors[state.i-1](ctx, req, info, state.next)
|
|
||||||
}
|
|
||||||
return state.next(ctx, req)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1132,9 +1069,7 @@ func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport.
|
||||||
if sh != nil {
|
if sh != nil {
|
||||||
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
|
return func(srv interface{}, ss ServerStream) error {
|
||||||
next StreamHandler
|
return interceptors[curr+1](srv, ss, info, getChainStreamHandler(interceptors, curr+1, info, finalHandler))
|
||||||
}
|
|
||||||
state.next = func(srv interface{}, ss ServerStream) error {
|
|
||||||
if state.i == len(interceptors)-1 {
|
|
||||||
return interceptors[state.i](srv, ss, info, handler)
|
|
||||||
}
|
|
||||||
state.i++
|
|
||||||
return interceptors[state.i-1](srv, ss, info, state.next)
|
|
||||||
}
|
|
||||||
return state.next(srv, ss)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1421,9 +1349,7 @@ func (s *Server) processStreamingRPC(t transport.ServerTransport, stream *transp
|
||||||
if sh != nil {
|
if sh != nil {
|
||||||
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,10 +1686,8 @@ 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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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())
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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,11 +389,14 @@ 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 {
|
||||||
// Return without converting to an RPC error so retry code can
|
if _, ok := err.(transport.PerformedIOError); ok {
|
||||||
// inspect.
|
// Return without converting to an RPC error so retry code can
|
||||||
return err
|
// inspect.
|
||||||
|
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}
|
||||||
|
|
@ -446,7 +417,8 @@ 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 {
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -28,18 +28,29 @@ 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
|
||||||
# Install the pinned versions as defined in module tools.
|
# Check for module support
|
||||||
pushd ./test/tools
|
if go help mod >& /dev/null; then
|
||||||
go install \
|
# Install the pinned versions as defined in module tools.
|
||||||
golang.org/x/lint/golint \
|
pushd ./test/tools
|
||||||
golang.org/x/tools/cmd/goimports \
|
go install \
|
||||||
honnef.co/go/tools/cmd/staticcheck \
|
golang.org/x/lint/golint \
|
||||||
github.com/client9/misspell/cmd/misspell
|
golang.org/x/tools/cmd/goimports \
|
||||||
popd
|
honnef.co/go/tools/cmd/staticcheck \
|
||||||
|
github.com/client9/misspell/cmd/misspell
|
||||||
|
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() {
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue