From 9cb1f8ed4bb918ba7953abc4b1ffd54bd18278c9 Mon Sep 17 00:00:00 2001 From: Dan Anglin Date: Thu, 17 Oct 2024 18:25:43 +0100 Subject: [PATCH] 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. --- internal/server/responses.go | 38 +++++++++++++++++++++++++++++------- internal/server/router.go | 30 +++++++++++++++++++++++----- 2 files changed, 56 insertions(+), 12 deletions(-) diff --git a/internal/server/responses.go b/internal/server/responses.go index 8341792..9c4fddb 100644 --- a/internal/server/responses.go +++ b/internal/server/responses.go @@ -2,21 +2,45 @@ package server import ( "encoding/json" + "fmt" "log/slog" "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) if err != nil { - slog.Error("Error marshalling the response to JSON", "error", err.Error()) - - w.WriteHeader(http.StatusInternalServerError) + sendServerError( + writer, + fmt.Errorf("error marshalling the JSON response: %w", err), + ) return } - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(statusCode) - _, _ = w.Write(data) + writer.Header().Set("Content-Type", "application/json") + writer.WriteHeader(statusCode) + _, _ = 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) } diff --git a/internal/server/router.go b/internal/server/router.go index 28366ea..22a0225 100644 --- a/internal/server/router.go +++ b/internal/server/router.go @@ -1,7 +1,10 @@ package server import ( + "crypto/rand" + "encoding/hex" "fmt" + "log/slog" "net/http" "codeflow.dananglin.me.uk/apollo/indieauth-server/internal/config" @@ -10,13 +13,30 @@ import ( func newMux(cfg config.Config) *http.ServeMux { 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 } -func metadataHandleFunc(domain string) http.HandlerFunc { - return func(writer http.ResponseWriter, _ *http.Request) { +func setRequestID(next http.Handler) http.Handler { + 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 { Issuer string `json:"issuer"` AuthorizationEndpoint string `json:"authorization_endpoint"` @@ -31,6 +51,6 @@ func metadataHandleFunc(domain string) http.HandlerFunc { CodeChallengeMethodsSupported: []string{"S256"}, } - sendJSONResponse(writer, http.StatusOK, metadata) - } + sendResponse(writer, http.StatusOK, metadata) + }) }