This is an automated email from the ASF dual-hosted git repository.

Alanxtl pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/dubbo-go.git


The following commit(s) were added to refs/heads/develop by this push:
     new da5838678 fix(triple): filter http.ErrServerClosed in single-protocol 
server shutdown (#3671)
da5838678 is described below

commit da5838678668437077f09e55f7f9fac9697a6c3c
Author: Li Zining <[email protected]>
AuthorDate: Thu Aug 20 13:13:11 2026 +0800

    fix(triple): filter http.ErrServerClosed in single-protocol server shutdown 
(#3671)
    
    * fix(triple): filter http.ErrServerClosed in single-protocol server 
shutdown
    
    startHttp2 and startHttp3 returned the net/http serve error verbatim, so a 
normal Stop surfaced http.ErrServerClosed from Server.Run and the outer 
startTransport goroutine logged a spurious "server serve failed" error. The 
dual-protocol startHttp2AndHttp3 already swallowed this normal-shutdown signal 
inside its errgroup. Filter it in both single-protocol paths so Run returns nil 
after a clean shutdown, matching the dual-protocol behavior. Only 
http.ErrServerClosed is swallowed; genuin [...]
    
    Fixes: #3670
    Signed-off-by: lizining <[email protected]>
    
    * test(triple): assert clean shutdown in single-protocol lifecycle tests
    
    The StartAndStop tests previously asserted that Run returns 
http.ErrServerClosed after a normal Stop, pinning the leak as expected 
behavior. Switch the six assertions to NoError and fold the now-uniform if/else 
in TestServer_RepeatedStartStop into a single assertion. Add 
TestServerRunReturnsBindErrorWhenPortInUse and 
TestServerRunHTTP3ReturnsBindErrorWhenPortInUse, which assert that genuine 
serve errors (port conflicts) still propagate, guarding the filter boundary. 
They fail on the p [...]
    
    Signed-off-by: lizining <[email protected]>
    
    ---------
    
    Signed-off-by: lizining <[email protected]>
---
 protocol/triple/triple_protocol/server.go          | 12 ++++--
 .../triple_protocol/server_lifecycle_test.go       | 50 +++++++++++++++-------
 2 files changed, 44 insertions(+), 18 deletions(-)

diff --git a/protocol/triple/triple_protocol/server.go 
b/protocol/triple/triple_protocol/server.go
index 10cde267a..41b12edb8 100644
--- a/protocol/triple/triple_protocol/server.go
+++ b/protocol/triple/triple_protocol/server.go
@@ -215,8 +215,10 @@ func (s *Server) startHttp2(tlsConf *tls.Config) error {
        } else {
                err = srv.ListenAndServe()
        }
-
-       return err
+       if err != nil && err != http.ErrServerClosed {
+               return err
+       }
+       return nil
 }
 
 func (s *Server) startHttp3(tlsConf *tls.Config) error {
@@ -246,7 +248,11 @@ func (s *Server) startHttp3(tlsConf *tls.Config) error {
 
        logger.Debugf("[Triple][Server] triple HTTP/3 Server starting on %v", 
s.addr)
 
-       return s.http3Srv.Load().ListenAndServe()
+       err = s.http3Srv.Load().ListenAndServe()
+       if err != nil && err != http.ErrServerClosed {
+               return err
+       }
+       return nil
 }
 
 func (s *Server) startHttp2AndHttp3(tlsConf *tls.Config) error {
diff --git a/protocol/triple/triple_protocol/server_lifecycle_test.go 
b/protocol/triple/triple_protocol/server_lifecycle_test.go
index 2e6c39d4d..25b7a4442 100644
--- a/protocol/triple/triple_protocol/server_lifecycle_test.go
+++ b/protocol/triple/triple_protocol/server_lifecycle_test.go
@@ -28,7 +28,6 @@ import (
        "errors"
        "math/big"
        "net"
-       "net/http"
        "syscall"
        "testing"
        "time"
@@ -175,7 +174,7 @@ func TestServer_HTTP2_StartAndStop(t *testing.T) {
        require.Nil(t, srv.http3Srv.Load())
 
        require.NoError(t, srv.Stop())
-       require.ErrorIs(t, waitForServerExit(t, errCh, 5*time.Second), 
http.ErrServerClosed)
+       require.NoError(t, waitForServerExit(t, errCh, 5*time.Second))
 }
 
 func TestServer_HTTP2_StartAndStopWithTLS(t *testing.T) {
@@ -186,7 +185,7 @@ func TestServer_HTTP2_StartAndStopWithTLS(t *testing.T) {
        require.Nil(t, srv.http3Srv.Load())
 
        require.NoError(t, srv.Stop())
-       require.ErrorIs(t, waitForServerExit(t, errCh, 5*time.Second), 
http.ErrServerClosed)
+       require.NoError(t, waitForServerExit(t, errCh, 5*time.Second))
 }
 
 func TestServer_HTTP3_StartAndStop(t *testing.T) {
@@ -200,7 +199,7 @@ func TestServer_HTTP3_StartAndStop(t *testing.T) {
        require.Nil(t, srv.httpSrv.Load())
 
        require.NoError(t, srv.Stop())
-       require.ErrorIs(t, waitForServerExit(t, errCh, 5*time.Second), 
http.ErrServerClosed)
+       require.NoError(t, waitForServerExit(t, errCh, 5*time.Second))
 }
 
 func TestServer_HTTP2AndHTTP3_StartAndStop(t *testing.T) {
@@ -215,8 +214,6 @@ func TestServer_HTTP2AndHTTP3_StartAndStop(t *testing.T) {
        require.NotNil(t, srv.http3Srv.Load())
 
        require.NoError(t, srv.Stop())
-       // startHttp2AndHttp3 swallows http.ErrServerClosed inside the errgroup,
-       // so Run returns nil after the servers are closed.
        require.NoError(t, waitForServerExit(t, errCh, 5*time.Second))
 }
 
@@ -230,7 +227,7 @@ func TestServer_HTTP2_StartAndGracefulStop(t *testing.T) {
        graceCtx, cancel := context.WithTimeout(context.Background(), 
constant.DefaultGracefulShutdownTimeout)
        defer cancel()
        require.NoError(t, srv.GracefulStop(graceCtx))
-       require.ErrorIs(t, waitForServerExit(t, errCh, 5*time.Second), 
http.ErrServerClosed)
+       require.NoError(t, waitForServerExit(t, errCh, 5*time.Second))
 }
 
 func TestServer_HTTP3_StartAndGracefulStop(t *testing.T) {
@@ -246,7 +243,7 @@ func TestServer_HTTP3_StartAndGracefulStop(t *testing.T) {
        graceCtx, cancel := context.WithTimeout(context.Background(), 
constant.DefaultGracefulShutdownTimeout)
        defer cancel()
        require.NoError(t, srv.GracefulStop(graceCtx))
-       require.ErrorIs(t, waitForServerExit(t, errCh, 5*time.Second), 
http.ErrServerClosed)
+       require.NoError(t, waitForServerExit(t, errCh, 5*time.Second))
 }
 
 func TestServer_HTTP2AndHTTP3_StartAndGracefulStop(t *testing.T) {
@@ -263,8 +260,6 @@ func TestServer_HTTP2AndHTTP3_StartAndGracefulStop(t 
*testing.T) {
        graceCtx, cancel := context.WithTimeout(context.Background(), 
constant.DefaultGracefulShutdownTimeout)
        defer cancel()
        require.NoError(t, srv.GracefulStop(graceCtx))
-       // startHttp2AndHttp3 swallows http.ErrServerClosed inside the errgroup,
-       // so Run returns nil after the servers are closed.
        require.NoError(t, waitForServerExit(t, errCh, 5*time.Second))
 }
 
@@ -334,11 +329,36 @@ func TestServer_RepeatedStartStop(t *testing.T) {
                        }
 
                        require.NoError(t, srv.Stop())
-                       if tc.protocol == constant.CallHTTP2AndHTTP3 {
-                               require.NoError(t, waitForServerExit(t, errCh, 
5*time.Second))
-                       } else {
-                               require.ErrorIs(t, waitForServerExit(t, errCh, 
5*time.Second), http.ErrServerClosed)
-                       }
+                       require.NoError(t, waitForServerExit(t, errCh, 
5*time.Second))
                }
        }
 }
+
+// TestServerRunReturnsBindErrorWhenPortInUse verifies that Run propagates a
+// genuine serve error (here a TCP port conflict) instead of swallowing it
+// together with http.ErrServerClosed. It guards the boundary of the shutdown
+// filter: only the normal-closure signal must be suppressed.
+func TestServerRunReturnsBindErrorWhenPortInUse(t *testing.T) {
+       l, err := net.Listen("tcp", "127.0.0.1:0")
+       require.NoError(t, err)
+       defer l.Close()
+
+       srv := NewServer(l.Addr().String(), &global.TripleConfig{})
+       err = srv.Run(constant.CallHTTP2, nil)
+       require.Error(t, err)
+       require.True(t, isAddrInUse(err), "expected EADDRINUSE, got %v", err)
+}
+
+// TestServerRunHTTP3ReturnsBindErrorWhenPortInUse verifies the same boundary
+// for the HTTP/3 path, where the QUIC endpoint fails to bind the occupied
+// UDP port.
+func TestServerRunHTTP3ReturnsBindErrorWhenPortInUse(t *testing.T) {
+       pc, err := net.ListenPacket("udp", "127.0.0.1:0")
+       require.NoError(t, err)
+       defer pc.Close()
+
+       srv := NewServer(pc.LocalAddr().String(), &global.TripleConfig{})
+       err = srv.Run(constant.CallHTTP3, newTestTLSConfig(t))
+       require.Error(t, err)
+       require.True(t, isAddrInUse(err), "expected EADDRINUSE, got %v", err)
+}

Reply via email to