Caddy: CEL expressions: comparisions only work enclosed by single quotes, not by double quotes

Created on 10 Jan 2021  路  4Comments  路  Source: caddyserver/caddy

Maybe am I missing something obvious, but it looks like in some case string enclosed by double quotes in CEL expressions create compilation errors. As a workaround, using single quotes fixes the issue.

This works as expected:

@pwa expression ( \
        {header.Accept}.matches('[, \\t]?text\\/html[;, \\t]?') \
        && !{path}.matches('(?i)(?:^/docs|^/graphql|^/bundles/|^/_profiler|^/_wdt|.+\\.(?:json|html$|csv$|ya?ml$|xml$))') \
    ) \
    || {path}.startsWith('/_next') \
    || {path}.startsWith('/sitemap') \
    || {path} == '/favicon.ico' \
    || {path} == '/manifest.json' \
    || {path} == '/robots.txt'

This throws an error:

@pwa expression ( \
        {header.Accept}.matches('[, \\t]?text\\/html[;, \\t]?') \
        && !{path}.matches('(?i)(?:^/docs|^/graphql|^/bundles/|^/_profiler|^/_wdt|.+\\.(?:json|html$|csv$|ya?ml$|xml$))') \
    ) \
    || {path}.startsWith('/_next') \
    || {path}.startsWith('/sitemap') \
    || {path} == '/favicon.ico' \
    || {path} == '/manifest.json' \
    || {path} == "/robots.txt"


Error with full logs

caddy_1 | 2021/01/10 15:03:55 WARNING: proto: file "pb.proto" is already registered
caddy_1 | A future release will panic on registration conflicts. See:
caddy_1 | https://developers.google.com/protocol-buffers/docs/reference/go/faq#namespace-conflict
caddy_1 |
caddy_1 | {"level":"info","ts":1610291035.7696621,"msg":"using provided configuration","config_file":"/etc/caddy/Caddyfile","config_adapter":"caddyfile"}
caddy_1 | {"level":"info","ts":1610291035.7774138,"logger":"admin","msg":"admin endpoint started","address":"tcp/localhost:2019","enforce_origin":false,"origins":["localhost:2019","[::1]:2019","127.0.0.1:2019"]}
caddy_1 | {"level":"info","ts":1610291035.778538,"logger":"tls.cache.maintenance","msg":"started background certificate maintenance","cache":"0xc0003ff730"}
caddy_1 | {"level":"info","ts":1610291035.7787566,"logger":"http","msg":"server is listening only on the HTTP port, so no automatic HTTPS will be applied to this server","server_name":"srv1","http_port":80}
caddy_1 | {"level":"info","ts":1610291035.778957,"logger":"http","msg":"server is listening only on the HTTPS port but has no TLS connection policies; adding one to enable TLS","server_name":"srv0","https_port":443}
caddy_1 | {"level":"info","ts":1610291035.7791483,"logger":"http","msg":"enabling automatic HTTP->HTTPS redirects","server_name":"srv0"}
caddy_1 | {"level":"warn","ts":1610291035.7997031,"logger":"http","msg":"user server is listening on same interface as automatic HTTP->HTTPS redirects; user-configured routes might override these redirects","server_name":"srv1","interface":"tcp/:80"}
caddy_1 | {"level":"info","ts":1610291035.813821,"logger":"tls.cache.maintenance","msg":"stopped background certificate maintenance","cache":"0xc0003ff730"}
caddy_1 | run: loading initial config: loading new config: loading http app module: provision http: server srv0: setting up route handlers: route 0: loading handler modules: position 0: loading module 'subroute': provision http.handlers.subroute: setting up subroutes: route 0: loading handler modules: position 0: loading module 'subroute': provision http.handlers.subroute: setting up subroutes: route 5: loading matcher modules: module name 'expression': provision http.matchers.expression: compiling CEL program: ERROR: :1:614: Syntax error: extraneous input '/' expecting {'[', '{', '(', '.', '-', '!', 'true', 'false', 'null', NUM_FLOAT, NUM_INT, NUM_UINT, STRING, BYTES, IDENTIFIER}
caddy_1 | | ( caddyPlaceholder(request, "http.request.header.Accept").matches('[, \t]?text\/html[;, \t]?') && !caddyPlaceholder(request, "http.request.uri.path").matches('(?i)(?:^/docs|^/graphql|^/bundles/|^/_profiler|^/_wdt|.+\.(?:json|html$|csv$|ya?ml$|xml$))') ) || caddyPlaceholder(request, "http.request.uri.path").startsWith('/_next') || caddyPlaceholder(request, "http.request.uri.path").startsWith('/sitemap') || caddyPlaceholder(request, "http.request.uri.path") == '/favicon.ico' || caddyPlaceholder(request, "http.request.uri.path") == '/manifest.json' || caddyPlaceholder(request, "http.request.uri.path") == /robots.txt
caddy_1 | | .....................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................^

question

All 4 comments

I think that's as expected, because in the Caddyfile, double-quotes are used for ensuring a string is a single token (important for directives which take multiple arguments).

https://caddyserver.com/docs/caddyfile/concepts#tokens-and-quotes

Backticks are also handled similarly. But single quotes are not specially handled by the Caddyfile parser, so it's fine to use that for CEL strings.

Basically the error says the CEL compiler doesn't know what to do when it encounters /, because basically it gets fed this:

{path} == /robots.txt

You can see the exact string passed to CEL (post-Caddyfile parsing) if you adapt your config to JSON (potentially with some escaped chars for JSON purposes)

Right, kind of like how when you're writing bash this often happens: you need to first format your input for the shell (to tokenize arguments correctly, etc), then format it for parsing after the fact.

For example, if you're doing:

$ caddy file-server --root /home/Bob Dillan's website

That won't work because you first need to tokenize it for the shell:

$ caddy file-server --root "/home/Bob Dillan's website"

Similar with the Caddyfile or any other parsed input. So in your case, this might be easiest/cleanest:

@pwa expression `(
        {header.Accept}.matches('[, \\t]?text\\/html[;, \\t]?')
        && !{path}.matches('(?i)(?:^/docs|^/graphql|^/bundles/|^/_profiler|^/_wdt|.+\\.(?:json|html$|csv$|ya?ml$|xml$))')
    )
    || {path}.startsWith('/_next')
    || {path}.startsWith('/sitemap')
    || {path} == '/favicon.ico'
    || {path} == '/manifest.json'
    || {path} == "/robots.txt"`

No need for \ at the end of lines and you can use either quote character that works for you.

Thank you for the detailed replies! It could be nice to add this to the documentation (or I maybe missed this part).

Yeah. It's in the docs that Francis linked to: https://caddyserver.com/docs/caddyfile/concepts#tokens-and-quotes

Was this page helpful?
0 / 5 - 0 ratings