~repos /gromer

#golang#htmx#ssr

git clone https://pyrossh.dev/repos/gromer.git

gromer is a framework and cli to build multipage web apps in golang using htmx and alpinejs.


fc24079a Peter John

3 years ago
use handlers
Files changed (4) hide show
  1. _example/main.go +1 -0
  2. go.mod +2 -0
  3. go.sum +4 -0
  4. http.go +25 -113
_example/main.go CHANGED
@@ -32,6 +32,7 @@ func main() {
32
32
 
33
33
  staticRouter := baseRouter.NewRoute().Subrouter()
34
34
  staticRouter.Use(gromer.CacheMiddleware)
35
+ staticRouter.Use(gromer.CompressMiddleware)
35
36
  gromer.StaticRoute(staticRouter, "/gromer/", gromer_assets.FS)
36
37
  gromer.StaticRoute(staticRouter, "/assets/", assets.FS)
37
38
  gromer.StylesRoute(staticRouter, "/styles.css")
go.mod CHANGED
@@ -24,6 +24,7 @@ require (
24
24
  github.com/aymerick/raymond v2.0.2+incompatible // indirect
25
25
  github.com/blang/semver v3.5.1+incompatible // indirect
26
26
  github.com/davecgh/go-spew v1.1.1 // indirect
27
+ github.com/felixge/httpsnoop v1.0.1 // indirect
27
28
  github.com/go-playground/locales v0.14.0 // indirect
28
29
  github.com/go-playground/universal-translator v0.18.0 // indirect
29
30
  github.com/gobuffalo/envy v1.6.5 // indirect
@@ -33,6 +34,7 @@ require (
33
34
  github.com/google/wire v0.5.0 // indirect
34
35
  github.com/googleapis/gax-go/v2 v2.1.0 // indirect
35
36
  github.com/gorilla/css v1.0.0 // indirect
37
+ github.com/gorilla/handlers v1.5.1 // indirect
36
38
  github.com/joho/godotenv v1.3.0 // indirect
37
39
  github.com/leodido/go-urn v1.2.1 // indirect
38
40
  github.com/markbates/inflect v1.0.4 // indirect
go.sum CHANGED
@@ -161,6 +161,8 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m
161
161
  github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
162
162
  github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
163
163
  github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
164
+ github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ=
165
+ github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
164
166
  github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
165
167
  github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
166
168
  github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
@@ -284,6 +286,8 @@ github.com/googleapis/gax-go/v2 v2.1.0 h1:6DWmvNpomjL1+3liNSZbVns3zsYzzCjm6pRBO1
284
286
  github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0=
285
287
  github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=
286
288
  github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
289
+ github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4=
290
+ github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q=
287
291
  github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
288
292
  github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
289
293
  github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
http.go CHANGED
@@ -19,8 +19,10 @@ import (
19
19
  "sync"
20
20
  "time"
21
21
 
22
+ "github.com/felixge/httpsnoop"
22
23
  "github.com/go-playground/validator/v10"
23
24
  "github.com/google/uuid"
25
+ "github.com/gorilla/handlers"
24
26
  "github.com/gorilla/mux"
25
27
  "github.com/pyros2097/gromer/gsx"
26
28
  "github.com/rs/zerolog"
@@ -29,6 +31,12 @@ import (
29
31
  "xojoc.pw/useragent"
30
32
  )
31
33
 
34
+ const (
35
+ gzipEncoding = "gzip"
36
+ flateEncoding = "deflate"
37
+ acceptEncoding = "Accept-Encoding"
38
+ )
39
+
32
40
  var (
33
41
  info *debug.BuildInfo
34
42
  IsCloundRun bool
@@ -73,12 +81,14 @@ func getFunctionName(temp interface{}) string {
73
81
  func RespondError(w http.ResponseWriter, status int, err error) {
74
82
  w.Header().Set("Content-Type", "application/json")
75
83
  w.WriteHeader(status) // always write status last
76
- w.(*LogResponseWriter).SetError(err)
77
84
  merror := map[string]interface{}{
78
85
  "error": err.Error(),
79
86
  }
80
87
  if status >= 500 {
81
88
  merror["error"] = "Internal Server Error"
89
+ stack := string(debug.Stack())
90
+ println(stack)
91
+ log.WithLevel(zerolog.ErrorLevel).Err(err).Bool("panic", status == 599).Str("stack", stack).Stack()
82
92
  }
83
93
  validationErrors, ok := err.(validator.ValidationErrors)
84
94
  if ok {
@@ -97,26 +107,6 @@ func GetRouteParams(route string) []string {
97
107
  return params
98
108
  }
99
109
 
100
- func addRouteDef(method, route string, h interface{}) {
101
- pathParams := GetRouteParams(route)
102
- var body any = nil
103
- funcType := reflect.TypeOf(h)
104
- if funcType.NumIn() > len(pathParams)+2 {
105
- structType := funcType.In(funcType.NumIn() - 1)
106
- instance := reflect.New(structType)
107
- if structType.Kind() != reflect.Struct {
108
- log.Fatal().Msgf("router '%s' '%s' func final param should be a struct", method, route)
109
- }
110
- body = instance.Interface()
111
- }
112
- routeDefs = append(routeDefs, RouteDefinition{
113
- Method: method,
114
- Path: route,
115
- PathParams: pathParams,
116
- Params: body,
117
- })
118
- }
119
-
120
110
  func PerformRequest(route string, h interface{}, ctx context.Context, w http.ResponseWriter, r *http.Request) {
121
111
  params := GetRouteParams(route)
122
112
  renderContext := gsx.NewContext(ctx, r.Header.Get("HX-Request") == "true")
@@ -224,107 +214,30 @@ func PerformRequest(route string, h interface{}, ctx context.Context, w http.Res
224
214
  w.Write(data)
225
215
  }
226
216
 
227
- type writeCounter int64
228
-
229
- func (wc *writeCounter) Write(p []byte) (n int, err error) {
230
- *wc += writeCounter(len(p))
231
- return len(p), nil
232
- }
233
- func headerSize(h http.Header) int64 {
234
- var wc writeCounter
235
- h.Write(&wc)
236
- return int64(wc) + 2 // for CRLF
237
- }
238
-
239
- type LogResponseWriter struct {
240
- http.ResponseWriter
241
- startTime time.Time
242
- responseStatusCode int
243
- responseContentLength int
244
- responseHeaderSize int
245
- err error
246
- }
247
-
248
- func NewLogResponseWriter(w http.ResponseWriter) *LogResponseWriter {
249
- return &LogResponseWriter{ResponseWriter: w, startTime: time.Now()}
250
- }
251
-
252
- func (w *LogResponseWriter) WriteHeader(code int) {
253
- w.ResponseWriter.WriteHeader(code)
254
- w.responseStatusCode = code
255
- w.responseHeaderSize = int(headerSize(w.Header()))
256
- }
257
-
258
- func (w *LogResponseWriter) Write(body []byte) (int, error) {
259
- w.responseContentLength += len(body)
260
- return w.ResponseWriter.Write(body)
261
- }
262
-
263
- func (w *LogResponseWriter) SetError(err error) {
264
- w.err = err
265
- }
266
-
267
- func (w *LogResponseWriter) LogRequest(r *http.Request) {
268
- ip, _, _ := net.SplitHostPort(r.RemoteAddr)
269
- if len(ip) > 0 && ip[0] == '[' {
270
- ip = ip[1 : len(ip)-1]
271
- }
272
- logger := log.WithLevel(zerolog.InfoLevel)
273
- if w.err != nil {
274
- stack := string(debug.Stack())
275
- println(stack)
276
- logger = log.WithLevel(zerolog.ErrorLevel).Err(w.err).Str("stack", stack).Stack()
277
- }
278
- ua := useragent.Parse(r.UserAgent())
279
- logger.Msgf("%s %d %.2f KB %s %s %s", r.Method,
280
- w.responseStatusCode,
281
- float32(w.responseContentLength)/1024.0,
282
- time.Since(w.startTime).Round(time.Millisecond).String(), ua.Name, r.URL.Path)
283
- // logger.
284
- // Str("method", r.Method).
285
- // Str("url", r.URL.String()).
286
- // Int("header_size", int(headerSize(r.Header))).
287
- // Int64("body_size", r.ContentLength).
288
- // Str("host", r.Host).
289
- // // Str("agent", r.UserAgent()).
290
- // Str("referer", r.Referer()).
291
- // Str("proto", r.Proto).
292
- // Str("remote_ip", ip).
293
- // Int("status", logRespWriter.responseStatusCode).
294
- // Int("resp_header_size", logRespWriter.responseHeaderSize).
295
- // Int("resp_body_size", logRespWriter.responseContentLength).
296
- // Str("latency", time.Since(startTime).String()).
297
- // Msgf("")
298
- }
299
-
300
217
  func LogMiddleware(next http.Handler) http.Handler {
301
218
  return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
302
- logRespWriter := NewLogResponseWriter(w)
303
219
  defer func() {
304
220
  if err := recover(); err != nil {
305
- RespondError(logRespWriter, 599, fmt.Errorf("%+v", err))
221
+ RespondError(w, 599, fmt.Errorf("%+v", err))
306
- logRespWriter.LogRequest(r)
307
222
  }
308
223
  }()
309
- next.ServeHTTP(logRespWriter, r)
224
+ m := httpsnoop.CaptureMetrics(next, w, r)
225
+ ip, _, _ := net.SplitHostPort(r.RemoteAddr)
226
+ if len(ip) > 0 && ip[0] == '[' {
310
- if IsCloundRun {
227
+ ip = ip[1 : len(ip)-1]
311
- return
312
228
  }
229
+ log.WithLevel(zerolog.InfoLevel).Msgf("%s %d %.2fkb %s %s %s", r.Method,
230
+ m.Code,
313
- logRespWriter.LogRequest(r)
231
+ float64(m.Written)/1024.0,
232
+ m.Duration.Round(time.Millisecond).String(),
233
+ useragent.Parse(r.UserAgent()).Name,
234
+ r.URL.Path,
235
+ )
314
236
  })
315
237
  }
316
238
 
317
- func CorsMiddleware(next http.Handler) http.Handler {
239
+ func CompressMiddleware(next http.Handler) http.Handler {
318
- return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
240
+ return handlers.CompressHandler(next)
319
- w.Header().Set("Access-Control-Allow-Origin", "*")
320
- w.Header().Set("Access-Control-Allow-Methods", "*")
321
- w.Header().Set("Access-Control-Allow-Headers", "*")
322
- if r.Method == "OPTIONS" {
323
- w.WriteHeader(200)
324
- return
325
- }
326
- next.ServeHTTP(w, r)
327
- })
328
241
  }
329
242
 
330
243
  func CacheMiddleware(next http.Handler) http.Handler {
@@ -367,7 +280,6 @@ func StylesRoute(router *mux.Router, path string) {
367
280
  }
368
281
 
369
282
  func Handle(router *mux.Router, method, route string, h interface{}) {
370
- addRouteDef(method, route, h)
371
283
  router.HandleFunc(route, func(w http.ResponseWriter, r *http.Request) {
372
284
  ctx := context.WithValue(context.WithValue(r.Context(), "url", r.URL), "header", r.Header)
373
285
  PerformRequest(route, h, ctx, w, r)