diff --git a/glance/api/v2/image_data.py b/glance/api/v2/image_data.py index 928b3ba..e981b6f 100644 --- a/glance/api/v2/image_data.py +++ b/glance/api/v2/image_data.py @@ -197,6 +197,8 @@ class ResponseSerializer(wsgi.JSONResponseSerializer): def download(self, response, image): offset, chunk_size = 0, None + + # Initially attempt to get "Content-Range" request range_val = response.request.get_content_range() if range_val: @@ -208,6 +210,21 @@ class ResponseSerializer(wsgi.JSONResponseSerializer): if range_val.stop is not None: chunk_size = range_val.stop - offset + # Return 206 Partial Content + response.status_int = 206 + else: + # Try for "Range" request header if ContentRange not present + range_obj = response.request.get_range() + if range_obj: + if range_obj.start is not None: + offset = range_obj.start + + if range_obj.end is not None: + chunk_size = range_obj.end - offset + + # Return 206 Partial Content + response.status_int = 206 + response.headers['Content-Type'] = 'application/octet-stream' try: @@ -227,7 +244,9 @@ class ResponseSerializer(wsgi.JSONResponseSerializer): response.headers['Content-MD5'] = image.checksum # NOTE(markwash): "response.app_iter = ..." also erroneously resets the # content-length - response.headers['Content-Length'] = str(image.size) + # If returning partial content Content-Length should be set to chunk_size + response.headers['Content-Length'] = \ + str(chunk_size) if chunk_size != 0 else str(image.size) def upload(self, response, result): response.status_int = 204 diff --git a/glance/common/wsgi.py b/glance/common/wsgi.py index 05307ab..14f1c3a 100644 --- a/glance/common/wsgi.py +++ b/glance/common/wsgi.py @@ -560,7 +560,7 @@ class Request(webob.Request): return self.accept_language.best_match(langs) def get_content_range(self): - """Return the `Range` in a request.""" + """Return the `Content-Range` in a request.""" range_str = self.headers.get('Content-Range') if range_str is not None: range_ = webob.byterange.ContentRange.parse(range_str) @@ -569,6 +569,16 @@ class Request(webob.Request): raise webob.exc.HTTPBadRequest(explanation=msg) return range_ + def get_range(self): + """Return the 'Range' in a reqyest.""" + range_str = self.headers.get('Range') + if range_str is not None: + range_ = webob.byterange.Range.parse(range_str) + if range_ is None: + msg = _('Malformed Range header: %s') % range_str + raise webob.exc.HTTPBadRequest(explanation=msg) + return range_ + class JSONRequestDeserializer(object): def has_body(self, request):