Handle context cancellations and avoid logged errors.

This commit is contained in:
mikestefanello 2022-01-09 00:23:26 -05:00
parent a6e99058f4
commit 10c0a23c0a
8 changed files with 83 additions and 35 deletions

View File

@ -1,5 +1,10 @@
package context
import (
"context"
"errors"
)
const (
// AuthenticatedUserKey is the key value used to store the authenticated user in context
AuthenticatedUserKey = "auth_user"
@ -13,3 +18,8 @@ const (
// PasswordTokenKey is the key value used to store a password token in context
PasswordTokenKey = "password_token"
)
// IsCanceledError determines if an error is due to a context cancelation
func IsCanceledError(err error) bool {
return errors.Is(err, context.Canceled)
}

24
context/context_test.go Normal file
View File

@ -0,0 +1,24 @@
package context
import (
"context"
"errors"
"testing"
"time"
"github.com/stretchr/testify/assert"
)
func TestIsCanceled(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
assert.False(t, IsCanceledError(ctx.Err()))
cancel()
assert.True(t, IsCanceledError(ctx.Err()))
ctx, cancel = context.WithTimeout(context.Background(), time.Microsecond)
time.Sleep(time.Microsecond * 2)
cancel()
assert.False(t, IsCanceledError(ctx.Err()))
assert.False(t, IsCanceledError(errors.New("test error")))
}

View File

@ -5,6 +5,7 @@ import (
"fmt"
"net/http"
"github.com/mikestefanello/pagoda/context"
"github.com/mikestefanello/pagoda/middleware"
"github.com/mikestefanello/pagoda/services"
@ -143,7 +144,10 @@ func (c *Controller) cachePage(ctx echo.Context, page Page, html *bytes.Buffer)
err := marshaler.New(c.Container.Cache).Set(ctx.Request().Context(), key, cp, opts)
if err != nil {
ctx.Logger().Errorf("failed to cache page: %v", err)
if !context.IsCanceledError(err) {
ctx.Logger().Errorf("failed to cache page: %v", err)
}
return
}
@ -158,6 +162,9 @@ func (c *Controller) Redirect(ctx echo.Context, route string, routeParams ...int
// Fail is a helper to fail a request by returning a 500 error and logging the error
func (c *Controller) Fail(ctx echo.Context, err error, log string) error {
if context.IsCanceledError(err) {
return nil
}
ctx.Logger().Errorf("%s: %v", log, err)
return echo.NewHTTPError(http.StatusInternalServerError)
}

View File

@ -24,6 +24,9 @@ func LoadAuthenticatedUser(authClient *services.AuthClient) echo.MiddlewareFunc
c.Set(context.AuthenticatedUserKey, u)
c.Logger().Infof("auth user loaded in to context: %d", u.ID)
default:
if context.IsCanceledError(err) {
return nil
}
c.Logger().Errorf("error querying for authenticated user: %v", err)
}
@ -55,6 +58,9 @@ func LoadValidPasswordToken(authClient *services.AuthClient) echo.MiddlewareFunc
msg.Warning(c, "The link is either invalid or has expired. Please request a new one.")
return c.Redirect(http.StatusFound, c.Echo().Reverse("forgot_password"))
default:
if context.IsCanceledError(err) {
return nil
}
c.Logger().Error(err)
return echo.NewHTTPError(http.StatusInternalServerError)
}

View File

@ -51,11 +51,15 @@ func ServeCachedPage(ch *cache.Cache) echo.MiddlewareFunc {
new(CachedPage),
)
if err != nil {
if err == redis.Nil {
switch {
case err == redis.Nil:
c.Logger().Info("no cached page found")
} else {
case context.IsCanceledError(err):
return nil
default:
c.Logger().Errorf("failed getting cached page: %v", err)
}
return next(c)
}

View File

@ -32,6 +32,9 @@ func LoadUser(orm *ent.Client) echo.MiddlewareFunc {
case *ent.NotFoundError:
return echo.NewHTTPError(http.StatusNotFound)
default:
if context.IsCanceledError(err) {
return nil
}
c.Logger().Error(err)
return echo.NewHTTPError(http.StatusInternalServerError)
}

View File

@ -3,6 +3,7 @@ package routes
import (
"net/http"
"github.com/mikestefanello/pagoda/context"
"github.com/mikestefanello/pagoda/controller"
"github.com/labstack/echo/v4"
@ -12,8 +13,8 @@ type Error struct {
controller.Controller
}
func (e *Error) Get(err error, c echo.Context) {
if c.Response().Committed {
func (e *Error) Get(err error, ctx echo.Context) {
if ctx.Response().Committed || context.IsCanceledError(err) {
return
}
@ -23,19 +24,19 @@ func (e *Error) Get(err error, c echo.Context) {
}
if code >= 500 {
c.Logger().Error(err)
ctx.Logger().Error(err)
} else {
c.Logger().Info(err)
ctx.Logger().Info(err)
}
p := controller.NewPage(c)
p.Layout = "main"
p.Title = http.StatusText(code)
p.Name = "error"
p.StatusCode = code
p.HTMX.Request.Enabled = false
page := controller.NewPage(ctx)
page.Layout = "main"
page.Title = http.StatusText(code)
page.Name = "error"
page.StatusCode = code
page.HTMX.Request.Enabled = false
if err = e.RenderPage(c, p); err != nil {
c.Logger().Error(err)
if err = e.RenderPage(ctx, page); err != nil {
ctx.Logger().Error(err)
}
}

View File

@ -14,12 +14,6 @@ type VerifyEmail struct {
}
func (c *VerifyEmail) Get(ctx echo.Context) error {
c.verifyToken(ctx)
return c.Redirect(ctx, "home")
}
func (c *VerifyEmail) verifyToken(ctx echo.Context) {
var usr *ent.User
// Validate the token
@ -27,7 +21,7 @@ func (c *VerifyEmail) verifyToken(ctx echo.Context) {
email, err := c.Container.Auth.ValidateEmailVerificationToken(token)
if err != nil {
msg.Warning(ctx, "The link is either invalid or has expired.")
return
return c.Redirect(ctx, "home")
}
// Check if it matches the authenticated user
@ -47,24 +41,23 @@ func (c *VerifyEmail) verifyToken(ctx echo.Context) {
Only(ctx.Request().Context())
if err != nil {
ctx.Logger().Errorf("error querying user during email verification: %v", err)
msg.Danger(ctx, "An error occurred. Please try again.")
return
return c.Fail(ctx, err, "query failed loading email verification token user")
}
}
// Verify the user
err = c.Container.ORM.User.
Update().
SetVerified(true).
Where(user.ID(usr.ID)).
Exec(ctx.Request().Context())
// Verify the user, if needed
if !usr.Verified {
err = c.Container.ORM.User.
Update().
SetVerified(true).
Where(user.ID(usr.ID)).
Exec(ctx.Request().Context())
if err != nil {
ctx.Logger().Errorf("error setting user as verified: %v", err)
msg.Danger(ctx, "An error occurred. Please try again.")
return
if err != nil {
return c.Fail(ctx, err, "failed to set user as verified")
}
}
msg.Success(ctx, "Your email has been successfully verified.")
return c.Redirect(ctx, "home")
}