-
Notifications
You must be signed in to change notification settings - Fork 0
/
server.go
147 lines (121 loc) · 3.24 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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
package gsrv
import (
"context"
"errors"
"fmt"
"net"
"net/http"
"time"
"google.golang.org/grpc"
)
const defaultTimeout = 5 * time.Second
type Server struct {
address string
listener net.Listener
timeout time.Duration
logger Logger
}
// Initializes new server and tries to create net listener on provided address if not specified.
func New(address string, opts ...Option) (*Server, error) {
s := &Server{
address: address,
timeout: defaultTimeout,
logger: newnopLogger(),
}
for _, opt := range opts {
opt(s)
}
if s.listener == nil {
listener, err := net.Listen("tcp", address)
if err != nil {
return nil, fmt.Errorf("Failed to create listener on %s: %w", address, err)
}
s.listener = listener
}
return s, nil
}
type Option func(*Server)
func WithListener(l net.Listener) Option {
return func(s *Server) {
s.listener = l
}
}
func WithLogger(l Logger) Option {
return func(s *Server) {
s.logger = l
}
}
func WithTimeout(t time.Duration) Option {
return func(s *Server) {
s.timeout = t
}
}
type Logger interface {
Info(args ...interface{})
Debug(args ...interface{})
Infof(template string, args ...interface{})
Debugf(template string, args ...interface{})
}
type nopLogger struct{}
// Creates a new dummy non-operate logger that never prints any messages.
func newnopLogger() *nopLogger {
return &nopLogger{}
}
func (l *nopLogger) Info(args ...interface{}) {}
func (l *nopLogger) Debug(args ...interface{}) {}
func (l *nopLogger) Infof(template string, args ...interface{}) {}
func (l *nopLogger) Debugf(template string, args ...interface{}) {}
// Serves HTTP server with a graceful shutdown on provided context done and shutdown timeout.
func (s *Server) ServeHTTP(ctx context.Context, handler http.Handler) error {
s.logger.Info("Listening HTTP on ", s.address)
srv := &http.Server{
Handler: handler,
}
errCh := make(chan error, 1)
go func() {
<-ctx.Done()
s.logger.Debug("Server context closed")
shutdownCtx, done := context.WithTimeout(context.Background(), s.timeout)
defer done()
s.logger.Debug("Shutting down server")
if err := srv.Shutdown(shutdownCtx); err != nil {
select {
case errCh <- err:
default:
}
}
}()
// This will block until the provided context is closed.
if err := srv.Serve(s.listener); err != nil && !errors.Is(err, http.ErrServerClosed) {
return fmt.Errorf("Failed to serve: %w", err)
}
s.logger.Debugf("Server stopped")
select {
case err := <-errCh:
return fmt.Errorf("Failed to shutdown: %w", err)
default:
return nil
}
}
// Serves gRPC server with a graceful shutdown on provided context done.
func (s *Server) ServeGRPC(ctx context.Context, srv *grpc.Server) error {
s.logger.Info("Listening GRPC on ", s.address)
errCh := make(chan error, 1)
go func() {
<-ctx.Done()
s.logger.Debug("Server context closed")
s.logger.Debug("Shutting down server")
srv.GracefulStop()
}()
if err := srv.Serve(s.listener); err != nil && !errors.Is(err, grpc.ErrServerStopped) {
return fmt.Errorf("Failed to serve: %w", err)
}
s.logger.Debugf("Server stopped")
// Return any errors that happened during shutdown.
select {
case err := <-errCh:
return fmt.Errorf("Failed to shutdown: %w", err)
default:
return nil
}
}