From d7bd1b74e85781e45353b3cccdce7fb289b339f0 Mon Sep 17 00:00:00 2001 From: Macrow Date: Fri, 18 Mar 2022 20:54:32 +0800 Subject: [PATCH 1/8] feat: support custom listener --- net/ghttp/ghttp_server.go | 23 +++++++++++++++++++---- net/ghttp/ghttp_server_config.go | 8 ++++++++ net/ghttp/ghttp_server_graceful.go | 12 ++++++++---- 3 files changed, 35 insertions(+), 8 deletions(-) diff --git a/net/ghttp/ghttp_server.go b/net/ghttp/ghttp_server.go index 0961cba70..198c27891 100644 --- a/net/ghttp/ghttp_server.go +++ b/net/ghttp/ghttp_server.go @@ -10,6 +10,7 @@ import ( "bytes" "context" "fmt" + "net" "net/http" "os" "runtime" @@ -262,6 +263,12 @@ func (s *Server) getListenAddress() string { } else { port = gconv.Int(array[0]) } + if s.config.Listeners != nil { + for _, v := range s.config.Listeners { + portArray := gstr.SplitAndTrim(v.Addr().String(), ":") + port = gconv.Int(portArray[len(portArray)-1]) + } + } return fmt.Sprintf(`http://%s:%d`, host, port) } @@ -496,10 +503,14 @@ func (s *Server) startServer(fdMap listenerFdMap) { fd = gconv.Int(addrAndFd[1]) } } + var ln net.Listener + if s.config.Listeners != nil { + ln = s.config.Listeners[v] + } if fd > 0 { - s.servers = append(s.servers, s.newGracefulServer(itemFunc, fd)) + s.servers = append(s.servers, s.newGracefulServer(itemFunc, ln, fd)) } else { - s.servers = append(s.servers, s.newGracefulServer(itemFunc)) + s.servers = append(s.servers, s.newGracefulServer(itemFunc, ln)) } s.servers[len(s.servers)-1].isHttps = true } @@ -531,10 +542,14 @@ func (s *Server) startServer(fdMap listenerFdMap) { fd = gconv.Int(addrAndFd[1]) } } + var ln net.Listener + if s.config.Listeners != nil { + ln = s.config.Listeners[v] + } if fd > 0 { - s.servers = append(s.servers, s.newGracefulServer(itemFunc, fd)) + s.servers = append(s.servers, s.newGracefulServer(itemFunc, ln, fd)) } else { - s.servers = append(s.servers, s.newGracefulServer(itemFunc)) + s.servers = append(s.servers, s.newGracefulServer(itemFunc, ln)) } } // Start listening asynchronously. diff --git a/net/ghttp/ghttp_server_config.go b/net/ghttp/ghttp_server_config.go index 1aa63a744..7f0da8521 100644 --- a/net/ghttp/ghttp_server_config.go +++ b/net/ghttp/ghttp_server_config.go @@ -9,6 +9,7 @@ package ghttp import ( "context" "crypto/tls" + "net" "net/http" "strconv" "time" @@ -50,6 +51,9 @@ type ServerConfig struct { // HTTPSAddr specifies the HTTPS addresses, multiple addresses joined using char ','. HTTPSAddr string `json:"httpsAddr"` + // Listeners specifies custom listeners, size should match address provided + Listeners map[string]net.Listener `json:"listeners"` + // HTTPSCertPath specifies certification file path for HTTPS service. HTTPSCertPath string `json:"httpsCertPath"` @@ -408,6 +412,10 @@ func (s *Server) SetHTTPSPort(port ...int) { } } +func (s *Server) SetListener(ln map[string]net.Listener) { + s.config.Listeners = ln +} + // EnableHTTPS enables HTTPS with given certification and key files for the server. // The optional parameter `tlsConfig` specifies custom TLS configuration. func (s *Server) EnableHTTPS(certFile, keyFile string, tlsConfig ...*tls.Config) { diff --git a/net/ghttp/ghttp_server_graceful.go b/net/ghttp/ghttp_server_graceful.go index 1eaad5321..6a2be8caf 100644 --- a/net/ghttp/ghttp_server_graceful.go +++ b/net/ghttp/ghttp_server_graceful.go @@ -36,15 +36,16 @@ type gracefulServer struct { // newGracefulServer creates and returns a graceful http server with given address. // The optional parameter `fd` specifies the file descriptor which is passed from parent server. -func (s *Server) newGracefulServer(address string, fd ...int) *gracefulServer { +func (s *Server) newGracefulServer(address string, ln net.Listener, fd ...int) *gracefulServer { // Change port to address like: 80 -> :80 if gstr.IsNumeric(address) { address = ":" + address } gs := &gracefulServer{ - server: s, - address: address, - httpServer: s.newHttpServer(address), + server: s, + address: address, + httpServer: s.newHttpServer(address), + rawListener: ln, } if len(fd) > 0 && fd[0] > 0 { gs.fd = uintptr(fd[0]) @@ -175,6 +176,9 @@ func (s *gracefulServer) doServe(ctx context.Context) error { // getNetListener retrieves and returns the wrapped net.Listener. func (s *gracefulServer) getNetListener() (net.Listener, error) { + if s.rawListener != nil { + return s.rawListener, nil + } var ( ln net.Listener err error From ad202ea735c55e68acb73b18787f9229cb4ab88e Mon Sep 17 00:00:00 2001 From: Macrow Date: Sun, 20 Mar 2022 02:31:21 +0800 Subject: [PATCH 2/8] refactor: adjust method SetListeners of server and add unit test --- net/ghttp/ghttp_server.go | 17 ++--- net/ghttp/ghttp_server_config.go | 27 ++++++-- net/ghttp/ghttp_server_graceful.go | 17 +++-- net/ghttp/ghttp_z_unit_feature_config_test.go | 7 +++ ...tp_z_unit_feature_custom_listeners_test.go | 63 +++++++++++++++++++ 5 files changed, 109 insertions(+), 22 deletions(-) create mode 100644 net/ghttp/ghttp_z_unit_feature_custom_listeners_test.go diff --git a/net/ghttp/ghttp_server.go b/net/ghttp/ghttp_server.go index 198c27891..e98bd07dd 100644 --- a/net/ghttp/ghttp_server.go +++ b/net/ghttp/ghttp_server.go @@ -10,7 +10,6 @@ import ( "bytes" "context" "fmt" - "net" "net/http" "os" "runtime" @@ -503,14 +502,10 @@ func (s *Server) startServer(fdMap listenerFdMap) { fd = gconv.Int(addrAndFd[1]) } } - var ln net.Listener - if s.config.Listeners != nil { - ln = s.config.Listeners[v] - } if fd > 0 { - s.servers = append(s.servers, s.newGracefulServer(itemFunc, ln, fd)) + s.servers = append(s.servers, s.newGracefulServer(itemFunc, fd)) } else { - s.servers = append(s.servers, s.newGracefulServer(itemFunc, ln)) + s.servers = append(s.servers, s.newGracefulServer(itemFunc)) } s.servers[len(s.servers)-1].isHttps = true } @@ -542,14 +537,10 @@ func (s *Server) startServer(fdMap listenerFdMap) { fd = gconv.Int(addrAndFd[1]) } } - var ln net.Listener - if s.config.Listeners != nil { - ln = s.config.Listeners[v] - } if fd > 0 { - s.servers = append(s.servers, s.newGracefulServer(itemFunc, ln, fd)) + s.servers = append(s.servers, s.newGracefulServer(itemFunc, fd)) } else { - s.servers = append(s.servers, s.newGracefulServer(itemFunc, ln)) + s.servers = append(s.servers, s.newGracefulServer(itemFunc)) } } // Start listening asynchronously. diff --git a/net/ghttp/ghttp_server_config.go b/net/ghttp/ghttp_server_config.go index 7f0da8521..d03dbe999 100644 --- a/net/ghttp/ghttp_server_config.go +++ b/net/ghttp/ghttp_server_config.go @@ -51,8 +51,9 @@ type ServerConfig struct { // HTTPSAddr specifies the HTTPS addresses, multiple addresses joined using char ','. HTTPSAddr string `json:"httpsAddr"` - // Listeners specifies custom listeners, size should match address provided - Listeners map[string]net.Listener `json:"listeners"` + // Listeners specifies the custom listeners. + // Listeners is a map, the key of map must specify the port of Address or HTTPSAddr. + Listeners map[int]net.Listener `json:"listeners"` // HTTPSCertPath specifies certification file path for HTTPS service. HTTPSCertPath string `json:"httpsCertPath"` @@ -258,6 +259,7 @@ func NewConfig() ServerConfig { Name: DefaultServerName, Address: ":0", HTTPSAddr: "", + Listeners: nil, Handler: nil, ReadTimeout: 60 * time.Second, WriteTimeout: 0, // No timeout. @@ -412,8 +414,25 @@ func (s *Server) SetHTTPSPort(port ...int) { } } -func (s *Server) SetListener(ln map[string]net.Listener) { - s.config.Listeners = ln +// SetListeners set the custom listener for the server. +// The key of map should specify the port like: SetListeners(map[int]net.Listener{80: ln}). +// If no port found, the listener will be ignored. +// If the listener's port not match the port provided in map, the listener will be ignored. +func (s *Server) SetListeners(listeners map[int]net.Listener) { + for k, v := range listeners { + addrArray := gstr.SplitAndTrim(v.Addr().String(), ":") + port, err := strconv.Atoi(addrArray[len(addrArray)-1]) + if err != nil { + intlog.Printf(context.TODO(), `ignore the listener with port %d`, k) + delete(listeners, k) + continue + } + if port != k { + intlog.Printf(context.TODO(), `ignore the listener with port %d`, k) + delete(listeners, k) + } + } + s.config.Listeners = listeners } // EnableHTTPS enables HTTPS with given certification and key files for the server. diff --git a/net/ghttp/ghttp_server_graceful.go b/net/ghttp/ghttp_server_graceful.go index 6a2be8caf..a62acd7e1 100644 --- a/net/ghttp/ghttp_server_graceful.go +++ b/net/ghttp/ghttp_server_graceful.go @@ -13,6 +13,7 @@ import ( "net" "net/http" "os" + "strconv" "sync" "github.com/gogf/gf/v2/errors/gerror" @@ -36,20 +37,26 @@ type gracefulServer struct { // newGracefulServer creates and returns a graceful http server with given address. // The optional parameter `fd` specifies the file descriptor which is passed from parent server. -func (s *Server) newGracefulServer(address string, ln net.Listener, fd ...int) *gracefulServer { +func (s *Server) newGracefulServer(address string, fd ...int) *gracefulServer { // Change port to address like: 80 -> :80 if gstr.IsNumeric(address) { address = ":" + address } gs := &gracefulServer{ - server: s, - address: address, - httpServer: s.newHttpServer(address), - rawListener: ln, + server: s, + address: address, + httpServer: s.newHttpServer(address), } if len(fd) > 0 && fd[0] > 0 { gs.fd = uintptr(fd[0]) } + if s.config.Listeners != nil { + addrArray := gstr.SplitAndTrim(address, ":") + port, err := strconv.Atoi(addrArray[len(addrArray)-1]) + if err == nil { + gs.rawListener = s.config.Listeners[port] + } + } return gs } diff --git a/net/ghttp/ghttp_z_unit_feature_config_test.go b/net/ghttp/ghttp_z_unit_feature_config_test.go index ff04f7e7e..f24db1506 100644 --- a/net/ghttp/ghttp_z_unit_feature_config_test.go +++ b/net/ghttp/ghttp_z_unit_feature_config_test.go @@ -8,6 +8,7 @@ package ghttp_test import ( "fmt" + "net" "testing" "time" @@ -23,8 +24,13 @@ import ( func Test_ConfigFromMap(t *testing.T) { gtest.C(t, func(t *gtest.T) { + ln, err := net.Listen("tcp", ":8199") + t.AssertNil(err) + listeners := map[int]net.Listener{8199: ln} + m := g.Map{ "address": ":8199", + "listeners": listeners, "readTimeout": "60s", "indexFiles": g.Slice{"index.php", "main.php"}, "errorLogEnabled": true, @@ -38,6 +44,7 @@ func Test_ConfigFromMap(t *testing.T) { d1, _ := time.ParseDuration(gconv.String(m["readTimeout"])) d2, _ := time.ParseDuration(gconv.String(m["cookieMaxAge"])) t.Assert(config.Address, m["address"]) + t.Assert(config.Listeners, listeners) t.Assert(config.ReadTimeout, d1) t.Assert(config.CookieMaxAge, d2) t.Assert(config.IndexFiles, m["indexFiles"]) diff --git a/net/ghttp/ghttp_z_unit_feature_custom_listeners_test.go b/net/ghttp/ghttp_z_unit_feature_custom_listeners_test.go new file mode 100644 index 000000000..0e6f58824 --- /dev/null +++ b/net/ghttp/ghttp_z_unit_feature_custom_listeners_test.go @@ -0,0 +1,63 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package ghttp_test + +import ( + "github.com/gogf/gf/v2/test/gtest" + "net" + "testing" + "time" + + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/net/ghttp" +) + +func Test_SetRightCustomListeners(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + s := g.Server() + s.Group("/", func(group *ghttp.RouterGroup) { + group.GET("/test", func(r *ghttp.Request) { + r.Response.Write("test") + }) + }) + s.SetAddr(":8199") + ln, err := net.Listen("tcp", ":8199") + t.AssertNil(err) + s.SetListeners(map[int]net.Listener{8199: ln}) + + s.Start() + defer s.Shutdown() + + time.Sleep(100 * time.Millisecond) + s.GetListenedPort() + + t.AssertEQ(s.GetListenedPort(), 8199) + }) +} + +func Test_SetWrongCustomListeners(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + s := g.Server() + s.Group("/", func(group *ghttp.RouterGroup) { + group.GET("/test", func(r *ghttp.Request) { + r.Response.Write("test") + }) + }) + s.SetAddr(":8199") + ln, err := net.Listen("tcp", ":8299") + t.AssertNil(err) + s.SetListeners(map[int]net.Listener{8199: ln}) + + s.Start() + defer s.Shutdown() + + time.Sleep(100 * time.Millisecond) + s.GetListenedPort() + + t.AssertEQ(s.GetListenedPort(), 8199) + }) +} From 147348e0d1f49a1fec13931c7c57b35002f8a2af Mon Sep 17 00:00:00 2001 From: Macrow Date: Sun, 20 Mar 2022 10:17:44 +0800 Subject: [PATCH 3/8] refactor: remove unnecessary code in method getListenAddress of Server --- net/ghttp/ghttp_server.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/net/ghttp/ghttp_server.go b/net/ghttp/ghttp_server.go index e98bd07dd..0961cba70 100644 --- a/net/ghttp/ghttp_server.go +++ b/net/ghttp/ghttp_server.go @@ -262,12 +262,6 @@ func (s *Server) getListenAddress() string { } else { port = gconv.Int(array[0]) } - if s.config.Listeners != nil { - for _, v := range s.config.Listeners { - portArray := gstr.SplitAndTrim(v.Addr().String(), ":") - port = gconv.Int(portArray[len(portArray)-1]) - } - } return fmt.Sprintf(`http://%s:%d`, host, port) } From 73ca527b0ab00cec427f25d0ec475dd6cf0b840a Mon Sep 17 00:00:00 2001 From: Macrow Date: Tue, 22 Mar 2022 13:34:15 +0800 Subject: [PATCH 4/8] feat: add SetListener and throw error in SetListeners --- net/ghttp/ghttp_server_config.go | 35 ++++++++++++++----- ...tp_z_unit_feature_custom_listeners_test.go | 28 ++++++++++++++- 2 files changed, 53 insertions(+), 10 deletions(-) diff --git a/net/ghttp/ghttp_server_config.go b/net/ghttp/ghttp_server_config.go index d03dbe999..85ed565a2 100644 --- a/net/ghttp/ghttp_server_config.go +++ b/net/ghttp/ghttp_server_config.go @@ -9,6 +9,9 @@ package ghttp import ( "context" "crypto/tls" + "fmt" + "github.com/gogf/gf/v2/errors/gcode" + "github.com/gogf/gf/v2/errors/gerror" "net" "net/http" "strconv" @@ -414,25 +417,39 @@ func (s *Server) SetHTTPSPort(port ...int) { } } -// SetListeners set the custom listener for the server. +// SetListener set the custom listener for the server. +// It will overwrite the address you specified before. +func (s *Server) SetListener(l net.Listener) error { + addrArray := gstr.SplitAndTrim(l.Addr().String(), ":") + port, err := strconv.Atoi(addrArray[len(addrArray)-1]) + if err != nil { + return err + } + s.config.Address = fmt.Sprintf(":%d", port) + s.config.Listeners = map[int]net.Listener{port: l} + return nil +} + +// SetListeners set the custom listeners for the server. // The key of map should specify the port like: SetListeners(map[int]net.Listener{80: ln}). -// If no port found, the listener will be ignored. -// If the listener's port not match the port provided in map, the listener will be ignored. -func (s *Server) SetListeners(listeners map[int]net.Listener) { +// If the listener's port not match the port provided in map, the method will return error. +func (s *Server) SetListeners(listeners map[int]net.Listener) error { for k, v := range listeners { addrArray := gstr.SplitAndTrim(v.Addr().String(), ":") port, err := strconv.Atoi(addrArray[len(addrArray)-1]) if err != nil { - intlog.Printf(context.TODO(), `ignore the listener with port %d`, k) - delete(listeners, k) - continue + return err } if port != k { - intlog.Printf(context.TODO(), `ignore the listener with port %d`, k) - delete(listeners, k) + return gerror.NewCodef( + gcode.CodeInvalidParameter, + "listener specified by port %d should listen at port %d, but got port: %d", + k, k, port, + ) } } s.config.Listeners = listeners + return nil } // EnableHTTPS enables HTTPS with given certification and key files for the server. diff --git a/net/ghttp/ghttp_z_unit_feature_custom_listeners_test.go b/net/ghttp/ghttp_z_unit_feature_custom_listeners_test.go index 0e6f58824..3efa2c193 100644 --- a/net/ghttp/ghttp_z_unit_feature_custom_listeners_test.go +++ b/net/ghttp/ghttp_z_unit_feature_custom_listeners_test.go @@ -16,6 +16,29 @@ import ( "github.com/gogf/gf/v2/net/ghttp" ) +func Test_SetCustomListener(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + s := g.Server() + s.Group("/", func(group *ghttp.RouterGroup) { + group.GET("/test", func(r *ghttp.Request) { + r.Response.Write("test") + }) + }) + ln, err := net.Listen("tcp", ":8199") + t.AssertNil(err) + err = s.SetListener(ln) + t.AssertNil(err) + + s.Start() + defer s.Shutdown() + + time.Sleep(100 * time.Millisecond) + s.GetListenedPort() + + t.AssertEQ(s.GetListenedPort(), 8199) + }) +} + func Test_SetRightCustomListeners(t *testing.T) { gtest.C(t, func(t *gtest.T) { s := g.Server() @@ -27,7 +50,10 @@ func Test_SetRightCustomListeners(t *testing.T) { s.SetAddr(":8199") ln, err := net.Listen("tcp", ":8199") t.AssertNil(err) - s.SetListeners(map[int]net.Listener{8199: ln}) + err = s.SetListeners(map[int]net.Listener{8299: ln}) + t.AssertNE(err, nil) + err = s.SetListeners(map[int]net.Listener{8199: ln}) + t.AssertNil(err) s.Start() defer s.Shutdown() From 325887fa1848d92907c042f7a503d54e18cdce7d Mon Sep 17 00:00:00 2001 From: Macrow Date: Tue, 22 Mar 2022 21:35:53 +0800 Subject: [PATCH 5/8] fix: SetListener overwrite default address --- net/ghttp/ghttp_server_config.go | 19 +++++++------------ ...tp_z_unit_feature_custom_listeners_test.go | 4 ++-- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/net/ghttp/ghttp_server_config.go b/net/ghttp/ghttp_server_config.go index 85ed565a2..437b662e1 100644 --- a/net/ghttp/ghttp_server_config.go +++ b/net/ghttp/ghttp_server_config.go @@ -420,11 +420,10 @@ func (s *Server) SetHTTPSPort(port ...int) { // SetListener set the custom listener for the server. // It will overwrite the address you specified before. func (s *Server) SetListener(l net.Listener) error { - addrArray := gstr.SplitAndTrim(l.Addr().String(), ":") - port, err := strconv.Atoi(addrArray[len(addrArray)-1]) - if err != nil { - return err + if l == nil { + return gerror.NewCodef(gcode.CodeInvalidParameter, "listener is nil") } + port := (l.Addr().(*net.TCPAddr)).Port s.config.Address = fmt.Sprintf(":%d", port) s.config.Listeners = map[int]net.Listener{port: l} return nil @@ -435,16 +434,12 @@ func (s *Server) SetListener(l net.Listener) error { // If the listener's port not match the port provided in map, the method will return error. func (s *Server) SetListeners(listeners map[int]net.Listener) error { for k, v := range listeners { - addrArray := gstr.SplitAndTrim(v.Addr().String(), ":") - port, err := strconv.Atoi(addrArray[len(addrArray)-1]) - if err != nil { - return err - } - if port != k { + portIndeed := (v.Addr().(*net.TCPAddr)).Port + if portIndeed != k { return gerror.NewCodef( gcode.CodeInvalidParameter, - "listener specified by port %d should listen at port %d, but got port: %d", - k, k, port, + "listener specified by port %d listen at port %d indeed", + k, portIndeed, ) } } diff --git a/net/ghttp/ghttp_z_unit_feature_custom_listeners_test.go b/net/ghttp/ghttp_z_unit_feature_custom_listeners_test.go index 3efa2c193..8846db62d 100644 --- a/net/ghttp/ghttp_z_unit_feature_custom_listeners_test.go +++ b/net/ghttp/ghttp_z_unit_feature_custom_listeners_test.go @@ -76,8 +76,8 @@ func Test_SetWrongCustomListeners(t *testing.T) { s.SetAddr(":8199") ln, err := net.Listen("tcp", ":8299") t.AssertNil(err) - s.SetListeners(map[int]net.Listener{8199: ln}) - + err = s.SetListeners(map[int]net.Listener{8199: ln}) + t.AssertNQ(err, nil) s.Start() defer s.Shutdown() From b57cbacc82745aa5d1d3bf1fd8edccda2047b9a0 Mon Sep 17 00:00:00 2001 From: Macrow Date: Tue, 29 Mar 2022 23:22:23 +0800 Subject: [PATCH 6/8] refactor: method SetListener accepts slice of net.Listener and remove method SetListeners --- net/ghttp/ghttp_server_config.go | 39 ++++++--------- net/ghttp/ghttp_server_graceful.go | 9 +++- net/ghttp/ghttp_z_unit_feature_config_test.go | 9 ++-- ...tp_z_unit_feature_custom_listeners_test.go | 50 ++++++++----------- 4 files changed, 49 insertions(+), 58 deletions(-) diff --git a/net/ghttp/ghttp_server_config.go b/net/ghttp/ghttp_server_config.go index 437b662e1..d70aa299d 100644 --- a/net/ghttp/ghttp_server_config.go +++ b/net/ghttp/ghttp_server_config.go @@ -15,6 +15,7 @@ import ( "net" "net/http" "strconv" + "strings" "time" "github.com/gogf/gf/v2/internal/intlog" @@ -55,8 +56,7 @@ type ServerConfig struct { HTTPSAddr string `json:"httpsAddr"` // Listeners specifies the custom listeners. - // Listeners is a map, the key of map must specify the port of Address or HTTPSAddr. - Listeners map[int]net.Listener `json:"listeners"` + Listeners []net.Listener `json:"listeners"` // HTTPSCertPath specifies certification file path for HTTPS service. HTTPSCertPath string `json:"httpsCertPath"` @@ -418,32 +418,21 @@ func (s *Server) SetHTTPSPort(port ...int) { } // SetListener set the custom listener for the server. -// It will overwrite the address you specified before. -func (s *Server) SetListener(l net.Listener) error { - if l == nil { - return gerror.NewCodef(gcode.CodeInvalidParameter, "listener is nil") +func (s *Server) SetListener(listeners ...net.Listener) error { + if listeners == nil { + return gerror.NewCodef(gcode.CodeInvalidParameter, "listener can not be nil") } - port := (l.Addr().(*net.TCPAddr)).Port - s.config.Address = fmt.Sprintf(":%d", port) - s.config.Listeners = map[int]net.Listener{port: l} - return nil -} - -// SetListeners set the custom listeners for the server. -// The key of map should specify the port like: SetListeners(map[int]net.Listener{80: ln}). -// If the listener's port not match the port provided in map, the method will return error. -func (s *Server) SetListeners(listeners map[int]net.Listener) error { - for k, v := range listeners { - portIndeed := (v.Addr().(*net.TCPAddr)).Port - if portIndeed != k { - return gerror.NewCodef( - gcode.CodeInvalidParameter, - "listener specified by port %d listen at port %d indeed", - k, portIndeed, - ) + if len(listeners) > 0 { + ports := make([]string, len(listeners)) + for k, v := range listeners { + if v == nil { + return gerror.NewCodef(gcode.CodeInvalidParameter, "listener can not be nil") + } + ports[k] = fmt.Sprintf(":%d", (v.Addr().(*net.TCPAddr)).Port) } + s.config.Address = strings.Join(ports, ",") + s.config.Listeners = listeners } - s.config.Listeners = listeners return nil } diff --git a/net/ghttp/ghttp_server_graceful.go b/net/ghttp/ghttp_server_graceful.go index a62acd7e1..492ef6324 100644 --- a/net/ghttp/ghttp_server_graceful.go +++ b/net/ghttp/ghttp_server_graceful.go @@ -52,9 +52,14 @@ func (s *Server) newGracefulServer(address string, fd ...int) *gracefulServer { } if s.config.Listeners != nil { addrArray := gstr.SplitAndTrim(address, ":") - port, err := strconv.Atoi(addrArray[len(addrArray)-1]) + addrPort, err := strconv.Atoi(addrArray[len(addrArray)-1]) if err == nil { - gs.rawListener = s.config.Listeners[port] + for _, v := range s.config.Listeners { + listenerPort := (v.Addr().(*net.TCPAddr)).Port + if listenerPort == addrPort { + gs.rawListener = v + } + } } } return gs diff --git a/net/ghttp/ghttp_z_unit_feature_config_test.go b/net/ghttp/ghttp_z_unit_feature_config_test.go index f24db1506..a67cd0a16 100644 --- a/net/ghttp/ghttp_z_unit_feature_config_test.go +++ b/net/ghttp/ghttp_z_unit_feature_config_test.go @@ -8,6 +8,7 @@ package ghttp_test import ( "fmt" + "github.com/gogf/gf/v2/net/gtcp" "net" "testing" "time" @@ -24,12 +25,14 @@ import ( func Test_ConfigFromMap(t *testing.T) { gtest.C(t, func(t *gtest.T) { - ln, err := net.Listen("tcp", ":8199") + p, _ := gtcp.GetFreePort() + addr := fmt.Sprintf(":%d", p) + ln, err := net.Listen("tcp", addr) t.AssertNil(err) - listeners := map[int]net.Listener{8199: ln} + listeners := []net.Listener{ln} m := g.Map{ - "address": ":8199", + "address": addr, "listeners": listeners, "readTimeout": "60s", "indexFiles": g.Slice{"index.php", "main.php"}, diff --git a/net/ghttp/ghttp_z_unit_feature_custom_listeners_test.go b/net/ghttp/ghttp_z_unit_feature_custom_listeners_test.go index 8846db62d..c21c16469 100644 --- a/net/ghttp/ghttp_z_unit_feature_custom_listeners_test.go +++ b/net/ghttp/ghttp_z_unit_feature_custom_listeners_test.go @@ -7,6 +7,8 @@ package ghttp_test import ( + "fmt" + "github.com/gogf/gf/v2/net/gtcp" "github.com/gogf/gf/v2/test/gtest" "net" "testing" @@ -16,15 +18,19 @@ import ( "github.com/gogf/gf/v2/net/ghttp" ) -func Test_SetCustomListener(t *testing.T) { +func Test_SetSingleCustomListener(t *testing.T) { gtest.C(t, func(t *gtest.T) { - s := g.Server() + p, _ := gtcp.GetFreePort() + addr := fmt.Sprintf(":%d", p) + s := g.Server(g.Map{ + "address": addr, + }) s.Group("/", func(group *ghttp.RouterGroup) { group.GET("/test", func(r *ghttp.Request) { r.Response.Write("test") }) }) - ln, err := net.Listen("tcp", ":8199") + ln, err := net.Listen("tcp", addr) t.AssertNil(err) err = s.SetListener(ln) t.AssertNil(err) @@ -33,13 +39,10 @@ func Test_SetCustomListener(t *testing.T) { defer s.Shutdown() time.Sleep(100 * time.Millisecond) - s.GetListenedPort() - - t.AssertEQ(s.GetListenedPort(), 8199) }) } -func Test_SetRightCustomListeners(t *testing.T) { +func Test_SetMultipleCustomListeners(t *testing.T) { gtest.C(t, func(t *gtest.T) { s := g.Server() s.Group("/", func(group *ghttp.RouterGroup) { @@ -47,21 +50,22 @@ func Test_SetRightCustomListeners(t *testing.T) { r.Response.Write("test") }) }) - s.SetAddr(":8199") - ln, err := net.Listen("tcp", ":8199") - t.AssertNil(err) - err = s.SetListeners(map[int]net.Listener{8299: ln}) - t.AssertNE(err, nil) - err = s.SetListeners(map[int]net.Listener{8199: ln}) - t.AssertNil(err) + p1, _ := gtcp.GetFreePort() + p2, _ := gtcp.GetFreePort() + + ln1, err := net.Listen("tcp", fmt.Sprintf(":%d", p1)) + ln2, err := net.Listen("tcp", fmt.Sprintf(":%d", p2)) + err = s.SetListener(ln1, ln2) + t.AssertEQ(err, nil) s.Start() defer s.Shutdown() time.Sleep(100 * time.Millisecond) - s.GetListenedPort() - - t.AssertEQ(s.GetListenedPort(), 8199) + ports := []int{p1, p2} + for _, p := range s.GetListenedPorts() { + t.AssertIN(p, ports) + } }) } @@ -73,17 +77,7 @@ func Test_SetWrongCustomListeners(t *testing.T) { r.Response.Write("test") }) }) - s.SetAddr(":8199") - ln, err := net.Listen("tcp", ":8299") - t.AssertNil(err) - err = s.SetListeners(map[int]net.Listener{8199: ln}) + err := s.SetListener(nil) t.AssertNQ(err, nil) - s.Start() - defer s.Shutdown() - - time.Sleep(100 * time.Millisecond) - s.GetListenedPort() - - t.AssertEQ(s.GetListenedPort(), 8199) }) } From c7f51b8e77dd987a15478314c61722ab9d08e14f Mon Sep 17 00:00:00 2001 From: Macrow Date: Wed, 30 Mar 2022 14:55:03 +0800 Subject: [PATCH 7/8] fix: SetListener test data race error --- net/ghttp/ghttp_server_config.go | 4 +- ...tp_z_unit_feature_custom_listeners_test.go | 94 +++++++++++-------- 2 files changed, 58 insertions(+), 40 deletions(-) diff --git a/net/ghttp/ghttp_server_config.go b/net/ghttp/ghttp_server_config.go index d70aa299d..834c91d03 100644 --- a/net/ghttp/ghttp_server_config.go +++ b/net/ghttp/ghttp_server_config.go @@ -420,13 +420,13 @@ func (s *Server) SetHTTPSPort(port ...int) { // SetListener set the custom listener for the server. func (s *Server) SetListener(listeners ...net.Listener) error { if listeners == nil { - return gerror.NewCodef(gcode.CodeInvalidParameter, "listener can not be nil") + return gerror.NewCodef(gcode.CodeInvalidParameter, "SetListener failed: listener can not be nil") } if len(listeners) > 0 { ports := make([]string, len(listeners)) for k, v := range listeners { if v == nil { - return gerror.NewCodef(gcode.CodeInvalidParameter, "listener can not be nil") + return gerror.NewCodef(gcode.CodeInvalidParameter, "SetListener failed: listener can not be nil") } ports[k] = fmt.Sprintf(":%d", (v.Addr().(*net.TCPAddr)).Port) } diff --git a/net/ghttp/ghttp_z_unit_feature_custom_listeners_test.go b/net/ghttp/ghttp_z_unit_feature_custom_listeners_test.go index c21c16469..47c48c229 100644 --- a/net/ghttp/ghttp_z_unit_feature_custom_listeners_test.go +++ b/net/ghttp/ghttp_z_unit_feature_custom_listeners_test.go @@ -10,6 +10,8 @@ import ( "fmt" "github.com/gogf/gf/v2/net/gtcp" "github.com/gogf/gf/v2/test/gtest" + "github.com/gogf/gf/v2/text/gstr" + "github.com/gogf/gf/v2/util/guid" "net" "testing" "time" @@ -19,59 +21,75 @@ import ( ) func Test_SetSingleCustomListener(t *testing.T) { + p, _ := gtcp.GetFreePort() + ln, _ := net.Listen("tcp", fmt.Sprintf(":%d", p)) + s := g.Server(guid.S()) + s.Group("/", func(group *ghttp.RouterGroup) { + group.GET("/test", func(r *ghttp.Request) { + r.Response.Write("test") + }) + }) + + s.SetListener(ln) + + s.Start() + defer s.Shutdown() + + time.Sleep(100 * time.Millisecond) + gtest.C(t, func(t *gtest.T) { - p, _ := gtcp.GetFreePort() - addr := fmt.Sprintf(":%d", p) - s := g.Server(g.Map{ - "address": addr, - }) - s.Group("/", func(group *ghttp.RouterGroup) { - group.GET("/test", func(r *ghttp.Request) { - r.Response.Write("test") - }) - }) - ln, err := net.Listen("tcp", addr) - t.AssertNil(err) - err = s.SetListener(ln) - t.AssertNil(err) + c := g.Client() + c.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort())) - s.Start() - defer s.Shutdown() - - time.Sleep(100 * time.Millisecond) + t.Assert( + gstr.Trim(c.GetContent(ctx, "/test")), + "test", + ) }) } func Test_SetMultipleCustomListeners(t *testing.T) { - gtest.C(t, func(t *gtest.T) { - s := g.Server() - s.Group("/", func(group *ghttp.RouterGroup) { - group.GET("/test", func(r *ghttp.Request) { - r.Response.Write("test") - }) + p1, _ := gtcp.GetFreePort() + p2, _ := gtcp.GetFreePort() + + ln1, _ := net.Listen("tcp", fmt.Sprintf(":%d", p1)) + ln2, _ := net.Listen("tcp", fmt.Sprintf(":%d", p2)) + + s := g.Server(guid.S()) + s.Group("/", func(group *ghttp.RouterGroup) { + group.GET("/test", func(r *ghttp.Request) { + r.Response.Write("test") }) - p1, _ := gtcp.GetFreePort() - p2, _ := gtcp.GetFreePort() + }) - ln1, err := net.Listen("tcp", fmt.Sprintf(":%d", p1)) - ln2, err := net.Listen("tcp", fmt.Sprintf(":%d", p2)) - err = s.SetListener(ln1, ln2) - t.AssertEQ(err, nil) + s.SetListener(ln1, ln2) - s.Start() - defer s.Shutdown() + s.Start() + defer s.Shutdown() - time.Sleep(100 * time.Millisecond) - ports := []int{p1, p2} - for _, p := range s.GetListenedPorts() { - t.AssertIN(p, ports) - } + time.Sleep(100 * time.Millisecond) + + gtest.C(t, func(t *gtest.T) { + c := g.Client() + c.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p1)) + + t.Assert( + gstr.Trim(c.GetContent(ctx, "/test")), + "test", + ) + + c.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p2)) + + t.Assert( + gstr.Trim(c.GetContent(ctx, "/test")), + "test", + ) }) } func Test_SetWrongCustomListeners(t *testing.T) { gtest.C(t, func(t *gtest.T) { - s := g.Server() + s := g.Server(guid.S()) s.Group("/", func(group *ghttp.RouterGroup) { group.GET("/test", func(r *ghttp.Request) { r.Response.Write("test") From 0e0d2e1c45f13a971462dcffed00e3e3893ee5db Mon Sep 17 00:00:00 2001 From: Macrow Date: Fri, 1 Apr 2022 09:03:36 +0800 Subject: [PATCH 8/8] fix: break when finished set custom listener in newGracefulServer method --- net/ghttp/ghttp_server_graceful.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/ghttp/ghttp_server_graceful.go b/net/ghttp/ghttp_server_graceful.go index 492ef6324..91f6eac11 100644 --- a/net/ghttp/ghttp_server_graceful.go +++ b/net/ghttp/ghttp_server_graceful.go @@ -55,9 +55,9 @@ func (s *Server) newGracefulServer(address string, fd ...int) *gracefulServer { addrPort, err := strconv.Atoi(addrArray[len(addrArray)-1]) if err == nil { for _, v := range s.config.Listeners { - listenerPort := (v.Addr().(*net.TCPAddr)).Port - if listenerPort == addrPort { + if listenerPort := (v.Addr().(*net.TCPAddr)).Port; listenerPort == addrPort { gs.rawListener = v + break } } }