istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pilot/pkg/grpc/grpc.go (about) 1 // Copyright Istio Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package grpc 16 17 import ( 18 "io" 19 "math" 20 "strings" 21 22 middleware "github.com/grpc-ecosystem/go-grpc-middleware" 23 "google.golang.org/grpc" 24 "google.golang.org/grpc/codes" 25 "google.golang.org/grpc/credentials/insecure" 26 "google.golang.org/grpc/keepalive" 27 "google.golang.org/grpc/status" 28 29 "istio.io/istio/pilot/pkg/features" 30 istiokeepalive "istio.io/istio/pkg/keepalive" 31 "istio.io/istio/pkg/util/sets" 32 ) 33 34 func ServerOptions(options *istiokeepalive.Options, interceptors ...grpc.UnaryServerInterceptor) []grpc.ServerOption { 35 maxStreams := features.MaxConcurrentStreams 36 maxRecvMsgSize := features.MaxRecvMsgSize 37 38 grpcOptions := []grpc.ServerOption{ 39 grpc.UnaryInterceptor(middleware.ChainUnaryServer(interceptors...)), 40 grpc.MaxConcurrentStreams(uint32(maxStreams)), 41 grpc.MaxRecvMsgSize(maxRecvMsgSize), 42 // Ensure we allow clients sufficient ability to send keep alives. If this is higher than client 43 // keep alive setting, it will prematurely get a GOAWAY sent. 44 grpc.KeepaliveEnforcementPolicy(keepalive.EnforcementPolicy{ 45 MinTime: options.Time / 2, 46 }), 47 grpc.KeepaliveParams(keepalive.ServerParameters{ 48 Time: options.Time, 49 Timeout: options.Timeout, 50 MaxConnectionAge: options.MaxServerConnectionAge, 51 MaxConnectionAgeGrace: options.MaxServerConnectionAgeGrace, 52 }), 53 } 54 55 return grpcOptions 56 } 57 58 const ( 59 defaultClientMaxReceiveMessageSize = math.MaxInt32 60 defaultInitialConnWindowSize = 1024 * 1024 // default gRPC InitialWindowSize 61 defaultInitialWindowSize = 1024 * 1024 // default gRPC ConnWindowSize 62 ) 63 64 // ClientOptions returns consistent grpc dial options with custom dial options 65 func ClientOptions(options *istiokeepalive.Options, tlsOpts *TLSOptions) ([]grpc.DialOption, error) { 66 if options == nil { 67 options = istiokeepalive.DefaultOption() 68 } 69 keepaliveOption := grpc.WithKeepaliveParams(keepalive.ClientParameters{ 70 Time: options.Time, 71 Timeout: options.Timeout, 72 }) 73 74 initialWindowSizeOption := grpc.WithInitialWindowSize(int32(defaultInitialWindowSize)) 75 initialConnWindowSizeOption := grpc.WithInitialConnWindowSize(int32(defaultInitialConnWindowSize)) 76 msgSizeOption := grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(defaultClientMaxReceiveMessageSize)) 77 var tlsDialOpts grpc.DialOption 78 var err error 79 if tlsOpts != nil { 80 tlsDialOpts, err = getTLSDialOption(tlsOpts) 81 if err != nil { 82 return nil, err 83 } 84 } else { 85 tlsDialOpts = grpc.WithTransportCredentials(insecure.NewCredentials()) 86 } 87 return []grpc.DialOption{keepaliveOption, initialWindowSizeOption, initialConnWindowSizeOption, msgSizeOption, tlsDialOpts}, nil 88 } 89 90 var expectedGrpcFailureMessages = sets.New( 91 "client disconnected", 92 "error reading from server: EOF", 93 "transport is closing", 94 ) 95 96 func containsExpectedMessage(msg string) bool { 97 for m := range expectedGrpcFailureMessages { 98 if strings.Contains(msg, m) { 99 return true 100 } 101 } 102 return false 103 } 104 105 // IsExpectedGRPCError checks a gRPC error code and determines whether it is an expected error when 106 // things are operating normally. This is basically capturing when the client disconnects. 107 func IsExpectedGRPCError(err error) bool { 108 if err == io.EOF { 109 return true 110 } 111 112 if s, ok := status.FromError(err); ok { 113 if s.Code() == codes.Canceled || s.Code() == codes.DeadlineExceeded { 114 return true 115 } 116 if s.Code() == codes.Unavailable && containsExpectedMessage(s.Message()) { 117 return true 118 } 119 } 120 // If this is not a gRPCStatus we should just error message. 121 if strings.Contains(err.Error(), "stream terminated by RST_STREAM with error code: NO_ERROR") { 122 return true 123 } 124 if strings.Contains(err.Error(), "received prior goaway: code: NO_ERROR") { 125 return true 126 } 127 128 return false 129 }