How to determine if a path is a directory or a file

1. Caddy version (caddy version):

Current master (744d04c2585d50f64cf3d43d139c310a18e78f73)

2. How I run Caddy:

a. System environment:

OSX

b. Command:

caddy run

d. My complete Caddyfile or JSON config:

localhost:8080

@directory {
  path */
}

handle @directory {
  file_server browse
}

handle {
   respond "serving a file"
}

3. The problem I’m having:

I’m trying to run a file server that does the following:

  1. It displays a directory listing for directory.
  2. It applies directives to files contained in the directories. In my case, my files are all pre-gzipped, and need to be served accordingly (e.g., by setting `header Content-Encoding gzip

4. Error messages and/or full log output:

Caddy responds “serving a file” for files (e.g. https://localhost:8080/file.txt) . This is as expected.
Caddy shows a directory listing for (e.g. https://localhost:8080/directory/) . This is as expected.
Caddy responds “serving a file” for URLs missing the “/”. (e.g. https://localhost:8080/directory) . This is the problem. I want Caddy to either display the directory listing, or redirect to https://localhost:8080/directory/.

Note that matching on the “.” in a file name (the “.” that separates filename and extension) doesn’t work, since not all files I’m serving actually have an extension.

5. What I already tried:

I already tried creating a “notafile” matcher:

@notafile {
  file {
    not try_files {path}
  }
}

Apparently, this doesn’t work as try_files also matches directories.

Btw, this is slightly incorrect. It should look like this:

@notafile {
  not file {
    try_files {path}
  }
}

But it can also be shorted to this since v2.1:

@notafile not file {path}

Anyways, the file vs directory aspect of the file matcher is actually something I was thinking about last week when resolving a different issue - I think we could have the file matcher set a value in the replacer (i.e. set up a placeholder that can be used) to determine whether the matched file was a directory or a file. I’ll see if I can get this implemented soon. Then essentially you could do something like:

@existsOnDisk file {path}
respond "serving a {http.matchers.file.type}"

If the {path} is a directory, it would respond with serving a directory and if it’s a file, it would respond with serving a file. Something like that. And you could use the expression matcher to do something conditionally based on that, like expression {http.matchers.file.type} == 'directory'

1 Like

I just updated my pending PR that fixes some behaviours with the file matcher, and added the {http.matcher.file.type} placeholder as a new feature, if you want to give it a shot:

https://github.com/caddyserver/caddy/pull/3684

Couldn’t this be achieved also with two file matchers? If it ends in / it will only match a directory, otherwise it should only match a file, I think?

That’s part of the bug that PR is meant to fix @matt, it wasn’t matching directories at all before.

1 Like

Gotcha, now I understand. thanks!

This topic was automatically closed after 30 days. New replies are no longer allowed.