149 lines
3.6 KiB
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")
|
|
}
|
|
}
|