Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS
Mirrored from https://github.com/Slazee/caddy.git
Synced Nov 14, 05:15 UTC
fastcgi: Set PATH_INFO to file matcher remainder as fallback (#3739)
* fastcgi: Set PATH_INFO to file matcher remainder as fallback * fastcgi: Avoid changing scriptName when not necessary * Stylistic tweaks Co-authored-by: Matthew Holt <mholt@users.noreply.github.com>
Francis Lavoie committed
Dec 4, 2020 at 19:12 UTC
6e9ac248dd5c2f7a4cd4e37ff277a86c18085be6
3 files changed
+51
-28
modules/caddyhttp/fileserver/matcher.go
+31
-20
@@ -44,6 +44,8 @@ func init() {
44
// of the matched file.
45
// - `{http.matchers.file.type}` Set to "directory" if
46
// the matched file is a directory, "file" otherwise.
47
+// - `{http.matchers.file.remainder}` Set to the remainder
48
+// of the path if the path was split by `split_path`.
49
type MatchFile struct {
50
// The root directory, used for creating absolute
51
// file paths, and required when working with
@@ -155,11 +157,12 @@ func (m MatchFile) Validate() error {
157
}
158
159
// Match returns true if r matches m. Returns true
158
-// if a file was matched. If so, three placeholders
160
+// if a file was matched. If so, four placeholders
161
// will be available:
162
// - http.matchers.file.relative
163
// - http.matchers.file.absolute
164
// - http.matchers.file.type
165
+// - http.matchers.file.remainder
166
func (m MatchFile) Match(r *http.Request) bool {
167
return m.selectFile(r)
168
}
@@ -179,19 +182,20 @@ func (m MatchFile) selectFile(r *http.Request) (matched bool) {
182
}
183
184
// common preparation of the file into parts
182
- prepareFilePath := func(file string) (string, string) {
183
- suffix := m.firstSplit(path.Clean(repl.ReplaceAll(file, "")))
185
+ prepareFilePath := func(file string) (suffix, fullpath, remainder string) {
186
+ suffix, remainder = m.firstSplit(path.Clean(repl.ReplaceAll(file, "")))
187
if strings.HasSuffix(file, "/") {
188
suffix += "/"
189
}
187
- fullpath := sanitizedPathJoin(root, suffix)
188
- return suffix, fullpath
190
+ fullpath = sanitizedPathJoin(root, suffix)
191
+ return
192
}
193
194
// sets up the placeholders for the matched file
192
- setPlaceholders := func(info os.FileInfo, rel string, abs string) {
195
+ setPlaceholders := func(info os.FileInfo, rel string, abs string, remainder string) {
196
repl.Set("http.matchers.file.relative", rel)
197
repl.Set("http.matchers.file.absolute", abs)
198
+ repl.Set("http.matchers.file.remainder", remainder)
199
200
fileType := "file"
201
if info.IsDir() {
@@ -203,9 +207,9 @@ func (m MatchFile) selectFile(r *http.Request) (matched bool) {
207
switch m.TryPolicy {
208
case "", tryPolicyFirstExist:
209
for _, f := range m.TryFiles {
206
- suffix, fullpath := prepareFilePath(f)
210
+ suffix, fullpath, remainder := prepareFilePath(f)
211
if info, exists := strictFileExists(fullpath); exists {
208
- setPlaceholders(info, suffix, fullpath)
212
+ setPlaceholders(info, suffix, fullpath, remainder)
213
return true
214
}
215
}
@@ -214,52 +218,58 @@ func (m MatchFile) selectFile(r *http.Request) (matched bool) {
218
var largestSize int64
219
var largestFilename string
220
var largestSuffix string
221
+ var remainder string
222
var info os.FileInfo
223
for _, f := range m.TryFiles {
219
- suffix, fullpath := prepareFilePath(f)
224
+ suffix, fullpath, splitRemainder := prepareFilePath(f)
225
info, err := os.Stat(fullpath)
226
if err == nil && info.Size() > largestSize {
227
largestSize = info.Size()
228
largestFilename = fullpath
229
largestSuffix = suffix
230
+ remainder = splitRemainder
231
}
232
}
227
- setPlaceholders(info, largestSuffix, largestFilename)
233
+ setPlaceholders(info, largestSuffix, largestFilename, remainder)
234
return true
235
236
case tryPolicySmallestSize:
237
var smallestSize int64
238
var smallestFilename string
239
var smallestSuffix string
240
+ var remainder string
241
var info os.FileInfo
242
for _, f := range m.TryFiles {
236
- suffix, fullpath := prepareFilePath(f)
243
+ suffix, fullpath, splitRemainder := prepareFilePath(f)
244
info, err := os.Stat(fullpath)
245
if err == nil && (smallestSize == 0 || info.Size() < smallestSize) {
246
smallestSize = info.Size()
247
smallestFilename = fullpath
248
smallestSuffix = suffix
249
+ remainder = splitRemainder
250
}
251
}
244
- setPlaceholders(info, smallestSuffix, smallestFilename)
252
+ setPlaceholders(info, smallestSuffix, smallestFilename, remainder)
253
return true
254
255
case tryPolicyMostRecentlyMod:
256
var recentDate time.Time
257
var recentFilename string
258
var recentSuffix string
259
+ var remainder string
260
var info os.FileInfo
261
for _, f := range m.TryFiles {
253
- suffix, fullpath := prepareFilePath(f)
262
+ suffix, fullpath, splitRemainder := prepareFilePath(f)
263
info, err := os.Stat(fullpath)
264
if err == nil &&
265
(recentDate.IsZero() || info.ModTime().After(recentDate)) {
266
recentDate = info.ModTime()
267
recentFilename = fullpath
268
recentSuffix = suffix
269
+ remainder = splitRemainder
270
}
271
}
262
- setPlaceholders(info, recentSuffix, recentFilename)
272
+ setPlaceholders(info, recentSuffix, recentFilename, remainder)
273
return true
274
}
275
@@ -297,10 +307,11 @@ func strictFileExists(file string) (os.FileInfo, bool) {
307
308
// firstSplit returns the first result where the path
309
// can be split in two by a value in m.SplitPath. The
300
-// result is the first piece of the path that ends with
301
-// in the split value. Returns the path as-is if the
302
-// path cannot be split.
303
-func (m MatchFile) firstSplit(path string) string {
310
+// return values are the first piece of the path that
311
+// ends with the split substring and the remainder.
312
+// If the path cannot be split, the path is returned
313
+// as-is (with no remainder).
314
+func (m MatchFile) firstSplit(path string) (splitPart, remainder string) {
315
for _, split := range m.SplitPath {
316
if idx := indexFold(path, split); idx > -1 {
317
pos := idx + len(split)
@@ -308,10 +319,10 @@ func (m MatchFile) firstSplit(path string) string {
319
if pos != len(path) && !strings.HasPrefix(path[pos:], "/") {
320
continue
321
}
311
- return path[:pos]
322
+ return path[:pos], path[pos:]
323
}
324
}
314
- return path
325
+ return path, ""
326
}
327
328
// There is no strings.IndexFold() function like there is strings.EqualFold(),
modules/caddyhttp/fileserver/matcher_test.go
+6
-2
@@ -206,9 +206,13 @@ func TestPHPFileMatcher(t *testing.T) {
206
207
func TestFirstSplit(t *testing.T) {
208
m := MatchFile{SplitPath: []string{".php"}}
209
- actual := m.firstSplit("index.PHP/somewhere")
209
+ actual, remainder := m.firstSplit("index.PHP/somewhere")
210
expected := "index.PHP"
211
+ expectedRemainder := "/somewhere"
212
if actual != expected {
212
- t.Errorf("Expected %s but got %s", expected, actual)
213
+ t.Errorf("Expected split %s but got %s", expected, actual)
214
+ }
215
+ if remainder != expectedRemainder {
216
+ t.Errorf("Expected remainder %s but got %s", expectedRemainder, remainder)
217
}
218
}
modules/caddyhttp/reverseproxy/fastcgi/fastcgi.go
+14
-6
@@ -195,19 +195,27 @@ func (t Transport) buildEnv(r *http.Request) (map[string]string, error) {
195
}
196
197
fpath := r.URL.Path
198
+ scriptName := fpath
199
200
+ docURI := fpath
201
// split "actual path" from "path info" if configured
200
- var docURI, pathInfo string
202
+ var pathInfo string
203
if splitPos := t.splitPos(fpath); splitPos > -1 {
204
docURI = fpath[:splitPos]
205
pathInfo = fpath[splitPos:]
204
- } else {
205
- docURI = fpath
206
+
207
+ // Strip PATH_INFO from SCRIPT_NAME
208
+ scriptName = strings.TrimSuffix(scriptName, pathInfo)
209
}
207
- scriptName := fpath
210
209
- // Strip PATH_INFO from SCRIPT_NAME
210
- scriptName = strings.TrimSuffix(scriptName, pathInfo)
211
+ // Try to grab the path remainder from a file matcher
212
+ // if we didn't get a split result here.
213
+ // See https://github.com/caddyserver/caddy/issues/3718
214
+ if pathInfo == "" {
215
+ if remainder, ok := repl.GetString("http.matchers.file.remainder"); ok {
216
+ pathInfo = remainder
217
+ }
218
+ }
219
220
// SCRIPT_FILENAME is the absolute path of SCRIPT_NAME
221
scriptFilename := filepath.Join(root, scriptName)