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") ErrReadingFile = errors.New("Error reading file") ) // 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, ErrReadingFile: 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) }