3998N/AProvide for HTTP Range requests in Glance API, and return correct 206
3998N/APartial Content.
3998N/A
3998N/ASee community bugs:
3998N/A https://bugs.launchpad.net/glance/+bug/1399851
3998N/A https://bugs.launchpad.net/glance/+bug/1417069
3998N/A
6850N/A--- glance-12.0.0/glance/api/v2/image_data.py.~1~ 2016-04-07 00:37:11.000000000 -0700
6850N/A+++ glance-12.0.0/glance/api/v2/image_data.py 2016-06-27 18:46:02.584760065 -0700
6850N/A@@ -281,6 +281,8 @@ class ResponseSerializer(wsgi.JSONRespon
3998N/A
3998N/A def download(self, response, image):
3998N/A offset, chunk_size = 0, None
3998N/A+
3998N/A+ # Initially attempt to get "Content-Range" request
3998N/A range_val = response.request.get_content_range()
3998N/A
3998N/A if range_val:
6850N/A@@ -292,6 +294,21 @@ class ResponseSerializer(wsgi.JSONRespon
3998N/A if range_val.stop is not None:
3998N/A chunk_size = range_val.stop - offset
3998N/A
3998N/A+ # Return 206 Partial Content
3998N/A+ response.status_int = 206
3998N/A+ else:
3998N/A+ # Try for "Range" request header if ContentRange not present
3998N/A+ range_obj = response.request.get_range()
3998N/A+ if range_obj:
3998N/A+ if range_obj.start is not None:
3998N/A+ offset = range_obj.start
3998N/A+
3998N/A+ if range_obj.end is not None:
3998N/A+ chunk_size = range_obj.end - offset
3998N/A+
3998N/A+ # Return 206 Partial Content
3998N/A+ response.status_int = 206
3998N/A+
3998N/A response.headers['Content-Type'] = 'application/octet-stream'
3998N/A
3998N/A try:
6881N/A@@ -317,7 +334,10 @@ class ResponseSerializer(wsgi.JSONRespon
3998N/A response.headers['Content-MD5'] = image.checksum
5403N/A # NOTE(markwash): "response.app_iter = ..." also erroneously resets the
3998N/A # content-length
3998N/A- response.headers['Content-Length'] = str(image.size)
3998N/A+ # NOTE(mattk): Should be set to chunk_size or image.size
3998N/A+ response.headers['Content-Length'] = \
6881N/A+ str(chunk_size) if (chunk_size is not None and chunk_size != 0) \
6881N/A+ else str(image.size)
3998N/A
3998N/A def upload(self, response, result):
3998N/A response.status_int = 204
6850N/A--- glance-12.0.0/glance/common/wsgi.py.~1~ 2016-04-07 00:37:13.000000000 -0700
6850N/A+++ glance-12.0.0/glance/common/wsgi.py 2016-06-27 18:46:02.583921325 -0700
6850N/A@@ -762,7 +762,7 @@ class Request(webob.Request):
3998N/A return self.accept_language.best_match(langs)
3998N/A
3998N/A def get_content_range(self):
3998N/A- """Return the `Range` in a request."""
3998N/A+ """Return the `Content-Range` in a request."""
3998N/A range_str = self.headers.get('Content-Range')
3998N/A if range_str is not None:
3998N/A range_ = webob.byterange.ContentRange.parse(range_str)
6850N/A@@ -771,6 +771,16 @@ class Request(webob.Request):
3998N/A raise webob.exc.HTTPBadRequest(explanation=msg)
3998N/A return range_
3998N/A
3998N/A+ def get_range(self):
3998N/A+ """Return the 'Range' in a reqyest."""
3998N/A+ range_str = self.headers.get('Range')
3998N/A+ if range_str is not None:
3998N/A+ range_ = webob.byterange.Range.parse(range_str)
3998N/A+ if range_ is None:
3998N/A+ msg = _('Malformed Range header: %s') % range_str
3998N/A+ raise webob.exc.HTTPBadRequest(explanation=msg)
3998N/A+ return range_
3998N/A+
3998N/A
3998N/A class JSONRequestDeserializer(object):
5403N/A valid_transfer_encoding = frozenset(['chunked', 'compress', 'deflate',