Skip to content

Commit 74e9b22

Browse files
committed
fix (server.Stop): graciously handle various Stop() states.
see: #45
1 parent 2ad3888 commit 74e9b22

File tree

2 files changed

+67
-16
lines changed

2 files changed

+67
-16
lines changed

server.go

+19-8
Original file line numberDiff line numberDiff line change
@@ -164,17 +164,28 @@ func (s *Server) Stop() error {
164164
const op = "gldap.(Server).Stop"
165165
s.mu.RLock()
166166
defer s.mu.RUnlock()
167-
if s.listener == nil {
168-
return fmt.Errorf("%s: no listener: %w", op, ErrInvalidState)
167+
168+
s.logger.Debug("shutting down")
169+
if s.listener == nil && s.shutdownCancel == nil {
170+
s.logger.Debug("nothing to do for shutdown")
171+
return nil
169172
}
170-
if s.shutdownCancel == nil {
171-
return fmt.Errorf("%s: no shutdown context cancel func: %w", op, ErrInvalidState)
173+
174+
if s.listener != nil {
175+
s.logger.Debug("closing listener")
176+
if err := s.listener.Close(); err != nil {
177+
switch {
178+
case !strings.Contains(err.Error(), "use of closed network connection"):
179+
return fmt.Errorf("%s: %w", op, err)
180+
default:
181+
s.logger.Debug("listener already closed")
182+
}
183+
}
172184
}
173-
if err := s.listener.Close(); err != nil {
174-
return fmt.Errorf("%s: %w", op, err)
185+
if s.shutdownCancel != nil {
186+
s.logger.Debug("shutdown cancel func")
187+
s.shutdownCancel()
175188
}
176-
s.logger.Debug("shutting down")
177-
s.shutdownCancel()
178189
s.logger.Debug("waiting on connections to close")
179190
s.connWg.Wait()
180191
s.logger.Debug("stopped")

server_internal_test.go

+48-8
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,27 @@ package gldap
22

33
import (
44
"context"
5+
"errors"
56
"fmt"
67
"net"
8+
"os"
9+
"strconv"
710
"testing"
811

12+
"github.com/hashicorp/go-hclog"
913
"github.com/stretchr/testify/assert"
1014
"github.com/stretchr/testify/require"
1115
)
1216

1317
func TestServer_Stop(t *testing.T) {
1418
t.Parallel()
19+
var testLogger hclog.Logger
20+
if ok, _ := strconv.ParseBool(os.Getenv("DEBUG")); ok {
21+
testLogger = hclog.New(&hclog.LoggerOptions{
22+
Name: "TestServer_Run-logger",
23+
Level: hclog.Debug,
24+
})
25+
}
1526
tests := []struct {
1627
name string
1728
server *Server
@@ -21,32 +32,40 @@ func TestServer_Stop(t *testing.T) {
2132
{
2233
name: "missing-listener",
2334
server: func() *Server {
24-
s, err := NewServer()
35+
s, err := NewServer(WithLogger(testLogger))
2536
require.NoError(t, err)
2637
s.mu.Lock()
2738
defer s.mu.Unlock()
2839
s.listener = nil
2940
return s
3041
}(),
31-
wantErr: true,
32-
wantErrContains: "no listener",
3342
},
3443
{
3544
name: "missing-cancel",
3645
server: func() *Server {
3746
p := freePort(t)
3847
l, err := net.Listen("tcp", fmt.Sprintf(":%d", p))
3948
require.NoError(t, err)
40-
s, err := NewServer()
49+
s, err := NewServer(WithLogger(testLogger))
4150
require.NoError(t, err)
4251
s.mu.Lock()
4352
defer s.mu.Unlock()
4453
s.listener = l
4554
s.shutdownCancel = nil
4655
return s
4756
}(),
48-
wantErr: true,
49-
wantErrContains: "no shutdown context cancel func",
57+
},
58+
{
59+
name: "nothing-to-do",
60+
server: func() *Server {
61+
s, err := NewServer(WithLogger(testLogger))
62+
require.NoError(t, err)
63+
s.mu.Lock()
64+
defer s.mu.Unlock()
65+
s.listener = nil
66+
s.shutdownCancel = nil
67+
return s
68+
}(),
5069
},
5170
{
5271
name: "listener-closed",
@@ -55,7 +74,7 @@ func TestServer_Stop(t *testing.T) {
5574
p := freePort(t)
5675
l, err := net.Listen("tcp", fmt.Sprintf(":%d", p))
5776
require.NoError(t, err)
58-
s, err := NewServer()
77+
s, err := NewServer(WithLogger(testLogger))
5978
require.NoError(t, err)
6079
s.mu.Lock()
6180
defer s.mu.Unlock()
@@ -64,8 +83,21 @@ func TestServer_Stop(t *testing.T) {
6483
l.Close()
6584
return s
6685
}(),
86+
},
87+
{
88+
name: "listener-close-err",
89+
server: func() *Server {
90+
_, cancel := context.WithCancel(context.Background())
91+
s, err := NewServer(WithLogger(testLogger))
92+
require.NoError(t, err)
93+
s.mu.Lock()
94+
defer s.mu.Unlock()
95+
s.listener = &mockListener{}
96+
s.shutdownCancel = cancel
97+
return s
98+
}(),
6799
wantErr: true,
68-
wantErrContains: "use of closed network connection",
100+
wantErrContains: "mockListener.Close error",
69101
},
70102
}
71103
for _, tc := range tests {
@@ -83,3 +115,11 @@ func TestServer_Stop(t *testing.T) {
83115
})
84116
}
85117
}
118+
119+
type mockListener struct {
120+
net.Listener
121+
}
122+
123+
func (*mockListener) Close() error {
124+
return errors.New("mockListener.Close error")
125+
}

0 commit comments

Comments
 (0)