selectFile chooses a file according to m.TryPolicy by appending the paths in m.TryFiles to m.Root, with placeholder replacements.
(r *http.Request)
| 331 | // selectFile chooses a file according to m.TryPolicy by appending |
| 332 | // the paths in m.TryFiles to m.Root, with placeholder replacements. |
| 333 | func (m MatchFile) selectFile(r *http.Request) (bool, error) { |
| 334 | repl := r.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer) |
| 335 | |
| 336 | root := filepath.Clean(repl.ReplaceAll(m.Root, ".")) |
| 337 | |
| 338 | fsName := repl.ReplaceAll(m.FileSystem, "") |
| 339 | |
| 340 | fileSystem, ok := m.fsmap.Get(fsName) |
| 341 | if !ok { |
| 342 | if c := m.logger.Check(zapcore.ErrorLevel, "use of unregistered filesystem"); c != nil { |
| 343 | c.Write(zap.String("fs", fsName)) |
| 344 | } |
| 345 | return false, nil |
| 346 | } |
| 347 | type matchCandidate struct { |
| 348 | fullpath, relative, splitRemainder string |
| 349 | } |
| 350 | |
| 351 | // makeCandidates evaluates placeholders in file and expands any glob expressions |
| 352 | // to build a list of file candidates. Special glob characters are escaped in |
| 353 | // placeholder replacements so globs cannot be expanded from placeholders, and |
| 354 | // globs are not evaluated on Windows because of its path separator character: |
| 355 | // escaping is not supported so we can't safely glob on Windows, or we can't |
| 356 | // support placeholders on Windows (pick one). (Actually, evaluating untrusted |
| 357 | // globs is not the end of the world since the file server will still hide any |
| 358 | // hidden files, it just might lead to unexpected behavior.) |
| 359 | makeCandidates := func(file string) []matchCandidate { |
| 360 | // first, evaluate placeholders in the file pattern |
| 361 | expandedFile, err := repl.ReplaceFunc(file, func(variable string, val any) (any, error) { |
| 362 | if runtime.GOOS == "windows" { |
| 363 | return val, nil |
| 364 | } |
| 365 | switch v := val.(type) { |
| 366 | case string: |
| 367 | return globSafeRepl.Replace(v), nil |
| 368 | case fmt.Stringer: |
| 369 | return globSafeRepl.Replace(v.String()), nil |
| 370 | } |
| 371 | return val, nil |
| 372 | }) |
| 373 | if err != nil { |
| 374 | if c := m.logger.Check(zapcore.ErrorLevel, "evaluating placeholders"); c != nil { |
| 375 | c.Write(zap.Error(err)) |
| 376 | } |
| 377 | |
| 378 | expandedFile = file // "oh well," I guess? |
| 379 | } |
| 380 | |
| 381 | // clean the path and split, if configured -- we must split before |
| 382 | // globbing so that the file system doesn't include the remainder |
| 383 | // ("afterSplit") in the filename; be sure to restore trailing slash |
| 384 | beforeSplit, afterSplit := m.firstSplit(path.Clean(expandedFile)) |
| 385 | if strings.HasSuffix(file, "/") { |
| 386 | beforeSplit += "/" |
| 387 | } |
| 388 | |
| 389 | // create the full path to the file by prepending the site root |
| 390 | fullPattern := caddyhttp.SanitizedPathJoin(root, beforeSplit) |
no test coverage detected