1. from datetime import datetime
    
  2. 
    
  3. from django.test import SimpleTestCase, override_settings
    
  4. 
    
  5. FULL_RESPONSE = "Test conditional get response"
    
  6. LAST_MODIFIED = datetime(2007, 10, 21, 23, 21, 47)
    
  7. LAST_MODIFIED_STR = "Sun, 21 Oct 2007 23:21:47 GMT"
    
  8. LAST_MODIFIED_NEWER_STR = "Mon, 18 Oct 2010 16:56:23 GMT"
    
  9. LAST_MODIFIED_INVALID_STR = "Mon, 32 Oct 2010 16:56:23 GMT"
    
  10. EXPIRED_LAST_MODIFIED_STR = "Sat, 20 Oct 2007 23:21:47 GMT"
    
  11. ETAG = '"b4246ffc4f62314ca13147c9d4f76974"'
    
  12. WEAK_ETAG = 'W/"b4246ffc4f62314ca13147c9d4f76974"'  # weak match to ETAG
    
  13. EXPIRED_ETAG = '"7fae4cd4b0f81e7d2914700043aa8ed6"'
    
  14. 
    
  15. 
    
  16. @override_settings(ROOT_URLCONF="conditional_processing.urls")
    
  17. class ConditionalGet(SimpleTestCase):
    
  18.     def assertFullResponse(self, response, check_last_modified=True, check_etag=True):
    
  19.         self.assertEqual(response.status_code, 200)
    
  20.         self.assertEqual(response.content, FULL_RESPONSE.encode())
    
  21.         if response.request["REQUEST_METHOD"] in ("GET", "HEAD"):
    
  22.             if check_last_modified:
    
  23.                 self.assertEqual(response.headers["Last-Modified"], LAST_MODIFIED_STR)
    
  24.             if check_etag:
    
  25.                 self.assertEqual(response.headers["ETag"], ETAG)
    
  26.         else:
    
  27.             self.assertNotIn("Last-Modified", response.headers)
    
  28.             self.assertNotIn("ETag", response.headers)
    
  29. 
    
  30.     def assertNotModified(self, response):
    
  31.         self.assertEqual(response.status_code, 304)
    
  32.         self.assertEqual(response.content, b"")
    
  33. 
    
  34.     def test_without_conditions(self):
    
  35.         response = self.client.get("/condition/")
    
  36.         self.assertFullResponse(response)
    
  37. 
    
  38.     def test_if_modified_since(self):
    
  39.         self.client.defaults["HTTP_IF_MODIFIED_SINCE"] = LAST_MODIFIED_STR
    
  40.         response = self.client.get("/condition/")
    
  41.         self.assertNotModified(response)
    
  42.         response = self.client.put("/condition/")
    
  43.         self.assertFullResponse(response)
    
  44.         self.client.defaults["HTTP_IF_MODIFIED_SINCE"] = LAST_MODIFIED_NEWER_STR
    
  45.         response = self.client.get("/condition/")
    
  46.         self.assertNotModified(response)
    
  47.         response = self.client.put("/condition/")
    
  48.         self.assertFullResponse(response)
    
  49.         self.client.defaults["HTTP_IF_MODIFIED_SINCE"] = LAST_MODIFIED_INVALID_STR
    
  50.         response = self.client.get("/condition/")
    
  51.         self.assertFullResponse(response)
    
  52.         self.client.defaults["HTTP_IF_MODIFIED_SINCE"] = EXPIRED_LAST_MODIFIED_STR
    
  53.         response = self.client.get("/condition/")
    
  54.         self.assertFullResponse(response)
    
  55. 
    
  56.     def test_if_unmodified_since(self):
    
  57.         self.client.defaults["HTTP_IF_UNMODIFIED_SINCE"] = LAST_MODIFIED_STR
    
  58.         response = self.client.get("/condition/")
    
  59.         self.assertFullResponse(response)
    
  60.         self.client.defaults["HTTP_IF_UNMODIFIED_SINCE"] = LAST_MODIFIED_NEWER_STR
    
  61.         response = self.client.get("/condition/")
    
  62.         self.assertFullResponse(response)
    
  63.         self.client.defaults["HTTP_IF_UNMODIFIED_SINCE"] = LAST_MODIFIED_INVALID_STR
    
  64.         response = self.client.get("/condition/")
    
  65.         self.assertFullResponse(response)
    
  66.         self.client.defaults["HTTP_IF_UNMODIFIED_SINCE"] = EXPIRED_LAST_MODIFIED_STR
    
  67.         response = self.client.get("/condition/")
    
  68.         self.assertEqual(response.status_code, 412)
    
  69. 
    
  70.     def test_if_none_match(self):
    
  71.         self.client.defaults["HTTP_IF_NONE_MATCH"] = ETAG
    
  72.         response = self.client.get("/condition/")
    
  73.         self.assertNotModified(response)
    
  74.         response = self.client.put("/condition/")
    
  75.         self.assertEqual(response.status_code, 412)
    
  76.         self.client.defaults["HTTP_IF_NONE_MATCH"] = EXPIRED_ETAG
    
  77.         response = self.client.get("/condition/")
    
  78.         self.assertFullResponse(response)
    
  79. 
    
  80.         # Several etags in If-None-Match is a bit exotic but why not?
    
  81.         self.client.defaults["HTTP_IF_NONE_MATCH"] = "%s, %s" % (ETAG, EXPIRED_ETAG)
    
  82.         response = self.client.get("/condition/")
    
  83.         self.assertNotModified(response)
    
  84. 
    
  85.     def test_weak_if_none_match(self):
    
  86.         """
    
  87.         If-None-Match comparisons use weak matching, so weak and strong ETags
    
  88.         with the same value result in a 304 response.
    
  89.         """
    
  90.         self.client.defaults["HTTP_IF_NONE_MATCH"] = ETAG
    
  91.         response = self.client.get("/condition/weak_etag/")
    
  92.         self.assertNotModified(response)
    
  93.         response = self.client.put("/condition/weak_etag/")
    
  94.         self.assertEqual(response.status_code, 412)
    
  95. 
    
  96.         self.client.defaults["HTTP_IF_NONE_MATCH"] = WEAK_ETAG
    
  97.         response = self.client.get("/condition/weak_etag/")
    
  98.         self.assertNotModified(response)
    
  99.         response = self.client.put("/condition/weak_etag/")
    
  100.         self.assertEqual(response.status_code, 412)
    
  101.         response = self.client.get("/condition/")
    
  102.         self.assertNotModified(response)
    
  103.         response = self.client.put("/condition/")
    
  104.         self.assertEqual(response.status_code, 412)
    
  105. 
    
  106.     def test_all_if_none_match(self):
    
  107.         self.client.defaults["HTTP_IF_NONE_MATCH"] = "*"
    
  108.         response = self.client.get("/condition/")
    
  109.         self.assertNotModified(response)
    
  110.         response = self.client.put("/condition/")
    
  111.         self.assertEqual(response.status_code, 412)
    
  112.         response = self.client.get("/condition/no_etag/")
    
  113.         self.assertFullResponse(response, check_last_modified=False, check_etag=False)
    
  114. 
    
  115.     def test_if_match(self):
    
  116.         self.client.defaults["HTTP_IF_MATCH"] = ETAG
    
  117.         response = self.client.put("/condition/")
    
  118.         self.assertFullResponse(response)
    
  119.         self.client.defaults["HTTP_IF_MATCH"] = EXPIRED_ETAG
    
  120.         response = self.client.put("/condition/")
    
  121.         self.assertEqual(response.status_code, 412)
    
  122. 
    
  123.     def test_weak_if_match(self):
    
  124.         """
    
  125.         If-Match comparisons use strong matching, so any comparison involving
    
  126.         a weak ETag return a 412 response.
    
  127.         """
    
  128.         self.client.defaults["HTTP_IF_MATCH"] = ETAG
    
  129.         response = self.client.get("/condition/weak_etag/")
    
  130.         self.assertEqual(response.status_code, 412)
    
  131. 
    
  132.         self.client.defaults["HTTP_IF_MATCH"] = WEAK_ETAG
    
  133.         response = self.client.get("/condition/weak_etag/")
    
  134.         self.assertEqual(response.status_code, 412)
    
  135.         response = self.client.get("/condition/")
    
  136.         self.assertEqual(response.status_code, 412)
    
  137. 
    
  138.     def test_all_if_match(self):
    
  139.         self.client.defaults["HTTP_IF_MATCH"] = "*"
    
  140.         response = self.client.get("/condition/")
    
  141.         self.assertFullResponse(response)
    
  142.         response = self.client.get("/condition/no_etag/")
    
  143.         self.assertEqual(response.status_code, 412)
    
  144. 
    
  145.     def test_both_headers(self):
    
  146.         # see https://tools.ietf.org/html/rfc7232#section-6
    
  147.         self.client.defaults["HTTP_IF_MODIFIED_SINCE"] = LAST_MODIFIED_STR
    
  148.         self.client.defaults["HTTP_IF_NONE_MATCH"] = ETAG
    
  149.         response = self.client.get("/condition/")
    
  150.         self.assertNotModified(response)
    
  151. 
    
  152.         self.client.defaults["HTTP_IF_MODIFIED_SINCE"] = EXPIRED_LAST_MODIFIED_STR
    
  153.         self.client.defaults["HTTP_IF_NONE_MATCH"] = ETAG
    
  154.         response = self.client.get("/condition/")
    
  155.         self.assertNotModified(response)
    
  156. 
    
  157.         self.client.defaults["HTTP_IF_MODIFIED_SINCE"] = LAST_MODIFIED_STR
    
  158.         self.client.defaults["HTTP_IF_NONE_MATCH"] = EXPIRED_ETAG
    
  159.         response = self.client.get("/condition/")
    
  160.         self.assertFullResponse(response)
    
  161. 
    
  162.         self.client.defaults["HTTP_IF_MODIFIED_SINCE"] = EXPIRED_LAST_MODIFIED_STR
    
  163.         self.client.defaults["HTTP_IF_NONE_MATCH"] = EXPIRED_ETAG
    
  164.         response = self.client.get("/condition/")
    
  165.         self.assertFullResponse(response)
    
  166. 
    
  167.     def test_both_headers_2(self):
    
  168.         self.client.defaults["HTTP_IF_UNMODIFIED_SINCE"] = LAST_MODIFIED_STR
    
  169.         self.client.defaults["HTTP_IF_MATCH"] = ETAG
    
  170.         response = self.client.get("/condition/")
    
  171.         self.assertFullResponse(response)
    
  172. 
    
  173.         self.client.defaults["HTTP_IF_UNMODIFIED_SINCE"] = EXPIRED_LAST_MODIFIED_STR
    
  174.         self.client.defaults["HTTP_IF_MATCH"] = ETAG
    
  175.         response = self.client.get("/condition/")
    
  176.         self.assertFullResponse(response)
    
  177. 
    
  178.         self.client.defaults["HTTP_IF_UNMODIFIED_SINCE"] = EXPIRED_LAST_MODIFIED_STR
    
  179.         self.client.defaults["HTTP_IF_MATCH"] = EXPIRED_ETAG
    
  180.         response = self.client.get("/condition/")
    
  181.         self.assertEqual(response.status_code, 412)
    
  182. 
    
  183.         self.client.defaults["HTTP_IF_UNMODIFIED_SINCE"] = LAST_MODIFIED_STR
    
  184.         self.client.defaults["HTTP_IF_MATCH"] = EXPIRED_ETAG
    
  185.         response = self.client.get("/condition/")
    
  186.         self.assertEqual(response.status_code, 412)
    
  187. 
    
  188.     def test_single_condition_1(self):
    
  189.         self.client.defaults["HTTP_IF_MODIFIED_SINCE"] = LAST_MODIFIED_STR
    
  190.         response = self.client.get("/condition/last_modified/")
    
  191.         self.assertNotModified(response)
    
  192.         response = self.client.get("/condition/etag/")
    
  193.         self.assertFullResponse(response, check_last_modified=False)
    
  194. 
    
  195.     def test_single_condition_2(self):
    
  196.         self.client.defaults["HTTP_IF_NONE_MATCH"] = ETAG
    
  197.         response = self.client.get("/condition/etag/")
    
  198.         self.assertNotModified(response)
    
  199.         response = self.client.get("/condition/last_modified/")
    
  200.         self.assertFullResponse(response, check_etag=False)
    
  201. 
    
  202.     def test_single_condition_3(self):
    
  203.         self.client.defaults["HTTP_IF_MODIFIED_SINCE"] = EXPIRED_LAST_MODIFIED_STR
    
  204.         response = self.client.get("/condition/last_modified/")
    
  205.         self.assertFullResponse(response, check_etag=False)
    
  206. 
    
  207.     def test_single_condition_4(self):
    
  208.         self.client.defaults["HTTP_IF_NONE_MATCH"] = EXPIRED_ETAG
    
  209.         response = self.client.get("/condition/etag/")
    
  210.         self.assertFullResponse(response, check_last_modified=False)
    
  211. 
    
  212.     def test_single_condition_5(self):
    
  213.         self.client.defaults["HTTP_IF_MODIFIED_SINCE"] = LAST_MODIFIED_STR
    
  214.         response = self.client.get("/condition/last_modified2/")
    
  215.         self.assertNotModified(response)
    
  216.         response = self.client.get("/condition/etag2/")
    
  217.         self.assertFullResponse(response, check_last_modified=False)
    
  218. 
    
  219.     def test_single_condition_6(self):
    
  220.         self.client.defaults["HTTP_IF_NONE_MATCH"] = ETAG
    
  221.         response = self.client.get("/condition/etag2/")
    
  222.         self.assertNotModified(response)
    
  223.         response = self.client.get("/condition/last_modified2/")
    
  224.         self.assertFullResponse(response, check_etag=False)
    
  225. 
    
  226.     def test_single_condition_7(self):
    
  227.         self.client.defaults["HTTP_IF_UNMODIFIED_SINCE"] = EXPIRED_LAST_MODIFIED_STR
    
  228.         response = self.client.get("/condition/last_modified/")
    
  229.         self.assertEqual(response.status_code, 412)
    
  230.         response = self.client.get("/condition/etag/")
    
  231.         self.assertEqual(response.status_code, 412)
    
  232. 
    
  233.     def test_single_condition_8(self):
    
  234.         self.client.defaults["HTTP_IF_UNMODIFIED_SINCE"] = LAST_MODIFIED_STR
    
  235.         response = self.client.get("/condition/last_modified/")
    
  236.         self.assertFullResponse(response, check_etag=False)
    
  237. 
    
  238.     def test_single_condition_9(self):
    
  239.         self.client.defaults["HTTP_IF_UNMODIFIED_SINCE"] = EXPIRED_LAST_MODIFIED_STR
    
  240.         response = self.client.get("/condition/last_modified2/")
    
  241.         self.assertEqual(response.status_code, 412)
    
  242.         response = self.client.get("/condition/etag2/")
    
  243.         self.assertEqual(response.status_code, 412)
    
  244. 
    
  245.     def test_single_condition_head(self):
    
  246.         self.client.defaults["HTTP_IF_MODIFIED_SINCE"] = LAST_MODIFIED_STR
    
  247.         response = self.client.head("/condition/")
    
  248.         self.assertNotModified(response)
    
  249. 
    
  250.     def test_unquoted(self):
    
  251.         """
    
  252.         The same quoted ETag should be set on the header regardless of whether
    
  253.         etag_func() in condition() returns a quoted or an unquoted ETag.
    
  254.         """
    
  255.         response_quoted = self.client.get("/condition/etag/")
    
  256.         response_unquoted = self.client.get("/condition/unquoted_etag/")
    
  257.         self.assertEqual(response_quoted["ETag"], response_unquoted["ETag"])
    
  258. 
    
  259.     # It's possible that the matching algorithm could use the wrong value even
    
  260.     # if the ETag header is set correctly correctly (as tested by
    
  261.     # test_unquoted()), so check that the unquoted value is matched.
    
  262.     def test_unquoted_if_none_match(self):
    
  263.         self.client.defaults["HTTP_IF_NONE_MATCH"] = ETAG
    
  264.         response = self.client.get("/condition/unquoted_etag/")
    
  265.         self.assertNotModified(response)
    
  266.         response = self.client.put("/condition/unquoted_etag/")
    
  267.         self.assertEqual(response.status_code, 412)
    
  268.         self.client.defaults["HTTP_IF_NONE_MATCH"] = EXPIRED_ETAG
    
  269.         response = self.client.get("/condition/unquoted_etag/")
    
  270.         self.assertFullResponse(response, check_last_modified=False)
    
  271. 
    
  272.     def test_invalid_etag(self):
    
  273.         self.client.defaults["HTTP_IF_NONE_MATCH"] = '"""'
    
  274.         response = self.client.get("/condition/etag/")
    
  275.         self.assertFullResponse(response, check_last_modified=False)