Skip to content

Commit a1df581

Browse files
committed
adding back old method of doing http requests for non keep-alive connections
(I tried setting Connection:'close' that *should* work, but ghostdriver wasn't honoring it and didn't respond with the right header) Fixes Issue #6707 #6706
1 parent 79f5c19 commit a1df581

File tree

1 file changed

+145
-30
lines changed

1 file changed

+145
-30
lines changed

py/selenium/webdriver/remote/remote_connection.py

Lines changed: 145 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
# Copyright 2008-2009 WebDriver committers
2-
# Copyright 2008-2009 Google Inc.
3-
# Copyright 2013 BrowserStack
1+
# Copyright 2008-2013 Software Freedom Conservancy
42
#
53
# Licensed under the Apache License, Version 2.0 (the "License");
64
# you may not use this file except in compliance with the License.
@@ -21,17 +19,11 @@
2119

2220
try:
2321
import http.client as httplib
24-
except ImportError:
25-
import httplib as httplib
26-
27-
try:
2822
from urllib import request as url_request
29-
except ImportError:
30-
import urllib2 as url_request
31-
32-
try:
3323
from urllib import parse
34-
except ImportError:
24+
except ImportError: # above is available in py3+, below is py2.7
25+
import httplib as httplib
26+
import urllib2 as url_request
3527
import urlparse as parse
3628

3729
from .command import Command
@@ -41,6 +33,98 @@
4133
LOGGER = logging.getLogger(__name__)
4234

4335

