@@ -47,30 +47,29 @@ func RegisterServerLifecycle(
4747 logger * zap.Logger ,
4848) {
4949 lc .Append (fx.Hook {
50- OnStart : func (ctx context.Context ) error {
51- addr := fmt .Sprintf ("%s:%d" , cfg .Host , cfg .Port )
52- logger .Info ("starting Hilt service" , zap .String ("address" , addr ))
50+ OnStart : func (ctx context.Context ) error {
51+ addr := fmt .Sprintf ("%s:%d" , cfg .Host , cfg .Port )
5352
54- errCh := make (chan error , 1 )
55- go func () {
56- if err := e .Start (addr ); err != nil && err != http .ErrServerClosed {
57- select {
58- case errCh <- err :
59- default :
60- logger .Error ("server error" , zap .Error (err ))
61- }
62- }
63- }()
53+ // Bind synchronously so a failure (e.g. port already in use) is
54+ // returned from OnStart and aborts fx startup. Racing the bind
55+ // against a timer can report success over a dead listener and
56+ // silently drop a late bind error.
57+ ln , err := net .Listen ("tcp" , addr )
58+ if err != nil {
59+ return fmt .Errorf ("binding %s: %w" , addr , err )
60+ }
61+ e .Listener = ln
62+ logger .Info ("starting Hilt service" , zap .String ("address" , addr ))
63+ go func () {
64+ // e.Start reuses the listener bound above and blocks serving
65+ // until Shutdown, which returns http.ErrServerClosed.
66+ if err := e .Start (addr ); err != nil && err != http .ErrServerClosed {
67+ logger .Error ("server stopped unexpectedly" , zap .Error (err ))
68+ }
69+ }()
6470
65- select {
66- case err := <- errCh :
67- return fmt .Errorf ("starting server: %w" , err )
68- case <- time .After (100 * time .Millisecond ):
69- return nil
70- case <- ctx .Done ():
71- return ctx .Err ()
72- }
73- },
71+ return nil
72+ },
7473 OnStop : func (ctx context.Context ) error {
7574 logger .Info ("shutting down server" )
7675 shutdownCtx , cancel := context .WithTimeout (ctx , 10 * time .Second )
0 commit comments