2022-02-23 14:34:16 +00:00
|
|
|
package server
|
2022-02-10 17:19:34 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"crypto/tls"
|
|
|
|
"fmt"
|
|
|
|
"net"
|
|
|
|
"net/http"
|
2022-06-15 14:49:31 +00:00
|
|
|
"net/url"
|
2022-02-10 17:19:34 +00:00
|
|
|
|
|
|
|
"go.uber.org/zap"
|
|
|
|
|
2022-05-09 13:07:57 +00:00
|
|
|
"github.com/status-im/status-go/logutils"
|
2022-02-10 17:19:34 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type Server struct {
|
2022-06-15 14:49:31 +00:00
|
|
|
run bool
|
|
|
|
server *http.Server
|
|
|
|
logger *zap.Logger
|
|
|
|
cert *tls.Certificate
|
|
|
|
hostname string
|
|
|
|
port int
|
|
|
|
handlers HandlerPatternMap
|
2022-02-10 17:19:34 +00:00
|
|
|
}
|
|
|
|
|
2022-06-15 14:49:31 +00:00
|
|
|
func NewServer(cert *tls.Certificate, hostname string) Server {
|
|
|
|
return Server{logger: logutils.ZapLogger(), cert: cert, hostname: hostname}
|
|
|
|
}
|
2022-02-10 17:19:34 +00:00
|
|
|
|
2022-06-15 14:49:31 +00:00
|
|
|
func (s *Server) getHost() string {
|
|
|
|
// TODO consider returning an error if s.getPort returns `0`, as this means that the listener is not ready
|
|
|
|
return fmt.Sprintf("%s:%d", s.hostname, s.port)
|
2022-02-10 17:19:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Server) listenAndServe() {
|
2022-06-15 14:49:31 +00:00
|
|
|
cfg := &tls.Config{Certificates: []tls.Certificate{*s.cert}, ServerName: s.hostname, MinVersion: tls.VersionTLS12}
|
2022-02-10 17:19:34 +00:00
|
|
|
|
|
|
|
// in case of restart, we should use the same port as the first start in order not to break existing links
|
2022-06-15 14:49:31 +00:00
|
|
|
listener, err := tls.Listen("tcp", s.getHost(), cfg)
|
2022-02-10 17:19:34 +00:00
|
|
|
if err != nil {
|
|
|
|
s.logger.Error("failed to start server, retrying", zap.Error(err))
|
2022-06-15 14:49:31 +00:00
|
|
|
s.port = 0
|
2022-02-10 17:19:34 +00:00
|
|
|
err = s.Start()
|
|
|
|
if err != nil {
|
|
|
|
s.logger.Error("server start failed, giving up", zap.Error(err))
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-06-15 14:49:31 +00:00
|
|
|
s.port = listener.Addr().(*net.TCPAddr).Port
|
2022-02-10 17:19:34 +00:00
|
|
|
s.run = true
|
2022-06-15 14:49:31 +00:00
|
|
|
|
2022-02-10 17:19:34 +00:00
|
|
|
err = s.server.Serve(listener)
|
|
|
|
if err != http.ErrServerClosed {
|
|
|
|
s.logger.Error("server failed unexpectedly, restarting", zap.Error(err))
|
|
|
|
err = s.Start()
|
|
|
|
if err != nil {
|
|
|
|
s.logger.Error("server start failed, giving up", zap.Error(err))
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
s.run = false
|
|
|
|
}
|
|
|
|
|
2022-06-15 14:49:31 +00:00
|
|
|
func (s *Server) resetServer() {
|
|
|
|
s.server = new(http.Server)
|
|
|
|
}
|
2022-05-09 13:07:57 +00:00
|
|
|
|
2022-06-15 14:49:31 +00:00
|
|
|
func (s *Server) applyHandlers() {
|
|
|
|
if s.server == nil {
|
|
|
|
s.server = new(http.Server)
|
|
|
|
}
|
|
|
|
mux := http.NewServeMux()
|
2022-02-10 17:19:34 +00:00
|
|
|
|
2022-06-15 14:49:31 +00:00
|
|
|
for p, h := range s.handlers {
|
|
|
|
mux.HandleFunc(p, h)
|
|
|
|
}
|
|
|
|
s.server.Handler = mux
|
|
|
|
}
|
2022-02-10 17:19:34 +00:00
|
|
|
|
2022-06-15 14:49:31 +00:00
|
|
|
func (s *Server) Start() error {
|
|
|
|
// Once Shutdown has been called on a server, it may not be reused;
|
|
|
|
s.resetServer()
|
|
|
|
s.applyHandlers()
|
|
|
|
go s.listenAndServe()
|
2022-02-10 17:19:34 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Server) Stop() error {
|
|
|
|
if s.server != nil {
|
|
|
|
return s.server.Shutdown(context.Background())
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Server) ToForeground() {
|
|
|
|
if !s.run && (s.server != nil) {
|
|
|
|
err := s.Start()
|
|
|
|
if err != nil {
|
|
|
|
s.logger.Error("server start failed during foreground transition", zap.Error(err))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Server) ToBackground() {
|
|
|
|
if s.run {
|
|
|
|
err := s.Stop()
|
|
|
|
if err != nil {
|
|
|
|
s.logger.Error("server stop failed during background transition", zap.Error(err))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-06-15 14:49:31 +00:00
|
|
|
|
|
|
|
func (s *Server) SetHandlers(handlers HandlerPatternMap) {
|
|
|
|
s.handlers = handlers
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Server) MakeBaseURL() *url.URL {
|
|
|
|
return &url.URL{
|
|
|
|
Scheme: "https",
|
|
|
|
Host: s.getHost(),
|
|
|
|
}
|
|
|
|
}
|