special case handling: if one file has been sent and it is empty, return a correct invalid HTTP code AND remove the resource folder

This commit is contained in:
Julien Neuhart
2019-08-19 16:29:58 +02:00
parent 49ac5e647f
commit cf5e530ed9
2 changed files with 65 additions and 46 deletions

View File

@@ -34,9 +34,10 @@ func contextMiddleware(config conf.Config, processes ...pm2.Process) echo.Middle
// if the endpoint is not for healthcheck, create a // if the endpoint is not for healthcheck, create a
// Resource. // Resource.
if err := ctx.WithResource(trace); err != nil { if err := ctx.WithResource(trace); err != nil {
// required to have a correct status code. err = doCleanup(ctx, err)
ctx.Error(err) err = doErr(ctx, err)
return ctx.LogRequestResult(err, false) return ctx.LogRequestResult(err, false)
} }
return next(ctx) return next(ctx)
} }
@@ -62,27 +63,9 @@ func loggerMiddleware() echo.MiddlewareFunc {
func cleanupMiddleware() echo.MiddlewareFunc { func cleanupMiddleware() echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc { return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error { return func(c echo.Context) error {
const op string = "xhttp.cleanupMiddleware"
err := next(c) err := next(c)
ctx := context.MustCastFromEchoContext(c) ctx := context.MustCastFromEchoContext(c)
if !ctx.HasResource() { return doCleanup(ctx, err)
// nothing to remove.
return err
}
r := ctx.MustResource()
// if a webhook URL has been given,
// do not remove the resource.Resource here because
// we don't know if the result file has been
// generated or sent.
if r.HasArg(resource.WebhookURLArgKey) {
return err
}
// a resource.Resource is associated with our custom context.
if resourceErr := r.Close(); resourceErr != nil {
xerr := xerror.New(op, resourceErr)
ctx.XLogger().ErrorOp(xerror.Op(xerr), xerr)
}
return err
} }
} }
} }
@@ -97,31 +80,57 @@ func errorMiddleware() echo.MiddlewareFunc {
// so far so good! // so far so good!
return nil return nil
} }
// if it's an error from echo return doErr(ctx, err)
// like 404 not found and so on.
if echoHTTPErr, ok := err.(*echo.HTTPError); ok {
return echoHTTPErr
}
// we log the initial error before returning
// the HTTP error.
errOp := xerror.Op(err)
logger := ctx.XLogger()
logger.ErrorOp(errOp, err)
// handle our custom HTTP error.
var httpErr error
errCode := xerror.Code(err)
errMessage := xerror.Message(err)
switch errCode {
case xerror.InvalidCode:
httpErr = echo.NewHTTPError(http.StatusBadRequest, errMessage)
case xerror.TimeoutCode:
httpErr = echo.NewHTTPError(http.StatusGatewayTimeout, errMessage)
default:
httpErr = echo.NewHTTPError(http.StatusInternalServerError, errMessage)
}
// required to have a correct status code.
ctx.Error(httpErr)
return httpErr
} }
} }
} }
func doCleanup(ctx context.Context, err error) error {
const op string = "xhttp.cleanup"
if !ctx.HasResource() {
// nothing to remove.
return err
}
r := ctx.MustResource()
// if a webhook URL has been given,
// do not remove the resource.Resource here because
// we don't know if the result file has been
// generated or sent.
if r.HasArg(resource.WebhookURLArgKey) {
return err
}
// a resource.Resource is associated with our custom context.
if resourceErr := r.Close(); resourceErr != nil {
xerr := xerror.New(op, resourceErr)
ctx.XLogger().ErrorOp(xerror.Op(xerr), xerr)
}
return err
}
func doErr(ctx context.Context, err error) error {
// if it's an error from echo
// like 404 not found and so on.
if echoHTTPErr, ok := err.(*echo.HTTPError); ok {
return echoHTTPErr
}
// we log the initial error before returning
// the HTTP error.
errOp := xerror.Op(err)
logger := ctx.XLogger()
logger.ErrorOp(errOp, err)
// handle our custom HTTP error.
var httpErr error
errCode := xerror.Code(err)
errMessage := xerror.Message(err)
switch errCode {
case xerror.InvalidCode:
httpErr = echo.NewHTTPError(http.StatusBadRequest, errMessage)
case xerror.TimeoutCode:
httpErr = echo.NewHTTPError(http.StatusGatewayTimeout, errMessage)
default:
httpErr = echo.NewHTTPError(http.StatusInternalServerError, errMessage)
}
// required to have a correct status code.
ctx.Error(httpErr)
return httpErr
}

View File

@@ -2,9 +2,11 @@ package context
import ( import (
"fmt" "fmt"
"io"
"net/http" "net/http"
"reflect" "reflect"
"strconv" "strconv"
"strings"
"time" "time"
"github.com/labstack/echo/v4" "github.com/labstack/echo/v4"
@@ -102,6 +104,14 @@ func (ctx *Context) WithResource(directoryName string) error {
// write form files from request. // write form files from request.
form, err := ctx.MultipartForm() form, err := ctx.MultipartForm()
if err != nil { if err != nil {
/*
(very) special case: one and
only one file has been sent
and it is empty.
*/
if strings.Contains(err.Error(), io.EOF.Error()) {
return r, xerror.Invalid(op, "one file has been sent but it is empty: does it exist?", err)
}
return r, err return r, err
} }
for _, files := range form.File { for _, files := range form.File {