Merge "Allow reading from object body on download"

This commit is contained in:
Jenkins 2015-06-04 12:08:07 +00:00 committed by Gerrit Code Review
commit ec3e2ab3a0
4 changed files with 89 additions and 11 deletions

@ -135,6 +135,37 @@ def encode_meta_headers(headers):
return ret
class _ObjectBody(object):
"""
Readable and iterable object body response wrapper.
"""
def __init__(self, resp, chunk_size):
"""
Wrap the underlying response
:param resp: the response to wrap
:param chunk_size: number of bytes to return each iteration/next call
"""
self.resp = resp
self.chunk_size = chunk_size
def read(self, length=None):
return self.resp.read(length)
def __iter__(self):
return self
def next(self):
buf = self.resp.read(self.chunk_size)
if not buf:
raise StopIteration()
return buf
def __next__(self):
return self.next()
class HTTPConnection(object):
def __init__(self, url, proxy=None, cacert=None, insecure=False,
ssl_compression=False, default_user_agent=None, timeout=None):
@ -883,13 +914,7 @@ def get_object(url, token, container, name, http_conn=None,
http_reason=resp.reason,
http_response_content=body)
if resp_chunk_size:
def _object_body():
buf = resp.read(resp_chunk_size)
while buf:
yield buf
buf = resp.read(resp_chunk_size)
object_body = _object_body()
object_body = _ObjectBody(resp, resp_chunk_size)
else:
object_body = resp.read()
http_log(('%s%s' % (url.replace(parsed.path, ''), path), method,),

@ -16,7 +16,6 @@
import os
import testtools
import time
import types
from io import BytesIO
from six.moves import configparser
@ -260,8 +259,24 @@ class TestFunctional(testtools.TestCase):
hdrs, body = self.conn.get_object(
self.containername, self.objectname,
resp_chunk_size=10)
self.assertTrue(isinstance(body, types.GeneratorType))
self.assertEqual(self.test_data, b''.join(body))
downloaded_contents = b''
while True:
try:
chunk = next(body)
except StopIteration:
break
downloaded_contents += chunk
self.assertEqual(self.test_data, downloaded_contents)
# Download in chunks, should also work with read
hdrs, body = self.conn.get_object(
self.containername, self.objectname,
resp_chunk_size=10)
num_bytes = 5
downloaded_contents = body.read(num_bytes)
self.assertEqual(num_bytes, len(downloaded_contents))
downloaded_contents += body.read()
self.assertEqual(self.test_data, downloaded_contents)
def test_post_account(self):
self.conn.post_account({'x-account-meta-data': 'Something'})

@ -685,6 +685,41 @@ class TestGetObject(MockHttpTest):
}),
])
def test_chunk_size_read_method(self):
conn = c.Connection('http://auth.url/', 'some_user', 'some_key')
with mock.patch('swiftclient.client.get_auth_1_0') as mock_get_auth:
mock_get_auth.return_value = ('http://auth.url/', 'tToken')
c.http_connection = self.fake_http_connection(200, body='abcde')
__, resp = conn.get_object('asdf', 'asdf', resp_chunk_size=3)
self.assertTrue(hasattr(resp, 'read'))
self.assertEquals(resp.read(3), 'abc')
self.assertEquals(resp.read(None), 'de')
self.assertEquals(resp.read(), '')
def test_chunk_size_iter(self):
conn = c.Connection('http://auth.url/', 'some_user', 'some_key')
with mock.patch('swiftclient.client.get_auth_1_0') as mock_get_auth:
mock_get_auth.return_value = ('http://auth.url/', 'tToken')
c.http_connection = self.fake_http_connection(200, body='abcde')
__, resp = conn.get_object('asdf', 'asdf', resp_chunk_size=3)
self.assertTrue(hasattr(resp, 'next'))
self.assertEquals(next(resp), 'abc')
self.assertEquals(next(resp), 'de')
self.assertRaises(StopIteration, next, resp)
def test_chunk_size_read_and_iter(self):
conn = c.Connection('http://auth.url/', 'some_user', 'some_key')
with mock.patch('swiftclient.client.get_auth_1_0') as mock_get_auth:
mock_get_auth.return_value = ('http://auth.url/', 'tToken')
c.http_connection = self.fake_http_connection(200, body='abcdef')
__, resp = conn.get_object('asdf', 'asdf', resp_chunk_size=2)
self.assertTrue(hasattr(resp, 'read'))
self.assertEquals(resp.read(3), 'abc')
self.assertEquals(next(resp), 'de')
self.assertEquals(resp.read(), 'f')
self.assertRaises(StopIteration, next, resp)
self.assertEquals(resp.read(), '')
class TestHeadObject(MockHttpTest):

@ -155,7 +155,10 @@ def fake_http_connect(*code_iter, **kwargs):
sleep(0.1)
return ' '
rv = self.body[:amt]
self.body = self.body[amt:]
if amt is not None:
self.body = self.body[amt:]
else:
self.body = ''
return rv
def send(self, amt=None):