html.go 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. // SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
  2. // SPDX-License-Identifier: Apache-2.0
  3. package response // import "miniflux.app/v2/internal/http/response"
  4. import (
  5. "html"
  6. "log/slog"
  7. "net/http"
  8. "miniflux.app/v2/internal/http/request"
  9. )
  10. // HTML creates a new HTML response with a 200 status code.
  11. func HTML[T []byte | string](w http.ResponseWriter, r *http.Request, body T) {
  12. builder := NewBuilder(w, r)
  13. builder.WithHeader("Content-Type", "text/html; charset=utf-8")
  14. builder.WithHeader("Cache-Control", "no-cache, max-age=0, must-revalidate, no-store")
  15. switch v := any(body).(type) {
  16. case []byte:
  17. builder.WithBodyAsBytes(v)
  18. case string:
  19. builder.WithBodyAsString(v)
  20. }
  21. builder.Write()
  22. }
  23. // HTMLServerError sends an internal error to the client.
  24. func HTMLServerError(w http.ResponseWriter, r *http.Request, err error) {
  25. slog.Error(http.StatusText(http.StatusInternalServerError),
  26. slog.Any("error", err),
  27. slog.String("client_ip", request.ClientIP(r)),
  28. slog.Group("request",
  29. slog.String("method", r.Method),
  30. slog.String("uri", r.RequestURI),
  31. slog.String("user_agent", r.UserAgent()),
  32. ),
  33. slog.Group("response",
  34. slog.Int("status_code", http.StatusInternalServerError),
  35. ),
  36. )
  37. builder := NewBuilder(w, r)
  38. builder.WithStatus(http.StatusInternalServerError)
  39. builder.WithHeader("Content-Security-Policy", ContentSecurityPolicyForUntrustedContent)
  40. builder.WithHeader("Content-Type", "text/plain; charset=utf-8")
  41. builder.WithHeader("Cache-Control", "no-cache, max-age=0, must-revalidate, no-store")
  42. builder.WithBodyAsString(html.EscapeString(err.Error()))
  43. builder.Write()
  44. }
  45. // HTMLBadRequest sends a bad request error to the client.
  46. func HTMLBadRequest(w http.ResponseWriter, r *http.Request, err error) {
  47. slog.Warn(http.StatusText(http.StatusBadRequest),
  48. slog.Any("error", err),
  49. slog.String("client_ip", request.ClientIP(r)),
  50. slog.Group("request",
  51. slog.String("method", r.Method),
  52. slog.String("uri", r.RequestURI),
  53. slog.String("user_agent", r.UserAgent()),
  54. ),
  55. slog.Group("response",
  56. slog.Int("status_code", http.StatusBadRequest),
  57. ),
  58. )
  59. builder := NewBuilder(w, r)
  60. builder.WithStatus(http.StatusBadRequest)
  61. builder.WithHeader("Content-Security-Policy", ContentSecurityPolicyForUntrustedContent)
  62. builder.WithHeader("Content-Type", "text/plain; charset=utf-8")
  63. builder.WithHeader("Cache-Control", "no-cache, max-age=0, must-revalidate, no-store")
  64. builder.WithBodyAsString(html.EscapeString(err.Error()))
  65. builder.Write()
  66. }
  67. // HTMLForbidden sends a forbidden error to the client.
  68. func HTMLForbidden(w http.ResponseWriter, r *http.Request) {
  69. slog.Warn(http.StatusText(http.StatusForbidden),
  70. slog.String("client_ip", request.ClientIP(r)),
  71. slog.Group("request",
  72. slog.String("method", r.Method),
  73. slog.String("uri", r.RequestURI),
  74. slog.String("user_agent", r.UserAgent()),
  75. ),
  76. slog.Group("response",
  77. slog.Int("status_code", http.StatusForbidden),
  78. ),
  79. )
  80. builder := NewBuilder(w, r)
  81. builder.WithStatus(http.StatusForbidden)
  82. builder.WithHeader("Content-Type", "text/html; charset=utf-8")
  83. builder.WithHeader("Cache-Control", "no-cache, max-age=0, must-revalidate, no-store")
  84. builder.WithBodyAsString("Access Forbidden")
  85. builder.Write()
  86. }
  87. // HTMLNotFound sends a page not found error to the client.
  88. func HTMLNotFound(w http.ResponseWriter, r *http.Request) {
  89. slog.Warn(http.StatusText(http.StatusNotFound),
  90. slog.String("client_ip", request.ClientIP(r)),
  91. slog.Group("request",
  92. slog.String("method", r.Method),
  93. slog.String("uri", r.RequestURI),
  94. slog.String("user_agent", r.UserAgent()),
  95. ),
  96. slog.Group("response",
  97. slog.Int("status_code", http.StatusNotFound),
  98. ),
  99. )
  100. builder := NewBuilder(w, r)
  101. builder.WithStatus(http.StatusNotFound)
  102. builder.WithHeader("Content-Type", "text/html; charset=utf-8")
  103. builder.WithHeader("Cache-Control", "no-cache, max-age=0, must-revalidate, no-store")
  104. builder.WithBodyAsString("Page Not Found")
  105. builder.Write()
  106. }
  107. // HTMLRedirect redirects the user to another location.
  108. func HTMLRedirect(w http.ResponseWriter, r *http.Request, uri string) {
  109. http.Redirect(w, r, uri, http.StatusFound)
  110. }
  111. // HTMLRequestedRangeNotSatisfiable sends a range not satisfiable error to the client.
  112. func HTMLRequestedRangeNotSatisfiable(w http.ResponseWriter, r *http.Request, contentRange string) {
  113. slog.Warn(http.StatusText(http.StatusRequestedRangeNotSatisfiable),
  114. slog.String("client_ip", request.ClientIP(r)),
  115. slog.Group("request",
  116. slog.String("method", r.Method),
  117. slog.String("uri", r.RequestURI),
  118. slog.String("user_agent", r.UserAgent()),
  119. ),
  120. slog.Group("response",
  121. slog.Int("status_code", http.StatusRequestedRangeNotSatisfiable),
  122. ),
  123. )
  124. builder := NewBuilder(w, r)
  125. builder.WithStatus(http.StatusRequestedRangeNotSatisfiable)
  126. builder.WithHeader("Content-Type", "text/html; charset=utf-8")
  127. builder.WithHeader("Cache-Control", "no-cache, max-age=0, must-revalidate, no-store")
  128. builder.WithHeader("Content-Range", contentRange)
  129. builder.WithBodyAsString("Range Not Satisfiable")
  130. builder.Write()
  131. }