mirror of
https://github.com/absmach/magistrala.git
synced 2026-06-23 04:10:28 +00:00
5807cf1754
Signed-off-by: dusan <borovcanindusan1@gmail.com>
135 lines
3.7 KiB
Go
135 lines
3.7 KiB
Go
// Copyright (c) Abstract Machines
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
package server
|
|
|
|
import (
|
|
"context"
|
|
"crypto/tls"
|
|
"crypto/x509"
|
|
"fmt"
|
|
"log/slog"
|
|
"os"
|
|
"os/signal"
|
|
"syscall"
|
|
"time"
|
|
)
|
|
|
|
const StopWaitTime = 5 * time.Second
|
|
|
|
// Server is an interface that defines the methods to start and stop a server.
|
|
type Server interface {
|
|
// Start starts the server.
|
|
Start() error
|
|
// Stop stops the server.
|
|
Stop() error
|
|
}
|
|
|
|
// Config is a struct that contains the configuration for the server.
|
|
type Config struct {
|
|
Host string `env:"HOST" envDefault:"localhost"`
|
|
Port string `env:"PORT" envDefault:""`
|
|
CertFile string `env:"SERVER_CERT" envDefault:""`
|
|
KeyFile string `env:"SERVER_KEY" envDefault:""`
|
|
ServerCAFile string `env:"SERVER_CA_CERTS" envDefault:""`
|
|
ClientCAFile string `env:"CLIENT_CA_CERTS" envDefault:""`
|
|
ReadTimeout time.Duration `env:"SERVER_READ_TIMEOUT" envDefault:"15s"`
|
|
WriteTimeout time.Duration `env:"SERVER_WRITE_TIMEOUT" envDefault:"15s"`
|
|
ReadHeaderTimeout time.Duration `env:"SERVER_READ_HEADER_TIMEOUT" envDefault:"5s"`
|
|
IdleTimeout time.Duration `env:"SERVER_IDLE_TIMEOUT" envDefault:"60s"`
|
|
MaxHeaderBytes int `env:"SERVER_MAX_HEADER_BYTES" envDefault:"1048576"` // 1 << 20
|
|
}
|
|
|
|
type BaseServer struct {
|
|
Ctx context.Context
|
|
Cancel context.CancelFunc
|
|
Name string
|
|
Address string
|
|
Config Config
|
|
Logger *slog.Logger
|
|
Protocol string
|
|
}
|
|
|
|
func NewBaseServer(ctx context.Context, cancel context.CancelFunc, name string, config Config, logger *slog.Logger) BaseServer {
|
|
address := fmt.Sprintf("%s:%s", config.Host, config.Port)
|
|
|
|
return BaseServer{
|
|
Ctx: ctx,
|
|
Cancel: cancel,
|
|
Name: name,
|
|
Address: address,
|
|
Config: config,
|
|
Logger: logger,
|
|
}
|
|
}
|
|
|
|
func stopAllServer(servers ...Server) error {
|
|
var err error
|
|
for _, server := range servers {
|
|
err1 := server.Stop()
|
|
if err1 != nil {
|
|
if err == nil {
|
|
err = fmt.Errorf("%w", err1)
|
|
} else {
|
|
err = fmt.Errorf("%v ; %w", err, err1)
|
|
}
|
|
}
|
|
}
|
|
return err
|
|
}
|
|
|
|
// StopSignalHandler stops the server when a signal is received.
|
|
func StopSignalHandler(ctx context.Context, cancel context.CancelFunc, logger *slog.Logger, svcName string, servers ...Server) error {
|
|
var err error
|
|
c := make(chan os.Signal, 1)
|
|
signal.Notify(c, syscall.SIGINT, syscall.SIGABRT)
|
|
select {
|
|
case sig := <-c:
|
|
defer cancel()
|
|
err = stopAllServer(servers...)
|
|
if err != nil {
|
|
logger.Error(fmt.Sprintf("%s service error during shutdown: %v", svcName, err))
|
|
}
|
|
logger.Info(fmt.Sprintf("%s service shutdown by signal: %s", svcName, sig))
|
|
return err
|
|
case <-ctx.Done():
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func ReadFileOrData(input string) ([]byte, error) {
|
|
if _, err := os.Stat(input); err == nil {
|
|
return os.ReadFile(input)
|
|
}
|
|
return []byte(input), nil
|
|
}
|
|
|
|
func LoadX509KeyPair(certFile, keyFile string) (tls.Certificate, error) {
|
|
cert, err := ReadFileOrData(certFile)
|
|
if err != nil {
|
|
return tls.Certificate{}, fmt.Errorf("failed to read cert: %v", err)
|
|
}
|
|
|
|
key, err := ReadFileOrData(keyFile)
|
|
if err != nil {
|
|
return tls.Certificate{}, fmt.Errorf("failed to read key: %v", err)
|
|
}
|
|
|
|
return tls.X509KeyPair(cert, key)
|
|
}
|
|
|
|
func LoadRootCACerts(input string) (*x509.CertPool, error) {
|
|
pemData, err := ReadFileOrData(input)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to load root CA data: %w", err)
|
|
}
|
|
|
|
if len(pemData) > 0 {
|
|
capool := x509.NewCertPool()
|
|
if !capool.AppendCertsFromPEM(pemData) {
|
|
return nil, fmt.Errorf("failed to append root ca to tls.Config")
|
|
}
|
|
return capool, nil
|
|
}
|
|
return nil, nil
|
|
}
|