1
0
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:
James Hillyerd
2022-08-13 13:22:34 -07:00
committed by GitHub
parent 29d1ed1e7f
commit eae4926b23
12 changed files with 288 additions and 188 deletions

View File

@@ -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
}