Skip to content

Commit

Permalink
client, transport: implement ability to send raw requests
Browse files Browse the repository at this point in the history
Introduce a new method to allow tests to explicitly inspect the
status code and complete body of HTTP responses received when issuing
commands. In addition, extend the behavior of the higher-order
`send_response` method with assertions for HTTP response headers.
  • Loading branch information
jugglinmike authored and andreastt committed Feb 3, 2017
1 parent de9b46e commit 16d1463
Show file tree
Hide file tree
Showing 2 changed files with 88 additions and 28 deletions.
47 changes: 42 additions & 5 deletions webdriver/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,13 +215,13 @@ def start(self):
#body["capabilities"] = caps
body = caps

resp = self.transport.send("POST", "session", body=body)
self.session_id = resp["sessionId"]
response = self.transport.send("POST", "session", body=body)
self.session_id = response.body["value"]["sessionId"]

if self.extension_cls:
self.extension = self.extension_cls(self)

return resp["value"]
return response.body["value"]

def end(self):
if self.session_id is None:
Expand All @@ -236,11 +236,48 @@ def end(self):
self.find = None
self.extension = None

def send_raw_command(self, method, url, body=None, headers=None):
"""Send a command to the remote end.
:param method: HTTP method to use in request
:param url: "command part" of the requests URL path
:param body: body of the HTTP request
:param headers: Additional headers to include in the HTTP request
:return: an instance of wdclient.Response describing the HTTP response
received from the remote end
"""
url = urlparse.urljoin("session/%s/" % self.session_id, url)
return self.transport.send(method, url, body, headers)

def send_command(self, method, url, body=None, key=None):
"""Send a command to the remote end and validate its success.
:param method: HTTP method to use in request
:param url: "command part" of the requests URL path
:param body: body of the HTTP request
:param key: (deprecated) when specified, this string value will be used
to de-reference the HTTP response body following JSON parsing
:return: None if the HTTP response body was empty, otherwise the
result of parsing the HTTP response body as JSON
"""

if self.session_id is None:
raise error.SessionNotCreatedException()
url = urlparse.urljoin("session/%s/" % self.session_id, url)
return self.transport.send(method, url, body, key=key)

response = self.send_raw_command(method, url, body)

if response.status != 200:
cls = error.get(response.body["value"].get("error"))
raise cls(response.body["value"].get("message"))

if key is not None:
response.body = response.body[key]
if not response.body:
response.body = None

return response.body

@property
@command
Expand Down
69 changes: 46 additions & 23 deletions webdriver/transport.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,46 @@
import json
import urlparse

import error


HTTP_TIMEOUT = 5

class Response(object):
"""Describes an HTTP response received from a remote en"Describes an HTTP
response received from a remote end whose body has been read and parsed as
appropriate."""
def __init__(self, status, body):
self.status = status
self.body = body

def __repr__(self):
return "wdclient.Response(status=%d, body=%s)" % (self.status, self.body)

@staticmethod
def from_http_response(http_response):
status = http_response.status
body = http_response.read()

# SpecID: dfn-send-a-response
#
# > 3. Set the response's header with name and value with the following
# > values:
# >
# > "Content-Type"
# > "application/json; charset=utf-8"
# > "cache-control"
# > "no-cache"
assert http_response.getheader("Content-Type") == "application/json; charset=utf-8"
assert http_response.getheader("Cache-Control") == "no-cache"

if body:
body = json.loads(body)

# SpecID: dfn-send-a-response
#
# > 4. If data is not null, let response's body be a JSON Object
# with a key `value` set to the JSON Serialization of data.
assert "value" in body

return Response(status, body)

class HTTPWireProtocol(object):
"""Transports messages (commands and responses) over the WebDriver
Expand All @@ -33,15 +68,16 @@ def __init__(self, host, port, url_prefix="/", timeout=HTTP_TIMEOUT):
def url(self, suffix):
return urlparse.urljoin(self.path_prefix, suffix)

def send(self, method, url, body=None, headers=None, key=None):
def send(self, method, url, body=None, headers=None):
"""Send a command to the remote.
:param method: "POST" or "GET".
:param url: "command part" of the requests URL path
:param body: Body of the request. Defaults to an empty dictionary
if ``method`` is "POST".
:param headers: Additional headers to include in the request.
:param key: Extract this key from the dictionary returned from
the remote.
:return: an instance of wdclient.Response describing the HTTP response
received from the remote end.
"""

if body is None and method == "POST":
Expand All @@ -62,22 +98,9 @@ def send(self, method, url, body=None, headers=None, key=None):
self.host, self.port, strict=True, timeout=self._timeout)
conn.request(method, url, body, headers)

resp = conn.getresponse()
resp_body = resp.read()
conn.close()

try:
data = json.loads(resp_body)
except:
raise IOError("Could not parse response body as JSON: '%s'" % resp_body)

if resp.status != 200:
cls = error.get(data.get("error"))
raise cls(data.get("message"))

if key is not None:
data = data[key]
if not data:
data = None
response = Response.from_http_response(conn.getresponse())
finally:
conn.close()

return data
return response

0 comments on commit 16d1463

Please sign in to comment.