36+
class Request(url_request.Request):
37+
"""
38+
Extends the url_request.Request to support all HTTP request types.
39+
"""
40+
41+
def __init__(self, url, data=None, method=None):
42+
"""
43+
Initialise a new HTTP request.
44+
45+
:Args:
46+
- url - String for the URL to send the request to.
47+
- data - Data to send with the request.
48+
"""
49+
if method is None:
50+
method = data is not None and 'POST' or 'GET'
51+
elif method != 'POST' and method != 'PUT':
52+
data = None
53+
self._method = method
54+
url_request.Request.__init__(self, url, data=data)
55+
56+
def get_method(self):
57+
"""
58+
Returns the HTTP method used by this request.
59+
"""
60+
return self._method
61+
62+
63+
class Response(object):
64+
"""
65+
Represents an HTTP response.
66+
"""
67+
68+
def __init__(self, fp, code, headers, url):
69+
"""
70+
Initialise a new Response.
71+
72+
:Args:
73+
- fp - The response body file object.
74+
- code - The HTTP status code returned by the server.
75+
- headers - A dictionary of headers returned by the server.
76+
- url - URL of the retrieved resource represented by this Response.
77+
"""
78+
self.fp = fp
79+
self.read = fp.read
80+
self.code = code
81+
self.headers = headers
82+
self.url = url
83+
84+
def close(self):
85+
"""
86+
Close the response body file object.
87+
"""
88+
self.read = None
89+
self.fp = None
90+
91+
def info(self):
92+
"""
93+
Returns the response headers.
94+
"""
95+
return self.headers
96+
97+
def geturl(self):
98+
"""
99+
Returns the URL for the resource returned in this response.
100+
"""
101+
return self.url
102+
103+
104+
class HttpErrorHandler(url_request.HTTPDefaultErrorHandler):
105+
"""
106+
A custom HTTP error handler.
107+
108+
Used to return Response objects instead of raising an HTTPError exception.
109+
"""
110+
111+
def http_error_default(self, req, fp, code, msg, headers):
112+
"""
113+
Default HTTP error handler.
114+
115+
:Args:
116+
- req - The original Request object.
117+
- fp - The response body file object.
118+
- code - The HTTP status code returned by the server.
119+
- msg - The HTTP status message returned by the server.
120+
- headers - The response headers.
121+
122+
:Returns:
123+
A new Response object.
124+
"""
125+
return Response(fp, code, headers, req.get_full_url())
126+
127+
44128
class RemoteConnection(object):
45129
"""
46130
A connection with the Remote WebDriver server.
@@ -71,7 +155,8 @@ def __init__(self, remote_server_addr, keep_alive=False):
71155
LOGGER.info('Could not get IP address for host: %s' % parsed_url.hostname)
72156

73157
self._url = remote_server_addr
74-
self._conn = httplib.HTTPConnection(str(addr), str(parsed_url.port))
158+
if keep_alive:
159+
self._conn = httplib.HTTPConnection(str(addr), str(parsed_url.port))
75160
self._commands = {
76161
Command.STATUS: ('GET', '/status'),
77162
Command.NEW_SESSION: ('POST', '/session'),
@@ -278,24 +363,54 @@ def _request(self, url, data=None, method=None):
278363
LOGGER.debug('%s %s %s' % (method, url, data))
279364

280365
parsed_url = parse.urlparse(url)
281-
headers = {method: parsed_url.path,
282-
"User-Agent": "Python http auth",
283-
"Content-type": "application/json;charset=\"UTF-8\"",
284-
"Accept": "application/json"}
366+
285367
if self.keep_alive:
286-
headers['Connection'] = 'keep-alive'
287-
288-
# for basic auth
289-
if parsed_url.username:
290-
auth = base64.standard_b64encode('%s:%s' % (parsed_url.username, parsed_url.password)).replace('\n', '')
291-
# Authorization header
292-
headers["Authorization"] = "Basic %s" % auth
293-
294-
self._conn.request(method, parsed_url.path, data, headers)
295-
resp = self._conn.getresponse()
296-
statuscode = resp.status
297-
statusmessage = resp.msg
298-
LOGGER.debug('%s %s' % (statuscode, statusmessage))
368+
headers = {"Connection": 'keep-alive', method: parsed_url.path,
369+
"User-Agent": "Python http auth",
370+
"Content-type": "application/json;charset=\"UTF-8\"",
371+
"Accept": "application/json"}
372+
if parsed_url.username:
373+
auth = base64.standard_b64encode('%s:%s' %
374+
(parsed_url.username, parsed_url.password)).replace('\n', '')
375+
headers["Authorization"] = "Basic %s" % auth
376+
self._conn.request(method, parsed_url.path, data, headers)
377+
resp = self._conn.getresponse()
378+
statuscode = resp.status
379+
else:
380+
password_manager = None
381+
if parsed_url.username:
382+
netloc = parsed_url.hostname
383+
if parsed_url.port:
384+
netloc += ":%s" % parsed_url.port
385+
cleaned_url = parse.urlunparse((parsed_url.scheme,
386+
netloc,
387+
parsed_url.path,
388+
parsed_url.params,
389+
parsed_url.query,
390+
parsed_url.fragment))
391+
password_manager = url_request.HTTPPasswordMgrWithDefaultRealm()
392+
password_manager.add_password(None,
393+
"%s://%s" % (parsed_url.scheme, netloc),
394+
parsed_url.username,
395+
parsed_url.password)
396+
request = Request(cleaned_url, data=data.encode('utf-8'), method=method)
397+
else:
398+
request = Request(url, data=data.encode('utf-8'), method=method)
399+
400+
request.add_header('Accept', 'application/json')
401+
request.add_header('Content-Type', 'application/json;charset=UTF-8')
402+
403+
if password_manager:
404+
opener = url_request.build_opener(url_request.HTTPRedirectHandler(),
405+
HttpErrorHandler(),
406+
url_request.HTTPBasicAuthHandler(password_manager))
407+
else:
408+
opener = url_request.build_opener(url_request.HTTPRedirectHandler(),
409+
HttpErrorHandler())
410+
resp = opener.open(request)
411+
statuscode = resp.code
412+
resp.getheader = lambda x: resp.headers.getheader(x)
413+
299414
data = resp.read()
300415
try:
301416
if 399 < statuscode < 500:

0 commit comments

Comments
 (0)