Merge "Allow reading from object body on download"
This commit is contained in:
commit
ec3e2ab3a0
@ -135,6 +135,37 @@ def encode_meta_headers(headers):
|
|||||||
return ret
|
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):
|
class HTTPConnection(object):
|
||||||
def __init__(self, url, proxy=None, cacert=None, insecure=False,
|
def __init__(self, url, proxy=None, cacert=None, insecure=False,
|
||||||
ssl_compression=False, default_user_agent=None, timeout=None):
|
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_reason=resp.reason,
|
||||||
http_response_content=body)
|
http_response_content=body)
|
||||||
if resp_chunk_size:
|
if resp_chunk_size:
|
||||||
|
object_body = _ObjectBody(resp, 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()
|
|
||||||
else:
|
else:
|
||||||
object_body = resp.read()
|
object_body = resp.read()
|
||||||
http_log(('%s%s' % (url.replace(parsed.path, ''), path), method,),
|
http_log(('%s%s' % (url.replace(parsed.path, ''), path), method,),
|
||||||
|
@ -16,7 +16,6 @@
|
|||||||
import os
|
import os
|
||||||
import testtools
|
import testtools
|
||||||
import time
|
import time
|
||||||
import types
|
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
from six.moves import configparser
|
from six.moves import configparser
|
||||||
@ -260,8 +259,24 @@ class TestFunctional(testtools.TestCase):
|
|||||||
hdrs, body = self.conn.get_object(
|
hdrs, body = self.conn.get_object(
|
||||||
self.containername, self.objectname,
|
self.containername, self.objectname,
|
||||||
resp_chunk_size=10)
|
resp_chunk_size=10)
|
||||||
self.assertTrue(isinstance(body, types.GeneratorType))
|
downloaded_contents = b''
|
||||||
self.assertEqual(self.test_data, b''.join(body))
|
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):
|
def test_post_account(self):
|
||||||
self.conn.post_account({'x-account-meta-data': 'Something'})
|
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):
|
class TestHeadObject(MockHttpTest):
|
||||||
|
|
||||||
|
@ -155,7 +155,10 @@ def fake_http_connect(*code_iter, **kwargs):
|
|||||||
sleep(0.1)
|
sleep(0.1)
|
||||||
return ' '
|
return ' '
|
||||||
rv = self.body[:amt]
|
rv = self.body[:amt]
|
||||||
self.body = self.body[amt:]
|
if amt is not None:
|
||||||
|
self.body = self.body[amt:]
|
||||||
|
else:
|
||||||
|
self.body = ''
|
||||||
return rv
|
return rv
|
||||||
|
|
||||||
def send(self, amt=None):
|
def send(self, amt=None):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user