[infrae.fileupload][Sylvain Viollon] Properly handle if the uplo...

[email protected]
Newsgroups gmane.comp.web.zope.silva.cvs
Message-ID <[email protected]>
author:    Sylvain Viollon
date:      Tue Aug 13 12:08:31 2013 +0200
revision:  11:1ed1c195cd4e in infrae.fileupload
branch:    
details:   https://hg.infrae.com/infrae.fileupload?cmd=changeset;node=1ed1c195cd4e
modified:  src/infrae/fileupload/middleware.py
added:     
removed:   
log:       Properly handle if the upload directory is deleted during the
	service execution.


diffstat:

 src/infrae/fileupload/middleware.py |  85 ++++++++++++++++++++++++++----------
 1 files changed, 61 insertions(+), 24 deletions(-)

diffs (149 lines):

diff -r 7e8f506893f3 -r 1ed1c195cd4e src/infrae/fileupload/middleware.py
--- a/src/infrae/fileupload/middleware.py	Fri Aug 09 11:20:59 2013 +0200
+++ b/src/infrae/fileupload/middleware.py	Tue Aug 13 12:08:31 2013 +0200
@@ -15,7 +15,32 @@
 VALID_ID = re.compile(r'^[a-zA-Z0-9=-]*$')
 IDENTIFIER_KEY = 'X-Progress-ID'
 BLOCK_SIZE = 16 * 1024 * 1024
-EOL = '\r\n'
+
+
+def compare(original, tested=''):
+    """Compare two lines together.
+
+    >>> compare(None, 'foo')
+    False
+    >>> compare('foo', 'bar')
+    False
+    >>> compare('foo', 'foo')
+    False
+    >>> compare('foo\\n', 'foo')
+    True
+    >>> compare('foo\\r\\n', 'foo')
+    True
+    >>> compare('foo\\t\\n', 'foo')
+    False
+    """
+    if original and original.startswith(tested):
+        if original[len(tested):] in ('\n', '\r\n'):
+            return True
+    return False
+
+
+class LockError(ValueError):
+    pass
 
 
 class Lock(object):
@@ -31,7 +56,7 @@
             self._opened = open(self._filename, 'wb')
             fcntl.flock(self._opened, fcntl.LOCK_EX)
         except IOError:
-            raise ValueError('Cannot lock upload directory')
+            raise LockError('Cannot lock upload directory')
 
     def __exit__(self, exc_type, exc_val, exc_tb):
         fcntl.flock(self._opened, fcntl.LOCK_UN)
@@ -301,19 +326,19 @@
         if 'boundary' not in options:
             return fail('Upload request is malformed #1')
 
-        part_boundary = '--' + options['boundary'] + EOL
-        end_boundary = '--' + options['boundary'] + '--' + EOL
+        part_boundary = '--' + options['boundary']
+        end_boundary = '--' + options['boundary'] + '--'
 
         input_stream = Reader(request.environ['wsgi.input'], length)
         # Read the first marker
         marker = input_stream.read()
-        if marker != part_boundary:
+        if marker.strip() != part_boundary:
             return fail('Upload request is malformed #2')
 
         # Read the headers
         headers = {}
         line = input_stream.read()
-        while line != EOL:
+        while compare(line):
             name, payload = line.split(':', 1)
             headers[name.lower().strip()] = cgi.parse_header(payload)
             line = input_stream.read()
@@ -325,21 +350,25 @@
             not headers['content-disposition'][1].get('filename')):
             return fail('Upload request is malformed #3')
 
-        upload = self.manager.create_upload_bucket(
-            identifier,
-            headers['content-disposition'][1]['filename'],
-            headers['content-type'][0],
-            length)
+        try:
+            upload = self.manager.create_upload_bucket(
+                identifier,
+                headers['content-disposition'][1]['filename'],
+                headers['content-type'][0],
+                length)
+        except LockError:
+            return fail('Upload server error #1')
+
         track_progress = upload.progress()
         track_progress.send(None)
         input_stream.subscribe(track_progress.send)
         request.environ['infrae.fileupload.current'] = upload
         line = None
         output_stream = upload.write()
-        while line != end_boundary:
+        while compare(line, end_boundary):
             output_stream.send(line)
             line = input_stream.read()
-            if line == part_boundary:
+            if compare(line, part_boundary):
                 # Multipart, we don't handle that
                 error = fail('Upload request is malformed #4')
                 output_stream.close()
@@ -361,26 +390,34 @@
         """Request an upload. This is called before starting the
         upload. To be sure the file is missing.
         """
-        upload = self.manager.access_upload_bucket(identifier)
-        if upload is not None:
-            upload.clear()
+        try:
+            upload = self.manager.access_upload_bucket(identifier)
+            if upload is not None:
+                upload.clear()
+        except LockError:
+            result = '{"success": false, "error": "Upload server error #1"}'
+        else:
+            result = '{"success": true}'
         response = Response()
         response.content_type = 'application/json'
         if 'callback' in request.GET:
-            response.body = str(request.GET['callback']) + '({"success": true})'
+            response.body = str(request.GET['callback']) + '(' + result + ')'
         else:
-            response.body = '{"success": true}'
+            response.body = result
         return response
 
     def status(self, request, identifier):
         """Handle status information on the upload process of a file.
         """
-        result = {'missing': True}
-        upload = self.manager.access_upload_bucket(identifier)
-        if upload is not None:
-            status = upload.get_status()
-            if status is not None:
-                result = status
+        result = {'state': 'starting'}
+        try:
+            upload = self.manager.access_upload_bucket(identifier)
+            if upload is not None:
+                status = upload.get_status()
+                if status is not None:
+                    result = status
+        except LockError:
+            result = {'state': 'error', 'error': 'Upload server error #1'}
 
         logger.info('%s: %s', identifier, result)
         response = Response()
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.