github.com/gravitational/teleport/api@v0.0.0-20240507183017-3110591cbafc/utils/grpc/interceptors/errors.go (about) 1 // Copyright 2023 Gravitational, Inc 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 interceptors 16 17 import ( 18 "context" 19 "errors" 20 "io" 21 22 "github.com/gravitational/trace" 23 "github.com/gravitational/trace/trail" 24 "google.golang.org/grpc" 25 ) 26 27 // grpcServerStreamWrapper wraps around the embedded grpc.ServerStream 28 // and intercepts the RecvMsg and SendMsg method calls converting errors 29 // to the appropriate gRPC status error. 30 type grpcServerStreamWrapper struct { 31 grpc.ServerStream 32 } 33 34 // SendMsg wraps around ServerStream.SendMsg and adds metrics reporting 35 func (s *grpcServerStreamWrapper) SendMsg(m interface{}) error { 36 return trace.Unwrap(trail.FromGRPC(s.ServerStream.SendMsg(m))) 37 } 38 39 // RecvMsg wraps around ServerStream.RecvMsg and adds metrics reporting 40 func (s *grpcServerStreamWrapper) RecvMsg(m interface{}) error { 41 return trace.Unwrap(trail.FromGRPC(s.ServerStream.RecvMsg(m))) 42 } 43 44 // grpcClientStreamWrapper wraps around the embedded grpc.ClientStream 45 // and intercepts the RecvMsg and SendMsg method calls converting errors 46 // to the appropriate gRPC status error. 47 type grpcClientStreamWrapper struct { 48 grpc.ClientStream 49 } 50 51 // SendMsg wraps around ClientStream.SendMsg 52 func (s *grpcClientStreamWrapper) SendMsg(m interface{}) error { 53 return wrapStreamErr(s.ClientStream.SendMsg(m)) 54 } 55 56 // RecvMsg wraps around ClientStream.RecvMsg 57 func (s *grpcClientStreamWrapper) RecvMsg(m interface{}) error { 58 return wrapStreamErr(s.ClientStream.RecvMsg(m)) 59 } 60 61 func wrapStreamErr(err error) error { 62 switch { 63 case err == nil: 64 return nil 65 case errors.Is(err, io.EOF): 66 // Do not wrap io.EOF errors, they are often used as stop guards for streams. 67 return err 68 default: 69 return &RemoteError{Err: trace.Unwrap(trail.FromGRPC(err))} 70 } 71 } 72 73 // GRPCServerUnaryErrorInterceptor is a gRPC unary server interceptor that 74 // handles converting errors to the appropriate gRPC status error. 75 func GRPCServerUnaryErrorInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { 76 resp, err := handler(ctx, req) 77 return resp, trace.Unwrap(trail.ToGRPC(err)) 78 } 79 80 // RemoteError annotates server-side errors translated into trace by 81 // [GRPCClientUnaryErrorInterceptor] or [GRPCClientStreamErrorInterceptor]. 82 type RemoteError struct { 83 // Err is the underlying error. 84 Err error 85 } 86 87 func (e *RemoteError) Error() string { 88 if e.Err == nil { 89 return "" 90 } 91 return e.Err.Error() 92 } 93 94 func (e *RemoteError) Unwrap() error { 95 return e.Err 96 } 97 98 // GRPCClientUnaryErrorInterceptor is a gRPC unary client interceptor that 99 // handles converting errors to the appropriate grpc status error. 100 func GRPCClientUnaryErrorInterceptor(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error { 101 if err := invoker(ctx, method, req, reply, cc, opts...); err != nil { 102 return &RemoteError{Err: trace.Unwrap(trail.FromGRPC(err))} 103 } 104 return nil 105 } 106 107 // GRPCServerStreamErrorInterceptor is a gRPC server stream interceptor that 108 // handles converting errors to the appropriate gRPC status error. 109 func GRPCServerStreamErrorInterceptor(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error { 110 serverWrapper := &grpcServerStreamWrapper{ss} 111 return trace.Unwrap(trail.ToGRPC(handler(srv, serverWrapper))) 112 } 113 114 // GRPCClientStreamErrorInterceptor is gRPC client stream interceptor that 115 // handles converting errors to the appropriate gRPC status error. 116 func GRPCClientStreamErrorInterceptor(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) { 117 s, err := streamer(ctx, desc, cc, method, opts...) 118 if err != nil { 119 return nil, &RemoteError{Err: trace.Unwrap(trail.ToGRPC(err))} 120 } 121 return &grpcClientStreamWrapper{s}, nil 122 }