-
Notifications
You must be signed in to change notification settings - Fork 423
Handle symlinking edge cases #1660
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
581859b
fd60f48
2227900
137f74c
96a3692
ce674a9
d1cec49
efc5627
eb6a4d9
7de7f55
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -138,6 +138,15 @@ func (f *FrankenPHPModule) Provision(ctx caddy.Context) error { | |
| } | ||
|
|
||
| f.resolvedDocumentRoot = root | ||
|
|
||
| // Also resolve symlinks in worker file paths when resolve_root_symlink is true | ||
| for i, wc := range f.Workers { | ||
| if !filepath.IsAbs(wc.FileName) { | ||
| continue | ||
| } | ||
| resolvedPath, _ := filepath.EvalSymlinks(wc.FileName) | ||
| f.Workers[i].FileName = resolvedPath | ||
| } | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -181,7 +190,10 @@ func (f *FrankenPHPModule) ServeHTTP(w http.ResponseWriter, r *http.Request, _ c | |
| if documentRoot == "" && frankenphp.EmbeddedAppPath != "" { | ||
| documentRoot = frankenphp.EmbeddedAppPath | ||
| } | ||
| documentRootOption = frankenphp.WithRequestDocumentRoot(documentRoot, *f.ResolveRootSymlink) | ||
| // If we do not have a resolved document root, then we cannot resolve the symlink of our cwd because it may | ||
| // resolve to a different directory than the one we are currently in. | ||
| // This is especially important if there are workers running. | ||
| documentRootOption = frankenphp.WithRequestDocumentRoot(documentRoot, false) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not checking for symlinks here seems like it might break some setups. Definitely would be nice to test all the edge cases somehow (should be possible with caddy_tests?). Tbh though, I don't know what the current edge cases with symlinks are
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I’m pretty sure we don’t want to evaluate symlinks here. In the case where a document root was expressly given (such as prod setups), we will never even enter this branch. In the case where a document root isn’t given, we need to be compatible with how we resolve workers. There is an edge case here that I didn’t cover, and I’m working out how to handle it: chained symlinks. I suspect I’ll have to go back to the drawing board. |
||
| } else { | ||
| documentRoot = f.resolvedDocumentRoot | ||
| documentRootOption = frankenphp.WithRequestResolvedDocumentRoot(documentRoot) | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1 @@ | ||||||||
| test | ||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a symlink, not a text file. I don't think we should be changing the content |
||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| <?php | ||
|
|
||
| if (isset($_SERVER['FRANKENPHP_WORKER'])) { | ||
| $i = 0; | ||
| do { | ||
| $ok = frankenphp_handle_request(function () use ($i): void { | ||
| echo "DOCUMENT_ROOT=" . $_SERVER['DOCUMENT_ROOT'] . "\n"; | ||
| }); | ||
| $i++; | ||
| } while ($ok); | ||
| } else { | ||
| echo "DOCUMENT_ROOT=" . $_SERVER['DOCUMENT_ROOT'] . "\n"; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| <?php | ||
|
|
||
| if (!isset($_SERVER['FRANKENPHP_WORKER'])) { | ||
| die("Error: This script must be run in worker mode (FRANKENPHP_WORKER not set to '1')\n"); | ||
| } | ||
|
|
||
| $i = 0; | ||
| do { | ||
| $ok = frankenphp_handle_request(function () use ($i): void { | ||
| echo sprintf("Request: %d\n", $i); | ||
| }); | ||
| $i++; | ||
| } while ($ok); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| <?php | ||
|
|
||
| if (!isset($_SERVER['FRANKENPHP_WORKER'])) { | ||
| die("Error: This script must be run in worker mode (FRANKENPHP_WORKER not set to '1')\n"); | ||
| } | ||
|
|
||
| $i = 0; | ||
| do { | ||
| $ok = frankenphp_handle_request(function () use ($i): void { | ||
| echo sprintf("Nested request: %d\n", $i); | ||
| }); | ||
| $i++; | ||
| } while ($ok); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -111,7 +111,14 @@ func getWorkerByPath(path string) *worker { | |
| } | ||
|
|
||
| func newWorker(o workerOpt) (*worker, error) { | ||
| absFileName, err := fastabs.FastAbs(o.fileName) | ||
| // Order is important! | ||
| // This order ensures that FrankenPHP started from inside a symlinked directory will properly resolve any paths. | ||
| // If it is started from outside a symlinked directory, it is resolved to the same path that we use in the Caddy module. | ||
| absFileName, err := filepath.EvalSymlinks(o.fileName) | ||
| if err != nil { | ||
| return nil, fmt.Errorf("worker filename is invalid %q: %w", o.fileName, err) | ||
| } | ||
|
Comment on lines
+117
to
+120
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I like it 👍 explicitly checking if a worker exists is something I actually wanted to do for a long time
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Or wait, evalSymlinks will just evaluate symlinks. In that case it would also be nice to error right away if there's no file there. Can also be done in another PR though
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it errors if the file doesn't exist (I had to update a failing test -- haven't pushed the commit yet) |
||
| absFileName, err = fastabs.FastAbs(absFileName) | ||
| if err != nil { | ||
| return nil, fmt.Errorf("worker filename is invalid %q: %w", o.fileName, err) | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Couldn't you use subtests instead a separated tests?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll refactor it tomorrow, thanks for the link.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh and Happy New Year!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Happy new year!! And thanks.