diff options
| author | cce <devnull@localhost> | 2005-12-25 23:16:43 +0000 |
|---|---|---|
| committer | cce <devnull@localhost> | 2005-12-25 23:16:43 +0000 |
| commit | 843425bf2c3f36d7fae7ae34ffcadd7f4f9f6280 (patch) | |
| tree | dad7f118dd83bfdc590ee0b70ffb3375819f505c /paste/httpheaders.py | |
| parent | b9e0615895518bf4a4561563950863171fc71885 (diff) | |
| download | paste-843425bf2c3f36d7fae7ae34ffcadd7f4f9f6280.tar.gz | |
Adding httpheaders, which is similar to httpexceptions only that it
contains a full set of headers /w their corresponding category, version,
and an indicator if they are single value or multi-value
Diffstat (limited to 'paste/httpheaders.py')
| -rw-r--r-- | paste/httpheaders.py | 180 |
1 files changed, 180 insertions, 0 deletions
diff --git a/paste/httpheaders.py b/paste/httpheaders.py new file mode 100644 index 0000000..9b3aace --- /dev/null +++ b/paste/httpheaders.py @@ -0,0 +1,180 @@ +# (c) 2005 Ian Bicking, Clark C. Evans and contributors +# This module is part of the Python Paste Project and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php +# Some of this code was funded by http://prometheusresearch.com +""" +HTTP Headers + +This contains useful information about various HTTP Headers; eventually +including parsers/constructors for them. It is modeled after the +HTTPExceptions class; only that a header such as 'Content-Type' is +converted into Python as HTTP_CONTENT_TYPE. Each HTTPHeader is a string +value, with various attributes describing how that header behaves; the +string value is the "common form" as described in RFC 2616. It also +overrides sorting so that general headers go first, followed by +request/response headers, and then entity headers. + +It is planned that HTTPHeader will grow three methods: + + ``parse()`` This will parse content of the corresponding header + and return a dictionary of its components for + ``singular`` headers, and a list of dict items for + the other headers. + + ``compose()`` This will take N keyword arguments corresponding + to the various parts of the header and will produce + a well-formed header value. For example, the + cashe_control and content_disposition code in + fileapp.py could move here. + + ``__call__()`` This will be similar to ``compose()`` only that it + will return a (header, value) tuple suitable for + a WSGI ``response_headers`` list. + +""" +__all__ = ['get_header','HTTPHeader' ] + +_headers = {} + +def get_header(name, raiseError=True): + """ + This function finds the corresponding ``HTTPHeader`` for the + ``name`` provided. So that python-style names can be used, + underscores are converted to dashes before the lookup. + """ + if isinstance(name,HTTPHeader): + return name + retval = _headers.get(name.strip().lower().replace("_","-")) + if not retval and raiseError: + raise NameError(name) + return retval + +class HTTPHeader(str): + """ + HTTP header field names in their normalized "common form" as given + by their source specification. + + Constructor Arguments: + + ``name`` This is the primary string value of the + header name and is meant to reflect the + "common form" of the header as provided in + its corresponding specification. + + ``category`` The kind of header field, one of: + - ``general`` + - ``request`` + - ``response`` + - ``entity`` + + ``version`` The version of HTTP with which the header + should be recognized (ie, don't send 1.1 + headers to a 1.0 client). + + ``singular`` This is a boolean value that indicates if only + one value for this header is allowed. The + default is ``False`` which allows several. + + The collected versions of initialized header instances are immediately + registered and accessable through the ``get_header`` function. + """ + #@@: add field-name validation + def __new__(cls, name, category, version, singular): + self = get_header(name, raiseError=False) + if self: + # Allow the registration to happen again, but assert + # that everything is identical. + assert self == name, \ + "duplicate registration with different capitalization" + assert self.category == category, \ + "duplicate registration with different category " + assert self.version == version, \ + "duplicate registration with different HTTP version" + assert self.singular == singular, \ + "duplicate registration with different value cardnality" + assert cls == self.__class__, \ + "duplicate registration with different class" + else: + assert version, "registration requires a HTTP Version" + assert isinstance(version,str), "HTTP version is a string" + assert singular in (True,False), "singular must be boolean" + assert category in ('general','request','response','entity') + self = str.__new__(cls, name) + self.version = version + self.singular = singular + self.category = category + self._catsort = {'general': 1, 'request': 2, 'response': 2, + 'entity': 3}[category] + assert self.lower() not in _headers + _headers[self.lower()] = self + return self + + def __lt__(self, other): + """ + Re-define sorting so that general headers are first, followed + by request/response headers, and then entity headers. The + list.sort() methods use the less-than operator for this purpose. + """ + if isinstance(other,HTTPHeader) and self._catsort != other._catsort: + return self._catsort < other._catsort + return str.__lt__(self, other) + +# +# For now, construct a minimalistic version of the field-names; at a +# later date more complicated headers may sprout content constructors. +# +for (name, category, version, singular, comment) in \ +(("Accept" , 'request' , '1.1', False, 'RFC 2616 $14.1' ) +,("Accept-Charset" , 'request' , '1.1', False, 'RFC 2616 $14.2' ) +,("Accept-Encoding" , 'request' , '1.1', False, 'RFC 2616 $14.3' ) +,("Accept-Language" , 'request' , '1.1', False, 'RFC 2616 $14.4' ) +,("Accept-Ranges" , 'response', '1.1', False, 'RFC 2616 $14.5' ) +,("Age" , 'response', '1.1', True , 'RFC 2616 $14.6' ) +,("Allow" , 'entity' , '1.0', False, 'RFC 2616 $14.7' ) +,("Authorization" , 'request' , '1.0', True , 'RFC 2616 $14.8' ) +,("Cache-Control" , 'general' , '1.1', False, 'RFC 2616 $14.9' ) +,("Cookie" , 'request' , '1.0', False, 'RFC 2109 / Netscape') +,("Connection" , 'general' , '1.1', False, 'RFC 2616 $14.10' ) +,("Content-Encoding" , 'entity' , '1.0', False, 'RFC 2616 $14.11' ) +,("Content-Language" , 'entity' , '1.1', False, 'RFC 2616 $14.12' ) +,("Content-Length" , 'entity' , '1.0', True , 'RFC 2616 $14.13' ) +,("Content-Location" , 'entity' , '1.1', True , 'RFC 2616 $14.14' ) +,("Content-MD5" , 'entity' , '1.1', True , 'RFC 2616 $14.15' ) +,("Content-Range" , 'entity' , '1.1', True , 'RFC 2616 $14.16' ) +,("Content-Type" , 'entity' , '1.0', True , 'RFC 2616 $14.17' ) +,("Date" , 'general' , '1.0', True , 'RFC 2616 $14.18' ) +,("ETag" , 'response', '1.1', True , 'RFC 2616 $14.19' ) +,("Expect" , 'request' , '1.1', False, 'RFC 2616 $14.20' ) +,("Expires" , 'entity' , '1.0', True , 'RFC 2616 $14.21' ) +,("From" , 'request' , '1.0', True , 'RFC 2616 $14.22' ) +,("Host" , 'request' , '1.1', True , 'RFC 2616 $14.23' ) +,("If-Match" , 'request' , '1.1', False, 'RFC 2616 $14.24' ) +,("If-Modified-Since" , 'request' , '1.0', True , 'RFC 2616 $14.25' ) +,("If-None-Match" , 'request' , '1.1', False, 'RFC 2616 $14.26' ) +,("If-Range" , 'request' , '1.1', True , 'RFC 2616 $14.27' ) +,("If-Unmodified-Since" , 'request' , '1.1', True , 'RFC 2616 $14.28' ) +,("Last-Modified" , 'entity' , '1.0', True , 'RFC 2616 $14.29' ) +,("Location" , 'response', '1.0', True , 'RFC 2616 $14.30' ) +,("Max-Forwards" , 'request' , '1.1', True , 'RFC 2616 $14.31' ) +,("Pragma" , 'general' , '1.0', False, 'RFC 2616 $14.32' ) +,("Proxy-Authenticate" , 'response', '1.1', False, 'RFC 2616 $14.33' ) +,("Proxy-Authorization" , 'request' , '1.1', True , 'RFC 2616 $14.34' ) +,("Range" , 'request' , '1.1', False, 'RFC 2616 $14.35' ) +,("Referer" , 'request' , '1.0', True , 'RFC 2616 $14.36' ) +,("Retry-After" , 'response', '1.1', True , 'RFC 2616 $14.37' ) +,("Server" , 'response', '1.0', True , 'RFC 2616 $14.38' ) +,("Set-Cookie" , 'response', '1.0', False, 'RFC 2109 / Netscape') +,("TE" , 'request' , '1.1', False, 'RFC 2616 $14.39' ) +,("Trailer" , 'general' , '1.1', False, 'RFC 2616 $14.40' ) +,("Transfer-Encoding" , 'general' , '1.1', False, 'RFC 2616 $14.41' ) +,("Upgrade" , 'general' , '1.1', False, 'RFC 2616 $14.42' ) +,("User-Agent" , 'request' , '1.0', True , 'RFC 2616 $14.43' ) +,("Vary" , 'response', '1.1', False, 'RFC 2616 $14.44' ) +,("Via" , 'general' , '1.1', False, 'RFC 2616 $14.45' ) +,("Warning" , 'general' , '1.1', False, 'RFC 2616 $14.46' ) +,("WWW-Authenticate" , 'response', '1.0', False, 'RFC 2616 $14.47' )): + head = HTTPHeader(name, category, version, singular) + head.__doc__ = comment + pyname = 'HTTP_' + name.replace("-","_").upper() + locals()[pyname] = head + __all__.append(pyname) |
