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  }