refactor HTTP error.

This commit is contained in:
Miroslav Šedivý 2021-09-16 20:16:51 +02:00
parent d46c5d9d30
commit 4fa11e6a2a
15 changed files with 166 additions and 102 deletions

View File

@ -2,7 +2,6 @@ package members
import (
"encoding/json"
"fmt"
"io"
"net/http"
@ -18,13 +17,13 @@ type MemberBulkUpdatePayload struct {
func (h *MembersHandler) membersBulkUpdate(w http.ResponseWriter, r *http.Request) {
bytes, err := io.ReadAll(r.Body)
if err != nil {
utils.HttpInternalServerError(w, err)
utils.HttpBadRequest(w).WithInternalErr(err).Msg("unable to read post body")
return
}
header := &MemberBulkUpdatePayload{}
if err := json.Unmarshal(bytes, &header); err != nil {
utils.HttpInternalServerError(w, err)
utils.HttpBadRequest(w).WithInternalErr(err).Msg("unable to unmarshal payload")
return
}
@ -32,7 +31,7 @@ func (h *MembersHandler) membersBulkUpdate(w http.ResponseWriter, r *http.Reques
// TODO: Bulk select?
profile, err := h.members.Select(memberId)
if err != nil {
utils.HttpInternalServerError(w, fmt.Sprintf("member %s: %v", memberId, err))
utils.HttpInternalServerError(w, err).WithInternalMsg("unable to select member profile").Msgf("failed to update member %s", memberId)
return
}
@ -41,12 +40,12 @@ func (h *MembersHandler) membersBulkUpdate(w http.ResponseWriter, r *http.Reques
}
if err := json.Unmarshal(bytes, &body); err != nil {
utils.HttpInternalServerError(w, fmt.Sprintf("member %s: %v", memberId, err))
utils.HttpBadRequest(w).WithInternalErr(err).Msgf("unable to unmarshal payload for member %s", memberId)
return
}
if err := h.members.UpdateProfile(memberId, body.Profile); err != nil {
utils.HttpInternalServerError(w, fmt.Sprintf("member %s: %v", memberId, err))
utils.HttpInternalServerError(w, err).WithInternalMsg("unable to update member profile").Msgf("failed to update member %s", memberId)
return
}
}

View File

@ -39,7 +39,7 @@ func (h *MembersHandler) membersList(w http.ResponseWriter, r *http.Request) {
entries, err := h.members.SelectAll(limit, offset)
if err != nil {
utils.HttpInternalServerError(w, err)
utils.HttpInternalServerError(w, err).Send()
return
}
@ -72,21 +72,21 @@ func (h *MembersHandler) membersCreate(w http.ResponseWriter, r *http.Request) {
}
if data.Username == "" {
utils.HttpBadRequest(w, "username cannot be empty")
utils.HttpBadRequest(w).Msg("username cannot be empty")
return
}
if data.Password == "" {
utils.HttpBadRequest(w, "password cannot be empty")
utils.HttpBadRequest(w).Msg("password cannot be empty")
return
}
id, err := h.members.Insert(data.Username, data.Password, data.Profile)
if err != nil {
if errors.Is(err, types.ErrMemberAlreadyExists) {
utils.HttpUnprocessableEntity(w, err)
utils.HttpUnprocessableEntity(w).Msg("member already exists")
} else {
utils.HttpInternalServerError(w, err)
utils.HttpInternalServerError(w, err).Send()
}
return
}
@ -113,7 +113,7 @@ func (h *MembersHandler) membersUpdateProfile(w http.ResponseWriter, r *http.Req
}
if err := h.members.UpdateProfile(member.ID, profile); err != nil {
utils.HttpInternalServerError(w, err)
utils.HttpInternalServerError(w, err).Send()
return
}
@ -129,7 +129,7 @@ func (h *MembersHandler) membersUpdatePassword(w http.ResponseWriter, r *http.Re
}
if err := h.members.UpdatePassword(member.ID, data.Password); err != nil {
utils.HttpInternalServerError(w, err)
utils.HttpInternalServerError(w, err).Send()
return
}
@ -140,7 +140,7 @@ func (h *MembersHandler) membersDelete(w http.ResponseWriter, r *http.Request) {
member := GetMember(r)
if err := h.members.Delete(member.ID); err != nil {
utils.HttpInternalServerError(w, err)
utils.HttpInternalServerError(w, err).Send()
return
}

View File

@ -71,9 +71,9 @@ func (h *MembersHandler) ExtractMember(next http.Handler) http.Handler {
profile, err := h.members.Select(memberId)
if err != nil {
if errors.Is(err, types.ErrMemberDoesNotExist) {
utils.HttpNotFound(w, err)
utils.HttpNotFound(w).Msg("member not found")
} else {
utils.HttpInternalServerError(w, err)
utils.HttpInternalServerError(w, err).Send()
}
return

View File

@ -28,18 +28,18 @@ func (h *RoomHandler) boradcastStart(w http.ResponseWriter, r *http.Request) {
}
if data.URL == "" {
utils.HttpBadRequest(w, "missing broadcast URL")
utils.HttpBadRequest(w).Msg("missing broadcast URL")
return
}
broadcast := h.capture.Broadcast()
if broadcast.Started() {
utils.HttpUnprocessableEntity(w, "server is already broadcasting")
utils.HttpUnprocessableEntity(w).Msg("server is already broadcasting")
return
}
if err := broadcast.Start(data.URL); err != nil {
utils.HttpInternalServerError(w, err)
utils.HttpInternalServerError(w, err).Send()
return
}
@ -56,7 +56,7 @@ func (h *RoomHandler) boradcastStart(w http.ResponseWriter, r *http.Request) {
func (h *RoomHandler) boradcastStop(w http.ResponseWriter, r *http.Request) {
broadcast := h.capture.Broadcast()
if !broadcast.Started() {
utils.HttpUnprocessableEntity(w, "server is not broadcasting")
utils.HttpUnprocessableEntity(w).Msg("server is not broadcasting")
return
}

View File

@ -18,7 +18,7 @@ type ClipboardPayload struct {
func (h *RoomHandler) clipboardGetText(w http.ResponseWriter, r *http.Request) {
data, err := h.desktop.ClipboardGetText()
if err != nil {
utils.HttpInternalServerError(w, err)
utils.HttpInternalServerError(w, err).Send()
return
}
@ -40,7 +40,7 @@ func (h *RoomHandler) clipboardSetText(w http.ResponseWriter, r *http.Request) {
})
if err != nil {
utils.HttpInternalServerError(w, err)
utils.HttpInternalServerError(w, err).Send()
return
}
@ -50,7 +50,7 @@ func (h *RoomHandler) clipboardSetText(w http.ResponseWriter, r *http.Request) {
func (h *RoomHandler) clipboardGetImage(w http.ResponseWriter, r *http.Request) {
bytes, err := h.desktop.ClipboardGetBinary("image/png")
if err != nil {
utils.HttpInternalServerError(w, err)
utils.HttpInternalServerError(w, err).Send()
return
}
@ -64,7 +64,7 @@ func (h *RoomHandler) clipboardGetImage(w http.ResponseWriter, r *http.Request)
func (h *RoomHandler) clipboardSetImage(w http.ResponseWriter, r *http.Request) {
err := r.ParseMultipartForm(MAX_UPLOAD_SIZE)
if err != nil {
utils.HttpBadRequest(w, "failed to parse multipart form")
utils.HttpBadRequest(w).WithInternalErr(err).Msg("failed to parse multipart form")
return
}
@ -73,7 +73,7 @@ func (h *RoomHandler) clipboardSetImage(w http.ResponseWriter, r *http.Request)
file, header, err := r.FormFile("file")
if err != nil {
utils.HttpBadRequest(w, "no file received")
utils.HttpBadRequest(w).WithInternalErr(err).Msg("no file received")
return
}
@ -81,20 +81,20 @@ func (h *RoomHandler) clipboardSetImage(w http.ResponseWriter, r *http.Request)
mime := header.Header.Get("Content-Type")
if !strings.HasPrefix(mime, "image/") {
utils.HttpBadRequest(w, "file must be image")
utils.HttpBadRequest(w).Msg("file must be image")
return
}
buffer := new(bytes.Buffer)
_, err = buffer.ReadFrom(file)
if err != nil {
utils.HttpInternalServerError(w, err)
utils.HttpInternalServerError(w, err).WithInternalMsg("unable to read from uploaded file").Send()
return
}
err = h.desktop.ClipboardSetBinary("image/png", buffer.Bytes())
if err != nil {
utils.HttpInternalServerError(w, err)
utils.HttpInternalServerError(w, err).WithInternalMsg("unable set image to clipboard").Send()
return
}
@ -104,7 +104,7 @@ func (h *RoomHandler) clipboardSetImage(w http.ResponseWriter, r *http.Request)
func (h *RoomHandler) clipboardGetTargets(w http.ResponseWriter, r *http.Request) {
targets, err := h.desktop.ClipboardGetTargets()
if err != nil {
utils.HttpInternalServerError(w, err)
utils.HttpInternalServerError(w, err).Send()
return
}

View File

@ -36,7 +36,7 @@ func (h *RoomHandler) controlStatus(w http.ResponseWriter, r *http.Request) {
func (h *RoomHandler) controlRequest(w http.ResponseWriter, r *http.Request) {
host := h.sessions.GetHost()
if host != nil {
utils.HttpUnprocessableEntity(w, "there is already a host")
utils.HttpUnprocessableEntity(w).Msg("there is already a host")
return
}
@ -49,7 +49,7 @@ func (h *RoomHandler) controlRequest(w http.ResponseWriter, r *http.Request) {
func (h *RoomHandler) controlRelease(w http.ResponseWriter, r *http.Request) {
session := auth.GetSession(r)
if !session.IsHost() {
utils.HttpUnprocessableEntity(w, "session is not the host")
utils.HttpUnprocessableEntity(w).Msg("session is not the host")
return
}
@ -71,12 +71,12 @@ func (h *RoomHandler) controlGive(w http.ResponseWriter, r *http.Request) {
target, ok := h.sessions.Get(sessionId)
if !ok {
utils.HttpNotFound(w, "target session was not found")
utils.HttpNotFound(w).Msg("target session was not found")
return
}
if !target.Profile().CanHost {
utils.HttpBadRequest(w, "target session is not allowed to host")
utils.HttpBadRequest(w).Msg("target session is not allowed to host")
return
}

View File

@ -90,7 +90,7 @@ func (h *RoomHandler) uploadMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
session := auth.GetSession(r)
if !session.IsHost() && (!session.Profile().CanHost || !h.sessions.ImplicitHosting()) {
utils.HttpForbidden(w, "without implicit hosting, only host can upload files")
utils.HttpForbidden(w).Msg("without implicit hosting, only host can upload files")
} else {
next.ServeHTTP(w, r)
}

View File

@ -29,7 +29,7 @@ func (h *RoomHandler) keyboardMapSet(w http.ResponseWriter, r *http.Request) {
})
if err != nil {
utils.HttpInternalServerError(w, "unable to change keyboard map")
utils.HttpInternalServerError(w, err).Send()
return
}
@ -40,7 +40,7 @@ func (h *RoomHandler) keyboardMapGet(w http.ResponseWriter, r *http.Request) {
data, err := h.desktop.GetKeyboardMap()
if err != nil {
utils.HttpInternalServerError(w, "unable to get keyboard map")
utils.HttpInternalServerError(w, err).Send()
return
}

View File

@ -20,7 +20,7 @@ func (h *RoomHandler) screenConfiguration(w http.ResponseWriter, r *http.Request
size := h.desktop.GetScreenSize()
if size == nil {
utils.HttpInternalServerError(w, "unable to get screen configuration")
utils.HttpInternalServerError(w, nil).WithInternalMsg("unable to get screen configuration").Send()
return
}
@ -42,7 +42,7 @@ func (h *RoomHandler) screenConfigurationChange(w http.ResponseWriter, r *http.R
Height: data.Height,
Rate: data.Rate,
}); err != nil {
utils.HttpUnprocessableEntity(w, err)
utils.HttpUnprocessableEntity(w).WithInternalErr(err).Msg("cannot set screen size")
return
}
@ -83,7 +83,7 @@ func (h *RoomHandler) screenShotGet(w http.ResponseWriter, r *http.Request) {
img := h.desktop.GetScreenshotImage()
bytes, err := utils.CreateJPGImage(img, quality)
if err != nil {
utils.HttpInternalServerError(w, err)
utils.HttpInternalServerError(w, err).Send()
return
}
@ -96,13 +96,13 @@ func (h *RoomHandler) screenShotGet(w http.ResponseWriter, r *http.Request) {
func (h *RoomHandler) screenCastGet(w http.ResponseWriter, r *http.Request) {
screencast := h.capture.Screencast()
if !screencast.Enabled() {
utils.HttpBadRequest(w, "screencast pipeline is not enabled")
utils.HttpBadRequest(w).Msg("screencast pipeline is not enabled")
return
}
bytes, err := screencast.Image()
if err != nil {
utils.HttpInternalServerError(w, err)
utils.HttpInternalServerError(w, err).Send()
return
}

View File

@ -10,13 +10,15 @@ import (
"demodesk/neko/internal/utils"
)
// TODO: Extract file uploading to custom utility.
// maximum upload size of 32 MB
const maxUploadSize = 32 << 20
func (h *RoomHandler) uploadDrop(w http.ResponseWriter, r *http.Request) {
err := r.ParseMultipartForm(maxUploadSize)
if err != nil {
utils.HttpBadRequest(w, "failed to parse multipart form")
utils.HttpBadRequest(w).WithInternalErr(err).Msg("failed to parse multipart form")
return
}
@ -25,25 +27,25 @@ func (h *RoomHandler) uploadDrop(w http.ResponseWriter, r *http.Request) {
X, err := strconv.Atoi(r.FormValue("x"))
if err != nil {
utils.HttpBadRequest(w, "no X coordinate received")
utils.HttpBadRequest(w).WithInternalErr(err).Msg("no X coordinate received")
return
}
Y, err := strconv.Atoi(r.FormValue("y"))
if err != nil {
utils.HttpBadRequest(w, "no Y coordinate received")
utils.HttpBadRequest(w).WithInternalErr(err).Msg("no Y coordinate received")
return
}
req_files := r.MultipartForm.File["files"]
if len(req_files) == 0 {
utils.HttpBadRequest(w, "no files received")
utils.HttpBadRequest(w).Msg("no files received")
return
}
dir, err := os.MkdirTemp("", "neko-drop-*")
if err != nil {
utils.HttpInternalServerError(w, err)
utils.HttpInternalServerError(w, err).WithInternalMsg("unable to create temporary directory").Send()
return
}
@ -53,7 +55,7 @@ func (h *RoomHandler) uploadDrop(w http.ResponseWriter, r *http.Request) {
srcFile, err := req_file.Open()
if err != nil {
utils.HttpInternalServerError(w, err)
utils.HttpInternalServerError(w, err).WithInternalMsg("unable to open uploaded file").Send()
return
}
@ -61,7 +63,7 @@ func (h *RoomHandler) uploadDrop(w http.ResponseWriter, r *http.Request) {
dstFile, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
utils.HttpInternalServerError(w, err)
utils.HttpInternalServerError(w, err).WithInternalMsg("unable to open destination file").Send()
return
}
@ -69,7 +71,7 @@ func (h *RoomHandler) uploadDrop(w http.ResponseWriter, r *http.Request) {
_, err = io.Copy(dstFile, srcFile)
if err != nil {
utils.HttpInternalServerError(w, err)
utils.HttpInternalServerError(w, err).WithInternalMsg("unable to copy uploaded file to destination file").Send()
return
}
@ -77,7 +79,7 @@ func (h *RoomHandler) uploadDrop(w http.ResponseWriter, r *http.Request) {
}
if !h.desktop.DropFiles(X, Y, files) {
utils.HttpInternalServerError(w, "unable to drop files")
utils.HttpInternalServerError(w, nil).WithInternalMsg("unable to drop files").Send()
return
}
@ -87,7 +89,7 @@ func (h *RoomHandler) uploadDrop(w http.ResponseWriter, r *http.Request) {
func (h *RoomHandler) uploadDialogPost(w http.ResponseWriter, r *http.Request) {
err := r.ParseMultipartForm(maxUploadSize)
if err != nil {
utils.HttpBadRequest(w, "failed to parse multipart form")
utils.HttpBadRequest(w).WithInternalErr(err).Msg("failed to parse multipart form")
return
}
@ -95,19 +97,19 @@ func (h *RoomHandler) uploadDialogPost(w http.ResponseWriter, r *http.Request) {
defer r.MultipartForm.RemoveAll()
if !h.desktop.IsFileChooserDialogOpened() {
utils.HttpBadRequest(w, "open file chooser dialog first")
utils.HttpUnprocessableEntity(w).Msg("file chooser dialog is not open")
return
}
req_files := r.MultipartForm.File["files"]
if len(req_files) == 0 {
utils.HttpBadRequest(w, "no files received")
utils.HttpInternalServerError(w, err).WithInternalMsg("unable to copy uploaded file to destination file").Send()
return
}
dir, err := os.MkdirTemp("", "neko-dialog-*")
if err != nil {
utils.HttpInternalServerError(w, err)
utils.HttpInternalServerError(w, err).WithInternalMsg("unable to create temporary directory").Send()
return
}
@ -116,7 +118,7 @@ func (h *RoomHandler) uploadDialogPost(w http.ResponseWriter, r *http.Request) {
srcFile, err := req_file.Open()
if err != nil {
utils.HttpInternalServerError(w, err)
utils.HttpInternalServerError(w, err).WithInternalMsg("unable to open uploaded file").Send()
return
}
@ -124,7 +126,7 @@ func (h *RoomHandler) uploadDialogPost(w http.ResponseWriter, r *http.Request) {
dstFile, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
utils.HttpInternalServerError(w, err)
utils.HttpInternalServerError(w, err).WithInternalMsg("unable to open destination file").Send()
return
}
@ -132,13 +134,13 @@ func (h *RoomHandler) uploadDialogPost(w http.ResponseWriter, r *http.Request) {
_, err = io.Copy(dstFile, srcFile)
if err != nil {
utils.HttpInternalServerError(w, err)
utils.HttpInternalServerError(w, err).WithInternalMsg("unable to copy uploaded file to destination file").Send()
return
}
}
if err := h.desktop.HandleFileChooserDialog(dir); err != nil {
utils.HttpInternalServerError(w, "unable to handle file chooser dialog")
utils.HttpInternalServerError(w, err).WithInternalMsg("unable to handle file chooser dialog").Send()
return
}
@ -147,7 +149,7 @@ func (h *RoomHandler) uploadDialogPost(w http.ResponseWriter, r *http.Request) {
func (h *RoomHandler) uploadDialogClose(w http.ResponseWriter, r *http.Request) {
if !h.desktop.IsFileChooserDialogOpened() {
utils.HttpBadRequest(w, "file chooser dialog is not open")
utils.HttpUnprocessableEntity(w).Msg("file chooser dialog is not open")
return
}

View File

@ -76,9 +76,9 @@ func (api *ApiManagerCtx) Authenticate(next http.Handler) http.Handler {
}
if errors.Is(err, types.ErrSessionLoginDisabled) {
utils.HttpForbidden(w, err)
utils.HttpForbidden(w).Msg("login is disabled for this session")
} else {
utils.HttpUnauthorized(w, err)
utils.HttpUnauthorized(w).WithInternalErr(err).Send()
}
return

View File

@ -28,7 +28,7 @@ func (api *ApiManagerCtx) Login(w http.ResponseWriter, r *http.Request) {
session, token, err := api.members.Login(data.Username, data.Password)
if err != nil {
utils.HttpUnauthorized(w, err)
utils.HttpUnauthorized(w).WithInternalErr(err).Send()
return
}
@ -52,7 +52,7 @@ func (api *ApiManagerCtx) Logout(w http.ResponseWriter, r *http.Request) {
err := api.members.Logout(session.ID())
if err != nil {
utils.HttpUnauthorized(w, err)
utils.HttpUnauthorized(w).WithInternalErr(err).Send()
return
}

View File

@ -25,7 +25,7 @@ func AdminsOnly(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
session := GetSession(r)
if !session.Profile().IsAdmin {
utils.HttpForbidden(w)
utils.HttpForbidden(w).Msg("session is not admin")
} else {
next.ServeHTTP(w, r)
}
@ -36,7 +36,7 @@ func HostsOnly(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
session := GetSession(r)
if !session.IsHost() {
utils.HttpForbidden(w, "only host can do this")
utils.HttpForbidden(w).Msg("session is not host")
} else {
next.ServeHTTP(w, r)
}
@ -47,7 +47,7 @@ func CanWatchOnly(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
session := GetSession(r)
if !session.Profile().CanWatch {
utils.HttpForbidden(w)
utils.HttpForbidden(w).Msg("session cannot watch")
} else {
next.ServeHTTP(w, r)
}
@ -58,7 +58,7 @@ func CanHostOnly(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
session := GetSession(r)
if !session.Profile().CanHost {
utils.HttpForbidden(w)
utils.HttpForbidden(w).Msg("session cannot host")
} else {
next.ServeHTTP(w, r)
}
@ -69,7 +69,7 @@ func CanAccessClipboardOnly(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
session := GetSession(r)
if !session.Profile().CanAccessClipboard {
utils.HttpForbidden(w)
utils.HttpForbidden(w).Msg("session cannot access clipboard")
} else {
next.ServeHTTP(w, r)
}

View File

@ -13,7 +13,6 @@ import (
"demodesk/neko/internal/config"
"demodesk/neko/internal/types"
"demodesk/neko/internal/utils"
)
type HttpManagerCtx struct {
@ -55,13 +54,13 @@ func New(WebSocketManager types.WebSocketManager, ApiManager types.ApiManager, c
if _, err := os.Stat(config.Static + r.URL.Path); !os.IsNotExist(err) {
fs.ServeHTTP(w, r)
} else {
utils.HttpNotFound(w)
http.NotFound(w, r)
}
})
}
router.NotFound(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
utils.HttpNotFound(w)
http.NotFound(w, r)
}))
return &HttpManagerCtx{

View File

@ -9,16 +9,12 @@ import (
"github.com/rs/zerolog/log"
)
type ErrResponse struct {
Message string `json:"message"`
}
func HttpJsonRequest(w http.ResponseWriter, r *http.Request, res interface{}) bool {
if err := json.NewDecoder(r.Body).Decode(res); err != nil {
if err == io.EOF {
HttpBadRequest(w, "no data provided")
HttpBadRequest(w).WithInternalErr(err).Msg("no data provided")
} else {
HttpBadRequest(w, err)
HttpBadRequest(w).WithInternalErr(err).Msg("unable to parse provided data")
}
return false
@ -27,21 +23,15 @@ func HttpJsonRequest(w http.ResponseWriter, r *http.Request, res interface{}) bo
return true
}
func HttpJsonResponse(w http.ResponseWriter, status int, res interface{}) {
func HttpJsonResponse(w http.ResponseWriter, code int, res interface{}) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(status)
w.WriteHeader(code)
if err := json.NewEncoder(w).Encode(res); err != nil {
log.Err(err).Str("module", "http").Msg("sending http json response failed")
}
}
func HttpError(w http.ResponseWriter, status int, res interface{}) {
HttpJsonResponse(w, status, &ErrResponse{
Message: fmt.Sprint(res),
})
}
func HttpSuccess(w http.ResponseWriter, res ...interface{}) {
if len(res) == 0 {
w.WriteHeader(http.StatusNoContent)
@ -50,34 +40,108 @@ func HttpSuccess(w http.ResponseWriter, res ...interface{}) {
}
}
func HttpBadRequest(w http.ResponseWriter, res ...interface{}) {
defHttpError(w, http.StatusBadRequest, "bad request", res...)
// HTTPError is an error with a message and an HTTP status code.
type HTTPError struct {
Code int `json:"code"`
Message string `json:"message"`
InternalErr error `json:"-"`
InternalMsg string `json:"-"`
w http.ResponseWriter `json:"-"`
}
func HttpUnauthorized(w http.ResponseWriter, res ...interface{}) {
defHttpError(w, http.StatusUnauthorized, "invalid or missing access token", res...)
func (e *HTTPError) Error() string {
if e.InternalMsg != "" {
return e.InternalMsg
}
return fmt.Sprintf("%d: %s", e.Code, e.Message)
}
func HttpForbidden(w http.ResponseWriter, res ...interface{}) {
defHttpError(w, http.StatusForbidden, "access token does not have the required scope", res...)
func (e *HTTPError) Cause() error {
if e.InternalErr != nil {
return e.InternalErr
}
return e
}
func HttpNotFound(w http.ResponseWriter, res ...interface{}) {
defHttpError(w, http.StatusNotFound, "resource not found", res...)
// WithInternalErr adds internal error information to the error
func (e *HTTPError) WithInternalErr(err error) *HTTPError {
e.InternalErr = err
return e
}
func HttpUnprocessableEntity(w http.ResponseWriter, res ...interface{}) {
defHttpError(w, http.StatusUnprocessableEntity, "unprocessable entity", res...)
// WithInternalMsg adds internal message information to the error
func (e *HTTPError) WithInternalMsg(msg string) *HTTPError {
e.InternalMsg = msg
return e
}
func HttpInternalServerError(w http.ResponseWriter, res ...interface{}) {
defHttpError(w, http.StatusInternalServerError, "internal server error", res...)
// WithInternalMsg adds internal formated message information to the error
func (e *HTTPError) WithInternalMsgf(fmtStr string, args ...interface{}) *HTTPError {
e.InternalMsg = fmt.Sprintf(fmtStr, args...)
return e
}
func defHttpError(w http.ResponseWriter, status int, text string, res ...interface{}) {
if len(res) == 0 {
HttpError(w, status, text)
} else {
HttpError(w, status, res[0])
// Sends error with custom formated message
func (e *HTTPError) Msgf(fmtSt string, args ...interface{}) {
e.Message = fmt.Sprintf(fmtSt, args...)
e.Send()
}
// Sends error with custom message
func (e *HTTPError) Msg(str string) {
e.Message = str
e.Send()
}
// Sends error with default status text
func (e *HTTPError) Send() {
if e.Message == "" {
e.Message = http.StatusText(e.Code)
}
logger := log.Error().
Err(e.InternalErr).
Str("module", "http").
Int("code", e.Code)
message := e.Message
if e.InternalMsg != "" {
message = e.InternalMsg
}
logger.Msg(message)
HttpJsonResponse(e.w, e.Code, e)
}
func HttpError(w http.ResponseWriter, code int) *HTTPError {
return &HTTPError{
Code: code,
w: w,
}
}
func HttpBadRequest(w http.ResponseWriter) *HTTPError {
return HttpError(w, http.StatusBadRequest)
}
func HttpUnauthorized(w http.ResponseWriter) *HTTPError {
return HttpError(w, http.StatusUnauthorized)
}
func HttpForbidden(w http.ResponseWriter) *HTTPError {
return HttpError(w, http.StatusForbidden)
}
func HttpNotFound(w http.ResponseWriter) *HTTPError {
return HttpError(w, http.StatusNotFound)
}
func HttpUnprocessableEntity(w http.ResponseWriter) *HTTPError {
return HttpError(w, http.StatusUnprocessableEntity)
}
func HttpInternalServerError(w http.ResponseWriter, err error) *HTTPError {
return HttpError(w, http.StatusInternalServerError).WithInternalErr(err)
}