google.golang.org/grpc@v1.72.2/xds/internal/clients/grpctransport/grpc_transport.go (about) 1 /* 2 * 3 * Copyright 2025 gRPC authors. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 */ 18 19 // Package grpctransport provides an implementation of the 20 // clients.TransportBuilder interface using gRPC. 21 package grpctransport 22 23 import ( 24 "context" 25 "fmt" 26 "time" 27 28 "google.golang.org/grpc" 29 "google.golang.org/grpc/credentials" 30 "google.golang.org/grpc/keepalive" 31 "google.golang.org/grpc/xds/internal/clients" 32 ) 33 34 // ServerIdentifierExtension holds settings for connecting to a gRPC server, 35 // such as an xDS management or an LRS server. 36 type ServerIdentifierExtension struct { 37 // Credentials will be used for all gRPC transports. If it is unset, 38 // transport creation will fail. 39 Credentials credentials.Bundle 40 } 41 42 // Builder creates gRPC-based Transports. It must be paired with ServerIdentifiers 43 // that contain an Extension field of type ServerIdentifierExtension. 44 type Builder struct{} 45 46 // Build returns a gRPC-based clients.Transport. 47 // 48 // The Extension field of the ServerIdentifier must be a ServerIdentifierExtension. 49 func (b *Builder) Build(si clients.ServerIdentifier) (clients.Transport, error) { 50 if si.ServerURI == "" { 51 return nil, fmt.Errorf("grpctransport: ServerURI is not set in ServerIdentifier") 52 } 53 if si.Extensions == nil { 54 return nil, fmt.Errorf("grpctransport: Extensions is not set in ServerIdentifier") 55 } 56 sce, ok := si.Extensions.(ServerIdentifierExtension) 57 if !ok { 58 return nil, fmt.Errorf("grpctransport: Extensions field is %T, but must be %T in ServerIdentifier", si.Extensions, ServerIdentifierExtension{}) 59 } 60 if sce.Credentials == nil { 61 return nil, fmt.Errorf("grptransport: Credentials field is not set in ServerIdentifierExtension") 62 } 63 64 // TODO: Incorporate reference count map for existing transports and 65 // deduplicate transports based on the provided ServerIdentifier so that 66 // transport channel to same server can be shared between xDS and LRS 67 // client. 68 69 // Create a new gRPC client/channel for the server with the provided 70 // credentials, server URI, and a byte codec to send and receive messages. 71 // Also set a static keepalive configuration that is common across gRPC 72 // language implementations. 73 kpCfg := grpc.WithKeepaliveParams(keepalive.ClientParameters{ 74 Time: 5 * time.Minute, 75 Timeout: 20 * time.Second, 76 }) 77 cc, err := grpc.NewClient(si.ServerURI, kpCfg, grpc.WithCredentialsBundle(sce.Credentials), grpc.WithDefaultCallOptions(grpc.ForceCodec(&byteCodec{}))) 78 if err != nil { 79 return nil, fmt.Errorf("grpctransport: failed to create transport to server %q: %v", si.ServerURI, err) 80 } 81 82 return &grpcTransport{cc: cc}, nil 83 } 84 85 type grpcTransport struct { 86 cc *grpc.ClientConn 87 } 88 89 // NewStream creates a new gRPC stream to the server for the specified method. 90 func (g *grpcTransport) NewStream(ctx context.Context, method string) (clients.Stream, error) { 91 s, err := g.cc.NewStream(ctx, &grpc.StreamDesc{ClientStreams: true, ServerStreams: true}, method) 92 if err != nil { 93 return nil, err 94 } 95 return &stream{stream: s}, nil 96 } 97 98 // Close closes the gRPC channel to the server. 99 func (g *grpcTransport) Close() error { 100 return g.cc.Close() 101 } 102 103 type stream struct { 104 stream grpc.ClientStream 105 } 106 107 // Send sends a message to the server. 108 func (s *stream) Send(msg []byte) error { 109 return s.stream.SendMsg(msg) 110 } 111 112 // Recv receives a message from the server. 113 func (s *stream) Recv() ([]byte, error) { 114 var typedRes []byte 115 116 if err := s.stream.RecvMsg(&typedRes); err != nil { 117 return nil, err 118 } 119 return typedRes, nil 120 } 121 122 type byteCodec struct{} 123 124 func (c *byteCodec) Marshal(v any) ([]byte, error) { 125 if b, ok := v.([]byte); ok { 126 return b, nil 127 } 128 return nil, fmt.Errorf("grpctransport: message is %T, but must be a []byte", v) 129 } 130 131 func (c *byteCodec) Unmarshal(data []byte, v any) error { 132 if b, ok := v.(*[]byte); ok { 133 *b = data 134 return nil 135 } 136 return fmt.Errorf("grpctransport: target is %T, but must be *[]byte", v) 137 } 138 139 func (c *byteCodec) Name() string { 140 return "grpctransport.byteCodec" 141 }