mirror of
https://github.com/jhillyerd/inbucket.git
synced 2025-12-17 17:47:03 +00:00
Dependency injection improvements (#288)
Refactor server life-cycle into it's own file, make service startup and monitoring more consistent and testable. * Extract services creation in preparation for DI * pop3: rename New to NewServer * lifecycle: Add fatal error Notify() * web: Introduce Server struct w/ Notify() * Extract Start in lifecycle * Add Start() to Hub * RetentionScanner startup consistent with other svcs * Remove global shutdown channel * Implement a readiness notification system
This commit is contained in:
@@ -31,10 +31,9 @@ var (
|
||||
// incoming requests to the correct handler function
|
||||
Router = mux.NewRouter()
|
||||
|
||||
rootConfig *config.Root
|
||||
server *http.Server
|
||||
listener net.Listener
|
||||
globalShutdown chan bool
|
||||
rootConfig *config.Root
|
||||
server *http.Server
|
||||
listener net.Listener
|
||||
|
||||
// ExpWebSocketConnectsCurrent tracks the number of open WebSockets
|
||||
ExpWebSocketConnectsCurrent = new(expvar.Int)
|
||||
@@ -45,15 +44,19 @@ func init() {
|
||||
m.Set("WebSocketConnectsCurrent", ExpWebSocketConnectsCurrent)
|
||||
}
|
||||
|
||||
// Initialize sets up things for unit tests or the Start() method.
|
||||
func Initialize(
|
||||
// Server defines an instance of the Web server.
|
||||
type Server struct {
|
||||
// TODO Migrate global vars here.
|
||||
notify chan error // Notify on fatal error.
|
||||
}
|
||||
|
||||
// NewServer sets up things for unit tests or the Start() method.
|
||||
func NewServer(
|
||||
conf *config.Root,
|
||||
shutdownChan chan bool,
|
||||
mm message.Manager,
|
||||
mh *msghub.Hub) {
|
||||
mh *msghub.Hub) *Server {
|
||||
|
||||
rootConfig = conf
|
||||
globalShutdown = shutdownChan
|
||||
|
||||
// NewContext() will use this DataStore for the web handlers.
|
||||
msgHub = mh
|
||||
@@ -118,10 +121,16 @@ func Initialize(
|
||||
http.StatusNotFound, "No route matches URI path")
|
||||
Router.MethodNotAllowedHandler = noMatchHandler(
|
||||
http.StatusMethodNotAllowed, "Method not allowed for URI path")
|
||||
|
||||
s := &Server{
|
||||
notify: make(chan error, 1),
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
// Start begins listening for HTTP requests
|
||||
func Start(ctx context.Context) {
|
||||
func (s *Server) Start(ctx context.Context, readyFunc func()) {
|
||||
server = &http.Server{
|
||||
Addr: rootConfig.Web.Addr,
|
||||
Handler: requestLoggingWrapper(Router),
|
||||
@@ -137,12 +146,14 @@ func Start(ctx context.Context) {
|
||||
if err != nil {
|
||||
log.Error().Str("module", "web").Str("phase", "startup").Err(err).
|
||||
Msg("HTTP failed to start TCP4 listener")
|
||||
emergencyShutdown()
|
||||
s.notify <- err
|
||||
close(s.notify)
|
||||
return
|
||||
}
|
||||
|
||||
// Listener go routine
|
||||
go serve(ctx)
|
||||
// Start listener go routine
|
||||
go s.serve(ctx)
|
||||
readyFunc()
|
||||
|
||||
// Wait for shutdown
|
||||
select {
|
||||
@@ -176,7 +187,7 @@ func appConfigCookie(webConfig config.Web) *http.Cookie {
|
||||
}
|
||||
|
||||
// serve begins serving HTTP requests
|
||||
func serve(ctx context.Context) {
|
||||
func (s *Server) serve(ctx context.Context) {
|
||||
// server.Serve blocks until we close the listener
|
||||
err := server.Serve(listener)
|
||||
|
||||
@@ -186,16 +197,13 @@ func serve(ctx context.Context) {
|
||||
default:
|
||||
log.Error().Str("module", "web").Str("phase", "startup").Err(err).
|
||||
Msg("HTTP server failed")
|
||||
emergencyShutdown()
|
||||
s.notify <- err
|
||||
close(s.notify)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func emergencyShutdown() {
|
||||
// Shutdown Inbucket
|
||||
select {
|
||||
case _ = <-globalShutdown:
|
||||
default:
|
||||
close(globalShutdown)
|
||||
}
|
||||
// Notify allows the running Web server to be monitored for a fatal error.
|
||||
func (s *Server) Notify() <-chan error {
|
||||
return s.notify
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user