summaryrefslogtreecommitdiff
path: root/paste
diff options
context:
space:
mode:
authorcce <devnull@localhost>2005-12-31 06:57:42 +0000
committercce <devnull@localhost>2005-12-31 06:57:42 +0000
commit98f2eef2897f1670bf5540e6199724aaf56ea5b4 (patch)
tree9543b1dad43f0772829ed8a2748ca6b2f8f6530e /paste
parent59ba5ee40b7da9f406a29f558ec21878dbf291b3 (diff)
downloadpaste-98f2eef2897f1670bf5540e6199724aaf56ea5b4.tar.gz
- cleaned up documentation for paste.auth.cookie
- cleaned up documentation for paste.auth.form - cleaned up documentation for paste.auth.basic - converted InternalServerError into its own class (httpexceptions) - converted BadRequest into its own class (httpexceptions) - a few minor cleanups
Diffstat (limited to 'paste')
-rw-r--r--paste/auth/__init__.py4
-rw-r--r--paste/auth/basic.py92
-rw-r--r--paste/auth/cas.py19
-rw-r--r--paste/auth/cookie.py179
-rw-r--r--paste/auth/form.py115
-rw-r--r--paste/auth/open_id.py2
-rw-r--r--paste/httpexceptions.py94
-rw-r--r--paste/httpheaders.py5
8 files changed, 337 insertions, 173 deletions
diff --git a/paste/auth/__init__.py b/paste/auth/__init__.py
index 76835b0..ad914d6 100644
--- a/paste/auth/__init__.py
+++ b/paste/auth/__init__.py
@@ -1,3 +1,7 @@
"""
Package for authentication/identification of requests.
+
+The objective of this package is to provide single-focused middleware
+components that implement a particular specification. Integration of
+the components into a usable system is up to a higher-level framework.
"""
diff --git a/paste/auth/basic.py b/paste/auth/basic.py
index 39764a9..b3e8f36 100644
--- a/paste/auth/basic.py
+++ b/paste/auth/basic.py
@@ -3,21 +3,34 @@
# the MIT License: http://www.opensource.org/licenses/mit-license.php
# This code was written with funding by http://prometheusresearch.com
"""
-Basic Authentication
+Basic HTTP/1.0 Authentication
+This module implements ``Basic`` authentication as described in HTTP/1.0
+specification [1]_ . Do not use this module unless you need to work
+with very out-dated clients, instead use ``digest`` authentication.
+Basically, you just put this module before your application, and it
+takes care of requesting and handling authentication requests.
+
+>>> from paste.wsgilib import dump_environ
+>>> from paste.util.httpserver import serve
+>>> realm = 'Test Realm'
+>>> def authfunc(username, password):
+... return username == password
+>>> serve(AuthBasicHandler(dump_environ, realm, authfunc))
+serving on...
+
+.. [1] http://www.w3.org/Protocols/HTTP/1.0/draft-ietf-http-spec.html#BasicAA
"""
from paste.httpexceptions import HTTPUnauthorized
-
-class BasicAuthenticator:
- """ Implementation of only 'Basic' authentication in 2617 """
- def __init__(self, realm, userfunc):
- """
- realm is a globally unique URI like tag:clarkevans.com,2005:basic
- that represents the authenticating authority
- userfunc(username, password) -> boolean
- """
+
+class AuthBasicAuthenticator:
+ """
+ implements ``Basic`` authentication details
+ """
+ type = 'basic'
+ def __init__(self, realm, authfunc):
self.realm = realm
- self.userfunc = userfunc
+ self.authfunc = authfunc
def build_authentication(self):
head = [('WWW-Authenticate','Basic realm="%s"' % self.realm)]
@@ -31,37 +44,60 @@ class BasicAuthenticator:
return self.build_authentication()
auth = auth.strip().decode('base64')
username, password = auth.split(':')
- if self.userfunc(username, password):
+ if self.authfunc(username, password):
return username
return self.build_authentication()
__call__ = authenticate
-def AuthBasicHandler(application, realm, userfunc):
- authenticator = BasicAuthenticator(realm, userfunc)
- def basic_application(environ, start_response):
+class AuthBasicHandler:
+ """
+ HTTP/1.0 ``Basic`` authentication middleware
+
+ Parameters:
+
+ ``application``
+
+ The application object is called only upon successful
+ authentication, and can assume ``environ['REMOTE_USER']``
+ is set. If the ``REMOTE_USER`` is already set, this
+ middleware is simply pass-through.
+
+ ``realm``
+
+ This is a identifier for the authority that is requesting
+ authorization. It is shown to the user and should be unique
+ within the domain it is being used.
+
+ ``authfunc``
+
+ This is a mandatory user-defined function which takes a
+ ``username`` and ``password`` for its first and second
+ arguments respectively. It should return ``True`` if
+ the user is authenticated.
+
+ """
+ def __init__(self, application, realm, authfunc):
+ self.application = application
+ self.authenticate = AuthBasicAuthenticator(realm, authfunc)
+
+ def __call__(self, environ, start_response):
username = environ.get('REMOTE_USER','')
if not username:
authorization = environ.get('HTTP_AUTHORIZATION','')
- result = authenticator(authorization)
- if isinstance(result,str):
+ result = self.authenticate(authorization)
+ if isinstance(result, str):
environ['AUTH_TYPE'] = 'basic'
environ['REMOTE_USER'] = result
else:
return result.wsgi_application(environ, start_response)
- return application(environ, start_response)
- return basic_application
+ return self.application(environ, start_response)
middleware = AuthBasicHandler
__all__ = ['AuthBasicHandler']
-if '__main__' == __name__:
- realm = 'tag:clarkevans.com,2005:basic'
- def userfunc(username, password):
- return username == password
- from paste.wsgilib import dump_environ
- from paste.util.httpserver import serve
- from paste.httpexceptions import *
- serve(HTTPExceptionHandler(
- AuthBasicHandler(dump_environ, realm, userfunc)))
+
+if "__main__" == __name__:
+ import doctest
+ doctest.testmod(optionflags=doctest.ELLIPSIS)
diff --git a/paste/auth/cas.py b/paste/auth/cas.py
index 613044e..92c7996 100644
--- a/paste/auth/cas.py
+++ b/paste/auth/cas.py
@@ -3,7 +3,7 @@
# the MIT License: http://www.opensource.org/licenses/mit-license.php
# This code was written with funding by http://prometheusresearch.com
"""
-CAS 1.0 Authentication
+CAS 1.0 Authentication
The Central Authentication System is a straight-forward single sign-on
mechanism developed by Yale University's ITS department. It has since
@@ -30,8 +30,9 @@ class CASAuthenticate(HTTPSeeOther):
def AuthCASHandler(application, authority):
"""
- This middleware implements CAS 1.0 Authentication There are several
- possible outcomes:
+ middleware to implement CAS 1.0 authentication
+
+ There are several possible outcomes:
0. If the REMOTE_USER environment variable is already populated;
then this middleware is a no-op, and the request is passed along
@@ -48,10 +49,14 @@ def AuthCASHandler(application, authority):
will send them back to this same URL, only with a 'ticket' query
argument.
- authority:
- This is a fully-qualified URL to a CAS 1.0 service. The URL
- should end with a '/' and have the 'login' and 'validate'
- sub-paths as described in the CAS 1.0 documentation.
+ Parameters:
+
+ ``authority``
+
+ This is a fully-qualified URL to a CAS 1.0 service. The URL
+ should end with a '/' and have the 'login' and 'validate'
+ sub-paths as described in the CAS 1.0 documentation.
+
"""
assert authority.endswith("/") and authority.startswith("http")
def cas_application(environ, start_response):
diff --git a/paste/auth/cookie.py b/paste/auth/cookie.py
index 1282cfb..b2f93f0 100644
--- a/paste/auth/cookie.py
+++ b/paste/auth/cookie.py
@@ -5,30 +5,44 @@
"""
Cookie "Saved" Authentication
-This Authentication middleware saves the current REMOTE_USER, and any
-other environment variables specified, in a cookie so that it can be
+This authentication middleware saves the current REMOTE_USER and any
+other environment variables specified in a cookie so that it can be
retrieved during the next request without requiring re-authentication.
This uses a session cookie on the client side (so it goes away when the
user closes their window) and does server-side expiration.
- NOTE: If you use HTTPFound or other redirections; it is likely that
- this module will not work unless it is _before_ the middleware
- that converts the exception into a response. Therefore, in your
- component stack, put this component darn near the top (before
- the exception handler).
-
-According to the cookie specifications, RFC2068 and RFC2109, browsers
-should allow each domain at least 20 cookies; each one with a content
-size of at least 4k (4096 bytes). This is rather small; so one should
-be parsimonious in your cookie name/sizes. It is recommended via the
-HMAC specification (RFC 2104) that the secret key be 64 bytes since
-this is the block size of the hashing.
+Following is a very simple example where a form is presented asking
+for a user name (no actual checking), and dummy session identifier
+(perhaps corresponding to a database session id) is stored in the
+cookie.
+
+>>> from paste.util.httpserver import serve
+>>> from paste.fileapp import DataApp
+>>> from paste.httpexceptions import *
+>>> from paste.wsgilib import parse_querystring
+>>> def testapp(environ, start_response):
+... user = dict(parse_querystring(environ)).get('user','')
+... if user:
+... environ['REMOTE_USER'] = user
+... environ['MY_SESSION'] = '1234' # save this too
+... environ['paste.auth.cookie'].append('MY_SESSION')
+... if environ.get('REMOTE_USER'):
+... page = '<html><body>Welcome %s (%s)</body></html>'
+... page %= (environ['REMOTE_USER'], environ['MY_SESSION'])
+... else:
+... page = ('<html><body><form><input name="user" />'
+... '<input type="submit" /></form></body></html>')
+... return DataApp(page, content_type="text/html")(
+... environ, start_response)
+>>> serve(AuthCookieHandler(testapp))
+serving on...
+
"""
+
import sha, hmac, base64, random, time, string, warnings
from paste.request import get_cookies
def make_time(value):
- """ return a human readable timestmp """
return time.strftime("%Y%m%d%H%M",time.gmtime(value))
_signature_size = len(hmac.new('x','x',sha).digest())
_header_size = _signature_size + len(make_time(time.time()))
@@ -52,10 +66,12 @@ def new_secret():
""" returns a 64 byte secret """
return ''.join(random.sample(_all_chars,64))
-class CookieSigner:
+class AuthCookieSigner:
"""
+ save/restore ``environ`` entries via digially signed cookie
+
This class converts content into a timed and digitally signed
- cookie, as well as having the facility to reverse this procedure.
+ cookie, as well as having the facility to reverse this procedure.
If the cookie, after the content is encoded and signed exceeds the
maximum length (4096), then CookieTooLarge exception is raised.
@@ -65,6 +81,36 @@ class CookieSigner:
has closed). Second, the user's clock may be wrong (perhaps
intentionally). The timeout is specified in minutes; and expiration
date returned is rounded to one second.
+
+ Constructor Arguments:
+
+ ``secret``
+
+ This is a secret key if you want to syncronize your keys so
+ that the cookie will be good across a cluster of computers.
+ It is recommended via the HMAC specification (RFC 2104) that
+ the secret key be 64 bytes since this is the block size of
+ the hashing. If you do not provide a secret key, a random
+ one is generated each time you create the handler; this
+ should be sufficient for most cases.
+
+ ``timeout``
+
+ This is the time (in minutes) from which the cookie is set
+ to expire. Note that on each request a new (replacement)
+ cookie is sent, hence this is effectively a session timeout
+ parameter for your entire cluster. If you do not provide a
+ timeout, it is set at 30 minutes.
+
+ ``maxlen``
+
+ This is the maximum size of the *signed* cookie; hence the
+ actual content signed will be somewhat less. If the cookie
+ goes over this size, a ``CookieTooLarge`` exception is
+ raised so that unexpected handling of cookies on the client
+ side are avoided. By default this is set at 4k (4096 bytes),
+ which is the standard cookie size limit.
+
"""
def __init__(self, secret = None, timeout = None, maxlen = None):
self.timeout = timeout or 30
@@ -86,7 +132,7 @@ class CookieSigner:
return cookie
def auth(self,cookie):
- """
+ """
Authenticate the cooke using the signature, verify that it
has not expired; and return the cookie's content
"""
@@ -111,17 +157,12 @@ class CookieSigner:
class AuthCookieEnviron(list):
"""
- This object is a list of `environ` keys that were restored from or
- will be added to the digially signed cookie. This object can be
- accessed from an `environ` variable by using this module's name.
-
- environ['paste.auth.cookie'].append('your.environ.variable')
-
- This environment-specific object can also be used to access/configure
- the base handler for all requests by using:
-
- environ['paste.auth.cookie'].handler
+ a list of environment keys to be saved via cookie
+ An instance of this object, found at ``environ['paste.auth.cookie']``
+ lists the `environ` keys that were restored from or will be added
+ to the digially signed cookie. This object can be accessed from an
+ `environ` variable by using this module's name.
"""
def __init__(self, handler, scanlist):
list.__init__(self, scanlist)
@@ -133,7 +174,9 @@ class AuthCookieEnviron(list):
class AuthCookieHandler:
"""
- This middleware uses cookies to stash-away a previously authenticated
+ the actual handler that should be put in your middleware stack
+
+ This middleware uses cookies to stash-away a previously authenticated
user (and perhaps other variables) so that re-authentication is not
needed. This does not implement sessions; and therefore N servers
can be syncronized to accept the same saved authentication if they
@@ -144,23 +187,55 @@ class AuthCookieHandler:
`environ` keys as well -- but be careful not to exceed 2-3k (so that
the encoded and signed cookie does not exceed 4k). You can ask it
to handle other environment variables by doing:
-
- environ['paste.auth.cookie'].append('your.environ.variable')
+ ``environ['paste.auth.cookie'].append('your.environ.variable')``
+
+
+ Constructor Arguments:
+
+ ``application``
+
+ This is the wrapped application which will have access to
+ the ``environ['REMOTE_USER']`` restored by this middleware.
+
+ ``cookie_name``
+
+ The name of the cookie used to store this content, by default
+ it is ``PASTE_AUTH_COOKIE``.
+
+ ``scanlist``
+
+ This is the initial set of ``environ`` keys to save/restore
+ to the signed cookie. By default is consists only of
+ ``REMOTE_USER``; any tuple or list of environment keys
+ will work. However, be careful, as the total saved size is
+ limited to around 3k.
+
+ ``signer``
+
+ This is the signer object used to create the actual cookie
+ values, by default, it is ``AuthCookieSigner`` and is passed
+ the remaining arguments to this function: ``secret``,
+ ``timeout``, and ``maxlen``.
+
+ At this time, each cookie is individually signed. To store more
+ than the 4k of data; it is possible to sub-class this object to
+ provide different ``environ_name`` and ``cookie_name``
"""
environ_name = 'paste.auth.cookie'
- signer_class = CookieSigner
+ cookie_name = 'PASTE_AUTH_COOKIE'
+ signer_class = AuthCookieSigner
environ_class = AuthCookieEnviron
- def __init__(self, application, cookie_name=None, secret=None,
- timeout=None, maxlen=None, signer=None, scanlist = None):
+ def __init__(self, application, cookie_name=None, scanlist=None,
+ signer=None, secret=None, timeout=None, maxlen=None):
if not signer:
signer = self.signer_class(secret,timeout,maxlen)
self.signer = signer
self.scanlist = scanlist or ('REMOTE_USER',)
self.application = application
- self.cookie_name = cookie_name or 'PASTE_AUTH_COOKIE'
-
+ self.cookie_name = cookie_name or self.cookie_name
+
def __call__(self, environ, start_response):
if self.environ_name in environ:
raise AssertionError("AuthCookie already installed!")
@@ -188,7 +263,7 @@ class AuthCookieHandler:
def response_hook(status, response_headers, exc_info=None):
"""
- Scan the environment for keys specified in the scanlist,
+ Scan the environment for keys specified in the scanlist,
pack up their values, signs the content and issues a cookie.
"""
scanlist = environ.get(self.environ_name)
@@ -210,26 +285,10 @@ class AuthCookieHandler:
middleware = AuthCookieHandler
-__all__ = ['AuthCookieHandler']
-
-if '__main__' == __name__:
- from paste.wsgilib import parse_querystring
- def AuthStupidHandler(application):
- def authstupid_application(environ, start_response):
- args = dict(parse_querystring(environ))
- user = args.get('user','')
- if user:
- environ['REMOTE_USER'] = user
- environ['AUTH_TYPE'] = 'stupid'
- test = args.get('test','')
- if test:
- environ['paste.auth.cookie.test'] = test
- environ['paste.auth.cookie'].append('paste.auth.cookie.test')
- return application(environ, start_response)
- return authstupid_application
- from paste.wsgilib import dump_environ
- from paste.util.httpserver import serve
- from paste.httpexceptions import *
- serve(AuthCookieHandler(
- HTTPExceptionHandler(
- AuthStupidHandler(dump_environ))))
+__all__ = ['AuthCookieHandler', 'AuthCookieSigner', 'AuthCookieEnviron']
+
+
+if "__main__" == __name__:
+ import doctest
+ doctest.testmod(optionflags=doctest.ELLIPSIS)
+
diff --git a/paste/auth/form.py b/paste/auth/form.py
index d32af2e..3dbe375 100644
--- a/paste/auth/form.py
+++ b/paste/auth/form.py
@@ -3,14 +3,31 @@
# the MIT License: http://www.opensource.org/licenses/mit-license.php
# This code was written with funding by http://prometheusresearch.com
"""
-HTTP Form Authentication
+Authentication via HTML Form
+
+This is a very simple HTML form login screen that asks for the username
+and password. This middleware component requires that an authorization
+function taking the name and passsword and that it be placed in your
+application stack. This class does not include any session management
+code or way to save the user's authorization; however, it is easy enough
+to put ``paste.auth.cookie`` in your application stack.
+
+>>> from paste.wsgilib import dump_environ
+>>> from paste.util.httpserver import serve
+>>> from paste.auth.cookie import AuthCookieHandler
+>>> from paste.auth.form import AuthFormHandler
+>>> def authfunc(username, password):
+... return username == password
+>>> serve(AuthCookieHandler(
+... AuthFormHandler(dump_environ, authfunc)))
+serving on...
"""
-from paste.wsgilib import parse_formvars, construct_url
+from paste.request import construct_url, parse_formvars
-template = """\
+TEMPLATE ="""\
<html>
- <head><title>Please Login</title></head>
+ <head><title>Please Login!</title></head>
<body>
<h1>Please Login</h1>
<form action="%s" method="post">
@@ -27,47 +44,83 @@ template = """\
</html>
"""
-def AuthFormHandler(application, userfunc, login_page = None):
- """ This causes a HTML form to be returned if REMOTE_USER has not
- been provided. This is a really simple implementation, it
- requires that the query arguments returned from the form have two
- variables "username" and "password". These are then passed to
- the userfunc; which should return True if authentication is granted.
+class AuthFormHandler:
+ """
+ HTML-based login middleware
+
+ This causes a HTML form to be returned if ``REMOTE_USER`` is
+ not found in the ``environ``. If the form is returned, the
+ ``username`` and ``password`` combination are given to a
+ user-supplied authentication function, ``authfunc``. If this
+ is successful, then application processing continues.
+
+ Parameters:
+
+ ``application``
+
+ The application object is called only upon successful
+ authentication, and can assume ``environ['REMOTE_USER']``
+ is set. If the ``REMOTE_USER`` is already set, this
+ middleware is simply pass-through.
+
+ ``authfunc``
+
+ This is a mandatory user-defined function which takes a
+ ``username`` and ``password`` for its first and second
+ arguments respectively. It should return ``True`` if
+ the user is authenticated.
+
+ ``template``
+
+ This is an optional (a default is provided) HTML
+ fragment that takes exactly one ``%s`` substution
+ argument; which *must* be used for the form's ``action``
+ to ensure that this middleware component does not alter
+ the current path. The HTML form must use ``POST`` and
+ have two input names: ``username`` and ``password``.
+
+ Since the authentication form is submitted (via ``POST``)
+ neither the ``PATH_INFO`` nor the ``QUERY_STRING`` are accessed,
+ and hence the current path remains _unaltered_ through the
+ entire authentication process. If authentication succeeds, the
+ ``REQUEST_METHOD`` is converted from a ``POST`` to a ``GET``,
+ so that a redirect is unnecessary (unlike most form auth
+ implementations)
"""
- login_page = login_page or template
- def form_application(environ, start_response):
+
+ def __init__(self, application, authfunc, template=None):
+ self.application = application
+ self.authfunc = authfunc
+ self.template = template or TEMPLATE
+
+ def __call__(self, environ, start_response):
username = environ.get('REMOTE_USER','')
if username:
- return application(environ, start_response)
+ return self.application(environ, start_response)
+
if 'POST' == environ['REQUEST_METHOD']:
- formvars = parse_formvars(environ)
+ formvars = parse_formvars(environ, include_get_vars=False)
username = formvars.get('username')
password = formvars.get('password')
if username and password:
- if userfunc(username,password):
+ if self.authfunc(username,password):
environ['AUTH_TYPE'] = 'form'
environ['REMOTE_USER'] = username
environ['REQUEST_METHOD'] = 'GET'
+ environ['CONTENT_LENGTH'] = ''
+ environ['CONTENT_TYPE'] = ''
del environ['paste.parsed_formvars']
- return application(environ, start_response)
+ return self.application(environ, start_response)
+
+ content = self.template % construct_url(environ)
start_response("200 OK",(('Content-Type', 'text/html'),
- ('Content-Length', len(login_page))))
- if "%s" in login_page:
- return [login_page % construct_url(environ) ]
- return [login_page]
- return form_application
+ ('Content-Length', len(content))))
+ return [content]
middleware = AuthFormHandler
__all__ = ['AuthFormHandler']
-if '__main__' == __name__:
- def userfunc(username, password):
- return username == password
- from paste.wsgilib import dump_environ
- from paste.util.httpserver import serve
- from paste.httpexceptions import *
- from cookie import AuthCookieHandler
- serve(HTTPExceptionHandler(
- AuthCookieHandler(
- AuthFormHandler(dump_environ, userfunc))))
+if "__main__" == __name__:
+ import doctest
+ doctest.testmod(optionflags=doctest.ELLIPSIS)
diff --git a/paste/auth/open_id.py b/paste/auth/open_id.py
index 9eade11..ad3a665 100644
--- a/paste/auth/open_id.py
+++ b/paste/auth/open_id.py
@@ -53,6 +53,8 @@ should be redirected to wherever they would normally go after a successful
login.
"""
+__all__ = ['AuthOpenIDHandler']
+
import cgi
import urlparse
import cgitb
diff --git a/paste/httpexceptions.py b/paste/httpexceptions.py
index 857535e..dd4e260 100644
--- a/paste/httpexceptions.py
+++ b/paste/httpexceptions.py
@@ -10,9 +10,9 @@ by defining a set of exceptions, all subclasses of HTTPException, and a
request handler (`middleware`) that catches these exceptions and turns
them into proper responses.
-This module defines exceptions according to RFC 2068 [1]: codes with
+This module defines exceptions according to RFC 2068 [1]_ : codes with
100-300 are not really errors; 400's are client errors, and 500's are
-server errors. According to the WSGI specification [2], the application
+server errors. According to the WSGI specification [2]_ , the application
can call ``start_response`` more then once only under two conditions:
(a) the response has not yet been sent, or (b) if the second and
subsequent invocations of ``start_response`` have a valid ``exc_info``
@@ -25,48 +25,50 @@ has been called are treated as serious errors and the ``exc_info`` is
filled-in with information needed for a lower level module to generate a
stack trace and log information.
-References:
-[1] http://www.python.org/peps/pep-0333.html#error-handling
-[2] http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5
-
Exception
HTTPException
HTTPRedirection
- 300 - HTTPMultipleChoices
- 301 - HTTPMovedPermanently
- 302 - HTTPFound
- 303 - HTTPSeeOther
- 304 - HTTPNotModified
- 305 - HTTPUseProxy
- 306 - Unused (not implemented, obviously)
- 307 - HTTPTemporaryRedirect
+ * 300 - HTTPMultipleChoices
+ * 301 - HTTPMovedPermanently
+ * 302 - HTTPFound
+ * 303 - HTTPSeeOther
+ * 304 - HTTPNotModified
+ * 305 - HTTPUseProxy
+ * 306 - Unused (not implemented, obviously)
+ * 307 - HTTPTemporaryRedirect
HTTPError
HTTPClientError
- 400 - HTTPBadRequest
- 401 - HTTPUnauthorized
- 402 - HTTPPaymentRequired
- 403 - HTTPForbidden
- 404 - HTTPNotFound
- 405 - HTTPMethodNotAllowed
- 406 - HTTPNotAcceptable
- 407 - HTTPProxyAuthenticationRequired
- 408 - HTTPRequestTimeout
- 409 - HTTPConfict
- 410 - HTTPGone
- 411 - HTTPLengthRequired
- 412 - HTTPPreconditionFailed
- 413 - HTTPRequestEntityTooLarge
- 414 - HTTPRequestURITooLong
- 415 - HTTPUnsupportedMediaType
- 416 - HTTPRequestRangeNotSatisfiable
- 417 - HTTPExpectationFailed
+ * 400 - HTTPBadRequest
+ * 401 - HTTPUnauthorized
+ * 402 - HTTPPaymentRequired
+ * 403 - HTTPForbidden
+ * 404 - HTTPNotFound
+ * 405 - HTTPMethodNotAllowed
+ * 406 - HTTPNotAcceptable
+ * 407 - HTTPProxyAuthenticationRequired
+ * 408 - HTTPRequestTimeout
+ * 409 - HTTPConfict
+ * 410 - HTTPGone
+ * 411 - HTTPLengthRequired
+ * 412 - HTTPPreconditionFailed
+ * 413 - HTTPRequestEntityTooLarge
+ * 414 - HTTPRequestURITooLong
+ * 415 - HTTPUnsupportedMediaType
+ * 416 - HTTPRequestRangeNotSatisfiable
+ * 417 - HTTPExpectationFailed
HTTPServerError
- 500 - HTTPInternalServerError
- 501 - HTTPNotImplemented
- 502 - HTTPBadGateway
- 503 - HTTPServiceUnavailable
- 504 - HTTPGatewayTimeout
- 505 - HTTPVersionNotSupported
+ * 500 - HTTPInternalServerError
+ * 501 - HTTPNotImplemented
+ * 502 - HTTPBadGateway
+ * 503 - HTTPServiceUnavailable
+ * 504 - HTTPGatewayTimeout
+ * 505 - HTTPVersionNotSupported
+
+References:
+
+.. [1] http://www.python.org/peps/pep-0333.html#error-handling
+.. [2] http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5
+
"""
import types
@@ -78,7 +80,7 @@ SERVER_NAME = 'WSGI Server'
class HTTPException(Exception):
"""
- an HTTP exception (including redirects or other status codes)
+ the HTTP exception base class
This encapsulates an HTTP response that interrupts normal application
flow; but one which is not necessarly an error condition. For
@@ -240,7 +242,7 @@ class HTTPException(Exception):
class HTTPError(HTTPException):
"""
- exception where an error has occurred
+ base class for status codes in the 400's and 500's
This is an exception which indicates that an error has occurred,
and that any work in progress should not be committed. These are
@@ -260,7 +262,7 @@ class HTTPError(HTTPException):
class HTTPRedirection(HTTPException):
"""
- a redirection
+ base class for 300's status code (redirections)
This is an abstract base class for 3xx redirection. It indicates
that further action needs to be taken by the user agent in order
@@ -359,7 +361,7 @@ class HTTPTemporaryRedirect(_HTTPMove):
class HTTPClientError(HTTPError):
"""
- an ``HTTPError`` where the client is in-error
+ base class for the 400's, where the client is in-error
This is an error condition in which the client is presumed to be
in-error. This is an expected problem, and thus is not considered
@@ -371,7 +373,8 @@ class HTTPClientError(HTTPError):
explanation = ('The server could not comply with the request since\r\n'
'it is either malformed or otherwise incorrect.\r\n')
-HTTPBadRequest = HTTPClientError
+class HTTPBadRequest(HTTPClientError):
+ pass
class HTTPUnauthorized(HTTPClientError):
required_headers = ('WWW-Authenticate',)
@@ -488,7 +491,7 @@ class HTTPExpectationFailed(HTTPClientError):
class HTTPServerError(HTTPError):
"""
- an ``HTTPError`` where the server is in-error
+ base class for the 500's, where the server is in-error
This is an error condition in which the server is presumed to be
in-error. This is usually unexpected, and thus requires a traceback;
@@ -501,7 +504,8 @@ class HTTPServerError(HTTPError):
'The server has either erred or is incapable of performing\r\n'
'the requested operation.\r\n')
-HTTPInternalServerError = HTTPServerError
+class HTTPInternalServerError(HTTPServerError):
+ pass
class HTTPNotImplemented(HTTPServerError):
code = 501
diff --git a/paste/httpheaders.py b/paste/httpheaders.py
index 2688558..2f8cf90 100644
--- a/paste/httpheaders.py
+++ b/paste/httpheaders.py
@@ -140,7 +140,7 @@ from httpexceptions import HTTPBadRequest
__all__ = ['get_header', 'list_headers', 'normalize_headers', 'HTTPHeader',
# additionally, all header instance objects are exported
- '_CacheControl', '_ContentDisposition', '_Range' # for docos
+# '_CacheControl', '_ContentDisposition', '_Range' for docos
]
_headers = {}
@@ -985,12 +985,13 @@ for (name, category, version, style, comment) in \
'date-header': _DateHeader,
'singular' : _SingleValueHeader}[style]
klass(name,category,comment,version).__doc__ = comment
+ del klass
for head in _headers.values():
headname = head.name.replace("-","_").upper()
locals()[headname] = head
__all__.append(headname)
-
+
__pudge_all__ = __all__[:]
for _name, _obj in globals().items():
if isinstance(_obj, type) and issubclass(_obj, HTTPHeader):