|
7 | 7 |
|
8 | 8 | from mock import Mock
|
9 | 9 | from pip._vendor.contextlib2 import nullcontext
|
| 10 | +from werkzeug.http import parse_range_header |
10 | 11 | from werkzeug.serving import WSGIRequestHandler
|
11 | 12 | from werkzeug.serving import make_server as _make_server
|
12 | 13 |
|
@@ -217,6 +218,40 @@ def responder(environ, start_response):
|
217 | 218 | return responder
|
218 | 219 |
|
219 | 220 |
|
| 221 | +def file_range_response(path): |
| 222 | + # type: (str) -> Responder |
| 223 | + def responder(environ, start_response): |
| 224 | + # type: (Environ, StartResponse) -> Body |
| 225 | + size = os.stat(path).st_size |
| 226 | + if 'Range' not in environ: |
| 227 | + start_response( |
| 228 | + "200 OK", [ |
| 229 | + ("Accept-Ranges", "bytes"), |
| 230 | + ("Content-Length", str(size)), |
| 231 | + ("Content-Type", "application/octet-stream"), |
| 232 | + ], |
| 233 | + ) |
| 234 | + if environ['REQUEST_METHOD'] == 'HEAD': |
| 235 | + return [] |
| 236 | + with open(path, 'rb') as f: |
| 237 | + return [f.read()] |
| 238 | + |
| 239 | + # TODO: Multipart ranges |
| 240 | + start, end = parse_range_header(environ['Range']).ranges[0] |
| 241 | + length = end - start + 1 |
| 242 | + start_response( |
| 243 | + "206 Partial Content", [ |
| 244 | + ("Content-Length", str(length)), |
| 245 | + ("Content-Range", "{}-{}/{}".format(start, end, size)), |
| 246 | + ("Content-Type", "application/octet-stream"), |
| 247 | + ], |
| 248 | + ) |
| 249 | + with open(path, 'rb') as f: |
| 250 | + f.seek(start) |
| 251 | + return [f.read(length)] |
| 252 | + return responder |
| 253 | + |
| 254 | + |
220 | 255 | def authorization_response(path):
|
221 | 256 | def responder(environ, start_response):
|
222 | 257 | # type: (Environ, StartResponse) -> Body
|
|
0 commit comments