@marcel91200 / caddy Mirror · read-only / commits / 6e9ac248
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 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)