add middlewares

This commit is contained in:
guochao 2025-01-10 01:20:45 +08:00
parent 45f69d994e
commit b2445cf774
5 changed files with 158 additions and 2 deletions

View File

@ -9,6 +9,11 @@ import (
"time"
cacheproxy "git.jeffthecoder.xyz/guochao/cache-proxy"
"git.jeffthecoder.xyz/guochao/cache-proxy/pkgs/middleware"
"git.jeffthecoder.xyz/guochao/cache-proxy/pkgs/middleware/handlerlog"
"git.jeffthecoder.xyz/guochao/cache-proxy/pkgs/middleware/httplog"
"git.jeffthecoder.xyz/guochao/cache-proxy/pkgs/middleware/recover"
"github.com/getsentry/sentry-go"
"gopkg.in/yaml.v3"
)
@ -115,9 +120,16 @@ func main() {
server := cacheproxy.NewServer(*config)
http.HandleFunc("GET /{path...}", server.HandleRequestWithCache)
mux := http.NewServeMux()
mux.HandleFunc("GET /", server.HandleRequestWithCache)
slog.With("addr", ":8881").Info("serving app")
if err := http.ListenAndServe(":8881", nil); err != nil {
if err := http.ListenAndServe(":8881", middleware.Use(mux,
recover.Recover(),
handlerlog.FindHandler(mux),
httplog.Log(httplog.Config{LogStart: true, LogFinish: true}),
)); err != nil {
slog.With("error", err).Error("failed to start server")
os.Exit(-1)
}

View File

@ -0,0 +1,30 @@
package handlerlog
import (
"context"
"net/http"
)
type ctxKey int
var (
key ctxKey = 0
)
func FindHandler(mux *http.ServeMux) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
if _, pattern := mux.Handler(r); pattern != "" {
ctx = context.WithValue(ctx, key, pattern)
}
next.ServeHTTP(w, r.WithContext(ctx))
})
}
}
func Pattern(r *http.Request) (string, bool) {
pattern, ok := r.Context().Value(key).(string)
return pattern, ok
}

View File

@ -0,0 +1,75 @@
package httplog
import (
"context"
"log/slog"
"net/http"
"time"
"git.jeffthecoder.xyz/guochao/cache-proxy/pkgs/middleware/handlerlog"
)
type ctxKey int
const (
loggerKey ctxKey = iota
)
type Config struct {
LogStart bool
LogStartLevel slog.Level
LogFinish bool
LogFinishLevel slog.Level
}
type responseRecorder struct {
http.ResponseWriter
StatusCode int
}
func (recorder *responseRecorder) WriteHeader(statusCode int) {
recorder.StatusCode = statusCode
recorder.ResponseWriter.WriteHeader(statusCode)
}
func Log(config Config) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
args := []any{
"remote_addr", r.RemoteAddr,
"host", r.Host,
"path", r.URL.Path,
}
if pattern, handlerFound := handlerlog.Pattern(r); handlerFound {
args = append(args, "handler", pattern)
}
startTime := time.Now()
if config.LogStart {
slog.With(append(args, "time", startTime)...).Log(r.Context(), config.LogStartLevel, "request start")
}
recorder := &responseRecorder{ResponseWriter: w, StatusCode: 200}
next.ServeHTTP(recorder, r.WithContext(context.WithValue(r.Context(), loggerKey, slog.With(args...))))
if config.LogFinish {
finishTime := time.Now()
slog.With(append(args, "time", finishTime, "duration", finishTime.Sub(startTime), "status", recorder.StatusCode)...).Log(r.Context(), config.LogFinishLevel, "request finished")
}
})
}
}
func Logger(r *http.Request) *slog.Logger {
if logger, ok := r.Context().Value(loggerKey).(*slog.Logger); ok {
return logger.With("time", time.Now())
}
return slog.With(
"remote_addr", r.RemoteAddr,
"host", r.Host,
"path", r.URL.Path,
"time", time.Now(),
)
}

View File

@ -0,0 +1,14 @@
package middleware
import (
"net/http"
"slices"
)
func Use(handler http.Handler, wrappers ...func(http.Handler) http.Handler) http.Handler {
slices.Reverse(wrappers)
for _, wrapper := range wrappers {
handler = wrapper(handler)
}
return handler
}

View File

@ -0,0 +1,25 @@
package recover
import (
"fmt"
"net/http"
"git.jeffthecoder.xyz/guochao/cache-proxy/pkgs/middleware/httplog"
)
func recoverer(w http.ResponseWriter, r *http.Request) {
if err := recover(); err != nil {
httplog.Logger(r).With("error", err).Error("request panicked")
http.Error(w, fmt.Sprint(err), http.StatusInternalServerError)
}
}
func Recover() func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
defer recoverer(w, r)
next.ServeHTTP(w, r)
})
}
}