-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
Respect AppContext.SetData with APP_CONFIG_FILE key #56748
Conversation
...ries/System.Configuration.ConfigurationManager/src/System/Configuration/ClientConfigPaths.cs
Outdated
Show resolved
Hide resolved
...ries/System.Configuration.ConfigurationManager/src/System/Configuration/ClientConfigPaths.cs
Outdated
Show resolved
Hide resolved
...stem.Configuration.ConfigurationManager/tests/System/Configuration/ConfigurationPathTests.cs
Show resolved
Hide resolved
} | ||
else | ||
{ | ||
ApplicationConfigUri = Path.GetFullPath(externalConfigPath); |
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.
This is implicitly prefixing with whatever the current directory is at the time the config system is initialized, I guess?
In the desktop code it is using the appbase to prefix (naming.cpp line 3241 .. assuming I'm looking in the right place)
I think in .NET Core that is AppDomain.BaseDirectory, so should this be Path.Combine(AppDomain.CurrentDomain.BaseDirectory, externalConfigPath)
? There is something similar higher up.
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.
You can change the tests to set Environment.CurrentDirectory to something random before reading the value (I'm guessing the test will fail right now)
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.
When you use Path.Combine
you need to be careful with paths that begin with \
, as you'll get the second path against the root of the current directory. Trim the start of the externalConfigPath
for safety.
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 have used Path.GetFullPath(externalConfigPath, AppDomain.CurrentDomain.BaseDirectory)
which seems to be working fine. The tests are now changing CurrentDirectory to validate we are relative to the BaseDirectory and not CurrentDirectory
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.
hmm seems that overload is not available everywhere...
@JeremyKuhne we don't have much familiarity with the config system -- does this seem reasonable based on your recollection of things? |
|
||
private static string CreateAppConfigFileWithSetting(string key, string rawUnquotedValue) | ||
{ | ||
string fileName = Path.GetRandomFileName() + ".config"; |
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.
These files will leak. it might be easiest to derive from FileCleanupTestBase and call GetTestFileName().
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.
this gets a bit tricky when I want to test @JeremyKuhne's scenario #56748 (comment)
I'll use that for everything but related test cases
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 have ended up modifying constructor of FileCleanupTestBase so that it allows to pickup something else than TempDirectory which in here I chose to be AppDomain.BaseDirectory - TempDirectory doesn't allow me to test relative paths correctly
...stem.Configuration.ConfigurationManager/tests/System/Configuration/ConfigurationPathTests.cs
Outdated
Show resolved
Hide resolved
@danmoseley Yes, this seems right. |
Please re-review since a bit changed here |
{ | ||
// Uri constructor would throw for relative paths but full framework doesn't. | ||
// We will mimic full framework behavior here. | ||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && externalConfigPath.Length >= 2 && externalConfigPath.StartsWith('\\') && externalConfigPath[1] != '\\') |
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.
Is there some way to avoid the OS check here? We don't normally do those. What if on Unix it started with '/' ... should it trim whatever the Path.DirectorySeparatorChar is?
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.
Just always trim any amount of leading Path.DirectorySeparatorChar
and you should be good.
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 think for Unix we will never go into this code anyway since that would mean absolute path which should be covered by the first check. I'll go ahead and trim all separators on the left - that I think this should cover 99% of the cases
src/libraries/Common/tests/TestUtilities/System/IO/FileCleanupTestBase.cs
Show resolved
Hide resolved
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.
thanks for adding this
(signed off assuming you can fix the OS check as above) |
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && externalConfigPath.Length >= 2 && externalConfigPath.StartsWith('\\') && externalConfigPath[1] != '\\') | ||
{ | ||
// if path starts with a single backslash Path.Combine would combine with a root of BaseDirectory so we trim that character to avoid that | ||
externalConfigPath = externalConfigPath.Substring(0, externalConfigPath.Length - 1); |
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.
Just wondering - does this match the .NET Framework behavior for paths starting with a single slash?
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.
using System;
using System.Reflection;
class Program
{
static void Main(string[] args)
{
AppDomain.CurrentDomain.SetData("APP_CONFIG_FILE", @"\abc");
Console.WriteLine(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile);
}
}
It prints c:\abc
, without taking BaseDirectory into account.
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.
To keep things simple, I would make this just:
if (!string.IsNullOrEmpty(externalConfigPath))
{
if (!Path.IsPathRooted(externalConfigPath))
{
path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, externalConfigPath);
}
path = Path.GetFullPath(path);
}
else if (!string.IsNullOrEmpty(ApplicationUri))
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 go ahead with solution suggested by @JeremyKuhne (#56748 (comment)), it's probably not covering all corner cases but it should be close enough. For the backslash starting directories I do not have any strong feeling we should support that as it's hard to describe the behavior in any docs especially since now we also work on Unix
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 go ahead with solution suggested by @JeremyKuhne (#56748 (comment))
Is there any other existing place in dotnet/runtime that is normalizing non-fully qualified paths using this algorithm? I think it does not have very intuitive behavior, e.g. it does not match what Path.GetFullPath(string path, string basePath)
does.
it should be close enough
I would pick the simplest solution, among the "close enough" ones. It was the gist of my suggestion.
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.
Sadly Path.IsPathRooted
will get you "relative" paths. Path.IsPathFullyQualified
(new) is the one that gives you accurate indication of whether or not the current working directory will impact the path.
It is unlikely that customers would actually desire paths that start with \
to be rooted to the current working directory drive. I think that is enough to be helpful here, other weird scenarios like C:foo\
don't need addressed I think.
I do not think it is necessary to match all .NET Framework behaviors to keep things simple. It is unlikely that you would be able to match the behavior for all corner cases anyway. The only case that is required to make the customers successful is the absolute path. The rest is optional and can be omitted. |
// We will mimic full framework behavior here. | ||
|
||
// If path starts with directory separator Path.Combine would combine with a root of BaseDirectory so we trim that character to avoid that | ||
// For absolute path we should already be covered by the other code path |
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 do not think that this is a correct statement for Linux.
Uri.IsWellFormedUriString("/home/jkotas/myconfig.xml", UriKind.Absolute)
returns false on Linux.
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.
you're right, I've done some combination of your solution but which supports URIs as well
Fixes: #931
Full framework works with following path types:
myconfig.config
file:///c:/myconfig.config
additionally: