-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathrun.go
More file actions
134 lines (119 loc) · 3.12 KB
/
run.go
File metadata and controls
134 lines (119 loc) · 3.12 KB
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
//revive:disable:package-comments
package server
import (
"context"
"crypto/tls"
"errors"
"fmt"
"net"
"net/http"
"os"
"os/signal"
)
const (
// LocalHost is the name of the localhost
LocalHost = "localhost"
// DefaultPort is the default port used for service
DefaultPort = "4430"
// HeartBeatName is the name of the heartbeat service
HeartBeatName = "heartbeat"
// ServerContextCancelled denotes when a server run returns because its context is cancelled
ServerContextCancelled = "Server context cancelled"
// ServerReceivedInterrupt denotes when a server run returns because its context is cancelled
ServerReceivedInterrupt = "Server received interrupt signal"
)
var (
// ErrContextCancelled - a server returns because its context is cancelled
ErrContextCancelled = fmt.Errorf(ServerContextCancelled)
// ErrReceivedInterrupt - a server returns because it received an interrupt signal
ErrReceivedInterrupt = fmt.Errorf(ServerReceivedInterrupt)
)
// Error contains the errors applicable from running and stopping a server
type Error struct {
Context error
Close error
}
// Starts up server
func start(
certs *[]tls.Certificate,
listener net.Listener,
mux *http.ServeMux,
internalError chan error,
) {
c := *certs
var err error
if len(c) > 0 {
tlsConfig := &tls.Config{
Certificates: c,
}
listener = tls.NewListener(listener, tlsConfig)
err = http.ServeTLS(listener, mux, "", "")
} else {
err = http.Serve(listener, mux)
}
if err != nil {
opError, isOpError := err.(*net.OpError)
if isOpError && errors.Is(opError.Err, net.ErrClosed) {
err = nil
}
}
internalError <- err
close(internalError)
}
// Awaits occurrence of 3 possible scenarios:
//
// 1. The context is cancelled
// 2. An OS SIGINT is sent
// 3. The servers stop (intentional or through fatal error)
func await(
ctx context.Context,
listener net.Listener,
internalError chan error,
reportedError chan Error,
) {
interrupt := make(chan os.Signal, 1)
signal.Notify(interrupt, os.Interrupt)
var err error
select {
case <-ctx.Done():
err = ErrContextCancelled
case <-interrupt:
err = ErrReceivedInterrupt
close(interrupt)
case err = <-internalError:
}
reportedError <- Error{err, listener.Close()}
close(reportedError)
}
// Run executes the main server loop in a goroutine
//
// It allows consumer cancellation through the context and server-side cancellation
// notification via the returned `reportedError` channel
//
// Fatal errors will be sent to the returned channel and the server will shutdown
func Run(
ctx context.Context,
certs *[]tls.Certificate,
mux *http.ServeMux,
portEnvKey string,
) (address string, reportedError chan Error) {
internalError := make(chan error, 0)
reportedError = make(chan Error, 1)
port, set := os.LookupEnv(portEnvKey)
if !set {
port = DefaultPort
}
address = fmt.Sprintf("%v:%v", LocalHost, port)
if mux == nil {
mux = http.DefaultServeMux
}
listener, err := net.Listen("tcp", address)
if err != nil {
reportedError <- Error{err, nil}
close(reportedError)
return
}
go start(certs, listener, mux, internalError)
go await(ctx, listener, internalError, reportedError)
return
}