summary refs log tree commit diff
path: root/vendor/go.mau.fi/util/exhttp
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/go.mau.fi/util/exhttp')
-rw-r--r--vendor/go.mau.fi/util/exhttp/cors.go26
-rw-r--r--vendor/go.mau.fi/util/exhttp/handleerrors.go58
-rw-r--r--vendor/go.mau.fi/util/exhttp/json.go27
-rw-r--r--vendor/go.mau.fi/util/exhttp/middleware.go24
4 files changed, 135 insertions, 0 deletions
diff --git a/vendor/go.mau.fi/util/exhttp/cors.go b/vendor/go.mau.fi/util/exhttp/cors.go
new file mode 100644
index 0000000..037be8d
--- /dev/null
+++ b/vendor/go.mau.fi/util/exhttp/cors.go
@@ -0,0 +1,26 @@
+package exhttp
+
+import "net/http"
+
+func AddCORSHeaders(w http.ResponseWriter) {
+	// Recommended CORS headers can be found in https://spec.matrix.org/v1.3/client-server-api/#web-browser-clients
+	w.Header().Set("Access-Control-Allow-Origin", "*")
+	w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
+	w.Header().Set("Access-Control-Allow-Headers", "X-Requested-With, Content-Type, Authorization")
+	w.Header().Set("Content-Security-Policy", "sandbox; default-src 'none'; script-src 'none'; plugin-types application/pdf; style-src 'unsafe-inline'; object-src 'self';")
+	// Allow browsers to cache above for 1 day
+	w.Header().Set("Access-Control-Max-Age", "86400")
+}
+
+// CORSMiddleware adds CORS headers to the response and handles OPTIONS
+// requests by returning 200 OK immediately.
+func CORSMiddleware(next http.Handler) http.Handler {
+	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		AddCORSHeaders(w)
+		if r.Method == http.MethodOptions {
+			w.WriteHeader(http.StatusOK)
+			return
+		}
+		next.ServeHTTP(w, r)
+	})
+}
diff --git a/vendor/go.mau.fi/util/exhttp/handleerrors.go b/vendor/go.mau.fi/util/exhttp/handleerrors.go
new file mode 100644
index 0000000..d2d37b1
--- /dev/null
+++ b/vendor/go.mau.fi/util/exhttp/handleerrors.go
@@ -0,0 +1,58 @@
+package exhttp
+
+import "net/http"
+
+type ErrorBodyGenerators struct {
+	NotFound         func() []byte
+	MethodNotAllowed func() []byte
+}
+
+func HandleErrors(next http.Handler, gen ErrorBodyGenerators) http.Handler {
+	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		next.ServeHTTP(&bodyOverrider{
+			ResponseWriter:                      w,
+			statusNotFoundBodyGenerator:         gen.NotFound,
+			statusMethodNotAllowedBodyGenerator: gen.MethodNotAllowed,
+		}, r)
+	})
+}
+
+type bodyOverrider struct {
+	http.ResponseWriter
+
+	code     int
+	override bool
+
+	statusNotFoundBodyGenerator         func() []byte
+	statusMethodNotAllowedBodyGenerator func() []byte
+}
+
+var _ http.ResponseWriter = (*bodyOverrider)(nil)
+
+func (b *bodyOverrider) WriteHeader(code int) {
+	if b.Header().Get("Content-Type") == "text/plain; charset=utf-8" {
+		b.Header().Set("Content-Type", "application/json")
+
+		b.override = true
+	}
+
+	b.code = code
+	b.ResponseWriter.WriteHeader(code)
+}
+
+func (b *bodyOverrider) Write(body []byte) (int, error) {
+	if b.override {
+		switch b.code {
+		case http.StatusNotFound:
+			if b.statusNotFoundBodyGenerator != nil {
+				body = b.statusNotFoundBodyGenerator()
+			}
+		case http.StatusMethodNotAllowed:
+			if b.statusMethodNotAllowedBodyGenerator != nil {
+				body = b.statusMethodNotAllowedBodyGenerator()
+			}
+		}
+	}
+
+	return b.ResponseWriter.Write(body)
+}
diff --git a/vendor/go.mau.fi/util/exhttp/json.go b/vendor/go.mau.fi/util/exhttp/json.go
new file mode 100644
index 0000000..48c8349
--- /dev/null
+++ b/vendor/go.mau.fi/util/exhttp/json.go
@@ -0,0 +1,27 @@
+package exhttp
+
+import (
+	"encoding/json"
+	"net/http"
+)
+
+func WriteJSONResponse(w http.ResponseWriter, httpStatusCode int, jsonData any) {
+	AddCORSHeaders(w)
+	w.Header().Set("Content-Type", "application/json")
+	w.WriteHeader(httpStatusCode)
+	_ = json.NewEncoder(w).Encode(jsonData)
+}
+
+func WriteJSONData(w http.ResponseWriter, httpStatusCode int, data []byte) {
+	AddCORSHeaders(w)
+	w.Header().Set("Content-Type", "application/json")
+	w.WriteHeader(httpStatusCode)
+	_, _ = w.Write(data)
+}
+
+func WriteEmptyJSONResponse(w http.ResponseWriter, httpStatusCode int) {
+	AddCORSHeaders(w)
+	w.Header().Set("Content-Type", "application/json")
+	w.WriteHeader(httpStatusCode)
+	_, _ = w.Write([]byte("{}"))
+}
diff --git a/vendor/go.mau.fi/util/exhttp/middleware.go b/vendor/go.mau.fi/util/exhttp/middleware.go
new file mode 100644
index 0000000..733d348
--- /dev/null
+++ b/vendor/go.mau.fi/util/exhttp/middleware.go
@@ -0,0 +1,24 @@
+package exhttp
+
+import "net/http"
+
+// Middleware represents a middleware that can be applied to an [http.Handler].
+type Middleware func(http.Handler) http.Handler
+
+// ApplyMiddleware applies the provided [Middleware] functions to the given
+// router. The middlewares will be applied in the order they are provided.
+func ApplyMiddleware(router http.Handler, middlewares ...Middleware) http.Handler {
+	// Apply middlewares in reverse order because the first middleware provided
+	// needs to be the outermost one.
+	for i := len(middlewares) - 1; i >= 0; i-- {
+		router = middlewares[i](router)
+	}
+	return router
+}
+
+// StripPrefix is a wrapper for [http.StripPrefix] is compatible with the middleware pattern.
+func StripPrefix(prefix string) Middleware {
+	return func(next http.Handler) http.Handler {
+		return http.StripPrefix(prefix, next)
+	}
+}