'not query' in named matcher not working as expected

1. The problem I’m having:

I am trying matching based on two query parameters ip and user with two named matchers @yes and @no. If query type=user respond with Yes, if type is anything else, respond with No.

Here is a minimal working example:

:80 {
                @no {
                        query id=1
                        not query type=*
                }


                @yes {
                        query id=1
                        query type=user
                }

                respond @no No
                respond @yes Yes
}

I would expect a request via curl 'http://localhost/?id=1&type=admin' (the type parameter is not user) to result in a No, but I get a Yes if I curl localhost.
In fact, as long as &type is present, caddy responds with Yes.

Can someone please tell if I understood the not directive wrong or what is happening? Thank you in advance!

2. Error messages and/or full log output:

n/a

3. Caddy version:

v2.7.6

4. How I installed and ran Caddy:

a. System environment:

Arch Linux
x64
Official Repos
systemd (no modifications to systemd unit)

not simply negates another matcher. That’s it.

query type=* matches if there is any type query with any value. It only does not match if there is no type query at all.

So combining those, not query type=* is true if there’s no type at all.

Wouldn’t you want to do something more like this?

@no {
	query id=1
	query type=*
	not query type=user
}

This would match when there is a type, but not if type=user (so type=admin would match).

That said, I recommend using handle blocks to implement fallback behaviour. Something like this:

:80 {
	@yes {
		query id=1
		query type=user
	}
	handle @yes {
		respond Yes
	}

	handle {
		respond No
	}
}

Only the first matching handle is run, and a handle without a matcher acts as fallback (think of it like the else of an if-elseif-else chain).

2 Likes

Thank you! handle was exactly what I was looking for!

I have this now, though the request matches on both @yes and @no (ignores the type param and executes the first handler):

:80 {
	@yes {
		query id=1
		query type=admin
	}

    @no {
         query id=1
         query type=user
    }
        
	handle @yes {
		respond Yes
	}

	handle @no {
		respond No
	}
}

If I remove the id query parameter it works as expected. I thought multiple query matchers inside a named matcher were ANDed?

1 Like

You’re right, that’s a bug. I see the problem. We’ll look to get that fixed.

That said, you’d be better served by using CEL expressions to express this boolean condition (plus, less lines of config):

@yes `query({'id': '1'}) && query({'type': 'admin'})`
2 Likes

Followup – the described bug was fixed, and will land in the next release: