mathpaquette / IQFeed.CSharpApiClient

IQFeed.CSharpApiClient is fastest and the most well-designed C# DTN IQFeed socket API connector available
MIT License
120 stars 43 forks source link

Login and Password in Registry #85

Open veng1 opened 4 years ago

veng1 commented 4 years ago

I'm not too fond of putting my IQfeed login and password in code like appconfig nor do I want to add it to the environment. It seems easier to use what IQFeed already stores in the Registry. Also the Product ID can be stored there. The following replacement for IQFeedLauncher.cs

using System; using System.Configuration; using System.Diagnostics; using System.Threading; using IQFeed.CSharpApiClient.Extensions; using IQFeed.CSharpApiClient.Socket; using IQFeed.CSharpApiClient.Streaming.Admin; using IQFeed.CSharpApiClient.Streaming.Admin.Messages; using Microsoft.Win32;

namespace IQFeed.CSharpApiClient { // ReSharper disable once InconsistentNaming public static class IQFeedLauncher { public static void Start(string login = null, string password = null, string productId = null, string productVersion = null, int connectionTimeoutMs = 100, int retry = 50) { var appSettings = ConfigurationManager.AppSettings;

        // Add <PackageReference Include="Microsoft.Win32.Registry" Version="4.4.0" /> to csproj
        RegistryKey key = Registry.CurrentUser.OpenSubKey("Software\\DTN\\IQFeed\\Startup");

        login = login ??
                key.GetValue("LOGIN").ToString() ??

                Environment.GetEnvironmentVariable("IQCONNECT_LOGIN") ??
                appSettings["IQConnect:login"].NullIfEmpty() ??
                throw new Exception("Unable to find IQConnect login from environment variable or app.config");

        password = password ??
                    key.GetValue("PASSWORD").ToString() ??
                   Environment.GetEnvironmentVariable("IQCONNECT_PASSWORD") ??
                   appSettings["IQConnect:password"].NullIfEmpty() ??
                   throw new Exception("Unable to find IQConnect password from environment variable or app.config");

        productId = productId ??
                    key.GetValue("PRODUCT").ToString() ??
                    Environment.GetEnvironmentVariable("IQCONNECT_PRODUCT_ID") ??
                    appSettings["IQConnect:product_id"].NullIfEmpty() ??
                    throw new Exception("Unable to find IQConnect product ID from environment variable or app.config");

        productVersion = productVersion ??
                         Environment.GetEnvironmentVariable("IQCONNECT_PRODUCT_VERSION") ??
                         appSettings["IQConnect:product_version"].NullIfEmpty() ??
                         "1.0.0.0";

        var iqConnectParameters = $"-product {productId} -version {productVersion} -login {login} -password {password} -autoconnect";
        Process.Start("IQConnect.exe", iqConnectParameters);

        WaitForAdminPortReady(connectionTimeoutMs, retry);
        WaitForServerConnectedStatus(IQFeedDefault.Hostname, IQFeedDefault.AdminPort);
    }

    public static void Terminate()
    {
        foreach (var process in Process.GetProcessesByName("IQConnect"))
        {
            process.Kill();
        }
    }

    private static void WaitForAdminPortReady(int connectionTimeoutMs, int retry)
    {
        var adminPortReady = SocketDiagnostic.IsPortOpen(IQFeedDefault.Hostname, IQFeedDefault.AdminPort, connectionTimeoutMs, retry);
        if (!adminPortReady)
            throw new Exception($"Can't establish TCP connection with host: {IQFeedDefault.Hostname}:{IQFeedDefault.AdminPort}");
    }

    private static void WaitForServerConnectedStatus(string host, int port, int timeoutMs = 10000)
    {
        var manualResetEvent = new ManualResetEvent(false);
        var adminClient = AdminClientFactory.CreateNew(host, port);

        adminClient.Stats += AdminClientOnStats;
        adminClient.Connect();

        var connected = manualResetEvent.WaitOne(timeoutMs);
        if (!connected)
            throw new Exception($"Haven't received connected status with host: {host}:{port}");

        adminClient.Stats -= AdminClientOnStats;
        adminClient.Disconnect();

        void AdminClientOnStats(StatsMessage message)
        {
            if (message.Status == StatsStatusType.Connected)
                manualResetEvent.Set();
        }
    }
}

}

mathpaquette commented 4 years ago

image

mathpaquette commented 4 years ago

but whats the problem of using env variables?

mathpaquette commented 4 years ago

@veng1 but look, I've understood your point. Basically, if you've saved already your credentials into IQFeed, there's no reason for throwing an error when launching the IQFeedLaucher isnt it. The problem is that your code is only windows compatible...

hunterfries commented 4 years ago

@mathpaquette I'm not advocating one way on the other, but conditional logic around what's returned from Environment.OSVersion could skirt this concern.

mathpaquette commented 4 years ago

@hunterfries I totally agree with you. at the end of the day, we need to start IQConnect without parameters if I understand correctly.