Skip to content

Commit 61a927f

Browse files
authored
Experimental add direct host (#189)
* add directXxxx params as an experimental * tweak naming * update comments * update comments, test code * update changelog * add a debug logger
1 parent c9fff7b commit 61a927f

File tree

6 files changed

+153
-0
lines changed

6 files changed

+153
-0
lines changed

CHANGELOG.md

+11
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,17 @@ Read `release_notes.md` for commit level details.
44

55
## [Unreleased]
66
### Enhancements
7+
- [Experimental] Add `direct_connect` capability for the Ruby client in order to handle `directConnect` capability in a create session response by Appium server
8+
- Update http client following `directConnectProtocol`, `directConnectHost`, `directConnectPort` and `directConnectPath`
9+
if `direct_connect` capability for ruby_lib_core is `true`
10+
- This will resolve a performance issue if a user has a proxy server to handle requests from client to Appium server.
11+
With this feature, the user can send requests directly to the Appium server after create session skipping the proxy server.
12+
```
13+
# create session
14+
client <---> proxy server <---> appium server <> devices
15+
# Following requests after the create session
16+
client <----------------------> appium server <> devices
17+
```
718
819
### Bug fixes
920
- Fix potential override of `AppManagement#background_app`

lib/appium_lib_core/common/base/driver.rb

+17
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,23 @@ def dialect
3737
@bridge.dialect
3838
end
3939

40+
# Update `server_url` and HTTP clients following this arguments, protocol, host, port and path.
41+
# After this method, `@bridge.http` will be a new instance following them instead of `server_url` which is
42+
# set before creating session.
43+
#
44+
# @example
45+
#
46+
# driver = core.start_driver server_url: 'http://example1.com:8000/wd/hub # @bridge.http is for `http://example1.com:8000/wd/hub/`
47+
# driver.update_sending_request_to protocol: 'https', host: 'example2.com', port: 9000, path: '/wd/hub'
48+
# driver.manage.timeouts.implicit_wait = 10 # @bridge.http is for `https://example2.com:9000/wd/hub/`
49+
#
50+
def update_sending_request_to(protocol:, host:, port:, path:)
51+
@bridge.http.update_sending_request_to(scheme: protocol,
52+
host: host,
53+
port: port,
54+
path: path)
55+
end
56+
4057
### Methods for Appium
4158

4259
# Lock the device

lib/appium_lib_core/common/base/http_default.rb

+31
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,37 @@ class Default < Selenium::WebDriver::Remote::Http::Default
1212
"appium/ruby_lib_core/#{VERSION} (#{::Selenium::WebDriver::Remote::Http::Common::DEFAULT_HEADERS['User-Agent']})"
1313
}.freeze
1414

15+
# Update `server_url` to.
16+
# Set `@http` as nil to re-create http client for the server_url
17+
# @private
18+
#
19+
# @param [string] scheme: A scheme to update server_url to
20+
# @param [string] host: A host to update server_url to
21+
# @param [string|integer] port: A port number to update server_url to
22+
# @param [string] path: A path to update server_url to
23+
#
24+
# @return [URI] An instance of URI updated to. Returns default `server_url` if some of arguments are `nil`
25+
def update_sending_request_to(scheme:, host:, port:, path:)
26+
return @server_url unless validate_url_param(scheme, host, port, path)
27+
28+
Logger.debug("[experimental] This feature, #{__method__}, is an experimental")
29+
30+
# Add / if `path` does not have it
31+
path = path.start_with?('/') ? path : "/#{path}"
32+
path = path.end_with?('/') ? path : "#{path}/"
33+
34+
@http = nil
35+
@server_url = URI.parse "#{scheme}://#{host}:#{port}#{path}"
36+
end
37+
38+
private
39+
40+
def validate_url_param(scheme, host, port, path)
41+
!(scheme.nil? || host.nil? || port.nil? || path.nil?)
42+
end
43+
44+
public
45+
1546
# override to use default header
1647
# https://github.com/SeleniumHQ/selenium/blob/master/rb/lib/selenium/webdriver/remote/http/common.rb#L46
1748
def call(verb, url, command_hash)

lib/appium_lib_core/driver.rb

+19
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,16 @@ class Driver
7575
# @return [Appium::Core::Base::Driver]
7676
attr_reader :driver
7777

78+
# [Experimental feature]
79+
# Enable an experimental feature updating Appium HTTP client following `directConnectProtocol`, `directConnectHost`,
80+
# `directConnectPort` and `directConnectPath` after session creation if the server returns them as a part of the response
81+
# capability in _create session_.
82+
#
83+
# Ignore them if this parameter is `false`. Defaults to false.
84+
#
85+
# @return [Bool]
86+
attr_reader :direct_connect
87+
7888
# Creates a new global driver and extend particular methods to `target`
7989
# @param [Class] target Extend particular methods to this target.
8090
# @param [Hash] opts A options include capabilities for the Appium Server and for the client.
@@ -244,6 +254,13 @@ def start_driver(server_url: nil,
244254
url: @custom_url,
245255
listener: @listener)
246256

