Skip to content

fix: update executor and driver server URL state on directConnect - #2500

Open
hoanle0126 wants to merge 1 commit into
appium:masterfrom
hoanle0126:fix/direct-connect-server-url-state
Open

hoanle0126 wants to merge 1 commit into
appium:masterfrom
hoanle0126:fix/direct-connect-server-url-state

Conversation

@hoanle0126

Copy link
Copy Markdown
Contributor

Change list

  • Update AppiumCommandExecutor.overrideServerUrl to refresh the stored client configuration and remote server address alongside the HTTP client, and populate the base URL from AppiumDriverLocalService when omitted in the client configuration.
  • Delegate AppiumDriver.getRemoteAddress to the active command executor so callers (including SeleniumBridge and log listeners) observe the updated server URL after directConnect or subsequent URL overrides.
  • Handle bare IPv6 literals, relative paths without a leading slash, blank direct-connect capability strings, and case-insensitive HTTPS protocol matching in DirectConnect.

Types of changes

  • No changes in production code.
  • Bugfix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)

Details

After the command executor became standalone in v11, overrideServerUrl created a new HTTP client with the overridden base URL without updating the executor fields. That left getAddressOfRemoteServer(), the driver remote address, and the Selenium bridge client configuration pointing to the initial hub URL after direct connect.

Added unit tests covering executor state updates, Selenium bridge client configuration propagation, local service base URL fallback, and IPv6 / path / blank-capability cases.

@mykola-mokhnach mykola-mokhnach left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • [minor] ListensToSyslogMessages.java:69 and ListensToLogcatMessages.java:68 aren't in the diff, but this change affects them. They choose ws or wss from getAddressOfRemoteServer(), which now switches to https after directConnect. The host and port arguments still come from the caller, usually the original hub. startSyslogBroadcast(hubHost, 4723) now opens wss://hubHost:4723/... against a plain-WS port, where before this PR it used ws. Should these helpers also take the host and port from the updated URL, or should the scheme stay tied to the caller's endpoint?

DirectConnectUrlSafety.requireSafeOverrideTarget(serverUrl);
HttpClient newClient = getHttpClientFactory().createClient(appiumClientConfig.baseUrl(serverUrl));
HttpClient oldClient = this.client;
DirectConnectUrlSafety.requireSafeOverrideTarget(requireNonNull(serverUrl, "Server URL"));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[minor] appiumClientConfig, remoteServer and client are now three separate volatile writes. A thread calling getClient() or getAddressOfRemoteServer() during an override can see the new URL with the old client. volatile doesn't make the group atomic. remoteServer is also just appiumClientConfig.baseUrl() cached, which is the kind of duplicated state that caused this bug. Could getAddressOfRemoteServer() read appiumClientConfig.baseUrl() directly, and could the config and client sit in one immutable holder behind a single volatile reference (or this swap be synchronized)?

public AppiumCommandExecutor(Map<String, AppiumCommandInfo> additionalCommands,
AppiumDriverLocalService service, AppiumClientConfig appiumClientConfig) {
this(additionalCommands, service, Factory.createDefault(), appiumClientConfig);
this(additionalCommands, requireNonNull(service), Factory.createDefault(), appiumClientConfig);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[minor] Adding requireNonNull(service) here changes behavior. The delegated 4-arg constructor accepts a null service (it wraps it in ofNullable), so a caller passing null to this overload used to get a working executor and now gets an NPE. Is that break intended? If so, it's worth a note in the PR description. It isn't part of the directConnect fix.

*/
public URL getUrl() throws MalformedURLException {
String newUrlCandidate = String.format("%s://%s:%s%s", this.protocol, this.host, this.port, this.path);
String formattedHost = formatHost(this.host);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nit] getUrl() formats the host and path before checking isValid(), only so the error message can include the candidate. That forces formatHost and normalizePath to handle null, and normalizePath returns the string "null". Checking isValid() first and formatting after would remove those placeholder branches.

* @return the remote server URL, or {@code null} if not configured
*/
@Nullable
public URL getRemoteAddress() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nit] getRemoteAddress() now reads from the executor, so the remoteAddress field is only a fallback for a non-AppiumCommandExecutor. Both constructors only ever create or accept an AppiumCommandExecutor, so the field and the instanceof branch look dead. Dropping them would make it clear where the value comes from.

Path tempDir;

private static final class RecordingHttpClient implements HttpClient {
private final ClientConfig config;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[minor] RecordingHttpClient.config is stored but never read. The overrideServerUrl test never checks that the replacement client was created with the overridden base URI, which was the original regression. Could it assert created.get(1).config.baseUri() equals the override URL, and the same for the preserved readTimeout? The drivers created in the new tests are also never quit.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants