-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathapplication.go
executable file
·166 lines (133 loc) · 4.69 KB
/
application.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
package nibbler
import (
"context"
"github.com/gorilla/mux"
"github.com/micro/go-micro/config"
"net/http"
"os"
"os/signal"
"strconv"
"syscall"
)
type Extension interface {
// GetName returns a static name for the extension, primarily for logging purposes
GetName() string
// Init should handle the initialization of the extension with the knowledge that the Router and other Extensions
// are not likely to be initialized yet
Init(app *Application) error
// PostInit should handle all initialization that could not be handled in Init. Specifically, anything requiring
// the Router and other Extensions to be initialized
PostInit(app *Application) error
// Destroy handles application shutdown, with the most recently-initialized extensions destroyed first
Destroy(app *Application) error
}
// Logger is a generic interface to reflect logging output at various levels
type Logger interface {
Trace(message ...string)
Debug(message ...string)
Error(message ...string)
Info(message ...string)
Warn(message ...string)
}
// Configuration is the composite of other configs, including values directly from external sources (Raw)
type Configuration struct {
Headers HeaderConfiguration
Port int
Raw config.Config
ApiPrefix string
StaticDirectory string
}
// HeaderConfiguration controls settings for request/response headers
type HeaderConfiguration struct {
AccessControlAllowHeaders string
AccessControlAllowMethods string
AccessControlAllowOrigin string
}
// Application stores the state of the running application
type Application struct {
Config *Configuration
Logger Logger
Router *mux.Router
extensions []Extension
stopSignal chan os.Signal
}
func (ac *Application) Init(config *Configuration, logger Logger, extensions []Extension) error {
ac.Config = config
ac.Logger = logger
ac.extensions = extensions
// prepare a general-use error variable
var err error
// initialize all extensions
for _, x := range extensions {
// if any error occurred, return the error and stop processing
if err = LogErrorNonNil(logger, x.Init(ac)); err != nil {
return err
} else {
ac.Logger.Info("ran Init on extension \"" + x.GetName() + "\"")
}
}
// if a port is provided, allocate a router for the application
if ac.Config.Port != 0 {
ac.Router = mux.NewRouter()
}
// call post-init on extensions (if applicable, a router will be available to extensions now)
for _, x := range extensions {
// if any error occurred, return the error and stop processing
if err = LogErrorNonNil(logger, x.PostInit(ac), "while running PostInit on extension \"" + x.GetName() + "\""); err != nil {
return err
} else {
ac.Logger.Info("ran PostInit on extension \"" + x.GetName() + "\"")
}
}
// if a port was provided, set up the static directory routing
if ac.Config.Port != 0 {
ac.Router.PathPrefix("/").Handler(http.FileServer(http.Dir(config.StaticDirectory)))
http.Handle("/", ac.Router)
}
return nil
}
// Run will put the app into its running state
func (ac *Application) Run() error {
var err error
// allocate and prep signal channel (listen for some stop signals from the OS)
ac.stopSignal = make(chan os.Signal, 1)
signal.Notify(ac.stopSignal, syscall.SIGINT, syscall.SIGTERM)
if ac.Config.Port != 0 {
// allocate a server
h := &http.Server{Addr: ":" + strconv.Itoa(ac.Config.Port), Handler: nil}
// run our server listener in a goroutine
go func() {
// log that we're listening and state the port
ac.Logger.Info("listening on " + strconv.Itoa((*ac.Config).Port))
// listen (this blocks) - log an error if it happened (handle ErrServerClosed error)
if err := h.ListenAndServe(); err != nil && err != http.ErrServerClosed {
LogFatalNonNil(ac.Logger, h.ListenAndServe(), "failed to initialize server")
}
}()
// wait for a signal
<-ac.stopSignal
// shut down the server
ac.Logger.Info("shutting down")
// log a shutdown error (factor into return value later)
if err = LogErrorNonNil(ac.Logger, h.Shutdown(context.Background()), "while shutting down"); err != nil {
ac.Logger.Error(err.Error())
}
} else {
// wait for a signal
<-ac.stopSignal
ac.Logger.Info("shutting down")
}
// destroy extensions in reverse order (keep going on error to try to close as much as we can)
for i := range ac.extensions {
x := ac.extensions[len(ac.extensions)-i-1]
destroyErr := LogErrorNonNil(ac.Logger, x.Destroy(ac), "while destroying extension \"" + x.GetName() + "\"")
if destroyErr != nil {
err = destroyErr
} else {
ac.Logger.Info("destroyed extension \"" + x.GetName() + "\"")
}
}
ac.Logger.Info("shutdown complete")
// return any (the latest) extension destroy error (all are logged)
return err
}