Wrap image data in iterator
This is establishing the API for a future optimization. We want to be able to offer true socket-level caching, but can't do that with httplib2 right now. For now, we will just fake the optimization by returning an iterator over the image body, which happens to already be fully loaded into a string. Change-Id: I2d36e3cdd45b26d7c7c27ba050bf6a4b5765df6c
This commit is contained in:
@@ -5,6 +5,7 @@ OpenStack Client interface. Handles the REST calls and responses.
|
||||
import copy
|
||||
import logging
|
||||
import os
|
||||
import StringIO
|
||||
import urlparse
|
||||
|
||||
import httplib2
|
||||
@@ -25,6 +26,7 @@ from glanceclient.common import exceptions
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
USER_AGENT = 'python-glanceclient'
|
||||
CHUNKSIZE = 1024 * 64 # 64kB
|
||||
|
||||
|
||||
class HTTPClient(httplib2.Http):
|
||||
@@ -88,7 +90,12 @@ class HTTPClient(httplib2.Http):
|
||||
# Redirected. Reissue the request to the new location.
|
||||
return self._http_request(resp['location'], method, **kwargs)
|
||||
|
||||
return resp, body
|
||||
#NOTE(bcwaldon): body has been loaded to a string at this point,
|
||||
# but we want to move to a world where it can be read from a
|
||||
# socket-level cache. This is here until we can do that.
|
||||
body_iter = ResponseBodyIterator(StringIO.StringIO(body))
|
||||
|
||||
return resp, body_iter
|
||||
|
||||
def json_request(self, method, url, **kwargs):
|
||||
kwargs.setdefault('headers', {})
|
||||
@@ -97,7 +104,8 @@ class HTTPClient(httplib2.Http):
|
||||
if 'body' in kwargs:
|
||||
kwargs['body'] = json.dumps(kwargs['body'])
|
||||
|
||||
resp, body = self._http_request(url, method, **kwargs)
|
||||
resp, body_iter = self._http_request(url, method, **kwargs)
|
||||
body = ''.join([chunk for chunk in body_iter])
|
||||
|
||||
if body:
|
||||
try:
|
||||
@@ -115,3 +123,21 @@ class HTTPClient(httplib2.Http):
|
||||
kwargs['headers'].setdefault('Content-Type',
|
||||
'application/octet-stream')
|
||||
return self._http_request(url, method, **kwargs)
|
||||
|
||||
|
||||
class ResponseBodyIterator(object):
|
||||
"""A class that acts as an iterator over an HTTP response."""
|
||||
|
||||
def __init__(self, resp):
|
||||
self.resp = resp
|
||||
|
||||
def __iter__(self):
|
||||
while True:
|
||||
yield self.next()
|
||||
|
||||
def next(self):
|
||||
chunk = self.resp.read(CHUNKSIZE)
|
||||
if chunk:
|
||||
return chunk
|
||||
else:
|
||||
raise StopIteration()
|
||||
|
||||
@@ -159,8 +159,9 @@ class ImageManager(base.Manager):
|
||||
if copy_from is not None:
|
||||
hdrs['x-glance-api-copy-from'] = copy_from
|
||||
|
||||
resp, body = self.api.raw_request(
|
||||
resp, body_iter = self.api.raw_request(
|
||||
'POST', '/v1/images', headers=hdrs, body=image_data)
|
||||
body = ''.join([c for c in body_iter])
|
||||
return Image(self, json.loads(body)['image'])
|
||||
|
||||
def update(self, image, **kwargs):
|
||||
@@ -199,6 +200,7 @@ class ImageManager(base.Manager):
|
||||
hdrs['x-glance-api-copy-from'] = copy_from
|
||||
|
||||
url = '/v1/images/%s' % base.getid(image)
|
||||
resp, body = self.api.raw_request(
|
||||
resp, body_iter = self.api.raw_request(
|
||||
'PUT', url, headers=hdrs, body=image_data)
|
||||
body = ''.join([c for c in body_iter])
|
||||
return Image(self, json.loads(body)['image'])
|
||||
|
||||
@@ -131,7 +131,7 @@ class ImageManagerTest(unittest.TestCase):
|
||||
self.assertEqual(image.name, 'image-1')
|
||||
|
||||
def test_data(self):
|
||||
data = self.mgr.data('1')
|
||||
data = ''.join([b for b in self.mgr.data('1')])
|
||||
expect = [('GET', '/v1/images/1', {}, None)]
|
||||
self.assertEqual(self.api.calls, expect)
|
||||
self.assertEqual(data, 'XXX')
|
||||
@@ -256,7 +256,7 @@ class ImageTest(unittest.TestCase):
|
||||
|
||||
def test_data(self):
|
||||
image = self.mgr.get('1')
|
||||
data = image.data()
|
||||
data = ''.join([b for b in image.data()])
|
||||
expect = [
|
||||
('HEAD', '/v1/images/1', {}, None),
|
||||
('GET', '/v1/images/1', {}, None),
|
||||
|
||||
Reference in New Issue
Block a user