code.vegaprotocol.io/vega@v0.79.0/core/api/rest/server.go (about) 1 // Copyright (C) 2023 Gobalsky Labs Limited 2 // 3 // This program is free software: you can redistribute it and/or modify 4 // it under the terms of the GNU Affero General Public License as 5 // published by the Free Software Foundation, either version 3 of the 6 // License, or (at your option) any later version. 7 // 8 // This program is distributed in the hope that it will be useful, 9 // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 // GNU Affero General Public License for more details. 12 // 13 // You should have received a copy of the GNU Affero General Public License 14 // along with this program. If not, see <http://www.gnu.org/licenses/>. 15 16 package rest 17 18 import ( 19 "context" 20 "net" 21 "net/http" 22 "strconv" 23 24 "code.vegaprotocol.io/vega/core/api" 25 libhttp "code.vegaprotocol.io/vega/libs/http" 26 "code.vegaprotocol.io/vega/logging" 27 protoapi "code.vegaprotocol.io/vega/protos/vega/api/v1" 28 29 "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" 30 "github.com/rs/cors" 31 "go.elastic.co/apm/module/apmhttp" 32 "google.golang.org/grpc" 33 ) 34 35 const ( 36 namedLogger = "api.restproxy" 37 ) 38 39 // ProxyServer implement a rest server acting as a proxy to the grpc api. 40 type ProxyServer struct { 41 log *logging.Logger 42 cfg api.Config 43 srv *http.Server 44 } 45 46 // NewProxyServer returns a new instance of the rest proxy server. 47 func NewProxyServer(log *logging.Logger, config api.Config) *ProxyServer { 48 // setup logger 49 log = log.Named(namedLogger) 50 log.SetLevel(config.Level.Get()) 51 52 return &ProxyServer{ 53 log: log, 54 cfg: config, 55 srv: nil, 56 } 57 } 58 59 // ReloadConf update the internal configuration of the server. 60 func (s *ProxyServer) ReloadConf(cfg api.Config) { 61 s.log.Info("reloading configuration") 62 if s.log.GetLevel() != cfg.Level.Get() { 63 s.log.Info("updating log level", 64 logging.String("old", s.log.GetLevel().String()), 65 logging.String("new", cfg.Level.String()), 66 ) 67 s.log.SetLevel(cfg.Level.Get()) 68 } 69 70 // TODO(): not updating the actual server for now, may need to look at this later 71 // e.g restart the http server on another port or whatever 72 s.cfg = cfg 73 } 74 75 // Start starts the server. 76 func (s *ProxyServer) Start() { 77 logger := s.log 78 79 logger.Info("Starting REST<>GRPC based API", 80 logging.String("addr", s.cfg.REST.IP), 81 logging.Int("port", s.cfg.REST.Port)) 82 83 ctx := context.Background() 84 ctx, cancel := context.WithCancel(ctx) 85 defer cancel() 86 87 restAddr := net.JoinHostPort(s.cfg.REST.IP, strconv.Itoa(s.cfg.REST.Port)) 88 grpcAddr := net.JoinHostPort(s.cfg.IP, strconv.Itoa(s.cfg.Port)) 89 jsonPB := &JSONPb{ 90 EmitDefaults: true, 91 Indent: " ", // formatted json output 92 OrigName: false, 93 } 94 95 mux := runtime.NewServeMux( 96 runtime.WithMarshalerOption(runtime.MIMEWildcard, jsonPB), 97 ) 98 99 opts := []grpc.DialOption{grpc.WithInsecure()} 100 if err := protoapi.RegisterCoreServiceHandlerFromEndpoint(ctx, mux, grpcAddr, opts); err != nil { 101 logger.Panic("Failure registering trading handler for REST proxy endpoints", logging.Error(err)) 102 } 103 if err := protoapi.RegisterCoreStateServiceHandlerFromEndpoint(ctx, mux, grpcAddr, opts); err != nil { 104 logger.Panic("Failure registering trading handler for REST proxy endpoints", logging.Error(err)) 105 } 106 107 // CORS support 108 corsOptions := libhttp.CORSOptions(s.cfg.REST.CORS) 109 handler := cors.New(corsOptions).Handler(mux) 110 handler = healthCheckMiddleware(handler) 111 handler = RemoteAddrMiddleware(logger, handler) 112 // Gzip encoding support 113 handler = newGzipHandler(*logger, handler.(http.HandlerFunc)) 114 // Metric support 115 handler = MetricCollectionMiddleware(handler) 116 117 // APM 118 if s.cfg.REST.APMEnabled { 119 handler = apmhttp.Wrap(handler) 120 } 121 122 s.srv = &http.Server{ 123 Addr: restAddr, 124 Handler: handler, 125 } 126 127 // Start http server on port specified 128 err := s.srv.ListenAndServe() 129 if err != nil && err != http.ErrServerClosed { 130 logger.Panic("Failure serving REST proxy API", logging.Error(err)) 131 } 132 } 133 134 // Stop stops the server. 135 func (s *ProxyServer) Stop() { 136 if s.srv != nil { 137 s.log.Info("Stopping REST<>GRPC based API") 138 139 if err := s.srv.Shutdown(context.Background()); err != nil { 140 s.log.Error("Failed to stop REST<>GRPC based API cleanly", 141 logging.Error(err)) 142 } 143 } 144 } 145 146 func healthCheckMiddleware(f http.Handler) http.HandlerFunc { 147 return func(w http.ResponseWriter, r *http.Request) { 148 if r.URL.Path == "/health" { 149 w.Write([]byte("ok")) 150 w.WriteHeader(http.StatusOK) 151 return 152 } 153 f.ServeHTTP(w, r) 154 } 155 }