Skip to content

Commit

Permalink
refact: server module (#310)
Browse files Browse the repository at this point in the history
  • Loading branch information
crlssn authored Dec 22, 2024
1 parent 0f85744 commit 02aa036
Show file tree
Hide file tree
Showing 3 changed files with 167 additions and 118 deletions.
66 changes: 66 additions & 0 deletions server/rpc/handlers/module.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package handlers

import (
"net/http"

"connectrpc.com/connect"
"go.uber.org/fx"

"github.com/crlssn/getstronger/server/gen/proto/api/v1/apiv1connect"
handlers "github.com/crlssn/getstronger/server/rpc/handlers/v1"
)

func Module() fx.Option {
return fx.Options(
fx.Provide(
BuildHandlers,
handlers.NewAuthHandler,
handlers.NewFeedHandler,
handlers.NewUserHandler,
handlers.NewRoutineHandler,
handlers.NewWorkoutHandler,
handlers.NewExerciseHandler,
handlers.NewNotificationHandler,
),
)
}

type BuildHandlersParams struct {
fx.In

Auth apiv1connect.AuthServiceHandler
Feed apiv1connect.FeedServiceHandler
User apiv1connect.UserServiceHandler
Routine apiv1connect.RoutineServiceHandler
Workout apiv1connect.WorkoutServiceHandler
Exercise apiv1connect.ExerciseServiceHandler
Notification apiv1connect.NotificationServiceHandler
}

type HandlerFunc func(opts ...connect.HandlerOption) (string, http.Handler)

func BuildHandlers(p BuildHandlersParams) []HandlerFunc {
return []HandlerFunc{
func(opts ...connect.HandlerOption) (string, http.Handler) {
return apiv1connect.NewAuthServiceHandler(p.Auth, opts...)
},
func(opts ...connect.HandlerOption) (string, http.Handler) {
return apiv1connect.NewFeedServiceHandler(p.Feed, opts...)
},
func(opts ...connect.HandlerOption) (string, http.Handler) {
return apiv1connect.NewUserServiceHandler(p.User, opts...)
},
func(opts ...connect.HandlerOption) (string, http.Handler) {
return apiv1connect.NewRoutineServiceHandler(p.Routine, opts...)
},
func(opts ...connect.HandlerOption) (string, http.Handler) {
return apiv1connect.NewWorkoutServiceHandler(p.Workout, opts...)
},
func(opts ...connect.HandlerOption) (string, http.Handler) {
return apiv1connect.NewExerciseServiceHandler(p.Exercise, opts...)
},
func(opts ...connect.HandlerOption) (string, http.Handler) {
return apiv1connect.NewNotificationServiceHandler(p.Notification, opts...)
},
}
}
129 changes: 11 additions & 118 deletions server/rpc/server/module.go
Original file line number Diff line number Diff line change
@@ -1,134 +1,27 @@
package server

import (
"context"
"crypto/tls"
"errors"
"fmt"
"log"
"net/http"
"time"

"connectrpc.com/connect"
"go.uber.org/fx"
"golang.org/x/net/http2"
"golang.org/x/net/http2/h2c"

"github.com/crlssn/getstronger/server/config"
"github.com/crlssn/getstronger/server/gen/proto/api/v1/apiv1connect"
handlers "github.com/crlssn/getstronger/server/rpc/handlers/v1"
"github.com/crlssn/getstronger/server/rpc/handlers"
"github.com/crlssn/getstronger/server/rpc/interceptors"
"github.com/crlssn/getstronger/server/rpc/middlewares"
"github.com/crlssn/getstronger/server/stream"
)

func Module() fx.Option {
return fx.Module("rpc", fx.Options(
interceptors.Module(),
fx.Provide(
registerHandlers,
handlers.NewAuthHandler,
handlers.NewFeedHandler,
handlers.NewUserHandler,
handlers.NewRoutineHandler,
handlers.NewWorkoutHandler,
handlers.NewExerciseHandler,
handlers.NewNotificationHandler,
NewServer,
NewMultiplexer,
middlewares.New,
),
fx.Invoke(
startServer,
),
handlers.Module(),
interceptors.Module(),
fx.Invoke(func(lc fx.Lifecycle, s *Server) {
lc.Append(fx.Hook{
OnStart: s.ListenAndServe,
OnStop: s.server.Shutdown,
})
}),
))
}

type Handlers struct {
fx.In

Auth apiv1connect.AuthServiceHandler
Feed apiv1connect.FeedServiceHandler
User apiv1connect.UserServiceHandler
Routine apiv1connect.RoutineServiceHandler
Workout apiv1connect.WorkoutServiceHandler
Exercise apiv1connect.ExerciseServiceHandler
Notification apiv1connect.NotificationServiceHandler
}

func registerHandlers(p Handlers, o []connect.HandlerOption, m *middlewares.Middleware) *http.ServeMux {
handlers := []func(opts ...connect.HandlerOption) (string, http.Handler){
func(opts ...connect.HandlerOption) (string, http.Handler) {
return apiv1connect.NewAuthServiceHandler(p.Auth, opts...)
},
func(opts ...connect.HandlerOption) (string, http.Handler) {
return apiv1connect.NewFeedServiceHandler(p.Feed, opts...)
},
func(opts ...connect.HandlerOption) (string, http.Handler) {
return apiv1connect.NewUserServiceHandler(p.User, opts...)
},
func(opts ...connect.HandlerOption) (string, http.Handler) {
return apiv1connect.NewRoutineServiceHandler(p.Routine, opts...)
},
func(opts ...connect.HandlerOption) (string, http.Handler) {
return apiv1connect.NewWorkoutServiceHandler(p.Workout, opts...)
},
func(opts ...connect.HandlerOption) (string, http.Handler) {
return apiv1connect.NewExerciseServiceHandler(p.Exercise, opts...)
},
func(opts ...connect.HandlerOption) (string, http.Handler) {
return apiv1connect.NewNotificationServiceHandler(p.Notification, opts...)
},
}

mux := http.NewServeMux()
for _, h := range handlers {
path, handler := h(o...)
mux.Handle(path, m.Register(handler))
}

return mux
}

const (
readTimeout = 10 * time.Second
idleTimeout = 120 * time.Second
)

func startServer(l fx.Lifecycle, c *config.Config, m *http.ServeMux, conn *stream.Conn) {
s := &http.Server{
Addr: fmt.Sprintf(":%s", c.Server.Port),
Handler: h2c.NewHandler(m, &http2.Server{}),
ReadTimeout: readTimeout,
WriteTimeout: 0,
IdleTimeout: idleTimeout,
TLSConfig: &tls.Config{
MinVersion: tls.VersionTLS12,
},
}

s.RegisterOnShutdown(conn.Cancel)

l.Append(fx.Hook{
OnStart: func(_ context.Context) error {
go func() {
if err := listenAndServe(s, c.Server.CertPath, c.Server.KeyPath); err != nil {
if errors.Is(err, http.ErrServerClosed) {
return
}
log.Fatalf("listen and serve: %v", err)
}
}()
return nil
},
OnStop: func(ctx context.Context) error {
return s.Shutdown(ctx)
},
})
}

func listenAndServe(s *http.Server, certPath, keyPath string) error {
if certPath == "" && keyPath == "" {
return s.ListenAndServe() //nolint:wrapcheck
}

return s.ListenAndServeTLS(certPath, keyPath) //nolint:wrapcheck
}
90 changes: 90 additions & 0 deletions server/rpc/server/server.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package server

import (
"context"
"crypto/tls"
"errors"
"fmt"
"net/http"
"time"

"connectrpc.com/connect"
"go.uber.org/fx"
"go.uber.org/zap"
"golang.org/x/net/http2"
"golang.org/x/net/http2/h2c"

"github.com/crlssn/getstronger/server/config"
"github.com/crlssn/getstronger/server/rpc/handlers"
"github.com/crlssn/getstronger/server/rpc/middlewares"
"github.com/crlssn/getstronger/server/stream"
)

type Server struct {
log *zap.Logger
conn *stream.Conn
server *http.Server
keyPath string
certPath string
}

type Params struct {
fx.In

Log *zap.Logger
Mux *http.ServeMux
Conn *stream.Conn
Config *config.Config
}

func NewServer(p Params) *Server {
return &Server{
conn: p.Conn,
keyPath: p.Config.Server.KeyPath,
certPath: p.Config.Server.CertPath,
server: &http.Server{
Addr: fmt.Sprintf(":%s", p.Config.Server.Port),
Handler: h2c.NewHandler(p.Mux, &http2.Server{}),
ReadTimeout: 10 * time.Second, //nolint:mnd
WriteTimeout: 0,
IdleTimeout: 120 * time.Second, //nolint:mnd
TLSConfig: &tls.Config{
MinVersion: tls.VersionTLS12,
},
},
}
}

func (s *Server) ListenAndServe(_ context.Context) error {
go func() {
if err := s.listenAndServe(); err != nil {
if errors.Is(err, http.ErrServerClosed) {
return
}

s.log.Fatal("server: listen and serve", zap.Error(err))
}
}()

return nil
}

func (s *Server) listenAndServe() error {
s.server.RegisterOnShutdown(s.conn.Cancel)

if s.certPath == "" && s.keyPath == "" {
return s.server.ListenAndServe() //nolint:wrapcheck
}

return s.server.ListenAndServeTLS(s.certPath, s.keyPath) //nolint:wrapcheck
}

func NewMultiplexer(f []handlers.HandlerFunc, o []connect.HandlerOption, m *middlewares.Middleware) *http.ServeMux {
mux := http.NewServeMux()
for _, h := range f {
path, handler := h(o...)
mux.Handle(path, m.Register(handler))
}

return mux
}

0 comments on commit 02aa036

Please sign in to comment.