| Thomas Wouters | 0e3f591 | 2006-08-11 14:57:12 +0000 | [diff] [blame] | 1 | """Manage HTTP Response Headers | 
 | 2 |  | 
 | 3 | Much of this module is red-handedly pilfered from email.Message in the stdlib, | 
 | 4 | so portions are Copyright (C) 2001,2002 Python Software Foundation, and were | 
 | 5 | written by Barry Warsaw. | 
 | 6 | """ | 
 | 7 |  | 
| Thomas Wouters | 0e3f591 | 2006-08-11 14:57:12 +0000 | [diff] [blame] | 8 | # Regular expression that matches `special' characters in parameters, the | 
 | 9 | # existance of which force quoting of the parameter value. | 
 | 10 | import re | 
 | 11 | tspecials = re.compile(r'[ \(\)<>@,;:\\"/\[\]\?=]') | 
 | 12 |  | 
 | 13 | def _formatparam(param, value=None, quote=1): | 
 | 14 |     """Convenience function to format and return a key=value pair. | 
 | 15 |  | 
 | 16 |     This will quote the value if needed or if quote is true. | 
 | 17 |     """ | 
 | 18 |     if value is not None and len(value) > 0: | 
 | 19 |         if quote or tspecials.search(value): | 
 | 20 |             value = value.replace('\\', '\\\\').replace('"', r'\"') | 
 | 21 |             return '%s="%s"' % (param, value) | 
 | 22 |         else: | 
 | 23 |             return '%s=%s' % (param, value) | 
 | 24 |     else: | 
 | 25 |         return param | 
 | 26 |  | 
 | 27 |  | 
 | 28 |  | 
 | 29 |  | 
 | 30 |  | 
 | 31 |  | 
 | 32 |  | 
 | 33 |  | 
 | 34 |  | 
 | 35 |  | 
 | 36 |  | 
 | 37 |  | 
 | 38 |  | 
 | 39 |  | 
 | 40 | class Headers: | 
 | 41 |  | 
 | 42 |     """Manage a collection of HTTP response headers""" | 
 | 43 |  | 
 | 44 |     def __init__(self,headers): | 
| Guido van Rossum | 1325790 | 2007-06-07 23:15:56 +0000 | [diff] [blame] | 45 |         if not isinstance(headers, list): | 
| Thomas Wouters | 0e3f591 | 2006-08-11 14:57:12 +0000 | [diff] [blame] | 46 |             raise TypeError("Headers must be a list of name/value tuples") | 
| Antoine Pitrou | 38a66ad | 2009-01-03 18:41:49 +0000 | [diff] [blame] | 47 |         self._headers = [] | 
 | 48 |         for k, v in headers: | 
 | 49 |             k = self._convert_string_type(k) | 
 | 50 |             v = self._convert_string_type(v) | 
 | 51 |             self._headers.append((k, v)) | 
 | 52 |  | 
 | 53 |     def _convert_string_type(self, value): | 
 | 54 |         """Convert/check value type.""" | 
 | 55 |         if isinstance(value, str): | 
 | 56 |             return value | 
 | 57 |         assert isinstance(value, bytes), ("Header names/values must be" | 
 | 58 |             " a string or bytes object (not {0})".format(value)) | 
 | 59 |         return str(value, "iso-8859-1") | 
| Thomas Wouters | 0e3f591 | 2006-08-11 14:57:12 +0000 | [diff] [blame] | 60 |  | 
 | 61 |     def __len__(self): | 
 | 62 |         """Return the total number of headers, including duplicates.""" | 
 | 63 |         return len(self._headers) | 
 | 64 |  | 
 | 65 |     def __setitem__(self, name, val): | 
 | 66 |         """Set the value of a header.""" | 
 | 67 |         del self[name] | 
| Antoine Pitrou | 38a66ad | 2009-01-03 18:41:49 +0000 | [diff] [blame] | 68 |         self._headers.append( | 
 | 69 |             (self._convert_string_type(name), self._convert_string_type(val))) | 
| Thomas Wouters | 0e3f591 | 2006-08-11 14:57:12 +0000 | [diff] [blame] | 70 |  | 
 | 71 |     def __delitem__(self,name): | 
 | 72 |         """Delete all occurrences of a header, if present. | 
 | 73 |  | 
 | 74 |         Does *not* raise an exception if the header is missing. | 
 | 75 |         """ | 
