|
3 | 3 | import tempfile |
4 | 4 | import unittest |
5 | 5 |
|
6 | | -from flask import Flask, render_template |
| 6 | +from flask import Flask, make_response, render_template, request |
7 | 7 | from flask_caching import Cache |
8 | 8 |
|
9 | 9 | from flask_compress import Compress, DictCache |
@@ -91,6 +91,13 @@ def test_quality_level_default_zstd(self): |
91 | 91 | """Tests COMPRESS_ZSTD_LEVEL default value is correctly set.""" |
92 | 92 | self.assertEqual(self.app.config["COMPRESS_ZSTD_LEVEL"], 3) |
93 | 93 |
|
| 94 | + def test_evaluate_conditional_request(self): |
| 95 | + """Tests COMPRESS_EVALUATE_CONDITIONAL_REQUEST default value |
| 96 | + is correctly set.""" |
| 97 | + self.assertEqual( |
| 98 | + self.app.config["COMPRESS_EVALUATE_CONDITIONAL_REQUEST"], False |
| 99 | + ) |
| 100 | + |
94 | 101 |
|
95 | 102 | class InitTests(unittest.TestCase): |
96 | 103 | def setUp(self): |
@@ -570,5 +577,135 @@ def test_compression(self): |
570 | 577 | self.assertEqual(self.cache_key_calls, 2) |
571 | 578 |
|
572 | 579 |
|
| 580 | +class ETagTests(unittest.TestCase): |
| 581 | + def setUp(self): |
| 582 | + self.app = Flask(__name__) |
| 583 | + self.app.testing = True |
| 584 | + self.app.config["COMPRESS_ALGORITHM"] = ["gzip"] |
| 585 | + self.app.config["COMPRESS_MIN_SIZE"] = 1 |
| 586 | + |
| 587 | + Compress(self.app) |
| 588 | + |
| 589 | + @self.app.route("/strong/") |
| 590 | + def strong(): |
| 591 | + rv = make_response(render_template("large.html")) |
| 592 | + rv.set_etag("abc123", weak=False) |
| 593 | + return rv.make_conditional(request) |
| 594 | + |
| 595 | + @self.app.route("/strong-compress-conditional/") |
| 596 | + def strong_compress_conditional(): |
| 597 | + rv = make_response(render_template("large.html")) |
| 598 | + rv.set_etag("abc123", weak=False) |
| 599 | + return rv |
| 600 | + |
| 601 | + @self.app.route("/weak/") |
| 602 | + def weak(): |
| 603 | + rv = make_response(render_template("large.html")) |
| 604 | + rv.set_etag("abc123", weak=True) |
| 605 | + return rv.make_conditional(request) |
| 606 | + |
| 607 | + @self.app.route("/weak-compress-conditional/") |
| 608 | + def weak_compress_conditional(): |
| 609 | + rv = make_response(render_template("large.html")) |
| 610 | + rv.set_etag("abc123", weak=True) |
| 611 | + return rv |
| 612 | + |
| 613 | + def test_strong_etag_is_mutated_with_suffix_and_remains_strong(self): |
| 614 | + client = self.app.test_client() |
| 615 | + r = client.get("/strong/", headers=[("Accept-Encoding", "gzip")]) |
| 616 | + self.assertEqual(r.status_code, 200) |
| 617 | + self.assertEqual(r.headers.get("Content-Encoding"), "gzip") |
| 618 | + |
| 619 | + tag, is_weak = r.get_etag() |
| 620 | + self.assertFalse(is_weak) |
| 621 | + self.assertEqual(tag, "abc123:gzip") |
| 622 | + self.assertEqual(int(r.headers["Content-Length"]), len(r.data)) |
| 623 | + |
| 624 | + def test_weak_etag_is_preserved(self): |
| 625 | + client = self.app.test_client() |
| 626 | + r = client.get("/weak/", headers=[("Accept-Encoding", "gzip")]) |
| 627 | + self.assertEqual(r.status_code, 200) |
| 628 | + self.assertEqual(r.headers.get("Content-Encoding"), "gzip") |
| 629 | + |
| 630 | + tag, is_weak = r.get_etag() |
| 631 | + self.assertTrue(is_weak) |
| 632 | + # No :gzip suffix when flag is False |
| 633 | + self.assertEqual(tag, "abc123") |
| 634 | + |
| 635 | + def test_conditional_get_uses_strong_compressed_representation(self): |
| 636 | + client = self.app.test_client() |
| 637 | + r1 = client.get("/strong/", headers=[("Accept-Encoding", "gzip")]) |
| 638 | + |
| 639 | + r2 = client.get( |
| 640 | + "/strong/", |
| 641 | + headers=[ |
| 642 | + ("Accept-Encoding", "gzip"), |
| 643 | + ("If-None-Match", r1.headers["ETag"]), |
| 644 | + ], |
| 645 | + ) |
| 646 | + # This is the current behavior that breaks make_conditional |
| 647 | + # strong etags due rewrite at after_request |
| 648 | + # We would expect a 304 but it does not because of etag mismatch |
| 649 | + self.assertEqual(r2.status_code, 200) |
| 650 | + |
| 651 | + def test_conditional_get_uses_weak_compressed_representation(self): |
| 652 | + client = self.app.test_client() |
| 653 | + r1 = client.get("/weak/", headers=[("Accept-Encoding", "gzip")]) |
| 654 | + etag_header = r1.headers["ETag"] |
| 655 | + |
| 656 | + r2 = client.get( |
| 657 | + "/weak/", |
| 658 | + headers=[("Accept-Encoding", "gzip"), ("If-None-Match", etag_header)], |
| 659 | + ) |
| 660 | + # This is the new behaviour we would expect by not mutating |
| 661 | + # the weak etags at after_request |
| 662 | + self.assertEqual(r2.status_code, 304) |
| 663 | + self.assertEqual(r2.headers.get("ETag"), etag_header) |
| 664 | + self.assertNotIn("Content-Encoding", r2.headers) |
| 665 | + self.assertEqual(len(r2.get_data()), 0) |
| 666 | + |
| 667 | + def test_conditional_get_uses_strong_compressed_representation_evaluate_conditional( |
| 668 | + self, |
| 669 | + ): |
| 670 | + self.app.config["COMPRESS_EVALUATE_CONDITIONAL_REQUEST"] = True |
| 671 | + client = self.app.test_client() |
| 672 | + r1 = client.get( |
| 673 | + "/strong-compress-conditional/", headers=[("Accept-Encoding", "gzip")] |
| 674 | + ) |
| 675 | + etag_header = r1.headers["ETag"] |
| 676 | + |
| 677 | + r2 = client.get( |
| 678 | + "/strong-compress-conditional/", |
| 679 | + headers=[("Accept-Encoding", "gzip"), ("If-None-Match", etag_header)], |
| 680 | + ) |
| 681 | + # This is the new behaviour we would expect after evaluating |
| 682 | + # flask make_conditional at after_request |
| 683 | + self.assertEqual(r2.status_code, 304) |
| 684 | + self.assertEqual(r2.headers.get("ETag"), etag_header) |
| 685 | + self.assertNotIn("Content-Encoding", r2.headers) |
| 686 | + self.assertEqual(len(r2.get_data()), 0) |
| 687 | + |
| 688 | + def test_conditional_get_uses_weak_compressed_representation_evaluate_conditional( |
| 689 | + self, |
| 690 | + ): |
| 691 | + self.app.config["COMPRESS_EVALUATE_CONDITIONAL_REQUEST"] = True |
| 692 | + client = self.app.test_client() |
| 693 | + r1 = client.get( |
| 694 | + "/weak-compress-conditional/", headers=[("Accept-Encoding", "gzip")] |
| 695 | + ) |
| 696 | + etag_header = r1.headers["ETag"] |
| 697 | + |
| 698 | + r2 = client.get( |
| 699 | + "/weak-compress-conditional/", |
| 700 | + headers=[("Accept-Encoding", "gzip"), ("If-None-Match", etag_header)], |
| 701 | + ) |
| 702 | + # This is the new behaviour we would expect after evaluating |
| 703 | + # flask make_conditional at after_request |
| 704 | + self.assertEqual(r2.status_code, 304) |
| 705 | + self.assertEqual(r2.headers.get("ETag"), etag_header) |
| 706 | + self.assertNotIn("Content-Encoding", r2.headers) |
| 707 | + self.assertEqual(len(r2.get_data()), 0) |
| 708 | + |
| 709 | + |
573 | 710 | if __name__ == "__main__": |
574 | 711 | unittest.main() |
0 commit comments