diff --git a/pkg/api/server.go b/pkg/api/server.go index 02b4a548..dd51cb32 100644 --- a/pkg/api/server.go +++ b/pkg/api/server.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "encoding/hex" + "encoding/json" "errors" "io" "log/slog" @@ -43,8 +44,21 @@ var ( headersContentPlain = map[string][]string{ http.CanonicalHeaderKey(common.HeaderContentType): []string{common.ContentTypePlain}, } + invalidPropertyResponse []byte ) +func init() { + var err error + vr := &VerificationResponse{ + Success: false, + Code: puzzle.InvalidPropertyError, + } + invalidPropertyResponse, err = json.Marshal(vr) + if err != nil { + panic(err) + } +} + type Server struct { APIHeaders map[string][]string Stage string @@ -282,7 +296,7 @@ func (s *Server) recaptchaVerifyHandler(w http.ResponseWriter, r *http.Request) propertyID := payload.Puzzle().PropertyID() if propertyExternalID := db.UUIDFromSiteKey(sitekey); !bytes.Equal(propertyExternalID.Bytes[:], propertyID[:]) { slog.WarnContext(ctx, "Expected property ID does not match", "expected", sitekey, "actual", hex.EncodeToString(propertyID[:])) - http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) + common.SendReponse(ctx, w, invalidPropertyResponse, common.JSONContentHeaders, common.NoCacheHeaders, s.APIHeaders) return } } @@ -353,7 +367,7 @@ func (s *Server) pcVerifyHandler(w http.ResponseWriter, r *http.Request) { propertyID := payload.Puzzle().PropertyID() if propertyExternalID := db.UUIDFromSiteKey(sitekey); !bytes.Equal(propertyExternalID.Bytes[:], propertyID[:]) { slog.WarnContext(ctx, "Expected property ID does not match", "expected", sitekey, "actual", hex.EncodeToString(propertyID[:])) - http.Error(w, "Expected sitekey does not match solution puzzle", http.StatusBadRequest) + common.SendReponse(ctx, w, invalidPropertyResponse, common.JSONContentHeaders, common.NoCacheHeaders, s.APIHeaders) return } } diff --git a/pkg/api/verify_test.go b/pkg/api/verify_test.go index 7597885e..060ece85 100644 --- a/pkg/api/verify_test.go +++ b/pkg/api/verify_test.go @@ -186,8 +186,8 @@ func TestVerifyPuzzleWrongExpectedSitekey(t *testing.T) { t.Fatal(err) } - if resp.StatusCode != http.StatusBadRequest { - t.Errorf("Unexpected submit status code %d", resp.StatusCode) + if err := checkSiteVerifyError(resp, puzzle.InvalidPropertyError); err != nil { + t.Fatal(err) } } @@ -226,8 +226,8 @@ func TestSiteVerifyWrongExpectedSitekey(t *testing.T) { t.Fatal(err) } - if resp.StatusCode != http.StatusBadRequest { - t.Errorf("Unexpected submit status code %d", resp.StatusCode) + if err := checkSiteVerifyError(resp, puzzle.InvalidPropertyError); err != nil { + t.Fatal(err) } } diff --git a/pkg/common/middlewares.go b/pkg/common/middlewares.go index 8d43a51d..936ca71a 100644 --- a/pkg/common/middlewares.go +++ b/pkg/common/middlewares.go @@ -42,6 +42,9 @@ var ( HtmlContentHeaders = map[string][]string{ http.CanonicalHeaderKey(HeaderContentType): []string{ContentTypeHTML}, } + JSONContentHeaders = map[string][]string{ + http.CanonicalHeaderKey(HeaderContentType): []string{ContentTypeJSON}, + } ) func NoopMiddleware(next http.Handler) http.Handler { diff --git a/pkg/common/utils.go b/pkg/common/utils.go index f3b9d9b5..96fc4722 100644 --- a/pkg/common/utils.go +++ b/pkg/common/utils.go @@ -68,6 +68,22 @@ func MaskEmail(email string, mask rune) string { return prefix + xxx + suffix + "@" + parts[1] } +func SendReponse(ctx context.Context, w http.ResponseWriter, response []byte, headers ...map[string][]string) { + wHeader := w.Header() + for _, hh := range headers { + for key, value := range hh { + wHeader[key] = value + } + } + + n, err := w.Write(response) + if err != nil { + slog.ErrorContext(ctx, "Failed to send response", ErrAttr(err)) + } else { + slog.Log(ctx, LevelTrace, "Sent response", "size", len(response), "sent", n) + } +} + func SendJSONResponse(ctx context.Context, w http.ResponseWriter, data interface{}, headers ...map[string][]string) { response, err := json.Marshal(data) if err != nil {