"Fossies" - the Fresh Open Source Software Archive 
Member "selenium-selenium-4.8.1/py/selenium/webdriver/remote/remote_connection.py" (17 Feb 2023, 16829 Bytes) of package /linux/www/selenium-selenium-4.8.1.tar.gz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style:
standard) with prefixed line numbers.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "remote_connection.py" see the
Fossies "Dox" file reference documentation and the last
Fossies "Diffs" side-by-side code changes report:
4.8.0_vs_4.8.1.
1 # Licensed to the Software Freedom Conservancy (SFC) under one
2 # or more contributor license agreements. See the NOTICE file
3 # distributed with this work for additional information
4 # regarding copyright ownership. The SFC licenses this file
5 # to you under the Apache License, Version 2.0 (the
6 # "License"); you may not use this file except in compliance
7 # with the License. You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing,
12 # software distributed under the License is distributed on an
13 # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14 # KIND, either express or implied. See the License for the
15 # specific language governing permissions and limitations
16 # under the License.
17
18 import logging
19 import os
20 import platform
21 import socket
22 import string
23 from base64 import b64encode
24 from urllib import parse
25
26 import certifi
27 import urllib3
28
29 from selenium import __version__
30
31 from . import utils
32 from .command import Command
33 from .errorhandler import ErrorCode
34
35 LOGGER = logging.getLogger(__name__)
36
37
38 class RemoteConnection:
39 """A connection with the Remote WebDriver server.
40
41 Communicates with the server using the WebDriver wire protocol:
42 https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol
43 """
44
45 browser_name = None
46 _timeout = socket._GLOBAL_DEFAULT_TIMEOUT
47 _ca_certs = certifi.where()
48
49 @classmethod
50 def get_timeout(cls):
51 """
52 :Returns:
53 Timeout value in seconds for all http requests made to the Remote Connection
54 """
55 return None if cls._timeout == socket._GLOBAL_DEFAULT_TIMEOUT else cls._timeout
56
57 @classmethod
58 def set_timeout(cls, timeout):
59 """Override the default timeout.
60
61 :Args:
62 - timeout - timeout value for http requests in seconds
63 """
64 cls._timeout = timeout
65
66 @classmethod
67 def reset_timeout(cls):
68 """Reset the http request timeout to socket._GLOBAL_DEFAULT_TIMEOUT."""
69 cls._timeout = socket._GLOBAL_DEFAULT_TIMEOUT
70
71 @classmethod
72 def get_certificate_bundle_path(cls):
73 """
74 :Returns:
75 Paths of the .pem encoded certificate to verify connection to command executor
76 """
77 return cls._ca_certs
78
79 @classmethod
80 def set_certificate_bundle_path(cls, path):
81 """Set the path to the certificate bundle to verify connection to
82 command executor. Can also be set to None to disable certificate
83 validation.
84
85 :Args:
86 - path - path of a .pem encoded certificate chain.
87 """
88 cls._ca_certs = path
89
90 @classmethod
91 def get_remote_connection_headers(cls, parsed_url, keep_alive=False):
92 """Get headers for remote request.
93
94 :Args:
95 - parsed_url - The parsed url
96 - keep_alive (Boolean) - Is this a keep-alive connection (default: False)
97 """
98
99 system = platform.system().lower()
100 if system == "darwin":
101 system = "mac"
102
103 headers = {
104 "Accept": "application/json",
105 "Content-Type": "application/json;charset=UTF-8",
106 "User-Agent": f"selenium/{__version__} (python {system})",
107 }
108
109 if parsed_url.username:
110 base64string = b64encode(f"{parsed_url.username}:{parsed_url.password}".encode())
111 headers.update({"Authorization": f"Basic {base64string.decode()}"})
112
113 if keep_alive:
114 headers.update({"Connection": "keep-alive"})
115
116 return headers
117
118 def _get_proxy_url(self):
119 if self._url.startswith("https://"):
120 return os.environ.get("https_proxy", os.environ.get("HTTPS_PROXY"))
121 if self._url.startswith("http://"):
122 return os.environ.get("http_proxy", os.environ.get("HTTP_PROXY"))
123
124 def _identify_http_proxy_auth(self):
125 url = self._proxy_url
126 url = url[url.find(":") + 3 :]
127 return "@" in url and len(url[: url.find("@")]) > 0
128
129 def _separate_http_proxy_auth(self):
130 url = self._proxy_url
131 protocol = url[: url.find(":") + 3]
132 no_protocol = url[len(protocol) :]
133 auth = no_protocol[: no_protocol.find("@")]
134 proxy_without_auth = protocol + no_protocol[len(auth) + 1 :]
135 return proxy_without_auth, auth
136
137 def _get_connection_manager(self):
138 pool_manager_init_args = {"timeout": self.get_timeout()}
139 if self._ca_certs:
140 pool_manager_init_args["cert_reqs"] = "CERT_REQUIRED"
141 pool_manager_init_args["ca_certs"] = self._ca_certs
142
143 if self._proxy_url:
144 if self._proxy_url.lower().startswith("sock"):
145 from urllib3.contrib.socks import SOCKSProxyManager
146
147 return SOCKSProxyManager(self._proxy_url, **pool_manager_init_args)
148 if self._identify_http_proxy_auth():
149 self._proxy_url, self._basic_proxy_auth = self._separate_http_proxy_auth()
150 pool_manager_init_args["proxy_headers"] = urllib3.make_headers(proxy_basic_auth=self._basic_proxy_auth)
151 return urllib3.ProxyManager(self._proxy_url, **pool_manager_init_args)
152
153 return urllib3.PoolManager(**pool_manager_init_args)
154
155 def __init__(self, remote_server_addr: str, keep_alive: bool = False, ignore_proxy: bool = False):
156 self.keep_alive = keep_alive
157 self._url = remote_server_addr
158
159 # Env var NO_PROXY will override this part of the code
160 _no_proxy = os.environ.get("no_proxy", os.environ.get("NO_PROXY"))
161 if _no_proxy:
162 for npu in _no_proxy.split(","):
163 npu = npu.strip()
164 if npu == "*":
165 ignore_proxy = True
166 break
167 n_url = parse.urlparse(npu)
168 remote_add = parse.urlparse(self._url)
169 if n_url.netloc:
170 if remote_add.netloc == n_url.netloc:
171 ignore_proxy = True
172 break
173 else:
174 if n_url.path in remote_add.netloc:
175 ignore_proxy = True
176 break
177
178 self._proxy_url = self._get_proxy_url() if not ignore_proxy else None
179 if keep_alive:
180 self._conn = self._get_connection_manager()
181
182 self._commands = {
183 Command.NEW_SESSION: ("POST", "/session"),
184 Command.QUIT: ("DELETE", "/session/$sessionId"),
185 Command.W3C_GET_CURRENT_WINDOW_HANDLE: ("GET", "/session/$sessionId/window"),
186 Command.W3C_GET_WINDOW_HANDLES: ("GET", "/session/$sessionId/window/handles"),
187 Command.GET: ("POST", "/session/$sessionId/url"),
188 Command.GO_FORWARD: ("POST", "/session/$sessionId/forward"),
189 Command.GO_BACK: ("POST", "/session/$sessionId/back"),
190 Command.REFRESH: ("POST", "/session/$sessionId/refresh"),
191 Command.W3C_EXECUTE_SCRIPT: ("POST", "/session/$sessionId/execute/sync"),
192 Command.W3C_EXECUTE_SCRIPT_ASYNC: ("POST", "/session/$sessionId/execute/async"),
193 Command.GET_CURRENT_URL: ("GET", "/session/$sessionId/url"),
194 Command.GET_TITLE: ("GET", "/session/$sessionId/title"),
195 Command.GET_PAGE_SOURCE: ("GET", "/session/$sessionId/source"),
196 Command.SCREENSHOT: ("GET", "/session/$sessionId/screenshot"),
197 Command.ELEMENT_SCREENSHOT: ("GET", "/session/$sessionId/element/$id/screenshot"),
198 Command.FIND_ELEMENT: ("POST", "/session/$sessionId/element"),
199 Command.FIND_ELEMENTS: ("POST", "/session/$sessionId/elements"),
200 Command.W3C_GET_ACTIVE_ELEMENT: ("GET", "/session/$sessionId/element/active"),
201 Command.FIND_CHILD_ELEMENT: ("POST", "/session/$sessionId/element/$id/element"),
202 Command.FIND_CHILD_ELEMENTS: ("POST", "/session/$sessionId/element/$id/elements"),
203 Command.CLICK_ELEMENT: ("POST", "/session/$sessionId/element/$id/click"),
204 Command.CLEAR_ELEMENT: ("POST", "/session/$sessionId/element/$id/clear"),
205 Command.GET_ELEMENT_TEXT: ("GET", "/session/$sessionId/element/$id/text"),
206 Command.SEND_KEYS_TO_ELEMENT: ("POST", "/session/$sessionId/element/$id/value"),
207 Command.UPLOAD_FILE: ("POST", "/session/$sessionId/se/file"),
208 Command.GET_ELEMENT_TAG_NAME: ("GET", "/session/$sessionId/element/$id/name"),
209 Command.IS_ELEMENT_SELECTED: ("GET", "/session/$sessionId/element/$id/selected"),
210 Command.IS_ELEMENT_ENABLED: ("GET", "/session/$sessionId/element/$id/enabled"),
211 Command.GET_ELEMENT_RECT: ("GET", "/session/$sessionId/element/$id/rect"),
212 Command.GET_ELEMENT_ATTRIBUTE: ("GET", "/session/$sessionId/element/$id/attribute/$name"),
213 Command.GET_ELEMENT_PROPERTY: ("GET", "/session/$sessionId/element/$id/property/$name"),
214 Command.GET_ELEMENT_ARIA_ROLE: ("GET", "/session/$sessionId/element/$id/computedrole"),
215 Command.GET_ELEMENT_ARIA_LABEL: ("GET", "/session/$sessionId/element/$id/computedlabel"),
216 Command.GET_SHADOW_ROOT: ("GET", "/session/$sessionId/element/$id/shadow"),
217 Command.FIND_ELEMENT_FROM_SHADOW_ROOT: ("POST", "/session/$sessionId/shadow/$shadowId/element"),
218 Command.FIND_ELEMENTS_FROM_SHADOW_ROOT: ("POST", "/session/$sessionId/shadow/$shadowId/elements"),
219 Command.GET_ALL_COOKIES: ("GET", "/session/$sessionId/cookie"),
220 Command.ADD_COOKIE: ("POST", "/session/$sessionId/cookie"),
221 Command.GET_COOKIE: ("GET", "/session/$sessionId/cookie/$name"),
222 Command.DELETE_ALL_COOKIES: ("DELETE", "/session/$sessionId/cookie"),
223 Command.DELETE_COOKIE: ("DELETE", "/session/$sessionId/cookie/$name"),
224 Command.SWITCH_TO_FRAME: ("POST", "/session/$sessionId/frame"),
225 Command.SWITCH_TO_PARENT_FRAME: ("POST", "/session/$sessionId/frame/parent"),
226 Command.SWITCH_TO_WINDOW: ("POST", "/session/$sessionId/window"),
227 Command.NEW_WINDOW: ("POST", "/session/$sessionId/window/new"),
228 Command.CLOSE: ("DELETE", "/session/$sessionId/window"),
229 Command.GET_ELEMENT_VALUE_OF_CSS_PROPERTY: ("GET", "/session/$sessionId/element/$id/css/$propertyName"),
230 Command.EXECUTE_ASYNC_SCRIPT: ("POST", "/session/$sessionId/execute_async"),
231 Command.SET_TIMEOUTS: ("POST", "/session/$sessionId/timeouts"),
232 Command.GET_TIMEOUTS: ("GET", "/session/$sessionId/timeouts"),
233 Command.W3C_DISMISS_ALERT: ("POST", "/session/$sessionId/alert/dismiss"),
234 Command.W3C_ACCEPT_ALERT: ("POST", "/session/$sessionId/alert/accept"),
235 Command.W3C_SET_ALERT_VALUE: ("POST", "/session/$sessionId/alert/text"),
236 Command.W3C_GET_ALERT_TEXT: ("GET", "/session/$sessionId/alert/text"),
237 Command.W3C_ACTIONS: ("POST", "/session/$sessionId/actions"),
238 Command.W3C_CLEAR_ACTIONS: ("DELETE", "/session/$sessionId/actions"),
239 Command.SET_WINDOW_RECT: ("POST", "/session/$sessionId/window/rect"),
240 Command.GET_WINDOW_RECT: ("GET", "/session/$sessionId/window/rect"),
241 Command.W3C_MAXIMIZE_WINDOW: ("POST", "/session/$sessionId/window/maximize"),
242 Command.SET_SCREEN_ORIENTATION: ("POST", "/session/$sessionId/orientation"),
243 Command.GET_SCREEN_ORIENTATION: ("GET", "/session/$sessionId/orientation"),
244 Command.GET_NETWORK_CONNECTION: ("GET", "/session/$sessionId/network_connection"),
245 Command.SET_NETWORK_CONNECTION: ("POST", "/session/$sessionId/network_connection"),
246 Command.GET_LOG: ("POST", "/session/$sessionId/se/log"),
247 Command.GET_AVAILABLE_LOG_TYPES: ("GET", "/session/$sessionId/se/log/types"),
248 Command.CURRENT_CONTEXT_HANDLE: ("GET", "/session/$sessionId/context"),
249 Command.CONTEXT_HANDLES: ("GET", "/session/$sessionId/contexts"),
250 Command.SWITCH_TO_CONTEXT: ("POST", "/session/$sessionId/context"),
251 Command.FULLSCREEN_WINDOW: ("POST", "/session/$sessionId/window/fullscreen"),
252 Command.MINIMIZE_WINDOW: ("POST", "/session/$sessionId/window/minimize"),
253 Command.PRINT_PAGE: ("POST", "/session/$sessionId/print"),
254 Command.ADD_VIRTUAL_AUTHENTICATOR: ("POST", "/session/$sessionId/webauthn/authenticator"),
255 Command.REMOVE_VIRTUAL_AUTHENTICATOR: (
256 "DELETE",
257 "/session/$sessionId/webauthn/authenticator/$authenticatorId",
258 ),
259 Command.ADD_CREDENTIAL: ("POST", "/session/$sessionId/webauthn/authenticator/$authenticatorId/credential"),
260 Command.GET_CREDENTIALS: ("GET", "/session/$sessionId/webauthn/authenticator/$authenticatorId/credentials"),
261 Command.REMOVE_CREDENTIAL: (
262 "DELETE",
263 "/session/$sessionId/webauthn/authenticator/$authenticatorId/credentials/$credentialId",
264 ),
265 Command.REMOVE_ALL_CREDENTIALS: (
266 "DELETE",
267 "/session/$sessionId/webauthn/authenticator/$authenticatorId/credentials",
268 ),
269 Command.SET_USER_VERIFIED: ("POST", "/session/$sessionId/webauthn/authenticator/$authenticatorId/uv"),
270 }
271
272 def execute(self, command, params):
273 """Send a command to the remote server.
274
275 Any path substitutions required for the URL mapped to the command should be
276 included in the command parameters.
277
278 :Args:
279 - command - A string specifying the command to execute.
280 - params - A dictionary of named parameters to send with the command as
281 its JSON payload.
282 """
283 command_info = self._commands[command]
284 assert command_info is not None, f"Unrecognised command {command}"
285 path = string.Template(command_info[1]).substitute(params)
286 if isinstance(params, dict) and "sessionId" in params:
287 del params["sessionId"]
288 data = utils.dump_json(params)
289 url = f"{self._url}{path}"
290 return self._request(command_info[0], url, body=data)
291
292 def _request(self, method, url, body=None):
293 """Send an HTTP request to the remote server.
294
295 :Args:
296 - method - A string for the HTTP method to send the request with.
297 - url - A string for the URL to send the request to.
298 - body - A string for request body. Ignored unless method is POST or PUT.
299
300 :Returns:
301 A dictionary with the server's parsed JSON response.
302 """
303 LOGGER.debug(f"{method} {url} {body}")
304 parsed_url = parse.urlparse(url)
305 headers = self.get_remote_connection_headers(parsed_url, self.keep_alive)
306 response = None
307 if body and method not in ("POST", "PUT"):
308 body = None
309
310 if self.keep_alive:
311 response = self._conn.request(method, url, body=body, headers=headers)
312 statuscode = response.status
313 else:
314 conn = self._get_connection_manager()
315 with conn as http:
316 response = http.request(method, url, body=body, headers=headers)
317 statuscode = response.status
318 data = response.data.decode("UTF-8")
319 LOGGER.debug(f"Remote response: status={response.status} | data={data} | headers={response.headers}")
320 try:
321 if 300 <= statuscode < 304:
322 return self._request("GET", response.headers.get("location", None))
323 if 399 < statuscode <= 500:
324 return {"status": statuscode, "value": data}
325 content_type = []
326 if response.headers.get("Content-Type", None):
327 content_type = response.headers.get("Content-Type", None).split(";")
328 if not any([x.startswith("image/png") for x in content_type]):
329 try:
330 data = utils.load_json(data.strip())
331 except ValueError:
332 if 199 < statuscode < 300:
333 status = ErrorCode.SUCCESS
334 else:
335 status = ErrorCode.UNKNOWN_ERROR
336 return {"status": status, "value": data.strip()}
337
338 # Some drivers incorrectly return a response
339 # with no 'value' field when they should return null.
340 if "value" not in data:
341 data["value"] = None
342 return data
343 data = {"status": 0, "value": data}
344 return data
345 finally:
346 LOGGER.debug("Finished Request")
347 response.close()
348
349 def close(self):
350 """Clean up resources when finished with the remote_connection."""
351 if hasattr(self, "_conn"):
352 self._conn.clear()