github.com/lastbackend/toolkit@v0.0.0-20241020043710-cafa37b95aad/pkg/server/grpc/grpc.go (about)

     1  /*
     2  Copyright [2014] - [2023] The Last.Backend authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package grpc
    18  
    19  import (
    20  	"context"
    21  	"crypto/tls"
    22  	"fmt"
    23  	"github.com/lastbackend/toolkit/pkg/runtime"
    24  	"github.com/lastbackend/toolkit/pkg/server"
    25  	"net"
    26  	"net/http"
    27  	"regexp"
    28  	"sync"
    29  
    30  	"github.com/improbable-eng/grpc-web/go/grpcweb"
    31  	"golang.org/x/net/netutil"
    32  	grpc "google.golang.org/grpc"
    33  	"google.golang.org/grpc/credentials"
    34  )
    35  
    36  const (
    37  	serviceName = "grpc"
    38  )
    39  
    40  type grpcServer struct {
    41  	runtime runtime.Runtime
    42  
    43  	sync.RWMutex
    44  
    45  	prefix  string
    46  	address string
    47  
    48  	opts Config
    49  
    50  	descriptor grpc.ServiceDesc
    51  
    52  	// fn for init user-defined service
    53  	// fn for server registration
    54  	provide interface{}
    55  	service interface{}
    56  
    57  	interceptors *Interceptors
    58  
    59  	grpc    *grpc.Server
    60  	options *server.GRPCServerOptions
    61  
    62  	isRunning bool
    63  
    64  	wait *sync.WaitGroup
    65  }
    66  
    67  // NewServer - init and return new grpc server instance
    68  func NewServer(runtime runtime.Runtime, name string, options *server.GRPCServerOptions) server.GRPCServer { //nolint
    69  
    70  	if name == "" {
    71  		name = serviceName
    72  	}
    73  
    74  	name = regexp.MustCompile(`[^_a-zA-Z0-9 ]+`).ReplaceAllString(name, "_")
    75  
    76  	srv := &grpcServer{
    77  		runtime:      runtime,
    78  		prefix:       name,
    79  		opts:         defaultOptions(),
    80  		wait:         &sync.WaitGroup{},
    81  		options:      options,
    82  		interceptors: newInterceptors(runtime.Log()),
    83  	}
    84  
    85  	if err := runtime.Config().Parse(&srv.opts, srv.prefix); err != nil {
    86  		return nil
    87  	}
    88  
    89  	return srv
    90  }
    91  
    92  func (g *grpcServer) Info() server.ServerInfo {
    93  	return server.ServerInfo{
    94  		Kind:      server.ServerKindGRPCServer,
    95  		Host:      g.opts.Host,
    96  		Port:      g.opts.Port,
    97  		TLSConfig: g.opts.TLSConfig,
    98  	}
    99  }
   100  
   101  // SetDescriptor - set generated grpc server descriptor
   102  func (g *grpcServer) SetDescriptor(descriptor grpc.ServiceDesc) {
   103  	g.descriptor = descriptor
   104  }
   105  
   106  // SetService - set user-defined handlers
   107  func (g *grpcServer) SetService(service interface{}) {
   108  	g.service = service
   109  	return
   110  }
   111  
   112  // GetService - set user-defined handlers
   113  func (g *grpcServer) GetService() interface{} {
   114  	return g.service
   115  }
   116  
   117  // RegisterService - set user-defined handlers
   118  func (g *grpcServer) RegisterService(service interface{}) {
   119  	g.service = service
   120  	return
   121  }
   122  
   123  // SetConstructor - set fx handlers definition
   124  func (g *grpcServer) SetConstructor(fn interface{}) {
   125  	g.provide = fn
   126  	return
   127  }
   128  
   129  func (s *grpcServer) GetInterceptors() []interface{} {
   130  	return s.interceptors.constructors
   131  }
   132  
   133  // SetInterceptor - set fx handlers definition
   134  func (g *grpcServer) SetInterceptor(interceptor any) {
   135  	g.interceptors.AddConstructor(interceptor)
   136  }
   137  
   138  // GetConstructor - set fx handlers definition
   139  func (g *grpcServer) GetConstructor() interface{} {
   140  	return g.provide
   141  }
   142  
   143  func (g *grpcServer) GetInterceptorsConstructor() interface{} {
   144  	return g.constructor
   145  }
   146  
   147  func (g *grpcServer) constructor(interceptors ...server.GRPCInterceptor) {
   148  	for _, interceptor := range interceptors {
   149  		g.interceptors.Add(interceptor)
   150  	}
   151  }
   152  
   153  // parseOptions - get options from config and convert them to grpc server options
   154  func (g *grpcServer) parseOptions(options *server.GRPCServerOptions) []grpc.ServerOption {
   155  
   156  	if options != nil {
   157  		if options.Host != "" {
   158  			g.opts.Host = options.Host
   159  		}
   160  
   161  		if options.Port > 0 {
   162  			g.opts.Port = options.Port
   163  		}
   164  
   165  		if options.TLSConfig != nil {
   166  			g.opts.TLSConfig = options.TLSConfig
   167  		}
   168  	}
   169  
   170  	gopts := []grpc.ServerOption{
   171  		grpc.MaxRecvMsgSize(g.opts.MaxRecvMsgSize),
   172  		grpc.MaxSendMsgSize(g.opts.MaxSendMsgSize),
   173  		grpc.UnknownServiceHandler(g.defaultHandler),
   174  	}
   175  
   176  	if g.opts.TLSConfig != nil {
   177  		gopts = append(gopts, grpc.Creds(credentials.NewTLS(g.opts.TLSConfig)))
   178  	}
   179  
   180  	if g.opts.GrpcOptions != nil && len(g.opts.GrpcOptions) > 0 {
   181  		gopts = append(gopts, g.opts.GrpcOptions...)
   182  	}
   183  
   184  	var interceptors = make([]grpc.UnaryServerInterceptor, 0)
   185  	for _, i := range g.interceptors.items {
   186  		interceptors = append(interceptors, i.Interceptor)
   187  	}
   188  
   189  	gopts = append(gopts, grpc.ChainUnaryInterceptor(interceptors...))
   190  
   191  	return gopts
   192  }
   193  
   194  func (g *grpcServer) Start(_ context.Context) error {
   195  
   196  	g.RLock()
   197  	if g.isRunning {
   198  		g.RUnlock()
   199  		return nil
   200  	}
   201  	g.RUnlock()
   202  
   203  	var (
   204  		listener net.Listener
   205  		err      error
   206  	)
   207  
   208  	address := fmt.Sprintf("%s:%d", g.opts.Host, g.opts.Port)
   209  	if transportConfig := g.opts.TLSConfig; transportConfig != nil {
   210  		listener, err = tls.Listen("tcp", address, transportConfig)
   211  	} else {
   212  		listener, err = net.Listen("tcp", address)
   213  	}
   214  	if err != nil {
   215  		return err
   216  	}
   217  
   218  	if g.opts.MaxConnSize > 0 {
   219  		listener = netutil.LimitListener(listener, g.opts.MaxConnSize)
   220  	}
   221  
   222  	g.runtime.Log().V(5).Infof("server [grpc] Listening on %s", listener.Addr().String())
   223  
   224  	g.Lock()
   225  	g.address = listener.Addr().String()
   226  	g.Unlock()
   227  
   228  	g.grpc = grpc.NewServer(g.parseOptions(g.options)...)
   229  	g.grpc.RegisterService(&g.descriptor, g.service)
   230  
   231  	if g.opts.GRPCWebPort > 0 {
   232  
   233  		grpcWebOptions := make([]grpcweb.Option, 0)
   234  		if g.opts.GrpcWebOptions != nil {
   235  			grpcWebOptions = g.opts.GrpcWebOptions
   236  		}
   237  
   238  		wrappedGrpc := grpcweb.WrapServer(g.grpc, grpcWebOptions...)
   239  		webGRPCServer := &http.Server{
   240  			Addr:      fmt.Sprintf("%s:%d", g.opts.GRPCWebHost, g.opts.GRPCWebPort),
   241  			TLSConfig: g.opts.TLSConfig,
   242  			Handler:   http.Handler(wrappedGrpc),
   243  		}
   244  
   245  		go func() {
   246  			g.wait.Add(1)
   247  			g.runtime.Log().V(5).Infof("server [gRPC-Web] [%s:%d] started", g.opts.GRPCWebHost, g.opts.GRPCWebPort)
   248  			if err = webGRPCServer.ListenAndServe(); err != nil {
   249  				g.runtime.Log().Errorf("server [grpc] [%s:%d]  start error: %v", g.opts.GRPCWebHost, g.opts.GRPCWebPort, err)
   250  			}
   251  			g.runtime.Log().V(5).Infof("server [gRPC-Web] [%s:%d] stopped", g.opts.GRPCWebHost, g.opts.GRPCWebPort)
   252  			g.wait.Done()
   253  		}()
   254  
   255  	}
   256  
   257  	go func() {
   258  		g.wait.Add(1)
   259  		g.runtime.Log().V(5).Infof("server [grpc] [%s:%d] started", g.opts.Host, g.opts.Port)
   260  		if err := g.grpc.Serve(listener); err != nil {
   261  			g.runtime.Log().Errorf("server [grpc] start error: %v", err)
   262  		}
   263  		g.wait.Done()
   264  		g.runtime.Log().V(5).Infof("server [grpc] [%s:%d] stopped", g.opts.Host, g.opts.Port)
   265  	}()
   266  
   267  	g.Lock()
   268  	g.isRunning = true
   269  	g.Unlock()
   270  
   271  	return nil
   272  }
   273  
   274  func (g *grpcServer) Stop() error {
   275  
   276  	g.runtime.Log().V(5).Infof("server [grpc] [%s:%d] stop call start", g.opts.Host, g.opts.Port)
   277  	g.grpc.GracefulStop()
   278  	g.wait.Wait()
   279  	g.runtime.Log().V(5).Infof("server [grpc] [%s:%d] stop call end", g.opts.Host, g.opts.Port)
   280  
   281  	return nil
   282  }
   283  
   284  // TODO: need implement defaultHandler method
   285  func (g *grpcServer) defaultHandler(_ interface{}, _ grpc.ServerStream) error {
   286  	return nil
   287  }