6767 // Echo is the top-level framework instance.
6868 Echo struct {
6969 common
70+ // startupMutex is mutex to lock Echo instance access during server configuration and startup. Useful for to get
71+ // listener address info (on which interface/port was listener binded) without having data races.
72+ startupMutex sync.RWMutex
7073 StdLogger * stdLog.Logger
7174 colorer * color.Color
7275 premiddleware []MiddlewareFunc
@@ -643,32 +646,48 @@ func (e *Echo) ServeHTTP(w http.ResponseWriter, r *http.Request) {
643646
644647// Start starts an HTTP server.
645648func (e * Echo ) Start (address string ) error {
649+ e .startupMutex .Lock ()
646650 e .Server .Addr = address
647- return e .StartServer (e .Server )
651+ if err := e .configureServer (e .Server ); err != nil {
652+ e .startupMutex .Unlock ()
653+ return err
654+ }
655+ e .startupMutex .Unlock ()
656+ return e .serve ()
648657}
649658
650659// StartTLS starts an HTTPS server.
651660// If `certFile` or `keyFile` is `string` the values are treated as file paths.
652661// If `certFile` or `keyFile` is `[]byte` the values are treated as the certificate or key as-is.
653662func (e * Echo ) StartTLS (address string , certFile , keyFile interface {}) (err error ) {
663+ e .startupMutex .Lock ()
654664 var cert []byte
655665 if cert , err = filepathOrContent (certFile ); err != nil {
666+ e .startupMutex .Unlock ()
656667 return
657668 }
658669
659670 var key []byte
660671 if key , err = filepathOrContent (keyFile ); err != nil {
672+ e .startupMutex .Unlock ()
661673 return
662674 }
663675
664676 s := e .TLSServer
665677 s .TLSConfig = new (tls.Config )
666678 s .TLSConfig .Certificates = make ([]tls.Certificate , 1 )
667679 if s .TLSConfig .Certificates [0 ], err = tls .X509KeyPair (cert , key ); err != nil {
680+ e .startupMutex .Unlock ()
668681 return
669682 }
670683
671- return e .startTLS (address )
684+ e .configureTLS (address )
685+ if err := e .configureServer (s ); err != nil {
686+ e .startupMutex .Unlock ()
687+ return err
688+ }
689+ e .startupMutex .Unlock ()
690+ return s .Serve (e .TLSListener )
672691}
673692
674693func filepathOrContent (fileOrContent interface {}) (content []byte , err error ) {
@@ -684,24 +703,41 @@ func filepathOrContent(fileOrContent interface{}) (content []byte, err error) {
684703
685704// StartAutoTLS starts an HTTPS server using certificates automatically installed from https://letsencrypt.org.
686705func (e * Echo ) StartAutoTLS (address string ) error {
706+ e .startupMutex .Lock ()
687707 s := e .TLSServer
688708 s .TLSConfig = new (tls.Config )
689709 s .TLSConfig .GetCertificate = e .AutoTLSManager .GetCertificate
690710 s .TLSConfig .NextProtos = append (s .TLSConfig .NextProtos , acme .ALPNProto )
691- return e .startTLS (address )
711+
712+ e .configureTLS (address )
713+ if err := e .configureServer (s ); err != nil {
714+ e .startupMutex .Unlock ()
715+ return err
716+ }
717+ e .startupMutex .Unlock ()
718+ return s .Serve (e .TLSListener )
692719}
693720
694- func (e * Echo ) startTLS (address string ) error {
721+ func (e * Echo ) configureTLS (address string ) {
695722 s := e .TLSServer
696723 s .Addr = address
697724 if ! e .DisableHTTP2 {
698725 s .TLSConfig .NextProtos = append (s .TLSConfig .NextProtos , "h2" )
699726 }
700- return e .StartServer (e .TLSServer )
701727}
702728
703729// StartServer starts a custom http server.
704730func (e * Echo ) StartServer (s * http.Server ) (err error ) {
731+ e .startupMutex .Lock ()
732+ if err := e .configureServer (s ); err != nil {
733+ e .startupMutex .Unlock ()
734+ return err
735+ }
736+ e .startupMutex .Unlock ()
737+ return e .serve ()
738+ }
739+
740+ func (e * Echo ) configureServer (s * http.Server ) (err error ) {
705741 // Setup
706742 e .colorer .SetOutput (e .Logger .Output ())
707743 s .ErrorLog = e .StdLogger
@@ -724,7 +760,7 @@ func (e *Echo) StartServer(s *http.Server) (err error) {
724760 if ! e .HidePort {
725761 e .colorer .Printf ("⇨ http server started on %s\n " , e .colorer .Green (e .Listener .Addr ()))
726762 }
727- return s . Serve ( e . Listener )
763+ return nil
728764 }
729765 if e .TLSListener == nil {
730766 l , err := newListener (s .Addr , e .ListenerNetwork )
@@ -736,11 +772,39 @@ func (e *Echo) StartServer(s *http.Server) (err error) {
736772 if ! e .HidePort {
737773 e .colorer .Printf ("⇨ https server started on %s\n " , e .colorer .Green (e .TLSListener .Addr ()))
738774 }
739- return s .Serve (e .TLSListener )
775+ return nil
776+ }
777+
778+ func (e * Echo ) serve () error {
779+ if e .TLSListener != nil {
780+ return e .Server .Serve (e .TLSListener )
781+ }
782+ return e .Server .Serve (e .Listener )
783+ }
784+
785+ // ListenerAddr returns net.Addr for Listener
786+ func (e * Echo ) ListenerAddr () net.Addr {
787+ e .startupMutex .RLock ()
788+ defer e .startupMutex .RUnlock ()
789+ if e .Listener == nil {
790+ return nil
791+ }
792+ return e .Listener .Addr ()
793+ }
794+
795+ // TLSListenerAddr returns net.Addr for TLSListener
796+ func (e * Echo ) TLSListenerAddr () net.Addr {
797+ e .startupMutex .RLock ()
798+ defer e .startupMutex .RUnlock ()
799+ if e .TLSListener == nil {
800+ return nil
801+ }
802+ return e .TLSListener .Addr ()
740803}
741804
742805// StartH2CServer starts a custom http/2 server with h2c (HTTP/2 Cleartext).
743806func (e * Echo ) StartH2CServer (address string , h2s * http2.Server ) (err error ) {
807+ e .startupMutex .Lock ()
744808 // Setup
745809 s := e .Server
746810 s .Addr = address
@@ -758,18 +822,22 @@ func (e *Echo) StartH2CServer(address string, h2s *http2.Server) (err error) {
758822 if e .Listener == nil {
759823 e .Listener , err = newListener (s .Addr , e .ListenerNetwork )
760824 if err != nil {
825+ e .startupMutex .Unlock ()
761826 return err
762827 }
763828 }
764829 if ! e .HidePort {
765830 e .colorer .Printf ("⇨ http server started on %s\n " , e .colorer .Green (e .Listener .Addr ()))
766831 }
832+ e .startupMutex .Unlock ()
767833 return s .Serve (e .Listener )
768834}
769835
770836// Close immediately stops the server.
771837// It internally calls `http.Server#Close()`.
772838func (e * Echo ) Close () error {
839+ e .startupMutex .Lock ()
840+ defer e .startupMutex .Unlock ()
773841 if err := e .TLSServer .Close (); err != nil {
774842 return err
775843 }
@@ -779,6 +847,8 @@ func (e *Echo) Close() error {
779847// Shutdown stops the server gracefully.
780848// It internally calls `http.Server#Shutdown()`.
781849func (e * Echo ) Shutdown (ctx stdContext.Context ) error {
850+ e .startupMutex .Lock ()
851+ defer e .startupMutex .Unlock ()
782852 if err := e .TLSServer .Shutdown (ctx ); err != nil {
783853 return err
784854 }
0 commit comments