/
wenex.go
217 lines (181 loc) · 4.72 KB
/
wenex.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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
package wenex
import (
"context"
"log"
"net"
"net/http"
"sync"
"time"
"github.com/nexcode/joneva"
)
// Wenex struct
type Wenex struct {
Router *Router
Logger func(string) *log.Logger
Config *joneva.Joneva
servers [2]*http.Server
}
// New return a new Wenex object:
//
// configFile: sets default config filename
// defaultConfig: contains default configuration parameters
// logWriter: interface for piping all logs to writer
// configOverride: overrides config values by values from specified files
//
// defaultConfig doesn't replace parameters declared in configuration file
// and writes new values to configuration file.
func New(configFile string, defaultConfig map[string]interface{}, logWriter LogWriter, configOverride ...string) (*Wenex, error) {
if configFile == "" {
configFile = "wenex.conf"
}
config, err := joneva.New(configFile, defaultConfig)
if err != nil {
return nil, err
}
if len(configOverride) != 0 {
for _, override := range configOverride {
if err = config.Load(override); err != nil {
return nil, err
}
}
}
wnx := &Wenex{
Router: newRouter(),
Config: config,
}
if wnx.Logger, err = NewLogger(config, logWriter); err != nil {
return nil, err
}
if wnx.servers, err = newServer(wnx); err != nil {
return nil, err
}
return wnx, nil
}
// ConnState specifies an optional callback function that is
// called when a client connection changes state. See the
// ConnState type and associated constants for details.
func (wnx *Wenex) ConnState(f func(net.Conn, http.ConnState)) {
for _, server := range wnx.servers {
if server != nil {
server.ConnState = f
}
}
}
// Run starts the web server. If an error occurs
// during the operation, the error will be returned.
// This method goes to asleep.
func (wnx *Wenex) Run() error {
if wnx.servers[0] == nil && wnx.servers[1] == nil {
return ErrNoServers
}
wnx.fixEmptyChain()
var (
err [2]error
wg sync.WaitGroup
)
if wnx.servers[0] != nil {
wg.Add(1)
go func() {
defer wg.Done()
err[0] = wnx.servers[0].ListenAndServe()
}()
}
if wnx.servers[1] != nil {
wg.Add(1)
go func() {
defer wg.Done()
err[1] = wnx.servers[1].ListenAndServeTLS("", "")
}()
}
wg.Wait()
for _, err := range err {
if err != nil {
return err
}
}
return nil
}
// Close immediately closes all active net.Listeners and any
// connections in state StateNew, StateActive, or StateIdle. For a
// graceful shutdown, use Shutdown.
//
// Close does not attempt to close (and does not even know about)
// any hijacked connections, such as WebSockets.
//
// Close returns any error returned from closing the Server's
// underlying Listener(s).
func (wnx *Wenex) Close() error {
var (
err [2]error
wg sync.WaitGroup
)
for i := 0; i <= 1; i++ {
if wnx.servers[i] != nil {
wg.Add(1)
go func(i int) {
defer wg.Done()
err[i] = wnx.servers[i].Close()
}(i)
}
}
wg.Wait()
for _, err := range err {
if err != nil {
return err
}
}
return nil
}
// Shutdown gracefully shuts down the server without interrupting any
// active connections. Shutdown works by first closing all open
// listeners, then closing all idle connections, and then waiting
// indefinitely for connections to return to idle and then shut down.
// If the provided context expires before the shutdown is complete,
// Shutdown returns the context's error, otherwise it returns any
// error returned from closing the Server's underlying Listener(s).
//
// When Shutdown is called, Serve, ListenAndServe, and
// ListenAndServeTLS immediately return ErrServerClosed. Make sure the
// program doesn't exit and waits instead for Shutdown to return.
//
// Shutdown does not attempt to close nor wait for hijacked
// connections such as WebSockets. The caller of Shutdown should
// separately notify such long-lived connections of shutdown and wait
// for them to close, if desired. See RegisterOnShutdown for a way to
// register shutdown notification functions.
//
// Once Shutdown has been called on a server, it may not be reused;
// future calls to methods such as Serve will return ErrServerClosed.
func (wnx *Wenex) Shutdown(timeout time.Duration) error {
var (
err [2]error
wg sync.WaitGroup
)
for i := 0; i <= 1; i++ {
if wnx.servers[i] != nil {
wg.Add(1)
go func(i int) {
defer wg.Done()
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
err[i] = wnx.servers[i].Shutdown(ctx)
}(i)
}
}
wg.Wait()
for _, err := range err {
if err != nil {
return err
}
}
return nil
}
func (wnx *Wenex) fixEmptyChain() {
for _, method := range wnx.Router.method {
for _, chain := range method {
if chain.handler == nil {
chain.handler = []http.Handler{http.NotFoundHandler()}
}
}
}
}