Selenium: FindElement so slow on .NetCore Version

Created on 1 Nov 2017  Â·  9Comments  Â·  Source: SeleniumHQ/selenium

Meta -

OS: Windows 10
Selenium Version: 3.7 latest on the github master branch, and 3.6 on nuget both .NET Core
Browser: Firefox and PhatomJS

Browser Version:
Firefox 47
PhatomJS 2.1

Python 3 bindings with practically the same code run way faster than the .net core version

In .net core with this code:
https://i.imgur.com/VdkiPdk.png

Output:
3168
3110

I believe it is not supposed to take 3 seconds to find an element on a simple page, in the python version it was so fast that i didn't even had to measure, it was way below 50 ms

Most helpful comment

This issue with command execution 1 second delay is a serious problem with using .NET Core. It reproduces with different browser drivers. And it makes unusable .NET Core version of WebDriver as 1 extra second for each WebDriver command decreases performance very much.

@MarioGK is right, using "127.0.0.1" instead of "localhost" fixes this issue. Here is how I had to implement a fix for Atata Framework: atata-framework/atata#101.

@jimevans is it possible to change the using of "localhost" with "127.0.0.1" in next WebDriver release? As I see, it is hardcoded in DriverService.ServiceUrl property and FirefoxDriver.CreateExtensionConnection method. As an alternative, it would be great if at least public configurational property/method will appear in WebDriver API to change "localhost" value to "127.0.0.1".

For people who wants to bypass this issue in current version of WebDriver (3.6.0 - 3.7.0) here is a method that replaces "localhost" to "127.0.0.1" in HttpCommandExecutor.remoteServerUri field and fixes the performance issue:

public static void FixDriverCommandExecutionDelay(RemoteWebDriver driver)
{
    PropertyInfo commandExecutorProperty = typeof(RemoteWebDriver).GetProperty("CommandExecutor", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.GetProperty);
    ICommandExecutor commandExecutor = (ICommandExecutor)commandExecutorProperty.GetValue(driver);

    FieldInfo remoteServerUriField = commandExecutor.GetType().GetField("remoteServerUri", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.SetField);

    if (remoteServerUriField == null)
    {
        FieldInfo internalExecutorField = commandExecutor.GetType().GetField("internalExecutor", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.GetField);
        commandExecutor = (ICommandExecutor)internalExecutorField.GetValue(commandExecutor);
        remoteServerUriField = commandExecutor.GetType().GetField("remoteServerUri", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.SetField);
    }

    if (remoteServerUriField != null)
    {
        string remoteServerUri = remoteServerUriField.GetValue(commandExecutor).ToString();

        string localhostUriPrefix = "http://localhost";

        if (remoteServerUri.StartsWith(localhostUriPrefix))
        {
            remoteServerUri = remoteServerUri.Replace(localhostUriPrefix, "http://127.0.0.1");

            remoteServerUriField.SetValue(commandExecutor, new Uri(remoteServerUri));
        }
    }
}

Just copy this method to some class and invoke it passing RemoteWebDriver instance. The workaround is using .NET reflection, but it is the only way to handle this issue that I currently see.

All 9 comments

This is a known issue with the .NET Core support, which is still considered experimental for this project. It is fully called out in the project CHANGELOG entry which outlines the support of .NET Core. The root cause of the issue is a bug in .NET Core itself.

@jimevans Isnt there any hotfix for the moment ? like using 127.0.0.1 instead of localhost, as mentioned in one of the issues on the second link you sent me ?

This issue with command execution 1 second delay is a serious problem with using .NET Core. It reproduces with different browser drivers. And it makes unusable .NET Core version of WebDriver as 1 extra second for each WebDriver command decreases performance very much.

@MarioGK is right, using "127.0.0.1" instead of "localhost" fixes this issue. Here is how I had to implement a fix for Atata Framework: atata-framework/atata#101.

@jimevans is it possible to change the using of "localhost" with "127.0.0.1" in next WebDriver release? As I see, it is hardcoded in DriverService.ServiceUrl property and FirefoxDriver.CreateExtensionConnection method. As an alternative, it would be great if at least public configurational property/method will appear in WebDriver API to change "localhost" value to "127.0.0.1".

For people who wants to bypass this issue in current version of WebDriver (3.6.0 - 3.7.0) here is a method that replaces "localhost" to "127.0.0.1" in HttpCommandExecutor.remoteServerUri field and fixes the performance issue:

public static void FixDriverCommandExecutionDelay(RemoteWebDriver driver)
{
    PropertyInfo commandExecutorProperty = typeof(RemoteWebDriver).GetProperty("CommandExecutor", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.GetProperty);
    ICommandExecutor commandExecutor = (ICommandExecutor)commandExecutorProperty.GetValue(driver);

    FieldInfo remoteServerUriField = commandExecutor.GetType().GetField("remoteServerUri", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.SetField);

    if (remoteServerUriField == null)
    {
        FieldInfo internalExecutorField = commandExecutor.GetType().GetField("internalExecutor", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.GetField);
        commandExecutor = (ICommandExecutor)internalExecutorField.GetValue(commandExecutor);
        remoteServerUriField = commandExecutor.GetType().GetField("remoteServerUri", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.SetField);
    }

    if (remoteServerUriField != null)
    {
        string remoteServerUri = remoteServerUriField.GetValue(commandExecutor).ToString();

        string localhostUriPrefix = "http://localhost";

        if (remoteServerUri.StartsWith(localhostUriPrefix))
        {
            remoteServerUri = remoteServerUri.Replace(localhostUriPrefix, "http://127.0.0.1");

            remoteServerUriField.SetValue(commandExecutor, new Uri(remoteServerUri));
        }
    }
}

Just copy this method to some class and invoke it passing RemoteWebDriver instance. The workaround is using .NET reflection, but it is the only way to handle this issue that I currently see.

What’s your solution for users who don’t have an IPv4 stack installed?

Should try "::1" instead of "127.0.0.1" for IPv6. That's why I pointed that will be good to have some configuration option.

Current "localhost" issue seems like a bug of .NET Core 2. Hopefully MS team will fix this issue in .NET Core someday. But it will be nice to have a workaround until that time. Having some writable host property with "localhost" value by default and ability to change this value.

But what’s to stop you from doing something like the following in the meantime as a workaround:

ChromeDriverService service = ChromeDriverService.CreateDefaultService();
service.Port = 5555; // Some port value.
service.Start();
IWebDriver driver = new RemoteWebDriver(new Uri(“http://127.0.0.1:5555”), new ChromeOptions());

Yes, I’m aware that’s supremely inelegant. No, I don’t like it any more than you do. And I’ll see what I can do about offering something a little cleaner for the local execution case when my limited time permits.

Thanks.

Is it solved now?

Was this page helpful?
0 / 5 - 0 ratings