web/http_headers.py
changeset 9572 73b2410bdadc
parent 9571 aaf83cc07eed
child 9726 8905267dc7ae
child 9936 5dbf45204109
equal deleted inserted replaced
9571:aaf83cc07eed 9572:73b2410bdadc
    26 
    26 
    27 header_case_mapping = {}
    27 header_case_mapping = {}
    28 
    28 
    29 def casemappingify(d):
    29 def casemappingify(d):
    30     global header_case_mapping
    30     global header_case_mapping
    31     newd = dict([(key.lower(),key) for key in d])
    31     newd = dict([(key.lower(), key) for key in d])
    32     header_case_mapping.update(newd)
    32     header_case_mapping.update(newd)
    33 
    33 
    34 def lowerify(d):
    34 def lowerify(d):
    35     return dict([(key.lower(),value) for key,value in d.items()])
    35     return dict([(key.lower(), value) for key, value in d.items()])
    36 
    36 
    37 
    37 
    38 class HeaderHandler(object):
    38 class HeaderHandler(object):
    39     """HeaderHandler manages header generating and parsing functions.
    39     """HeaderHandler manages header generating and parsing functions.
    40     """
    40     """
    72         if parser is None:
    72         if parser is None:
    73             raise ValueError("No header parser for header '%s', either add one or use getHeaderRaw." % (name,))
    73             raise ValueError("No header parser for header '%s', either add one or use getHeaderRaw." % (name,))
    74 
    74 
    75         try:
    75         try:
    76             for p in parser:
    76             for p in parser:
    77                 # print "Parsing %s: %s(%s)" % (name, repr(p), repr(h))
    77                 #print "==> Parsing %s: %s(%s)" % (name, repr(p), repr(header))
    78                 header = p(header)
    78                 header = p(header)
    79                 # if isinstance(h, types.GeneratorType):
    79                 # if isinstance(h, types.GeneratorType):
    80                 #     h=list(h)
    80                 #     h = list(h)
    81         except ValueError as v:
    81         except ValueError as v:
    82             # print v
    82             # print v
    83             header=None
    83             header = None
    84 
    84 
    85         return header
    85         return header
    86 
    86 
    87     def generate(self, name, header):
    87     def generate(self, name, header):
    88         """
    88         """
   186         # (Note: "GMT" is literal, not a variable timezone)
   186         # (Note: "GMT" is literal, not a variable timezone)
   187         # (also handles without without "GMT")
   187         # (also handles without without "GMT")
   188         # Two digit year, yucko.
   188         # Two digit year, yucko.
   189         day, month, year = parts[1].split('-')
   189         day, month, year = parts[1].split('-')
   190         time = parts[2]
   190         time = parts[2]
   191         year=int(year)
   191         year = int(year)
   192         if year < 69:
   192         if year < 69:
   193             year = year + 2000
   193             year = year + 2000
   194         elif year < 100:
   194         elif year < 100:
   195             year = year + 1900
   195             year = year + 1900
   196     elif len(parts) == 5:
   196     elif len(parts) == 5:
   241     NOTE: not all headers can be parsed with this function.
   241     NOTE: not all headers can be parsed with this function.
   242 
   242 
   243     Takes a raw header value (list of strings), and
   243     Takes a raw header value (list of strings), and
   244     Returns a generator of strings and Token class instances.
   244     Returns a generator of strings and Token class instances.
   245     """
   245     """
   246     tokens=http_tokens
   246     tokens = http_tokens
   247     ctls=http_ctls
   247     ctls = http_ctls
   248 
   248 
   249     string = ",".join(header)
   249     string = ",".join(header)
   250     list = []
   250     list = []
   251     start = 0
   251     start = 0
   252     cur = 0
   252     cur = 0
   264             elif x == '\\':
   264             elif x == '\\':
   265                 qpair = True
   265                 qpair = True
   266             elif x == '"':
   266             elif x == '"':
   267                 quoted = False
   267                 quoted = False
   268                 yield qstring+string[start:cur]
   268                 yield qstring+string[start:cur]
   269                 qstring=None
   269                 qstring = None
   270                 start = cur+1
   270                 start = cur+1
   271         elif x in tokens:
   271         elif x in tokens:
   272             if start != cur:
   272             if start != cur:
   273                 if foldCase:
   273                 if foldCase:
   274                     yield string[start:cur].lower()
   274                     yield string[start:cur].lower()
   338     Apache does it this way and has some comments about broken clients which
   338     Apache does it this way and has some comments about broken clients which
   339     forget commas (?), so I'm doing it the same way. It shouldn't
   339     forget commas (?), so I'm doing it the same way. It shouldn't
   340     hurt anything, in any case.
   340     hurt anything, in any case.
   341     """
   341     """
   342 
   342 
   343     l=[]
   343     l = []
   344     for x in seq:
   344     for x in seq:
   345         if not isinstance(x, Token):
   345         if not isinstance(x, Token):
   346             l.append(x)
   346             l.append(x)
   347     return l
   347     return l
   348 
   348 
   352         raise ValueError, "Expected single token, not %s." % (tokens,)
   352         raise ValueError, "Expected single token, not %s." % (tokens,)
   353     return tokens[0]
   353     return tokens[0]
   354 
   354 
   355 def parseKeyValue(val):
   355 def parseKeyValue(val):
   356     if len(val) == 1:
   356     if len(val) == 1:
   357         return val[0],None
   357         return val[0], None
   358     elif len(val) == 3 and val[1] == Token('='):
   358     elif len(val) == 3 and val[1] == Token('='):
   359         return val[0],val[2]
   359         return val[0], val[2]
   360     raise ValueError, "Expected key or key=value, but got %s." % (val,)
   360     raise ValueError, "Expected key or key=value, but got %s." % (val,)
   361 
   361 
   362 def parseArgs(field):
   362 def parseArgs(field):
   363     args=split(field, Token(';'))
   363     args = split(field, Token(';'))
   364     val = args.next()
   364     val = args.next()
   365     args = [parseKeyValue(arg) for arg in args]
   365     args = [parseKeyValue(arg) for arg in args]
   366     return val,args
   366     return val, args
   367 
   367 
   368 def listParser(fun):
   368 def listParser(fun):
   369     """Return a function which applies 'fun' to every element in the
   369     """Return a function which applies 'fun' to every element in the
   370     comma-separated list"""
   370     comma-separated list"""
   371     def listParserHelper(tokens):
   371     def listParserHelper(tokens):
   376 
   376 
   377     return listParserHelper
   377     return listParserHelper
   378 
   378 
   379 def last(seq):
   379 def last(seq):
   380     """Return seq[-1]"""
   380     """Return seq[-1]"""
   381 
       
   382     return seq[-1]
   381     return seq[-1]
   383 
   382 
   384 def unique(seq):
   383 def unique(seq):
   385     '''if seq is not a string, check it's a sequence of one element and return it'''
   384     '''if seq is not a string, check it's a sequence of one element and return it'''
   386     if isinstance(seq, basestring):
   385     if isinstance(seq, basestring):
   437     return [item]
   436     return [item]
   438 
   437 
   439 def generateKeyValues(kvs):
   438 def generateKeyValues(kvs):
   440     l = []
   439     l = []
   441     # print kvs
   440     # print kvs
   442     for k,v in kvs:
   441     for k, v in kvs:
   443         if v is None:
   442         if v is None:
   444             l.append('%s' % k)
   443             l.append('%s' % k)
   445         else:
   444         else:
   446             l.append('%s=%s' % (k,v))
   445             l.append('%s=%s' % (k, v))
   447     return ";".join(l)
   446     return ";".join(l)
   448 
   447 
   449 
   448 
   450 class MimeType(object):
   449 class MimeType(object):
   451     def fromString(klass, mimeTypeString):
   450     def fromString(klass, mimeTypeString):
   489     def __hash__(self):
   488     def __hash__(self):
   490         return hash(self.mediaType)^hash(self.mediaSubtype)^hash(tuple(self.params.iteritems()))
   489         return hash(self.mediaType)^hash(self.mediaSubtype)^hash(tuple(self.params.iteritems()))
   491 
   490 
   492 ##### Specific header parsers.
   491 ##### Specific header parsers.
   493 def parseAccept(field):
   492 def parseAccept(field):
   494     type,args = parseArgs(field)
   493     type, args = parseArgs(field)
   495 
   494 
   496     if len(type) != 3 or type[1] != Token('/'):
   495     if len(type) != 3 or type[1] != Token('/'):
   497         raise ValueError, "MIME Type "+str(type)+" invalid."
   496         raise ValueError, "MIME Type "+str(type)+" invalid."
   498 
   497 
   499     # okay, this spec is screwy. A 'q' parameter is used as the separator
   498     # okay, this spec is screwy. A 'q' parameter is used as the separator
   501     # parameters.
   500     # parameters.
   502 
   501 
   503     num = 0
   502     num = 0
   504     for arg in args:
   503     for arg in args:
   505         if arg[0] == 'q':
   504         if arg[0] == 'q':
   506             mimeparams=tuple(args[0:num])
   505             mimeparams = tuple(args[0:num])
   507             params=args[num:]
   506             params = args[num:]
   508             break
   507             break
   509         num = num + 1
   508         num = num + 1
   510     else:
   509     else:
   511         mimeparams=tuple(args)
   510         mimeparams = tuple(args)
   512         params=[]
   511         params = []
   513 
   512 
   514     # Default values for parameters:
   513     # Default values for parameters:
   515     qval = 1.0
   514     qval = 1.0
   516 
   515 
   517     # Parse accept parameters:
   516     # Parse accept parameters:
   518     for param in params:
   517     for param in params:
   519         if param[0] =='q':
   518         if param[0] == 'q':
   520             qval = float(param[1])
   519             qval = float(param[1])
   521         else:
   520         else:
   522             # Warn? ignored parameter.
   521             # Warn? ignored parameter.
   523             pass
   522             pass
   524 
   523 
   525     ret = MimeType(type[0],type[2],mimeparams),qval
   524     ret = MimeType(type[0], type[2], mimeparams), qval
   526     return ret
   525     return ret
   527 
   526 
   528 def parseAcceptQvalue(field):
   527 def parseAcceptQvalue(field):
   529     type,args=parseArgs(field)
   528     type, args = parseArgs(field)
   530 
   529 
   531     type = checkSingleToken(type)
   530     type = checkSingleToken(type)
   532 
   531 
   533     qvalue = 1.0 # Default qvalue is 1
   532     qvalue = 1.0 # Default qvalue is 1
   534     for arg in args:
   533     for arg in args:
   535         if arg[0] == 'q':
   534         if arg[0] == 'q':
   536             qvalue = float(arg[1])
   535             qvalue = float(arg[1])
   537     return type,qvalue
   536     return type, qvalue
   538 
   537 
   539 def addDefaultCharset(charsets):
   538 def addDefaultCharset(charsets):
   540     if charsets.get('*') is None and charsets.get('iso-8859-1') is None:
   539     if charsets.get('*') is None and charsets.get('iso-8859-1') is None:
   541         charsets['iso-8859-1'] = 1.0
   540         charsets['iso-8859-1'] = 1.0
   542     return charsets
   541     return charsets
   552 def parseContentType(header):
   551 def parseContentType(header):
   553     # Case folding is disabled for this header, because of use of
   552     # Case folding is disabled for this header, because of use of
   554     # Content-Type: multipart/form-data; boundary=CaSeFuLsTuFf
   553     # Content-Type: multipart/form-data; boundary=CaSeFuLsTuFf
   555     # So, we need to explicitly .lower() the type/subtype and arg keys.
   554     # So, we need to explicitly .lower() the type/subtype and arg keys.
   556 
   555 
   557     type,args = parseArgs(header)
   556     type, args = parseArgs(header)
   558 
   557 
   559     if len(type) != 3 or type[1] != Token('/'):
   558     if len(type) != 3 or type[1] != Token('/'):
   560         raise ValueError, "MIME Type "+str(type)+" invalid."
   559         raise ValueError, "MIME Type "+str(type)+" invalid."
   561 
   560 
   562     args = [(kv[0].lower(), kv[1]) for kv in args]
   561     args = [(kv[0].lower(), kv[1]) for kv in args]
   571 
   570 
   572 def parseContentRange(header):
   571 def parseContentRange(header):
   573     """Parse a content-range header into (kind, start, end, realLength).
   572     """Parse a content-range header into (kind, start, end, realLength).
   574 
   573 
   575     realLength might be None if real length is not known ('*').
   574     realLength might be None if real length is not known ('*').
   576     start and end might be None if start,end unspecified (for response code 416)
   575     start and end might be None if start, end unspecified (for response code 416)
   577     """
   576     """
   578     kind, other = header.strip().split()
   577     kind, other = header.strip().split()
   579     if kind.lower() != "bytes":
   578     if kind.lower() != "bytes":
   580         raise ValueError("a range of type %r is not supported")
   579         raise ValueError("a range of type %r is not supported")
   581     startend, realLength = other.split("/")
   580     startend, realLength = other.split("/")
   582     if startend.strip() == '*':
   581     if startend.strip() == '*':
   583         start,end=None,None
   582         start, end = None, None
   584     else:
   583     else:
   585         start, end = map(int, startend.split("-"))
   584         start, end = map(int, startend.split("-"))
   586     if realLength == "*":
   585     if realLength == "*":
   587         realLength = None
   586         realLength = None
   588     else:
   587     else:
   589         realLength = int(realLength)
   588         realLength = int(realLength)
   590     return (kind, start, end, realLength)
   589     return (kind, start, end, realLength)
   591 
   590 
   592 def parseExpect(field):
   591 def parseExpect(field):
   593     type,args=parseArgs(field)
   592     type, args = parseArgs(field)
   594 
   593 
   595     type=parseKeyValue(type)
   594     type = parseKeyValue(type)
   596     return (type[0], (lambda *args:args)(type[1], *args))
   595     return (type[0], (lambda *args:args)(type[1], *args))
   597 
   596 
   598 def parseExpires(header):
   597 def parseExpires(header):
   599     # """HTTP/1.1 clients and caches MUST treat other invalid date formats,
   598     # """HTTP/1.1 clients and caches MUST treat other invalid date formats,
   600     #    especially including the value 0, as in the past (i.e., "already expired")."""
   599     #    especially including the value 0, as in the past (i.e., "already expired")."""
   622 def parseRange(range):
   621 def parseRange(range):
   623     range = list(range)
   622     range = list(range)
   624     if len(range) < 3 or range[1] != Token('='):
   623     if len(range) < 3 or range[1] != Token('='):
   625         raise ValueError("Invalid range header format: %s" %(range,))
   624         raise ValueError("Invalid range header format: %s" %(range,))
   626 
   625 
   627     type=range[0]
   626     type = range[0]
   628     if type != 'bytes':
   627     if type != 'bytes':
   629         raise ValueError("Unknown range unit: %s." % (type,))
   628         raise ValueError("Unknown range unit: %s." % (type,))
   630     rangeset=split(range[2:], Token(','))
   629     rangeset = split(range[2:], Token(','))
   631     ranges = []
   630     ranges = []
   632 
   631 
   633     for byterangespec in rangeset:
   632     for byterangespec in rangeset:
   634         if len(byterangespec) != 1:
   633         if len(byterangespec) != 1:
   635             raise ValueError("Invalid range header format: %s" % (range,))
   634             raise ValueError("Invalid range header format: %s" % (range,))
   636         start,end=byterangespec[0].split('-')
   635         start, end = byterangespec[0].split('-')
   637 
   636 
   638         if not start and not end:
   637         if not start and not end:
   639             raise ValueError("Invalid range header format: %s" % (range,))
   638             raise ValueError("Invalid range header format: %s" % (range,))
   640 
   639 
   641         if start:
   640         if start:
   648         else:
   647         else:
   649             end = None
   648             end = None
   650 
   649 
   651         if start and end and start > end:
   650         if start and end and start > end:
   652             raise ValueError("Invalid range header, start > end: %s" % (range,))
   651             raise ValueError("Invalid range header, start > end: %s" % (range,))
   653         ranges.append((start,end))
   652         ranges.append((start, end))
   654     return type,ranges
   653     return type, ranges
   655 
   654 
   656 def parseRetryAfter(header):
   655 def parseRetryAfter(header):
   657     try:
   656     try:
   658         # delta seconds
   657         # delta seconds
   659         return time.time() + int(header)
   658         return time.time() + int(header)
   712     # in the unquoted base64 encoded credentials
   711     # in the unquoted base64 encoded credentials
   713     return scheme.lower(), rest
   712     return scheme.lower(), rest
   714 
   713 
   715 #### Header generators
   714 #### Header generators
   716 def generateAccept(accept):
   715 def generateAccept(accept):
   717     mimeType,q = accept
   716     mimeType, q = accept
   718 
   717 
   719     out="%s/%s"%(mimeType.mediaType, mimeType.mediaSubtype)
   718     out ="%s/%s"%(mimeType.mediaType, mimeType.mediaSubtype)
   720     if mimeType.params:
   719     if mimeType.params:
   721         out+=';'+generateKeyValues(mimeType.params.iteritems())
   720         out+=';'+generateKeyValues(mimeType.params.iteritems())
   722 
   721 
   723     if q != 1.0:
   722     if q != 1.0:
   724         out+=(';q=%.3f' % (q,)).rstrip('0').rstrip('.')
   723         out+=(';q=%.3f' % (q,)).rstrip('0').rstrip('.')
   760     else:
   759     else:
   761         if k == 'no-cache' or k == 'private':
   760         if k == 'no-cache' or k == 'private':
   762             # quoted list of values
   761             # quoted list of values
   763             v = quoteString(generateList(
   762             v = quoteString(generateList(
   764                 [header_case_mapping.get(name) or dashCapitalize(name) for name in v]))
   763                 [header_case_mapping.get(name) or dashCapitalize(name) for name in v]))
   765         return '%s=%s' % (k,v)
   764         return '%s=%s' % (k, v)
   766 
   765 
   767 def generateContentRange(tup):
   766 def generateContentRange(tup):
   768     """tup is (type, start, end, len)
   767     """tup is (type, start, end, len)
   769     len can be None.
   768     len can be None.
   770     """
   769     """
   803     def noneOr(s):
   802     def noneOr(s):
   804         if s is None:
   803         if s is None:
   805             return ''
   804             return ''
   806         return s
   805         return s
   807 
   806 
   808     type,ranges=range
   807     type, ranges = range
   809 
   808 
   810     if type != 'bytes':
   809     if type != 'bytes':
   811         raise ValueError("Unknown range unit: "+type+".")
   810         raise ValueError("Unknown range unit: "+type+".")
   812 
   811 
   813     return (type+'='+
   812     return (type+'='+
   817 def generateRetryAfter(when):
   816 def generateRetryAfter(when):
   818     # always generate delta seconds format
   817     # always generate delta seconds format
   819     return str(int(when - time.time()))
   818     return str(int(when - time.time()))
   820 
   819 
   821 def generateContentType(mimeType):
   820 def generateContentType(mimeType):
   822     out="%s/%s"%(mimeType.mediaType, mimeType.mediaSubtype)
   821     out = "%s/%s" % (mimeType.mediaType, mimeType.mediaSubtype)
   823     if mimeType.params:
   822     if mimeType.params:
   824         out+=';'+generateKeyValues(mimeType.params.iteritems())
   823         out += ';' + generateKeyValues(mimeType.params.iteritems())
   825     return out
   824     return out
   826 
   825 
   827 def generateIfRange(dateOrETag):
   826 def generateIfRange(dateOrETag):
   828     if isinstance(dateOrETag, ETag):
   827     if isinstance(dateOrETag, ETag):
   829         return dateOrETag.generate()
   828         return dateOrETag.generate()
   840         # If we're going to parse out to something other than a dict
   839         # If we're going to parse out to something other than a dict
   841         # we need to be able to generate from something other than a dict
   840         # we need to be able to generate from something other than a dict
   842 
   841 
   843         try:
   842         try:
   844             l = []
   843             l = []
   845             for k,v in dict(challenge).iteritems():
   844             for k, v in dict(challenge).iteritems():
   846                 l.append("%s=%s" % (k, quoteString(v)))
   845                 l.append("%s=%s" % (k, quoteString(v)))
   847 
   846 
   848             _generated.append("%s %s" % (scheme, ", ".join(l)))
   847             _generated.append("%s %s" % (scheme, ", ".join(l)))
   849         except ValueError:
   848         except ValueError:
   850             _generated.append("%s %s" % (scheme, challenge))
   849             _generated.append("%s %s" % (scheme, challenge))
   885 
   884 
   886     def __repr__(self):
   885     def __repr__(self):
   887         return "Etag(%r, weak=%r)" % (self.tag, self.weak)
   886         return "Etag(%r, weak=%r)" % (self.tag, self.weak)
   888 
   887 
   889     def parse(tokens):
   888     def parse(tokens):
   890         tokens=tuple(tokens)
   889         tokens = tuple(tokens)
   891         if len(tokens) == 1 and not isinstance(tokens[0], Token):
   890         if len(tokens) == 1 and not isinstance(tokens[0], Token):
   892             return ETag(tokens[0])
   891             return ETag(tokens[0])
   893 
   892 
   894         if(len(tokens) == 3 and tokens[0] == "w"
   893         if(len(tokens) == 3 and tokens[0] == "w"
   895            and tokens[1] == Token('/')):
   894            and tokens[1] == Token('/')):
   896             return ETag(tokens[2], weak=True)
   895             return ETag(tokens[2], weak=True)
   897 
   896 
   898         raise ValueError("Invalid ETag.")
   897         raise ValueError("Invalid ETag.")
   899 
   898 
   900     parse=staticmethod(parse)
   899     parse = staticmethod(parse)
   901 
   900 
   902     def generate(self):
   901     def generate(self):
   903         if self.weak:
   902         if self.weak:
   904             return 'W/'+quoteString(self.tag)
   903             return 'W/'+quoteString(self.tag)
   905         else:
   904         else:
   906             return quoteString(self.tag)
   905             return quoteString(self.tag)
   907 
   906 
   908 def parseStarOrETag(tokens):
   907 def parseStarOrETag(tokens):
   909     tokens=tuple(tokens)
   908     tokens = tuple(tokens)
   910     if tokens == ('*',):
   909     if tokens == ('*',):
   911         return '*'
   910         return '*'
   912     else:
   911     else:
   913         return ETag.parse(tokens)
   912         return ETag.parse(tokens)
   914 
   913 
   915 def generateStarOrETag(etag):
   914 def generateStarOrETag(etag):
   916     if etag=='*':
   915     if etag == '*':
   917         return etag
   916         return etag
   918     else:
   917     else:
   919         return etag.generate()
   918         return etag.generate()
   920 
   919 
   921 #### Cookies. Blech!
   920 #### Cookies. Blech!
   922 class Cookie(object):
   921 class Cookie(object):
   923     # __slots__ = ['name', 'value', 'path', 'domain', 'ports', 'expires', 'discard', 'secure', 'comment', 'commenturl', 'version']
   922     # __slots__ = ['name', 'value', 'path', 'domain', 'ports', 'expires', 'discard', 'secure', 'comment', 'commenturl', 'version']
   924 
   923 
   925     def __init__(self, name, value, path=None, domain=None, ports=None, expires=None, discard=False, secure=False, comment=None, commenturl=None, version=0):
   924     def __init__(self, name, value, path=None, domain=None, ports=None, expires=None, discard=False, secure=False, comment=None, commenturl=None, version=0):
   926         self.name=name
   925         self.name = name
   927         self.value=value
   926         self.value = value
   928         self.path=path
   927         self.path = path
   929         self.domain=domain
   928         self.domain = domain
   930         self.ports=ports
   929         self.ports = ports
   931         self.expires=expires
   930         self.expires = expires
   932         self.discard=discard
   931         self.discard = discard
   933         self.secure=secure
   932         self.secure = secure
   934         self.comment=comment
   933         self.comment = comment
   935         self.commenturl=commenturl
   934         self.commenturl = commenturl
   936         self.version=version
   935         self.version = version
   937 
   936 
   938     def __repr__(self):
   937     def __repr__(self):
   939         s="Cookie(%r=%r" % (self.name, self.value)
   938         s = "Cookie(%r=%r" % (self.name, self.value)
   940         if self.path is not None: s+=", path=%r" % (self.path,)
   939         if self.path is not None: s+=", path=%r" % (self.path,)
   941         if self.domain is not None: s+=", domain=%r" % (self.domain,)
   940         if self.domain is not None: s+=", domain=%r" % (self.domain,)
   942         if self.ports is not None: s+=", ports=%r" % (self.ports,)
   941         if self.ports is not None: s+=", ports=%r" % (self.ports,)
   943         if self.expires is not None: s+=", expires=%r" % (self.expires,)
   942         if self.expires is not None: s+=", expires=%r" % (self.expires,)
   944         if self.secure is not False: s+=", secure=%r" % (self.secure,)
   943         if self.secure is not False: s+=", secure=%r" % (self.secure,)
   977     # Neither new RFC2965 cookies nor old netscape cookies are.
   976     # Neither new RFC2965 cookies nor old netscape cookies are.
   978 
   977 
   979     header = ';'.join(headers)
   978     header = ';'.join(headers)
   980     if header[0:8].lower() == "$version":
   979     if header[0:8].lower() == "$version":
   981         # RFC2965 cookie
   980         # RFC2965 cookie
   982         h=tokenize([header], foldCase=False)
   981         h = tokenize([header], foldCase=False)
   983         r_cookies = split(h, Token(','))
   982         r_cookies = split(h, Token(','))
   984         for r_cookie in r_cookies:
   983         for r_cookie in r_cookies:
   985             last_cookie = None
   984             last_cookie = None
   986             rr_cookies = split(r_cookie, Token(';'))
   985             rr_cookies = split(r_cookie, Token(';'))
   987             for cookie in rr_cookies:
   986             for cookie in rr_cookies:
   990                     (name,), (value,) = nameval
   989                     (name,), (value,) = nameval
   991                 else:
   990                 else:
   992                     (name,), = nameval
   991                     (name,), = nameval
   993                     value = None
   992                     value = None
   994 
   993 
   995                 name=name.lower()
   994                 name = name.lower()
   996                 if name == '$version':
   995                 if name == '$version':
   997                     continue
   996                     continue
   998                 if name[0] == '$':
   997                 if name[0] == '$':
   999                     if last_cookie is not None:
   998                     if last_cookie is not None:
  1000                         if name == '$path':
   999                         if name == '$path':
  1001                             last_cookie.path=value
  1000                             last_cookie.path = value
  1002                         elif name == '$domain':
  1001                         elif name == '$domain':
  1003                             last_cookie.domain=value
  1002                             last_cookie.domain = value
  1004                         elif name == '$port':
  1003                         elif name == '$port':
  1005                             if value is None:
  1004                             if value is None:
  1006                                 last_cookie.ports = ()
  1005                                 last_cookie.ports = ()
  1007                             else:
  1006                             else:
  1008                                 last_cookie.ports=tuple([int(s) for s in value.split(',')])
  1007                                 last_cookie.ports = tuple([int(s) for s in value.split(',')])
  1009                 else:
  1008                 else:
  1010                     last_cookie = Cookie(name, value, version=1)
  1009                     last_cookie = Cookie(name, value, version=1)
  1011                     cookies.append(last_cookie)
  1010                     cookies.append(last_cookie)
  1012     else:
  1011     else:
  1013         # Oldstyle cookies don't do quoted strings or anything sensible.
  1012         # Oldstyle cookies don't do quoted strings or anything sensible.
  1014         # All characters are valid for names except ';' and '=', and all
  1013         # All characters are valid for names except ';' and '=', and all
  1015         # characters are valid for values except ';'. Spaces are stripped,
  1014         # characters are valid for values except ';'. Spaces are stripped,
  1016         # however.
  1015         # however.
  1017         r_cookies = header.split(';')
  1016         r_cookies = header.split(';')
  1018         for r_cookie in r_cookies:
  1017         for r_cookie in r_cookies:
  1019             name,value = r_cookie.split('=', 1)
  1018             name, value = r_cookie.split('=', 1)
  1020             name=name.strip(' \t')
  1019             name = name.strip(' \t')
  1021             value=value.strip(' \t')
  1020             value = value.strip(' \t')
  1022 
  1021 
  1023             cookies.append(Cookie(name, value))
  1022             cookies.append(Cookie(name, value))
  1024 
  1023 
  1025     return cookies
  1024     return cookies
  1026 
  1025 
  1084                 # the other side.
  1083                 # the other side.
  1085 
  1084 
  1086                 if cookie_validname_re.match(cookie.name) is None:
  1085                 if cookie_validname_re.match(cookie.name) is None:
  1087                     continue
  1086                     continue
  1088 
  1087 
  1089                 value=cookie.value
  1088                 value = cookie.value
  1090                 if cookie_validvalue_re.match(cookie.value) is None:
  1089                 if cookie_validvalue_re.match(cookie.value) is None:
  1091                     value = quoteString(value)
  1090                     value = quoteString(value)
  1092 
  1091 
  1093                 str_cookies.append("%s=%s" % (cookie.name, value))
  1092                 str_cookies.append("%s=%s" % (cookie.name, value))
  1094             else:
  1093             else:
  1114             l = []
  1113             l = []
  1115 
  1114 
  1116             for part in parts:
  1115             for part in parts:
  1117                 namevalue = part.split('=',1)
  1116                 namevalue = part.split('=',1)
  1118                 if len(namevalue) == 1:
  1117                 if len(namevalue) == 1:
  1119                     name=namevalue[0]
  1118                     name = namevalue[0]
  1120                     value=None
  1119                     value = None
  1121                 else:
  1120                 else:
  1122                     name,value=namevalue
  1121                     name, value = namevalue
  1123                     value=value.strip(' \t')
  1122                     value = value.strip(' \t')
  1124 
  1123 
  1125                 name=name.strip(' \t')
  1124                 name = name.strip(' \t')
  1126 
  1125 
  1127                 l.append((name, value))
  1126                 l.append((name, value))
  1128 
  1127 
  1129             setCookies.append(makeCookieFromList(l, True))
  1128             setCookies.append(makeCookieFromList(l, True))
  1130         except ValueError:
  1129         except ValueError:
  1151     if name.startswith("$"):
  1150     if name.startswith("$"):
  1152         raise ValueError("Invalid cookie name: %r, starts with '$'." % name)
  1151         raise ValueError("Invalid cookie name: %r, starts with '$'." % name)
  1153     cookie = Cookie(name, value)
  1152     cookie = Cookie(name, value)
  1154     hadMaxAge = False
  1153     hadMaxAge = False
  1155 
  1154 
  1156     for name,value in tup[1:]:
  1155     for name, value in tup[1:]:
  1157         name = name.lower()
  1156         name = name.lower()
  1158 
  1157 
  1159         if value is None:
  1158         if value is None:
  1160             if name in ("discard", "secure"):
  1159             if name in ("discard", "secure"):
  1161                 # Boolean attrs
  1160                 # Boolean attrs
  1265 #         if item1[0] == item2[0]:
  1264 #         if item1[0] == item2[0]:
  1266 #             return 0
  1265 #             return 0
  1267 
  1266 
  1268 
  1267 
  1269 # def getMimeQuality(mimeType, accepts):
  1268 # def getMimeQuality(mimeType, accepts):
  1270 #     type,args = parseArgs(mimeType)
  1269 #     type, args = parseArgs(mimeType)
  1271 #     type=type.split(Token('/'))
  1270 #     type = type.split(Token('/'))
  1272 #     if len(type) != 2:
  1271 #     if len(type) != 2:
  1273 #         raise ValueError, "MIME Type "+s+" invalid."
  1272 #         raise ValueError, "MIME Type "+s+" invalid."
  1274 
  1273 
  1275 #     for accept in accepts:
  1274 #     for accept in accepts:
  1276 #         accept,acceptQual=accept
  1275 #         accept, acceptQual = accept
  1277 #         acceptType=accept[0:1]
  1276 #         acceptType = accept[0:1]
  1278 #         acceptArgs=accept[2]
  1277 #         acceptArgs = accept[2]
  1279 
  1278 
  1280 #         if ((acceptType == type or acceptType == (type[0],'*') or acceptType==('*','*')) and
  1279 #         if ((acceptType == type or acceptType == (type[0],'*') or acceptType==('*','*')) and
  1281 #             (args == acceptArgs or len(acceptArgs) == 0)):
  1280 #             (args == acceptArgs or len(acceptArgs) == 0)):
  1282 #             return acceptQual
  1281 #             return acceptQual
  1283 
  1282 
  1335     hasHeader = __contains__
  1334     hasHeader = __contains__
  1336 
  1335 
  1337     def getRawHeaders(self, name, default=None):
  1336     def getRawHeaders(self, name, default=None):
  1338         """Returns a list of headers matching the given name as the raw string given."""
  1337         """Returns a list of headers matching the given name as the raw string given."""
  1339 
  1338 
  1340         name=name.lower()
  1339         name = name.lower()
  1341         raw_header = self._raw_headers.get(name, default)
  1340         raw_header = self._raw_headers.get(name, default)
  1342         if raw_header is not _RecalcNeeded:
  1341         if raw_header is not _RecalcNeeded:
  1343             return raw_header
  1342             return raw_header
  1344 
  1343 
  1345         return self._toRaw(name)
  1344         return self._toRaw(name)
  1350 
  1349 
  1351         If no parser for the header exists, raise ValueError.
  1350         If no parser for the header exists, raise ValueError.
  1352 
  1351 
  1353         If the header doesn't exist, return default (or None if not specified)
  1352         If the header doesn't exist, return default (or None if not specified)
  1354         """
  1353         """
  1355         name=name.lower()
  1354         name = name.lower()
  1356         parsed = self._headers.get(name, default)
  1355         parsed = self._headers.get(name, default)
  1357         if parsed is not _RecalcNeeded:
  1356         if parsed is not _RecalcNeeded:
  1358             return parsed
  1357             return parsed
  1359         return self._toParsed(name)
  1358         return self._toParsed(name)
  1360 
  1359 
  1361     def setRawHeaders(self, name, value):
  1360     def setRawHeaders(self, name, value):
  1362         """Sets the raw representation of the given header.
  1361         """Sets the raw representation of the given header.
  1363         Value should be a list of strings, each being one header of the
  1362         Value should be a list of strings, each being one header of the
  1364         given name.
  1363         given name.
  1365         """
  1364         """
  1366         name=name.lower()
  1365         name = name.lower()
  1367         self._raw_headers[name] = value
  1366         self._raw_headers[name] = value
  1368         self._headers[name] = _RecalcNeeded
  1367         self._headers[name] = _RecalcNeeded
  1369 
  1368 
  1370     def setHeader(self, name, value):
  1369     def setHeader(self, name, value):
  1371         """Sets the parsed representation of the given header.
  1370         """Sets the parsed representation of the given header.
  1372         Value should be a list of objects whose exact form depends
  1371         Value should be a list of objects whose exact form depends
  1373         on the header in question.
  1372         on the header in question.
  1374         """
  1373         """
  1375         name=name.lower()
  1374         name = name.lower()
  1376         self._raw_headers[name] = _RecalcNeeded
  1375         self._raw_headers[name] = _RecalcNeeded
  1377         self._headers[name] = value
  1376         self._headers[name] = value
  1378 
  1377 
  1379     def addRawHeader(self, name, value):
  1378     def addRawHeader(self, name, value):
  1380         """
  1379         """
  1381         Add a raw value to a header that may or may not already exist.
  1380         Add a raw value to a header that may or may not already exist.
  1382         If it exists, add it as a separate header to output; do not
  1381         If it exists, add it as a separate header to output; do not
  1383         replace anything.
  1382         replace anything.
  1384         """
  1383         """
  1385         name=name.lower()
  1384         name = name.lower()
  1386         raw_header = self._raw_headers.get(name)
  1385         raw_header = self._raw_headers.get(name)
  1387         if raw_header is None:
  1386         if raw_header is None:
  1388             # No header yet
  1387             # No header yet
  1389             raw_header = []
  1388             raw_header = []
  1390             self._raw_headers[name] = raw_header
  1389             self._raw_headers[name] = raw_header
  1398         """
  1397         """
  1399         Add a parsed representatoin to a header that may or may not already exist.
  1398         Add a parsed representatoin to a header that may or may not already exist.
  1400         If it exists, add it as a separate header to output; do not
  1399         If it exists, add it as a separate header to output; do not
  1401         replace anything.
  1400         replace anything.
  1402         """
  1401         """
  1403         name=name.lower()
  1402         name = name.lower()
  1404         header = self._headers.get(name)
  1403         header = self._headers.get(name)
  1405         if header is None:
  1404         if header is None:
  1406             # No header yet
  1405             # No header yet
  1407             header = []
  1406             header = []
  1408             self._headers[name] = header
  1407             self._headers[name] = header
  1411         header.append(value)
  1410         header.append(value)
  1412         self._raw_headers[name] = _RecalcNeeded
  1411         self._raw_headers[name] = _RecalcNeeded
  1413 
  1412 
  1414     def removeHeader(self, name):
  1413     def removeHeader(self, name):
  1415         """Removes the header named."""
  1414         """Removes the header named."""
  1416         name=name.lower()
  1415         name = name.lower()
  1417         if name in self._raw_headers:
  1416         if name in self._raw_headers:
  1418             del self._raw_headers[name]
  1417             del self._raw_headers[name]
  1419             del self._headers[name]
  1418             del self._headers[name]
  1420 
  1419 
  1421     def __repr__(self):
  1420     def __repr__(self):
  1425         """Return the name with the canonical capitalization, if known,
  1424         """Return the name with the canonical capitalization, if known,
  1426         otherwise, Caps-After-Dashes"""
  1425         otherwise, Caps-After-Dashes"""
  1427         return header_case_mapping.get(name) or dashCapitalize(name)
  1426         return header_case_mapping.get(name) or dashCapitalize(name)
  1428 
  1427 
  1429     def getAllRawHeaders(self):
  1428     def getAllRawHeaders(self):
  1430         """Return an iterator of key,value pairs of all headers
  1429         """Return an iterator of key, value pairs of all headers
  1431         contained in this object, as strings. The keys are capitalized
  1430         contained in this object, as strings. The keys are capitalized
  1432         in canonical capitalization."""
  1431         in canonical capitalization."""
  1433         for k,v in self._raw_headers.iteritems():
  1432         for k, v in self._raw_headers.iteritems():
  1434             if v is _RecalcNeeded:
  1433             if v is _RecalcNeeded:
  1435                 v = self._toRaw(k)
  1434                 v = self._toRaw(k)
  1436             yield self.canonicalNameCaps(k), v
  1435             yield self.canonicalNameCaps(k), v
  1437 
  1436 
  1438     def makeImmutable(self):
  1437     def makeImmutable(self):
  1454 
  1453 
  1455 iteritems = lambda x: x.iteritems()
  1454 iteritems = lambda x: x.iteritems()
  1456 
  1455 
  1457 
  1456 
  1458 parser_general_headers = {
  1457 parser_general_headers = {
  1459     'Cache-Control':(tokenize, listParser(parseCacheControl), dict),
  1458     'Cache-Control': (tokenize, listParser(parseCacheControl), dict),
  1460     'Connection':(tokenize,filterTokens),
  1459     'Connection': (tokenize, filterTokens),
  1461     'Date':(last,parseDateTime),
  1460     'Date': (last, parseDateTime),
  1462 #    'Pragma':tokenize
  1461 #    'Pragma':tokenize
  1463 #    'Trailer':tokenize
  1462 #    'Trailer':tokenize
  1464     'Transfer-Encoding':(tokenize,filterTokens),
  1463     'Transfer-Encoding': (tokenize, filterTokens),
  1465 #    'Upgrade':tokenize
  1464 #    'Upgrade':tokenize
  1466 #    'Via':tokenize,stripComment
  1465 #    'Via':tokenize, stripComment
  1467 #    'Warning':tokenize
  1466 #    'Warning':tokenize
  1468 }
  1467 }
  1469 
  1468 
  1470 generator_general_headers = {
  1469 generator_general_headers = {
  1471     'Cache-Control':(iteritems, listGenerator(generateCacheControl), singleHeader),
  1470     'Cache-Control': (iteritems, listGenerator(generateCacheControl), singleHeader),
  1472     'Connection':(generateList,singleHeader),
  1471     'Connection': (generateList, singleHeader),
  1473     'Date':(generateDateTime,singleHeader),
  1472     'Date': (generateDateTime, singleHeader),
  1474 #    'Pragma':
  1473 #    'Pragma':
  1475 #    'Trailer':
  1474 #    'Trailer':
  1476     'Transfer-Encoding':(generateList,singleHeader),
  1475     'Transfer-Encoding': (generateList, singleHeader),
  1477 #    'Upgrade':
  1476 #    'Upgrade':
  1478 #    'Via':
  1477 #    'Via':
  1479 #    'Warning':
  1478 #    'Warning':
  1480 }
  1479 }
  1481 
  1480 
  1482 parser_request_headers = {
  1481 parser_request_headers = {
  1483     'Accept': (tokenize, listParser(parseAccept), dict),
  1482     'Accept': (tokenize, listParser(parseAccept), dict),
  1484     'Accept-Charset': (tokenize, listParser(parseAcceptQvalue), dict, addDefaultCharset),
  1483     'Accept-Charset': (tokenize, listParser(parseAcceptQvalue), dict, addDefaultCharset),
  1485     'Accept-Encoding':(tokenize, listParser(parseAcceptQvalue), dict, addDefaultEncoding),
  1484     'Accept-Encoding': (tokenize, listParser(parseAcceptQvalue), dict, addDefaultEncoding),
  1486     'Accept-Language':(tokenize, listParser(parseAcceptQvalue), dict),
  1485     'Accept-Language': (tokenize, listParser(parseAcceptQvalue), dict),
  1487     'Access-Control-Allow-Origin': (last, parseAllowOrigin,),
  1486     'Access-Control-Allow-Origin': (last, parseAllowOrigin,),
  1488     'Access-Control-Allow-Credentials': (last, parseAllowCreds,),
  1487     'Access-Control-Allow-Credentials': (last, parseAllowCreds,),
  1489     'Access-Control-Allow-Methods': (tokenize, listParser(parseHTTPMethod), list),
  1488     'Access-Control-Allow-Methods': (tokenize, listParser(parseHTTPMethod), list),
  1490     'Access-Control-Request-Method': (parseHTTPMethod, ),
  1489     'Access-Control-Request-Method': (parseHTTPMethod, ),
  1491     'Access-Control-Request-Headers': (filterTokens, ),
  1490     'Access-Control-Request-Headers': (filterTokens, ),
  1492     'Access-Control-Expose-Headers': (filterTokens, ),
  1491     'Access-Control-Expose-Headers': (filterTokens, ),
  1493     'Authorization': (last, parseAuthorization),
  1492     'Authorization': (last, parseAuthorization),
  1494     'Cookie':(parseCookie,),
  1493     'Cookie': (parseCookie,),
  1495     'Expect':(tokenize, listParser(parseExpect), dict),
  1494     'Expect': (tokenize, listParser(parseExpect), dict),
  1496     'From':(last,),
       
  1497     'Host':(last,),
       
  1498     'If-Match':(tokenize, listParser(parseStarOrETag), list),
       
  1499     'If-Modified-Since':(last, parseIfModifiedSince),
       
  1500     'If-None-Match':(tokenize, listParser(parseStarOrETag), list),
       
  1501     'If-Range':(parseIfRange,),
       
  1502     'If-Unmodified-Since':(last,parseDateTime),
       
  1503     'Max-Forwards':(last,int),
       
  1504     'Origin': (last,),
  1495     'Origin': (last,),
       
  1496     'From': (last,),
       
  1497     'Host': (last,),
       
  1498     'If-Match': (tokenize, listParser(parseStarOrETag), list),
       
  1499     'If-Modified-Since': (last, parseIfModifiedSince),
       
  1500     'If-None-Match': (tokenize, listParser(parseStarOrETag), list),
       
  1501     'If-Range': (parseIfRange,),
       
  1502     'If-Unmodified-Since': (last, parseDateTime),
       
  1503     'Max-Forwards': (last, int),
  1505 #    'Proxy-Authorization':str, # what is "credentials"
  1504 #    'Proxy-Authorization':str, # what is "credentials"
  1506     'Range':(tokenize, parseRange),
  1505     'Range': (tokenize, parseRange),
  1507     'Referer':(last,str), # TODO: URI object?
  1506     'Referer': (last, str), # TODO: URI object?
  1508     'TE':(tokenize, listParser(parseAcceptQvalue), dict),
  1507     'TE': (tokenize, listParser(parseAcceptQvalue), dict),
  1509     'User-Agent':(last,str),
  1508     'User-Agent': (last, str),
  1510 }
  1509 }
  1511 
  1510 
  1512 generator_request_headers = {
  1511 generator_request_headers = {
  1513     'Accept': (iteritems,listGenerator(generateAccept),singleHeader),
  1512     'Accept': (iteritems, listGenerator(generateAccept), singleHeader),
  1514     'Accept-Charset': (iteritems, listGenerator(generateAcceptQvalue),singleHeader),
  1513     'Accept-Charset': (iteritems, listGenerator(generateAcceptQvalue), singleHeader),
  1515     'Accept-Encoding': (iteritems, removeDefaultEncoding, listGenerator(generateAcceptQvalue),singleHeader),
  1514     'Accept-Encoding': (iteritems, removeDefaultEncoding,
  1516     'Accept-Language': (iteritems, listGenerator(generateAcceptQvalue),singleHeader),
  1515                         listGenerator(generateAcceptQvalue), singleHeader),
       
  1516     'Accept-Language': (iteritems, listGenerator(generateAcceptQvalue), singleHeader),
  1517     'Access-Control-Request-Method': (unique, str, singleHeader, ),
  1517     'Access-Control-Request-Method': (unique, str, singleHeader, ),
  1518     'Access-Control-Expose-Headers': (listGenerator(str), ),
  1518     'Access-Control-Expose-Headers': (listGenerator(str), ),
  1519     'Access-Control-Allow-Headers': (listGenerator(str), ),
  1519     'Access-Control-Allow-Headers': (listGenerator(str), ),
  1520     'Authorization': (generateAuthorization,), # what is "credentials"
  1520     'Authorization': (generateAuthorization,), # what is "credentials"
  1521     'Cookie':(generateCookie,singleHeader),
  1521     'Cookie': (generateCookie, singleHeader),
  1522     'Expect':(iteritems, listGenerator(generateExpect), singleHeader),
  1522     'Expect': (iteritems, listGenerator(generateExpect), singleHeader),
  1523     'From':(unique, str,singleHeader),
  1523     'From': (unique, str, singleHeader),
  1524     'Host':(unique, str,singleHeader),
  1524     'Host': (unique, str, singleHeader),
       
  1525     'If-Match': (listGenerator(generateStarOrETag), singleHeader),
       
  1526     'If-Modified-Since': (generateDateTime, singleHeader),
       
  1527     'If-None-Match': (listGenerator(generateStarOrETag), singleHeader),
       
  1528     'If-Range': (generateIfRange, singleHeader),
       
  1529     'If-Unmodified-Since': (generateDateTime, singleHeader),
       
  1530     'Max-Forwards': (unique, str, singleHeader),
  1525     'Origin': (unique, str, singleHeader),
  1531     'Origin': (unique, str, singleHeader),
  1526     'If-Match':(listGenerator(generateStarOrETag), singleHeader),
       
  1527     'If-Modified-Since':(generateDateTime,singleHeader),
       
  1528     'If-None-Match':(listGenerator(generateStarOrETag), singleHeader),
       
  1529     'If-Range':(generateIfRange, singleHeader),
       
  1530     'If-Unmodified-Since':(generateDateTime,singleHeader),
       
  1531     'Max-Forwards':(unique, str, singleHeader),
       
  1532 #    'Proxy-Authorization':str, # what is "credentials"
  1532 #    'Proxy-Authorization':str, # what is "credentials"
  1533     'Range':(generateRange,singleHeader),
  1533     'Range': (generateRange, singleHeader),
  1534     'Referer':(unique, str,singleHeader),
  1534     'Referer': (unique, str, singleHeader),
  1535     'TE': (iteritems, listGenerator(generateAcceptQvalue),singleHeader),
  1535     'TE': (iteritems, listGenerator(generateAcceptQvalue), singleHeader),
  1536     'User-Agent':(unique, str,singleHeader),
  1536     'User-Agent': (unique, str, singleHeader),
  1537 }
  1537 }
  1538 
  1538 
  1539 parser_response_headers = {
  1539 parser_response_headers = {
  1540     'Accept-Ranges':(tokenize, filterTokens),
  1540     'Accept-Ranges': (tokenize, filterTokens),
  1541     'Age':(last,int),
  1541     'Age': (last, int),
  1542     'ETag':(tokenize, ETag.parse),
  1542     'ETag': (tokenize, ETag.parse),
  1543     'Location':(last,), # TODO: URI object?
  1543     'Location': (last,), # TODO: URI object?
  1544 #    'Proxy-Authenticate'
  1544 #    'Proxy-Authenticate'
  1545     'Retry-After':(last, parseRetryAfter),
  1545     'Retry-After': (last, parseRetryAfter),
  1546     'Server':(last,),
  1546     'Server': (last,),
  1547     'Set-Cookie':(parseSetCookie,),
  1547     'Set-Cookie': (parseSetCookie,),
  1548     'Set-Cookie2':(tokenize, parseSetCookie2),
  1548     'Set-Cookie2': (tokenize, parseSetCookie2),
  1549     'Vary':(tokenize, filterTokens),
  1549     'Vary': (tokenize, filterTokens),
  1550     'WWW-Authenticate': (lambda h: tokenize(h, foldCase=False),
  1550     'WWW-Authenticate': (lambda h: tokenize(h, foldCase=False),
  1551                          parseWWWAuthenticate,)
  1551                          parseWWWAuthenticate,)
  1552 }
  1552 }
  1553 
  1553 
  1554 generator_response_headers = {
  1554 generator_response_headers = {
  1555     'Accept-Ranges':(generateList, singleHeader),
  1555     'Accept-Ranges': (generateList, singleHeader),
  1556     'Age':(unique, str, singleHeader),
  1556     'Age': (unique, str, singleHeader),
  1557     'ETag':(ETag.generate, singleHeader),
  1557     'ETag': (ETag.generate, singleHeader),
  1558     'Location':(unique, str, singleHeader),
  1558     'Location': (unique, str, singleHeader),
  1559 #    'Proxy-Authenticate'
  1559 #    'Proxy-Authenticate'
  1560     'Retry-After':(generateRetryAfter, singleHeader),
  1560     'Retry-After': (generateRetryAfter, singleHeader),
  1561     'Server':(unique, str, singleHeader),
  1561     'Server': (unique, str, singleHeader),
  1562     'Set-Cookie':(generateSetCookie,),
  1562     'Set-Cookie': (generateSetCookie,),
  1563     'Set-Cookie2':(generateSetCookie2,),
  1563     'Set-Cookie2': (generateSetCookie2,),
  1564     'Vary':(generateList, singleHeader),
  1564     'Vary': (generateList, singleHeader),
  1565     'WWW-Authenticate':(generateWWWAuthenticate,)
  1565     'WWW-Authenticate': (generateWWWAuthenticate,)
  1566 }
  1566 }
  1567 
  1567 
  1568 parser_entity_headers = {
  1568 parser_entity_headers = {
  1569     'Allow':(lambda str:tokenize(str, foldCase=False), filterTokens),
  1569     'Allow': (lambda str:tokenize(str, foldCase=False), filterTokens),
  1570     'Content-Encoding':(tokenize, filterTokens),
  1570     'Content-Encoding': (tokenize, filterTokens),
  1571     'Content-Language':(tokenize, filterTokens),
  1571     'Content-Language': (tokenize, filterTokens),
  1572     'Content-Length':(last, int),
  1572     'Content-Length': (last, int),
  1573     'Content-Location':(last,), # TODO: URI object?
  1573     'Content-Location': (last,), # TODO: URI object?
  1574     'Content-MD5':(last, parseContentMD5),
  1574     'Content-MD5': (last, parseContentMD5),
  1575     'Content-Range':(last, parseContentRange),
  1575     'Content-Range': (last, parseContentRange),
  1576     'Content-Type':(lambda str:tokenize(str, foldCase=False), parseContentType),
  1576     'Content-Type': (lambda str:tokenize(str, foldCase=False), parseContentType),
  1577     'Expires':(last, parseExpires),
  1577     'Expires': (last, parseExpires),
  1578     'Last-Modified':(last, parseDateTime),
  1578     'Last-Modified': (last, parseDateTime),
  1579     }
  1579     }
  1580 
  1580 
  1581 generator_entity_headers = {
  1581 generator_entity_headers = {
  1582     'Allow':(generateList, singleHeader),
  1582     'Allow': (generateList, singleHeader),
  1583     'Content-Encoding':(generateList, singleHeader),
  1583     'Content-Encoding': (generateList, singleHeader),
  1584     'Content-Language':(generateList, singleHeader),
  1584     'Content-Language': (generateList, singleHeader),
  1585     'Content-Length':(unique, str, singleHeader),
  1585     'Content-Length': (unique, str, singleHeader),
  1586     'Content-Location':(unique, str, singleHeader),
  1586     'Content-Location': (unique, str, singleHeader),
  1587     'Content-MD5':(base64.encodestring, lambda x: x.strip("\n"), singleHeader),
  1587     'Content-MD5': (base64.encodestring, lambda x: x.strip("\n"), singleHeader),
  1588     'Content-Range':(generateContentRange, singleHeader),
  1588     'Content-Range': (generateContentRange, singleHeader),
  1589     'Content-Type':(generateContentType, singleHeader),
  1589     'Content-Type': (generateContentType, singleHeader),
  1590     'Expires':(generateDateTime, singleHeader),
  1590     'Expires': (generateDateTime, singleHeader),
  1591     'Last-Modified':(generateDateTime, singleHeader),
  1591     'Last-Modified': (generateDateTime, singleHeader),
  1592     }
  1592     }
  1593 
  1593 
  1594 DefaultHTTPHandler.updateParsers(parser_general_headers)
  1594 DefaultHTTPHandler.updateParsers(parser_general_headers)
  1595 DefaultHTTPHandler.updateParsers(parser_request_headers)
  1595 DefaultHTTPHandler.updateParsers(parser_request_headers)
  1596 DefaultHTTPHandler.updateParsers(parser_response_headers)
  1596 DefaultHTTPHandler.updateParsers(parser_response_headers)