Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(473)

Side by Side Diff: third_party/google-endpoints/future/backports/http/cookies.py

Issue 2666783008: Add google-endpoints to third_party/. (Closed)
Patch Set: Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 ####
2 # Copyright 2000 by Timothy O'Malley <timo@alum.mit.edu>
3 #
4 # All Rights Reserved
5 #
6 # Permission to use, copy, modify, and distribute this software
7 # and its documentation for any purpose and without fee is hereby
8 # granted, provided that the above copyright notice appear in all
9 # copies and that both that copyright notice and this permission
10 # notice appear in supporting documentation, and that the name of
11 # Timothy O'Malley not be used in advertising or publicity
12 # pertaining to distribution of the software without specific, written
13 # prior permission.
14 #
15 # Timothy O'Malley DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
16 # SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
17 # AND FITNESS, IN NO EVENT SHALL Timothy O'Malley BE LIABLE FOR
18 # ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19 # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
20 # WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
21 # ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
22 # PERFORMANCE OF THIS SOFTWARE.
23 #
24 ####
25 #
26 # Id: Cookie.py,v 2.29 2000/08/23 05:28:49 timo Exp
27 # by Timothy O'Malley <timo@alum.mit.edu>
28 #
29 # Cookie.py is a Python module for the handling of HTTP
30 # cookies as a Python dictionary. See RFC 2109 for more
31 # information on cookies.
32 #
33 # The original idea to treat Cookies as a dictionary came from
34 # Dave Mitchell (davem@magnet.com) in 1995, when he released the
35 # first version of nscookie.py.
36 #
37 ####
38
39 r"""
40 http.cookies module ported to python-future from Py3.3
41
42 Here's a sample session to show how to use this module.
43 At the moment, this is the only documentation.
44
45 The Basics
46 ----------
47
48 Importing is easy...
49
50 >>> from http import cookies
51
52 Most of the time you start by creating a cookie.
53
54 >>> C = cookies.SimpleCookie()
55
56 Once you've created your Cookie, you can add values just as if it were
57 a dictionary.
58
59 >>> C = cookies.SimpleCookie()
60 >>> C["fig"] = "newton"
61 >>> C["sugar"] = "wafer"
62 >>> C.output()
63 'Set-Cookie: fig=newton\r\nSet-Cookie: sugar=wafer'
64
65 Notice that the printable representation of a Cookie is the
66 appropriate format for a Set-Cookie: header. This is the
67 default behavior. You can change the header and printed
68 attributes by using the .output() function
69
70 >>> C = cookies.SimpleCookie()
71 >>> C["rocky"] = "road"
72 >>> C["rocky"]["path"] = "/cookie"
73 >>> print(C.output(header="Cookie:"))
74 Cookie: rocky=road; Path=/cookie
75 >>> print(C.output(attrs=[], header="Cookie:"))
76 Cookie: rocky=road
77
78 The load() method of a Cookie extracts cookies from a string. In a
79 CGI script, you would use this method to extract the cookies from the
80 HTTP_COOKIE environment variable.
81
82 >>> C = cookies.SimpleCookie()
83 >>> C.load("chips=ahoy; vienna=finger")
84 >>> C.output()
85 'Set-Cookie: chips=ahoy\r\nSet-Cookie: vienna=finger'
86
87 The load() method is darn-tootin smart about identifying cookies
88 within a string. Escaped quotation marks, nested semicolons, and other
89 such trickeries do not confuse it.
90
91 >>> C = cookies.SimpleCookie()
92 >>> C.load('keebler="E=everybody; L=\\"Loves\\"; fudge=\\012;";')
93 >>> print(C)
94 Set-Cookie: keebler="E=everybody; L=\"Loves\"; fudge=\012;"
95
96 Each element of the Cookie also supports all of the RFC 2109
97 Cookie attributes. Here's an example which sets the Path
98 attribute.
99
100 >>> C = cookies.SimpleCookie()
101 >>> C["oreo"] = "doublestuff"
102 >>> C["oreo"]["path"] = "/"
103 >>> print(C)
104 Set-Cookie: oreo=doublestuff; Path=/
105
106 Each dictionary element has a 'value' attribute, which gives you
107 back the value associated with the key.
108
109 >>> C = cookies.SimpleCookie()
110 >>> C["twix"] = "none for you"
111 >>> C["twix"].value
112 'none for you'
113
114 The SimpleCookie expects that all values should be standard strings.
115 Just to be sure, SimpleCookie invokes the str() builtin to convert
116 the value to a string, when the values are set dictionary-style.
117
118 >>> C = cookies.SimpleCookie()
119 >>> C["number"] = 7
120 >>> C["string"] = "seven"
121 >>> C["number"].value
122 '7'
123 >>> C["string"].value
124 'seven'
125 >>> C.output()
126 'Set-Cookie: number=7\r\nSet-Cookie: string=seven'
127
128 Finis.
129 """
130 from __future__ import unicode_literals
131 from __future__ import print_function
132 from __future__ import division
133 from __future__ import absolute_import
134 from future.builtins import chr, dict, int, str
135 from future.utils import PY2, as_native_str
136
137 #
138 # Import our required modules
139 #
140 import re
141 re.ASCII = 0 # for py2 compatibility
142 import string
143
144 __all__ = ["CookieError", "BaseCookie", "SimpleCookie"]
145
146 _nulljoin = ''.join
147 _semispacejoin = '; '.join
148 _spacejoin = ' '.join
149
150 #
151 # Define an exception visible to External modules
152 #
153 class CookieError(Exception):
154 pass
155
156
157 # These quoting routines conform to the RFC2109 specification, which in
158 # turn references the character definitions from RFC2068. They provide
159 # a two-way quoting algorithm. Any non-text character is translated
160 # into a 4 character sequence: a forward-slash followed by the
161 # three-digit octal equivalent of the character. Any '\' or '"' is
162 # quoted with a preceeding '\' slash.
163 #
164 # These are taken from RFC2068 and RFC2109.
165 # _LegalChars is the list of chars which don't require "'s
166 # _Translator hash-table for fast quoting
167 #
168 _LegalChars = string.ascii_letters + string.digits + "!#$%&'*+-.^_`|~:"
169 _Translator = {
170 '\000' : '\\000', '\001' : '\\001', '\002' : '\\002',
171 '\003' : '\\003', '\004' : '\\004', '\005' : '\\005',
172 '\006' : '\\006', '\007' : '\\007', '\010' : '\\010',
173 '\011' : '\\011', '\012' : '\\012', '\013' : '\\013',
174 '\014' : '\\014', '\015' : '\\015', '\016' : '\\016',
175 '\017' : '\\017', '\020' : '\\020', '\021' : '\\021',
176 '\022' : '\\022', '\023' : '\\023', '\024' : '\\024',
177 '\025' : '\\025', '\026' : '\\026', '\027' : '\\027',
178 '\030' : '\\030', '\031' : '\\031', '\032' : '\\032',
179 '\033' : '\\033', '\034' : '\\034', '\035' : '\\035',
180 '\036' : '\\036', '\037' : '\\037',
181
182 # Because of the way browsers really handle cookies (as opposed
183 # to what the RFC says) we also encode , and ;
184
185 ',' : '\\054', ';' : '\\073',
186
187 '"' : '\\"', '\\' : '\\\\',
188
189 '\177' : '\\177', '\200' : '\\200', '\201' : '\\201',
190 '\202' : '\\202', '\203' : '\\203', '\204' : '\\204',
191 '\205' : '\\205', '\206' : '\\206', '\207' : '\\207',
192 '\210' : '\\210', '\211' : '\\211', '\212' : '\\212',
193 '\213' : '\\213', '\214' : '\\214', '\215' : '\\215',
194 '\216' : '\\216', '\217' : '\\217', '\220' : '\\220',
195 '\221' : '\\221', '\222' : '\\222', '\223' : '\\223',
196 '\224' : '\\224', '\225' : '\\225', '\226' : '\\226',
197 '\227' : '\\227', '\230' : '\\230', '\231' : '\\231',
198 '\232' : '\\232', '\233' : '\\233', '\234' : '\\234',
199 '\235' : '\\235', '\236' : '\\236', '\237' : '\\237',
200 '\240' : '\\240', '\241' : '\\241', '\242' : '\\242',
201 '\243' : '\\243', '\244' : '\\244', '\245' : '\\245',
202 '\246' : '\\246', '\247' : '\\247', '\250' : '\\250',
203 '\251' : '\\251', '\252' : '\\252', '\253' : '\\253',
204 '\254' : '\\254', '\255' : '\\255', '\256' : '\\256',
205 '\257' : '\\257', '\260' : '\\260', '\261' : '\\261',
206 '\262' : '\\262', '\263' : '\\263', '\264' : '\\264',
207 '\265' : '\\265', '\266' : '\\266', '\267' : '\\267',
208 '\270' : '\\270', '\271' : '\\271', '\272' : '\\272',
209 '\273' : '\\273', '\274' : '\\274', '\275' : '\\275',
210 '\276' : '\\276', '\277' : '\\277', '\300' : '\\300',
211 '\301' : '\\301', '\302' : '\\302', '\303' : '\\303',
212 '\304' : '\\304', '\305' : '\\305', '\306' : '\\306',
213 '\307' : '\\307', '\310' : '\\310', '\311' : '\\311',
214 '\312' : '\\312', '\313' : '\\313', '\314' : '\\314',
215 '\315' : '\\315', '\316' : '\\316', '\317' : '\\317',
216 '\320' : '\\320', '\321' : '\\321', '\322' : '\\322',
217 '\323' : '\\323', '\324' : '\\324', '\325' : '\\325',
218 '\326' : '\\326', '\327' : '\\327', '\330' : '\\330',
219 '\331' : '\\331', '\332' : '\\332', '\333' : '\\333',
220 '\334' : '\\334', '\335' : '\\335', '\336' : '\\336',
221 '\337' : '\\337', '\340' : '\\340', '\341' : '\\341',
222 '\342' : '\\342', '\343' : '\\343', '\344' : '\\344',
223 '\345' : '\\345', '\346' : '\\346', '\347' : '\\347',
224 '\350' : '\\350', '\351' : '\\351', '\352' : '\\352',
225 '\353' : '\\353', '\354' : '\\354', '\355' : '\\355',
226 '\356' : '\\356', '\357' : '\\357', '\360' : '\\360',
227 '\361' : '\\361', '\362' : '\\362', '\363' : '\\363',
228 '\364' : '\\364', '\365' : '\\365', '\366' : '\\366',
229 '\367' : '\\367', '\370' : '\\370', '\371' : '\\371',
230 '\372' : '\\372', '\373' : '\\373', '\374' : '\\374',
231 '\375' : '\\375', '\376' : '\\376', '\377' : '\\377'
232 }
233
234 def _quote(str, LegalChars=_LegalChars):
235 r"""Quote a string for use in a cookie header.
236
237 If the string does not need to be double-quoted, then just return the
238 string. Otherwise, surround the string in doublequotes and quote
239 (with a \) special characters.
240 """
241 if all(c in LegalChars for c in str):
242 return str
243 else:
244 return '"' + _nulljoin(_Translator.get(s, s) for s in str) + '"'
245
246
247 _OctalPatt = re.compile(r"\\[0-3][0-7][0-7]")
248 _QuotePatt = re.compile(r"[\\].")
249
250 def _unquote(mystr):
251 # If there aren't any doublequotes,
252 # then there can't be any special characters. See RFC 2109.
253 if len(mystr) < 2:
254 return mystr
255 if mystr[0] != '"' or mystr[-1] != '"':
256 return mystr
257
258 # We have to assume that we must decode this string.
259 # Down to work.
260
261 # Remove the "s
262 mystr = mystr[1:-1]
263
264 # Check for special sequences. Examples:
265 # \012 --> \n
266 # \" --> "
267 #
268 i = 0
269 n = len(mystr)
270 res = []
271 while 0 <= i < n:
272 o_match = _OctalPatt.search(mystr, i)
273 q_match = _QuotePatt.search(mystr, i)
274 if not o_match and not q_match: # Neither matched
275 res.append(mystr[i:])
276 break
277 # else:
278 j = k = -1
279 if o_match:
280 j = o_match.start(0)
281 if q_match:
282 k = q_match.start(0)
283 if q_match and (not o_match or k < j): # QuotePatt matched
284 res.append(mystr[i:k])
285 res.append(mystr[k+1])
286 i = k + 2
287 else: # OctalPatt matched
288 res.append(mystr[i:j])
289 res.append(chr(int(mystr[j+1:j+4], 8)))
290 i = j + 4
291 return _nulljoin(res)
292
293 # The _getdate() routine is used to set the expiration time in the cookie's HTTP
294 # header. By default, _getdate() returns the current time in the appropriate
295 # "expires" format for a Set-Cookie header. The one optional argument is an
296 # offset from now, in seconds. For example, an offset of -3600 means "one hour
297 # ago". The offset may be a floating point number.
298 #
299
300 _weekdayname = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
301
302 _monthname = [None,
303 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
304 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
305
306 def _getdate(future=0, weekdayname=_weekdayname, monthname=_monthname):
307 from time import gmtime, time
308 now = time()
309 year, month, day, hh, mm, ss, wd, y, z = gmtime(now + future)
310 return "%s, %02d %3s %4d %02d:%02d:%02d GMT" % \
311 (weekdayname[wd], day, monthname[month], year, hh, mm, ss)
312
313
314 class Morsel(dict):
315 """A class to hold ONE (key, value) pair.
316
317 In a cookie, each such pair may have several attributes, so this class is
318 used to keep the attributes associated with the appropriate key,value pair.
319 This class also includes a coded_value attribute, which is used to hold
320 the network representation of the value. This is most useful when Python
321 objects are pickled for network transit.
322 """
323 # RFC 2109 lists these attributes as reserved:
324 # path comment domain
325 # max-age secure version
326 #
327 # For historical reasons, these attributes are also reserved:
328 # expires
329 #
330 # This is an extension from Microsoft:
331 # httponly
332 #
333 # This dictionary provides a mapping from the lowercase
334 # variant on the left to the appropriate traditional
335 # formatting on the right.
336 _reserved = {
337 "expires" : "expires",
338 "path" : "Path",
339 "comment" : "Comment",
340 "domain" : "Domain",
341 "max-age" : "Max-Age",
342 "secure" : "secure",
343 "httponly" : "httponly",
344 "version" : "Version",
345 }
346
347 _flags = set(['secure', 'httponly'])
348
349 def __init__(self):
350 # Set defaults
351 self.key = self.value = self.coded_value = None
352
353 # Set default attributes
354 for key in self._reserved:
355 dict.__setitem__(self, key, "")
356
357 def __setitem__(self, K, V):
358 K = K.lower()
359 if not K in self._reserved:
360 raise CookieError("Invalid Attribute %s" % K)
361 dict.__setitem__(self, K, V)
362
363 def isReservedKey(self, K):
364 return K.lower() in self._reserved
365
366 def set(self, key, val, coded_val, LegalChars=_LegalChars):
367 # First we verify that the key isn't a reserved word
368 # Second we make sure it only contains legal characters
369 if key.lower() in self._reserved:
370 raise CookieError("Attempt to set a reserved key: %s" % key)
371 if any(c not in LegalChars for c in key):
372 raise CookieError("Illegal key value: %s" % key)
373
374 # It's a good key, so save it.
375 self.key = key
376 self.value = val
377 self.coded_value = coded_val
378
379 def output(self, attrs=None, header="Set-Cookie:"):
380 return "%s %s" % (header, self.OutputString(attrs))
381
382 __str__ = output
383
384 @as_native_str()
385 def __repr__(self):
386 if PY2 and isinstance(self.value, unicode):
387 val = str(self.value) # make it a newstr to remove the u prefix
388 else:
389 val = self.value
390 return '<%s: %s=%s>' % (self.__class__.__name__,
391 str(self.key), repr(val))
392
393 def js_output(self, attrs=None):
394 # Print javascript
395 return """
396 <script type="text/javascript">
397 <!-- begin hiding
398 document.cookie = \"%s\";
399 // end hiding -->
400 </script>
401 """ % (self.OutputString(attrs).replace('"', r'\"'))
402
403 def OutputString(self, attrs=None):
404 # Build up our result
405 #
406 result = []
407 append = result.append
408
409 # First, the key=value pair
410 append("%s=%s" % (self.key, self.coded_value))
411
412 # Now add any defined attributes
413 if attrs is None:
414 attrs = self._reserved
415 items = sorted(self.items())
416 for key, value in items:
417 if value == "":
418 continue
419 if key not in attrs:
420 continue
421 if key == "expires" and isinstance(value, int):
422 append("%s=%s" % (self._reserved[key], _getdate(value)))
423 elif key == "max-age" and isinstance(value, int):
424 append("%s=%d" % (self._reserved[key], value))
425 elif key == "secure":
426 append(str(self._reserved[key]))
427 elif key == "httponly":
428 append(str(self._reserved[key]))
429 else:
430 append("%s=%s" % (self._reserved[key], value))
431
432 # Return the result
433 return _semispacejoin(result)
434
435
436 #
437 # Pattern for finding cookie
438 #
439 # This used to be strict parsing based on the RFC2109 and RFC2068
440 # specifications. I have since discovered that MSIE 3.0x doesn't
441 # follow the character rules outlined in those specs. As a
442 # result, the parsing rules here are less strict.
443 #
444
445 _LegalCharsPatt = r"[\w\d!#%&'~_`><@,:/\$\*\+\-\.\^\|\)\(\?\}\{\=]"
446 _CookiePattern = re.compile(r"""
447 (?x) # This is a verbose pattern
448 (?P<key> # Start of group 'key'
449 """ + _LegalCharsPatt + r"""+? # Any word of at least one letter
450 ) # End of group 'key'
451 ( # Optional group: there may not be a value.
452 \s*=\s* # Equal Sign
453 (?P<val> # Start of group 'val'
454 "(?:[^\\"]|\\.)*" # Any doublequoted string
455 | # or
456 \w{3},\s[\w\d\s-]{9,11}\s[\d:]{8}\sGMT # Special case for "expires" attr
457 | # or
458 """ + _LegalCharsPatt + r"""* # Any word or empty string
459 ) # End of group 'val'
460 )? # End of optional value group
461 \s* # Any number of spaces.
462 (\s+|;|$) # Ending either at space, semicolon, or EOS.
463 """, re.ASCII) # May be removed if safe.
464
465
466 # At long last, here is the cookie class. Using this class is almost just like
467 # using a dictionary. See this module's docstring for example usage.
468 #
469 class BaseCookie(dict):
470 """A container class for a set of Morsels."""
471
472 def value_decode(self, val):
473 """real_value, coded_value = value_decode(STRING)
474 Called prior to setting a cookie's value from the network
475 representation. The VALUE is the value read from HTTP
476 header.
477 Override this function to modify the behavior of cookies.
478 """
479 return val, val
480
481 def value_encode(self, val):
482 """real_value, coded_value = value_encode(VALUE)
483 Called prior to setting a cookie's value from the dictionary
484 representation. The VALUE is the value being assigned.
485 Override this function to modify the behavior of cookies.
486 """
487 strval = str(val)
488 return strval, strval
489
490 def __init__(self, input=None):
491 if input:
492 self.load(input)
493
494 def __set(self, key, real_value, coded_value):
495 """Private method for setting a cookie's value"""
496 M = self.get(key, Morsel())
497 M.set(key, real_value, coded_value)
498 dict.__setitem__(self, key, M)
499
500 def __setitem__(self, key, value):
501 """Dictionary style assignment."""
502 rval, cval = self.value_encode(value)
503 self.__set(key, rval, cval)
504
505 def output(self, attrs=None, header="Set-Cookie:", sep="\015\012"):
506 """Return a string suitable for HTTP."""
507 result = []
508 items = sorted(self.items())
509 for key, value in items:
510 result.append(value.output(attrs, header))
511 return sep.join(result)
512
513 __str__ = output
514
515 @as_native_str()
516 def __repr__(self):
517 l = []
518 items = sorted(self.items())
519 for key, value in items:
520 if PY2 and isinstance(value.value, unicode):
521 val = str(value.value) # make it a newstr to remove the u pre fix
522 else:
523 val = value.value
524 l.append('%s=%s' % (str(key), repr(val)))
525 return '<%s: %s>' % (self.__class__.__name__, _spacejoin(l))
526
527 def js_output(self, attrs=None):
528 """Return a string suitable for JavaScript."""
529 result = []
530 items = sorted(self.items())
531 for key, value in items:
532 result.append(value.js_output(attrs))
533 return _nulljoin(result)
534
535 def load(self, rawdata):
536 """Load cookies from a string (presumably HTTP_COOKIE) or
537 from a dictionary. Loading cookies from a dictionary 'd'
538 is equivalent to calling:
539 map(Cookie.__setitem__, d.keys(), d.values())
540 """
541 if isinstance(rawdata, str):
542 self.__parse_string(rawdata)
543 else:
544 # self.update() wouldn't call our custom __setitem__
545 for key, value in rawdata.items():
546 self[key] = value
547 return
548
549 def __parse_string(self, mystr, patt=_CookiePattern):
550 i = 0 # Our starting point
551 n = len(mystr) # Length of string
552 M = None # current morsel
553
554 while 0 <= i < n:
555 # Start looking for a cookie
556 match = patt.search(mystr, i)
557 if not match:
558 # No more cookies
559 break
560
561 key, value = match.group("key"), match.group("val")
562
563 i = match.end(0)
564
565 # Parse the key, value in case it's metainfo
566 if key[0] == "$":
567 # We ignore attributes which pertain to the cookie
568 # mechanism as a whole. See RFC 2109.
569 # (Does anyone care?)
570 if M:
571 M[key[1:]] = value
572 elif key.lower() in Morsel._reserved:
573 if M:
574 if value is None:
575 if key.lower() in Morsel._flags:
576 M[key] = True
577 else:
578 M[key] = _unquote(value)
579 elif value is not None:
580 rval, cval = self.value_decode(value)
581 self.__set(key, rval, cval)
582 M = self[key]
583
584
585 class SimpleCookie(BaseCookie):
586 """
587 SimpleCookie supports strings as cookie values. When setting
588 the value using the dictionary assignment notation, SimpleCookie
589 calls the builtin str() to convert the value to a string. Values
590 received from HTTP are kept as strings.
591 """
592 def value_decode(self, val):
593 return _unquote(val), val
594
595 def value_encode(self, val):
596 strval = str(val)
597 return strval, _quote(strval)
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698