|
1 import re |
|
2 import urllib |
|
3 from email.Utils import formatdate |
|
4 |
|
5 from django.utils.encoding import smart_str, force_unicode |
|
6 from django.utils.functional import allow_lazy |
|
7 |
|
8 ETAG_MATCH = re.compile(r'(?:W/)?"((?:\\.|[^"])*)"') |
|
9 |
|
10 def urlquote(url, safe='/'): |
|
11 """ |
|
12 A version of Python's urllib.quote() function that can operate on unicode |
|
13 strings. The url is first UTF-8 encoded before quoting. The returned string |
|
14 can safely be used as part of an argument to a subsequent iri_to_uri() call |
|
15 without double-quoting occurring. |
|
16 """ |
|
17 return force_unicode(urllib.quote(smart_str(url), safe)) |
|
18 |
|
19 urlquote = allow_lazy(urlquote, unicode) |
|
20 |
|
21 def urlquote_plus(url, safe=''): |
|
22 """ |
|
23 A version of Python's urllib.quote_plus() function that can operate on |
|
24 unicode strings. The url is first UTF-8 encoded before quoting. The |
|
25 returned string can safely be used as part of an argument to a subsequent |
|
26 iri_to_uri() call without double-quoting occurring. |
|
27 """ |
|
28 return force_unicode(urllib.quote_plus(smart_str(url), safe)) |
|
29 urlquote_plus = allow_lazy(urlquote_plus, unicode) |
|
30 |
|
31 def urlencode(query, doseq=0): |
|
32 """ |
|
33 A version of Python's urllib.urlencode() function that can operate on |
|
34 unicode strings. The parameters are first case to UTF-8 encoded strings and |
|
35 then encoded as per normal. |
|
36 """ |
|
37 if hasattr(query, 'items'): |
|
38 query = query.items() |
|
39 return urllib.urlencode( |
|
40 [(smart_str(k), |
|
41 isinstance(v, (list,tuple)) and [smart_str(i) for i in v] or smart_str(v)) |
|
42 for k, v in query], |
|
43 doseq) |
|
44 |
|
45 def cookie_date(epoch_seconds=None): |
|
46 """ |
|
47 Formats the time to ensure compatibility with Netscape's cookie standard. |
|
48 |
|
49 Accepts a floating point number expressed in seconds since the epoch, in |
|
50 UTC - such as that outputted by time.time(). If set to None, defaults to |
|
51 the current time. |
|
52 |
|
53 Outputs a string in the format 'Wdy, DD-Mon-YYYY HH:MM:SS GMT'. |
|
54 """ |
|
55 rfcdate = formatdate(epoch_seconds) |
|
56 return '%s-%s-%s GMT' % (rfcdate[:7], rfcdate[8:11], rfcdate[12:25]) |
|
57 |
|
58 def http_date(epoch_seconds=None): |
|
59 """ |
|
60 Formats the time to match the RFC1123 date format as specified by HTTP |
|
61 RFC2616 section 3.3.1. |
|
62 |
|
63 Accepts a floating point number expressed in seconds since the epoch, in |
|
64 UTC - such as that outputted by time.time(). If set to None, defaults to |
|
65 the current time. |
|
66 |
|
67 Outputs a string in the format 'Wdy, DD Mon YYYY HH:MM:SS GMT'. |
|
68 """ |
|
69 rfcdate = formatdate(epoch_seconds) |
|
70 return '%s GMT' % rfcdate[:25] |
|
71 |
|
72 # Base 36 functions: useful for generating compact URLs |
|
73 |
|
74 def base36_to_int(s): |
|
75 """ |
|
76 Convertd a base 36 string to an integer |
|
77 """ |
|
78 return int(s, 36) |
|
79 |
|
80 def int_to_base36(i): |
|
81 """ |
|
82 Converts an integer to a base36 string |
|
83 """ |
|
84 digits = "0123456789abcdefghijklmnopqrstuvwxyz" |
|
85 factor = 0 |
|
86 # Find starting factor |
|
87 while True: |
|
88 factor += 1 |
|
89 if i < 36 ** factor: |
|
90 factor -= 1 |
|
91 break |
|
92 base36 = [] |
|
93 # Construct base36 representation |
|
94 while factor >= 0: |
|
95 j = 36 ** factor |
|
96 base36.append(digits[i / j]) |
|
97 i = i % j |
|
98 factor -= 1 |
|
99 return ''.join(base36) |
|
100 |
|
101 def parse_etags(etag_str): |
|
102 """ |
|
103 Parses a string with one or several etags passed in If-None-Match and |
|
104 If-Match headers by the rules in RFC 2616. Returns a list of etags |
|
105 without surrounding double quotes (") and unescaped from \<CHAR>. |
|
106 """ |
|
107 etags = ETAG_MATCH.findall(etag_str) |
|
108 if not etags: |
|
109 # etag_str has wrong format, treat it as an opaque string then |
|
110 return [etag_str] |
|
111 etags = [e.decode('string_escape') for e in etags] |
|
112 return etags |
|
113 |
|
114 def quote_etag(etag): |
|
115 """ |
|
116 Wraps a string in double quotes escaping contents as necesary. |
|
117 """ |
|
118 return '"%s"' % etag.replace('\\', '\\\\').replace('"', '\\"') |
|
119 |