diff --git a/errors.go b/errors.go new file mode 100644 index 0000000..abf1640 --- /dev/null +++ b/errors.go @@ -0,0 +1,57 @@ +package keydbextension + +import ( + "encoding/json" + "fmt" + "net/http" + "sync" +) + +const ( + ErrNoAuthToken = "ErrNoAuthToken" + ErrUnauthorized = "ErrUnauthorized" + ErrIncompleteReq = "ErrIncompleteReq" + ErrConnFailed = "ErrConnFailed" + ErrReqNotProcessed = "ErrReqNotProcessed" + ErrUnknown = "ErrUnknown" +) + +var ( + errorResponses map[string][]byte + errorStatusCodes map[string]int + errorResponsesOnce sync.Once +) + +func initErrorResponses() { + errorResponses = make(map[string][]byte) + errorStatusCodes = make(map[string]int) + + errors := []struct { + key string + code int + message string + }{ + {ErrNoAuthToken, http.StatusUnauthorized, "No auth token provided"}, + {ErrUnauthorized, http.StatusForbidden, "Unauthorized access"}, + {ErrIncompleteReq, http.StatusBadRequest, "Request was incomplete"}, + {ErrConnFailed, 523, "Connection to GuardianPT failed"}, + {ErrReqNotProcessed, 522, "Request could not be processed"}, + {ErrUnknown, 520, "An unknown error occurred"}, + } + + for _, err := range errors { + error_code := fmt.Sprintf("0x%08X", 0xC0043293+err.code) + response, _ := json.Marshal(map[string]interface{}{ + "error": true, + "code": error_code, + "message": err.message, + }) + errorResponses[err.key] = response + errorStatusCodes[err.key] = err.code + } +} + +func getHTTPStatusCode(key string) int { + errorResponsesOnce.Do(initErrorResponses) + return errorStatusCodes[key] +} diff --git a/keydb_extension.go b/keydb_extension.go index 8d115f2..6e647df 100644 --- a/keydb_extension.go +++ b/keydb_extension.go @@ -2,7 +2,6 @@ package keydbextension import ( "context" - "encoding/json" "fmt" "net/http" "strconv" @@ -69,18 +68,18 @@ func (handler *KeyDBHandler) Provision(ctx caddy.Context) error { func (handler KeyDBHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request, next caddyhttp.Handler) error { authToken := request.Header.Get(authTokenHeader) if authToken == "" { - sendJSONError(writer, http.StatusUnauthorized, "No auth token provided") + sendJSONError(writer, ErrNoAuthToken) return nil } if !handler.validTokens[authToken] { - sendJSONError(writer, http.StatusForbidden, "Unauthorized access") + sendJSONError(writer, ErrUnauthorized) return nil } question := request.URL.Query().Get("q") if question == "" { - sendJSONError(writer, http.StatusBadRequest, "Request was incomplete") + sendJSONError(writer, ErrIncompleteReq) return nil } @@ -89,10 +88,10 @@ func (handler KeyDBHandler) ServeHTTP(writer http.ResponseWriter, request *http. val, err := handler.client.Get(ctx, question).Result() if err == redis.Nil { - sendJSONError(writer, http.StatusNotFound, "Connection to GuardianPT failed") + sendJSONError(writer, ErrConnFailed) return nil } else if err != nil { - sendJSONError(writer, http.StatusInternalServerError, "Request could not be processed") + sendJSONError(writer, ErrReqNotProcessed) return err } @@ -144,18 +143,15 @@ func parseCaddyfile(helper httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, e } // sendJSONError writes a JSON-formatted error response to the provided http.ResponseWriter. -func sendJSONError(w http.ResponseWriter, code int, message string) { +func sendJSONError(w http.ResponseWriter, errorKey string) { + errorResponse, exists := errorResponses[errorKey] + if !exists { + errorResponse = errorResponses["ErrUnknown"] + } + w.Header().Set("Content-Type", "application/json") - w.WriteHeader(code) - - // Spooky high error codes - mysteriousCode := fmt.Sprintf("0x%08X", 0xC0043293+code) - - json.NewEncoder(w).Encode(map[string]interface{}{ - "error": true, - "code": mysteriousCode, - "message": message, - }) + w.WriteHeader(getHTTPStatusCode(errorKey)) + w.Write(errorResponse) } // The KeyDBHandler type implements several interfaces that allow it to be used as a Caddy module: