feat: add an ID for every request

- Add a middleware to add an ID for every request for the purpose of
  cross referencing requests and errors.
- Add functions for handling client and server errors.
This commit is contained in:
Dan Anglin 2024-10-17 18:25:43 +01:00
parent 78cbc1bac5
commit 9cb1f8ed4b
Signed by: dananglin
GPG key ID: 0C1D44CFBEE68638
2 changed files with 56 additions and 12 deletions

View file

@ -2,21 +2,45 @@ package server
import ( import (
"encoding/json" "encoding/json"
"fmt"
"log/slog" "log/slog"
"net/http" "net/http"
) )
func sendJSONResponse(w http.ResponseWriter, statusCode int, payload any) { func sendResponse(writer http.ResponseWriter, statusCode int, payload any) {
data, err := json.Marshal(payload) data, err := json.Marshal(payload)
if err != nil { if err != nil {
slog.Error("Error marshalling the response to JSON", "error", err.Error()) sendServerError(
writer,
w.WriteHeader(http.StatusInternalServerError) fmt.Errorf("error marshalling the JSON response: %w", err),
)
return return
} }
w.Header().Set("Content-Type", "application/json") writer.Header().Set("Content-Type", "application/json")
w.WriteHeader(statusCode) writer.WriteHeader(statusCode)
_, _ = w.Write(data) _, _ = writer.Write(data)
}
func sendClientError(writer http.ResponseWriter, statusCode int, err error) {
sendErrorResponse(
writer,
statusCode,
"Client Error: "+err.Error(),
)
}
func sendServerError(writer http.ResponseWriter, err error) {
sendErrorResponse(
writer,
http.StatusInternalServerError,
"Server Error: "+err.Error(),
)
}
func sendErrorResponse(writer http.ResponseWriter, statusCode int, message string) {
slog.Error(message, "request-id", writer.Header().Get("X-Request-ID"))
http.Error(writer, http.StatusText(statusCode), statusCode)
} }

View file

@ -1,7 +1,10 @@
package server package server
import ( import (
"crypto/rand"
"encoding/hex"
"fmt" "fmt"
"log/slog"
"net/http" "net/http"
"codeflow.dananglin.me.uk/apollo/indieauth-server/internal/config" "codeflow.dananglin.me.uk/apollo/indieauth-server/internal/config"
@ -10,13 +13,30 @@ import (
func newMux(cfg config.Config) *http.ServeMux { func newMux(cfg config.Config) *http.ServeMux {
mux := http.NewServeMux() mux := http.NewServeMux()
mux.HandleFunc("GET /.well-known/oauth-authorization-server", metadataHandleFunc(cfg.Domain)) mux.Handle("GET /.well-known/oauth-authorization-server", setRequestID(metadataHandler(cfg.Domain)))
return mux return mux
} }
func metadataHandleFunc(domain string) http.HandlerFunc { func setRequestID(next http.Handler) http.Handler {
return func(writer http.ResponseWriter, _ *http.Request) { return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
requestID := "UNKNOWN"
id := make([]byte, 16)
if _, err := rand.Read(id); err != nil {
slog.Error("unable to create the request ID.", "error", err.Error())
} else {
requestID = hex.EncodeToString(id)
}
writer.Header().Set("X-Request-ID", requestID)
next.ServeHTTP(writer, request)
})
}
func metadataHandler(domain string) http.Handler {
return http.HandlerFunc(func(writer http.ResponseWriter, _ *http.Request) {
metadata := struct { metadata := struct {
Issuer string `json:"issuer"` Issuer string `json:"issuer"`
AuthorizationEndpoint string `json:"authorization_endpoint"` AuthorizationEndpoint string `json:"authorization_endpoint"`
@ -31,6 +51,6 @@ func metadataHandleFunc(domain string) http.HandlerFunc {
CodeChallengeMethodsSupported: []string{"S256"}, CodeChallengeMethodsSupported: []string{"S256"},
} }
sendJSONResponse(writer, http.StatusOK, metadata) sendResponse(writer, http.StatusOK, metadata)
} })
} }