Ticket #7894: file_wrapper_response3.diff
File file_wrapper_response3.diff, 11.4 KB (added by , 16 years ago) |
---|
-
django/http/__init__.py
424 424 def __init__(self, *args, **kwargs): 425 425 HttpResponse.__init__(self, *args, **kwargs) 426 426 427 428 class FileWrapper(object): 429 def __init__(self, file_path, block_size, byte_count, offset): 430 self.__dict__.update(locals()) 431 432 def __iter__(self): 433 self.file = open(self.file_path, 'rb') 434 self.close = self.file.close 435 if self.offset: 436 self.file.seek(self.offset) 437 self.bytes_left = self.byte_count 438 return self 439 440 def next(self): 441 if self.bytes_left > 0: 442 data_size = min(self.block_size, self.bytes_left) 443 data = self.file.read(data_size) 444 if data: 445 self.bytes_left -= data_size 446 return data 447 raise StopIteration 448 449 450 class HttpResponseFileWrapper(HttpResponse): 451 def __init__(self, file_path, block_size=8192, length=None, offset=0, **kwargs): 452 content_length = length or (os.path.getsize(file_path) - offset) 453 HttpResponse.__init__(self, content=FileWrapper(file_path, block_size, content_length, offset), **kwargs) 454 self.file_path = file_path 455 self.block_size = block_size 456 self.length = length 457 self.offset = offset 458 self['Content-Length'] = content_length 459 460 427 461 # A backwards compatible alias for HttpRequest.get_host. 428 462 def get_host(request): 429 463 return request.get_host() -
django/core/servers/basehttp.py
312 312 in the event loop to iterate over the data, and to call 313 313 'self.close()' once the response is finished. 314 314 """ 315 if not self.result_is_file() andnot self.sendfile():315 if not self.result_is_file() or not self.sendfile(): 316 316 for data in self.result: 317 317 self.write(data) 318 318 self.finish_content() -
django/core/handlers/wsgi.py
1 1 from threading import Lock 2 import os 2 3 from pprint import pformat 3 4 try: 4 5 from cStringIO import StringIO … … 194 195 initLock = Lock() 195 196 request_class = WSGIRequest 196 197 198 def wsgi_adaptor_honours_content_length(self, environ): 199 return 'mod_wsgi.callable_object' in environ 200 197 201 def __call__(self, environ, start_response): 198 202 from django.conf import settings 199 203 … … 232 236 for c in response.cookies.values(): 233 237 response_headers.append(('Set-Cookie', str(c.output(header='')))) 234 238 start_response(status, response_headers) 235 return response 239 if (isinstance(response, http.HttpResponseFileWrapper) and 240 'wsgi.file_wrapper'in environ and 241 (response.length is None or 242 self.wsgi_adaptor_honours_content_length(environ) or 243 response.length == os.path.getsize(response.file_path) - response.offset)): 244 f = open(response.file_path, 'rb') 245 if response.offset: 246 f.seek(response.offset) 247 return environ['wsgi.file_wrapper'](f, response.block_size) 248 else: 249 return response 236 250 -
django/core/handlers/modpython.py
199 199 req.headers_out.add('Set-Cookie', c.output(header='')) 200 200 req.status = response.status_code 201 201 try: 202 for chunk in response: 203 req.write(chunk) 202 if isinstance(response, http.HttpResponseFileWrapper): 203 if response.length is None: 204 length = -1 205 else: 206 length = response.length 207 req.sendfile(response.file_path, response.offset, length) 208 else: 209 for chunk in response: 210 req.write(chunk) 204 211 finally: 205 212 response.close() 206 207 213 return 0 # mod_python.apache.OK 208 214 209 215 def handler(req): -
django/views/static.py
12 12 from email.Utils import parsedate_tz, mktime_tz 13 13 14 14 from django.template import loader 15 from django.http import Http404, HttpResponse, HttpResponseRedirect, HttpResponseNotModified 15 from django.http import Http404, HttpResponse, HttpResponseRedirect, HttpResponseNotModified, HttpResponseFileWrapper 16 16 from django.template import Template, Context, TemplateDoesNotExist 17 17 from django.utils.http import http_date 18 18 … … 60 60 statobj[stat.ST_MTIME], statobj[stat.ST_SIZE]): 61 61 return HttpResponseNotModified() 62 62 mimetype = mimetypes.guess_type(fullpath)[0] or 'application/octet-stream' 63 contents = open(fullpath, 'rb').read() 64 response = HttpResponse(contents, mimetype=mimetype) 63 response = HttpResponseFileWrapper(fullpath, mimetype=mimetype) 65 64 response["Last-Modified"] = http_date(statobj[stat.ST_MTIME]) 66 response["Content-Length"] = len(contents)67 65 return response 68 66 69 67 DEFAULT_DIRECTORY_INDEX_TEMPLATE = """ -
tests/regressiontests/handlers/tests.py
1 """ 2 ############################################# 3 # WSGI handler with HttpResponseFileWrapper # 4 ############################################# 5 6 >>> h = wsgi.WSGIHandler() 7 8 Patch handler to shortcut dispatch process and provide a mocked response 9 10 >>> import os 11 >>> test_file_path = os.path.join(os.path.dirname(__file__), 'test_file.txt') 12 >>> r = http.HttpResponseFileWrapper(test_file_path) 13 >>> h.get_response = lambda self: r 14 15 Mock the environ and start_response 16 17 >>> environ = {'REQUEST_METHOD': 'GET'} 18 >>> start_response = lambda status, headers: None 19 20 No wsgi.file_wrapper in the environ defaults to returning the response 21 22 >>> r is h(environ, start_response) 23 True 24 25 A wsgi.file_wrapper in the environ with no length or offset makes the handler call the file_wrapper with the open file and suggested block_size 26 27 >>> file_wrapper = lambda f, block_size: (f, block_size) 28 >>> environ = {'REQUEST_METHOD': 'GET', 'wsgi.file_wrapper': file_wrapper} 29 30 >>> f, block_size = h(environ, start_response) 31 >>> f 32 <open file '.../tests/regressiontests/handlers/test_file.txt', mode 'rb' at ...> 33 >>> f.tell() == 0 34 True 35 >>> block_size == r.block_size 36 True 37 >>> f.close() 38 39 An response with an offset causes a file seek before the handler returns 40 41 >>> r = http.HttpResponseFileWrapper(test_file_path, offset=4) 42 >>> h.get_response = lambda self: r 43 44 >>> f, block_size = h(environ, start_response) 45 >>> f.tell() == 4 46 True 47 >>> f.close() 48 49 A response with a length that isn't the remaining file length in a wsgi adaptor that isn't recognised as supporting this returns the response 50 51 >>> r = http.HttpResponseFileWrapper(test_file_path, length=10) 52 >>> h.get_response = lambda self: r 53 54 >>> r is h(environ, start_response) 55 True 56 57 If the length is the remaining file length then all adaptors should support this 58 59 >>> r = http.HttpResponseFileWrapper(test_file_path, block_size=8192, length=26) 60 >>> h.get_response = lambda self: r 61 >>> h(environ, start_response) 62 (<open file '.../tests/regressiontests/handlers/test_file.txt', mode 'rb' at ...>, 8192) 63 64 >>> r = http.HttpResponseFileWrapper(test_file_path, block_size=8192, offset=1, length=25) 65 >>> h.get_response = lambda self: r 66 >>> h(environ, start_response) 67 (<open file '.../tests/regressiontests/handlers/test_file.txt', mode 'rb' at ...>, 8192) 68 69 If the wsgi adaptor is recognised as honouring the response's Content-Length header then the file_wrapper is used even if the length isn't the remaining file length 70 71 >>> environ = {'REQUEST_METHOD': 'GET', 'wsgi.file_wrapper': file_wrapper, 'mod_wsgi.callable_object': 'foo'} 72 >>> r = http.HttpResponseFileWrapper(test_file_path, block_size=8192, length=5) 73 >>> h.get_response = lambda self: r 74 >>> h(environ, start_response) 75 (<open file '.../tests/regressiontests/handlers/test_file.txt', mode 'rb' at ...>, 8192) 76 77 """ 78 79 from django.core.handlers import wsgi 80 from django import http 81 82 if __name__ == "__main__": 83 import doctest 84 doctest.testmod() -
tests/regressiontests/handlers/test_file.txt
1 123456789 Heres some data! 2 No newline at end of file -
tests/regressiontests/httpwrappers/tests.py
426 426 Traceback (most recent call last): 427 427 ... 428 428 UnicodeEncodeError: ..., HTTP response headers must be in US-ASCII format 429 429 430 431 ########################### 432 # HttpResponseFileWrapper # 433 ########################### 434 435 Get the path of the test file 436 437 >>> import os 438 >>> test_file_path = os.path.join(os.path.dirname(__file__), 'test_file.txt') 439 440 Create a response with the file path 441 442 >>> r = HttpResponseFileWrapper(test_file_path, mimetype='text/plain') 443 444 The response's content defaults to an iterator over the file data 445 446 >>> ''.join(list(r)) 447 '123456789 Heres some data!' 448 449 The Content-Length header is set to size of the file 450 451 >>> r['Content-Length'] 452 '26' 453 454 An offset and length can be provided to limit the data read from the file 455 456 >>> r = HttpResponseFileWrapper(test_file_path, offset=2, length=5) 457 >>> ''.join(list(r)) 458 '34567' 459 >>> r['Content-Length'] 460 '5' 461 >>> r = HttpResponseFileWrapper(test_file_path, offset=2) 462 >>> r['Content-Length'] 463 '24' 464 465 A block_size can also be passed in as a suggestion for the block size to read 466 467 >>> r = HttpResponseFileWrapper(test_file_path, offset=2, length=5, block_size=4024) 468 469 The handler can choose to deliver the file's data using alternative means by using the response's properties rather than treating it as an iterator 470 471 >>> r.file_path == test_file_path 472 True 473 474 >>> r.offset 475 2 476 >>> r.length 477 5 478 >>> r.block_size 479 4024 480 430 481 """ 431 482 432 from django.http import QueryDict, HttpResponse 483 from django.http import QueryDict, HttpResponse, HttpResponseFileWrapper 433 484 434 485 if __name__ == "__main__": 435 486 import doctest -
tests/regressiontests/httpwrappers/test_file.txt
1 123456789 Heres some data! 2 No newline at end of file