Skip to content

Commit 1d3a709

Browse files
committed
Fix handling of BadStatusLine.
Fixes issue #250. Review in https://codereview.appspot.com/7529045/.
1 parent fdfd04a commit 1d3a709

File tree

4 files changed

+120
-3
lines changed

4 files changed

+120
-3
lines changed

python2/httplib2/__init__.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1246,7 +1246,10 @@ def clear_credentials(self):
12461246
self.authorizations = []
12471247

12481248
def _conn_request(self, conn, request_uri, method, body, headers):
1249-
for i in range(RETRIES):
1249+
i = 0
1250+
seen_bad_status_line = False
1251+
while i < RETRIES:
1252+
i += 1
12501253
try:
12511254
if hasattr(conn, 'sock') and conn.sock is None:
12521255
conn.connect()
@@ -1284,6 +1287,19 @@ def _conn_request(self, conn, request_uri, method, body, headers):
12841287
continue
12851288
try:
12861289
response = conn.getresponse()
1290+
except httplib.BadStatusLine:
1291+
# If we get a BadStatusLine on the first try then that means
1292+
# the connection just went stale, so retry regardless of the
1293+
# number of RETRIES set.
1294+
if not seen_bad_status_line and i == 1:
1295+
i = 0
1296+
seen_bad_status_line = True
1297+
conn.close()
1298+
conn.connect()
1299+
continue
1300+
else:
1301+
conn.close()
1302+
raise
12871303
except (socket.error, httplib.HTTPException):
12881304
if i < RETRIES-1:
12891305
conn.close()

python2/httplib2test.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,36 @@ def request(self, method, request_uri, body, headers):
144144
def getresponse(self):
145145
return _MyResponse("the body", status="200")
146146

147+
class _MyHTTPBadStatusConnection(object):
148+
"Mock of httplib.HTTPConnection that raises BadStatusLine."
149+
150+
num_calls = 0
151+
152+
def __init__(self, host, port=None, key_file=None, cert_file=None,
153+
strict=None, timeout=None, proxy_info=None):
154+
self.host = host
155+
self.port = port
156+
self.timeout = timeout
157+
self.log = ""
158+
self.sock = None
159+
_MyHTTPBadStatusConnection.num_calls = 0
160+
161+
def set_debuglevel(self, level):
162+
pass
163+
164+
def connect(self):
165+
pass
166+
167+
def close(self):
168+
pass
169+
170+
def request(self, method, request_uri, body, headers):
171+
pass
172+
173+
def getresponse(self):
174+
_MyHTTPBadStatusConnection.num_calls += 1
175+
raise httplib.BadStatusLine("")
176+
147177

148178
class HttpTest(unittest.TestCase):
149179
def setUp(self):
@@ -187,6 +217,17 @@ def testConnectionType(self):
187217
self.assertEqual(response['content-location'], "http://bitworking.org")
188218
self.assertEqual(content, "the body")
189219

220+
def testBadStatusLineRetry(self):
221+
old_retries = httplib2.RETRIES
222+
httplib2.RETRIES = 1
223+
self.http.force_exception_to_status_code = False
224+
try:
225+
response, content = self.http.request("http://bitworking.org",
226+
connection_type=_MyHTTPBadStatusConnection)
227+
except httplib.BadStatusLine:
228+
self.assertEqual(2, _MyHTTPBadStatusConnection.num_calls)
229+
httplib2.RETRIES = old_retries
230+
190231
def testGetUnknownServer(self):
191232
self.http.force_exception_to_status_code = False
192233
try:

python3/httplib2/__init__.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -957,7 +957,10 @@ def clear_credentials(self):
957957
self.authorizations = []
958958

959959
def _conn_request(self, conn, request_uri, method, body, headers):
960-
for i in range(RETRIES):
960+
i = 0
961+
seen_bad_status_line = False
962+
while i < RETRIES:
963+
i += 1
961964
try:
962965
if conn.sock is None:
963966
conn.connect()
@@ -990,6 +993,19 @@ def _conn_request(self, conn, request_uri, method, body, headers):
990993
pass
991994
try:
992995
response = conn.getresponse()
996+
except (http.client.BadStatusLine, http.client.ResponseNotReady):
997+
# If we get a BadStatusLine on the first try then that means
998+
# the connection just went stale, so retry regardless of the
999+
# number of RETRIES set.
1000+
if not seen_bad_status_line and i == 1:
1001+
i = 0
1002+
seen_bad_status_line = True
1003+
conn.close()
1004+
conn.connect()
1005+
continue
1006+
else:
1007+
conn.close()
1008+
raise
9931009
except socket.timeout:
9941010
raise
9951011
except (socket.error, http.client.HTTPException):

python3/httplib2test.py

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,37 @@ def getresponse(self):
138138
return _MyResponse(b"the body", status="200")
139139

140140

141+
class _MyHTTPBadStatusConnection(object):
142+
"Mock of httplib.HTTPConnection that raises BadStatusLine."
143+
144+
num_calls = 0
145+
146+
def __init__(self, host, port=None, key_file=None, cert_file=None,
147+
strict=None, timeout=None, proxy_info=None):
148+
self.host = host
149+
self.port = port
150+
self.timeout = timeout
151+
self.log = ""
152+
self.sock = None
153+
_MyHTTPBadStatusConnection.num_calls = 0
154+
155+
def set_debuglevel(self, level):
156+
pass
157+
158+
def connect(self):
159+
pass
160+
161+
def close(self):
162+
pass
163+
164+
def request(self, method, request_uri, body, headers):
165+
pass
166+
167+
def getresponse(self):
168+
_MyHTTPBadStatusConnection.num_calls += 1
169+
raise http.client.BadStatusLine("")
170+
171+
141172
class HttpTest(unittest.TestCase):
142173
def setUp(self):
143174
if os.path.exists(cacheDirName):
@@ -169,6 +200,19 @@ def testConnectionType(self):
169200
self.assertEqual(response['content-location'], "http://bitworking.org")
170201
self.assertEqual(content, b"the body")
171202

203+
204+
def testBadStatusLineRetry(self):
205+
old_retries = httplib2.RETRIES
206+
httplib2.RETRIES = 1
207+
self.http.force_exception_to_status_code = False
208+
try:
209+
response, content = self.http.request("http://bitworking.org",
210+
connection_type=_MyHTTPBadStatusConnection)
211+
except http.client.BadStatusLine:
212+
self.assertEqual(2, _MyHTTPBadStatusConnection.num_calls)
213+
httplib2.RETRIES = old_retries
214+
215+
172216
def testGetUnknownServer(self):
173217
self.http.force_exception_to_status_code = False
174218
try:
@@ -485,7 +529,7 @@ def testSslCertValidation(self):
485529

486530
# Test that we get a SSLHandshakeError if we try to access
487531
# https://www.google.com, using a CA cert file that doesn't contain
488-
# the CA Gogole uses (i.e., simulating a cert that's not signed by a
532+
# the CA Google uses (i.e., simulating a cert that's not signed by a
489533
# trusted CA).
490534
other_ca_certs = os.path.join(
491535
os.path.dirname(os.path.abspath(httplib2.__file__ )),

0 commit comments

Comments
 (0)