github.com/status-im/status-go@v1.1.0/server/server.go (about) 1 package server 2 3 import ( 4 "context" 5 "crypto/tls" 6 "fmt" 7 "net" 8 "net/http" 9 "net/url" 10 11 "go.uber.org/zap" 12 ) 13 14 type Server struct { 15 isRunning bool 16 server *http.Server 17 logger *zap.Logger 18 cert *tls.Certificate 19 hostname string 20 handlers HandlerPatternMap 21 22 portManger 23 *timeoutManager 24 } 25 26 func NewServer(cert *tls.Certificate, hostname string, afterPortChanged func(int), logger *zap.Logger) Server { 27 return Server{ 28 logger: logger, 29 cert: cert, 30 hostname: hostname, 31 portManger: newPortManager(logger.Named("Server"), afterPortChanged), 32 timeoutManager: newTimeoutManager(), 33 } 34 } 35 36 func (s *Server) getHost() string { 37 return fmt.Sprintf("%s:%d", s.hostname, s.GetPort()) 38 } 39 40 func (s *Server) GetHostname() string { 41 return s.hostname 42 } 43 44 func (s *Server) GetCert() *tls.Certificate { 45 return s.cert 46 } 47 48 func (s *Server) GetLogger() *zap.Logger { 49 return s.logger 50 } 51 52 func (s *Server) mustGetHost() string { 53 return fmt.Sprintf("%s:%d", s.hostname, s.MustGetPort()) 54 } 55 56 func (s *Server) listenAndServe() { 57 cfg := &tls.Config{Certificates: []tls.Certificate{*s.cert}, ServerName: s.hostname, MinVersion: tls.VersionTLS12} 58 59 // in case of restart, we should use the same port as the first start in order not to break existing links 60 listener, err := tls.Listen("tcp", s.getHost(), cfg) 61 if err != nil { 62 s.logger.Error("failed to start server, retrying", zap.Error(err)) 63 s.ResetPort() 64 err = s.Start() 65 if err != nil { 66 s.logger.Error("server start failed, giving up", zap.Error(err)) 67 } 68 return 69 } 70 71 err = s.SetPort(listener.Addr().(*net.TCPAddr).Port) 72 if err != nil { 73 s.logger.Error("failed to set Server.port", zap.Error(err)) 74 return 75 } 76 77 s.isRunning = true 78 79 s.StartTimeout(func() { 80 err := s.Stop() 81 if err != nil { 82 s.logger.Error("server termination fail", zap.Error(err)) 83 } 84 }) 85 86 err = s.server.Serve(listener) 87 if err != http.ErrServerClosed { 88 s.logger.Error("server failed unexpectedly, restarting", zap.Error(err)) 89 err = s.Start() 90 if err != nil { 91 s.logger.Error("server start failed, giving up", zap.Error(err)) 92 } 93 return 94 } 95 s.isRunning = false 96 } 97 98 func (s *Server) resetServer() { 99 s.StopTimeout() 100 s.server = new(http.Server) 101 s.ResetPort() 102 } 103 104 func (s *Server) applyHandlers() { 105 if s.server == nil { 106 s.server = new(http.Server) 107 } 108 mux := http.NewServeMux() 109 110 for p, h := range s.handlers { 111 mux.HandleFunc(p, h) 112 } 113 s.server.Handler = mux 114 } 115 116 func (s *Server) Start() error { 117 // Once Shutdown has been called on a server, it may not be reused; 118 s.resetServer() 119 s.applyHandlers() 120 go s.listenAndServe() 121 return nil 122 } 123 124 func (s *Server) Stop() error { 125 s.StopTimeout() 126 if s.server != nil { 127 return s.server.Shutdown(context.Background()) 128 } 129 130 return nil 131 } 132 133 func (s *Server) IsRunning() bool { 134 return s.isRunning 135 } 136 137 func (s *Server) ToForeground() { 138 if !s.isRunning && (s.server != nil) { 139 err := s.Start() 140 if err != nil { 141 s.logger.Error("server start failed during foreground transition", zap.Error(err)) 142 } 143 } 144 } 145 146 func (s *Server) ToBackground() { 147 if s.isRunning { 148 err := s.Stop() 149 if err != nil { 150 s.logger.Error("server stop failed during background transition", zap.Error(err)) 151 } 152 } 153 } 154 155 func (s *Server) SetHandlers(handlers HandlerPatternMap) { 156 s.handlers = handlers 157 } 158 159 func (s *Server) AddHandlers(handlers HandlerPatternMap) { 160 if s.handlers == nil { 161 s.handlers = make(HandlerPatternMap) 162 } 163 164 for name := range handlers { 165 s.handlers[name] = handlers[name] 166 } 167 } 168 169 func (s *Server) MakeBaseURL() *url.URL { 170 return &url.URL{ 171 Scheme: "https", 172 Host: s.mustGetHost(), 173 } 174 }