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/net v0.0.0-20211029224645-99673261e6eb
|
||||
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
|
||||
gopkg.in/square/go-jose.v2 v2.4.1
|
||||
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.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
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/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/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/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/beeker1121/goque v1.0.3-0.20191103205551-d618510128af h1:XbgLdZvVbWsK9HAhAYOp6rksTAdOVYDBQtGSVOLlJrw=
|
||||
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/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
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-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/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||
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/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.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.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/facebookgo/clock v0.0.0-20150410010913-600d898af40a h1:yDWHCSQ40h88yih2JAcL6Ls/kVkSE8GFACTGVnMPruw=
|
||||
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.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
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.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||
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/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.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||
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/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/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 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.5.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
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/trace v0.19.0 h1:1ucYlenXIDA1OlHVLDZKX0ObXV5RLaq06DtUKz5e5zc=
|
||||
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.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
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-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-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-20190311183353-d8887717615a/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-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-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-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
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/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-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-20181108010431-42b317875d0f/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/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-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/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
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.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
|
||||
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.36.1 h1:cmUfbeGKnz9+2DD/UYsMQXeqbHZqZDs4eQwW0sFOpBY=
|
||||
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-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
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.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.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.5/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 (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
|
@ -16,6 +17,7 @@ import (
|
|||
"github.com/letsencrypt/boulder/cmd"
|
||||
bcreds "github.com/letsencrypt/boulder/grpc/creds"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/connectivity"
|
||||
)
|
||||
|
||||
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(
|
||||
"dns:///akamai-purger.boulder:9199",
|
||||
grpc.WithTransportCredentials(creds),
|
||||
grpc.WithDefaultCallOptions(grpc.WaitForReady(true)),
|
||||
)
|
||||
if err != nil {
|
||||
sigterm()
|
||||
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)
|
||||
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.
|
||||
|
||||
## Maintainers (in alphabetical order)
|
||||
|
||||
- [canguler](https://github.com/canguler), Google LLC
|
||||
- [cesarghali](https://github.com/cesarghali), Google LLC
|
||||
- [dfawley](https://github.com/dfawley), Google LLC
|
||||
- [easwars](https://github.com/easwars), Google LLC
|
||||
- [jadekler](https://github.com/jadekler), Google LLC
|
||||
- [menghanl](https://github.com/menghanl), Google LLC
|
||||
- [srini100](https://github.com/srini100), Google LLC
|
||||
|
||||
## Emeritus Maintainers (in alphabetical order)
|
||||
- [adelez](https://github.com/adelez), Google LLC
|
||||
- [canguler](https://github.com/canguler), Google LLC
|
||||
- [iamqizhao](https://github.com/iamqizhao), Google LLC
|
||||
- [jadekler](https://github.com/jadekler), Google LLC
|
||||
- [jtattermusch](https://github.com/jtattermusch), Google LLC
|
||||
- [lyuxuan](https://github.com/lyuxuan), Google LLC
|
||||
- [makmukhi](https://github.com/makmukhi), Google LLC
|
||||
|
|
|
|||
|
|
@ -41,6 +41,8 @@ vetdeps:
|
|||
clean \
|
||||
proto \
|
||||
test \
|
||||
testappengine \
|
||||
testappenginedeps \
|
||||
testrace \
|
||||
vet \
|
||||
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
|
||||
[gRPC]: https://grpc.io
|
||||
[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
|
||||
[go-releases]: https://golang.org/doc/devel/release.html
|
||||
|
|
|
|||
|
|
@ -25,77 +25,55 @@
|
|||
// later release.
|
||||
package attributes
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Attributes is an immutable struct for storing and retrieving generic
|
||||
// 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
|
||||
// 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.
|
||||
// types for keys.
|
||||
type Attributes struct {
|
||||
m map[interface{}]interface{}
|
||||
}
|
||||
|
||||
// New returns a new Attributes containing the key/value pair.
|
||||
func New(key, value interface{}) *Attributes {
|
||||
return &Attributes{m: map[interface{}]interface{}{key: value}}
|
||||
// New returns a new Attributes containing all key/value pairs in kvs. If the
|
||||
// same key appears multiple times, the last value overwrites all previous
|
||||
// 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
|
||||
// and the new key/value pair. If the same key appears multiple times, the
|
||||
// last value overwrites all previous values for that key. To remove an
|
||||
// existing key, use a nil value. value should not be modified later.
|
||||
func (a *Attributes) WithValue(key, value interface{}) *Attributes {
|
||||
// WithValues returns a new Attributes containing all key/value pairs in a and
|
||||
// kvs. Panics if len(kvs) is not even. If the same key appears multiple
|
||||
// times, the last value overwrites all previous values for that key. To
|
||||
// remove an existing key, use a nil value.
|
||||
func (a *Attributes) WithValues(kvs ...interface{}) *Attributes {
|
||||
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 {
|
||||
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
|
||||
}
|
||||
|
||||
// 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{} {
|
||||
if a == nil {
|
||||
return nil
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
// 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
|
||||
// connecting, Balancers must call Connect. If a connection re-enters IDLE,
|
||||
// Balancers must call Connect again to trigger a new connection attempt.
|
||||
// All SubConns start in IDLE, and will not try to connect. To trigger
|
||||
// the connecting, Balancers must call Connect.
|
||||
// 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
|
||||
// remainder once the first connection is successful. If an attempt to connect
|
||||
// to all addresses encounters an error, the SubConn will enter
|
||||
// TRANSIENT_FAILURE for a backoff period, and then transition to IDLE.
|
||||
//
|
||||
// Once established, if a connection is lost, the SubConn will transition
|
||||
// directly to IDLE.
|
||||
//
|
||||
// This interface is to be implemented by gRPC. Users should not need their own
|
||||
// implementation of this interface. For situations like testing, any
|
||||
// implementations should embed this interface. This allows gRPC to add new
|
||||
// methods to this interface.
|
||||
// This interface is to be implemented by gRPC. Users should not need a
|
||||
// brand new implementation of this interface. For the situations like
|
||||
// testing, the new implementation should embed this interface. This allows
|
||||
// gRPC to add new methods to this interface.
|
||||
type SubConn interface {
|
||||
// UpdateAddresses updates the addresses used in this SubConn.
|
||||
// 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.
|
||||
//
|
||||
// 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)
|
||||
// Connect starts the connecting for this SubConn.
|
||||
Connect()
|
||||
|
|
@ -148,13 +143,6 @@ type ClientConn interface {
|
|||
// RemoveSubConn removes the SubConn from ClientConn.
|
||||
// The SubConn will be shutdown.
|
||||
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
|
||||
// changed.
|
||||
|
|
@ -174,32 +162,25 @@ type ClientConn interface {
|
|||
|
||||
// BuildOptions contains additional information for Build.
|
||||
type BuildOptions struct {
|
||||
// DialCreds is the transport credentials to use when communicating with a
|
||||
// remote load balancer server. Balancer implementations which do not
|
||||
// communicate with a remote load balancer server can ignore this field.
|
||||
// DialCreds is the transport credential the Balancer implementation can
|
||||
// use to dial to a remote load balancer server. The Balancer implementations
|
||||
// can ignore this if it does not need to talk to another party securely.
|
||||
DialCreds credentials.TransportCredentials
|
||||
// CredsBundle is the credentials bundle to use when communicating with a
|
||||
// remote load balancer server. Balancer implementations which do not
|
||||
// communicate with a remote load balancer server can ignore this field.
|
||||
// CredsBundle is the credentials bundle that the Balancer can use.
|
||||
CredsBundle credentials.Bundle
|
||||
// Dialer is the custom dialer to use when communicating with a remote load
|
||||
// balancer server. Balancer implementations which do not communicate with a
|
||||
// remote load balancer server can ignore this field.
|
||||
// Dialer is the custom dialer the Balancer implementation can use to dial
|
||||
// to a remote load balancer server. The Balancer implementations
|
||||
// can ignore this if it doesn't need to talk to remote balancer.
|
||||
Dialer func(context.Context, string) (net.Conn, error)
|
||||
// Authority is the server name to use as part of the authentication
|
||||
// handshake when communicating with a remote load balancer server. Balancer
|
||||
// implementations which do not communicate with a remote load balancer
|
||||
// server can ignore this field.
|
||||
Authority string
|
||||
// ChannelzParentID is the parent ClientConn's channelz ID.
|
||||
// ChannelzParentID is the entity parent's channelz unique identification number.
|
||||
ChannelzParentID int64
|
||||
// CustomUserAgent is the custom user agent set on the parent ClientConn.
|
||||
// The balancer should set the same custom user agent if it creates a
|
||||
// ClientConn.
|
||||
CustomUserAgent string
|
||||
// Target contains the parsed address info of the dial target. It is the
|
||||
// same resolver.Target as passed to the resolver. See the documentation for
|
||||
// the resolver.Target type for details about what it contains.
|
||||
// Target contains the parsed address info of the dial target. It is the same resolver.Target as
|
||||
// passed to the resolver.
|
||||
// See the documentation for the resolver.Target type for details about what it contains.
|
||||
Target resolver.Target
|
||||
}
|
||||
|
||||
|
|
@ -335,20 +316,6 @@ type Balancer interface {
|
|||
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.
|
||||
type SubConnState struct {
|
||||
// ConnectivityState is the connectivity state of the SubConn.
|
||||
|
|
@ -376,10 +343,8 @@ var ErrBadResolverState = errors.New("bad resolver state")
|
|||
//
|
||||
// It's not thread safe.
|
||||
type ConnectivityStateEvaluator struct {
|
||||
numReady uint64 // Number of addrConns in ready 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.
|
||||
numReady uint64 // Number of addrConns in ready state.
|
||||
numConnecting uint64 // Number of addrConns in connecting state.
|
||||
}
|
||||
|
||||
// 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;
|
||||
// - 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 if at least one SubConn is Idle, the aggregated state is Idle;
|
||||
// - Else there are no subconns and the aggregated state is Transient Failure
|
||||
// - Else the aggregated state is TransientFailure.
|
||||
//
|
||||
// Shutdown is not considered.
|
||||
// Idle and Shutdown are not considered.
|
||||
func (cse *ConnectivityStateEvaluator) RecordTransition(oldState, newState connectivity.State) connectivity.State {
|
||||
// Update counters.
|
||||
for idx, state := range []connectivity.State{oldState, newState} {
|
||||
|
|
@ -401,10 +364,6 @@ func (cse *ConnectivityStateEvaluator) RecordTransition(oldState, newState conne
|
|||
cse.numReady += updateVal
|
||||
case connectivity.Connecting:
|
||||
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 {
|
||||
return connectivity.Connecting
|
||||
}
|
||||
if cse.numTransientFailure > 0 {
|
||||
return connectivity.TransientFailure
|
||||
}
|
||||
if cse.numIdle > 0 {
|
||||
return connectivity.Idle
|
||||
}
|
||||
return connectivity.TransientFailure
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ func (bb *baseBuilder) Build(cc balancer.ClientConn, opt balancer.BuildOptions)
|
|||
cc: cc,
|
||||
pickerBuilder: bb.pickerBuilder,
|
||||
|
||||
subConns: resolver.NewAddressMap(),
|
||||
subConns: make(map[resolver.Address]balancer.SubConn),
|
||||
scStates: make(map[balancer.SubConn]connectivity.State),
|
||||
csEvltr: &balancer.ConnectivityStateEvaluator{},
|
||||
config: bb.config,
|
||||
|
|
@ -64,7 +64,7 @@ type baseBalancer struct {
|
|||
csEvltr *balancer.ConnectivityStateEvaluator
|
||||
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
|
||||
picker balancer.Picker
|
||||
config Config
|
||||
|
|
@ -75,7 +75,7 @@ type baseBalancer struct {
|
|||
|
||||
func (b *baseBalancer) ResolverError(err error) {
|
||||
b.resolverErr = err
|
||||
if b.subConns.Len() == 0 {
|
||||
if len(b.subConns) == 0 {
|
||||
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.
|
||||
b.resolverErr = nil
|
||||
// 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 {
|
||||
addrsSet.Set(a, nil)
|
||||
if _, ok := b.subConns.Get(a); !ok {
|
||||
// Strip attributes from addresses before using them as map keys. So
|
||||
// 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).
|
||||
//
|
||||
// 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})
|
||||
if err != nil {
|
||||
logger.Warningf("base.baseBalancer: failed to create new SubConn: %v", err)
|
||||
continue
|
||||
}
|
||||
b.subConns.Set(a, sc)
|
||||
b.subConns[aNoAttrs] = sc
|
||||
b.scStates[sc] = connectivity.Idle
|
||||
b.csEvltr.RecordTransition(connectivity.Shutdown, connectivity.Idle)
|
||||
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() {
|
||||
sci, _ := b.subConns.Get(a)
|
||||
sc := sci.(balancer.SubConn)
|
||||
for a, sc := range b.subConns {
|
||||
// a was removed by resolver.
|
||||
if _, ok := addrsSet.Get(a); !ok {
|
||||
if _, ok := addrsSet[a]; !ok {
|
||||
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.
|
||||
// The entry will be deleted in UpdateSubConnState.
|
||||
}
|
||||
|
|
@ -163,9 +184,7 @@ func (b *baseBalancer) regeneratePicker() {
|
|||
readySCs := make(map[balancer.SubConn]SubConnInfo)
|
||||
|
||||
// Filter out all ready SCs from full subConn map.
|
||||
for _, addr := range b.subConns.Keys() {
|
||||
sci, _ := b.subConns.Get(addr)
|
||||
sc := sci.(balancer.SubConn)
|
||||
for addr, sc := range b.subConns {
|
||||
if st, ok := b.scStates[sc]; ok && st == connectivity.Ready {
|
||||
readySCs[sc] = SubConnInfo{Address: addr}
|
||||
}
|
||||
|
|
@ -185,14 +204,10 @@ func (b *baseBalancer) UpdateSubConnState(sc balancer.SubConn, state balancer.Su
|
|||
}
|
||||
return
|
||||
}
|
||||
if oldS == connectivity.TransientFailure &&
|
||||
(s == connectivity.Connecting || s == connectivity.Idle) {
|
||||
// Once a subconn enters TRANSIENT_FAILURE, ignore subsequent IDLE or
|
||||
if oldS == connectivity.TransientFailure && s == connectivity.Connecting {
|
||||
// Once a subconn enters TRANSIENT_FAILURE, ignore subsequent
|
||||
// CONNECTING transitions to prevent the aggregated state from being
|
||||
// always CONNECTING when many backends exist but are all down.
|
||||
if s == connectivity.Idle {
|
||||
sc.Connect()
|
||||
}
|
||||
return
|
||||
}
|
||||
b.scStates[sc] = s
|
||||
|
|
@ -218,6 +233,7 @@ func (b *baseBalancer) UpdateSubConnState(sc balancer.SubConn, state balancer.Su
|
|||
b.state == connectivity.TransientFailure {
|
||||
b.regeneratePicker()
|
||||
}
|
||||
|
||||
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() {
|
||||
}
|
||||
|
||||
// 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().
|
||||
func NewErrPicker(err error) balancer.Picker {
|
||||
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
|
||||
// data should not be mutated after calling Set.
|
||||
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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -47,11 +47,11 @@ func init() {
|
|||
type rrPickerBuilder struct{}
|
||||
|
||||
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 {
|
||||
return base.NewErrPicker(balancer.ErrNoSubConnAvailable)
|
||||
}
|
||||
scs := make([]balancer.SubConn, 0, len(info.ReadySCs))
|
||||
var scs []balancer.SubConn
|
||||
for sc := range info.ReadySCs {
|
||||
scs = append(scs, sc)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,20 +37,14 @@ type scStateUpdate struct {
|
|||
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.
|
||||
// It implements balancer.ClientConn interface.
|
||||
type ccBalancerWrapper struct {
|
||||
cc *ClientConn
|
||||
balancerMu sync.Mutex // synchronizes calls to the balancer
|
||||
balancer balancer.Balancer
|
||||
hasExitIdle bool
|
||||
updateCh *buffer.Unbounded
|
||||
closed *grpcsync.Event
|
||||
done *grpcsync.Event
|
||||
cc *ClientConn
|
||||
balancerMu sync.Mutex // synchronizes calls to the balancer
|
||||
balancer balancer.Balancer
|
||||
scBuffer *buffer.Unbounded
|
||||
done *grpcsync.Event
|
||||
|
||||
mu sync.Mutex
|
||||
subConns map[*acBalancerWrapper]struct{}
|
||||
|
|
@ -59,14 +53,12 @@ type ccBalancerWrapper struct {
|
|||
func newCCBalancerWrapper(cc *ClientConn, b balancer.Builder, bopts balancer.BuildOptions) *ccBalancerWrapper {
|
||||
ccb := &ccBalancerWrapper{
|
||||
cc: cc,
|
||||
updateCh: buffer.NewUnbounded(),
|
||||
closed: grpcsync.NewEvent(),
|
||||
scBuffer: buffer.NewUnbounded(),
|
||||
done: grpcsync.NewEvent(),
|
||||
subConns: make(map[*acBalancerWrapper]struct{}),
|
||||
}
|
||||
go ccb.watcher()
|
||||
ccb.balancer = b.Build(ccb, bopts)
|
||||
_, ccb.hasExitIdle = ccb.balancer.(balancer.ExitIdler)
|
||||
return ccb
|
||||
}
|
||||
|
||||
|
|
@ -75,72 +67,35 @@ func newCCBalancerWrapper(cc *ClientConn, b balancer.Builder, bopts balancer.Bui
|
|||
func (ccb *ccBalancerWrapper) watcher() {
|
||||
for {
|
||||
select {
|
||||
case t := <-ccb.updateCh.Get():
|
||||
ccb.updateCh.Load()
|
||||
if ccb.closed.HasFired() {
|
||||
case t := <-ccb.scBuffer.Get():
|
||||
ccb.scBuffer.Load()
|
||||
if ccb.done.HasFired() {
|
||||
break
|
||||
}
|
||||
switch u := t.(type) {
|
||||
case *scStateUpdate:
|
||||
ccb.balancerMu.Lock()
|
||||
ccb.balancer.UpdateSubConnState(u.sc, balancer.SubConnState{ConnectivityState: u.state, ConnectionError: u.err})
|
||||
ccb.balancerMu.Unlock()
|
||||
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():
|
||||
ccb.balancerMu.Lock()
|
||||
su := t.(*scStateUpdate)
|
||||
ccb.balancer.UpdateSubConnState(su.sc, balancer.SubConnState{ConnectivityState: su.state, ConnectionError: su.err})
|
||||
ccb.balancerMu.Unlock()
|
||||
case <-ccb.done.Done():
|
||||
}
|
||||
|
||||
if ccb.closed.HasFired() {
|
||||
ccb.balancerMu.Lock()
|
||||
if ccb.done.HasFired() {
|
||||
ccb.balancer.Close()
|
||||
ccb.balancerMu.Unlock()
|
||||
ccb.mu.Lock()
|
||||
scs := ccb.subConns
|
||||
ccb.subConns = nil
|
||||
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 {
|
||||
ccb.cc.removeAddrConn(acbw.getAddrConn(), errConnDrain)
|
||||
}
|
||||
ccb.UpdateState(balancer.State{ConnectivityState: connectivity.Connecting, Picker: nil})
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (ccb *ccBalancerWrapper) close() {
|
||||
ccb.closed.Fire()
|
||||
<-ccb.done.Done()
|
||||
}
|
||||
|
||||
func (ccb *ccBalancerWrapper) exitIdle() bool {
|
||||
if !ccb.hasExitIdle {
|
||||
return false
|
||||
}
|
||||
ccb.updateCh.Put(exitIdle{})
|
||||
return true
|
||||
ccb.done.Fire()
|
||||
}
|
||||
|
||||
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 {
|
||||
return
|
||||
}
|
||||
ccb.updateCh.Put(&scStateUpdate{
|
||||
ccb.scBuffer.Put(&scStateUpdate{
|
||||
sc: sc,
|
||||
state: s,
|
||||
err: err,
|
||||
|
|
@ -169,8 +124,8 @@ func (ccb *ccBalancerWrapper) updateClientConnState(ccs *balancer.ClientConnStat
|
|||
|
||||
func (ccb *ccBalancerWrapper) resolverError(err error) {
|
||||
ccb.balancerMu.Lock()
|
||||
defer ccb.balancerMu.Unlock()
|
||||
ccb.balancer.ResolverError(err)
|
||||
ccb.balancerMu.Unlock()
|
||||
}
|
||||
|
||||
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) {
|
||||
// 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)
|
||||
if !ok {
|
||||
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) {
|
||||
|
|
@ -243,7 +197,7 @@ func (acbw *acBalancerWrapper) UpdateAddresses(addrs []resolver.Address) {
|
|||
acbw.mu.Lock()
|
||||
defer acbw.mu.Unlock()
|
||||
if len(addrs) <= 0 {
|
||||
acbw.ac.cc.removeAddrConn(acbw.ac, errConnDrain)
|
||||
acbw.ac.tearDown(errConnDrain)
|
||||
return
|
||||
}
|
||||
if !acbw.ac.tryUpdateAddrs(addrs) {
|
||||
|
|
@ -258,23 +212,23 @@ func (acbw *acBalancerWrapper) UpdateAddresses(addrs []resolver.Address) {
|
|||
acbw.ac.acbw = nil
|
||||
acbw.ac.mu.Unlock()
|
||||
acState := acbw.ac.getState()
|
||||
acbw.ac.cc.removeAddrConn(acbw.ac, errConnDrain)
|
||||
acbw.ac.tearDown(errConnDrain)
|
||||
|
||||
if acState == connectivity.Shutdown {
|
||||
return
|
||||
}
|
||||
|
||||
newAC, err := cc.newAddrConn(addrs, opts)
|
||||
ac, err := cc.newAddrConn(addrs, opts)
|
||||
if err != nil {
|
||||
channelz.Warningf(logger, acbw.ac.channelzID, "acBalancerWrapper: UpdateAddresses: failed to newAddrConn: %v", err)
|
||||
return
|
||||
}
|
||||
acbw.ac = newAC
|
||||
newAC.mu.Lock()
|
||||
newAC.acbw = acbw
|
||||
newAC.mu.Unlock()
|
||||
acbw.ac = ac
|
||||
ac.mu.Lock()
|
||||
ac.acbw = acbw
|
||||
ac.mu.Unlock()
|
||||
if acState != connectivity.Idle {
|
||||
go newAC.connect()
|
||||
ac.connect()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -282,7 +236,7 @@ func (acbw *acBalancerWrapper) UpdateAddresses(addrs []resolver.Address) {
|
|||
func (acbw *acBalancerWrapper) Connect() {
|
||||
acbw.mu.Lock()
|
||||
defer acbw.mu.Unlock()
|
||||
go acbw.ac.connect()
|
||||
acbw.ac.connect()
|
||||
}
|
||||
|
||||
func (acbw *acBalancerWrapper) getAddrConn() *addrConn {
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"strings"
|
||||
"sync"
|
||||
|
|
@ -38,6 +37,7 @@ import (
|
|||
"google.golang.org/grpc/internal/backoff"
|
||||
"google.golang.org/grpc/internal/channelz"
|
||||
"google.golang.org/grpc/internal/grpcsync"
|
||||
"google.golang.org/grpc/internal/grpcutil"
|
||||
iresolver "google.golang.org/grpc/internal/resolver"
|
||||
"google.golang.org/grpc/internal/transport"
|
||||
"google.golang.org/grpc/keepalive"
|
||||
|
|
@ -83,13 +83,13 @@ var (
|
|||
// errTransportCredsAndBundle indicates that creds bundle is used together
|
||||
// with other individual Transport Credentials.
|
||||
errTransportCredsAndBundle = errors.New("grpc: credentials.Bundle may not be used with individual TransportCredentials")
|
||||
// errNoTransportCredsInBundle indicated that the configured creds bundle
|
||||
// returned a transport credentials which was nil.
|
||||
errNoTransportCredsInBundle = errors.New("grpc: credentials.Bundle must return non-nil transport credentials")
|
||||
// errTransportCredentialsMissing indicates that users want to transmit
|
||||
// security information (e.g., OAuth2 token) which requires secure
|
||||
// connection on an insecure connection.
|
||||
// errTransportCredentialsMissing 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)")
|
||||
// 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 (
|
||||
|
|
@ -143,7 +143,6 @@ func DialContext(ctx context.Context, target string, opts ...DialOption) (conn *
|
|||
firstResolveEvent: grpcsync.NewEvent(),
|
||||
}
|
||||
cc.retryThrottler.Store((*retryThrottler)(nil))
|
||||
cc.safeConfigSelector.UpdateConfigSelector(&defaultConfigSelector{nil})
|
||||
cc.ctx, cc.cancel = context.WithCancel(context.Background())
|
||||
|
||||
for _, opt := range opts {
|
||||
|
|
@ -177,20 +176,17 @@ func DialContext(ctx context.Context, target string, opts ...DialOption) (conn *
|
|||
cc.csMgr.channelzID = cc.channelzID
|
||||
}
|
||||
|
||||
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.CredsBundle != nil && cc.dopts.copts.CredsBundle.TransportCredentials() == nil {
|
||||
return nil, errNoTransportCredsInBundle
|
||||
}
|
||||
transportCreds := cc.dopts.copts.TransportCredentials
|
||||
if transportCreds == nil {
|
||||
transportCreds = cc.dopts.copts.CredsBundle.TransportCredentials()
|
||||
}
|
||||
if transportCreds.Info().SecurityProtocol == "insecure" {
|
||||
if !cc.dopts.insecure {
|
||||
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
|
||||
}
|
||||
} else {
|
||||
if cc.dopts.copts.TransportCredentials != nil || cc.dopts.copts.CredsBundle != nil {
|
||||
return nil, errCredentialsConflict
|
||||
}
|
||||
for _, cd := range cc.dopts.copts.PerRPCCredentials {
|
||||
if cd.RequireTransportSecurity() {
|
||||
return nil, errTransportCredentialsMissing
|
||||
|
|
@ -251,15 +247,38 @@ func DialContext(ctx context.Context, target string, opts ...DialOption) (conn *
|
|||
}
|
||||
|
||||
// Determine the resolver to use.
|
||||
resolverBuilder, err := cc.parseTargetAndFindResolver()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
cc.parsedTarget = grpcutil.ParseTarget(cc.target, cc.dopts.copts.Dialer != nil)
|
||||
channelz.Infof(logger, cc.channelzID, "parsed scheme: %q", cc.parsedTarget.Scheme)
|
||||
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 {
|
||||
return nil, err
|
||||
|
||||
creds := cc.dopts.copts.TransportCredentials
|
||||
if creds != nil && creds.Info().ServerName != "" {
|
||||
cc.authority = creds.Info().ServerName
|
||||
} else if cc.dopts.insecure && cc.dopts.authority != "" {
|
||||
cc.authority = cc.dopts.authority
|
||||
} else if strings.HasPrefix(cc.target, "unix:") || strings.HasPrefix(cc.target, "unix-abstract:") {
|
||||
cc.authority = "localhost"
|
||||
} else if strings.HasPrefix(cc.parsedTarget.Endpoint, ":") {
|
||||
cc.authority = "localhost" + cc.parsedTarget.Endpoint
|
||||
} else {
|
||||
// Use endpoint from "scheme://authority/endpoint" as the default
|
||||
// authority for ClientConn.
|
||||
cc.authority = cc.parsedTarget.Endpoint
|
||||
}
|
||||
channelz.Infof(logger, cc.channelzID, "Channel authority set to %q", cc.authority)
|
||||
|
||||
if cc.dopts.scChan != nil && !scSet {
|
||||
// Blocking wait for the initial service config.
|
||||
|
|
@ -285,7 +304,6 @@ func DialContext(ctx context.Context, target string, opts ...DialOption) (conn *
|
|||
DialCreds: credsClone,
|
||||
CredsBundle: cc.dopts.copts.CredsBundle,
|
||||
Dialer: cc.dopts.copts.Dialer,
|
||||
Authority: cc.authority,
|
||||
CustomUserAgent: cc.dopts.copts.UserAgent,
|
||||
ChannelzParentID: cc.channelzID,
|
||||
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.
|
||||
if cc.dopts.block {
|
||||
for {
|
||||
cc.Connect()
|
||||
s := cc.GetState()
|
||||
if s == connectivity.Ready {
|
||||
break
|
||||
|
|
@ -521,31 +538,12 @@ func (cc *ClientConn) WaitForStateChange(ctx context.Context, sourceState connec
|
|||
//
|
||||
// Experimental
|
||||
//
|
||||
// Notice: This API is EXPERIMENTAL and may be changed or removed in a later
|
||||
// release.
|
||||
// Notice: This API is EXPERIMENTAL and may be changed or removed in a
|
||||
// later release.
|
||||
func (cc *ClientConn) GetState() connectivity.State {
|
||||
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() {
|
||||
for {
|
||||
select {
|
||||
|
|
@ -633,10 +631,7 @@ func (cc *ClientConn) updateResolverState(s resolver.State, err error) error {
|
|||
}
|
||||
|
||||
var ret error
|
||||
if cc.dopts.disableServiceConfig {
|
||||
channelz.Infof(logger, cc.channelzID, "ignoring service config from resolver (%v) and applying the default because service config is disabled", s.ServiceConfig)
|
||||
cc.maybeApplyDefaultServiceConfig(s.Addresses)
|
||||
} else if s.ServiceConfig == nil {
|
||||
if cc.dopts.disableServiceConfig || s.ServiceConfig == nil {
|
||||
cc.maybeApplyDefaultServiceConfig(s.Addresses)
|
||||
// TODO: do we need to apply a failing LB policy if there is no
|
||||
// default, per the error handling design?
|
||||
|
|
@ -715,12 +710,7 @@ func (cc *ClientConn) switchBalancer(name string) {
|
|||
return
|
||||
}
|
||||
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.mu.Lock()
|
||||
}
|
||||
|
||||
builder := balancer.Get(name)
|
||||
|
|
@ -849,7 +839,8 @@ func (ac *addrConn) connect() error {
|
|||
ac.updateConnectivityState(connectivity.Connecting, nil)
|
||||
ac.mu.Unlock()
|
||||
|
||||
ac.resetTransport()
|
||||
// Start a goroutine connecting to the server asynchronously.
|
||||
go ac.resetTransport()
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -886,7 +877,6 @@ func (ac *addrConn) tryUpdateAddrs(addrs []resolver.Address) bool {
|
|||
// ac.state is Ready, try to find the connected address.
|
||||
var curAddrFound bool
|
||||
for _, a := range addrs {
|
||||
a.ServerName = ac.cc.getServerName(a)
|
||||
if reflect.DeepEqual(ac.curAddr, a) {
|
||||
curAddrFound = true
|
||||
break
|
||||
|
|
@ -900,26 +890,6 @@ func (ac *addrConn) tryUpdateAddrs(addrs []resolver.Address) bool {
|
|||
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 {
|
||||
if sc == nil {
|
||||
return MethodConfig{}
|
||||
|
|
@ -1075,12 +1045,12 @@ func (cc *ClientConn) Close() error {
|
|||
|
||||
cc.blockingpicker.close()
|
||||
|
||||
if bWrapper != nil {
|
||||
bWrapper.close()
|
||||
}
|
||||
if rWrapper != nil {
|
||||
rWrapper.close()
|
||||
}
|
||||
if bWrapper != nil {
|
||||
bWrapper.close()
|
||||
}
|
||||
|
||||
for ac := range conns {
|
||||
ac.tearDown(ErrClientConnClosing)
|
||||
|
|
@ -1159,86 +1129,112 @@ func (ac *addrConn) adjustParams(r transport.GoAwayReason) {
|
|||
}
|
||||
|
||||
func (ac *addrConn) resetTransport() {
|
||||
ac.mu.Lock()
|
||||
if ac.state == connectivity.Shutdown {
|
||||
ac.mu.Unlock()
|
||||
return
|
||||
}
|
||||
for i := 0; ; i++ {
|
||||
if i > 0 {
|
||||
ac.cc.resolveNow(resolver.ResolveNowOptions{})
|
||||
}
|
||||
|
||||
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()
|
||||
if ac.state == connectivity.Shutdown {
|
||||
ac.mu.Unlock()
|
||||
return
|
||||
}
|
||||
ac.updateConnectivityState(connectivity.TransientFailure, err)
|
||||
|
||||
// Backoff.
|
||||
b := ac.resetBackoff
|
||||
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.transport = nil
|
||||
ac.mu.Unlock()
|
||||
|
||||
timer := time.NewTimer(backoffFor)
|
||||
select {
|
||||
case <-timer.C:
|
||||
newTr, addr, reconnect, err := ac.tryAllAddrs(addrs, connectDeadline)
|
||||
if err != nil {
|
||||
// After exhausting all addresses, the addrConn enters
|
||||
// TRANSIENT_FAILURE.
|
||||
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()
|
||||
case <-b:
|
||||
timer.Stop()
|
||||
case <-ac.ctx.Done():
|
||||
timer.Stop()
|
||||
return
|
||||
|
||||
timer := time.NewTimer(backoffFor)
|
||||
select {
|
||||
case <-timer.C:
|
||||
ac.mu.Lock()
|
||||
ac.backoffIdx++
|
||||
ac.mu.Unlock()
|
||||
case <-b:
|
||||
timer.Stop()
|
||||
case <-ac.ctx.Done():
|
||||
timer.Stop()
|
||||
return
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
ac.mu.Lock()
|
||||
if ac.state != connectivity.Shutdown {
|
||||
ac.updateConnectivityState(connectivity.Idle, err)
|
||||
if ac.state == connectivity.Shutdown {
|
||||
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()
|
||||
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
|
||||
// the first successful one. It returns an error if no address was successfully
|
||||
// connected, or updates ac appropriately with the new transport.
|
||||
func (ac *addrConn) tryAllAddrs(addrs []resolver.Address, connectDeadline time.Time) error {
|
||||
// tryAllAddrs tries to creates a connection to the addresses, and stop when at the
|
||||
// first successful one. It returns the transport, the address and a Event in
|
||||
// the successful case. The Event fires when the returned transport disconnects.
|
||||
func (ac *addrConn) tryAllAddrs(addrs []resolver.Address, connectDeadline time.Time) (transport.ClientTransport, resolver.Address, *grpcsync.Event, error) {
|
||||
var firstConnErr error
|
||||
for _, addr := range addrs {
|
||||
ac.mu.Lock()
|
||||
if ac.state == connectivity.Shutdown {
|
||||
ac.mu.Unlock()
|
||||
return errConnClosing
|
||||
return nil, resolver.Address{}, nil, errConnClosing
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
err := ac.createTransport(addr, copts, connectDeadline)
|
||||
newTr, reconnect, err := ac.createTransport(addr, copts, connectDeadline)
|
||||
if err == nil {
|
||||
return nil
|
||||
return newTr, addr, reconnect, nil
|
||||
}
|
||||
if firstConnErr == nil {
|
||||
firstConnErr = err
|
||||
|
|
@ -1264,50 +1260,57 @@ func (ac *addrConn) tryAllAddrs(addrs []resolver.Address, connectDeadline time.T
|
|||
}
|
||||
|
||||
// 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
|
||||
// address was not successfully connected, or updates ac appropriately with the
|
||||
// new transport.
|
||||
func (ac *addrConn) createTransport(addr resolver.Address, copts transport.ConnectOptions, connectDeadline time.Time) error {
|
||||
// TODO: Delete prefaceReceived and move the logic to wait for it into the
|
||||
// transport.
|
||||
prefaceReceived := grpcsync.NewEvent()
|
||||
connClosed := grpcsync.NewEvent()
|
||||
// createTransport creates a connection to addr. It returns the transport and a
|
||||
// Event in the successful case. The Event fires when the returned transport
|
||||
// disconnects.
|
||||
func (ac *addrConn) createTransport(addr resolver.Address, copts transport.ConnectOptions, connectDeadline time.Time) (transport.ClientTransport, *grpcsync.Event, error) {
|
||||
prefaceReceived := make(chan struct{})
|
||||
onCloseCalled := make(chan struct{})
|
||||
reconnect := grpcsync.NewEvent()
|
||||
|
||||
addr.ServerName = ac.cc.getServerName(addr)
|
||||
hctx, hcancel := context.WithCancel(ac.ctx)
|
||||
hcStarted := false // protected by ac.mu
|
||||
|
||||
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)
|
||||
}
|
||||
// addr.ServerName takes precedent over ClientConn authority, if present.
|
||||
if addr.ServerName == "" {
|
||||
addr.ServerName = ac.cc.authority
|
||||
}
|
||||
|
||||
once := sync.Once{}
|
||||
onGoAway := func(r transport.GoAwayReason) {
|
||||
ac.mu.Lock()
|
||||
ac.adjustParams(r)
|
||||
once.Do(func() {
|
||||
if ac.state == connectivity.Ready {
|
||||
// Prevent this SubConn from being used for new RPCs by setting its
|
||||
// state to Connecting.
|
||||
//
|
||||
// TODO: this should be Idle when grpc-go properly supports it.
|
||||
ac.updateConnectivityState(connectivity.Connecting, nil)
|
||||
}
|
||||
})
|
||||
ac.mu.Unlock()
|
||||
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)
|
||||
|
|
@ -1316,67 +1319,27 @@ func (ac *addrConn) createTransport(addr resolver.Address, copts transport.Conne
|
|||
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 {
|
||||
// newTr is either nil, or closed.
|
||||
channelz.Warningf(logger, ac.channelzID, "grpc: addrConn.createTransport failed to connect to %v. Err: %v", addr, err)
|
||||
return err
|
||||
channelz.Warningf(logger, ac.channelzID, "grpc: addrConn.createTransport failed to connect to %v. Err: %v. Reconnecting...", addr, err)
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
select {
|
||||
case <-connectCtx.Done():
|
||||
case <-time.After(time.Until(connectDeadline)):
|
||||
// We didn't get the preface in time.
|
||||
// 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.
|
||||
newTr.Close(transport.ErrConnClosing)
|
||||
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():
|
||||
newTr.Close()
|
||||
channelz.Warningf(logger, ac.channelzID, "grpc: addrConn.createTransport failed to connect to %v: didn't receive server preface in time. Reconnecting...", addr)
|
||||
return nil, nil, errors.New("timed out waiting for server handshake")
|
||||
case <-prefaceReceived:
|
||||
// We got the preface - huzzah! things are good.
|
||||
ac.mu.Lock()
|
||||
defer ac.mu.Unlock()
|
||||
if connClosed.HasFired() {
|
||||
// onClose called first; go idle but do nothing else.
|
||||
if ac.state != connectivity.Shutdown {
|
||||
ac.updateConnectivityState(connectivity.Idle, nil)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if ac.state == connectivity.Shutdown {
|
||||
// This can happen if the subConn was removed while in `Connecting`
|
||||
// state. tearDown() would have set the state to `Shutdown`, but
|
||||
// would not have closed the transport since ac.transport would not
|
||||
// been set at that point.
|
||||
//
|
||||
// We run this in a goroutine because newTr.Close() calls onClose()
|
||||
// inline, which requires locking ac.mu.
|
||||
//
|
||||
// The error we pass to Close() is immaterial since there are no open
|
||||
// streams at this point, so no trailers with error details will be sent
|
||||
// out. We just need to pass a non-nil error.
|
||||
go newTr.Close(transport.ErrConnClosing)
|
||||
return nil
|
||||
}
|
||||
ac.curAddr = addr
|
||||
ac.transport = newTr
|
||||
hcStarted = true
|
||||
ac.startHealthCheck(hctx) // Will set state to READY if appropriate.
|
||||
return nil
|
||||
case <-connClosed.Done():
|
||||
// The transport has already closed. If we received the preface, too,
|
||||
// this is not an error.
|
||||
select {
|
||||
case <-prefaceReceived.Done():
|
||||
return nil
|
||||
default:
|
||||
return errors.New("connection closed before server preface received")
|
||||
}
|
||||
case <-onCloseCalled:
|
||||
// The transport has already closed - noop.
|
||||
return nil, nil, errors.New("connection closed")
|
||||
// TODO(deklerk) this should bail on ac.ctx.Done(). Add a test and fix.
|
||||
}
|
||||
return newTr, reconnect, nil
|
||||
}
|
||||
|
||||
// startHealthCheck starts the health checking stream (RPC) to watch the health
|
||||
|
|
@ -1460,20 +1423,33 @@ func (ac *addrConn) resetConnectBackoff() {
|
|||
ac.mu.Unlock()
|
||||
}
|
||||
|
||||
// getReadyTransport returns the transport if ac's state is READY or nil if not.
|
||||
func (ac *addrConn) getReadyTransport() transport.ClientTransport {
|
||||
// getReadyTransport returns the transport if ac's state is READY.
|
||||
// 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()
|
||||
defer ac.mu.Unlock()
|
||||
if ac.state == connectivity.Ready {
|
||||
return ac.transport
|
||||
if ac.state == connectivity.Ready && ac.transport != nil {
|
||||
t := 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.
|
||||
//
|
||||
// Note that tearDown doesn't remove ac from ac.cc.conns, so the addrConn struct
|
||||
// will leak. In most cases, call cc.removeAddrConn() instead.
|
||||
// TODO(zhaoq): Make this synchronous to avoid unbounded memory consumption in
|
||||
// some edge cases (e.g., the caller opens and closes many addrConn's in a
|
||||
// tight loop.
|
||||
// tearDown doesn't remove ac from ac.cc.conns.
|
||||
func (ac *addrConn) tearDown(err error) {
|
||||
ac.mu.Lock()
|
||||
if ac.state == connectivity.Shutdown {
|
||||
|
|
@ -1618,114 +1594,3 @@ func (cc *ClientConn) connectionError() error {
|
|||
defer cc.lceMu.Unlock()
|
||||
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.
|
||||
// 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
|
||||
|
||||
import (
|
||||
|
|
@ -44,7 +45,7 @@ func (s State) String() string {
|
|||
return "SHUTDOWN"
|
||||
default:
|
||||
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
|
||||
)
|
||||
|
||||
// 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"
|
||||
"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
|
||||
|
|
@ -140,11 +140,6 @@ type TransportCredentials interface {
|
|||
// Additionally, ClientHandshakeInfo data will be available via the context
|
||||
// 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.
|
||||
ClientHandshake(context.Context, string, net.Conn) (net.Conn, AuthInfo, error)
|
||||
// ServerHandshake does the authentication handshake for servers. It returns
|
||||
|
|
@ -158,13 +153,9 @@ type TransportCredentials interface {
|
|||
Info() ProtocolInfo
|
||||
// Clone makes a copy of this TransportCredentials.
|
||||
Clone() TransportCredentials
|
||||
// OverrideServerName specifies the value used for the following:
|
||||
// - verifying the hostname on the returned certificates
|
||||
// - as SNI in the client's handshake to support virtual hosting
|
||||
// - as the value for `:authority` header at stream creation time
|
||||
//
|
||||
// Deprecated: use grpc.WithAuthority instead. Will be supported
|
||||
// throughout 1.x.
|
||||
// OverrideServerName overrides the server name used to verify the hostname on the returned certificates from the server.
|
||||
// gRPC internals also use it to override the virtual hosting name if it is set.
|
||||
// It must be called before dialing. Currently, this is only used by grpclb.
|
||||
OverrideServerName(string) error
|
||||
}
|
||||
|
||||
|
|
@ -178,18 +169,8 @@ type TransportCredentials interface {
|
|||
//
|
||||
// This API is experimental.
|
||||
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
|
||||
|
||||
// PerRPCCredentials returns the per-RPC credentials from the Bundle.
|
||||
//
|
||||
// May be nil if per-RPC credentials are not needed.
|
||||
PerRPCCredentials() PerRPCCredentials
|
||||
|
||||
// NewWithMode should make a copy of Bundle, and switch mode. Modifying the
|
||||
// existing Bundle may cause races.
|
||||
//
|
||||
|
|
@ -207,12 +188,15 @@ type RequestInfo struct {
|
|||
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.
|
||||
//
|
||||
// This API is experimental.
|
||||
func RequestInfoFromContext(ctx context.Context) (ri RequestInfo, ok bool) {
|
||||
ri, ok = icredentials.RequestInfoFromContext(ctx).(RequestInfo)
|
||||
return ri, ok
|
||||
ri, ok = ctx.Value(requestInfoKey{}).(RequestInfo)
|
||||
return
|
||||
}
|
||||
|
||||
// ClientHandshakeInfo holds data to be passed to ClientHandshake. This makes
|
||||
|
|
@ -227,12 +211,16 @@ type ClientHandshakeInfo struct {
|
|||
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
|
||||
// in ctx.
|
||||
//
|
||||
// This API is experimental.
|
||||
func ClientHandshakeInfoFromContext(ctx context.Context) ClientHandshakeInfo {
|
||||
chi, _ := icredentials.ClientHandshakeInfoFromContext(ctx).(ClientHandshakeInfo)
|
||||
chi, _ := ctx.Value(clientHandshakeInfoKey{}).(ClientHandshakeInfo)
|
||||
return chi
|
||||
}
|
||||
|
||||
|
|
@ -261,6 +249,15 @@ func CheckSecurityLevel(ai AuthInfo, level SecurityLevel) error {
|
|||
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
|
||||
// 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_CHACHA20_POLY1305: "TLS_ECDHE_RSA_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/balancer"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
"google.golang.org/grpc/internal"
|
||||
internalbackoff "google.golang.org/grpc/internal/backoff"
|
||||
"google.golang.org/grpc/internal/envconfig"
|
||||
"google.golang.org/grpc/internal/transport"
|
||||
"google.golang.org/grpc/keepalive"
|
||||
"google.golang.org/grpc/resolver"
|
||||
|
|
@ -50,6 +50,7 @@ type dialOptions struct {
|
|||
bs internalbackoff.Strategy
|
||||
block bool
|
||||
returnLastError bool
|
||||
insecure bool
|
||||
timeout time.Duration
|
||||
scChan <-chan ServiceConfig
|
||||
authority string
|
||||
|
|
@ -65,7 +66,11 @@ type dialOptions struct {
|
|||
minConnectTimeout func() time.Duration
|
||||
defaultServiceConfig *ServiceConfig // defaultServiceConfig is parsed from defaultServiceConfigRawJSON.
|
||||
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.
|
||||
|
|
@ -227,14 +232,18 @@ func WithServiceConfig(c <-chan ServiceConfig) DialOption {
|
|||
})
|
||||
}
|
||||
|
||||
// WithConnectParams configures the ClientConn to use the provided ConnectParams
|
||||
// for creating and maintaining connections to servers.
|
||||
// WithConnectParams configures the dialer to use the provided ConnectParams.
|
||||
//
|
||||
// The backoff configuration specified as part of the ConnectParams overrides
|
||||
// all defaults specified in
|
||||
// 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
|
||||
// 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 {
|
||||
return newFuncDialOption(func(o *dialOptions) {
|
||||
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
|
||||
// connecting the server happens in background.
|
||||
func WithBlock() DialOption {
|
||||
|
|
@ -298,17 +307,11 @@ func WithReturnConnectionError() DialOption {
|
|||
}
|
||||
|
||||
// WithInsecure returns a DialOption which disables transport security for this
|
||||
// ClientConn. Under the hood, it uses insecure.NewCredentials().
|
||||
//
|
||||
// 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.
|
||||
// ClientConn. Note that transport security is required unless WithInsecure is
|
||||
// set.
|
||||
func WithInsecure() DialOption {
|
||||
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
|
||||
// :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 {
|
||||
return newFuncDialOption(func(o *dialOptions) {
|
||||
o.authority = a
|
||||
|
|
@ -519,16 +523,14 @@ func WithDisableServiceConfig() DialOption {
|
|||
// WithDefaultServiceConfig returns a DialOption that configures the default
|
||||
// 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
|
||||
// invalid service config.
|
||||
// Experimental
|
||||
//
|
||||
// The parameter s is the JSON representation of the default service config.
|
||||
// For more information about service configs, see:
|
||||
// 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
|
||||
// Notice: This API is EXPERIMENTAL and may be changed or removed in a
|
||||
// later release.
|
||||
func WithDefaultServiceConfig(s string) DialOption {
|
||||
return newFuncDialOption(func(o *dialOptions) {
|
||||
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
|
||||
// unprocessed by the remote server.
|
||||
//
|
||||
// Retry support is currently enabled by default, but may be disabled by
|
||||
// setting the environment variable "GRPC_GO_RETRY" to "off".
|
||||
// Retry support is currently disabled by default, but will be enabled by
|
||||
// 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 {
|
||||
return newFuncDialOption(func(o *dialOptions) {
|
||||
o.disableRetry = true
|
||||
|
|
@ -581,12 +589,14 @@ func withHealthCheckFunc(f internal.HealthChecker) DialOption {
|
|||
|
||||
func defaultDialOptions() dialOptions {
|
||||
return dialOptions{
|
||||
disableRetry: !envconfig.Retry,
|
||||
healthCheckFunc: internal.HealthCheckFunc,
|
||||
copts: transport.ConnectOptions{
|
||||
WriteBufferSize: defaultWriteBufSize,
|
||||
ReadBufferSize: defaultReadBufSize,
|
||||
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
|
||||
// locally with the ClientConn without needing to be globally registered via
|
||||
// resolver.Register. They will be matched against the scheme used for the
|
||||
|
|
|
|||
|
|
@ -19,14 +19,11 @@
|
|||
package grpclog
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"google.golang.org/grpc/internal/grpclog"
|
||||
)
|
||||
|
|
@ -98,9 +95,8 @@ var severityName = []string{
|
|||
|
||||
// loggerT is the default logger used by grpclog.
|
||||
type loggerT struct {
|
||||
m []*log.Logger
|
||||
v int
|
||||
jsonFormat bool
|
||||
m []*log.Logger
|
||||
v int
|
||||
}
|
||||
|
||||
// NewLoggerV2 creates a loggerV2 with the provided writers.
|
||||
|
|
@ -109,32 +105,19 @@ type loggerT struct {
|
|||
// Warning logs will be written to warningW and infoW.
|
||||
// Info logs will be written to infoW.
|
||||
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
|
||||
// verbosity level.
|
||||
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
|
||||
flag := log.LstdFlags
|
||||
if c.jsonFormat {
|
||||
flag = 0
|
||||
}
|
||||
m = append(m, log.New(infoW, "", flag))
|
||||
m = append(m, log.New(io.MultiWriter(infoW, warningW), "", flag))
|
||||
m = append(m, log.New(infoW, severityName[infoLog]+": ", log.LstdFlags))
|
||||
m = append(m, log.New(io.MultiWriter(infoW, warningW), severityName[warningLog]+": ", log.LstdFlags))
|
||||
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, "", flag))
|
||||
return &loggerT{m: m, v: c.verbose, jsonFormat: c.jsonFormat}
|
||||
m = append(m, log.New(ew, severityName[errorLog]+": ", log.LstdFlags))
|
||||
m = append(m, log.New(ew, severityName[fatalLog]+": ", log.LstdFlags))
|
||||
return &loggerT{m: m, v: v}
|
||||
}
|
||||
|
||||
// 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 {
|
||||
v = vl
|
||||
}
|
||||
|
||||
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))
|
||||
return NewLoggerV2WithVerbosity(infoW, warningW, errorW, v)
|
||||
}
|
||||
|
||||
func (g *loggerT) Info(args ...interface{}) {
|
||||
g.output(infoLog, fmt.Sprint(args...))
|
||||
g.m[infoLog].Print(args...)
|
||||
}
|
||||
|
||||
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{}) {
|
||||
g.output(infoLog, fmt.Sprintf(format, args...))
|
||||
g.m[infoLog].Printf(format, args...)
|
||||
}
|
||||
|
||||
func (g *loggerT) Warning(args ...interface{}) {
|
||||
g.output(warningLog, fmt.Sprint(args...))
|
||||
g.m[warningLog].Print(args...)
|
||||
}
|
||||
|
||||
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{}) {
|
||||
g.output(warningLog, fmt.Sprintf(format, args...))
|
||||
g.m[warningLog].Printf(format, args...)
|
||||
}
|
||||
|
||||
func (g *loggerT) Error(args ...interface{}) {
|
||||
g.output(errorLog, fmt.Sprint(args...))
|
||||
g.m[errorLog].Print(args...)
|
||||
}
|
||||
|
||||
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{}) {
|
||||
g.output(errorLog, fmt.Sprintf(format, args...))
|
||||
g.m[errorLog].Printf(format, args...)
|
||||
}
|
||||
|
||||
func (g *loggerT) Fatal(args ...interface{}) {
|
||||
g.output(fatalLog, fmt.Sprint(args...))
|
||||
os.Exit(1)
|
||||
g.m[fatalLog].Fatal(args...)
|
||||
// No need to call os.Exit() again because log.Logger.Fatal() calls os.Exit().
|
||||
}
|
||||
|
||||
func (g *loggerT) Fatalln(args ...interface{}) {
|
||||
g.output(fatalLog, fmt.Sprintln(args...))
|
||||
os.Exit(1)
|
||||
g.m[fatalLog].Fatalln(args...)
|
||||
// No need to call os.Exit() again because log.Logger.Fatal() calls os.Exit().
|
||||
}
|
||||
|
||||
func (g *loggerT) Fatalf(format string, args ...interface{}) {
|
||||
g.output(fatalLog, fmt.Sprintf(format, args...))
|
||||
os.Exit(1)
|
||||
g.m[fatalLog].Fatalf(format, args...)
|
||||
// No need to call os.Exit() again because log.Logger.Fatal() calls os.Exit().
|
||||
}
|
||||
|
||||
func (g *loggerT) V(l int) bool {
|
||||
|
|
@ -248,12 +210,12 @@ func (g *loggerT) V(l int) bool {
|
|||
// later release.
|
||||
type DepthLoggerV2 interface {
|
||||
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{})
|
||||
// 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{})
|
||||
// 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{})
|
||||
// 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{})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,4 @@
|
|||
// 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
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
b, err := proto.Marshal(e)
|
||||
if err != nil {
|
||||
grpclogLogger.Errorf("binary logging: failed to marshal proto message: %v", err)
|
||||
return err
|
||||
grpclogLogger.Infof("binary logging: failed to marshal proto message: %v", err)
|
||||
}
|
||||
hdr := make([]byte, 4)
|
||||
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 }
|
||||
|
||||
type bufferedSink struct {
|
||||
mu sync.Mutex
|
||||
closer io.Closer
|
||||
out Sink // out is built on buf.
|
||||
buf *bufio.Writer // buf is kept for flush.
|
||||
flusherStarted bool
|
||||
mu sync.Mutex
|
||||
closer io.Closer
|
||||
out Sink // out is built on buf.
|
||||
buf *bufio.Writer // buf is kept for flush.
|
||||
|
||||
writeTicker *time.Ticker
|
||||
done chan struct{}
|
||||
writeStartOnce sync.Once
|
||||
writeTicker *time.Ticker
|
||||
}
|
||||
|
||||
func (fs *bufferedSink) Write(e *pb.GrpcLogEntry) error {
|
||||
// Start the write loop when Write is called.
|
||||
fs.writeStartOnce.Do(fs.startFlushGoroutine)
|
||||
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 {
|
||||
fs.mu.Unlock()
|
||||
return err
|
||||
}
|
||||
fs.mu.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -117,12 +113,7 @@ const (
|
|||
func (fs *bufferedSink) startFlushGoroutine() {
|
||||
fs.writeTicker = time.NewTicker(bufFlushDuration)
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-fs.done:
|
||||
return
|
||||
case <-fs.writeTicker.C:
|
||||
}
|
||||
for range fs.writeTicker.C {
|
||||
fs.mu.Lock()
|
||||
if err := fs.buf.Flush(); err != nil {
|
||||
grpclogLogger.Warningf("failed to flush to Sink: %v", err)
|
||||
|
|
@ -133,12 +124,10 @@ func (fs *bufferedSink) startFlushGoroutine() {
|
|||
}
|
||||
|
||||
func (fs *bufferedSink) Close() error {
|
||||
fs.mu.Lock()
|
||||
defer fs.mu.Unlock()
|
||||
if fs.writeTicker != nil {
|
||||
fs.writeTicker.Stop()
|
||||
}
|
||||
close(fs.done)
|
||||
fs.mu.Lock()
|
||||
if err := fs.buf.Flush(); err != nil {
|
||||
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 {
|
||||
grpclogLogger.Warningf("failed to close the Sink: %v", err)
|
||||
}
|
||||
fs.mu.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -165,6 +155,5 @@ func NewBufferedSink(o io.WriteCloser) Sink {
|
|||
closer: o,
|
||||
out: newWriterSink(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())},
|
||||
}
|
||||
if pid == 0 {
|
||||
db.get().addChannel(id, cn, true, pid)
|
||||
db.get().addChannel(id, cn, true, pid, ref)
|
||||
} else {
|
||||
db.get().addChannel(id, cn, false, pid)
|
||||
db.get().addChannel(id, cn, false, pid, ref)
|
||||
}
|
||||
return id
|
||||
}
|
||||
|
|
@ -228,7 +228,7 @@ func RegisterSubChannel(c Channel, pid int64, ref string) int64 {
|
|||
pid: pid,
|
||||
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
|
||||
}
|
||||
|
||||
|
|
@ -258,7 +258,7 @@ func RegisterListenSocket(s Socket, pid int64, ref string) int64 {
|
|||
}
|
||||
id := idGen.genID()
|
||||
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
|
||||
}
|
||||
|
||||
|
|
@ -273,11 +273,11 @@ func RegisterNormalSocket(s Socket, pid int64, ref string) int64 {
|
|||
}
|
||||
id := idGen.genID()
|
||||
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
|
||||
}
|
||||
|
||||
// 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.
|
||||
func RemoveEntry(id int64) {
|
||||
db.get().removeEntry(id)
|
||||
|
|
@ -333,7 +333,7 @@ func (c *channelMap) addServer(id int64, s *server) {
|
|||
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()
|
||||
cn.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()
|
||||
}
|
||||
|
||||
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()
|
||||
sc.cm = c
|
||||
sc.trace.cm = c
|
||||
|
|
@ -355,7 +355,7 @@ func (c *channelMap) addSubChannel(id int64, sc *subChannel, pid int64) {
|
|||
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()
|
||||
ls.cm = c
|
||||
c.listenSockets[id] = ls
|
||||
|
|
@ -363,7 +363,7 @@ func (c *channelMap) addListenSocket(id int64, ls *listenSocket, pid int64) {
|
|||
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()
|
||||
ns.cm = c
|
||||
c.normalSockets[id] = ns
|
||||
|
|
@ -630,7 +630,7 @@ func (c *channelMap) GetServerSockets(id int64, startID int64, maxResults int64)
|
|||
if count == 0 {
|
||||
end = true
|
||||
}
|
||||
s := make([]*SocketMetric, 0, len(sks))
|
||||
var s []*SocketMetric
|
||||
for _, ns := range sks {
|
||||
sm := &SocketMetric{}
|
||||
sm.SocketData = ns.s.ChannelzMetric()
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
// +build !appengine
|
||||
|
||||
/*
|
||||
*
|
||||
* Copyright 2018 gRPC authors.
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
//go:build !linux
|
||||
// +build !linux
|
||||
// +build !linux appengine
|
||||
|
||||
/*
|
||||
*
|
||||
|
|
@ -38,6 +37,6 @@ type SocketOptionData struct {
|
|||
// Windows OS doesn't support Socket Option
|
||||
func (s *SocketOptionData) Getsockopt(fd uintptr) {
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
//go:build !linux
|
||||
// +build !linux
|
||||
// +build !linux appengine
|
||||
|
||||
/*
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
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.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
// +build appengine
|
||||
|
||||
/*
|
||||
*
|
||||
* Copyright 2021 gRPC authors.
|
||||
* Copyright 2018 gRPC authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (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 grpcutil
|
||||
package credentials
|
||||
|
||||
import (
|
||||
"net"
|
||||
)
|
||||
|
||||
// WrapSyscallConn returns newConn on appengine.
|
||||
func WrapSyscallConn(rawConn, newConn net.Conn) net.Conn {
|
||||
return newConn
|
||||
}
|
||||
|
|
@ -18,9 +18,7 @@
|
|||
|
||||
package credentials
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
)
|
||||
import "crypto/tls"
|
||||
|
||||
const alpnProtoStrH2 = "h2"
|
||||
|
||||
|
|
|
|||
|
|
@ -26,10 +26,13 @@ import (
|
|||
|
||||
const (
|
||||
prefix = "GRPC_GO_"
|
||||
retryStr = prefix + "RETRY"
|
||||
txtErrIgnoreStr = prefix + "IGNORE_TXT_ERRORS"
|
||||
)
|
||||
|
||||
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 = !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
|
||||
// later release.
|
||||
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{})
|
||||
// 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{})
|
||||
// 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{})
|
||||
// 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{})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,37 +31,26 @@ var (
|
|||
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.
|
||||
func Int63n(n int64) int64 {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
return r.Int63n(n)
|
||||
res := r.Int63n(n)
|
||||
mu.Unlock()
|
||||
return res
|
||||
}
|
||||
|
||||
// Intn implements rand.Intn on the grpcrand global source.
|
||||
func Intn(n int) int {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
return r.Intn(n)
|
||||
res := r.Intn(n)
|
||||
mu.Unlock()
|
||||
return res
|
||||
}
|
||||
|
||||
// Float64 implements rand.Float64 on the grpcrand global source.
|
||||
func Float64() float64 {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
return r.Float64()
|
||||
}
|
||||
|
||||
// Uint64 implements rand.Uint64 on the grpcrand global source.
|
||||
func Uint64() uint64 {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
return r.Uint64()
|
||||
res := r.Float64()
|
||||
mu.Unlock()
|
||||
return res
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
// default, but tests may wish to set it lower for convenience.
|
||||
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
|
||||
// ClientConn for resolver testing only
|
||||
ParseServiceConfigForTesting interface{} // func(string) *serviceconfig.ParseResult
|
||||
|
|
@ -59,11 +65,6 @@ var (
|
|||
// 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.
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -30,38 +30,14 @@ type mdKeyType string
|
|||
|
||||
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.
|
||||
func Get(addr resolver.Address) metadata.MD {
|
||||
attrs := addr.Attributes
|
||||
if attrs == nil {
|
||||
return nil
|
||||
}
|
||||
md, _ := attrs.Value(mdKey).(mdValue)
|
||||
return metadata.MD(md)
|
||||
md, _ := attrs.Value(mdKey).(metadata.MD)
|
||||
return md
|
||||
}
|
||||
|
||||
// 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
|
||||
// have this metadata.
|
||||
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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@ import (
|
|||
"sync"
|
||||
|
||||
"google.golang.org/grpc/internal/serviceconfig"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/resolver"
|
||||
)
|
||||
|
||||
|
|
@ -52,77 +51,6 @@ type RPCConfig struct {
|
|||
Context context.Context
|
||||
MethodConfig serviceconfig.MethodConfig // configuration to use for this RPC
|
||||
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
|
||||
|
|
@ -132,7 +60,7 @@ const csKey = csKeyType("grpc.internal.resolver.configSelector")
|
|||
// SetConfigSelector sets the config selector in state and returns the new
|
||||
// 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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -34,7 +34,6 @@ import (
|
|||
|
||||
grpclbstate "google.golang.org/grpc/balancer/grpclb/state"
|
||||
"google.golang.org/grpc/grpclog"
|
||||
"google.golang.org/grpc/internal/backoff"
|
||||
"google.golang.org/grpc/internal/envconfig"
|
||||
"google.golang.org/grpc/internal/grpcrand"
|
||||
"google.golang.org/grpc/resolver"
|
||||
|
|
@ -47,13 +46,6 @@ var EnableSRVLookups = false
|
|||
|
||||
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() {
|
||||
resolver.Register(NewBuilder())
|
||||
}
|
||||
|
|
@ -151,6 +143,7 @@ func (b *dnsBuilder) Build(target resolver.Target, cc resolver.ClientConn, opts
|
|||
|
||||
d.wg.Add(1)
|
||||
go d.watcher()
|
||||
d.ResolveNow(resolver.ResolveNowOptions{})
|
||||
return d, nil
|
||||
}
|
||||
|
||||
|
|
@ -208,38 +201,28 @@ func (d *dnsResolver) Close() {
|
|||
|
||||
func (d *dnsResolver) watcher() {
|
||||
defer d.wg.Done()
|
||||
backoffIndex := 1
|
||||
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 {
|
||||
case <-d.ctx.Done():
|
||||
timer.Stop()
|
||||
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
|
||||
}
|
||||
|
||||
func handleDNSError(err error, lookupType string) error {
|
||||
var filterError = func(err error) error {
|
||||
if dnsErr, ok := err.(*net.DNSError); ok && !dnsErr.IsTimeout && !dnsErr.IsTemporary {
|
||||
// Timeouts and temporary errors should be communicated to gRPC to
|
||||
// attempt another DNS query (with backoff). Other errors should be
|
||||
// suppressed (they may represent the absence of a TXT record).
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func handleDNSError(err error, lookupType string) error {
|
||||
err = filterError(err)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("dns: %v record lookup error: %v", lookupType, err)
|
||||
logger.Info(err)
|
||||
|
|
@ -318,12 +306,12 @@ func (d *dnsResolver) lookupTXT() *serviceconfig.ParseResult {
|
|||
}
|
||||
|
||||
func (d *dnsResolver) lookupHost() ([]resolver.Address, error) {
|
||||
var newAddrs []resolver.Address
|
||||
addrs, err := d.resolver.LookupHost(d.ctx, d.host)
|
||||
if err != nil {
|
||||
err = handleDNSError(err, "A")
|
||||
return nil, err
|
||||
}
|
||||
newAddrs := make([]resolver.Address, 0, len(addrs))
|
||||
for _, a := range addrs {
|
||||
ip, ok := formatIP(a)
|
||||
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");
|
||||
* 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 FullMatchWithRegex(re *regexp.Regexp, text string) bool {
|
||||
if len(text) == 0 {
|
||||
return re.MatchString(text)
|
||||
func init() {
|
||||
filterError = func(err error) error {
|
||||
if dnsErr, ok := err.(*net.DNSError); ok && dnsErr.IsNotFound {
|
||||
// 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 != "" {
|
||||
return nil, fmt.Errorf("invalid (non-empty) authority: %v", target.Authority)
|
||||
}
|
||||
|
||||
// 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}
|
||||
addr := resolver.Address{Addr: target.Endpoint}
|
||||
if b.scheme == unixAbstractScheme {
|
||||
// prepend "\x00" to address for unix-abstract
|
||||
addr.Addr = "\x00" + addr.Addr
|
||||
|
|
|
|||
|
|
@ -46,22 +46,6 @@ type BalancerConfig struct {
|
|||
|
||||
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.
|
||||
//
|
||||
// ServiceConfig contains a list of loadBalancingConfigs, each with a name and
|
||||
|
|
@ -78,7 +62,6 @@ func (bc *BalancerConfig) UnmarshalJSON(b []byte) error {
|
|||
return err
|
||||
}
|
||||
|
||||
var names []string
|
||||
for i, lbcfg := range ir {
|
||||
if len(lbcfg) != 1 {
|
||||
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 {
|
||||
}
|
||||
|
||||
names = append(names, name)
|
||||
builder := balancer.Get(name)
|
||||
if builder == nil {
|
||||
// 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
|
||||
// encounter a registered policy. The config is considered invalid in this
|
||||
// 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
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ func (s *Status) Err() error {
|
|||
if s.Code() == codes.OK {
|
||||
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.
|
||||
|
|
@ -136,23 +136,19 @@ func (s *Status) Details() []interface{} {
|
|||
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,
|
||||
// and a nil *Error should never be returned by this package.
|
||||
type Error struct {
|
||||
s *Status
|
||||
e *spb.Status
|
||||
}
|
||||
|
||||
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.
|
||||
func (e *Error) GRPCStatus() *Status {
|
||||
return e.s
|
||||
return FromProto(e.e)
|
||||
}
|
||||
|
||||
// Is implements future error.Is functionality.
|
||||
|
|
@ -162,5 +158,5 @@ func (e *Error) Is(target error) bool {
|
|||
if !ok {
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
//go:build !linux
|
||||
// +build !linux
|
||||
// +build !linux appengine
|
||||
|
||||
/*
|
||||
*
|
||||
|
|
@ -36,41 +35,41 @@ var logger = grpclog.Component("core")
|
|||
|
||||
func log() {
|
||||
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
|
||||
// process. It always returns 0 under non-linux environments.
|
||||
// GetCPUTime returns the how much CPU time has passed since the start of this process.
|
||||
// It always returns 0 under non-linux or appengine environment.
|
||||
func GetCPUTime() int64 {
|
||||
log()
|
||||
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{}
|
||||
|
||||
// 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 {
|
||||
log()
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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) {
|
||||
log()
|
||||
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 {
|
||||
log()
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetTCPUserTimeout is a no-op function under non-linux environments.
|
||||
// A negative return value indicates the operation is not supported
|
||||
// GetTCPUserTimeout is a no-op function under non-linux or appengine environments
|
||||
// a negative return value indicates the operation is not supported
|
||||
func GetTCPUserTimeout(conn net.Conn) (int, error) {
|
||||
log()
|
||||
return -1, nil
|
||||
|
|
|
|||
|
|
@ -20,17 +20,13 @@ package transport
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"golang.org/x/net/http2"
|
||||
"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) {
|
||||
|
|
@ -132,15 +128,6 @@ type cleanupStream struct {
|
|||
|
||||
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 {
|
||||
streamID uint32
|
||||
endStream bool
|
||||
|
|
@ -297,7 +284,7 @@ type controlBuffer struct {
|
|||
// closed and nilled when transportResponseFrames drops below the
|
||||
// threshold. Both fields are protected by mu.
|
||||
transportResponseFrames int
|
||||
trfChan atomic.Value // chan struct{}
|
||||
trfChan atomic.Value // *chan struct{}
|
||||
}
|
||||
|
||||
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
|
||||
// controlbuf.
|
||||
func (c *controlBuffer) throttle() {
|
||||
ch, _ := c.trfChan.Load().(chan struct{})
|
||||
ch, _ := c.trfChan.Load().(*chan struct{})
|
||||
if ch != nil {
|
||||
select {
|
||||
case <-ch:
|
||||
case <-*ch:
|
||||
case <-c.done:
|
||||
}
|
||||
}
|
||||
|
|
@ -348,7 +335,8 @@ func (c *controlBuffer) executeAndPut(f func(it interface{}) bool, it cbItem) (b
|
|||
if c.transportResponseFrames == maxQueuedTransportResponseFrames {
|
||||
// We are adding the frame that puts us over the threshold; create
|
||||
// a throttling channel.
|
||||
c.trfChan.Store(make(chan struct{}))
|
||||
ch := make(chan struct{})
|
||||
c.trfChan.Store(&ch)
|
||||
}
|
||||
}
|
||||
c.mu.Unlock()
|
||||
|
|
@ -389,9 +377,9 @@ func (c *controlBuffer) get(block bool) (interface{}, error) {
|
|||
if c.transportResponseFrames == maxQueuedTransportResponseFrames {
|
||||
// We are removing the frame that put us over the
|
||||
// threshold; close and clear the throttling channel.
|
||||
ch := c.trfChan.Load().(chan struct{})
|
||||
close(ch)
|
||||
c.trfChan.Store((chan struct{})(nil))
|
||||
ch := c.trfChan.Load().(*chan struct{})
|
||||
close(*ch)
|
||||
c.trfChan.Store((*chan struct{})(nil))
|
||||
}
|
||||
c.transportResponseFrames--
|
||||
}
|
||||
|
|
@ -407,6 +395,7 @@ func (c *controlBuffer) get(block bool) (interface{}, error) {
|
|||
select {
|
||||
case <-c.ch:
|
||||
case <-c.done:
|
||||
c.finish()
|
||||
return nil, ErrConnClosing
|
||||
}
|
||||
}
|
||||
|
|
@ -431,14 +420,6 @@ func (c *controlBuffer) finish() {
|
|||
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()
|
||||
}
|
||||
|
||||
|
|
@ -768,27 +749,6 @@ func (l *loopyWriter) cleanupStreamHandler(c *cleanupStream) error {
|
|||
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 {
|
||||
if l.side == clientSide {
|
||||
l.draining = true
|
||||
|
|
@ -827,8 +787,6 @@ func (l *loopyWriter) handle(i interface{}) error {
|
|||
return l.registerStreamHandler(i)
|
||||
case *cleanupStream:
|
||||
return l.cleanupStreamHandler(i)
|
||||
case *earlyAbortStream:
|
||||
return l.earlyAbortStreamHandler(i)
|
||||
case *incomingGoAway:
|
||||
return l.incomingGoAwayHandler(i)
|
||||
case *dataFrame:
|
||||
|
|
|
|||
|
|
@ -136,10 +136,12 @@ type inFlow struct {
|
|||
|
||||
// newLimit updates the inflow window to a new value n.
|
||||
// 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()
|
||||
d := n - f.limit
|
||||
f.limit = n
|
||||
f.mu.Unlock()
|
||||
return d
|
||||
}
|
||||
|
||||
func (f *inFlow) maybeAdjust(n uint32) uint32 {
|
||||
|
|
|
|||
|
|
@ -141,8 +141,9 @@ type serverHandlerTransport struct {
|
|||
stats stats.Handler
|
||||
}
|
||||
|
||||
func (ht *serverHandlerTransport) Close() {
|
||||
func (ht *serverHandlerTransport) Close() error {
|
||||
ht.closeOnce.Do(ht.closeCloseChanOnce)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ht *serverHandlerTransport) closeCloseChanOnce() { close(ht.closedCh) }
|
||||
|
|
|
|||
|
|
@ -24,8 +24,6 @@ import (
|
|||
"io"
|
||||
"math"
|
||||
"net"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
|
@ -34,14 +32,15 @@ import (
|
|||
|
||||
"golang.org/x/net/http2"
|
||||
"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"
|
||||
imetadata "google.golang.org/grpc/internal/metadata"
|
||||
"google.golang.org/grpc/internal/syscall"
|
||||
"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/metadata"
|
||||
"google.golang.org/grpc/peer"
|
||||
|
|
@ -117,9 +116,6 @@ type http2Client struct {
|
|||
// goAwayReason records the http2.ErrCode and debug data received with the
|
||||
// GoAway frame.
|
||||
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
|
||||
// go dormant. The condition for dormancy is based on the number of active
|
||||
// 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
|
||||
networkType, ok := networktype.Get(addr)
|
||||
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") {
|
||||
// Supported unix targets are either "unix://absolute-path" or
|
||||
// "unix:relative-path".
|
||||
if filepath.IsAbs(address) {
|
||||
return fn(ctx, "unix://"+address)
|
||||
}
|
||||
return fn(ctx, "unix:"+address)
|
||||
// For backward compatibility, if the user dialed "unix:///path",
|
||||
// the passthrough resolver would be used and the user's custom
|
||||
// dialer would see "unix:///path". Since the unix resolver is used
|
||||
// and the address is now "/path", prepend "unix://" so the user's
|
||||
// custom dialer sees the same address.
|
||||
return fn(ctx, "unix://"+address)
|
||||
}
|
||||
return fn(ctx, 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)
|
||||
if err != nil {
|
||||
if opts.FailOnNonTempDialError {
|
||||
|
|
@ -251,15 +234,13 @@ func newHTTP2Client(connectCtx, ctx context.Context, addr resolver.Address, opts
|
|||
}
|
||||
}
|
||||
if transportCreds != nil {
|
||||
rawConn := conn
|
||||
// Pull the deadline from the connectCtx, which will be used for
|
||||
// timeouts in the authentication protocol handshake. Can ignore the
|
||||
// boolean as the deadline will return the zero value, which will make
|
||||
// the conn not timeout on I/O operations.
|
||||
deadline, _ := connectCtx.Deadline()
|
||||
rawConn.SetDeadline(deadline)
|
||||
conn, authInfo, err = transportCreds.ClientHandshake(connectCtx, addr.ServerName, rawConn)
|
||||
rawConn.SetDeadline(time.Time{})
|
||||
// gRPC, resolver, balancer etc. can specify arbitrary data in the
|
||||
// Attributes field of resolver.Address, which is shoved into connectCtx
|
||||
// and passed to the credential handshaker. This makes it possible for
|
||||
// address specific arbitrary data to reach the credential handshaker.
|
||||
contextWithHandshakeInfo := internal.NewClientHandshakeInfoContext.(func(context.Context, credentials.ClientHandshakeInfo) context.Context)
|
||||
connectCtx = contextWithHandshakeInfo(connectCtx, credentials.ClientHandshakeInfo{Attributes: addr.Attributes})
|
||||
conn, authInfo, err = transportCreds.ClientHandshake(connectCtx, addr.ServerName, conn)
|
||||
if err != nil {
|
||||
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.
|
||||
n, err := t.conn.Write(clientPreface)
|
||||
if err != nil {
|
||||
err = connectionErrorf(true, err, "transport: failed to write client preface: %v", err)
|
||||
t.Close(err)
|
||||
return nil, err
|
||||
t.Close()
|
||||
return nil, connectionErrorf(true, err, "transport: failed to write client preface: %v", err)
|
||||
}
|
||||
if n != len(clientPreface) {
|
||||
err = connectionErrorf(true, nil, "transport: preface mismatch, wrote %d bytes; want %d", n, len(clientPreface))
|
||||
t.Close(err)
|
||||
return nil, err
|
||||
t.Close()
|
||||
return nil, connectionErrorf(true, err, "transport: preface mismatch, wrote %d bytes; want %d", n, len(clientPreface))
|
||||
}
|
||||
var ss []http2.Setting
|
||||
|
||||
|
|
@ -391,16 +370,14 @@ func newHTTP2Client(connectCtx, ctx context.Context, addr resolver.Address, opts
|
|||
}
|
||||
err = t.framer.fr.WriteSettings(ss...)
|
||||
if err != nil {
|
||||
err = connectionErrorf(true, err, "transport: failed to write initial settings frame: %v", err)
|
||||
t.Close(err)
|
||||
return nil, err
|
||||
t.Close()
|
||||
return nil, connectionErrorf(true, err, "transport: failed to write initial settings frame: %v", err)
|
||||
}
|
||||
// Adjust the connection flow control window if needed.
|
||||
if delta := uint32(icwz - defaultWindowSize); delta > 0 {
|
||||
if err := t.framer.fr.WriteWindowUpdate(0, delta); err != nil {
|
||||
err = connectionErrorf(true, err, "transport: failed to write window update: %v", err)
|
||||
t.Close(err)
|
||||
return nil, err
|
||||
t.Close()
|
||||
return nil, connectionErrorf(true, err, "transport: failed to write window update: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -417,10 +394,11 @@ func newHTTP2Client(connectCtx, ctx context.Context, addr resolver.Address, opts
|
|||
logger.Errorf("transport: loopyWriter.run returning. Err: %v", err)
|
||||
}
|
||||
}
|
||||
// Do not close the transport. Let reader goroutine handle it since
|
||||
// there might be data in the buffers.
|
||||
t.conn.Close()
|
||||
t.controlBuf.finish()
|
||||
// If it's a connection error, let reader goroutine handle it
|
||||
// since there might be data in the buffers.
|
||||
if _, ok := err.(net.Error); !ok {
|
||||
t.conn.Close()
|
||||
}
|
||||
close(t.writerDone)
|
||||
}()
|
||||
return t, nil
|
||||
|
|
@ -436,7 +414,6 @@ func (t *http2Client) newStream(ctx context.Context, callHdr *CallHdr) *Stream {
|
|||
buf: newRecvBuffer(),
|
||||
headerChan: make(chan struct{}),
|
||||
contentSubtype: callHdr.ContentSubtype,
|
||||
doneFunc: callHdr.DoneFunc,
|
||||
}
|
||||
s.wq = newWriteQuota(defaultWriteQuota, s.done)
|
||||
s.requestRead = func(n int) {
|
||||
|
|
@ -476,7 +453,7 @@ func (t *http2Client) createHeaderFields(ctx context.Context, callHdr *CallHdr)
|
|||
Method: callHdr.Method,
|
||||
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)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -588,7 +565,7 @@ func (t *http2Client) getTrAuthData(ctx context.Context, audience string) (map[s
|
|||
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 {
|
||||
// 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
|
||||
}
|
||||
|
||||
// NewStreamError wraps an error and reports additional information. Typically
|
||||
// NewStream errors result in transparent retry, as they mean nothing went onto
|
||||
// the wire. However, there are two notable exceptions:
|
||||
//
|
||||
// 1. If the stream headers violate the max header list size allowed by the
|
||||
// server. In this case there is no reason to retry at all, as it is
|
||||
// assumed the RPC would continue to fail on subsequent attempts.
|
||||
// 2. If the credentials errored when requesting their headers. In this case,
|
||||
// it's possible a retry can fix the problem, but indefinitely transparently
|
||||
// retrying is not appropriate as it is likely the credentials, if they can
|
||||
// eventually succeed, would need I/O to do so.
|
||||
type NewStreamError struct {
|
||||
// PerformedIOError wraps an error to indicate IO may have been performed
|
||||
// before the error occurred.
|
||||
type PerformedIOError struct {
|
||||
Err error
|
||||
|
||||
DoNotRetry bool
|
||||
DoNotTransparentRetry bool
|
||||
}
|
||||
|
||||
func (e NewStreamError) Error() string {
|
||||
return e.Err.Error()
|
||||
// Error implements error.
|
||||
func (p PerformedIOError) Error() string {
|
||||
return p.Err.Error()
|
||||
}
|
||||
|
||||
// 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) {
|
||||
ctx = peer.NewContext(ctx, t.getPeer())
|
||||
headerFields, err := t.createHeaderFields(ctx, callHdr)
|
||||
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)
|
||||
cleanup := func(err error) {
|
||||
|
|
@ -753,23 +721,23 @@ func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr) (_ *Strea
|
|||
return true
|
||||
}, hdr)
|
||||
if err != nil {
|
||||
return nil, &NewStreamError{Err: err}
|
||||
return nil, err
|
||||
}
|
||||
if success {
|
||||
break
|
||||
}
|
||||
if hdrListSizeErr != nil {
|
||||
return nil, &NewStreamError{Err: hdrListSizeErr, DoNotRetry: true}
|
||||
return nil, hdrListSizeErr
|
||||
}
|
||||
firstTry = false
|
||||
select {
|
||||
case <-ch:
|
||||
case <-ctx.Done():
|
||||
return nil, &NewStreamError{Err: ContextErr(ctx.Err())}
|
||||
case <-s.ctx.Done():
|
||||
return nil, ContextErr(s.ctx.Err())
|
||||
case <-t.goAway:
|
||||
return nil, &NewStreamError{Err: errStreamDrain}
|
||||
return nil, errStreamDrain
|
||||
case <-t.ctx.Done():
|
||||
return nil, &NewStreamError{Err: ErrConnClosing}
|
||||
return nil, ErrConnClosing
|
||||
}
|
||||
}
|
||||
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)
|
||||
// This will unblock write.
|
||||
close(s.done)
|
||||
if s.doneFunc != nil {
|
||||
s.doneFunc()
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
// re-connected. This happens because t.onClose() begins reconnect logic at the
|
||||
// addrConn level and blocks until the addrConn is successfully connected.
|
||||
func (t *http2Client) Close(err error) {
|
||||
func (t *http2Client) Close() error {
|
||||
t.mu.Lock()
|
||||
// Make sure we only Close once.
|
||||
if t.state == closing {
|
||||
t.mu.Unlock()
|
||||
return
|
||||
return nil
|
||||
}
|
||||
// Call t.onClose before setting the state to closing to prevent the client
|
||||
// from attempting to create new streams ASAP.
|
||||
|
|
@ -897,25 +862,13 @@ func (t *http2Client) Close(err error) {
|
|||
t.mu.Unlock()
|
||||
t.controlBuf.finish()
|
||||
t.cancel()
|
||||
t.conn.Close()
|
||||
err := t.conn.Close()
|
||||
if channelz.IsOn() {
|
||||
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.
|
||||
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 {
|
||||
connEnd := &stats.ConnEnd{
|
||||
|
|
@ -923,6 +876,7 @@ func (t *http2Client) Close(err error) {
|
|||
}
|
||||
t.statsHandler.HandleConn(t.ctx, connEnd)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// GracefulClose sets the state to draining, which prevents new streams from
|
||||
|
|
@ -941,7 +895,7 @@ func (t *http2Client) GracefulClose() {
|
|||
active := len(t.activeStreams)
|
||||
t.mu.Unlock()
|
||||
if active == 0 {
|
||||
t.Close(ErrConnClosing)
|
||||
t.Close()
|
||||
return
|
||||
}
|
||||
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 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)
|
||||
}
|
||||
}
|
||||
|
|
@ -1187,9 +1141,9 @@ func (t *http2Client) handleGoAway(f *http2.GoAwayFrame) {
|
|||
}
|
||||
}
|
||||
id := f.LastStreamID
|
||||
if id > 0 && id%2 == 0 {
|
||||
if id > 0 && id%2 != 1 {
|
||||
t.mu.Unlock()
|
||||
t.Close(connectionErrorf(true, nil, "received goaway with non-zero even-numbered numbered stream id: %v", id))
|
||||
t.Close()
|
||||
return
|
||||
}
|
||||
// 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 id > t.prevGoAwayID {
|
||||
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
|
||||
}
|
||||
default:
|
||||
|
|
@ -1237,7 +1191,7 @@ func (t *http2Client) handleGoAway(f *http2.GoAwayFrame) {
|
|||
active := len(t.activeStreams)
|
||||
t.mu.Unlock()
|
||||
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
|
||||
}
|
||||
}
|
||||
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()
|
||||
defer t.mu.Unlock()
|
||||
return t.goAwayReason, t.goAwayDebugMessage
|
||||
return t.goAwayReason
|
||||
}
|
||||
|
||||
func (t *http2Client) handleWindowUpdate(f *http2.WindowUpdateFrame) {
|
||||
|
|
@ -1290,128 +1239,35 @@ func (t *http2Client) operateHeaders(frame *http2.MetaHeadersFrame) {
|
|||
return
|
||||
}
|
||||
|
||||
// frame.Truncated is set to true when framer detects that the current header
|
||||
// list size hits MaxHeaderListSize limit.
|
||||
if frame.Truncated {
|
||||
se := status.New(codes.Internal, "peer header list size exceeded limit")
|
||||
t.closeStream(s, se.Err(), true, http2.ErrCodeFrameSize, se, nil, endStream)
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
// If a gRPC Response-Headers has already been received, then it means
|
||||
// that the peer is speaking gRPC and we are in gRPC mode.
|
||||
isGRPC = !initialHeader
|
||||
mdata = make(map[string][]string)
|
||||
contentTypeErr = "malformed header: missing HTTP content-type"
|
||||
grpcMessage string
|
||||
statusGen *status.Status
|
||||
recvCompress string
|
||||
httpStatusCode *int
|
||||
httpStatusErr string
|
||||
rawStatusCode = codes.Unknown
|
||||
// headerError is set if an error is encountered while parsing the headers
|
||||
headerError string
|
||||
)
|
||||
|
||||
if initialHeader {
|
||||
httpStatusErr = "malformed header: missing HTTP status"
|
||||
}
|
||||
|
||||
for _, hf := range frame.Fields {
|
||||
switch hf.Name {
|
||||
case "content-type":
|
||||
if _, validContentType := grpcutil.ContentSubtype(hf.Value); !validContentType {
|
||||
contentTypeErr = fmt.Sprintf("transport: received unexpected content-type %q", hf.Value)
|
||||
break
|
||||
}
|
||||
contentTypeErr = ""
|
||||
mdata[hf.Name] = append(mdata[hf.Name], hf.Value)
|
||||
isGRPC = true
|
||||
case "grpc-encoding":
|
||||
recvCompress = hf.Value
|
||||
case "grpc-status":
|
||||
code, err := strconv.ParseInt(hf.Value, 10, 32)
|
||||
if err != nil {
|
||||
se := status.New(codes.Internal, fmt.Sprintf("transport: malformed grpc-status: %v", err))
|
||||
t.closeStream(s, se.Err(), true, http2.ErrCodeProtocol, se, nil, endStream)
|
||||
return
|
||||
}
|
||||
rawStatusCode = codes.Code(uint32(code))
|
||||
case "grpc-message":
|
||||
grpcMessage = decodeGrpcMessage(hf.Value)
|
||||
case "grpc-status-details-bin":
|
||||
var err error
|
||||
statusGen, err = decodeGRPCStatusDetails(hf.Value)
|
||||
if err != nil {
|
||||
headerError = fmt.Sprintf("transport: malformed grpc-status-details-bin: %v", err)
|
||||
}
|
||||
case ":status":
|
||||
if hf.Value == "200" {
|
||||
httpStatusErr = ""
|
||||
statusCode := 200
|
||||
httpStatusCode = &statusCode
|
||||
break
|
||||
}
|
||||
|
||||
c, err := strconv.ParseInt(hf.Value, 10, 32)
|
||||
if err != nil {
|
||||
se := status.New(codes.Internal, fmt.Sprintf("transport: malformed http-status: %v", err))
|
||||
t.closeStream(s, se.Err(), true, http2.ErrCodeProtocol, se, nil, endStream)
|
||||
return
|
||||
}
|
||||
statusCode := int(c)
|
||||
httpStatusCode = &statusCode
|
||||
|
||||
httpStatusErr = fmt.Sprintf(
|
||||
"unexpected HTTP status code received from server: %d (%s)",
|
||||
statusCode,
|
||||
http.StatusText(statusCode),
|
||||
)
|
||||
default:
|
||||
if isReservedHeader(hf.Name) && !isWhitelistedHeader(hf.Name) {
|
||||
break
|
||||
}
|
||||
v, err := decodeMetadataHeader(hf.Name, hf.Value)
|
||||
if err != nil {
|
||||
headerError = fmt.Sprintf("transport: malformed %s: %v", hf.Name, err)
|
||||
logger.Warningf("Failed to decode metadata header (%q, %q): %v", hf.Name, hf.Value, err)
|
||||
break
|
||||
}
|
||||
mdata[hf.Name] = append(mdata[hf.Name], v)
|
||||
}
|
||||
}
|
||||
|
||||
if !isGRPC || httpStatusErr != "" {
|
||||
var code = codes.Internal // when header does not include HTTP status, return INTERNAL
|
||||
|
||||
if httpStatusCode != nil {
|
||||
var ok bool
|
||||
code, ok = HTTPStatusConvTab[*httpStatusCode]
|
||||
if !ok {
|
||||
code = codes.Unknown
|
||||
}
|
||||
}
|
||||
var errs []string
|
||||
if httpStatusErr != "" {
|
||||
errs = append(errs, httpStatusErr)
|
||||
}
|
||||
if contentTypeErr != "" {
|
||||
errs = append(errs, contentTypeErr)
|
||||
}
|
||||
// Verify the HTTP response is a 200.
|
||||
se := status.New(code, strings.Join(errs, "; "))
|
||||
t.closeStream(s, se.Err(), true, http2.ErrCodeProtocol, se, nil, endStream)
|
||||
return
|
||||
}
|
||||
|
||||
if headerError != "" {
|
||||
se := status.New(codes.Internal, headerError)
|
||||
t.closeStream(s, se.Err(), true, http2.ErrCodeProtocol, se, nil, endStream)
|
||||
state := &decodeState{}
|
||||
// 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.
|
||||
state.data.isGRPC = !initialHeader
|
||||
if h2code, err := state.decodeHeader(frame); err != nil {
|
||||
t.closeStream(s, err, true, h2code, status.Convert(err), nil, endStream)
|
||||
return
|
||||
}
|
||||
|
||||
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 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
|
||||
// stream goroutine will read it only after seeing a closed
|
||||
// headerChan which we'll close after setting this.
|
||||
s.recvCompress = recvCompress
|
||||
if len(mdata) > 0 {
|
||||
s.header = mdata
|
||||
s.recvCompress = state.data.encoding
|
||||
if len(state.data.mdata) > 0 {
|
||||
s.header = state.data.mdata
|
||||
}
|
||||
} else {
|
||||
// HEADERS frame block carries a Trailers-Only.
|
||||
|
|
@ -1433,36 +1289,13 @@ func (t *http2Client) operateHeaders(frame *http2.MetaHeadersFrame) {
|
|||
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 {
|
||||
return
|
||||
}
|
||||
|
||||
if statusGen == nil {
|
||||
statusGen = status.New(rawStatusCode, grpcMessage)
|
||||
}
|
||||
|
||||
// if client received END_STREAM from server while stream was still active, send RST_STREAM
|
||||
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
|
||||
|
|
@ -1476,8 +1309,7 @@ func (t *http2Client) reader() {
|
|||
// Check the validity of server preface.
|
||||
frame, err := t.framer.fr.ReadFrame()
|
||||
if err != nil {
|
||||
err = connectionErrorf(true, err, "error reading server preface: %v", err)
|
||||
t.Close(err) // this kicks off resetTransport, so must be last before return
|
||||
t.Close() // this kicks off resetTransport, so must be last before return
|
||||
return
|
||||
}
|
||||
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)
|
||||
if !ok {
|
||||
// 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))
|
||||
t.Close() // this kicks off resetTransport, so must be last before return
|
||||
return
|
||||
}
|
||||
t.onPrefaceReceipt()
|
||||
|
|
@ -1523,7 +1354,7 @@ func (t *http2Client) reader() {
|
|||
continue
|
||||
} else {
|
||||
// Transport error.
|
||||
t.Close(connectionErrorf(true, err, "error reading from server: %v", err))
|
||||
t.Close()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
|
@ -1557,7 +1388,7 @@ func minTime(a, b time.Duration) time.Duration {
|
|||
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() {
|
||||
p := &ping{data: [8]byte{}}
|
||||
// True iff a ping has been sent, and no data has been received since then.
|
||||
|
|
@ -1582,7 +1413,7 @@ func (t *http2Client) keepalive() {
|
|||
continue
|
||||
}
|
||||
if outstandingPing && timeoutLeft <= 0 {
|
||||
t.Close(connectionErrorf(true, nil, "keepalive ping failed to receive ACK within timeout"))
|
||||
t.Close()
|
||||
return
|
||||
}
|
||||
t.mu.Lock()
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@ import (
|
|||
"io"
|
||||
"math"
|
||||
"net"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
|
@ -73,6 +72,7 @@ type http2Server struct {
|
|||
writerDone chan struct{} // sync point to enable testing.
|
||||
remoteAddr net.Addr
|
||||
localAddr net.Addr
|
||||
maxStreamID uint32 // max stream ID ever seen
|
||||
authInfo credentials.AuthInfo // auth info about the connection
|
||||
inTapHandle tap.ServerInHandle
|
||||
framer *framer
|
||||
|
|
@ -101,11 +101,11 @@ type http2Server struct {
|
|||
|
||||
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.
|
||||
// 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.
|
||||
// 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.
|
||||
drainChan chan struct{}
|
||||
state transportState
|
||||
|
|
@ -122,37 +122,11 @@ type http2Server struct {
|
|||
bufferPool *bufferPool
|
||||
|
||||
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
|
||||
// options from config.
|
||||
//
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
// newHTTP2Server constructs a ServerTransport based on HTTP2. ConnectionError is
|
||||
// returned if something goes wrong.
|
||||
func newHTTP2Server(conn net.Conn, config *ServerConfig) (_ ServerTransport, err error) {
|
||||
writeBufSize := config.WriteBufferSize
|
||||
readBufSize := config.ReadBufferSize
|
||||
maxHeaderListSize := defaultServerMaxHeaderListSize
|
||||
|
|
@ -235,15 +209,14 @@ func NewServerTransport(conn net.Conn, config *ServerConfig) (_ ServerTransport,
|
|||
if kep.MinTime == 0 {
|
||||
kep.MinTime = defaultKeepalivePolicyMinTime
|
||||
}
|
||||
|
||||
done := make(chan struct{})
|
||||
t := &http2Server{
|
||||
ctx: setConnection(context.Background(), rawConn),
|
||||
ctx: context.Background(),
|
||||
done: done,
|
||||
conn: conn,
|
||||
remoteAddr: conn.RemoteAddr(),
|
||||
localAddr: conn.LocalAddr(),
|
||||
authInfo: authInfo,
|
||||
authInfo: config.AuthInfo,
|
||||
framer: framer,
|
||||
readerDone: 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.
|
||||
preface := make([]byte, len(clientPreface))
|
||||
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)
|
||||
}
|
||||
if !bytes.Equal(preface, clientPreface) {
|
||||
|
|
@ -329,7 +294,6 @@ func NewServerTransport(conn net.Conn, config *ServerConfig) (_ ServerTransport,
|
|||
}
|
||||
}
|
||||
t.conn.Close()
|
||||
t.controlBuf.finish()
|
||||
close(t.writerDone)
|
||||
}()
|
||||
go t.keepalive()
|
||||
|
|
@ -338,144 +302,38 @@ func NewServerTransport(conn net.Conn, config *ServerConfig) (_ ServerTransport,
|
|||
|
||||
// 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) {
|
||||
// Acquire max stream ID lock for entire duration
|
||||
t.maxStreamMu.Lock()
|
||||
defer t.maxStreamMu.Unlock()
|
||||
|
||||
streamID := frame.Header().StreamID
|
||||
|
||||
// frame.Truncated is set to true when framer detects that the current header
|
||||
// list size hits MaxHeaderListSize limit.
|
||||
if frame.Truncated {
|
||||
t.controlBuf.put(&cleanupStream{
|
||||
streamID: streamID,
|
||||
rst: true,
|
||||
rstCode: http2.ErrCodeFrameSize,
|
||||
onWrite: func() {},
|
||||
})
|
||||
state := &decodeState{
|
||||
serverSide: true,
|
||||
}
|
||||
if h2code, err := state.decodeHeader(frame); err != nil {
|
||||
if _, ok := status.FromError(err); ok {
|
||||
t.controlBuf.put(&cleanupStream{
|
||||
streamID: streamID,
|
||||
rst: true,
|
||||
rstCode: h2code,
|
||||
onWrite: func() {},
|
||||
})
|
||||
}
|
||||
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()
|
||||
s := &Stream{
|
||||
id: streamID,
|
||||
st: t,
|
||||
buf: buf,
|
||||
fc: &inFlow{limit: uint32(t.initialWindowSize)},
|
||||
id: streamID,
|
||||
st: t,
|
||||
buf: buf,
|
||||
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() {
|
||||
// s is just created by the caller. No lock needed.
|
||||
s.state = streamReadDone
|
||||
}
|
||||
if timeoutSet {
|
||||
s.ctx, s.cancel = context.WithTimeout(t.ctx, timeout)
|
||||
if state.data.timeoutSet {
|
||||
s.ctx, s.cancel = context.WithTimeout(t.ctx, state.data.timeout)
|
||||
} else {
|
||||
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)
|
||||
// Attach the received metadata to the context.
|
||||
if len(mdata) > 0 {
|
||||
s.ctx = metadata.NewIncomingContext(s.ctx, mdata)
|
||||
if statsTags := mdata["grpc-tags-bin"]; len(statsTags) > 0 {
|
||||
s.ctx = stats.SetIncomingTags(s.ctx, []byte(statsTags[len(statsTags)-1]))
|
||||
if len(state.data.mdata) > 0 {
|
||||
s.ctx = metadata.NewIncomingContext(s.ctx, state.data.mdata)
|
||||
}
|
||||
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 = stats.SetIncomingTrace(s.ctx, []byte(statsTrace[len(statsTrace)-1]))
|
||||
s.ctx, err = t.inTapHandle(s.ctx, info)
|
||||
if err != nil {
|
||||
if logger.V(logLevel) {
|
||||
logger.Warningf("transport: http2Server.operateHeaders got an error from InTapHandle: %v", err)
|
||||
}
|
||||
t.controlBuf.put(&cleanupStream{
|
||||
streamID: s.id,
|
||||
rst: true,
|
||||
rstCode: http2.ErrCodeRefusedStream,
|
||||
onWrite: func() {},
|
||||
})
|
||||
s.cancel()
|
||||
return false
|
||||
}
|
||||
}
|
||||
t.mu.Lock()
|
||||
|
|
@ -514,40 +392,16 @@ func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func(
|
|||
s.cancel()
|
||||
return false
|
||||
}
|
||||
if httpMethod != http.MethodPost {
|
||||
if streamID%2 != 1 || streamID <= t.maxStreamID {
|
||||
t.mu.Unlock()
|
||||
// illegal gRPC stream id.
|
||||
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()
|
||||
return false
|
||||
}
|
||||
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
|
||||
}
|
||||
return true
|
||||
}
|
||||
t.maxStreamID = streamID
|
||||
t.activeStreams[streamID] = s
|
||||
if len(t.activeStreams) == 1 {
|
||||
t.idle = time.Time{}
|
||||
|
|
@ -569,7 +423,7 @@ func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func(
|
|||
LocalAddr: t.localAddr,
|
||||
Compression: s.recvCompress,
|
||||
WireLength: int(frame.Header().Length),
|
||||
Header: metadata.MD(mdata).Copy(),
|
||||
Header: metadata.MD(state.data.mdata).Copy(),
|
||||
}
|
||||
t.stats.HandleRPC(s.ctx, inHeader)
|
||||
}
|
||||
|
|
@ -781,7 +635,7 @@ func (t *http2Server) handleData(f *http2.DataFrame) {
|
|||
s.write(recvMsg{buffer: buffer})
|
||||
}
|
||||
}
|
||||
if f.StreamEnded() {
|
||||
if f.Header().Flags.Has(http2.FlagDataEndStream) {
|
||||
// Received the end of stream from the client.
|
||||
s.compareAndSwapState(streamActive, streamReadDone)
|
||||
s.write(recvMsg{err: io.EOF})
|
||||
|
|
@ -1136,12 +990,12 @@ func (t *http2Server) keepalive() {
|
|||
if val <= 0 {
|
||||
// The connection has been idle for a duration of keepalive.MaxConnectionIdle or more.
|
||||
// Gracefully close the connection.
|
||||
t.Drain()
|
||||
t.drain(http2.ErrCodeNo, []byte{})
|
||||
return
|
||||
}
|
||||
idleTimer.Reset(val)
|
||||
case <-ageTimer.C:
|
||||
t.Drain()
|
||||
t.drain(http2.ErrCodeNo, []byte{})
|
||||
ageTimer.Reset(t.kp.MaxConnectionAgeGrace)
|
||||
select {
|
||||
case <-ageTimer.C:
|
||||
|
|
@ -1195,11 +1049,11 @@ func (t *http2Server) keepalive() {
|
|||
// Close starts shutting down the http2Server transport.
|
||||
// TODO(zhaoq): Now the destruction is not blocked on any pending streams. This
|
||||
// could cause some resource issue. Revisit this later.
|
||||
func (t *http2Server) Close() {
|
||||
func (t *http2Server) Close() error {
|
||||
t.mu.Lock()
|
||||
if t.state == closing {
|
||||
t.mu.Unlock()
|
||||
return
|
||||
return errors.New("transport: Close() was already called")
|
||||
}
|
||||
t.state = closing
|
||||
streams := t.activeStreams
|
||||
|
|
@ -1207,9 +1061,7 @@ func (t *http2Server) Close() {
|
|||
t.mu.Unlock()
|
||||
t.controlBuf.finish()
|
||||
close(t.done)
|
||||
if err := t.conn.Close(); err != nil && logger.V(logLevel) {
|
||||
logger.Infof("transport: error closing conn during Close: %v", err)
|
||||
}
|
||||
err := t.conn.Close()
|
||||
if channelz.IsOn() {
|
||||
channelz.RemoveEntry(t.channelzID)
|
||||
}
|
||||
|
|
@ -1221,6 +1073,7 @@ func (t *http2Server) Close() {
|
|||
connEnd := &stats.ConnEnd{}
|
||||
t.stats.HandleConn(t.ctx, connEnd)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// deleteStream deletes the stream s from transport's active streams.
|
||||
|
|
@ -1285,13 +1138,17 @@ func (t *http2Server) RemoteAddr() net.Addr {
|
|||
}
|
||||
|
||||
func (t *http2Server) Drain() {
|
||||
t.drain(http2.ErrCodeNo, []byte{})
|
||||
}
|
||||
|
||||
func (t *http2Server) drain(code http2.ErrCode, debugData []byte) {
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
if t.drainChan != nil {
|
||||
return
|
||||
}
|
||||
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}}
|
||||
|
|
@ -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
|
||||
// in draining mode.
|
||||
func (t *http2Server) outgoingGoAwayHandler(g *goAway) (bool, error) {
|
||||
t.maxStreamMu.Lock()
|
||||
t.mu.Lock()
|
||||
if t.state == closing { // TODO(mmukhi): This seems unnecessary.
|
||||
t.mu.Unlock()
|
||||
t.maxStreamMu.Unlock()
|
||||
// The transport is closing.
|
||||
return false, ErrConnClosing
|
||||
}
|
||||
sid := t.maxStreamID
|
||||
if !g.headsUp {
|
||||
// Stop accepting more streams now.
|
||||
t.state = draining
|
||||
sid := t.maxStreamID
|
||||
if len(t.activeStreams) == 0 {
|
||||
g.closeConn = true
|
||||
}
|
||||
t.mu.Unlock()
|
||||
t.maxStreamMu.Unlock()
|
||||
if err := t.framer.fr.WriteGoAway(sid, g.code, g.debugData); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
|
@ -1328,7 +1182,6 @@ func (t *http2Server) outgoingGoAwayHandler(g *goAway) (bool, error) {
|
|||
return true, nil
|
||||
}
|
||||
t.mu.Unlock()
|
||||
t.maxStreamMu.Unlock()
|
||||
// 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
|
||||
// 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
|
||||
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"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/grpclog"
|
||||
"google.golang.org/grpc/internal/grpcutil"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
|
|
@ -95,6 +96,52 @@ var (
|
|||
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
|
||||
// reserved by gRPC protocol. Any other headers are classified as the
|
||||
// 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"
|
||||
|
||||
func encodeBinHeader(v []byte) string {
|
||||
|
|
@ -161,16 +216,166 @@ func decodeMetadataHeader(k, v string) (string, error) {
|
|||
return v, nil
|
||||
}
|
||||
|
||||
func decodeGRPCStatusDetails(rawDetails string) (*status.Status, error) {
|
||||
v, err := decodeBinHeader(rawDetails)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
func (d *decodeState) decodeHeader(frame *http2.MetaHeadersFrame) (http2.ErrCode, error) {
|
||||
// frame.Truncated is set to true when framer detects that the current header
|
||||
// list size hits MaxHeaderListSize limit.
|
||||
if frame.Truncated {
|
||||
return http2.ErrCodeFrameSize, status.Error(codes.Internal, "peer header list size exceeded limit")
|
||||
}
|
||||
st := &spb.Status{}
|
||||
if err = proto.Unmarshal(v, st); err != nil {
|
||||
return nil, err
|
||||
|
||||
for _, hf := range frame.Fields {
|
||||
d.processHeaderField(hf)
|
||||
}
|
||||
|
||||
if d.data.isGRPC {
|
||||
if d.data.grpcErr != nil {
|
||||
return http2.ErrCodeProtocol, d.data.grpcErr
|
||||
}
|
||||
if d.serverSide {
|
||||
return http2.ErrCodeNo, nil
|
||||
}
|
||||
if d.data.rawStatusCode == nil && d.data.statusGen == nil {
|
||||
// gRPC status doesn't exist.
|
||||
// Set rawStatusCode to be unknown and return nil error.
|
||||
// So that, if the stream has ended this Unknown status
|
||||
// will be propagated to the user.
|
||||
// Otherwise, it will be ignored. In which case, status from
|
||||
// a later trailer, that has StreamEnded flag set, is propagated.
|
||||
code := int(codes.Unknown)
|
||||
d.data.rawStatusCode = &code
|
||||
}
|
||||
return http2.ErrCodeNo, nil
|
||||
}
|
||||
|
||||
// HTTP fallback mode
|
||||
if d.data.httpErr != nil {
|
||||
return http2.ErrCodeProtocol, d.data.httpErr
|
||||
}
|
||||
|
||||
var (
|
||||
code = codes.Internal // when header does not include HTTP status, return INTERNAL
|
||||
ok bool
|
||||
)
|
||||
|
||||
if d.data.httpStatus != nil {
|
||||
code, ok = HTTPStatusConvTab[*(d.data.httpStatus)]
|
||||
if !ok {
|
||||
code = codes.Unknown
|
||||
}
|
||||
}
|
||||
|
||||
return http2.ErrCodeProtocol, status.Error(code, d.constructHTTPErrMsg())
|
||||
}
|
||||
|
||||
// constructErrMsg constructs error message to be returned in HTTP fallback mode.
|
||||
// Format: HTTP status code and its corresponding message + content-type error message.
|
||||
func (d *decodeState) constructHTTPErrMsg() string {
|
||||
var errMsgs []string
|
||||
|
||||
if d.data.httpStatus == nil {
|
||||
errMsgs = append(errMsgs, "malformed header: missing HTTP status")
|
||||
} else {
|
||||
errMsgs = append(errMsgs, fmt.Sprintf("%s: HTTP status code %d", http.StatusText(*(d.data.httpStatus)), *d.data.httpStatus))
|
||||
}
|
||||
|
||||
if d.data.contentTypeErr == "" {
|
||||
errMsgs = append(errMsgs, "transport: missing content-type field")
|
||||
} else {
|
||||
errMsgs = append(errMsgs, d.data.contentTypeErr)
|
||||
}
|
||||
|
||||
return strings.Join(errMsgs, "; ")
|
||||
}
|
||||
|
||||
func (d *decodeState) addMetadata(k, v string) {
|
||||
if d.data.mdata == nil {
|
||||
d.data.mdata = make(map[string][]string)
|
||||
}
|
||||
d.data.mdata[k] = append(d.data.mdata[k], v)
|
||||
}
|
||||
|
||||
func (d *decodeState) processHeaderField(f hpack.HeaderField) {
|
||||
switch f.Name {
|
||||
case "content-type":
|
||||
contentSubtype, validContentType := grpcutil.ContentSubtype(f.Value)
|
||||
if !validContentType {
|
||||
d.data.contentTypeErr = fmt.Sprintf("transport: received the unexpected content-type %q", f.Value)
|
||||
return
|
||||
}
|
||||
d.data.contentSubtype = contentSubtype
|
||||
// TODO: do we want to propagate the whole content-type in the metadata,
|
||||
// or come up with a way to just propagate the content-subtype if it was set?
|
||||
// ie {"content-type": "application/grpc+proto"} or {"content-subtype": "proto"}
|
||||
// in the metadata?
|
||||
d.addMetadata(f.Name, f.Value)
|
||||
d.data.isGRPC = true
|
||||
case "grpc-encoding":
|
||||
d.data.encoding = f.Value
|
||||
case "grpc-status":
|
||||
code, err := strconv.Atoi(f.Value)
|
||||
if err != nil {
|
||||
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
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
*/
|
||||
|
||||
// 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
|
||||
|
||||
import (
|
||||
|
|
@ -31,7 +31,7 @@ const key = keyType("grpc.internal.transport.networktype")
|
|||
|
||||
// Set returns a copy of the provided address with attributes containing networkType.
|
||||
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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ var (
|
|||
httpProxyFromEnvironment = http.ProxyFromEnvironment
|
||||
)
|
||||
|
||||
func mapAddress(address string) (*url.URL, error) {
|
||||
func mapAddress(ctx context.Context, address string) (*url.URL, error) {
|
||||
req := &http.Request{
|
||||
URL: &url.URL{
|
||||
Scheme: "https",
|
||||
|
|
@ -114,7 +114,7 @@ func doHTTPConnectHandshake(ctx context.Context, conn net.Conn, backendAddr stri
|
|||
// connection.
|
||||
func proxyDial(ctx context.Context, addr string, grpcUA string) (conn net.Conn, err error) {
|
||||
newAddr := addr
|
||||
proxyURL, err := mapAddress(addr)
|
||||
proxyURL, err := mapAddress(ctx, addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@ import (
|
|||
"net"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/credentials"
|
||||
|
|
@ -242,7 +241,6 @@ type Stream struct {
|
|||
ctx context.Context // the associated context of the 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.
|
||||
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)
|
||||
method string // the associated RPC method of the stream
|
||||
recvCompress string
|
||||
|
|
@ -519,8 +517,7 @@ const (
|
|||
// ServerConfig consists of all the configurations to establish a server transport.
|
||||
type ServerConfig struct {
|
||||
MaxStreams uint32
|
||||
ConnectionTimeout time.Duration
|
||||
Credentials credentials.TransportCredentials
|
||||
AuthInfo credentials.AuthInfo
|
||||
InTapHandle tap.ServerInHandle
|
||||
StatsHandler stats.Handler
|
||||
KeepaliveParams keepalive.ServerParameters
|
||||
|
|
@ -534,6 +531,12 @@ type ServerConfig struct {
|
|||
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.
|
||||
type ConnectOptions struct {
|
||||
// UserAgent is the application user agent.
|
||||
|
|
@ -608,8 +611,6 @@ type CallHdr struct {
|
|||
ContentSubtype string
|
||||
|
||||
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
|
||||
|
|
@ -618,7 +619,7 @@ type ClientTransport interface {
|
|||
// Close tears down this transport. Once it returns, the transport
|
||||
// should not be accessed any more. The caller must make sure this
|
||||
// is called only once.
|
||||
Close(err error)
|
||||
Close() error
|
||||
|
||||
// GracefulClose starts to tear down the transport: the transport will stop
|
||||
// accepting new RPCs and NewStream will return error. Once all streams are
|
||||
|
|
@ -652,9 +653,8 @@ type ClientTransport interface {
|
|||
// HTTP/2).
|
||||
GoAway() <-chan struct{}
|
||||
|
||||
// GetGoAwayReason returns the reason why GoAway frame was received, along
|
||||
// with a human readable string with debug info.
|
||||
GetGoAwayReason() (GoAwayReason, string)
|
||||
// GetGoAwayReason returns the reason why GoAway frame was received.
|
||||
GetGoAwayReason() GoAwayReason
|
||||
|
||||
// RemoteAddr returns the remote network address.
|
||||
RemoteAddr() net.Addr
|
||||
|
|
@ -690,7 +690,7 @@ type ServerTransport interface {
|
|||
// Close tears down the transport. Once it is called, the transport
|
||||
// should not be accessed any more. All the pending streams and their
|
||||
// handlers will be terminated asynchronously.
|
||||
Close()
|
||||
Close() error
|
||||
|
||||
// RemoteAddr returns the remote network address.
|
||||
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)))
|
||||
}
|
||||
md := MD{}
|
||||
for i := 0; i < len(kv); i += 2 {
|
||||
key := strings.ToLower(kv[i])
|
||||
md[key] = append(md[key], kv[i+1])
|
||||
var key string
|
||||
for i, s := range kv {
|
||||
if i%2 == 0 {
|
||||
key = strings.ToLower(s)
|
||||
continue
|
||||
}
|
||||
md[key] = append(md[key], s)
|
||||
}
|
||||
return md
|
||||
}
|
||||
|
|
@ -93,16 +97,12 @@ func (md MD) Copy() MD {
|
|||
}
|
||||
|
||||
// Get obtains the values for a given key.
|
||||
//
|
||||
// k is converted to lowercase before searching in md.
|
||||
func (md MD) Get(k string) []string {
|
||||
k = strings.ToLower(k)
|
||||
return md[k]
|
||||
}
|
||||
|
||||
// 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) {
|
||||
if len(vals) == 0 {
|
||||
return
|
||||
|
|
@ -111,10 +111,7 @@ func (md MD) Set(k string, vals ...string) {
|
|||
md[k] = vals
|
||||
}
|
||||
|
||||
// Append adds the values to key k, not overwriting what was already stored at
|
||||
// that key.
|
||||
//
|
||||
// k is converted to lowercase before storing in md.
|
||||
// Append adds the values to key k, not overwriting what was already stored at that key.
|
||||
func (md MD) Append(k string, vals ...string) {
|
||||
if len(vals) == 0 {
|
||||
return
|
||||
|
|
@ -123,17 +120,9 @@ func (md MD) Append(k string, vals ...string) {
|
|||
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.
|
||||
//
|
||||
// The order of values for each key is determined by the order in which the mds
|
||||
// containing those values are presented to Join.
|
||||
// The order of values for each key is determined by the order in which
|
||||
// the mds containing those values are presented to Join.
|
||||
func Join(mds ...MD) MD {
|
||||
out := MD{}
|
||||
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
|
||||
// with any existing metadata in the context. Please refer to the documentation
|
||||
// of Pairs for a description of kv.
|
||||
// with any existing metadata in the context. Please refer to the
|
||||
// documentation of Pairs for a description of kv.
|
||||
func AppendToOutgoingContext(ctx context.Context, kv ...string) context.Context {
|
||||
if len(kv)%2 == 1 {
|
||||
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})
|
||||
}
|
||||
|
||||
// FromIncomingContext returns the incoming metadata in ctx if it exists.
|
||||
//
|
||||
// All keys in the returned MD are lowercase.
|
||||
func FromIncomingContext(ctx context.Context) (MD, bool) {
|
||||
md, ok := ctx.Value(mdIncomingKey{}).(MD)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
out := MD{}
|
||||
for k, v := range md {
|
||||
// We need to manually convert all keys to lower case, because MD is a
|
||||
// map, and there's no guarantee that the MD attached to the context is
|
||||
// created using our helper functions.
|
||||
key := strings.ToLower(k)
|
||||
out[key] = v
|
||||
}
|
||||
return out, true
|
||||
// FromIncomingContext returns the incoming metadata in ctx if it exists. The
|
||||
// returned MD should not be modified. Writing to it may cause races.
|
||||
// Modification should be made to copies of the returned MD.
|
||||
func FromIncomingContext(ctx context.Context) (md MD, ok bool) {
|
||||
md, ok = ctx.Value(mdIncomingKey{}).(MD)
|
||||
return
|
||||
}
|
||||
|
||||
// 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
|
||||
// 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.
|
||||
// This is intended for gRPC-internal use ONLY.
|
||||
func FromOutgoingContextRaw(ctx context.Context) (MD, [][]string, bool) {
|
||||
raw, ok := ctx.Value(mdOutgoingKey{}).(rawMD)
|
||||
if !ok {
|
||||
|
|
@ -211,34 +186,21 @@ func FromOutgoingContextRaw(ctx context.Context) (MD, [][]string, bool) {
|
|||
return raw.md, raw.added, true
|
||||
}
|
||||
|
||||
// FromOutgoingContext returns the outgoing metadata in ctx if it exists.
|
||||
//
|
||||
// All keys in the returned MD are lowercase.
|
||||
// FromOutgoingContext returns the outgoing metadata in ctx if it exists. The
|
||||
// returned MD should not be modified. Writing to it may cause races.
|
||||
// Modification should be made to copies of the returned MD.
|
||||
func FromOutgoingContext(ctx context.Context) (MD, bool) {
|
||||
raw, ok := ctx.Value(mdOutgoingKey{}).(rawMD)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
out := MD{}
|
||||
for k, v := range raw.md {
|
||||
// We need to manually convert all keys to lower case, because MD is a
|
||||
// map, and there's no guarantee that the MD attached to the context is
|
||||
// created using our helper functions.
|
||||
key := strings.ToLower(k)
|
||||
out[key] = v
|
||||
mds := make([]MD, 0, len(raw.added)+1)
|
||||
mds = append(mds, raw.md)
|
||||
for _, vv := range raw.added {
|
||||
mds = append(mds, Pairs(vv...))
|
||||
}
|
||||
for _, added := range raw.added {
|
||||
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
|
||||
return Join(mds...), ok
|
||||
}
|
||||
|
||||
type rawMD struct {
|
||||
|
|
|
|||
|
|
@ -144,10 +144,10 @@ func (pw *pickerWrapper) pick(ctx context.Context, failfast bool, info balancer.
|
|||
|
||||
acw, ok := pickResult.SubConn.(*acBalancerWrapper)
|
||||
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
|
||||
}
|
||||
if t := acw.getAddrConn().getReadyTransport(); t != nil {
|
||||
if t, ok := acw.getAddrConn().getReadyTransport(); ok {
|
||||
if channelz.IsOn() {
|
||||
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.sc.Connect()
|
||||
} else {
|
||||
b.cc.UpdateAddresses(b.sc, cs.ResolverState.Addresses)
|
||||
b.sc.UpdateAddresses(cs.ResolverState.Addresses)
|
||||
b.sc.Connect()
|
||||
}
|
||||
return nil
|
||||
|
|
@ -107,12 +107,10 @@ func (b *pickfirstBalancer) UpdateSubConnState(sc balancer.SubConn, s balancer.S
|
|||
}
|
||||
|
||||
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}}})
|
||||
case connectivity.Connecting:
|
||||
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:
|
||||
b.cc.UpdateState(balancer.State{
|
||||
ConnectivityState: s.ConnectivityState,
|
||||
|
|
@ -124,12 +122,6 @@ func (b *pickfirstBalancer) UpdateSubConnState(sc balancer.SubConn, s balancer.S
|
|||
func (b *pickfirstBalancer) Close() {
|
||||
}
|
||||
|
||||
func (b *pickfirstBalancer) ExitIdle() {
|
||||
if b.sc != nil && b.state == connectivity.Idle {
|
||||
b.sc.Connect()
|
||||
}
|
||||
}
|
||||
|
||||
type picker struct {
|
||||
result balancer.PickResult
|
||||
err error
|
||||
|
|
@ -139,17 +131,6 @@ func (p *picker) Pick(info balancer.PickInfo) (balancer.PickResult, error) {
|
|||
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() {
|
||||
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"
|
||||
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
|
||||
|
||||
# Generates sources without the embed requirement
|
||||
|
|
@ -71,26 +76,13 @@ SOURCES=(
|
|||
${WORKDIR}/grpc-proto/grpc/service_config/service_config.proto
|
||||
${WORKDIR}/grpc-proto/grpc/testing/*.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
|
||||
# import path of 'bar' in the generated code when 'foo.proto' is imported in
|
||||
# one of the sources.
|
||||
#
|
||||
# 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
|
||||
OPTS=Mgrpc/service_config/service_config.proto=/internal/proto/grpc_service_config,Mgrpc/core/stats.proto=google.golang.org/grpc/interop/grpc_testing/core
|
||||
|
||||
for src in ${SOURCES[@]}; do
|
||||
echo "protoc ${src}"
|
||||
|
|
@ -99,6 +91,7 @@ for src in ${SOURCES[@]}; do
|
|||
-I${WORKDIR}/grpc-proto \
|
||||
-I${WORKDIR}/googleapis \
|
||||
-I${WORKDIR}/protobuf/src \
|
||||
-I${WORKDIR}/istio \
|
||||
${src}
|
||||
done
|
||||
|
||||
|
|
@ -109,13 +102,14 @@ for src in ${LEGACY_SOURCES[@]}; do
|
|||
-I${WORKDIR}/grpc-proto \
|
||||
-I${WORKDIR}/googleapis \
|
||||
-I${WORKDIR}/protobuf/src \
|
||||
-I${WORKDIR}/istio \
|
||||
${src}
|
||||
done
|
||||
|
||||
# The go_package option in grpc/lookup/v1/rls.proto doesn't match the
|
||||
# current location. Move it into the right place.
|
||||
mkdir -p ${WORKDIR}/out/google.golang.org/grpc/internal/proto/grpc_lookup_v1
|
||||
mv ${WORKDIR}/out/google.golang.org/grpc/lookup/grpc_lookup_v1/* ${WORKDIR}/out/google.golang.org/grpc/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/balancer/rls/internal/proto/grpc_lookup_v1
|
||||
|
||||
# grpc_testingv3/testv3.pb.go is not re-generated because it was
|
||||
# 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/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/* .
|
||||
|
|
|
|||
|
|
@ -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 (
|
||||
"context"
|
||||
"net"
|
||||
"net/url"
|
||||
|
||||
"google.golang.org/grpc/attributes"
|
||||
"google.golang.org/grpc/credentials"
|
||||
|
|
@ -117,14 +116,9 @@ type Address struct {
|
|||
ServerName string
|
||||
|
||||
// Attributes contains arbitrary data about this address intended for
|
||||
// consumption by the SubConn.
|
||||
// consumption by the load balancing policy.
|
||||
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.
|
||||
//
|
||||
// Deprecated: use Attributes instead.
|
||||
|
|
@ -137,15 +131,6 @@ type Address struct {
|
|||
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
|
||||
// the resolver.
|
||||
type BuildOptions struct {
|
||||
|
|
@ -196,7 +181,7 @@ type State struct {
|
|||
// gRPC to add new methods to this interface.
|
||||
type ClientConn interface {
|
||||
// UpdateState updates the state of the ClientConn appropriately.
|
||||
UpdateState(State) error
|
||||
UpdateState(State)
|
||||
// ReportError notifies the ClientConn that the Resolver encountered an
|
||||
// error. The ClientConn will notify the load balancer and begin calling
|
||||
// ResolveNow on the Resolver with exponential backoff.
|
||||
|
|
@ -219,36 +204,25 @@ type ClientConn interface {
|
|||
|
||||
// Target represents a target for gRPC, as specified in:
|
||||
// https://github.com/grpc/grpc/blob/master/doc/naming.md.
|
||||
// It is parsed from the target string that gets passed into Dial or DialContext
|
||||
// by the user. And gRPC passes it to the resolver and the balancer.
|
||||
// It is parsed from the target string that gets passed into Dial or DialContext by the user. And
|
||||
// grpc passes it to the resolver and the balancer.
|
||||
//
|
||||
// If the target follows the naming spec, and the parsed scheme is registered
|
||||
// with gRPC, we will parse the target string according to the spec. If the
|
||||
// target does not contain a scheme or if the parsed scheme is not registered
|
||||
// (i.e. no corresponding resolver available to resolve the endpoint), we will
|
||||
// apply the default scheme, and will attempt to reparse it.
|
||||
// If the target follows the naming spec, and the parsed scheme is registered with grpc, we will
|
||||
// parse the target string according to the spec. e.g. "dns://some_authority/foo.bar" will be parsed
|
||||
// into &Target{Scheme: "dns", Authority: "some_authority", Endpoint: "foo.bar"}
|
||||
//
|
||||
// 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"
|
||||
// Target{Scheme: "dns", Authority: "some_authority", Endpoint: "foo.bar"}
|
||||
// - "foo.bar"
|
||||
// Target{Scheme: resolver.GetDefaultScheme(), Endpoint: "foo.bar"}
|
||||
// - "unknown_scheme://authority/endpoint"
|
||||
// Target{Scheme: resolver.GetDefaultScheme(), Endpoint: "unknown_scheme://authority/endpoint"}
|
||||
// If the parsed scheme is not registered (i.e. no corresponding resolver available to resolve the
|
||||
// endpoint), we set the Scheme to be the default scheme, and set the Endpoint to be the full target
|
||||
// string. e.g. target string "unknown_scheme://authority/endpoint" will be parsed into
|
||||
// &Target{Scheme: resolver.GetDefaultScheme(), Endpoint: "unknown_scheme://authority/endpoint"}.
|
||||
type Target struct {
|
||||
// Deprecated: use URL.Scheme instead.
|
||||
Scheme string
|
||||
// Deprecated: use URL.Host instead.
|
||||
Scheme string
|
||||
Authority string
|
||||
// Deprecated: use URL.Path or URL.Opaque instead. The latter is set when
|
||||
// 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
|
||||
Endpoint string
|
||||
}
|
||||
|
||||
// Builder creates a resolver that will be used to watch name resolution updates.
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import (
|
|||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"google.golang.org/grpc/balancer"
|
||||
"google.golang.org/grpc/credentials"
|
||||
|
|
@ -40,7 +41,8 @@ type ccResolverWrapper struct {
|
|||
done *grpcsync.Event
|
||||
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
|
||||
|
|
@ -91,37 +93,71 @@ func (ccr *ccResolverWrapper) close() {
|
|||
ccr.resolverMu.Unlock()
|
||||
}
|
||||
|
||||
func (ccr *ccResolverWrapper) UpdateState(s resolver.State) error {
|
||||
ccr.incomingMu.Lock()
|
||||
defer ccr.incomingMu.Unlock()
|
||||
// poll begins or ends asynchronous polling of the resolver based on whether
|
||||
// err is ErrBadResolverState.
|
||||
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() {
|
||||
return nil
|
||||
return
|
||||
}
|
||||
channelz.Infof(logger, ccr.cc.channelzID, "ccResolverWrapper: sending update to cc: %v", s)
|
||||
if channelz.IsOn() {
|
||||
ccr.addChannelzTraceEvent(s)
|
||||
}
|
||||
ccr.curState = s
|
||||
if err := ccr.cc.updateResolverState(ccr.curState, nil); err == balancer.ErrBadResolverState {
|
||||
return balancer.ErrBadResolverState
|
||||
}
|
||||
return nil
|
||||
ccr.poll(ccr.cc.updateResolverState(ccr.curState, nil))
|
||||
}
|
||||
|
||||
func (ccr *ccResolverWrapper) ReportError(err error) {
|
||||
ccr.incomingMu.Lock()
|
||||
defer ccr.incomingMu.Unlock()
|
||||
if ccr.done.HasFired() {
|
||||
return
|
||||
}
|
||||
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.
|
||||
func (ccr *ccResolverWrapper) NewAddress(addrs []resolver.Address) {
|
||||
ccr.incomingMu.Lock()
|
||||
defer ccr.incomingMu.Unlock()
|
||||
if ccr.done.HasFired() {
|
||||
return
|
||||
}
|
||||
|
|
@ -130,14 +166,12 @@ func (ccr *ccResolverWrapper) NewAddress(addrs []resolver.Address) {
|
|||
ccr.addChannelzTraceEvent(resolver.State{Addresses: addrs, ServiceConfig: ccr.curState.ServiceConfig})
|
||||
}
|
||||
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
|
||||
// configs to gRPC.
|
||||
func (ccr *ccResolverWrapper) NewServiceConfig(sc string) {
|
||||
ccr.incomingMu.Lock()
|
||||
defer ccr.incomingMu.Unlock()
|
||||
if ccr.done.HasFired() {
|
||||
return
|
||||
}
|
||||
|
|
@ -149,13 +183,14 @@ func (ccr *ccResolverWrapper) NewServiceConfig(sc string) {
|
|||
scpr := parseServiceConfig(sc)
|
||||
if scpr.Err != nil {
|
||||
channelz.Warningf(logger, ccr.cc.channelzID, "ccResolverWrapper: error parsing service config: %v", scpr.Err)
|
||||
ccr.poll(balancer.ErrBadResolverState)
|
||||
return
|
||||
}
|
||||
if channelz.IsOn() {
|
||||
ccr.addChannelzTraceEvent(resolver.State{Addresses: ccr.curState.Addresses, 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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
// connections or unreachable servers. If waitForReady is false and the
|
||||
// connection is in the TRANSIENT_FAILURE state, the RPC will fail
|
||||
// connections or unreachable servers. If waitForReady is false, the RPC will fail
|
||||
// immediately. Otherwise, the RPC client will block the call until a
|
||||
// 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
|
||||
|
|
@ -430,10 +429,9 @@ func (o ContentSubtypeCallOption) before(c *callInfo) error {
|
|||
}
|
||||
func (o ContentSubtypeCallOption) after(c *callInfo, attempt *csAttempt) {}
|
||||
|
||||
// ForceCodec returns a CallOption that will set codec to be used for all
|
||||
// request and response messages for a call. The result of calling Name() will
|
||||
// be used as the content-subtype after converting to lowercase, unless
|
||||
// CallContentSubtype is also used.
|
||||
// ForceCodec returns a CallOption that will set the given Codec to be
|
||||
// used for all request and response messages for a call. The result of calling
|
||||
// String() will be used as the content-subtype in a case-insensitive manner.
|
||||
//
|
||||
// See Content-Type on
|
||||
// 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 {
|
||||
return nil, status.Errorf(codes.Internal, "grpc: failed to decompress the received message %v", err)
|
||||
}
|
||||
if size > maxReceiveMessageSize {
|
||||
// TODO: Revisit the error code. Currently keep it consistent with java
|
||||
// implementation.
|
||||
return nil, status.Errorf(codes.ResourceExhausted, "grpc: received message after decompression larger than max (%d vs. %d)", size, maxReceiveMessageSize)
|
||||
}
|
||||
} else {
|
||||
size = len(d)
|
||||
}
|
||||
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
|
||||
}
|
||||
|
|
@ -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.
|
||||
func toRPCErr(err error) error {
|
||||
switch err {
|
||||
case nil, io.EOF:
|
||||
if err == nil || err == io.EOF {
|
||||
return err
|
||||
case context.DeadlineExceeded:
|
||||
return status.Error(codes.DeadlineExceeded, err.Error())
|
||||
case context.Canceled:
|
||||
return status.Error(codes.Canceled, err.Error())
|
||||
case io.ErrUnexpectedEOF:
|
||||
}
|
||||
if err == io.ErrUnexpectedEOF {
|
||||
return status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
|
||||
switch e := err.(type) {
|
||||
case transport.ConnectionError:
|
||||
return status.Error(codes.Unavailable, e.Desc)
|
||||
case *transport.NewStreamError:
|
||||
return toRPCErr(e.Err)
|
||||
}
|
||||
|
||||
if _, ok := status.FromError(err); ok {
|
||||
return err
|
||||
}
|
||||
|
||||
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())
|
||||
}
|
||||
|
||||
// setCallInfoCodec should only be called after CallOptions have been applied.
|
||||
func setCallInfoCodec(c *callInfo) error {
|
||||
if c.codec != nil {
|
||||
// codec was already set by a CallOption; use it, but set the content
|
||||
// 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())
|
||||
}
|
||||
}
|
||||
// codec was already set by a CallOption; use it.
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -57,22 +57,12 @@ import (
|
|||
const (
|
||||
defaultServerMaxReceiveMessageSize = 1024 * 1024 * 4
|
||||
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() {
|
||||
internal.GetServerCredentials = func(srv *Server) credentials.TransportCredentials {
|
||||
return srv.opts.creds
|
||||
}
|
||||
internal.DrainServerTransports = func(srv *Server, addr string) {
|
||||
srv.drainServerTransports(addr)
|
||||
}
|
||||
}
|
||||
|
||||
var statusOK = status.New(codes.OK, "")
|
||||
|
|
@ -117,12 +107,9 @@ type serverWorkerData struct {
|
|||
type Server struct {
|
||||
opts serverOptions
|
||||
|
||||
mu sync.Mutex // guards following
|
||||
lis map[net.Listener]bool
|
||||
// conns contains all active server transports. It is a map keyed on a
|
||||
// listener address with the value being the set of active transports
|
||||
// belonging to that listener.
|
||||
conns map[string]map[transport.ServerTransport]bool
|
||||
mu sync.Mutex // guards following
|
||||
lis map[net.Listener]bool
|
||||
conns map[transport.ServerTransport]bool
|
||||
serve bool
|
||||
drain bool
|
||||
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
|
||||
// messages. For backward compatibility, all outbound messages will be sent
|
||||
// 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
|
||||
// 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 {
|
||||
return newFuncServerOption(func(o *serverOptions) {
|
||||
if o.inTapHandle != nil {
|
||||
|
|
@ -566,7 +519,7 @@ func NewServer(opt ...ServerOption) *Server {
|
|||
s := &Server{
|
||||
lis: make(map[net.Listener]bool),
|
||||
opts: opts,
|
||||
conns: make(map[string]map[transport.ServerTransport]bool),
|
||||
conns: make(map[transport.ServerTransport]bool),
|
||||
services: make(map[string]*serviceInfo),
|
||||
quit: grpcsync.NewEvent(),
|
||||
done: grpcsync.NewEvent(),
|
||||
|
|
@ -710,6 +663,13 @@ func (s *Server) GetServiceInfo() map[string]ServiceInfo {
|
|||
// the server being 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 {
|
||||
net.Listener
|
||||
channelzID int64
|
||||
|
|
@ -818,7 +778,7 @@ func (s *Server) Serve(lis net.Listener) error {
|
|||
// s.conns before this conn can be added.
|
||||
s.serveWG.Add(1)
|
||||
go func() {
|
||||
s.handleRawConn(lis.Addr().String(), rawConn)
|
||||
s.handleRawConn(rawConn)
|
||||
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
|
||||
// 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() {
|
||||
rawConn.Close()
|
||||
return
|
||||
}
|
||||
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)
|
||||
st := s.newHTTP2Transport(rawConn)
|
||||
rawConn.SetDeadline(time.Time{})
|
||||
st := s.newHTTP2Transport(conn, authInfo)
|
||||
if st == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if !s.addConn(lisAddr, st) {
|
||||
rawConn.SetDeadline(time.Time{})
|
||||
if !s.addConn(st) {
|
||||
return
|
||||
}
|
||||
go func() {
|
||||
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
|
||||
// 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{
|
||||
MaxStreams: s.opts.maxConcurrentStreams,
|
||||
ConnectionTimeout: s.opts.connectionTimeout,
|
||||
Credentials: s.opts.creds,
|
||||
AuthInfo: authInfo,
|
||||
InTapHandle: s.opts.inTapHandle,
|
||||
StatsHandler: s.opts.statsHandler,
|
||||
KeepaliveParams: s.opts.keepaliveParams,
|
||||
|
|
@ -877,20 +841,13 @@ func (s *Server) newHTTP2Transport(c net.Conn) transport.ServerTransport {
|
|||
MaxHeaderListSize: s.opts.maxHeaderListSize,
|
||||
HeaderTableSize: s.opts.headerTableSize,
|
||||
}
|
||||
st, err := transport.NewServerTransport(c, config)
|
||||
st, err := transport.NewServerTransport("http2", c, config)
|
||||
if err != nil {
|
||||
s.mu.Lock()
|
||||
s.errorf("NewServerTransport(%q) failed: %v", c.RemoteAddr(), err)
|
||||
s.mu.Unlock()
|
||||
// ErrConnDispatched means that the connection was dispatched away from
|
||||
// gRPC; those connections should be left open.
|
||||
if err != credentials.ErrConnDispatched {
|
||||
// Don't log on ErrConnDispatched and io.EOF to prevent log spam.
|
||||
if err != io.EOF {
|
||||
channelz.Warning(logger, s.channelzID, "grpc: Server.Serve failed to create ServerTransport: ", err)
|
||||
}
|
||||
c.Close()
|
||||
}
|
||||
c.Close()
|
||||
channelz.Warning(logger, s.channelzID, "grpc: Server.Serve failed to create ServerTransport: ", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -967,10 +924,10 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if !s.addConn(listenerAddressForServeHTTP, st) {
|
||||
if !s.addConn(st) {
|
||||
return
|
||||
}
|
||||
defer s.removeConn(listenerAddressForServeHTTP, st)
|
||||
defer s.removeConn(st)
|
||||
s.serveStreams(st)
|
||||
}
|
||||
|
||||
|
|
@ -998,7 +955,7 @@ func (s *Server) traceInfo(st transport.ServerTransport, stream *transport.Strea
|
|||
return trInfo
|
||||
}
|
||||
|
||||
func (s *Server) addConn(addr string, st transport.ServerTransport) bool {
|
||||
func (s *Server) addConn(st transport.ServerTransport) bool {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
if s.conns == nil {
|
||||
|
|
@ -1010,28 +967,15 @@ func (s *Server) addConn(addr string, st transport.ServerTransport) bool {
|
|||
// immediately.
|
||||
st.Drain()
|
||||
}
|
||||
|
||||
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
|
||||
s.conns[st] = true
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *Server) removeConn(addr string, st transport.ServerTransport) {
|
||||
func (s *Server) removeConn(st transport.ServerTransport) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
conns := s.conns[addr]
|
||||
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)
|
||||
}
|
||||
if s.conns != nil {
|
||||
delete(s.conns, st)
|
||||
s.cv.Broadcast()
|
||||
}
|
||||
}
|
||||
|
|
@ -1096,29 +1040,22 @@ func chainUnaryServerInterceptors(s *Server) {
|
|||
} else if len(interceptors) == 1 {
|
||||
chainedInt = interceptors[0]
|
||||
} 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
|
||||
}
|
||||
|
||||
func chainUnaryInterceptors(interceptors []UnaryServerInterceptor) UnaryServerInterceptor {
|
||||
return func(ctx context.Context, req interface{}, info *UnaryServerInfo, handler UnaryHandler) (interface{}, error) {
|
||||
// the struct ensures the variables are allocated together, rather than separately, since we
|
||||
// know they should be garbage collected together. This saves 1 allocation and decreases
|
||||
// time/call by about 10% on the microbenchmark.
|
||||
var state struct {
|
||||
i int
|
||||
next UnaryHandler
|
||||
}
|
||||
state.next = func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
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)
|
||||
// getChainUnaryHandler recursively generate the chained UnaryHandler
|
||||
func getChainUnaryHandler(interceptors []UnaryServerInterceptor, curr int, info *UnaryServerInfo, finalHandler UnaryHandler) UnaryHandler {
|
||||
if curr == len(interceptors)-1 {
|
||||
return finalHandler
|
||||
}
|
||||
|
||||
return func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return interceptors[curr+1](ctx, req, info, getChainUnaryHandler(interceptors, curr+1, info, finalHandler))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1132,9 +1069,7 @@ func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport.
|
|||
if sh != nil {
|
||||
beginTime := time.Now()
|
||||
statsBegin = &stats.Begin{
|
||||
BeginTime: beginTime,
|
||||
IsClientStream: false,
|
||||
IsServerStream: false,
|
||||
BeginTime: beginTime,
|
||||
}
|
||||
sh.HandleRPC(stream.Context(), statsBegin)
|
||||
}
|
||||
|
|
@ -1386,29 +1321,22 @@ func chainStreamServerInterceptors(s *Server) {
|
|||
} else if len(interceptors) == 1 {
|
||||
chainedInt = interceptors[0]
|
||||
} 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
|
||||
}
|
||||
|
||||
func chainStreamInterceptors(interceptors []StreamServerInterceptor) StreamServerInterceptor {
|
||||
return func(srv interface{}, ss ServerStream, info *StreamServerInfo, handler StreamHandler) error {
|
||||
// the struct ensures the variables are allocated together, rather than separately, since we
|
||||
// know they should be garbage collected together. This saves 1 allocation and decreases
|
||||
// time/call by about 10% on the microbenchmark.
|
||||
var state struct {
|
||||
i int
|
||||
next StreamHandler
|
||||
}
|
||||
state.next = func(srv interface{}, ss ServerStream) error {
|
||||
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)
|
||||
// getChainStreamHandler recursively generate the chained StreamHandler
|
||||
func getChainStreamHandler(interceptors []StreamServerInterceptor, curr int, info *StreamServerInfo, finalHandler StreamHandler) StreamHandler {
|
||||
if curr == len(interceptors)-1 {
|
||||
return finalHandler
|
||||
}
|
||||
|
||||
return func(srv interface{}, ss ServerStream) error {
|
||||
return interceptors[curr+1](srv, ss, info, getChainStreamHandler(interceptors, curr+1, info, finalHandler))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1421,9 +1349,7 @@ func (s *Server) processStreamingRPC(t transport.ServerTransport, stream *transp
|
|||
if sh != nil {
|
||||
beginTime := time.Now()
|
||||
statsBegin = &stats.Begin{
|
||||
BeginTime: beginTime,
|
||||
IsClientStream: sd.ClientStreams,
|
||||
IsServerStream: sd.ServerStreams,
|
||||
BeginTime: beginTime,
|
||||
}
|
||||
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 {
|
||||
trInfo.tr.LazyLog(&trInfo.firstLine, false)
|
||||
}
|
||||
|
|
@ -1595,7 +1519,7 @@ func (s *Server) handleStream(t transport.ServerTransport, stream *transport.Str
|
|||
trInfo.tr.SetError()
|
||||
}
|
||||
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 {
|
||||
trInfo.tr.LazyLog(&fmtStringer{"%v", []interface{}{err}}, true)
|
||||
trInfo.tr.SetError()
|
||||
|
|
@ -1715,7 +1639,7 @@ func (s *Server) Stop() {
|
|||
s.mu.Lock()
|
||||
listeners := s.lis
|
||||
s.lis = nil
|
||||
conns := s.conns
|
||||
st := s.conns
|
||||
s.conns = nil
|
||||
// interrupt GracefulStop if Stop and GracefulStop are called concurrently.
|
||||
s.cv.Broadcast()
|
||||
|
|
@ -1724,10 +1648,8 @@ func (s *Server) Stop() {
|
|||
for lis := range listeners {
|
||||
lis.Close()
|
||||
}
|
||||
for _, cs := range conns {
|
||||
for st := range cs {
|
||||
st.Close()
|
||||
}
|
||||
for c := range st {
|
||||
c.Close()
|
||||
}
|
||||
if s.opts.numServerWorkers > 0 {
|
||||
s.stopServerWorkers()
|
||||
|
|
@ -1764,10 +1686,8 @@ func (s *Server) GracefulStop() {
|
|||
}
|
||||
s.lis = nil
|
||||
if !s.drain {
|
||||
for _, conns := range s.conns {
|
||||
for st := range conns {
|
||||
st.Drain()
|
||||
}
|
||||
for st := range s.conns {
|
||||
st.Drain()
|
||||
}
|
||||
s.drain = true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,22 +36,15 @@ type RPCStats interface {
|
|||
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.
|
||||
type Begin struct {
|
||||
// Client is true if this Begin is from client side.
|
||||
Client bool
|
||||
// BeginTime is the time when the RPC attempt begins.
|
||||
// BeginTime is the time when the RPC begins.
|
||||
BeginTime time.Time
|
||||
// FailFast indicates if this RPC is failfast.
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@ package status
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
spb "google.golang.org/genproto/googleapis/rpc/status"
|
||||
|
|
@ -74,16 +73,11 @@ func FromProto(s *spb.Status) *Status {
|
|||
return status.FromProto(s)
|
||||
}
|
||||
|
||||
// FromError returns a Status representation of err.
|
||||
//
|
||||
// - If err was produced by this package or implements the method `GRPCStatus()
|
||||
// *Status`, the appropriate Status is returned.
|
||||
//
|
||||
// - 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.
|
||||
// FromError returns a Status representing err if it was produced by this
|
||||
// package or has a method `GRPCStatus() *Status`.
|
||||
// If err is nil, a Status is returned with codes.OK and no message.
|
||||
// Otherwise, ok is false and a Status is returned with codes.Unknown and
|
||||
// the original error message.
|
||||
func FromError(err error) (s *Status, ok bool) {
|
||||
if err == nil {
|
||||
return nil, true
|
||||
|
|
@ -118,18 +112,18 @@ func Code(err error) codes.Code {
|
|||
return codes.Unknown
|
||||
}
|
||||
|
||||
// FromContextError converts a context error or wrapped context error into a
|
||||
// Status. It returns a Status with codes.OK if err is nil, or a Status with
|
||||
// codes.Unknown if err is non-nil and not a context error.
|
||||
// FromContextError converts a context error into a Status. It returns a
|
||||
// Status with codes.OK if err is nil, or a Status with codes.Unknown if err is
|
||||
// non-nil and not a context error.
|
||||
func FromContextError(err error) *Status {
|
||||
if err == nil {
|
||||
switch err {
|
||||
case nil:
|
||||
return nil
|
||||
}
|
||||
if errors.Is(err, context.DeadlineExceeded) {
|
||||
case context.DeadlineExceeded:
|
||||
return New(codes.DeadlineExceeded, err.Error())
|
||||
}
|
||||
if errors.Is(err, context.Canceled) {
|
||||
case context.Canceled:
|
||||
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.
|
||||
type StreamHandler func(srv interface{}, stream ServerStream) error
|
||||
|
||||
// StreamDesc represents a streaming RPC service's method specification. Used
|
||||
// on the server when registering services and on the client when initiating
|
||||
// new streams.
|
||||
// StreamDesc represents a streaming RPC service's method specification.
|
||||
type StreamDesc struct {
|
||||
// StreamName and Handler are only used when registering handlers on a
|
||||
// server.
|
||||
StreamName string // the name of the method excluding the service
|
||||
Handler StreamHandler // the handler called for the method
|
||||
StreamName string
|
||||
Handler StreamHandler
|
||||
|
||||
// ServerStreams and ClientStreams are used for registering handlers on a
|
||||
// server as well as defining RPC behavior when passed to NewClientStream
|
||||
// and ClientConn.NewStream. At least one must be true.
|
||||
ServerStreams bool // indicates the server can perform streaming sends
|
||||
ClientStreams bool // indicates the client can perform streaming sends
|
||||
// At least one of these is true.
|
||||
ServerStreams bool
|
||||
ClientStreams bool
|
||||
}
|
||||
|
||||
// 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
|
||||
// provided by the resolver.
|
||||
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 onCommit func()
|
||||
var newStream = func(ctx context.Context, done func()) (iresolver.ClientStream, error) {
|
||||
return newClientStreamWithParams(ctx, desc, cc, method, mc, onCommit, done, opts...)
|
||||
}
|
||||
|
||||
rpcInfo := iresolver.RPCInfo{Context: ctx, Method: method}
|
||||
rpcConfig, err := cc.safeConfigSelector.SelectConfig(rpcInfo)
|
||||
rpcConfig, err := cc.safeConfigSelector.SelectConfig(iresolver.RPCInfo{Context: ctx, Method: method})
|
||||
if err != nil {
|
||||
return nil, toRPCErr(err)
|
||||
return nil, status.Convert(err).Err()
|
||||
}
|
||||
|
||||
if rpcConfig != nil {
|
||||
if rpcConfig.Context != nil {
|
||||
ctx = rpcConfig.Context
|
||||
}
|
||||
mc = rpcConfig.MethodConfig
|
||||
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 {
|
||||
c.failFast = !*mc.WaitForReady
|
||||
}
|
||||
|
|
@ -250,7 +223,6 @@ func newClientStreamWithParams(ctx context.Context, desc *StreamDesc, cc *Client
|
|||
Host: cc.authority,
|
||||
Method: method,
|
||||
ContentSubtype: c.contentSubtype,
|
||||
DoneFunc: doneFunc,
|
||||
}
|
||||
|
||||
// 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 {
|
||||
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{
|
||||
callHdr: callHdr,
|
||||
|
|
@ -287,6 +286,7 @@ func newClientStreamWithParams(ctx context.Context, desc *StreamDesc, cc *Client
|
|||
cp: cp,
|
||||
comp: comp,
|
||||
cancel: cancel,
|
||||
beginTime: beginTime,
|
||||
firstAttempt: true,
|
||||
onCommit: onCommit,
|
||||
}
|
||||
|
|
@ -295,7 +295,9 @@ func newClientStreamWithParams(ctx context.Context, desc *StreamDesc, cc *Client
|
|||
}
|
||||
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)
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -343,43 +345,8 @@ func newClientStreamWithParams(ctx context.Context, desc *StreamDesc, cc *Client
|
|||
|
||||
// newAttemptLocked creates a new attempt with a transport.
|
||||
// If it succeeds, then it replaces clientStream's attempt with this new attempt.
|
||||
func (cs *clientStream) newAttemptLocked(isTransparent bool) (retErr error) {
|
||||
ctx := newContextWithRPCInfo(cs.ctx, cs.callInfo.failFast, cs.callInfo.codec, cs.cp, cs.comp)
|
||||
method := cs.callHdr.Method
|
||||
sh := cs.cc.dopts.copts.StatsHandler
|
||||
var beginTime time.Time
|
||||
if sh != nil {
|
||||
ctx = sh.TagRPC(ctx, &stats.RPCTagInfo{FullMethodName: method, FailFast: cs.callInfo.failFast})
|
||||
beginTime = time.Now()
|
||||
begin := &stats.Begin{
|
||||
Client: true,
|
||||
BeginTime: beginTime,
|
||||
FailFast: cs.callInfo.failFast,
|
||||
IsClientStream: cs.desc.ClientStreams,
|
||||
IsServerStream: cs.desc.ServerStreams,
|
||||
IsTransparentRetryAttempt: isTransparent,
|
||||
}
|
||||
sh.HandleRPC(ctx, begin)
|
||||
}
|
||||
|
||||
var trInfo *traceInfo
|
||||
if EnableTracing {
|
||||
trInfo = &traceInfo{
|
||||
tr: trace.New("grpc.Sent."+methodFamily(method), method),
|
||||
firstLine: firstLine{
|
||||
client: true,
|
||||
},
|
||||
}
|
||||
if deadline, ok := ctx.Deadline(); ok {
|
||||
trInfo.firstLine.deadline = time.Until(deadline)
|
||||
}
|
||||
trInfo.tr.LazyLog(&trInfo.firstLine, false)
|
||||
ctx = trace.NewContext(ctx, trInfo.tr)
|
||||
}
|
||||
|
||||
func (cs *clientStream) newAttemptLocked(sh stats.Handler, trInfo *traceInfo) (retErr error) {
|
||||
newAttempt := &csAttempt{
|
||||
ctx: ctx,
|
||||
beginTime: beginTime,
|
||||
cs: cs,
|
||||
dc: cs.cc.dopts.dc,
|
||||
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)
|
||||
}
|
||||
|
||||
ctx := cs.ctx
|
||||
if cs.cc.parsedTarget.Scheme == "xds" {
|
||||
// Add extra metadata (metadata that will be added by transport) to context
|
||||
// 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),
|
||||
))
|
||||
}
|
||||
|
|
@ -421,11 +389,14 @@ func (cs *clientStream) newAttemptLocked(isTransparent bool) (retErr error) {
|
|||
func (a *csAttempt) newStream() error {
|
||||
cs := a.cs
|
||||
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 {
|
||||
// Return without converting to an RPC error so retry code can
|
||||
// inspect.
|
||||
return err
|
||||
if _, ok := err.(transport.PerformedIOError); ok {
|
||||
// Return without converting to an RPC error so retry code can
|
||||
// inspect.
|
||||
return err
|
||||
}
|
||||
return toRPCErr(err)
|
||||
}
|
||||
cs.attempt.s = s
|
||||
cs.attempt.p = &parser{r: s}
|
||||
|
|
@ -446,7 +417,8 @@ type clientStream struct {
|
|||
|
||||
cancel context.CancelFunc // cancels all attempts
|
||||
|
||||
sentLast bool // sent an end stream
|
||||
sentLast bool // sent an end stream
|
||||
beginTime time.Time
|
||||
|
||||
methodConfig *MethodConfig
|
||||
|
||||
|
|
@ -486,7 +458,6 @@ type clientStream struct {
|
|||
// csAttempt implements a single transport stream attempt within a
|
||||
// clientStream.
|
||||
type csAttempt struct {
|
||||
ctx context.Context
|
||||
cs *clientStream
|
||||
t transport.ClientTransport
|
||||
s *transport.Stream
|
||||
|
|
@ -505,7 +476,6 @@ type csAttempt struct {
|
|||
trInfo *traceInfo
|
||||
|
||||
statsHandler stats.Handler
|
||||
beginTime time.Time
|
||||
}
|
||||
|
||||
func (cs *clientStream) commitAttemptLocked() {
|
||||
|
|
@ -523,57 +493,46 @@ func (cs *clientStream) commitAttempt() {
|
|||
}
|
||||
|
||||
// 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
|
||||
// retried, the bool indicates whether it is being retried transparently.
|
||||
func (cs *clientStream) shouldRetry(err error) (bool, error) {
|
||||
// the error that should be returned by the operation.
|
||||
func (cs *clientStream) shouldRetry(err error) error {
|
||||
unprocessed := false
|
||||
if cs.attempt.s == nil {
|
||||
// Error from NewClientStream.
|
||||
nse, ok := err.(*transport.NewStreamError)
|
||||
if !ok {
|
||||
// Unexpected, but assume no I/O was performed and the RPC is not
|
||||
// fatal, so retry indefinitely.
|
||||
return true, nil
|
||||
pioErr, ok := err.(transport.PerformedIOError)
|
||||
if ok {
|
||||
// Unwrap error.
|
||||
err = toRPCErr(pioErr.Err)
|
||||
} else {
|
||||
unprocessed = true
|
||||
}
|
||||
|
||||
// Unwrap and convert error.
|
||||
err = toRPCErr(nse.Err)
|
||||
|
||||
// Never retry DoNotRetry errors, which indicate the RPC should not be
|
||||
// retried due to max header list size violation, etc.
|
||||
if nse.DoNotRetry {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// In the event of a non-IO operation error from NewStream, we never
|
||||
// attempted to write anything to the wire, so we can retry
|
||||
// indefinitely.
|
||||
if !nse.DoNotTransparentRetry {
|
||||
return true, nil
|
||||
if !ok && !cs.callInfo.failFast {
|
||||
// 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 for non-fail-fast RPCs.
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if cs.finished || cs.committed {
|
||||
// RPC is finished or committed; cannot retry.
|
||||
return false, err
|
||||
return err
|
||||
}
|
||||
// Wait for the trailers.
|
||||
unprocessed := false
|
||||
if cs.attempt.s != nil {
|
||||
<-cs.attempt.s.Done()
|
||||
unprocessed = cs.attempt.s.Unprocessed()
|
||||
}
|
||||
if cs.firstAttempt && unprocessed {
|
||||
// First attempt, stream unprocessed: transparently retry.
|
||||
return true, nil
|
||||
return nil
|
||||
}
|
||||
if cs.cc.dopts.disableRetry {
|
||||
return false, err
|
||||
return err
|
||||
}
|
||||
|
||||
pushback := 0
|
||||
hasPushback := false
|
||||
if cs.attempt.s != nil {
|
||||
if !cs.attempt.s.TrailersOnly() {
|
||||
return false, err
|
||||
return err
|
||||
}
|
||||
|
||||
// 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 {
|
||||
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.
|
||||
return false, err
|
||||
return err
|
||||
}
|
||||
hasPushback = true
|
||||
} else if len(sps) > 1 {
|
||||
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.
|
||||
return false, err
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -603,16 +562,16 @@ func (cs *clientStream) shouldRetry(err error) (bool, error) {
|
|||
|
||||
rp := cs.methodConfig.RetryPolicy
|
||||
if rp == nil || !rp.RetryableStatusCodes[code] {
|
||||
return false, err
|
||||
return err
|
||||
}
|
||||
|
||||
// Note: the ordering here is important; we count this as a failure
|
||||
// only if the code matched a retryable code.
|
||||
if cs.retryThrottler.throttle() {
|
||||
return false, err
|
||||
return err
|
||||
}
|
||||
if cs.numRetries+1 >= rp.MaxAttempts {
|
||||
return false, err
|
||||
return err
|
||||
}
|
||||
|
||||
var dur time.Duration
|
||||
|
|
@ -635,24 +594,23 @@ func (cs *clientStream) shouldRetry(err error) (bool, error) {
|
|||
select {
|
||||
case <-t.C:
|
||||
cs.numRetries++
|
||||
return false, nil
|
||||
return nil
|
||||
case <-cs.ctx.Done():
|
||||
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.
|
||||
func (cs *clientStream) retryLocked(lastErr error) error {
|
||||
for {
|
||||
cs.attempt.finish(toRPCErr(lastErr))
|
||||
isTransparent, err := cs.shouldRetry(lastErr)
|
||||
if err != nil {
|
||||
cs.attempt.finish(lastErr)
|
||||
if err := cs.shouldRetry(lastErr); err != nil {
|
||||
cs.commitAttemptLocked()
|
||||
return err
|
||||
}
|
||||
cs.firstAttempt = false
|
||||
if err := cs.newAttemptLocked(isTransparent); err != nil {
|
||||
if err := cs.newAttemptLocked(nil, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
if lastErr = cs.replayBufferLocked(); lastErr == nil {
|
||||
|
|
@ -673,11 +631,7 @@ func (cs *clientStream) withRetry(op func(a *csAttempt) error, onSuccess func())
|
|||
for {
|
||||
if cs.committed {
|
||||
cs.mu.Unlock()
|
||||
// toRPCErr is used in case the error from the attempt comes from
|
||||
// NewClientStream, which intentionally doesn't return a status
|
||||
// error to allow for further inspection; all other errors should
|
||||
// already be status errors.
|
||||
return toRPCErr(op(cs.attempt))
|
||||
return op(cs.attempt)
|
||||
}
|
||||
a := cs.attempt
|
||||
cs.mu.Unlock()
|
||||
|
|
@ -942,7 +896,7 @@ func (a *csAttempt) sendMsg(m interface{}, hdr, payld, data []byte) error {
|
|||
return io.EOF
|
||||
}
|
||||
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() {
|
||||
a.t.IncrMsgSent()
|
||||
|
|
@ -990,7 +944,7 @@ func (a *csAttempt) recvMsg(m interface{}, payInfo *payloadInfo) (err error) {
|
|||
a.mu.Unlock()
|
||||
}
|
||||
if a.statsHandler != nil {
|
||||
a.statsHandler.HandleRPC(a.ctx, &stats.InPayload{
|
||||
a.statsHandler.HandleRPC(cs.ctx, &stats.InPayload{
|
||||
Client: true,
|
||||
RecvTime: time.Now(),
|
||||
Payload: m,
|
||||
|
|
@ -1052,12 +1006,12 @@ func (a *csAttempt) finish(err error) {
|
|||
if a.statsHandler != nil {
|
||||
end := &stats.End{
|
||||
Client: true,
|
||||
BeginTime: a.beginTime,
|
||||
BeginTime: a.cs.beginTime,
|
||||
EndTime: time.Now(),
|
||||
Trailer: tr,
|
||||
Error: err,
|
||||
}
|
||||
a.statsHandler.HandleRPC(a.ctx, end)
|
||||
a.statsHandler.HandleRPC(a.cs.ctx, end)
|
||||
}
|
||||
if a.trInfo != nil && a.trInfo.tr != nil {
|
||||
if err == nil {
|
||||
|
|
|
|||
|
|
@ -37,16 +37,16 @@ type Info struct {
|
|||
// TODO: More to be added.
|
||||
}
|
||||
|
||||
// ServerInHandle defines the function which runs before a new stream is
|
||||
// created on the server side. If it returns a non-nil error, the stream will
|
||||
// not be created and an error will be returned to the client. If the error
|
||||
// returned is a status error, that status code and message will be used,
|
||||
// otherwise PermissionDenied will be the code and err.Error() will be the
|
||||
// message.
|
||||
// ServerInHandle defines the function which runs before a new stream is created
|
||||
// on the server side. If it returns a non-nil error, the stream will not be
|
||||
// created and a RST_STREAM will be sent back to the client with REFUSED_STREAM.
|
||||
// The client will receive an RPC error "code = Unavailable, desc = stream
|
||||
// terminated by RST_STREAM with error code: REFUSED_STREAM".
|
||||
//
|
||||
// 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
|
||||
// usages, please use interceptors.
|
||||
// resources to accept the new stream (e.g. rate-limiting). And the content of
|
||||
// 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
|
||||
// per-RPC goroutine. Therefore, users should NOT have any
|
||||
|
|
|
|||
|
|
@ -19,4 +19,4 @@
|
|||
package grpc
|
||||
|
||||
// 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
|
||||
|
||||
PATH="${HOME}/go/bin:${GOROOT}/bin:${PATH}"
|
||||
go version
|
||||
PATH="${GOPATH}/bin:${GOROOT}/bin:${PATH}"
|
||||
|
||||
if [[ "$1" = "-install" ]]; then
|
||||
# Install the pinned versions as defined in module tools.
|
||||
pushd ./test/tools
|
||||
go install \
|
||||
golang.org/x/lint/golint \
|
||||
golang.org/x/tools/cmd/goimports \
|
||||
honnef.co/go/tools/cmd/staticcheck \
|
||||
github.com/client9/misspell/cmd/misspell
|
||||
popd
|
||||
# Check for module support
|
||||
if go help mod >& /dev/null; then
|
||||
# Install the pinned versions as defined in module tools.
|
||||
pushd ./test/tools
|
||||
go install \
|
||||
golang.org/x/lint/golint \
|
||||
golang.org/x/tools/cmd/goimports \
|
||||
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 [[ "${TRAVIS}" = "true" ]]; then
|
||||
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.
|
||||
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 .
|
||||
|
||||
# - 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)
|
||||
fi
|
||||
|
||||
# - gofmt, goimports, golint (with exceptions for generated code), go vet,
|
||||
# go mod tidy.
|
||||
# Perform these checks on each module inside gRPC.
|
||||
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
|
||||
# - Check that our modules are tidy.
|
||||
if go help mod >& /dev/null; then
|
||||
find . -name 'go.mod' | xargs -IXXX bash -c 'cd $(dirname XXX); go mod tidy'
|
||||
git status --porcelain 2>&1 | fail_on_output || \
|
||||
(git status; git --no-pager diff; exit 1)
|
||||
popd
|
||||
done
|
||||
fi
|
||||
|
||||
# - Collection of static analysis checks
|
||||
#
|
||||
|
|
@ -130,11 +141,8 @@ not grep -Fv '.CredsBundle
|
|||
.NewAddress
|
||||
.NewServiceConfig
|
||||
.Type is deprecated: use Attributes
|
||||
BuildVersion is deprecated
|
||||
balancer.ErrTransientFailure
|
||||
balancer.Picker
|
||||
extDesc.Filename is deprecated
|
||||
github.com/golang/protobuf/jsonpb is deprecated
|
||||
grpc.CallCustomCodec
|
||||
grpc.Code
|
||||
grpc.Compressor
|
||||
|
|
@ -156,7 +164,13 @@ grpc.WithServiceConfig
|
|||
grpc.WithTimeout
|
||||
http.CloseNotifier
|
||||
info.SecurityVersion
|
||||
resolver.Backend
|
||||
resolver.GRPCLB
|
||||
extDesc.Filename is deprecated
|
||||
BuildVersion is deprecated
|
||||
github.com/golang/protobuf/jsonpb is deprecated
|
||||
proto is deprecated
|
||||
xxx_messageInfo_
|
||||
proto.InternalMessageInfo is deprecated
|
||||
proto.EnumName is deprecated
|
||||
proto.ErrInternalBadWireType is deprecated
|
||||
|
|
@ -170,12 +184,7 @@ proto.RegisterExtension is deprecated
|
|||
proto.RegisteredExtension is deprecated
|
||||
proto.RegisteredExtensions is deprecated
|
||||
proto.RegisterMapType is deprecated
|
||||
proto.Unmarshaler is deprecated
|
||||
resolver.Backend
|
||||
resolver.GRPCLB
|
||||
Target is deprecated: Use the Target field in the BuildOptions instead.
|
||||
xxx_messageInfo_
|
||||
' "${SC_OUT}"
|
||||
proto.Unmarshaler is deprecated' "${SC_OUT}"
|
||||
|
||||
# - special golint on package comments.
|
||||
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
|
||||
## explicit; go 1.11
|
||||
google.golang.org/genproto/googleapis/rpc/status
|
||||
# google.golang.org/grpc v1.44.0
|
||||
## explicit; go 1.14
|
||||
# google.golang.org/grpc v1.36.1
|
||||
## explicit; go 1.11
|
||||
google.golang.org/grpc
|
||||
google.golang.org/grpc/attributes
|
||||
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/connectivity
|
||||
google.golang.org/grpc/credentials
|
||||
google.golang.org/grpc/credentials/insecure
|
||||
google.golang.org/grpc/encoding
|
||||
google.golang.org/grpc/encoding/proto
|
||||
google.golang.org/grpc/grpclog
|
||||
|
|
|
|||
Loading…
Reference in New Issue