257+
if @direct_connect
258+
@driver.update_sending_request_to(protocol: @driver.capabilities['directConnectProtocol'],
259+
host: @driver.capabilities['directConnectHost'],
260+
port: @driver.capabilities['directConnectPort'],
261+
path: @driver.capabilities['directConnectPath'])
262+
end
263+
247264
# export session
248265
write_session_id(@driver.session_id, @export_session_path) if @export_session
249266
rescue Errno::ECONNREFUSED
@@ -454,6 +471,8 @@ def set_appium_lib_specific_values(appium_lib_opts)
454471
@export_session = appium_lib_opts.fetch :export_session, false
455472
@export_session_path = appium_lib_opts.fetch :export_session_path, default_tmp_appium_lib_session
456473

474+
@direct_connect = appium_lib_opts.fetch :direct_access, false
475+
457476
@port = appium_lib_opts.fetch :port, DEFAULT_APPIUM_PORT
458477

459478
# timeout and interval used in ::Appium::Comm.wait/wait_true

test/test_helper.rb

+55
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,10 @@ def self.android(activity_name = nil)
5959
new.android(activity_name)
6060
end
6161

62+
def self.android_direct
63+
new.android_direct
64+
end
65+
6266
def self.android_web
6367
new.android_web
6468
end
@@ -132,6 +136,19 @@ def android(activity_name = nil)
132136
}
133137
end
134138

139+
def android_direct
140+
{
141+
desired_capabilities: android[:desired_capabilities],
142+
appium_lib: {
143+
export_session: true,
144+
wait: 30,
145+
wait_timeout: 20,
146+
wait_interval: 1,
147+
direct_access: true
148+
}
149+
}
150+
end
151+
135152
def android_web
136153
{
137154
caps: {
@@ -287,6 +304,44 @@ def android_mock_create_session_w3c
287304
driver
288305
end
289306

307+
def android_mock_create_session_w3c_direct(core)
308+
response = {
309+
value: {
310+
sessionId: '1234567890',
311+
capabilities: {
312+
platformName: :android,
313+
automationName: ENV['AUTOMATION_NAME_DROID'] || 'uiautomator2',
314+
app: 'test/functional/app/api.apk.zip',
315+
platformVersion: '7.1.1',
316+
deviceName: 'Android Emulator',
317+
appPackage: 'io.appium.android.apis',
318+
appActivity: 'io.appium.android.apis.ApiDemos',
319+
someCapability: 'some_capability',
320+
unicodeKeyboard: true,
321+
resetKeyboard: true,
322+
directConnectProtocol: 'http',
323+
directConnectHost: 'localhost',
324+
directConnectPort: '8888',
325+
directConnectPath: '/wd/hub'
326+
}
327+
}
328+
}.to_json
329+
330+
stub_request(:post, 'http://127.0.0.1:4723/wd/hub/session')
331+
.to_return(headers: HEADER, status: 200, body: response)
332+
333+
stub_request(:post, 'http://localhost:8888/wd/hub/session/1234567890/timeouts')
334+
.with(body: { implicit: 30_000 }.to_json)
335+
.to_return(headers: HEADER, status: 200, body: { value: nil }.to_json)
336+
337+
driver = core.start_driver
338+
339+
assert_requested(:post, 'http://127.0.0.1:4723/wd/hub/session', times: 1)
340+
assert_requested(:post, 'http://localhost:8888/wd/hub/session/1234567890/timeouts',
341+
body: { implicit: 30_000 }.to_json, times: 1)
342+
driver
343+
end
344+
290345
def ios_mock_create_session
291346
response = {
292347
status: 0, # To make bridge.dialect == :oss

test/unit/driver_test.rb

+20
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,26 @@ def test_default_timeout_for_http_client
7979

8080
assert_equal 999_999, @core.http_client.open_timeout
8181
assert_equal 999_999, @core.http_client.read_timeout
82+
uri = @driver.send(:bridge).http.send(:server_url)
83+
assert !@core.direct_connect
84+
assert_equal 'http', uri.scheme
85+
assert_equal '127.0.0.1', uri.host
86+
assert_equal 4723, uri.port
87+
assert_equal '/wd/hub/', uri.path
88+
end
89+
90+
def test_default_timeout_for_http_client_with_direct
91+
core = ::Appium::Core.for(Caps.android_direct)
92+
driver = android_mock_create_session_w3c_direct(core)
93+
94+
assert_equal 999_999, driver.send(:bridge).http.open_timeout
95+
assert_equal 999_999, driver.send(:bridge).http.read_timeout
96+
uri = driver.send(:bridge).http.send(:server_url)
97+
assert core.direct_connect
98+
assert_equal 'http', uri.scheme
99+
assert_equal 'localhost', uri.host
100+
assert_equal 8888, uri.port
101+
assert_equal '/wd/hub/', uri.path
82102
end
83103

84104
# https://www.w3.org/TR/webdriver1/

0 commit comments

Comments
 (0)