commit:     8c6e5d06afbf6fca1893cff5ed777e44f93b7a5d
Author:     Alexey Gladkov <legion <AT> kernel <DOT> org>
AuthorDate: Sun Mar  3 16:41:08 2024 +0000
Commit:     Sam James <sam <AT> gentoo <DOT> org>
CommitDate: Sun Apr 28 00:04:08 2024 +0000
URL:        https://gitweb.gentoo.org/proj/portage.git/commit/?id=8c6e5d06

sync/zipfile: Handle ETag header

Most services add an ETag header and determine whether the locally
cached version of the URL has expired. So we can add ETag processing to
avoid unnecessary downloading and unpacking of the zip archive.

Signed-off-by: Alexey Gladkov <legion <AT> kernel.org>
Signed-off-by: Sam James <sam <AT> gentoo.org>

 lib/portage/sync/modules/zipfile/zipfile.py | 36 +++++++++++++++++++++++------
 1 file changed, 29 insertions(+), 7 deletions(-)

diff --git a/lib/portage/sync/modules/zipfile/zipfile.py 
b/lib/portage/sync/modules/zipfile/zipfile.py
index 1762d2c8f1..bb78b39243 100644
--- a/lib/portage/sync/modules/zipfile/zipfile.py
+++ b/lib/portage/sync/modules/zipfile/zipfile.py
@@ -10,7 +10,7 @@ import tempfile
 import urllib.request
 
 import portage
-from portage.util import writemsg_level
+from portage.util import writemsg_level, writemsg_stdout
 from portage.sync.syncbase import SyncBase
 
 
@@ -31,13 +31,31 @@ class ZipFile(SyncBase):
         if kwargs:
             self._kwargs(kwargs)
 
-        # initial checkout
-        zip_uri = self.repo.sync_uri
+        req = urllib.request.Request(url=self.repo.sync_uri)
 
-        with urllib.request.urlopen(zip_uri) as response:
-            with tempfile.NamedTemporaryFile(delete=False) as tmp_file:
-                shutil.copyfileobj(response, tmp_file)
-            zip_file = tmp_file.name
+        info = portage.grabdict(os.path.join(self.repo.location, ".info"))
+        if "etag" in info:
+            req.add_header("If-None-Match", info["etag"][0])
+
+        try:
+            with urllib.request.urlopen(req) as response:
+                with tempfile.NamedTemporaryFile(delete=False) as tmp_file:
+                    shutil.copyfileobj(response, tmp_file)
+
+                zip_file = tmp_file.name
+                etag = response.headers.get("etag")
+
+        except urllib.error.HTTPError as resp:
+            if resp.code == 304:
+                writemsg_stdout(">>> The repository has not changed.\n", 
noiselevel=-1)
+                return (os.EX_OK, False)
+
+            writemsg_level(
+                f"!!! Unable to obtain zip archive: {resp}\n",
+                noiselevel=-1,
+                level=logging.ERROR,
+            )
+            return (1, False)
 
         if not zipfile.is_zipfile(zip_file):
             msg = "!!! file is not a zip archive."
@@ -77,6 +95,10 @@ class ZipFile(SyncBase):
                     with open(dstpath, "wb") as dstfile:
                         shutil.copyfileobj(srcfile, dstfile)
 
+        with open(os.path.join(self.repo.location, ".info"), "w") as infofile:
+            if etag:
+                infofile.write(f"etag {etag}\n")
+
         os.unlink(zip_file)
 
         return (os.EX_OK, True)

Reply via email to