headers.go 2.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. package internal
  2. import (
  3. "log/slog"
  4. "net"
  5. "net/http"
  6. "strings"
  7. "github.com/TecharoHQ/anubis"
  8. "github.com/sebest/xff"
  9. )
  10. // UnchangingCache sets the Cache-Control header to cache a response for 1 year if
  11. // and only if the application is compiled in "release" mode by Docker.
  12. func UnchangingCache(next http.Handler) http.Handler {
  13. //goland:noinspection GoBoolExpressions
  14. if anubis.Version == "devel" {
  15. return next
  16. }
  17. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  18. w.Header().Set("Cache-Control", "public, max-age=31536000")
  19. next.ServeHTTP(w, r)
  20. })
  21. }
  22. // RemoteXRealIP sets the X-Real-Ip header to the request's real IP if
  23. // the setting is enabled by the user.
  24. func RemoteXRealIP(useRemoteAddress bool, bindNetwork string, next http.Handler) http.Handler {
  25. if !useRemoteAddress {
  26. slog.Debug("skipping middleware, useRemoteAddress is empty")
  27. return next
  28. }
  29. if bindNetwork == "unix" {
  30. // For local sockets there is no real remote address but the localhost
  31. // address should be sensible.
  32. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  33. r.Header.Set("X-Real-Ip", "127.0.0.1")
  34. next.ServeHTTP(w, r)
  35. })
  36. }
  37. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  38. host, _, err := net.SplitHostPort(r.RemoteAddr)
  39. if err != nil {
  40. panic(err) // this should never happen
  41. }
  42. r.Header.Set("X-Real-Ip", host)
  43. next.ServeHTTP(w, r)
  44. })
  45. }
  46. // XForwardedForToXRealIP sets the X-Real-Ip header based on the contents
  47. // of the X-Forwarded-For header.
  48. func XForwardedForToXRealIP(next http.Handler) http.Handler {
  49. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  50. if xffHeader := r.Header.Get("X-Forwarded-For"); r.Header.Get("X-Real-Ip") == "" && xffHeader != "" {
  51. ip := xff.Parse(xffHeader)
  52. slog.Debug("setting x-real-ip", "val", ip)
  53. r.Header.Set("X-Real-Ip", ip)
  54. }
  55. next.ServeHTTP(w, r)
  56. })
  57. }
  58. // NoStoreCache sets the Cache-Control header to no-store for the response.
  59. func NoStoreCache(next http.Handler) http.Handler {
  60. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  61. w.Header().Set("Cache-Control", "no-store")
  62. next.ServeHTTP(w, r)
  63. })
  64. }
  65. // NoBrowsing prevents directory browsing by returning a 404 for any request that ends with a "/".
  66. func NoBrowsing(next http.Handler) http.Handler {
  67. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  68. if strings.HasSuffix(r.URL.Path, "/") {
  69. http.NotFound(w, r)
  70. return
  71. }
  72. next.ServeHTTP(w, r)
  73. })
  74. }