@@ -3,12 +3,15 @@ package xhttp
3
3
4
4
import (
5
5
"context"
6
+ "errors"
6
7
"log"
7
8
"net"
8
9
"net/http"
9
- "time"
10
-
11
10
"oss.terrastruct.com/util-go/xcontext"
11
+ "sync"
12
+
13
+ "sync/atomic"
14
+ "time"
12
15
)
13
16
14
17
func NewServer (log * log.Logger , h http.Handler ) * http.Server {
@@ -22,23 +25,56 @@ func NewServer(log *log.Logger, h http.Handler) *http.Server {
22
25
}
23
26
}
24
27
28
+ type safeServer struct {
29
+ * http.Server
30
+ running int32
31
+ mu sync.Mutex
32
+ }
33
+
34
+ func newSafeServer (s * http.Server ) * safeServer {
35
+ return & safeServer {
36
+ Server : s ,
37
+ }
38
+ }
39
+ func (s * safeServer ) ListenAndServe (l net.Listener ) error {
40
+ s .mu .Lock ()
41
+ defer s .mu .Unlock ()
42
+ if ! atomic .CompareAndSwapInt32 (& s .running , 0 , 1 ) {
43
+ return errors .New ("server is already running" )
44
+ }
45
+ defer atomic .StoreInt32 (& s .running , 0 )
46
+ return s .Serve (l )
47
+ }
48
+ func (s * safeServer ) Shutdown (ctx context.Context ) error {
49
+ s .mu .Lock ()
50
+ defer s .mu .Unlock ()
51
+ if atomic .LoadInt32 (& s .running ) == 0 {
52
+ return nil
53
+ }
54
+ return s .Server .Shutdown (ctx )
55
+ }
25
56
func Serve (ctx context.Context , shutdownTimeout time.Duration , s * http.Server , l net.Listener ) error {
26
57
s .BaseContext = func (net.Listener ) context.Context {
27
58
return ctx
28
59
}
29
-
30
- done := make (chan error , 1 )
60
+ ss := newSafeServer (s )
61
+ serverClosed := make (chan struct {})
62
+ var serverError error
31
63
go func () {
32
- done <- s .Serve (l )
64
+ serverError = ss .ListenAndServe (l )
65
+ close (serverClosed )
33
66
}()
34
-
35
67
select {
36
- case err := <- done :
37
- return err
68
+ case <- serverClosed :
69
+ return serverError
38
70
case <- ctx .Done ():
39
- ctx = xcontext .WithoutCancel (ctx )
40
- ctx , cancel := context .WithTimeout (ctx , shutdownTimeout )
71
+ shutdownCtx , cancel := context .WithTimeout (xcontext .WithoutCancel (ctx ), shutdownTimeout )
41
72
defer cancel ()
42
- return s .Shutdown (ctx )
73
+ err := ss .Shutdown (shutdownCtx )
74
+ <- serverClosed // Wait for server to exit
75
+ if err != nil {
76
+ return err
77
+ }
78
+ return serverError
43
79
}
44
80
}
0 commit comments