Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,17 @@ Read `release_notes.md` for commit level details.

## [Unreleased]
### Enhancements
- [Experimental] Add `direct_connect` capability for the Ruby client in order to handle `directConnect` capability in a create session response by Appium server
- Update http client following `directConnectProtocol`, `directConnectHost`, `directConnectPort` and `directConnectPath`
if `direct_connect` capability for ruby_lib_core is `true`
- This will resolve a performance issue if a user has a proxy server to handle requests from client to Appium server.
With this feature, the user can send requests directly to the Appium server after create session skipping the proxy server.
```
# create session
client <---> proxy server <---> appium server <> devices
# Following requests after the create session
client <----------------------> appium server <> devices
```

### Bug fixes
- Fix potential override of `AppManagement#background_app`
Expand Down
17 changes: 17 additions & 0 deletions lib/appium_lib_core/common/base/driver.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,23 @@ def dialect
@bridge.dialect
end

# Update `server_url` and HTTP clients following this arguments, protocol, host, port and path.
# After this method, `@bridge.http` will be a new instance following them instead of `server_url` which is
# set before creating session.
#
# @example
#
# driver = core.start_driver server_url: 'http://example1.com:8000/wd/hub # @bridge.http is for `http://example1.com:8000/wd/hub/`
# driver.update_sending_request_to protocol: 'https', host: 'example2.com', port: 9000, path: '/wd/hub'
# driver.manage.timeouts.implicit_wait = 10 # @bridge.http is for `https://example2.com:9000/wd/hub/`
#
def update_sending_request_to(protocol:, host:, port:, path:)
@bridge.http.update_sending_request_to(scheme: protocol,
host: host,
port: port,
path: path)
end

### Methods for Appium

# Lock the device
Expand Down
31 changes: 31 additions & 0 deletions lib/appium_lib_core/common/base/http_default.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,37 @@ class Default < Selenium::WebDriver::Remote::Http::Default
"appium/ruby_lib_core/#{VERSION} (#{::Selenium::WebDriver::Remote::Http::Common::DEFAULT_HEADERS['User-Agent']})"
}.freeze

# Update `server_url` to.
# Set `@http` as nil to re-create http client for the server_url
# @private
#
# @param [string] scheme: A scheme to update server_url to
# @param [string] host: A host to update server_url to
# @param [string|integer] port: A port number to update server_url to
# @param [string] path: A path to update server_url to
#
# @return [URI] An instance of URI updated to. Returns default `server_url` if some of arguments are `nil`
def update_sending_request_to(scheme:, host:, port:, path:)
return @server_url unless validate_url_param(scheme, host, port, path)

Logger.debug("[experimental] This feature, #{__method__}, is an experimental")

# Add / if `path` does not have it
path = path.start_with?('/') ? path : "/#{path}"
path = path.end_with?('/') ? path : "#{path}/"

@http = nil
@server_url = URI.parse "#{scheme}://#{host}:#{port}#{path}"
end

private

def validate_url_param(scheme, host, port, path)
!(scheme.nil? || host.nil? || port.nil? || path.nil?)
end

public

# override to use default header
# https://github.com/SeleniumHQ/selenium/blob/master/rb/lib/selenium/webdriver/remote/http/common.rb#L46
def call(verb, url, command_hash)
Expand Down
19 changes: 19 additions & 0 deletions lib/appium_lib_core/driver.rb
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,16 @@ class Driver
# @return [Appium::Core::Base::Driver]
attr_reader :driver

# [Experimental feature]
# Enable an experimental feature updating Appium HTTP client following `directConnectProtocol`, `directConnectHost`,
# `directConnectPort` and `directConnectPath` after session creation if the server returns them as a part of the response
# capability in _create session_.
#
# Ignore them if this parameter is `false`. Defaults to false.
#
# @return [Bool]
attr_reader :direct_connect

# Creates a new global driver and extend particular methods to `target`
# @param [Class] target Extend particular methods to this target.
# @param [Hash] opts A options include capabilities for the Appium Server and for the client.
Expand Down Expand Up @@ -244,6 +254,13 @@ def start_driver(server_url: nil,
url: @custom_url,
listener: @listener)

