-
Notifications
You must be signed in to change notification settings - Fork 31
/
webgo.go
208 lines (177 loc) · 5.42 KB
/
webgo.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
/*
Package webgo is a lightweight framework for building web apps. It has a multiplexer,
middleware plugging mechanism & context management of its own. The primary goal
of webgo is to get out of the developer's way as much as possible. i.e. it does
not enforce you to build your app in any particular pattern, instead just helps you
get all the trivial things done faster and easier.
e.g.
1. Getting named URI parameters.
2. Multiplexer for regex matching of URI and such.
3. Inject special app level configurations or any such objects to the request context as required.
*/
package webgo
import (
"context"
"crypto/tls"
"net/http"
)
var supportedHTTPMethods = []string{
http.MethodOptions,
http.MethodHead,
http.MethodGet,
http.MethodPost,
http.MethodPut,
http.MethodPatch,
http.MethodDelete,
}
// ctxkey is a custom string type to store the WebGo context inside HTTP request context
type ctxkey string
const wgoCtxKey = ctxkey("webgocontext")
// ContextPayload is the WebgoContext. A new instance of ContextPayload is injected inside every request's context object
type ContextPayload struct {
Route *Route
Err error
URIParams map[string]string
}
// Params returns the URI parameters of the respective route
func (cp *ContextPayload) Params() map[string]string {
return cp.URIParams
}
func (cp *ContextPayload) reset() {
cp.Route = nil
cp.Err = nil
}
// SetError sets the err within the context
func (cp *ContextPayload) SetError(err error) {
cp.Err = err
}
// Error returns the error set within the context
func (cp *ContextPayload) Error() error {
return cp.Err
}
// Context returns the ContextPayload injected inside the HTTP request context
func Context(r *http.Request) *ContextPayload {
return r.Context().Value(wgoCtxKey).(*ContextPayload)
}
// SetError is a helper function to set the error in webgo context
func SetError(r *http.Request, err error) {
ctx := Context(r)
ctx.SetError(err)
}
// GetError is a helper function to get the error from webgo context
func GetError(r *http.Request) error {
return Context(r).Error()
}
// ResponseStatus returns the response status code. It works only if the http.ResponseWriter
// is not wrapped in another response writer before calling ResponseStatus
func ResponseStatus(rw http.ResponseWriter) int {
crw, ok := rw.(*customResponseWriter)
if !ok {
return http.StatusOK
}
return crw.statusCode
}
func (router *Router) setupServer() {
cfg := router.config
router.httpsServer = &http.Server{
Addr: "",
Handler: router,
ReadTimeout: cfg.ReadTimeout,
WriteTimeout: cfg.WriteTimeout,
TLSConfig: &tls.Config{
InsecureSkipVerify: cfg.InsecureSkipVerify,
},
}
router.httpServer = &http.Server{
Addr: "",
Handler: router,
ReadTimeout: cfg.ReadTimeout,
WriteTimeout: cfg.WriteTimeout,
}
router.SetupMiddleware()
}
// SetupMiddleware initializes all the middleware added using "Use".
// This function need not be called explicitly, if using router.Start()
// or router.StartHTTPS(). Instead if the router is being passed to an external server
// then the SetupMiddleware function should be called
func (router *Router) SetupMiddleware() {
// load middleware for all routes
for _, routes := range router.allHandlers {
for _, route := range routes {
route.setupMiddleware(router.config.ReverseMiddleware)
}
}
}
// StartHTTPS starts the server with HTTPS enabled
func (router *Router) StartHTTPS() {
cfg := router.config
if cfg.CertFile == "" {
LOGHANDLER.Fatal("No certificate provided for HTTPS")
}
if cfg.KeyFile == "" {
LOGHANDLER.Fatal("No key file provided for HTTPS")
}
router.setupServer()
host := cfg.Host
if len(cfg.HTTPSPort) > 0 {
host += ":" + cfg.HTTPSPort
}
router.httpsServer.Addr = host
LOGHANDLER.Info("HTTPS server, listening on", router.httpsServer.Addr)
err := router.httpsServer.ListenAndServeTLS(cfg.CertFile, cfg.KeyFile)
if err != nil && err != http.ErrServerClosed {
LOGHANDLER.Error("HTTPS server exited with error:", err.Error())
}
}
// Start starts the HTTP server with the appropriate configurations
func (router *Router) Start() {
router.setupServer()
cfg := router.config
host := cfg.Host
if len(cfg.Port) > 0 {
host += ":" + cfg.Port
}
router.httpServer.Addr = host
LOGHANDLER.Info("HTTP server, listening on", router.httpServer.Addr)
err := router.httpServer.ListenAndServe()
if err != nil && err != http.ErrServerClosed {
LOGHANDLER.Error("HTTP server exited with error:", err.Error())
}
}
// Shutdown gracefully shuts down HTTP server
func (router *Router) Shutdown() error {
if router.httpServer == nil {
return nil
}
timer := router.config.ShutdownTimeout
ctx, cancel := context.WithTimeout(context.TODO(), timer)
defer cancel()
err := router.httpServer.Shutdown(ctx)
if err != nil {
LOGHANDLER.Error(err)
}
return err
}
// ShutdownHTTPS gracefully shuts down HTTPS server
func (router *Router) ShutdownHTTPS() error {
if router.httpsServer == nil {
return nil
}
timer := router.config.ShutdownTimeout
ctx, cancel := context.WithTimeout(context.TODO(), timer)
defer cancel()
err := router.httpsServer.Shutdown(ctx)
if err != nil && err != http.ErrServerClosed {
LOGHANDLER.Error(err)
}
return err
}
// OriginalResponseWriter returns the Go response writer stored within the webgo custom response
// writer
func OriginalResponseWriter(rw http.ResponseWriter) http.ResponseWriter {
crw, ok := rw.(*customResponseWriter)
if !ok {
return nil
}
return crw.ResponseWriter
}