-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathapp.go
202 lines (177 loc) · 6.36 KB
/
app.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
package expresso
import (
"crypto/tls"
"encoding/xml"
"net/http"
"time"
"github.com/julienschmidt/httprouter"
)
// Config holds server configuration settings such as read/write timeouts and maximum header bytes.
type Config struct {
ReadTimeout time.Duration // Maximum duration for reading the entire request, including the body.
WriteTimeout time.Duration // Maximum duration before timing out writes of the response.
MaxHeaderBytes int // Maximum number of bytes the server will read parsing the request header.
}
// App is the main structure of the application, encapsulating the router and server configuration.
type App struct {
router *httprouter.Router // HTTP request router.
Config // Server configuration settings.
TLSConfig *tls.Config // TLS configuration for HTTPS server.
}
// DefaultApp creates and returns an App instance with default configurations.
// Default Config values:
// - ReadTimeout: 10 seconds
// - WriteTimeout: 10 seconds
// - MaxHeaderBytes: 1MB (1 << 20 bytes)
func DefaultApp() App {
return NewApp(Config{
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
MaxHeaderBytes: 1 << 20,
}, nil)
}
// NewApp creates and returns an App instance with custom configuration settings provided by the user.
func NewApp(c Config, t *tls.Config) App {
return App{
router: httprouter.New(),
Config: c,
TLSConfig: t,
}
}
// ListenAndServe starts the HTTP server on the specified address with the settings provided in the App's Config.
func (a App) ListenAndServe(addr string, cb func(error)) error {
server := http.Server{
Addr: addr,
Handler: a.router,
ReadTimeout: a.Config.ReadTimeout,
WriteTimeout: a.Config.WriteTimeout,
MaxHeaderBytes: a.Config.MaxHeaderBytes,
}
err := server.ListenAndServe()
if cb != nil {
cb(err)
}
return err
}
// ListenAndServeTLS starts the HTTPS server with the given certificate and key files on the specified address.
func (a App) ListenAndServeTLS(addr, certFile, keyFile string, cb func(error)) error {
server := http.Server{
Addr: addr,
Handler: a.router,
ReadTimeout: a.Config.ReadTimeout,
WriteTimeout: a.Config.WriteTimeout,
MaxHeaderBytes: a.Config.MaxHeaderBytes,
TLSConfig: a.TLSConfig,
}
err := server.ListenAndServeTLS(certFile, keyFile)
if cb != nil {
cb(err)
}
return err
}
// HEAD registers a HEAD request handler for the specified path with optional middleware.
func (a App) HEAD(path string, middlewares ...Middleware) {
a.router.HEAD(path, handle(middlewares))
}
// OPTIONS registers an OPTIONS request handler for the specified path with optional middleware.
func (a App) OPTIONS(path string, middlewares ...Middleware) {
a.router.OPTIONS(path, handle(middlewares))
}
// GET registers a GET request handler for the specified path with optional middleware.
func (a App) GET(path string, middlewares ...Middleware) {
a.router.GET(path, handle(middlewares))
}
// POST registers a POST request handler for the specified path with optional middleware.
func (a App) POST(path string, middlewares ...Middleware) {
a.router.POST(path, handle(middlewares))
}
// PATCH registers a PATCH request handler for the specified path with optional middleware.
func (a App) PATCH(path string, middlewares ...Middleware) {
a.router.PATCH(path, handle(middlewares))
}
// PUT registers a PUT request handler for the specified path with optional middleware.
func (a App) PUT(path string, middlewares ...Middleware) {
a.router.PUT(path, handle(middlewares))
}
// DELETE registers a DELETE request handler for the specified path with optional middleware.
func (a App) DELETE(path string, middlewares ...Middleware) {
a.router.DELETE(path, handle(middlewares))
}
// ServeStatic serves static files from the provided directory for the specified path.
func (a App) ServeStatic(path string, root http.FileSystem) {
a.router.ServeFiles(path, root)
}
// HandleNotFound sets up a custom 404 Not Found handler with optional middleware.
func (a App) HandleNotFound(middlewares ...Middleware) {
h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
handle(middlewares)(w, r, nil)
})
a.router.NotFound = h
}
// HandleError sets up a custom error handler with optional middleware.
func (a App) HandleError(handler func(http.ResponseWriter, *http.Request, interface{})) {
a.router.PanicHandler = handler
}
// handle is a helper function that processes a list of middleware and invokes them sequentially.
func handle(middlewares []Middleware) func(http.ResponseWriter, *http.Request, httprouter.Params) {
return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
req := requestFromHttpRequest(r) // Convert the incoming HTTP request to a custom request type.
res := responseFromHttpResponseWriter(w) // Convert the response writer to a custom response type.
if req == nil {
// Handle errors if the request couldn't be processed.
res.Status(500).Formatted(r, Formatted{
Text: &Text{"Error - Unable to process the request"},
HTML: &HTML{"<html><head><title>Error - Unable to process the request</title></head><body>Error - Unable to process the request</body></html>"},
JSON: &JSON{
Data: map[string]interface{}{
"status": 500,
"error": "Unable to process the request",
},
},
XML: &XML{
Data: struct {
XMLName xml.Name `xml:"error"`
Status int `xml:"status"`
Error string `xml:"error"`
}{
Status: 500,
Error: "Unable to process the request",
},
},
YAML: &YAML{
Data: map[string]interface{}{
"status": 500,
"error": "Unable to process the request",
},
},
Default: &JSON{
Data: map[string]interface{}{
"status": 500,
"error": "Unable to process the request",
},
},
})
return
}
req.Params = p // Attach the URL parameters to the request.
// Initialize the context for middleware processing.
ctx := &Context{
Request: req,
Response: res,
Extras: map[interface{}]interface{}{},
goNext: false,
Logger: NewLogger(req),
}
ctx.Response.Context = ctx // Link the response to the context.
// Execute the middleware chain.
for _, middleware := range middlewares {
middleware(ctx)
if !ctx.goNext {
break
}
ctx.goNext = false
}
// Log the response context if needed.
ctx.Dump()
}
}