-
-
Notifications
You must be signed in to change notification settings - Fork 4.2k
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: Normalize & flatten all unmarshalers #6037
Conversation
53daadf
to
fcfcff6
Compare
In Caddy 1, it was not uncommon for directives to occur multiple times and all their tokens were combined and the parser handled it in one go. This pattern carried over into Caddy 2 for the flexibility it provides in case we do that.
That may be true. I hope we don't ever (unintentionally or otherwise) re-introduce the pattern of grouping segments then. Even though our docs demonstrate using I dunno. I still think the loop is an elegant, robust way to consume tokens, that gives us more flexibility, but at the same time we don't document this pattern that I can recall... This change is probably fine. I'm slightly sad but not for any technical reason 😂 |
Oh -- actually! We did document this:
So now I wonder if removing it is really beneficial. There will be many plugins that still use the loop. |
No, I'm quite sure nothing uses that pattern except for matchers. It's super unintuitive and isn't something we ever mention as possible except for matchers, so I see no reason plugins would have experimented to the point where they found that they could do this. Remember that the caller to the unmarshaler must have actually passed a dispenser which includes multiple segments for it to be possible to parse multiple copies of a directive. That never happens, we never pass more than one directive segment to an unmarshaler. It only happens with the named matcher unmarshaler. The reason it works with matchers is because we do this ( // in case there are multiple instances of the same matcher, concatenate
// their tokens (we expect that UnmarshalCaddyfile should be able to
// handle more than one segment); otherwise, we'd overwrite other
// instances of the matcher in this set
tokensByMatcherName := make(map[string][]caddyfile.Token)
for nesting := d.Nesting(); d.NextArg() || d.NextBlock(nesting); {
matcherName := d.Val()
tokensByMatcherName[matcherName] = append(tokensByMatcherName[matcherName], d.NextSegment()...)
} We collect tokens by their matcher name together (essentially grouping them sequentially) and then pass that entire set of tokens to the matcher's unmarshaler with We never do this kind of thing anywhere else. We never collect tokens ahead of time by name then feed them to an unmarshaler. We always just pass a single segment in. One reason we never do it for directives is because of matchers. Since there may be an inline matcher token, we can never group directives that have a matcher with any other with either no matcher or a different matcher. They need to be separate. Also, directives are by design ordered because they run as a middleware chain. It's very rare that a directive might be okay with running nondeterministically at the same time as another. You might think
I would agree with you, if the loops ever iterated more than once, which they don't 😂 |
fcfcff6
to
2a9b7ef
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alrighty, we can try this.
As mentioned in Slack, it's a bummer that the unmarshalers now have an unnecessary call to Next()
at the beginning. (There's no point in passing in the directive name if there's no loop.) That's my bad, I guess. A carry-over from v1.
Thanks for doing this!!
2a9b7ef
to
b5a71b0
Compare
This is something I've been noticing over the years; I was always confused as to why we were using a
for d.Next()
loop around all the Caddyfile unmarshaling code, it never seemed useful.In practice, the only time when the loop is useful is when we intentionally want to merge multiple consecutive segments, and this only happens for request matchers. For all other module types, we never need this kind of behaviour.
So to improve code readability and make it feel less arcane, it's time to flatten all the unmarshalers to only have a single
d.Next()
line with no loop. Removes one level of indentation in almost all unmarshalers.Also, I changed allThis was a bad idea after thinking about it for a few more minutes.for d.NextBlock(0) {
tofor nesting := d.Nesting(); d.NextBlock(nesting); {
which avoids a hard-coded number, making better use of our provided APIs.I strongly recommend reviewing with the whitespace collapse option turned on in github for this one.