Permalink
Browse files

Make resumable transfers work around httplib bug wrt broken pipes

  • Loading branch information...
1 parent 2463033 commit 25d61fa40c2633609a8a8dc6a703c870087b3194 @mfschwartz mfschwartz committed May 13, 2011
@@ -526,6 +526,13 @@ def send_file(self, key, fp, headers, cb=None, num_cb=10):
except self.RETRYABLE_EXCEPTIONS, e:
if debug >= 1:
print('Caught exception (%s)' % e.__repr__())
+ if isinstance(e, IOError) and e.errno == errno.EPIPE:
+ # Broken pipe error causes httplib to immediately
+ # close the socket (http://bugs.python.org/issue5542),
+ # so we need to close the connection before we resume
+ # the upload (which will cause a new connection to be
+ # opened the next time an HTTP request is sent).
+ key.bucket.connection.connection.close()
except ResumableUploadException, e:
if (e.disposition ==
ResumableTransferDisposition.ABORT_CUR_PROCESS):
@@ -294,6 +294,13 @@ def get_file(self, key, fp, headers, cb=None, num_cb=10, torrent=False,
except self.RETRYABLE_EXCEPTIONS, e:
if debug >= 1:
print('Caught exception (%s)' % e.__repr__())
+ if isinstance(e, IOError) and e.errno == errno.EPIPE:
+ # Broken pipe error causes httplib to immediately
+ # close the socket (http://bugs.python.org/issue5542),
+ # so we need to close and reopen the key before resuming
+ # the download.
+ key.get_file(fp, headers, cb, num_cb, torrent, version_id,
+ override_num_retries=0)
except ResumableDownloadException, e:
if (e.disposition ==
ResumableTransferDisposition.ABORT_CUR_PROCESS):
@@ -247,6 +247,22 @@ def test_retryable_exception_recovery(self):
self.assertEqual(self.small_src_key_as_string,
self.small_src_key.get_contents_as_string())
+ def test_broken_pipe_recovery(self):
+ """
+ Tests handling of a Broken Pipe (which interacts with an httplib bug)
+ """
+ exception = IOError(errno.EPIPE, "Broken pipe")
+ harnass = CallbackTestHarnass(exception=exception)
+ res_download_handler = ResumableDownloadHandler(num_retries=1)
+ self.small_src_key.get_contents_to_file(
+ self.dst_fp, cb=harnass.call,
+ res_download_handler=res_download_handler)
+ # Ensure downloaded object has correct content.
+ self.assertEqual(self.small_src_key_size,
+ get_cur_file_size(self.dst_fp))
+ self.assertEqual(self.small_src_key_as_string,
+ self.small_src_key.get_contents_as_string())
+
def test_non_retryable_exception_handling(self):
"""
Tests resumable download that fails with a non-retryable exception
@@ -244,6 +244,21 @@ def test_retryable_exception_recovery(self):
self.assertEqual(self.small_src_file_as_string,
self.dst_key.get_contents_as_string())
+ def test_broken_pipe_recovery(self):
+ """
+ Tests handling of a Broken Pipe (which interacts with an httplib bug)
+ """
+ exception = IOError(errno.EPIPE, "Broken pipe")
+ harnass = CallbackTestHarnass(exception=exception)
+ res_upload_handler = ResumableUploadHandler(num_retries=1)
+ self.dst_key.set_contents_from_file(
+ self.small_src_file, cb=harnass.call,
+ res_upload_handler=res_upload_handler)
+ # Ensure uploaded object has correct content.
+ self.assertEqual(self.small_src_file_size, self.dst_key.size)
+ self.assertEqual(self.small_src_file_as_string,
+ self.dst_key.get_contents_as_string())
+
def test_non_retryable_exception_handling(self):
"""
Tests a resumable upload that fails with a non-retryable exception

0 comments on commit 25d61fa

Please sign in to comment.