]> git.ipfire.org Git - pakfire.git/commitdiff
http: Download to a temporary file
authorMichael Tremer <michael.tremer@ipfire.org>
Sun, 11 Dec 2016 22:04:26 +0000 (23:04 +0100)
committerMichael Tremer <michael.tremer@ipfire.org>
Sun, 11 Dec 2016 22:04:26 +0000 (23:04 +0100)
This patch makes the HTTP client download data
into memory first and writes it to a temporary
file after 10M of data was downloaded.

This makes downloads faster and reduces IO load
but does not give us any limitation in case bigger
files are downloaded.

We get rid of some file locking which is always
difficult to entertain.

Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
src/pakfire/http.py

index 9c25016be68b378429d7f49b77473a420e4afa46..ec9aa3d7ab65b766fe8ee34d9ecf2fef3a38a828 100644 (file)
 ###############################################################################
 
 import base64
-import fcntl
 import json
 import logging
+import shutil
 import ssl
+import tempfile
 import time
 import urllib.parse
 import urllib.request
@@ -38,6 +39,9 @@ from . import errors
 log = logging.getLogger("pakfire.http")
 log.propagate = 1
 
+# Maximum size of temporary files that is being kept in memory
+TMP_MAX_SIZE = 10485760 # 10M
+
 class Client(object):
        """
                Implements a basic HTTP client which is used to download
@@ -287,15 +291,14 @@ class Client(object):
                if self.mirrors and not self.mirror:
                        self._next_mirror()
 
+               # Create a temporary file where the downloaded data is stored
+               # This is a spooled file which will be kept in memory and only
+               # be written to disk when max_size is exceeded.
+               f = tempfile.SpooledTemporaryFile(max_size=TMP_MAX_SIZE)
+
                try:
                        with self._make_progressbar(message) as p:
-                               with open(filename, "wb") as f:
-                                       # Exclusively lock the file for download
-                                       try:
-                                               fcntl.flock(f, fcntl.LOCK_EX)
-                                       except OSError as e:
-                                               raise DownloadError(_("Could not lock target file")) from e
-
+                               with f:
                                        while True:
                                                # Prepare HTTP request
                                                r = self._make_request(url, mirror=self.mirror, **kwargs)
@@ -342,6 +345,10 @@ class Client(object):
                                                        # Otherwise raise this error
                                                        raise e
 
+                                               # Downloaded succeeded, writing data to filesystem
+                                               with open(filename, "wb") as output:
+                                                       shutil.copyobj(f, output)
+
                finally:
                        # Re-add any skipped mirrors again so that the next
                        # request will be tried on all mirrors, too.