| Antoine Pitrou | a9ecbda | 2009-01-03 20:28:05 +0000 | [diff] [blame] | 76 |         name = self._convert_string_type(name.lower()) | 
| Guido van Rossum | b053cd8 | 2006-08-24 03:53:23 +0000 | [diff] [blame] | 77 |         self._headers[:] = [kv for kv in self._headers if kv[0].lower() != name] | 
| Thomas Wouters | 0e3f591 | 2006-08-11 14:57:12 +0000 | [diff] [blame] | 78 |  | 
 | 79 |     def __getitem__(self,name): | 
 | 80 |         """Get the first header value for 'name' | 
 | 81 |  | 
 | 82 |         Return None if the header is missing instead of raising an exception. | 
 | 83 |  | 
 | 84 |         Note that if the header appeared multiple times, the first exactly which | 
 | 85 |         occurrance gets returned is undefined.  Use getall() to get all | 
 | 86 |         the values matching a header field name. | 
 | 87 |         """ | 
 | 88 |         return self.get(name) | 
 | 89 |  | 
 | 90 |  | 
 | 91 |  | 
 | 92 |  | 
 | 93 |  | 
| Guido van Rossum | e2b70bc | 2006-08-18 22:13:04 +0000 | [diff] [blame] | 94 |     def __contains__(self, name): | 
| Thomas Wouters | 0e3f591 | 2006-08-11 14:57:12 +0000 | [diff] [blame] | 95 |         """Return true if the message contains the header.""" | 
 | 96 |         return self.get(name) is not None | 
 | 97 |  | 
| Thomas Wouters | 0e3f591 | 2006-08-11 14:57:12 +0000 | [diff] [blame] | 98 |  | 
 | 99 |     def get_all(self, name): | 
 | 100 |         """Return a list of all the values for the named field. | 
 | 101 |  | 
 | 102 |         These will be sorted in the order they appeared in the original header | 
 | 103 |         list or were added to this instance, and may contain duplicates.  Any | 
 | 104 |         fields deleted and re-inserted are always appended to the header list. | 
 | 105 |         If no fields exist with the given name, returns an empty list. | 
 | 106 |         """ | 
| Antoine Pitrou | a9ecbda | 2009-01-03 20:28:05 +0000 | [diff] [blame] | 107 |         name = self._convert_string_type(name.lower()) | 
| Thomas Wouters | 0e3f591 | 2006-08-11 14:57:12 +0000 | [diff] [blame] | 108 |         return [kv[1] for kv in self._headers if kv[0].lower()==name] | 
 | 109 |  | 
 | 110 |  | 
 | 111 |     def get(self,name,default=None): | 
 | 112 |         """Get the first header value for 'name', or return 'default'""" | 
| Antoine Pitrou | a9ecbda | 2009-01-03 20:28:05 +0000 | [diff] [blame] | 113 |         name = self._convert_string_type(name.lower()) | 
| Thomas Wouters | 0e3f591 | 2006-08-11 14:57:12 +0000 | [diff] [blame] | 114 |         for k,v in self._headers: | 
 | 115 |             if k.lower()==name: | 
 | 116 |                 return v | 
 | 117 |         return default | 
 | 118 |  | 
 | 119 |  | 
 | 120 |     def keys(self): | 
 | 121 |         """Return a list of all the header field names. | 
 | 122 |  | 
 | 123 |         These will be sorted in the order they appeared in the original header | 
 | 124 |         list, or were added to this instance, and may contain duplicates. | 
 | 125 |         Any fields deleted and re-inserted are always appended to the header | 
 | 126 |         list. | 
 | 127 |         """ | 
 | 128 |         return [k for k, v in self._headers] | 
 | 129 |  | 
 | 130 |  | 
 | 131 |  | 
 | 132 |  | 
 | 133 |     def values(self): | 
 | 134 |         """Return a list of all header values. | 
 | 135 |  | 
 | 136 |         These will be sorted in the order they appeared in the original header | 
 | 137 |         list, or were added to this instance, and may contain duplicates. | 
 | 138 |         Any fields deleted and re-inserted are always appended to the header | 
 | 139 |         list. | 
 | 140 |         """ | 
 | 141 |         return [v for k, v in self._headers] | 
 | 142 |  | 
 | 143 |     def items(self): | 
 | 144 |         """Get all the header fields and values. | 
 | 145 |  | 
 | 146 |         These will be sorted in the order they were in the original header | 
 | 147 |         list, or were added to this instance, and may contain duplicates. | 
 | 148 |         Any fields deleted and re-inserted are always appended to the header | 
 | 149 |         list. | 
 | 150 |         """ | 
 | 151 |         return self._headers[:] | 
 | 152 |  | 
 | 153 |     def __repr__(self): | 
