-
Notifications
You must be signed in to change notification settings - Fork 327
/
server.go
125 lines (99 loc) · 2.48 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
package server
import (
"context"
"net"
"net/http"
"github.com/cortezaproject/corteza/server/pkg/options"
"github.com/go-chi/chi/v5"
"go.uber.org/zap"
)
type (
server struct {
log *zap.Logger
opts *options.Options
endpoints []func(r chi.Router)
// last error
err error
demux *demux
}
)
const (
waiting uint32 = iota
active
shutdown
)
// New initializes new HTTP server with special powers
// Server is started as early as possible and with a special request handler
// that demultiplexes request to one of the configured routers according to the server state.
//
// Waiting state
// This is initial state with the ofllowing route handlers:
// - /version
// - /healthcheck
func New(log *zap.Logger, opts *options.Options) *server {
s := &server{
endpoints: make([]func(r chi.Router), 0),
log: log.Named("http"),
opts: opts,
}
s.demux = Demux(waiting, waitingRoutes(s.log.Named("waiting"), s.opts.HTTPServer))
s.demux.Router(shutdown, shutdownRoutes())
return s
}
func (s *server) LastError() error {
return s.err
}
func Test(o *options.Options) error {
listener, err := net.Listen("tcp", o.HTTPServer.Addr)
if err != nil {
return err
}
if err = listener.Close(); err != nil {
return err
}
return nil
}
// Activate reconfigures server to use active routes
func (s *server) Activate(mm ...func(chi.Router)) {
s.demux.Router(active, activeRoutes(s.log, mm, s.opts))
s.log.Debug("entering active state")
s.demux.State(active)
}
// Shutdown reconfigures server to use shutdown routes
func (s *server) Shutdown() {
s.log.Debug("entering shutdown state")
s.demux.State(shutdown)
}
func (s server) Serve(ctx context.Context) {
var (
listener net.Listener
)
s.log.Info(
"starting HTTP server",
zap.String("path-prefix", s.opts.HTTPServer.BaseUrl),
zap.String("address", s.opts.HTTPServer.Addr),
)
listener, s.err = net.Listen("tcp", s.opts.HTTPServer.Addr)
if s.err != nil {
s.log.Error("cannot start server", zap.Error(s.err))
return
}
go func() {
srv := http.Server{
Handler: s.demux,
// use root context as server's base context and as a basis for
// context for all requests
// this enables us to send cancellation down to every request
BaseContext: func(listener net.Listener) context.Context { return ctx },
}
s.err = srv.Serve(listener)
}()
<-ctx.Done()
if s.err == nil {
s.err = ctx.Err()
if s.err == context.Canceled {
s.err = nil
}
}
s.log.Info("HTTP server stopped", zap.Error(s.err))
}