|
4 | 4 | import binascii
|
5 | 5 | import cgi
|
6 | 6 | import collections
|
| 7 | +import datetime |
7 | 8 | import http.cookies
|
8 | 9 | import io
|
9 | 10 | import json
|
| 11 | +import math |
| 12 | +import time |
10 | 13 | import warnings
|
11 | 14 |
|
12 |
| -from urllib.parse import urlsplit, parse_qsl, unquote |
| 15 | +from email.utils import parsedate |
13 | 16 | from types import MappingProxyType
|
| 17 | +from urllib.parse import urlsplit, parse_qsl, unquote |
14 | 18 |
|
15 | 19 | from . import hdrs
|
16 | 20 | from .helpers import reify
|
@@ -65,6 +69,33 @@ def content_length(self, _CONTENT_LENGTH=hdrs.CONTENT_LENGTH):
|
65 | 69 | else:
|
66 | 70 | return int(l)
|
67 | 71 |
|
| 72 | + @property |
| 73 | + def if_modified_since(self, _IF_MODIFIED_SINCE=hdrs.IF_MODIFIED_SINCE): |
| 74 | + """The value of If-Modified-Since HTTP header, or None. |
| 75 | +
|
| 76 | + This header is represented as a `datetime` object. |
| 77 | + """ |
| 78 | + httpdate = self.headers.get(_IF_MODIFIED_SINCE) |
| 79 | + if httpdate is not None: |
| 80 | + timetuple = parsedate(httpdate) |
| 81 | + if timetuple is not None: |
| 82 | + return datetime.datetime(*timetuple[:6], |
| 83 | + tzinfo=datetime.timezone.utc) |
| 84 | + return None |
| 85 | + |
| 86 | + @property |
| 87 | + def last_modified(self, _LAST_MODIFIED=hdrs.LAST_MODIFIED): |
| 88 | + """The value of Last-Modified HTTP header, or None. |
| 89 | +
|
| 90 | + This header is represented as a `datetime` object. |
| 91 | + """ |
| 92 | + httpdate = self.headers.get(_LAST_MODIFIED) |
| 93 | + if httpdate is not None: |
| 94 | + timetuple = parsedate(httpdate) |
| 95 | + if timetuple is not None: |
| 96 | + return datetime.datetime(*timetuple[:6], |
| 97 | + tzinfo=datetime.timezone.utc) |
| 98 | + return None |
68 | 99 |
|
69 | 100 | FileField = collections.namedtuple('Field', 'name filename file content_type')
|
70 | 101 |
|
@@ -513,6 +544,25 @@ def charset(self, value):
|
513 | 544 | self._content_dict['charset'] = str(value).lower()
|
514 | 545 | self._generate_content_type_header()
|
515 | 546 |
|
| 547 | + @property |
| 548 | + def last_modified(self): |
| 549 | + # Just a placeholder for adding setter |
| 550 | + return super().last_modified |
| 551 | + |
| 552 | + @last_modified.setter |
| 553 | + def last_modified(self, value): |
| 554 | + if value is None: |
| 555 | + if hdrs.LAST_MODIFIED in self.headers: |
| 556 | + del self.headers[hdrs.LAST_MODIFIED] |
| 557 | + elif isinstance(value, (int, float)): |
| 558 | + self.headers[hdrs.LAST_MODIFIED] = time.strftime( |
| 559 | + "%a, %d %b %Y %H:%M:%S GMT", time.gmtime(math.ceil(value))) |
| 560 | + elif isinstance(value, datetime.datetime): |
| 561 | + self.headers[hdrs.LAST_MODIFIED] = time.strftime( |
| 562 | + "%a, %d %b %Y %H:%M:%S GMT", value.utctimetuple()) |
| 563 | + elif isinstance(value, str): |
| 564 | + self.headers[hdrs.LAST_MODIFIED] = value |
| 565 | + |
516 | 566 | def _generate_content_type_header(self, CONTENT_TYPE=hdrs.CONTENT_TYPE):
|
517 | 567 | params = '; '.join("%s=%s" % i for i in self._content_dict.items())
|
518 | 568 | if params:
|
|
0 commit comments