if @direct_connect
@driver.update_sending_request_to(protocol: @driver.capabilities['directConnectProtocol'],
host: @driver.capabilities['directConnectHost'],
port: @driver.capabilities['directConnectPort'],
path: @driver.capabilities['directConnectPath'])
end

# export session
write_session_id(@driver.session_id, @export_session_path) if @export_session
rescue Errno::ECONNREFUSED
Expand Down Expand Up @@ -454,6 +471,8 @@ def set_appium_lib_specific_values(appium_lib_opts)
@export_session = appium_lib_opts.fetch :export_session, false
@export_session_path = appium_lib_opts.fetch :export_session_path, default_tmp_appium_lib_session

@direct_connect = appium_lib_opts.fetch :direct_access, false

@port = appium_lib_opts.fetch :port, DEFAULT_APPIUM_PORT

# timeout and interval used in ::Appium::Comm.wait/wait_true
Expand Down
55 changes: 55 additions & 0 deletions test/test_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ def self.android(activity_name = nil)
new.android(activity_name)
end

def self.android_direct
new.android_direct
end

def self.android_web
new.android_web
end
Expand Down Expand Up @@ -132,6 +136,19 @@ def android(activity_name = nil)
}
end

def android_direct
{
desired_capabilities: android[:desired_capabilities],
appium_lib: {
export_session: true,
wait: 30,
wait_timeout: 20,
wait_interval: 1,
direct_access: true
}
}
end

def android_web
{
caps: {
Expand Down Expand Up @@ -287,6 +304,44 @@ def android_mock_create_session_w3c
driver
end

def android_mock_create_session_w3c_direct(core)
response = {
value: {
sessionId: '1234567890',
capabilities: {
platformName: :android,
automationName: ENV['AUTOMATION_NAME_DROID'] || 'uiautomator2',
app: 'test/functional/app/api.apk.zip',
platformVersion: '7.1.1',
deviceName: 'Android Emulator',
appPackage: 'io.appium.android.apis',
appActivity: 'io.appium.android.apis.ApiDemos',
someCapability: 'some_capability',
unicodeKeyboard: true,
resetKeyboard: true,
directConnectProtocol: 'http',
directConnectHost: 'localhost',
directConnectPort: '8888',
directConnectPath: '/wd/hub'
}
}
}.to_json

stub_request(:post, 'http://127.0.0.1:4723/wd/hub/session')
.to_return(headers: HEADER, status: 200, body: response)

stub_request(:post, 'http://localhost:8888/wd/hub/session/1234567890/timeouts')
.with(body: { implicit: 30_000 }.to_json)
.to_return(headers: HEADER, status: 200, body: { value: nil }.to_json)

driver = core.start_driver

assert_requested(:post, 'http://127.0.0.1:4723/wd/hub/session', times: 1)
assert_requested(:post, 'http://localhost:8888/wd/hub/session/1234567890/timeouts',
body: { implicit: 30_000 }.to_json, times: 1)
driver
end

def ios_mock_create_session
response = {
status: 0, # To make bridge.dialect == :oss
Expand Down
20 changes: 20 additions & 0 deletions test/unit/driver_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,26 @@ def test_default_timeout_for_http_client

assert_equal 999_999, @core.http_client.open_timeout
assert_equal 999_999, @core.http_client.read_timeout
uri = @driver.send(:bridge).http.send(:server_url)
assert !@core.direct_connect
assert_equal 'http', uri.scheme
assert_equal '127.0.0.1', uri.host
assert_equal 4723, uri.port
assert_equal '/wd/hub/', uri.path
end

def test_default_timeout_for_http_client_with_direct
core = ::Appium::Core.for(Caps.android_direct)
driver = android_mock_create_session_w3c_direct(core)

assert_equal 999_999, driver.send(:bridge).http.open_timeout
assert_equal 999_999, driver.send(:bridge).http.read_timeout
uri = driver.send(:bridge).http.send(:server_url)
assert core.direct_connect
assert_equal 'http', uri.scheme
assert_equal 'localhost', uri.host
assert_equal 8888, uri.port
assert_equal '/wd/hub/', uri.path
end

# https://www.w3.org/TR/webdriver1/
Expand Down