diff --git a/cmd/inbucket/main.go b/cmd/inbucket/main.go index 254d9e7..a93c83e 100644 --- a/cmd/inbucket/main.go +++ b/cmd/inbucket/main.go @@ -37,7 +37,7 @@ func init() { startTime.Set(time.Now().UnixNano() / 1000000) // Goroutine count for status page. - expvar.Publish("goroutines", expvar.Func(func() interface{} { + expvar.Publish("goroutines", expvar.Func(func() any { return runtime.NumGoroutine() })) diff --git a/pkg/server/pop3/handler.go b/pkg/server/pop3/handler.go index 8e8364d..9bf619f 100644 --- a/pkg/server/pop3/handler.go +++ b/pkg/server/pop3/handler.go @@ -2,6 +2,7 @@ package pop3 import ( "bufio" + "context" "crypto/tls" "fmt" "io" @@ -101,7 +102,7 @@ func (s *Session) String() string { * 4. If bad cmd, respond error * 5. Goto 2 */ -func (s *Server) startSession(id int, conn net.Conn) { +func (s *Server) startSession(ctx context.Context, id int, conn net.Conn) { logger := log.With().Str("module", "pop3").Str("remote", conn.RemoteAddr().String()). Int("session", id).Logger() logger.Debug().Msgf("ForceTLS: %t", s.config.ForceTLS) @@ -165,7 +166,7 @@ func (s *Server) startSession(id int, conn net.Conn) { // Send command to handler for current state switch ssn.state { case AUTHORIZATION: - ssn.authorizationHandler(cmd, arg) + ssn.authorizationHandler(ctx, cmd, arg) continue case TRANSACTION: ssn.transactionHandler(cmd, arg) @@ -206,7 +207,7 @@ func (s *Server) startSession(id int, conn net.Conn) { } // AUTHORIZATION state -func (s *Session) authorizationHandler(cmd string, args []string) { +func (s *Session) authorizationHandler(ctx context.Context, cmd string, args []string) { switch cmd { case "QUIT": s.send("+OK Goodnight and good luck") @@ -229,9 +230,11 @@ func (s *Session) authorizationHandler(cmd string, args []string) { s.logger.Debug().Msg("Initiating TLS context.") // Start TLS connection handshake. + tlsCtx, cancel := context.WithTimeout(ctx, s.config.Timeout) + defer cancel() s.send("+OK Begin TLS Negotiation") tlsConn := tls.Server(s.conn, s.tlsConfig) - if err := tlsConn.Handshake(); err != nil { + if err := tlsConn.HandshakeContext(tlsCtx); err != nil { s.logger.Error().Msgf("-ERR TLS handshake failed %v", err) s.ooSeq(cmd) } diff --git a/pkg/server/pop3/handler_test.go b/pkg/server/pop3/handler_test.go index 5405493..704c925 100644 --- a/pkg/server/pop3/handler_test.go +++ b/pkg/server/pop3/handler_test.go @@ -302,7 +302,7 @@ func setupPOPServer(t *testing.T, ds storage.Store, tls bool, forceTLS bool) *Se cfg := config.POP3{ Addr: "127.0.0.1:2500", Domain: "inbucket.local", - Timeout: 5, + Timeout: 5 * time.Second, Debug: true, ForceTLS: forceTLS, } @@ -344,7 +344,7 @@ func setupPOPSession(t *testing.T, server *Server) net.Conn { // Start the session. server.wg.Add(1) sessionNum++ - go server.startSession(sessionNum, &mockConn{serverConn}) + go server.startSession(context.Background(), sessionNum, &mockConn{serverConn}) return clientConn } diff --git a/pkg/server/pop3/listener.go b/pkg/server/pop3/listener.go index 4c73702..e2e2136 100644 --- a/pkg/server/pop3/listener.go +++ b/pkg/server/pop3/listener.go @@ -120,7 +120,7 @@ func (s *Server) serve(ctx context.Context) { } else { tempDelay = 0 s.wg.Add(1) - go s.startSession(sid, conn) + go s.startSession(ctx, sid, conn) } } } diff --git a/pkg/server/web/server.go b/pkg/server/web/server.go index bf52289..530e423 100644 --- a/pkg/server/web/server.go +++ b/pkg/server/web/server.go @@ -130,6 +130,11 @@ func NewServer(conf *config.Root, mm message.Manager, mh *msghub.Hub) *Server { // Start begins listening for HTTP requests func (s *Server) Start(ctx context.Context, readyFunc func()) { + var ( + err error + listenCfg net.ListenConfig + ) + server = &http.Server{ Addr: rootConfig.Web.Addr, Handler: requestLoggingWrapper(Router), @@ -140,8 +145,11 @@ func (s *Server) Start(ctx context.Context, readyFunc func()) { // We don't use ListenAndServe because it lacks a way to close the listener log.Info().Str("module", "web").Str("phase", "startup").Str("addr", server.Addr). Msg("HTTP listening on tcp4") - var err error - listener, err = net.Listen("tcp", server.Addr) + + // This context is only used while the listener is resolving our address. + listenCtx, cancel := context.WithTimeout(ctx, 30*time.Second) + defer cancel() + listener, err = listenCfg.Listen(listenCtx, "tcp", server.Addr) if err != nil { log.Error().Str("module", "web").Str("phase", "startup").Err(err). Msg("HTTP failed to start TCP4 listener")