#8622 closed (fixed)
Exceptions in UploadHandler cause hang
Reported by: | Karen Tracey | Owned by: | nobody |
---|---|---|---|
Component: | File uploads/storage | Version: | dev |
Severity: | Keywords: | ||
Cc: | Triage Stage: | Unreviewed | |
Has patch: | yes | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
I ran across this trying to recreate another problem. On Windows, if I set FILE_UPLOAD_TEMP_DIR to reside on a disk that is nearly out of space, and then use the django.core.files.uploadhandler.TemporaryFileUploadHandler
to handle uploaded files, file uploads will hang. I changed the implementation of write in the Windows implementation of TemporaryFile in django/core/files/temp.py to be:
def write(self, s): print 'in TemporaryFile(nt), about to call self.file.write(s)' x = self.file.write(s) print 'in TemporaryFile(nt), back from self.file.write(s)' return x
and then when I try the upload (using dev server) I see the first print, but not the second. It's not in a hard loop, the CPU is mostly idle, so it's like its waiting for free space. Clearing space on the disk doesn't wake it up, though.
Attachments (2)
Change History (10)
comment:1 by , 16 years ago
milestone: | → 1.0 |
---|---|
Summary: | Windows TemporaryFile hangs on write when disk is full → Exceptions in UploadHandler cause hang |
comment:2 by , 16 years ago
Karen, what's the full stack trace leading to that exception? traceback.print_stack()
will be handy there.
comment:3 by , 16 years ago
Here it is:
in TemporaryFile(nt), about to call self.file.write(s) Caught exception on self.file.write(s): [Errno 28] No space left on device File "d:\u\kmt\django\trunk\django\core\management\commands\runserver.py", line 54, in inner_run run(addr, int(port), handler) File "d:\u\kmt\django\trunk\django\core\servers\basehttp.py", line 665, in run httpd.serve_forever() File "d:\bin\Python2.5.2\lib\SocketServer.py", line 201, in serve_forever self.handle_request() File "d:\bin\Python2.5.2\lib\SocketServer.py", line 222, in handle_request self.process_request(request, client_address) File "d:\bin\Python2.5.2\lib\SocketServer.py", line 241, in process_request self.finish_request(request, client_address) File "d:\bin\Python2.5.2\lib\SocketServer.py", line 254, in finish_request self.RequestHandlerClass(request, client_address, self) File "d:\u\kmt\django\trunk\django\core\servers\basehttp.py", line 557, in __init__ BaseHTTPRequestHandler.__init__(self, *args, **kwargs) File "d:\bin\Python2.5.2\lib\SocketServer.py", line 522, in __init__ self.handle() File "d:\u\kmt\django\trunk\django\core\servers\basehttp.py", line 602, in handle handler.run(self.server.get_app()) File "d:\u\kmt\django\trunk\django\core\servers\basehttp.py", line 277, in run self.result = application(self.environ, self.start_response) File "d:\u\kmt\django\trunk\django\core\servers\basehttp.py", line 634, in __call__ return self.application(environ, start_response) File "d:\u\kmt\django\trunk\django\core\handlers\wsgi.py", line 222, in __call__ response = self.get_response(request) File "d:\u\kmt\django\trunk\django\core\handlers\base.py", line 86, in get_response response = callback(request, *callback_args, **callback_kwargs) File "d:\u\kmt\django\trunk\django\contrib\auth\decorators.py", line 67, in __call__ return self.view_func(request, *args, **kwargs) File "D:\u\kmt\software\web\xword\..\xword\crossword\views.py", line 51, in upload_puzzle form = UploadPuzzleForm(request.POST, request.FILES) File "d:\u\kmt\django\trunk\django\core\handlers\wsgi.py", line 152, in _get_post self._load_post_and_files() File "d:\u\kmt\django\trunk\django\core\handlers\wsgi.py", line 130, in _load_post_and_files self._post, self._files = self.parse_file_upload(self.META, self.environ['wsgi.input']) File "d:\u\kmt\django\trunk\django\http\__init__.py", line 124, in parse_file_upload return parser.parse() File "d:\u\kmt\django\trunk\django\http\multipartparser.py", line 205, in parse counters[i]) File "d:\u\kmt\django\trunk\django\core\files\uploadhandler.py", line 142, in receive_data_chunk self.file.write(raw_data) File "d:\u\kmt\django\trunk\django\core\files\uploadedfile.py", line 89, in write def write(self, s): return self._file.write(s) File "d:\u\kmt\django\trunk\django\core\files\temp.py", line 60, in write traceback.print_stack()
comment:4 by , 16 years ago
Karen mentioned it above that there are general problems with exceptions in FileUploadHandlers.
When I raise an Exception in my custom File UploadHandler, than the server gets stuck.
comment:5 by , 16 years ago
request.POST
is computed the first time it is accessed and cached in request._post
; if request._post
is missing, the request.POST
accessor starts parsing POST data.
Parsing POST data means that a django.http.MultiPartParser is instantiated and starts reading from the socket.
If an exception is raised in the meantime, request._post
is not set and any access to request.POST will trigger again the parsing.
This second parsing happens when the exception is handled, in django.core.handlers.base.BaseHandler.handle_uncaught_exception, which calls repr(request), etc.
A second MultiPartParser is instantiated and since the first parser already consumed some bytes this second one will never get the chance to read up to content_length, so it will block waiting for content -- the exact place is django.http.LimitBytes.read()
I made a small test which reproduces the problem (no solution, though)
by , 16 years ago
Attachment: | file_upload_hang_test.diff added |
---|
by , 16 years ago
Attachment: | file_upload_error_handling.diff added |
---|
comment:6 by , 16 years ago
Has patch: | set |
---|
Since the error is caused by triggering again POST data parsing, it would make sense for the fix to try to prevent it somehow; this is what the attached patch does.
Unrelated, but maybe useful for someone who (like me, until now) doesn't know: if an error is elusive, set DEBUG_PROPAGATE_EXCEPTIONS to True in settings.py
comment:7 by , 16 years ago
Resolution: | → fixed |
---|---|
Status: | new → closed |
Further investigation shows the write is not hanging, it is raising an exception. If I change the code to:
and try to upload to a disk with not enough free space, I see:
in my dev server console output. No error is reflected anywhere I can see, there is no further console output and the browser page that initiated the upload is stuck at "Loading....".
Changing milestone to 1.0 since this appears to be a more far-reaching problem than I initially thought and someone should probably take a look at it sooner rather than later.
Also exceptions in upload handlers causing requests to hang has been reported here: http://groups.google.com/group/django-users/browse_thread/thread/b2fb9bf7fe1d47f9#