|
1 Provide for HTTP Range requests in Glance API, and return correct 206 |
|
2 Partial Content. |
|
3 |
|
4 See community bugs: |
|
5 https://bugs.launchpad.net/glance/+bug/1399851 |
|
6 https://bugs.launchpad.net/glance/+bug/1417069 |
|
7 |
|
8 --- glance-2014.2.2/glance/api/v2/image_data.py.~1~ 2015-02-05 07:19:44.000000000 -0800 |
|
9 +++ glance-2014.2.2/glance/api/v2/image_data.py 2015-02-23 14:04:17.091921881 -0800 |
|
10 @@ -199,6 +199,8 @@ class ResponseSerializer(wsgi.JSONRespon |
|
11 |
|
12 def download(self, response, image): |
|
13 offset, chunk_size = 0, None |
|
14 + |
|
15 + # Initially attempt to get "Content-Range" request |
|
16 range_val = response.request.get_content_range() |
|
17 |
|
18 if range_val: |
|
19 @@ -210,6 +212,21 @@ class ResponseSerializer(wsgi.JSONRespon |
|
20 if range_val.stop is not None: |
|
21 chunk_size = range_val.stop - offset |
|
22 |
|
23 + # Return 206 Partial Content |
|
24 + response.status_int = 206 |
|
25 + else: |
|
26 + # Try for "Range" request header if ContentRange not present |
|
27 + range_obj = response.request.get_range() |
|
28 + if range_obj: |
|
29 + if range_obj.start is not None: |
|
30 + offset = range_obj.start |
|
31 + |
|
32 + if range_obj.end is not None: |
|
33 + chunk_size = range_obj.end - offset |
|
34 + |
|
35 + # Return 206 Partial Content |
|
36 + response.status_int = 206 |
|
37 + |
|
38 response.headers['Content-Type'] = 'application/octet-stream' |
|
39 |
|
40 try: |
|
41 @@ -229,7 +246,9 @@ class ResponseSerializer(wsgi.JSONRespon |
|
42 response.headers['Content-MD5'] = image.checksum |
|
43 #NOTE(markwash): "response.app_iter = ..." also erroneously resets the |
|
44 # content-length |
|
45 - response.headers['Content-Length'] = str(image.size) |
|
46 + # NOTE(mattk): Should be set to chunk_size or image.size |
|
47 + response.headers['Content-Length'] = \ |
|
48 + str(chunk_size) if chunk_size != 0 else str(image.size) |
|
49 |
|
50 def upload(self, response, result): |
|
51 response.status_int = 204 |
|
52 --- glance-2014.2.2/glance/common/wsgi.py.~1~ 2015-02-05 07:19:44.000000000 -0800 |
|
53 +++ glance-2014.2.2/glance/common/wsgi.py 2015-02-23 14:04:17.092284573 -0800 |
|
54 @@ -556,7 +556,7 @@ class Request(webob.Request): |
|
55 return self.accept_language.best_match(langs) |
|
56 |
|
57 def get_content_range(self): |
|
58 - """Return the `Range` in a request.""" |
|
59 + """Return the `Content-Range` in a request.""" |
|
60 range_str = self.headers.get('Content-Range') |
|
61 if range_str is not None: |
|
62 range_ = webob.byterange.ContentRange.parse(range_str) |
|
63 @@ -565,6 +565,16 @@ class Request(webob.Request): |
|
64 raise webob.exc.HTTPBadRequest(explanation=msg) |
|
65 return range_ |
|
66 |
|
67 + def get_range(self): |
|
68 + """Return the 'Range' in a reqyest.""" |
|
69 + range_str = self.headers.get('Range') |
|
70 + if range_str is not None: |
|
71 + range_ = webob.byterange.Range.parse(range_str) |
|
72 + if range_ is None: |
|
73 + msg = _('Malformed Range header: %s') % range_str |
|
74 + raise webob.exc.HTTPBadRequest(explanation=msg) |
|
75 + return range_ |
|
76 + |
|
77 |
|
78 class JSONRequestDeserializer(object): |
|
79 def has_body(self, request): |