From 6adb8bb17fe55a33d3605944653ca956eabc9ece Mon Sep 17 00:00:00 2001
From: Tim Burke <tim.burke@gmail.com>
Date: Fri, 10 Mar 2023 09:22:34 -0800
Subject: [PATCH] service: Check content-length before etag

If the received content-length does not match expectations, of course
the etag won't match!

Co-Authored-By: Alistair Coles <alistairncoles@gmail.com>
Change-Id: I1a0c066c11b94718fffbb11e13b82d0b16e01626
---
 swiftclient/service.py    | 14 +++++++-------
 test/unit/test_service.py | 20 ++++++++++++++++++--
 2 files changed, 25 insertions(+), 9 deletions(-)

diff --git a/swiftclient/service.py b/swiftclient/service.py
index 9a6c7a11..d1c98d6c 100644
--- a/swiftclient/service.py
+++ b/swiftclient/service.py
@@ -457,13 +457,6 @@ class _SwiftReader:
         self._check_contents()
 
     def _check_contents(self):
-        if self._actual_md5 and self._expected_md5:
-            etag = self._actual_md5.hexdigest()
-            if etag != self._expected_md5:
-                raise SwiftError('Error downloading {0}: md5sum != etag, '
-                                 '{1} != {2}'.format(
-                                     self._path, etag, self._expected_md5))
-
         if (self._content_length is not None and
                 self._actual_read != self._content_length):
             raise SwiftError('Error downloading {0}: read_length != '
@@ -471,6 +464,13 @@ class _SwiftReader:
                                  self._path, self._actual_read,
                                  self._content_length))
 
+        if self._actual_md5 and self._expected_md5:
+            etag = self._actual_md5.hexdigest()
+            if etag != self._expected_md5:
+                raise SwiftError('Error downloading {0}: md5sum != etag, '
+                                 '{1} != {2}'.format(
+                                     self._path, etag, self._expected_md5))
+
     def bytes_read(self):
         return self._actual_read
 
diff --git a/test/unit/test_service.py b/test/unit/test_service.py
index 1176a1f8..b6db22d2 100644
--- a/test/unit/test_service.py
+++ b/test/unit/test_service.py
@@ -196,8 +196,24 @@ class TestSwiftReader(unittest.TestCase):
 
         # Check error is raised if SwiftReader doesn't read the same length
         # as the content length it is created with
-        sr = self.sr('path', BytesIO(b'body'), {'content-length': 5})
-        self.assertRaises(SwiftError, _consume, sr)
+        sr = self.sr('path', BytesIO(b'body'), {'content-length': 5,
+                                                'etag': 'bad etag'})
+        with self.assertRaises(SwiftError) as cm:
+            _consume(sr)
+        self.assertEqual(
+            "'Error downloading path: read_length != content_length, 4 != 5'",
+            str(cm.exception))
+
+        # Check error is raised if SwiftReader doesn't calculate the expected
+        # hash
+        sr = self.sr('path', BytesIO(b'body'), {'content-length': 4,
+                                                'etag': 'bad etag'})
+        with self.assertRaises(SwiftError) as cm:
+            _consume(sr)
+        self.assertEqual(
+            "'Error downloading path: md5sum != etag, "
+            "841a2d689ad86bd1611447453c22c6fc != bad etag'",
+            str(cm.exception))
 
         sr = self.sr('path', BytesIO(b'body'), {'content-length': 4})
         _consume(sr)