grpc-demo/cmd/server/main.go

149 lines
3.6 KiB
Go

package main
import (
"context"
"crypto/tls"
"crypto/x509"
"errors"
"fmt"
"log"
"net"
"os"
"os/signal"
"syscall"
"gitea.dwysokinski.me/Kichiyaki/grpc-demo/cmd/internal"
"gitea.dwysokinski.me/Kichiyaki/grpc-demo/internal/greeter"
"gitea.dwysokinski.me/Kichiyaki/grpc-demo/proto"
grpc_zap "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap"
grpc_recovery "github.com/grpc-ecosystem/go-grpc-middleware/recovery"
grpc_ctxtags "github.com/grpc-ecosystem/go-grpc-middleware/tags"
"go.uber.org/zap"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/credentials"
_ "google.golang.org/grpc/encoding/gzip"
"google.golang.org/grpc/status"
)
const defaultAddress = ":50001"
func main() {
logger, err := internal.NewLogger()
if err != nil {
log.Fatalln(err)
}
defer func() {
_ = logger.Sync()
}()
srv, err := newServer(defaultAddress, logger)
if err != nil {
logger.Fatal("couldn't initialize server", zap.Error(err))
}
go func(srv *server, logger *zap.Logger) {
ctx, stop := signal.NotifyContext(
context.Background(),
os.Interrupt,
syscall.SIGTERM,
syscall.SIGQUIT,
)
defer stop()
<-ctx.Done()
logger.Info("shutdown signal received")
srv.GracefulStop()
}(srv, logger)
logger.Info("listening and serving", zap.String("address", srv.lis.Addr().String()))
if err = srv.Serve(); err != nil {
logger.Fatal("something went wrong while serving", zap.Error(err))
}
logger.Info("shutdown completed")
}
type server struct {
lis net.Listener
grpcSrv *grpc.Server
}
func newServer(address string, logger *zap.Logger) (*server, error) {
transport, err := newTransportCredentials()
if err != nil {
return nil, err
}
lis, err := net.Listen("tcp", address)
if err != nil {
return nil, err
}
grpc_zap.ReplaceGrpcLoggerV2(logger)
srv := grpc.NewServer(
grpc.Creds(transport),
grpc.ChainUnaryInterceptor(
grpc_recovery.UnaryServerInterceptor(grpc_recovery.WithRecoveryHandler(createPanicHandler(logger))),
grpc_ctxtags.UnaryServerInterceptor(grpc_ctxtags.WithFieldExtractor(grpc_ctxtags.CodeGenRequestFieldExtractor)),
grpc_zap.UnaryServerInterceptor(logger),
),
grpc.ChainStreamInterceptor(
grpc_recovery.StreamServerInterceptor(grpc_recovery.WithRecoveryHandler(createPanicHandler(logger))),
grpc_ctxtags.StreamServerInterceptor(grpc_ctxtags.WithFieldExtractor(grpc_ctxtags.CodeGenRequestFieldExtractor)),
grpc_zap.StreamServerInterceptor(logger),
),
)
proto.RegisterGreeterServer(srv, greeter.NewServer(logger))
return &server{
lis: lis,
grpcSrv: srv,
}, nil
}
func (s *server) Serve() error {
return s.grpcSrv.Serve(s.lis)
}
func (s *server) GracefulStop() {
// GracefulStop also closes the listener.
s.grpcSrv.GracefulStop()
}
func newTransportCredentials() (credentials.TransportCredentials, error) {
cert, err := tls.LoadX509KeyPair("./certs/server.pem", "./certs/server-key.pem")
if err != nil {
return nil, fmt.Errorf("couldn't load server cert: %w", err)
}
data, err := os.ReadFile("./certs/ca.pem")
if err != nil {
return nil, fmt.Errorf("couldn't load CA file: %w", err)
}
capool := x509.NewCertPool()
if !capool.AppendCertsFromPEM(data) {
return nil, errors.New("couldn't add ca cart to cert pool")
}
return credentials.NewTLS(&tls.Config{
ClientAuth: tls.RequireAndVerifyClientCert,
Certificates: []tls.Certificate{cert},
ClientCAs: capool,
MinVersion: tls.VersionTLS13,
}), nil
}
func createPanicHandler(logger *zap.Logger) grpc_recovery.RecoveryHandlerFunc {
return func(p interface{}) error {
logger.Panic(fmt.Sprintf("%v", p), zap.Stack("stack"))
return status.Errorf(codes.Internal, "internal server error")
}
}