|
|
@@ -0,0 +1,160 @@
|
|
|
+package module
|
|
|
+
|
|
|
+import (
|
|
|
+ "encoding/json"
|
|
|
+ "errors"
|
|
|
+ "fmt"
|
|
|
+ "log"
|
|
|
+ "net/http"
|
|
|
+ "runtime"
|
|
|
+)
|
|
|
+
|
|
|
+// Err... are errors used globally
|
|
|
+var (
|
|
|
+ ErrUnknown = errors.New("Unknown error")
|
|
|
+ // ErrRetrievingImage = errors.New("Error retrieving image")
|
|
|
+ // ErrInvalidURL = errors.New("Passed URL is not valid")
|
|
|
+ // ErrDecodingImage = errors.New("Error decoding image")
|
|
|
+ // ErrWidthNoInteger = errors.New("Error parsing width parameter as integer")
|
|
|
+ // ErrHeightNoInteger = errors.New("Error parsing height parameter as integer")
|
|
|
+ // ErrAspectRatioWidthNoInteger = errors.New("Error parsing aspect ratio width parameter as integer")
|
|
|
+ // ErrAspectRatioHeightNoInteger = errors.New("Error parsing aspect ratio height parameter as integer")
|
|
|
+ ErrReadingRequestBody = errors.New("Error reading request body")
|
|
|
+ ErrDecodingJSON = errors.New("Error decoding JSON")
|
|
|
+ // ErrDecodingBase64 = errors.New("Error decoding base64 encoded image")
|
|
|
+ // ErrReadingCroppedImage = errors.New("Error reading cropped image")
|
|
|
+ // ErrCreatingThumbnail = errors.New("Error creating thumbnail")
|
|
|
+ // ErrDecodingImageInternal = errors.New("Error decoding image")
|
|
|
+ // ErrEncodingImage = errors.New("Error encoding image")
|
|
|
+ ErrSendingResponse = errors.New("Error sending response")
|
|
|
+ ErrEncodingJSON = errors.New("Error encoding JSON")
|
|
|
+ ErrDatabase = errors.New("Database error")
|
|
|
+ ErrRetrievingTestData = errors.New("Error receiving Testdata")
|
|
|
+ ErrParsingInt = errors.New("Error parsing string to int")
|
|
|
+)
|
|
|
+
|
|
|
+// StatusCodeMapper maps errors to HTTP Status codes
|
|
|
+var StatusCodeMapper = map[error]int{
|
|
|
+ ErrUnknown: http.StatusInternalServerError,
|
|
|
+ // ErrRetrievingImage: http.StatusBadRequest,
|
|
|
+ // ErrInvalidURL: http.StatusBadRequest,
|
|
|
+ // ErrDecodingImage: http.StatusBadRequest,
|
|
|
+ // ErrWidthNoInteger: http.StatusBadRequest,
|
|
|
+ // ErrHeightNoInteger: http.StatusBadRequest,
|
|
|
+ // ErrAspectRatioWidthNoInteger: http.StatusBadRequest,
|
|
|
+ // ErrAspectRatioHeightNoInteger: http.StatusBadRequest,
|
|
|
+ ErrReadingRequestBody: http.StatusBadRequest,
|
|
|
+ ErrDecodingJSON: http.StatusBadRequest,
|
|
|
+ // ErrDecodingBase64: http.StatusBadRequest,
|
|
|
+ // ErrReadingCroppedImage: http.StatusInternalServerError,
|
|
|
+ // ErrCreatingThumbnail: http.StatusInternalServerError,
|
|
|
+ // ErrDecodingImageInternal: http.StatusInternalServerError,
|
|
|
+ // ErrEncodingImage: http.StatusInternalServerError,
|
|
|
+ ErrSendingResponse: http.StatusInternalServerError,
|
|
|
+ ErrEncodingJSON: http.StatusInternalServerError,
|
|
|
+ ErrDatabase: http.StatusInternalServerError,
|
|
|
+ ErrRetrievingTestData: http.StatusBadGateway,
|
|
|
+ ErrParsingInt: http.StatusInternalServerError,
|
|
|
+}
|
|
|
+
|
|
|
+// MetaError is two errors in one
|
|
|
+type MetaError struct {
|
|
|
+ outerError error
|
|
|
+ innerError error
|
|
|
+ httpStatus int
|
|
|
+}
|
|
|
+
|
|
|
+// NewMetaError returns a new MetaError and automatically selects the suited HTTP Status Code
|
|
|
+func NewMetaError(outerError error, innerError error) MetaError {
|
|
|
+ return MetaError{outerError, innerError, StatusCodeMapper[outerError]}
|
|
|
+}
|
|
|
+
|
|
|
+// NewMetaErrorWithStatus returns a new MetaError with manually selected HTTP Status Code
|
|
|
+func NewMetaErrorWithStatus(outerError error, innerError error, httpStatus int) MetaError {
|
|
|
+ return MetaError{outerError, innerError, httpStatus}
|
|
|
+}
|
|
|
+
|
|
|
+func (err MetaError) Error() string {
|
|
|
+ return err.outerError.Error() + ": " + err.innerError.Error()
|
|
|
+}
|
|
|
+
|
|
|
+type StatusError struct {
|
|
|
+ error
|
|
|
+ httpStatus int
|
|
|
+}
|
|
|
+
|
|
|
+func NewStatusError(err error, status int) StatusError {
|
|
|
+ return StatusError{err, status}
|
|
|
+}
|
|
|
+
|
|
|
+// HandleError will handle errors
|
|
|
+func HandleError(outerErr error, innerErr error) {
|
|
|
+ if innerErr != nil {
|
|
|
+
|
|
|
+ switch outerErr {
|
|
|
+ case ErrDatabase:
|
|
|
+ switch innerErr.Error() {
|
|
|
+ case "record not found":
|
|
|
+ panic(NewStatusError(innerErr, http.StatusNotFound))
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ panic(NewMetaError(outerErr, innerErr))
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// HTTPStatusHandler ...
|
|
|
+type HTTPStatusHandler struct {
|
|
|
+ Debug bool // TODO implement
|
|
|
+}
|
|
|
+
|
|
|
+// NewHTTPStatusHandler ...
|
|
|
+func NewHTTPStatusHandler() *HTTPStatusHandler {
|
|
|
+ return &HTTPStatusHandler{
|
|
|
+ Debug: false,
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// HTTPStatusRecovery catches errors and serves http statuses
|
|
|
+func (rec *HTTPStatusHandler) ServeHTTP(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
|
|
|
+ defer func() {
|
|
|
+ if err := recover(); err != nil {
|
|
|
+ // get the stack traces and print it out
|
|
|
+ // stack := stack(0)
|
|
|
+ // log.Printf("PANIC: %s\n%s", err, stack)
|
|
|
+
|
|
|
+ // convert err to a string
|
|
|
+ var errMsg string
|
|
|
+ if e, ok := err.(error); ok {
|
|
|
+ errMsg = e.Error()
|
|
|
+ } else {
|
|
|
+ errMsg = fmt.Sprint(err)
|
|
|
+ }
|
|
|
+
|
|
|
+ // Logging
|
|
|
+ _, file, line, _ := runtime.Caller(4)
|
|
|
+ log.Printf("%s:%d: %s", file, line, errMsg)
|
|
|
+
|
|
|
+ errText, jErr := json.Marshal(map[string]string{"status": errMsg})
|
|
|
+ if jErr != nil {
|
|
|
+ log.Println(jErr)
|
|
|
+ // TODO handle error
|
|
|
+ }
|
|
|
+
|
|
|
+ if _, ok := err.(MetaError); ok {
|
|
|
+ http.Error(w, string(errText), err.(MetaError).httpStatus)
|
|
|
+ } else if _, ok = err.(StatusError); ok {
|
|
|
+ http.Error(w, string(errText), err.(StatusError).httpStatus)
|
|
|
+ } else {
|
|
|
+ http.Error(w, string(errText), http.StatusInternalServerError)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }()
|
|
|
+
|
|
|
+ next(w, r)
|
|
|
+}
|
|
|
+
|
|
|
+// Log logs
|
|
|
+func Log(text string) {
|
|
|
+ log.Println(text)
|
|
|
+}
|