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  }