package server import ( "context" "fmt" "log/slog" "net" "net/http" "time" "git.zhouxhere.com/zhouxhere/syz/config" "git.zhouxhere.com/zhouxhere/syz/store" "github.com/labstack/echo/v4" "github.com/pkg/errors" "github.com/soheilhy/cmux" "google.golang.org/grpc" ) type Server struct { config *config.Config Store *store.Store httpServer *http.Server grpcServer *grpc.Server } func NewServer(ctx context.Context, config *config.Config, store *store.Store) *Server { s := &Server{ Store: store, } s.config = config echoServer := echo.New() echoServer.GET("/ping", func(c echo.Context) error { return c.JSON(http.StatusOK, "pong") }) addr := fmt.Sprintf("%s:%d", config.Addr, config.Port) s.httpServer = &http.Server{ Addr: addr, Handler: echoServer, } grpcServer := grpc.NewServer() s.grpcServer = grpcServer return s } func (s *Server) Start(ctx context.Context) error { addr := fmt.Sprintf("%s:%d", s.config.Addr, s.config.Port) listener, err := net.Listen("tcp", addr) if err != nil { return errors.Wrap(err, "failed to listen") } muxServer := cmux.New(listener) go func() { grpcListener := muxServer.MatchWithWriters(cmux.HTTP2MatchHeaderFieldSendSettings("content-type", "application/grpc")) if err := s.grpcServer.Serve(grpcListener); err != nil { slog.Error("failed to start grpc server", "error", err) } }() go func() { httpListener := muxServer.Match(cmux.HTTP1Fast(http.MethodPatch)) if err := s.httpServer.Serve(httpListener); err != nil { slog.Error("failed to start http server", "error", err) } }() go func() { if err := muxServer.Serve(); err != nil { slog.Error("failed to start server", "error", err) } }() return nil } func (s *Server) Stop(ctx context.Context) { ctx, cancel := context.WithTimeout(ctx, 10*time.Second) defer cancel() if err := s.httpServer.Shutdown(ctx); err != nil { slog.Error("failed to stop http server", "error", err) } s.grpcServer.GracefulStop() if err := s.Store.Close(); err != nil { slog.Error("failed to close store", "error", err) } slog.Info("server stopped") }