gitlab.com/ignitionrobotics/web/ign-go@v1.0.0-rc4/net/server.go (about) 1 package net 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware" 8 grpc_recovery "github.com/grpc-ecosystem/go-grpc-middleware/recovery" 9 grpc_ctxtags "github.com/grpc-ecosystem/go-grpc-middleware/tags" 10 grpc_opentracing "github.com/grpc-ecosystem/go-grpc-middleware/tracing/opentracing" 11 grpc_validator "github.com/grpc-ecosystem/go-grpc-middleware/validator" 12 "google.golang.org/grpc" 13 "log" 14 "net" 15 "net/http" 16 "time" 17 ) 18 19 // Server is a web server to listen to incoming requests. It supports different types of transport mechanisms used through Ignition Robotics projects. 20 // It currently supports HTTP and gRPC servers. 21 type Server struct { 22 // httpServer holds a group of HTTP servers. All these servers will be listening to different ports. 23 httpServers []*http.Server 24 // grpcServers holds a group of gRPC servers. All these servers will be listening in the same port. 25 grpcServers []*grpc.Server 26 // listener holds a reference to a TCP listener for gRPC servers. 27 // All gRPC servers will use this listener to listen for incoming requests. 28 listener net.Listener 29 } 30 31 // ListenAndServe starts listening for incoming requests for the different HTTP and gRPC servers. 32 // Returns a channel that will receive an error from any of the current underlying transport mechanisms. 33 // Each underlying server will be launched in a different go routine. 34 func (s *Server) ListenAndServe() <-chan error { 35 errs := make(chan error, 1) 36 37 // Start HTTP servers 38 for _, srv := range s.httpServers { 39 go func(srv *http.Server, errs chan<- error) { 40 if err := srv.ListenAndServe(); err != nil { 41 errs <- err 42 close(errs) 43 } 44 }(srv, errs) 45 } 46 47 // Start gRPC servers 48 if s.grpcServers != nil && len(s.grpcServers) > 0 && s.listener == nil { 49 errs <- errors.New("a listener must be provided to the Server through an Option to support gRPC") 50 return errs 51 } 52 for _, srv := range s.grpcServers { 53 go func(srv *grpc.Server, listener net.Listener, errs chan<- error) { 54 if err := srv.Serve(listener); err != nil { 55 errs <- err 56 close(errs) 57 } 58 }(srv, s.listener, errs) 59 } 60 61 return errs 62 } 63 64 // Close gracefully closes all the underlying servers (HTTP & gRPC). 65 func (s *Server) Close() { 66 for _, srv := range s.httpServers { 67 ctx, cancel := context.WithTimeout(context.Background(), time.Minute) 68 if err := srv.Shutdown(ctx); err != nil { 69 log.Println("Failed to shutdown HTTP server:", err) 70 } 71 cancel() 72 } 73 for _, srv := range s.grpcServers { 74 srv.GracefulStop() 75 } 76 } 77 78 // Option contains logic that can be passed to a server in its initializer to modify it. 79 type Option func(*Server) *Server 80 81 // HTTP initializes a new HTTP server to serve endpoints defined in handler on a certain port. 82 func HTTP(handler http.Handler, port uint) Option { 83 return func(s *Server) *Server { 84 s.httpServers = append(s.httpServers, &http.Server{ 85 Handler: handler, 86 Addr: fmt.Sprintf(":%d", port), 87 }) 88 89 return s 90 } 91 } 92 93 // GRPC initializes and adds a new gRPC server to the Server. 94 // Multiple gRPC servers use the same ListenerTCP. ListenerTCP is required if this Option is passed. 95 // A set of gRPC options are passed as a function in order to inject custom middlewares to the gRPC server. 96 // DefaultServerOptionsGRPC contains a set of basic middlewares to use in any application, but we encourage you to create or extend 97 // your own opts function. 98 func GRPC(register func(s grpc.ServiceRegistrar), opts []grpc.ServerOption) Option { 99 return func(s *Server) *Server { 100 grpcServer := newGRPC(opts) 101 register(grpcServer) 102 s.grpcServers = append(s.grpcServers, grpcServer) 103 104 return s 105 } 106 } 107 108 // DefaultServerOptionsGRPC is a predefined set of gRPC server options that can be used by any Server when calling the GRPC function. 109 // We encourage you to create or extend your own opts function taking this one as a starting point. 110 func DefaultServerOptionsGRPC() []grpc.ServerOption { 111 return []grpc.ServerOption{ 112 grpc.StreamInterceptor( 113 grpc_middleware.ChainStreamServer( 114 grpc_ctxtags.StreamServerInterceptor(), 115 grpc_opentracing.StreamServerInterceptor(), 116 grpc_recovery.StreamServerInterceptor(), 117 grpc_validator.StreamServerInterceptor(), 118 ), 119 ), 120 grpc.UnaryInterceptor( 121 grpc_middleware.ChainUnaryServer( 122 grpc_ctxtags.UnaryServerInterceptor(), 123 grpc_opentracing.UnaryServerInterceptor(), 124 grpc_recovery.UnaryServerInterceptor(), 125 grpc_validator.UnaryServerInterceptor(), 126 ), 127 ), 128 } 129 } 130 131 // newGRPC initializes a new gRPC server and uses the options received from calling opts(). 132 func newGRPC(opts []grpc.ServerOption) *grpc.Server { 133 return grpc.NewServer( 134 opts..., 135 ) 136 } 137 138 // ListenerTCP initializes a new Listener for server. 139 // This Option is required for GRPC servers when registered in Server. 140 func ListenerTCP(port uint) Option { 141 return func(s *Server) *Server { 142 var err error 143 s.listener, err = net.Listen("tcp", fmt.Sprintf(":%d", port)) 144 if err != nil { 145 panic(fmt.Sprintf("failed to initialize listener: %s", err)) 146 } 147 return s 148 } 149 } 150 151 // NewServer initializes a new server. 152 func NewServer(opts ...Option) *Server { 153 var s Server 154 for _, o := range opts { 155 o(&s) 156 } 157 158 return &s 159 } 160 161 // ServiceRegistrator registers a gRPC service in a gRPC server. 162 type ServiceRegistrator interface { 163 // Register registers the current service into the given server. 164 Register(s grpc.ServiceRegistrar) 165 }