| Brett Cannon | 0b70cca | 2006-08-25 02:59:59 +0000 | [diff] [blame] | 154 |         return "Headers(%r)" % self._headers | 
| Thomas Wouters | 0e3f591 | 2006-08-11 14:57:12 +0000 | [diff] [blame] | 155 |  | 
 | 156 |     def __str__(self): | 
 | 157 |         """str() returns the formatted headers, complete with end line, | 
 | 158 |         suitable for direct HTTP transmission.""" | 
 | 159 |         return '\r\n'.join(["%s: %s" % kv for kv in self._headers]+['','']) | 
 | 160 |  | 
 | 161 |     def setdefault(self,name,value): | 
 | 162 |         """Return first matching header value for 'name', or 'value' | 
 | 163 |  | 
 | 164 |         If there is no header named 'name', add a new header with name 'name' | 
 | 165 |         and value 'value'.""" | 
 | 166 |         result = self.get(name) | 
 | 167 |         if result is None: | 
| Antoine Pitrou | 38a66ad | 2009-01-03 18:41:49 +0000 | [diff] [blame] | 168 |             self._headers.append((self._convert_string_type(name), | 
 | 169 |                 self._convert_string_type(value))) | 
| Thomas Wouters | 0e3f591 | 2006-08-11 14:57:12 +0000 | [diff] [blame] | 170 |             return value | 
 | 171 |         else: | 
 | 172 |             return result | 
 | 173 |  | 
 | 174 |  | 
 | 175 |     def add_header(self, _name, _value, **_params): | 
 | 176 |         """Extended header setting. | 
 | 177 |  | 
 | 178 |         _name is the header field to add.  keyword arguments can be used to set | 
 | 179 |         additional parameters for the header field, with underscores converted | 
 | 180 |         to dashes.  Normally the parameter will be added as key="value" unless | 
 | 181 |         value is None, in which case only the key will be added. | 
 | 182 |  | 
 | 183 |         Example: | 
 | 184 |  | 
 | 185 |         h.add_header('content-disposition', 'attachment', filename='bud.gif') | 
 | 186 |  | 
 | 187 |         Note that unlike the corresponding 'email.Message' method, this does | 
 | 188 |         *not* handle '(charset, language, value)' tuples: all values must be | 
 | 189 |         strings or None. | 
 | 190 |         """ | 
 | 191 |         parts = [] | 
 | 192 |         if _value is not None: | 
| Antoine Pitrou | 38a66ad | 2009-01-03 18:41:49 +0000 | [diff] [blame] | 193 |             _value = self._convert_string_type(_value) | 
| Thomas Wouters | 0e3f591 | 2006-08-11 14:57:12 +0000 | [diff] [blame] | 194 |             parts.append(_value) | 
 | 195 |         for k, v in _params.items(): | 
| Antoine Pitrou | 38a66ad | 2009-01-03 18:41:49 +0000 | [diff] [blame] | 196 |             k = self._convert_string_type(k) | 
| Thomas Wouters | 0e3f591 | 2006-08-11 14:57:12 +0000 | [diff] [blame] | 197 |             if v is None: | 
 | 198 |                 parts.append(k.replace('_', '-')) | 
 | 199 |             else: | 
| Antoine Pitrou | 38a66ad | 2009-01-03 18:41:49 +0000 | [diff] [blame] | 200 |                 v = self._convert_string_type(v) | 
| Thomas Wouters | 0e3f591 | 2006-08-11 14:57:12 +0000 | [diff] [blame] | 201 |                 parts.append(_formatparam(k.replace('_', '-'), v)) | 
| Antoine Pitrou | 38a66ad | 2009-01-03 18:41:49 +0000 | [diff] [blame] | 202 |         self._headers.append((self._convert_string_type(_name), "; ".join(parts))) | 
| Thomas Wouters | 0e3f591 | 2006-08-11 14:57:12 +0000 | [diff] [blame] | 203 |  | 
 | 204 |  | 
 | 205 |  | 
 | 206 |  | 
 | 207 |  | 
 | 208 |  | 
 | 209 |  | 
 | 210 |  | 
 | 211 |  | 
 | 212 |  | 
 | 213 |  | 
 | 214 |  | 
 | 215 |  | 
 | 216 |  | 
 | 217 |  | 
 | 218 | # |