mercurial/hgweb/wsgiheaders.py
changeset 37605 74e1362585c0
child 41588 765a608c2108
equal deleted inserted replaced
37604:daafaff4e5be 37605:74e1362585c0
       
     1 """This was forked from cpython's wsgiref.headers module to work on bytes.
       
     2 
       
     3 Header from old file showing copyright is below.
       
     4 
       
     5 Much of this module is red-handedly pilfered from email.message in the stdlib,
       
     6 so portions are Copyright (C) 2001,2002 Python Software Foundation, and were
       
     7 written by Barry Warsaw.
       
     8 """
       
     9 
       
    10 # Regular expression that matches `special' characters in parameters, the
       
    11 # existence of which force quoting of the parameter value.
       
    12 from __future__ import absolute_import, print_function
       
    13 
       
    14 import re
       
    15 tspecials = re.compile(br'[ \(\)<>@,;:\\"/\[\]\?=]')
       
    16 
       
    17 def _formatparam(param, value=None, quote=1):
       
    18     """Convenience function to format and return a key=value pair.
       
    19     This will quote the value if needed or if quote is true.
       
    20     """
       
    21     if value is not None and len(value) > 0:
       
    22         if quote or tspecials.search(value):
       
    23             value = value.replace('\\', '\\\\').replace('"', r'\"')
       
    24             return '%s="%s"' % (param, value)
       
    25         else:
       
    26             return '%s=%s' % (param, value)
       
    27     else:
       
    28         return param
       
    29 
       
    30 
       
    31 class Headers(object):
       
    32     """Manage a collection of HTTP response headers"""
       
    33 
       
    34     def __init__(self, headers=None):
       
    35         headers = headers if headers is not None else []
       
    36         if type(headers) is not list:
       
    37             raise TypeError("Headers must be a list of name/value tuples")
       
    38         self._headers = headers
       
    39         if __debug__:
       
    40             for k, v in headers:
       
    41                 self._convert_string_type(k)
       
    42                 self._convert_string_type(v)
       
    43 
       
    44     def _convert_string_type(self, value):
       
    45         """Convert/check value type."""
       
    46         if type(value) is bytes:
       
    47             return value
       
    48         raise AssertionError(u"Header names/values must be"
       
    49                              u" of type bytes (got %s)" % repr(value))
       
    50 
       
    51     def __len__(self):
       
    52         """Return the total number of headers, including duplicates."""
       
    53         return len(self._headers)
       
    54 
       
    55     def __setitem__(self, name, val):
       
    56         """Set the value of a header."""
       
    57         del self[name]
       
    58         self._headers.append(
       
    59             (self._convert_string_type(name), self._convert_string_type(val)))
       
    60 
       
    61     def __delitem__(self, name):
       
    62         """Delete all occurrences of a header, if present.
       
    63         Does *not* raise an exception if the header is missing.
       
    64         """
       
    65         name = self._convert_string_type(name.lower())
       
    66         self._headers[:] = [kv for kv in self._headers if kv[0].lower() != name]
       
    67 
       
    68     def __getitem__(self, name):
       
    69         """Get the first header value for 'name'
       
    70         Return None if the header is missing instead of raising an exception.
       
    71         Note that if the header appeared multiple times, the first exactly which
       
    72         occurrence gets returned is undefined.  Use getall() to get all
       
    73         the values matching a header field name.
       
    74         """
       
    75         return self.get(name)
       
    76 
       
    77     def __contains__(self, name):
       
    78         """Return true if the message contains the header."""
       
    79         return self.get(name) is not None
       
    80 
       
    81 
       
    82     def get_all(self, name):
       
    83         """Return a list of all the values for the named field.
       
    84         These will be sorted in the order they appeared in the original header
       
    85         list or were added to this instance, and may contain duplicates.  Any
       
    86         fields deleted and re-inserted are always appended to the header list.
       
    87         If no fields exist with the given name, returns an empty list.
       
    88         """
       
    89         name = self._convert_string_type(name.lower())
       
    90         return [kv[1] for kv in self._headers if kv[0].lower()==name]
       
    91 
       
    92 
       
    93     def get(self, name, default=None):
       
    94         """Get the first header value for 'name', or return 'default'"""
       
    95         name = self._convert_string_type(name.lower())
       
    96         for k, v in self._headers:
       
    97             if k.lower()==name:
       
    98                 return v
       
    99         return default
       
   100 
       
   101 
       
   102     def keys(self):
       
   103         """Return a list of all the header field names.
       
   104         These will be sorted in the order they appeared in the original header
       
   105         list, or were added to this instance, and may contain duplicates.
       
   106         Any fields deleted and re-inserted are always appended to the header
       
   107         list.
       
   108         """
       
   109         return [k for k, v in self._headers]
       
   110 
       
   111     def values(self):
       
   112         """Return a list of all header values.
       
   113         These will be sorted in the order they appeared in the original header
       
   114         list, or were added to this instance, and may contain duplicates.
       
   115         Any fields deleted and re-inserted are always appended to the header
       
   116         list.
       
   117         """
       
   118         return [v for k, v in self._headers]
       
   119 
       
   120     def items(self):
       
   121         """Get all the header fields and values.
       
   122         These will be sorted in the order they were in the original header
       
   123         list, or were added to this instance, and may contain duplicates.
       
   124         Any fields deleted and re-inserted are always appended to the header
       
   125         list.
       
   126         """
       
   127         return self._headers[:]
       
   128 
       
   129     def __repr__(self):
       
   130         return "%s(%r)" % (self.__class__.__name__, self._headers)
       
   131 
       
   132     def __str__(self):
       
   133         """str() returns the formatted headers, complete with end line,
       
   134         suitable for direct HTTP transmission."""
       
   135         return '\r\n'.join(["%s: %s" % kv for kv in self._headers]+['',''])
       
   136 
       
   137     def __bytes__(self):
       
   138         return str(self).encode('iso-8859-1')
       
   139 
       
   140     def setdefault(self, name, value):
       
   141         """Return first matching header value for 'name', or 'value'
       
   142         If there is no header named 'name', add a new header with name 'name'
       
   143         and value 'value'."""
       
   144         result = self.get(name)
       
   145         if result is None:
       
   146             self._headers.append((self._convert_string_type(name),
       
   147                 self._convert_string_type(value)))
       
   148             return value
       
   149         else:
       
   150             return result
       
   151 
       
   152     def add_header(self, _name, _value, **_params):
       
   153         """Extended header setting.
       
   154         _name is the header field to add.  keyword arguments can be used to set
       
   155         additional parameters for the header field, with underscores converted
       
   156         to dashes.  Normally the parameter will be added as key="value" unless
       
   157         value is None, in which case only the key will be added.
       
   158         Example:
       
   159         h.add_header('content-disposition', 'attachment', filename='bud.gif')
       
   160         Note that unlike the corresponding 'email.message' method, this does
       
   161         *not* handle '(charset, language, value)' tuples: all values must be
       
   162         strings or None.
       
   163         """
       
   164         parts = []
       
   165         if _value is not None:
       
   166             _value = self._convert_string_type(_value)
       
   167             parts.append(_value)
       
   168         for k, v in _params.items():
       
   169             k = self._convert_string_type(k)
       
   170             if v is None:
       
   171                 parts.append(k.replace('_', '-'))
       
   172             else:
       
   173                 v = self._convert_string_type(v)
       
   174                 parts.append(_formatparam(k.replace('_', '-'), v))
       
   175         self._headers.append(
       
   176             (self._convert_string_type(_name), "; ".join(parts)))