...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package baseapp
16
17 import (
18 "context"
19 "encoding/json"
20 "fmt"
21 "net/http"
22 "os"
23 "os/signal"
24 "strconv"
25 "sync"
26 "syscall"
27
28 "github.com/pkg/errors"
29 "github.com/rcrowley/go-metrics"
30 "github.com/rs/zerolog"
31 "goji.io"
32 )
33
34
35
36 type Server struct {
37 config HTTPConfig
38 middleware []func(http.Handler) http.Handler
39 logger zerolog.Logger
40 mux *goji.Mux
41 server *http.Server
42
43 registry metrics.Registry
44
45
46 initFns []func(*Server)
47 init sync.Once
48 }
49
50
51 type Param func(b *Server) error
52
53
54 func NewServer(c HTTPConfig, params ...Param) (*Server, error) {
55 logger := zerolog.Nop()
56 base := &Server{
57 config: c,
58 middleware: nil,
59 logger: logger,
60 mux: goji.NewMux(),
61 registry: metrics.DefaultRegistry,
62 }
63
64 for _, p := range params {
65 if err := p(base); err != nil {
66 return base, err
67 }
68 }
69
70 if base.middleware == nil {
71 base.middleware = DefaultMiddleware(base.logger, base.registry)
72 }
73
74 for _, middleware := range base.middleware {
75 base.mux.Use(middleware)
76 }
77
78 if base.server == nil {
79 base.server = &http.Server{}
80 }
81
82 if base.server.Addr == "" {
83 addr := c.Address + ":" + strconv.Itoa(c.Port)
84 base.server.Addr = addr
85 }
86
87 if base.server.Handler == nil {
88 base.server.Handler = base.mux
89 }
90
91 return base, nil
92 }
93
94
95 func (s *Server) HTTPConfig() HTTPConfig {
96 return s.config
97 }
98
99
100 func (s *Server) HTTPServer() *http.Server {
101 return s.server
102 }
103
104
105 func (s *Server) Mux() *goji.Mux {
106 return s.mux
107 }
108
109
110 func (s *Server) Logger() zerolog.Logger {
111 return s.logger
112 }
113
114
115 func (s *Server) Registry() metrics.Registry {
116 return s.registry
117 }
118
119
120 func (s *Server) start() error {
121 s.init.Do(func() {
122 for _, fn := range s.initFns {
123 fn(s)
124 }
125 })
126
127 addr := s.config.Address + ":" + strconv.Itoa(s.config.Port)
128 s.logger.Info().Msgf("Server listening on %s", addr)
129
130 tlsConfig := s.config.TLSConfig
131 if tlsConfig != nil {
132 return s.server.ListenAndServeTLS(tlsConfig.CertFile, tlsConfig.KeyFile)
133 }
134
135 return s.server.ListenAndServe()
136 }
137
138
139 func (s *Server) Start() error {
140
141 if s.config.ShutdownWaitTime == nil {
142 return s.start()
143 }
144
145 quit := make(chan error)
146 go func() {
147 if err := s.start(); err != nil {
148 quit <- err
149 }
150 }()
151
152
153 interrupt := make(chan os.Signal, 2)
154 signal.Notify(interrupt, syscall.SIGINT, syscall.SIGTERM)
155
156 select {
157 case <-interrupt:
158 s.logger.Info().Msg("Caught interrupt, gracefully shutting down")
159 case err := <-quit:
160 if err != http.ErrServerClosed {
161 return err
162 }
163 }
164
165 ctx, cancel := context.WithTimeout(context.Background(), *s.config.ShutdownWaitTime)
166 defer cancel()
167 return errors.Wrap(s.HTTPServer().Shutdown(ctx), "Failed shutting down gracefully")
168 }
169
170
171 func WriteJSON(w http.ResponseWriter, status int, obj interface{}) {
172 w.Header().Set("Content-Type", "application/json")
173
174 b, err := json.Marshal(obj)
175 if err != nil {
176 w.WriteHeader(http.StatusInternalServerError)
177 _, _ = fmt.Fprintf(w, `{"error": %s}`, strconv.Quote(err.Error()))
178 } else {
179 w.WriteHeader(status)
180 _, _ = w.Write(b)
181 }
182 }
183
View as plain text