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 }