diff --git a/cmd/screenjournal/main.go b/cmd/screenjournal/main.go index 1e5efe84..b845e881 100644 --- a/cmd/screenjournal/main.go +++ b/cmd/screenjournal/main.go @@ -10,13 +10,12 @@ import ( "strconv" "time" - gorilla "github.com/mtlynch/gorilla-handlers" - email_announce "github.com/mtlynch/screenjournal/v2/announce/email" "github.com/mtlynch/screenjournal/v2/announce/quiet" "github.com/mtlynch/screenjournal/v2/auth" "github.com/mtlynch/screenjournal/v2/email/smtp" "github.com/mtlynch/screenjournal/v2/handlers" + "github.com/mtlynch/screenjournal/v2/handlers/middleware" "github.com/mtlynch/screenjournal/v2/handlers/sessions" "github.com/mtlynch/screenjournal/v2/metadata/tmdb" "github.com/mtlynch/screenjournal/v2/store/sqlite" @@ -67,9 +66,9 @@ func main() { log.Fatalf("failed to create metadata finder: %v", err) } - h := gorilla.LoggingHandler(os.Stdout, handlers.New(authenticator, announcer, sessionManager, store, metadataFinder).Router()) + h := middleware.LoggingHandler(os.Stdout, handlers.New(authenticator, announcer, sessionManager, store, metadataFinder).Router()) if os.Getenv("SJ_BEHIND_PROXY") != "" { - h = gorilla.ProxyIPHeadersHandler(h) + h = middleware.ProxyIPHeaders(h) } http.Handle("/", h) diff --git a/go.mod b/go.mod index 0bacb3b9..7edc534a 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,6 @@ require ( github.com/gorilla/mux v1.8.0 github.com/kylelemons/godebug v1.1.0 github.com/microcosm-cc/bluemonday v1.0.27 - github.com/mtlynch/gorilla-handlers v1.5.2 github.com/mtlynch/simpleauth/v2 v2.0.0-20241108014613-2f32145d692d github.com/ncruces/go-sqlite3 v0.22.0 github.com/ryanbradynd05/go-tmdb v0.0.0-20220721194547-2ab6191c6273 @@ -17,7 +16,6 @@ require ( require ( codeberg.org/mtlynch/go-evolutionary-migrate v0.0.1 github.com/aymerick/douceur v0.2.0 // indirect - github.com/felixge/httpsnoop v1.0.1 // indirect github.com/gorilla/css v1.0.1 // indirect github.com/kylelemons/go-gypsy v1.0.0 // indirect github.com/mtlynch/jeff v0.2.4 // indirect diff --git a/go.sum b/go.sum index 690c4eef..3b64d540 100644 --- a/go.sum +++ b/go.sum @@ -6,8 +6,6 @@ github.com/bradfitz/gomemcache v0.0.0-20180710155616-bc664df96737 h1:rRISKWyXfVx github.com/bradfitz/gomemcache v0.0.0-20180710155616-bc664df96737/go.mod h1:PmM6Mmwb0LSuEubjR8N7PtNe1KxZLtOUHtbeikc5h60= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ= -github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM= github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/gomarkdown/markdown v0.0.0-20240723152757-afa4a469d4f9 h1:TRYrIWJziqvMVn1owO8bmkDJTlMQFYnf74yhD8LXfgU= @@ -28,8 +26,6 @@ github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwp github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk= github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA= -github.com/mtlynch/gorilla-handlers v1.5.2 h1:CnOI7baYzjgdumJMc7Dh192JFxqGsNgRewa7NTqq9Pc= -github.com/mtlynch/gorilla-handlers v1.5.2/go.mod h1:qZmXSCK7pPjN71Pl9ARbQX2ec1t11FI/j8bDS+ZVbmQ= github.com/mtlynch/jeff v0.2.4 h1:+sfnAwKZ7yO44IuaJM3n/53Tvc6lqscHeR9Z2WTbacE= github.com/mtlynch/jeff v0.2.4/go.mod h1:bxdRG/C7U5XEt1nTKU282IAeVRJu2pLR2cV4dHNLkOk= github.com/mtlynch/simpleauth/v2 v2.0.0-20241108014613-2f32145d692d h1:unicX/aP5Ii6c3zRxfqggdF8oR3ur0ePrqdDDu1hnzA= diff --git a/handlers/middleware/logging.go b/handlers/middleware/logging.go new file mode 100644 index 00000000..108c43e6 --- /dev/null +++ b/handlers/middleware/logging.go @@ -0,0 +1,44 @@ +package middleware + +import ( + "fmt" + "io" + "net/http" + "time" +) + +// responseRecorder captures the status code and bytes written. +type responseRecorder struct { + http.ResponseWriter + status int + size int +} + +func (r *responseRecorder) WriteHeader(status int) { + r.status = status + r.ResponseWriter.WriteHeader(status) +} + +func (r *responseRecorder) Write(b []byte) (int, error) { + n, err := r.ResponseWriter.Write(b) + r.size += n + return n, err +} + +// LoggingHandler returns a handler that logs requests in Common Log Format. +func LoggingHandler(out io.Writer, h http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + rec := &responseRecorder{ResponseWriter: w, status: http.StatusOK} + start := time.Now() + h.ServeHTTP(rec, r) + _, _ = fmt.Fprintf(out, "%s - - [%s] \"%s %s %s\" %d %d\n", + r.RemoteAddr, + start.Format("02/Jan/2006:15:04:05 -0700"), + r.Method, + r.RequestURI, + r.Proto, + rec.status, + rec.size, + ) + }) +} diff --git a/handlers/middleware/proxy.go b/handlers/middleware/proxy.go new file mode 100644 index 00000000..ce10f3f5 --- /dev/null +++ b/handlers/middleware/proxy.go @@ -0,0 +1,47 @@ +package middleware + +import ( + "net" + "net/http" + "strings" +) + +// ProxyIPHeaders returns a handler that sets r.RemoteAddr from proxy headers. +func ProxyIPHeaders(h http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if ip := realIP(r); ip != "" { + r.RemoteAddr = ip + } + h.ServeHTTP(w, r) + }) +} + +func realIP(r *http.Request) string { + if xff := r.Header.Get("X-Forwarded-For"); xff != "" { + // Take the first IP in the comma-separated list. + if i := strings.IndexByte(xff, ','); i > 0 { + xff = xff[:i] + } + return strings.TrimSpace(xff) + } + if xri := r.Header.Get("X-Real-IP"); xri != "" { + return strings.TrimSpace(xri) + } + // Parse the Forwarded header (RFC 7239). + if fwd := r.Header.Get("Forwarded"); fwd != "" { + for _, part := range strings.Split(fwd, ";") { + part = strings.TrimSpace(part) + if strings.HasPrefix(strings.ToLower(part), "for=") { + addr := part[4:] + addr = strings.Trim(addr, `"`) + // Handle IPv6 with brackets. + host, _, err := net.SplitHostPort(addr) + if err == nil { + return host + } + return addr + } + } + } + return "" +}