Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Semantics of DefaultHTTPErrorHandler seem wrong when HTTPError.Message is customized && e.Debug #1477

Closed
3 tasks done
danielbprice opened this issue Jan 13, 2020 · 9 comments

Comments

@danielbprice
Copy link

Issue Description

While trying to write a custom HTTP error handler, I discovered a relatively new and seemingly incorrect semantic in the DefaultHTTPErrorHandler. I am still using echo v3, and the old semantic seems to be sensible, while the new one does not:

In the old semantic (I am on v3.3.9), the code does this:

        if he, ok := err.(*HTTPError); ok {
		code = he.Code
		msg = he.Message
		if he.Internal != nil {
			err = fmt.Errorf("%v, %v", err, he.Internal)
		}
	} else if e.Debug {
		msg = err.Error()
	} else {
		msg = http.StatusText(code)
	}
	if _, ok := msg.(string); ok {
		msg = Map{"message": msg}
	}

That is to say, if the error is an HTTPError, then we use its Code and Message. If the error is some other type of error, then in debug mode, we use err.Error(). This seems reasonable. It's useful to rember that HTTPError.Message is of type interface{}. And so it is reasonable to pass it anything that could be marshalled to JSON.

In the new code, (I am testing with v4.1.13), introduced by commit ed51400 the logic is different:

	he, ok := err.(*HTTPError)
	if ok {
		if he.Internal != nil {
			if herr, ok := he.Internal.(*HTTPError); ok {
				he = herr
			}
		}
	} else {
		he = &HTTPError{
			Code:    http.StatusInternalServerError,
			Message: http.StatusText(http.StatusInternalServerError),
		}
	}

	// Issue #1426
	code := he.Code
	message := he.Message
	if e.Debug {  // <-------------------- unconditional overwrite of message
		message = err.Error()
	} else if m, ok := message.(string); ok {
		message = Map{"message": m}
	}

This means that toggling on Debug ALWAYS overwrites message (which may be a map full of an arbitrary set of things). This seems to run counter to what a developer would reasonably expect.

Checklist

  • Dependencies installed
  • No typos
  • Searched existing issues and docs

Expected behaviour

e.Debug should not modify the semantics of error messages which have a developer-supplied payload

Actual behaviour

e.Debug now wipes out customized error Message payload

Steps to reproduce

Run server below. Hit endpoint with e.g. curl http://localhost:9999

Working code to debug

package main

import (
  "github.com/labstack/echo/v4"
)

func main() {
  // Echo instance
  e := echo.New()
  e.Debug = true

  // Routes
  e.GET("/", hello)

  // Start server
  e.Logger.Fatal(e.Start(":9999"))
}

// Handler
func hello(c echo.Context) error {
  myError := make(map[string]string)
  myError["error"] = "Bad thing happened"
  myError["reason"] = "no one knows why"
  return echo.NewHTTPError(500, myError)
}

In the above code, if e.Debug is set to False, then curl against this endpoint produces:

{"error":"Bad thing happened","reason":"no one knows why"}

Which seems to be the expected result. But when debug=True, it produces:

"code=500, message=map[error:Bad thing happened reason:no one knows why], internal=\u003cnil\u003e"

Which seems like an unexpected result-- the presence of Debug is changing the error semantics of API. This means that the Debug server can't be used with clients which expect to interpret specific fields from API errors.

Version/commit

4.1.13

@stale
Copy link

stale bot commented Mar 13, 2020

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@stale stale bot added the wontfix label Mar 13, 2020
@danielbprice
Copy link
Author

Is an issue really stale if no one has read it or responded? Seems suboptimal.

@stale stale bot removed the wontfix label Mar 14, 2020
@stale
Copy link

stale bot commented May 13, 2020

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@stale stale bot added the wontfix label May 13, 2020
@danielbprice
Copy link
Author

I'm willing to work on a PR for this, if the team can acknowledge that they agree with my statement that this is a problem.

@stale
Copy link

stale bot commented Jul 13, 2020

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@stale stale bot added the wontfix label Jul 13, 2020
@stale stale bot closed this as completed Jul 25, 2020
@lammel lammel reopened this Sep 3, 2020
@stale stale bot removed the wontfix label Sep 3, 2020
@lammel
Copy link
Contributor

lammel commented Sep 4, 2020

That looks like a bug. Sorry for overlooking this issue.

As we have no "production" mode identification, e.Debug is used to decide if the error should be returned.
Still it should not everwrite the whole message. Adding custom information to an Error is useful.

@danielbprice Yes, a PR would be welcome!

@danielbprice
Copy link
Author

I will see what I can do.

@lammel
Copy link
Contributor

lammel commented Nov 5, 2020

PR #1666 created now. Open for review, will be merged in the next view days.

lammel added a commit that referenced this issue Nov 5, 2020
…ler-with-debug

Fix DefaultHTTPErrorHandler with enabled debug (#1477)
@lammel
Copy link
Contributor

lammel commented Nov 22, 2020

Should be fixed already with #1666 .

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants