Skip to content

Instantly share code, notes, and snippets.

@joerx
Last active December 27, 2024 07:10
Show Gist options
  • Save joerx/98ffe7a5cd41083d5b11c33e1f9cfec3 to your computer and use it in GitHub Desktop.
Save joerx/98ffe7a5cd41083d5b11c33e1f9cfec3 to your computer and use it in GitHub Desktop.
Go Middleware Chains
package main
// Loosely based on https://www.alexedwards.net/blog/making-and-using-middleware
// and https://blog.questionable.services/article/guide-logging-middleware-go/
// Similar to https://github.com/justinas/alice but more minimalist :)
import (
"log"
"net/http"
)
type Middleware func(http.Handler) http.Handler
type loggingInterceptor struct {
w http.ResponseWriter
statusCode int
statusWritten bool
}
func (l *loggingInterceptor) Header() http.Header {
return l.w.Header()
}
func (l *loggingInterceptor) Write(bs []byte) (int, error) {
return l.w.Write(bs)
}
func (l *loggingInterceptor) WriteHeader(statusCode int) {
if l.statusWritten {
return
}
l.statusCode = statusCode
l.w.WriteHeader(statusCode)
l.statusWritten = true
}
func wrapResponseWriter(w http.ResponseWriter) *loggingInterceptor {
return &loggingInterceptor{w: w, statusCode: http.StatusOK}
}
func requestLogger(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
wrapped := wrapResponseWriter(w)
next.ServeHTTP(wrapped, r)
log.Printf("%s %s - %d", r.Method, r.URL.Path, wrapped.statusCode)
})
}
func authHandler(password string) Middleware {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
authHeader := r.Header.Get("Authorization")
if authHeader != password {
log.Println("Suspected hacking attempt detected!")
http.Error(w, "Please present valid multipass!", http.StatusUnauthorized)
return
}
next.ServeHTTP(w, r)
})
}
}
func messageHandler(message string) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
log.Printf("Sending '%s'", message)
// w.WriteHeader(http.StatusOK)
w.Write([]byte(message))
}
}
func chain(c ...Middleware) Middleware {
return func(next http.Handler) http.Handler {
for i := len(c) - 1; i >= 0; i-- {
m := c[i]
next = m(next)
}
return next
}
}
func main() {
mux := http.NewServeMux()
auth := authHandler("multipass")
finalHandler := messageHandler("final")
otherHandler := messageHandler("other")
c := chain(requestLogger, auth)
mux.Handle("/", c(finalHandler))
mux.Handle("/foo", c(otherHandler))
log.Print("Listening on :3000...")
err := http.ListenAndServe(":3000", mux)
log.Fatal(err)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment