Skip to content

Commit

Permalink
Fixes image-download error of v2
Browse files Browse the repository at this point in the history
When trying to retrieve a Glance image using the v2 api, user will
get an error:

'utf8' codec can't decode byte 0xcc in position 1217: invalid continuation byte

This is because Glance client expects a response of type content-type:
application/octet-stream but it's getting a Content-Type:

text/html; charset=UTF-8 Transfer-Encoding: chunked

The root cause is Glance will return the image data from cache if
there is. However, in method _process_v2_request, the content type
has never been configured. See:
https://github.com/openstack/glance/blob/master/glance/api/middleware/cache.py#L162

Fixes bug 1193310

Change-Id: Icab5e28db63455358ab3919bd94c484411987e39
  • Loading branch information
Fei Long Wang committed Jul 11, 2013
1 parent 67ec72d commit 5e71b5e
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 0 deletions.
12 changes: 12 additions & 0 deletions glance/api/middleware/cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,18 @@ def _process_v2_request(self, request, image_id, image_iterator):
image_meta['size'],
image_iterator,
notifier.Notifier())
# NOTE (flwang): Set the content-type, content-md5 and content-length
# explicitly to be consistent with the non-cache scenario.
# Besides, it's not worth the candle to invoke the "download" method
# of ResponseSerializer under image_data. Because method "download"
# will reset the app_iter. Then we have to call method
# "size_checked_iter" to avoid missing any notification. But after
# call "size_checked_iter", we will lose the content-md5 and
# content-length got by the method "download" because of this issue:
# https://github.com/Pylons/webob/issues/86
response.headers['Content-Type'] = 'application/octet-stream'
response.headers['Content-MD5'] = image.checksum
response.headers['Content-Length'] = str(image.size)
return response

def process_response(self, resp):
Expand Down
47 changes: 47 additions & 0 deletions glance/tests/unit/test_cache_middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import glance.api.middleware.cache
from glance.common import exception
from glance import context
import glance.db.sqlalchemy.api as db
import glance.registry.client.v1.api as registry
from glance.tests import utils

Expand Down Expand Up @@ -234,6 +235,52 @@ def fake_get_image_size(image_id):
cache_filter._verify_metadata(image_meta)
self.assertTrue(image_meta['size'] == image_size)

def test_v2_process_request_response_headers(self):
def dummy_img_iterator():
for i in range(3):
yield i

def fake_image_get(self, image_id):
return {
'id': 'test1',
'name': 'fake_image',
'status': 'Active',
'created_at': '',
'min_disk': '10G',
'min_ram': '1024M',
'protected': False,
'locations': '',
'checksum': 'c352f4e7121c6eae958bc1570324f17e',
'owner': '',
'disk_format': 'raw',
'container_format': 'bare',
'size': '123456789',
'is_public': 'public',
'deleted': False,
'updated_at': '',
'properties': {},
}

def fake_image_tag_get_all(context, image_id, session=None):
return None

image_id = 'test1'
request = webob.Request.blank('/v2/images/test1/file')
request.context = context.RequestContext()

self.stubs.Set(db, 'image_get', fake_image_get)
self.stubs.Set(db, 'image_tag_get_all', fake_image_tag_get_all)

cache_filter = ProcessRequestTestCacheFilter()
response = cache_filter._process_v2_request(
request, image_id, dummy_img_iterator)
self.assertEqual(response.headers['Content-Type'],
'application/octet-stream')
self.assertEqual(response.headers['Content-MD5'],
'c352f4e7121c6eae958bc1570324f17e')
self.assertEqual(response.headers['Content-Length'],
'123456789')


class TestProcessResponse(utils.BaseTestCase):
def setUp(self):
Expand Down

0 comments on commit 5e71b5e

Please sign in to comment.