@marcel91200 / caddy Mirror · read-only / commits / efd9251a
Fork 0

Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS

Mirrored from https://github.com/Slazee/caddy.git

Synced Nov 12, 05:45 UTC

fileserver: Add `first_exist_fallback` strategy for `try_files` (#6699)

* feat: add first_exist_or_fallback strategy for try_files * fix tests * linter

Kévin Dunglas committed Dec 3, 2024 at 13:44 UTC efd9251ad38a4fd9f7d900445400ac3c8e564c28
7 files changed +40 -14
caddytest/integration/caddyfile_adapt/php_fastcgi_handle_response.caddyfiletest
+1
@@ -58,6 +58,7 @@
58 "{http.request.uri.path}/index.php",
59 "index.php"
60 ],
61 + "try_policy": "first_exist_fallback",
62 "split_path": [
63 ".php"
64 ]
caddytest/integration/caddyfile_adapt/php_fastcgi_matcher.caddyfiletest
+2 -1
@@ -73,7 +73,8 @@ php_fastcgi @test localhost:9000
73 "{http.request.uri.path}",
74 "{http.request.uri.path}/index.php",
75 "index.php"
76 - ]
76 + ],
77 + "try_policy": "first_exist_fallback"
78 }
79 }
80 ]
caddytest/integration/caddyfile_adapt/php_fastcgi_subdirectives.caddyfiletest
+1
@@ -59,6 +59,7 @@ php_fastcgi localhost:9000 {
59 "{http.request.uri.path}/index.php5",
60 "index.php5"
61 ],
62 + "try_policy": "first_exist_fallback",
63 "split_path": [
64 ".php",
65 ".php5"
caddytest/integration/caddyfile_adapt/php_fastcgi_try_files_override_no_dir_index.caddyfiletest
+1
@@ -32,6 +32,7 @@ php_fastcgi localhost:9000 {
32 "{http.request.uri.path}",
33 "index.php"
34 ],
35 + "try_policy": "first_exist_fallback",
36 "split_path": [
37 ".php",
38 ".php5"
modules/caddyhttp/fileserver/caddyfile.go
+1 -1
@@ -274,7 +274,7 @@ func parseTryFiles(h httpcaddyfile.Helper) ([]httpcaddyfile.ConfigValue, error)
274 tryPolicy = h.Val()
275
276 switch tryPolicy {
277 - case tryPolicyFirstExist, tryPolicyLargestSize, tryPolicySmallestSize, tryPolicyMostRecentlyMod:
277 + case tryPolicyFirstExist, tryPolicyFirstExistFallback, tryPolicyLargestSize, tryPolicySmallestSize, tryPolicyMostRecentlyMod:
278 default:
279 return nil, h.Errf("unrecognized try policy: %s", tryPolicy)
280 }
modules/caddyhttp/fileserver/matcher.go
+26 -12
@@ -90,6 +90,7 @@ type MatchFile struct {
90 // How to choose a file in TryFiles. Can be:
91 //
92 // - first_exist
93 + // - first_exist_fallback
94 // - smallest_size
95 // - largest_size
96 // - most_recently_modified
@@ -415,13 +416,13 @@ func (m MatchFile) selectFile(r *http.Request) (bool, error) {
416 }
417
418 // setPlaceholders creates the placeholders for the matched file
418 - setPlaceholders := func(candidate matchCandidate, info fs.FileInfo) {
419 + setPlaceholders := func(candidate matchCandidate, isDir bool) {
420 repl.Set("http.matchers.file.relative", filepath.ToSlash(candidate.relative))
421 repl.Set("http.matchers.file.absolute", filepath.ToSlash(candidate.fullpath))
422 repl.Set("http.matchers.file.remainder", filepath.ToSlash(candidate.splitRemainder))
423
424 fileType := "file"
424 - if info.IsDir() {
425 + if isDir {
426 fileType = "directory"
427 }
428 repl.Set("http.matchers.file.type", fileType)
@@ -429,8 +430,13 @@ func (m MatchFile) selectFile(r *http.Request) (bool, error) {
430
431 // match file according to the configured policy
432 switch m.TryPolicy {
432 - case "", tryPolicyFirstExist:
433 - for _, pattern := range m.TryFiles {
433 + case "", tryPolicyFirstExist, tryPolicyFirstExistFallback:
434 + maxI := -1
435 + if m.TryPolicy == tryPolicyFirstExistFallback {
436 + maxI = len(m.TryFiles) - 1
437 + }
438 +
439 + for i, pattern := range m.TryFiles {
440 // If the pattern is a status code, emit an error,
441 // which short-circuits the middleware pipeline and
442 // writes an HTTP error response.
@@ -440,8 +446,15 @@ func (m MatchFile) selectFile(r *http.Request) (bool, error) {
446
447 candidates := makeCandidates(pattern)
448 for _, c := range candidates {
449 + // Skip the IO if using fallback policy and it's the latest item
450 + if i == maxI {
451 + setPlaceholders(c, false)
452 +
453 + return true, nil
454 + }
455 +
456 if info, exists := m.strictFileExists(fileSystem, c.fullpath); exists {
444 - setPlaceholders(c, info)
457 + setPlaceholders(c, info.IsDir())
458 return true, nil
459 }
460 }
@@ -465,7 +478,7 @@ func (m MatchFile) selectFile(r *http.Request) (bool, error) {
478 if largestInfo == nil {
479 return false, nil
480 }
468 - setPlaceholders(largest, largestInfo)
481 + setPlaceholders(largest, largestInfo.IsDir())
482 return true, nil
483
484 case tryPolicySmallestSize:
@@ -486,7 +499,7 @@ func (m MatchFile) selectFile(r *http.Request) (bool, error) {
499 if smallestInfo == nil {
500 return false, nil
501 }
489 - setPlaceholders(smallest, smallestInfo)
502 + setPlaceholders(smallest, smallestInfo.IsDir())
503 return true, nil
504
505 case tryPolicyMostRecentlyMod:
@@ -506,7 +519,7 @@ func (m MatchFile) selectFile(r *http.Request) (bool, error) {
519 if recentInfo == nil {
520 return false, nil
521 }
509 - setPlaceholders(recent, recentInfo)
522 + setPlaceholders(recent, recentInfo.IsDir())
523 return true, nil
524 }
525
@@ -708,10 +721,11 @@ var globSafeRepl = strings.NewReplacer(
721 )
722
723 const (
711 - tryPolicyFirstExist = "first_exist"
712 - tryPolicyLargestSize = "largest_size"
713 - tryPolicySmallestSize = "smallest_size"
714 - tryPolicyMostRecentlyMod = "most_recently_modified"
724 + tryPolicyFirstExist = "first_exist"
725 + tryPolicyFirstExistFallback = "first_exist_fallback"
726 + tryPolicyLargestSize = "largest_size"
727 + tryPolicySmallestSize = "smallest_size"
728 + tryPolicyMostRecentlyMod = "most_recently_modified"
729 )
730
731 // Interface guards
modules/caddyhttp/reverseproxy/fastcgi/caddyfile.go
+8
@@ -18,6 +18,7 @@ import (
18 "encoding/json"
19 "net/http"
20 "strconv"
21 + "strings"
22
23 "github.com/caddyserver/caddy/v2"
24 "github.com/caddyserver/caddy/v2/caddyconfig"
@@ -312,12 +313,18 @@ func parsePHPFastCGI(h httpcaddyfile.Helper) ([]httpcaddyfile.ConfigValue, error
313 if indexFile != "off" {
314 dirRedir := false
315 dirIndex := "{http.request.uri.path}/" + indexFile
316 + tryPolicy := "first_exist_fallback"
317
318 // if tryFiles wasn't overridden, use a reasonable default
319 if len(tryFiles) == 0 {
320 tryFiles = []string{"{http.request.uri.path}", dirIndex, indexFile}
321 dirRedir = true
322 } else {
323 + if !strings.HasSuffix(tryFiles[len(tryFiles)-1], ".php") {
324 + // use first_exist strategy if the last file is not a PHP file
325 + tryPolicy = ""
326 + }
327 +
328 for _, tf := range tryFiles {
329 if tf == dirIndex {
330 dirRedir = true
@@ -357,6 +364,7 @@ func parsePHPFastCGI(h httpcaddyfile.Helper) ([]httpcaddyfile.ConfigValue, error
364 rewriteMatcherSet := caddy.ModuleMap{
365 "file": h.JSON(fileserver.MatchFile{
366 TryFiles: tryFiles,
367 + TryPolicy: tryPolicy,
368 SplitPath: extensions,
369 }),
370 }