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

caddyfile: keep error chain info in Dispenser.Errf #4233

Merged
merged 2 commits into from
Jul 19, 2021

Conversation

ggicci
Copy link
Contributor

@ggicci ggicci commented Jul 5, 2021

Use fmt.Errorf instead of fmt.Sprintf in Dispenser.Errf method to reserve the error chain information. Make the underlying error unwrapable by using errors.Is or errors.As helpers.

@CLAassistant
Copy link

CLAassistant commented Jul 5, 2021

CLA assistant check
All committers have signed the CLA.

@ggicci ggicci changed the title caddyfile: Errf enable error chain unwrapping caddyfile: keey error chain info in Dispenser.Errf Jul 5, 2021
@ggicci ggicci changed the title caddyfile: keey error chain info in Dispenser.Errf caddyfile: keep error chain info in Dispenser.Errf Jul 5, 2021
@francislavoie francislavoie requested a review from mholt July 5, 2021 14:13
@francislavoie francislavoie added the under review 🧐 Review is pending before merging label Jul 5, 2021
@francislavoie francislavoie added this to the v2.4.4 milestone Jul 5, 2021
@mholt
Copy link
Member

mholt commented Jul 14, 2021

Thanks for this. What is the use case for this? (There's no context / issue associated.) And why do we have to define our own error type instead of using %w to do it for us?

@ggicci
Copy link
Contributor Author

ggicci commented Jul 15, 2021

What is the use case for this? (There's no context / issue associated.)

This API Dispenser.Errf is useful when extending the Caddyfile parser. When I was writing the Caddyfile parser for ggicci/caddy-jwt, I found that it's not "easy" to check the error in a test. I can't use errors.Is nor errors.As to reach some more specific error information in the error chain. The root error may be wrapped multiple times during the parsing stuff. For example:

In the codebase at this time, I returned an error object as follows:

func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error) {
    // ...
    return nil, h.Errf("invalid sign_key")
}

And in the corresponding test, I wrote:

func TestParsingCaddyfileError(t *testing.T) {
    // ...
    _, err := parseCaddyfile(helper)
    assert.Contains(t, err.Error(), "sign_key")  // fragile, broken when error content changed
}

Assertion statements checking some error information by string comparison can be fragile.

If Dispenser.Errf can wrap errors, It can help writing more confident code as follows:

var ErrInvalidSignKey = errors.New("invalid sign_key")
func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error) {
    // ...
    return nil, h.Errf("%w", ErrInvalidSignKey)
}

func TestParsingCaddyfileError(t *testing.T) {
    // ...
    _, err := parseCaddyfile(helper)
    assert.ErrorIs(t, err, ErrInvalidSignKey)  // more stable
}

And why do we have to define our own error type instead of using %w to do it for us?

  1. It does no harm to performance nor resource usage of the program.
  2. The code can be more readable.

Copy link
Member

@mholt mholt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gotcha, thanks for the explanation.

If using %w would achieve the same result, I think I'd rather use that just for simplicity's sake; I'm not sure what about that is harder to read than having a whole new type defintion to manually wrap the error. But if this is the only way to get the result you want, then that's OK I guess.

// Err generates a custom parse-time error with a message of msg.
func (d *Dispenser) Err(msg string) error {
msg = fmt.Sprintf("%s:%d - Error during parsing: %s", d.File(), d.Line(), msg)
return errors.New(msg)
return parseError{d.File(), d.Line(), errors.New(msg)}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If Err could use Errf, or vice-versa, that might be nice to reduce a little bit of code duplication / repetition.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I refactored it and I think it's more concise now.

Copy link
Member

@mholt mholt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM now -- thanks!

@mholt mholt removed the under review 🧐 Review is pending before merging label Jul 19, 2021
@mholt mholt merged commit b6f5125 into caddyserver:master Jul 19, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants