Skip to content

Commit 6119aec

Browse files
authored
Merge pull request #1735 from aldas/refactor_echo_instance_listener_access
refactor Echo server startup to allow data race free access to listener address
2 parents b065180 + d18c040 commit 6119aec

File tree

2 files changed

+381
-53
lines changed

2 files changed

+381
-53
lines changed

echo.go

+77-7
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@ type (
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.
645648
func (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.
653662
func (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

674693
func 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.
686705
func (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.
704730
func (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).
743806
func (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()`.
772838
func (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()`.
781849
func (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

Comments
 (0)