Mercurial > p > roundup > code
changeset 8020:60c98a8a23bd
fix: make If-None-Match work for static file (@@file) case
Found by Redbot testing.
| author | John Rouillard <rouilj@ieee.org> |
|---|---|
| date | Sun, 02 Jun 2024 20:35:47 -0400 |
| parents | 16cc72cd9c17 |
| children | 98429efb80cb |
| files | CHANGES.txt roundup/cgi/client.py test/test_liveserver.py |
| diffstat | 3 files changed, 117 insertions(+), 1 deletions(-) [+] |
line wrap: on
line diff
--- a/CHANGES.txt Sun Jun 02 18:22:53 2024 -0400 +++ b/CHANGES.txt Sun Jun 02 20:35:47 2024 -0400 @@ -164,6 +164,8 @@ - Send Vary: Accept-Encoding on any file that could be compressed even if the file is not encoded/compressed. Found by Redbot testing. (John Rouillard) +- make If-None-Match work for static file (@@file) case. Found by + Redbot testing (John Rouillard) Features:
--- a/roundup/cgi/client.py Sun Jun 02 18:22:53 2024 -0400 +++ b/roundup/cgi/client.py Sun Jun 02 20:35:47 2024 -0400 @@ -2716,10 +2716,32 @@ # Hence the intermediate proxy should/must match # Accept-Encoding and ETag to determine whether to return # a 304 or report cache miss and fetch from origin server. + # + # RFC 9110 8.8.3.3 shows a different strong entity tag + # generated for gzip and non gzip replies. etag = '"%x-%x-%x"' % (stat_info[stat.ST_INO], length, stat_info[stat.ST_MTIME]) self.setHeader("ETag", etag) + + inm = self.request.headers.get('If-None-Match') + if (inm): + inm_etags = inm.split(',') + inm_etags = [tag.strip() for tag in inm_etags] + if etag in inm_etags: + self.setHeader('ETag', etag) + self.setVary('Accept-Encoding') + raise NotModified + + # need to check for etag-compression_code: + # a41932-8b5-664ce93d-zstd or a41932-8b5-664ce93d-gzip + tag_prefix = etag[:-1] + '-' + for inm_etag in inm_etags: + if inm_etag.startswith(tag_prefix): + self.setHeader('ETag', inm_etag) + self.setVary('Accept-Encoding') + raise NotModified + # RFC 2616 14.5: Accept-Ranges # # Let the client know that we will accept range requests.
--- a/test/test_liveserver.py Sun Jun 02 18:22:53 2024 -0400 +++ b/test/test_liveserver.py Sun Jun 02 20:35:47 2024 -0400 @@ -707,9 +707,101 @@ self.assertEqual(3, len(json.loads(f.content)['data']['collection'])) + def test_inm(self): + '''retrieve the user_utils.js file without an if-none-match etag + header, a bad if-none-match header and valid single and + multiple values. + ''' + f = requests.get(self.url_base() + '/@@file/user_utils.js', + headers = { 'Accept-Encoding': 'gzip, foo', + 'Accept': '*/*'}) + print(f.status_code) + print(f.headers) + + self.assertEqual(f.status_code, 200) + expected = { 'Content-Type': self.js_mime_type, + 'Content-Encoding': 'gzip', + 'Vary': 'Accept-Encoding', + } + + # use dict comprehension to remove fields like date, + # etag etc. from f.headers. + self.assertDictEqual({ key: value for (key, value) in + f.headers.items() if key in expected }, + expected) + + # use etag in previous response + etag = f.headers['etag'] + f = requests.get(self.url_base() + '/@@file/user_utils.js', + headers = { 'Accept-Encoding': 'gzip, foo', + 'If-None-Match': etag, + 'Accept': '*/*'}) + print(f.status_code) + print(f.headers) + + self.assertEqual(f.status_code, 304) + expected = { 'Vary': 'Accept-Encoding', + 'Content-Length': '0', + 'ETag': etag, + 'Vary': 'Accept-Encoding' + } + + # use dict comprehension to remove fields like date, server, + # etc. from f.headers. + self.assertDictEqual({ key: value for (key, value) in f.headers.items() if key in expected }, expected) + + # test again with etag supplied w/o content-encoding + # and multiple etags + self.assertTrue(etag.endswith('-gzip"')) + + # keep etag intact. Used below. + base_etag = etag[:-6] + '"' + + all_etags = ( + '"a41932-8b5-664ce93d", %s", "a41932-8b5-664ce93d-br"' % + base_etag + ) + + f = requests.get(self.url_base() + '/@@file/user_utils.js', + headers = { 'Accept-Encoding': 'gzip, foo', + 'If-None-Match': base_etag, + 'Accept': '*/*'}) + print(f.status_code) + print(f.headers) + + self.assertEqual(f.status_code, 304) + expected = { 'Vary': 'Accept-Encoding', + 'Content-Length': '0', + 'ETag': base_etag, + 'Vary': 'Accept-Encoding' + } + + # use dict comprehension to remove fields like date, server, + # etc. from f.headers. + self.assertDictEqual({ key: value for (key, value) in f.headers.items() if key in expected }, expected) + + + # test with bad etag + f = requests.get(self.url_base() + '/@@file/user_utils.js', + headers = { 'Accept-Encoding': 'gzip, foo', + 'If-None-Match': '"a41932-8b5-664ce93d"', + 'Accept': '*/*'}) + print(f.status_code) + print(f.headers) + + self.assertEqual(f.status_code, 200) + expected = { 'Content-Type': self.js_mime_type, + 'ETag': etag, + 'Content-Encoding': 'gzip', + 'Vary': 'Accept-Encoding', + } + + # use dict comprehension to remove fields like date, server, + # etc. from f.headers. + self.assertDictEqual({ key: value for (key, value) in f.headers.items() if key in expected }, expected) def test_ims(self): - ''' retreive the user_utils.js file with old and new + ''' retrieve the user_utils.js file with old and new if-modified-since timestamps. ''' from datetime import datetime
