/
server.go
129 lines (108 loc) · 2.89 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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
package web
import (
"context"
"io"
"net"
"net/http"
"os"
"os/signal"
"syscall"
"time"
"github.com/fireworq/fireworq/config"
"github.com/gorilla/mux"
serverstarter "github.com/lestrrat-go/server-starter/listener"
"github.com/rs/zerolog"
"github.com/rs/zerolog/hlog"
"github.com/rs/zerolog/log"
)
func graceful(server *http.Server, shutdownTimeout time.Duration) (time.Duration, error) {
sigc := make(chan os.Signal, 1)
signal.Notify(sigc, syscall.SIGTERM, syscall.SIGINT, syscall.SIGHUP)
sig := <-sigc
log.Info().Msgf(
"Received signal %q; shutting down gracefully in %s ...",
sig,
shutdownTimeout,
)
deadline := time.Now().Add(shutdownTimeout)
ctx, cancel := context.WithTimeout(context.Background(), shutdownTimeout)
defer cancel()
errc := make(chan error)
go func() { errc <- server.Shutdown(ctx) }()
select {
case sig := <-sigc:
log.Info().Msgf("Received second signal %q; shutdown now", sig)
cancel()
return 0, server.Close()
case err := <-errc:
return time.Until(deadline), err
}
}
type server struct {
addrs []net.Addr
makeHandler func(h http.Handler) http.Handler
mux *mux.Router
}
func newServer(out io.Writer) *server {
tag := config.Get("access_log_tag")
logger := zerolog.New(out).With().
Timestamp().
Logger()
accessLog := hlog.AccessHandler(func(r *http.Request, status, size int, duration time.Duration) {
hlog.FromRequest(r).Info().
Str("tag", tag).
Str("method", r.Method).
Str("url", r.URL.String()).
Int("status", status).
Dur("duration", duration).
Msg("")
})
remoteAddr := hlog.RemoteAddrHandler("remote_addr")
ua := hlog.UserAgentHandler("user_agent")
s := &server{
makeHandler: func(h http.Handler) http.Handler {
return hlog.NewHandler(logger)(accessLog(remoteAddr(ua(h))))
},
mux: mux.NewRouter(),
}
return s
}
func (s *server) start() (*http.Server, error) {
server := &http.Server{Handler: s.mux}
listeners, err := serverstarter.ListenAll()
if err == serverstarter.ErrNoListeningTarget {
log.Info().Msg("Starting a server ...")
ln, err := net.Listen("tcp", config.Get("bind"))
if err != nil {
return nil, err
}
listeners = []net.Listener{ln}
} else if err != nil {
return nil, err
} else {
log.Info().Msg("Starting a server under start_server ...")
}
addrs := make([]net.Addr, 0)
for _, ln := range listeners {
ln := ln
addr := ln.Addr()
addrs = append(addrs, addr)
log.Info().Msgf("Listening on %s", addr.String())
go func() {
err := server.Serve(ln)
log.Info().Msg(err.Error())
}()
}
s.addrs = addrs
return server, nil
}
func (s *server) startGracefully(shutdownTimeout time.Duration) (time.Duration, error) {
server, err := s.start()
if err != nil {
return 0, err
}
return graceful(server, shutdownTimeout)
}
func (s *server) handle(pattern string, h func(http.ResponseWriter, *http.Request) error) {
s.mux.Handle(pattern, s.makeHandler(handler(h)))
}