This repository has been archived by the owner on Feb 16, 2018. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 3
/
server.go
108 lines (92 loc) · 2.37 KB
/
server.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
// Package mannersagain combines manners and goagain to provide graceful hot
// restarting of net/http servers.
package mannersagain
import (
"errors"
"log"
"net"
"net/http"
"os"
"time"
"github.com/titanous/goagain"
"github.com/titanous/manners"
)
func newListener(l net.Listener) net.Listener {
return listener{Listener: l, closed: make(chan struct{})}
}
type listener struct {
net.Listener
closed chan struct{}
}
var ErrClosed = errors.New("mannersagain: listener has been gracefully closed")
func (l listener) Accept() (net.Conn, error) {
for {
select {
case <-l.closed:
return nil, ErrClosed
default:
}
// Set a deadline so Accept doesn't block forever, which gives
// us an opportunity to stop gracefully.
l.Listener.(*net.TCPListener).SetDeadline(time.Now().Add(100 * time.Millisecond))
c, err := l.Listener.Accept()
if opErr, ok := err.(*net.OpError); ok && opErr.Timeout() {
continue
}
return c, err
}
}
func (l listener) Close() error {
close(l.closed)
return nil
}
func ListenAndServe(addr string, handler http.Handler) error {
var gl *manners.GracefulListener
srv := manners.NewServer()
done := make(chan struct{})
serve := func(l net.Listener) {
srv.Serve(l, handler)
close(done)
}
// Attempt to inherit a listener from our parent
l, err := goagain.Listener()
if err != nil {
// We don't have an inherited listener, create a new one
l, err = net.Listen("tcp", addr)
if err != nil {
return err
}
log.Println("Listening on", l.Addr())
gl = manners.NewListener(newListener(l), srv)
go serve(gl)
} else {
log.Println("Resuming listening on", l.Addr())
gl = manners.NewListener(newListener(l), srv)
go serve(gl)
if err := goagain.Kill(); nil != err {
return err
}
}
// Block the main goroutine awaiting signals.
sig, err := goagain.Wait(l)
if err != nil {
return err
}
// Wait returns one of SIGINT, SIGTERM, SIGQUIT, SIGUSR2
// We should stop gracefully if we receive one of the second two
if sig != goagain.SIGINT && sig != goagain.SIGTERM {
// Stop accepting new connections
gl.Close()
// Wait for all existing connections to complete
<-done
}
if goagain.Strategy == goagain.Double && sig == goagain.SIGUSR2 {
// If we received SIGUSR2, re-exec the parent process.
if err := goagain.Exec(l); err != nil {
return err
}
}
// We were told to exit, so do it!
os.Exit(0)
return nil
}