Repository: pypi/warehouse

Issue:
Double check conditional HTTP implementation
The conditional HTTP implementation doesn't check the status code of the response at all. Determine if it should, and if it should update it to do the right thing.



Discussion:

Resolving patch:
diff --git a/warehouse/cache/http.py b/warehouse/cache/http.py
--- a/warehouse/cache/http.py
+++ b/warehouse/cache/http.py
@@ -75,8 +75,13 @@ def conditional_http_tween(request):
             response.conditional_response = True
         elif (isinstance(response.app_iter, collections.abc.Sequence) and
                 len(response.app_iter) == 1):
-            response.conditional_response = True
-            response.md5_etag()
+            # We can only reasonably implement automatic ETags on 200 responses
+            # to GET or HEAD requests. The subtles of doing it in other cases
+            # are too hard to get right.
+            if (request.method in {"GET", "HEAD"} and
+                    response.status_code == 200):
+                response.conditional_response = True
+                response.md5_etag()
 
         return response
     return conditional_http_tween


Test patch:
diff --git a/tests/cache/test_http.py b/tests/cache/test_http.py
--- a/tests/cache/test_http.py
+++ b/tests/cache/test_http.py
@@ -165,16 +165,18 @@ def test_explicit_etag(self):
         assert handler.calls == [pretend.call(request)]
         assert response.conditional_response
 
-    def test_implicit_etag(self):
+    @pytest.mark.parametrize("method", ["GET", "HEAD"])
+    def test_implicit_etag(self, method):
         response = pretend.stub(
             last_modified=None,
             etag=None,
             conditional_response=False,
             md5_etag=pretend.call_recorder(lambda: None),
             app_iter=[b"foo"],
+            status_code=200,
         )
         handler = pretend.call_recorder(lambda request: response)
-        request = pretend.stub()
+        request = pretend.stub(method=method)
 
         tween = conditional_http_tween_factory(handler, pretend.stub())
 
@@ -183,6 +185,46 @@ def test_implicit_etag(self):
         assert response.conditional_response
         assert response.md5_etag.calls == [pretend.call()]
 
+    @pytest.mark.parametrize("method", ["GET", "HEAD"])
+    def test_no_implicit_etag_no_200(self, method):
+        response = pretend.stub(
+            last_modified=None,
+            etag=None,
+            conditional_response=False,
+            md5_etag=pretend.call_recorder(lambda: None),
+            app_iter=[b"foo"],
+            status_code=201,
+        )
+        handler = pretend.call_recorder(lambda request: response)
+        request = pretend.stub(method=method)
+
+        tween = conditional_http_tween_factory(handler, pretend.stub())
+
+        assert tween(request) is response
+        assert handler.calls == [pretend.call(request)]
+        assert not response.conditional_response
+        assert response.md5_etag.calls == []
+
+    @pytest.mark.parametrize("method", ["POST", "PUT"])
+    def test_no_implicit_etag_wrong_method(self, method):
+        response = pretend.stub(
+            last_modified=None,
+            etag=None,
+            conditional_response=False,
+            md5_etag=pretend.call_recorder(lambda: None),
+            app_iter=[b"foo"],
+            status_code=200,
+        )
+        handler = pretend.call_recorder(lambda request: response)
+        request = pretend.stub(method=method)
+
+        tween = conditional_http_tween_factory(handler, pretend.stub())
+
+        assert tween(request) is response
+        assert handler.calls == [pretend.call(request)]
+        assert not response.conditional_response
+        assert response.md5_etag.calls == []
+
     def test_no_etag(self):
         response = pretend.stub(
             last_modified=None,