Commit acb7c5e9 authored by Jason Madden's avatar Jason Madden

wsgi documentation. [skip ci]

parent 4feedea5
...@@ -27,7 +27,11 @@ from gevent.server import StreamServer ...@@ -27,7 +27,11 @@ from gevent.server import StreamServer
from gevent.hub import GreenletExit, PY3, reraise from gevent.hub import GreenletExit, PY3, reraise
__all__ = ['WSGIHandler', 'WSGIServer', 'LoggingLogAdapter'] __all__ = [
'WSGIServer',
'WSGIHandler',
'LoggingLogAdapter',
]
MAX_REQUEST_LINE = 8192 MAX_REQUEST_LINE = 8192
...@@ -313,6 +317,20 @@ except ImportError: ...@@ -313,6 +317,20 @@ except ImportError:
class WSGIHandler(object): class WSGIHandler(object):
"""
Handles HTTP requests from a socket, creates the WSGI environment, and
interacts with the WSGI application.
This is the default value of :attr:`WSGIServer.handler_class`.
This class may be subclassed carefully, and that class set on a
:class:`WSGIServer` instance through a keyword argument at
construction time.
Instances are constructed with the same arguments as passed to the
server's :meth:`WSGIServer.handle` method followed by the server
itself. The application and environment are obtained from the server.
"""
protocol_version = 'HTTP/1.1' protocol_version = 'HTTP/1.1'
if PY3: if PY3:
# if we do like Py2, then headers_factory unconditionally # if we do like Py2, then headers_factory unconditionally
...@@ -340,8 +358,10 @@ class WSGIHandler(object): ...@@ -340,8 +358,10 @@ class WSGIHandler(object):
""" """
The main request handling method, called by the server. The main request handling method, called by the server.
This method runs until all requests on the connection have This method runs a request handling loop, calling
been handled (that is, it implements pipelining). :meth:`handle_one_request` until all requests on the
connection have been handled (that is, it implements
keep-alive).
""" """
try: try:
while self.socket is not None: while self.socket is not None:
...@@ -353,6 +373,7 @@ class WSGIHandler(object): ...@@ -353,6 +373,7 @@ class WSGIHandler(object):
break break
if result is True: if result is True:
continue continue
self.status, response_body = result self.status, response_body = result
self.socket.sendall(response_body) self.socket.sendall(response_body)
if self.time_finish == 0: if self.time_finish == 0:
...@@ -387,10 +408,17 @@ class WSGIHandler(object): ...@@ -387,10 +408,17 @@ class WSGIHandler(object):
def read_request(self, raw_requestline): def read_request(self, raw_requestline):
""" """
Process the incoming request. Parse various headers. Process the incoming request.
Parses various headers into ``self.headers`` using
:attr:`MessageClass`.
:param str raw_requestline: A native :class:`str` representing
the request line. A processed version of this will be stored
into ``self.requestline``.
:raises ValueError: If the request is invalid. This error will :raises ValueError: If the request is invalid. This error will
not be logged (because it's a client issue, not a server problem). not be logged as a traceback (because it's a client issue, not a server problem).
""" """
self.requestline = raw_requestline.rstrip() self.requestline = raw_requestline.rstrip()
words = self.requestline.split() words = self.requestline.split()
...@@ -476,6 +504,42 @@ class WSGIHandler(object): ...@@ -476,6 +504,42 @@ class WSGIHandler(object):
return line return line
def handle_one_request(self): def handle_one_request(self):
"""
Handles one HTTP request using ``self.socket`` and ``self.rfile``.
Each invocation of this method will do several things, including (but not limited to):
- Read the request line using :meth:`read_requestline`;
- Read the rest of the request, including headers, with :meth:`read_request`;
- Construct a new WSGI environment in ``self.environ`` using :meth:`get_environ`;
- Store the application in ``self.application``, retrieving it from the server;
- Handle the remainder of the request, including invoking the application,
with :meth:`handle_one_response`
There are several possible return values to indicate the state
of the client connection:
- ``None``
The client connection is already closed or should
be closed because the WSGI application or client set the
``Connection: close`` header. The request handling
loop should terminate and perform cleanup steps.
- (status, body)
An HTTP status and body tuple. The request was in error,
as detailed by the status and body. The request handling
loop should terminate, close the connection, and perform
cleanup steps. Note that the ``body`` is the complete contents
to send to the client, including all headers and the initial
status line.
- ``True``
The literal ``True`` value. The request was successfully handled
and the response sent to the client by :meth:`handle_one_response`.
The connection remains open to process more requests and the connection
handling loop should call this method again. This is the typical return
value.
.. seealso:: :meth:`handle`
"""
if self.rfile.closed: if self.rfile.closed:
return return
try: try:
...@@ -501,7 +565,7 @@ class WSGIHandler(object): ...@@ -501,7 +565,7 @@ class WSGIHandler(object):
return ('400', _BAD_REQUEST_RESPONSE) return ('400', _BAD_REQUEST_RESPONSE)
except Exception as ex: except Exception as ex:
if not isinstance(ex, ValueError): if not isinstance(ex, ValueError):
traceback.print_exc() traceback.print_exc() # Why not self.handle_error?
self.log_error('Invalid request: %s', str(ex) or ex.__class__.__name__) self.log_error('Invalid request: %s', str(ex) or ex.__class__.__name__)
return ('400', _BAD_REQUEST_RESPONSE) return ('400', _BAD_REQUEST_RESPONSE)
...@@ -796,6 +860,16 @@ class WSGIHandler(object): ...@@ -796,6 +860,16 @@ class WSGIHandler(object):
yield 'HTTP_' + key, value.strip() yield 'HTTP_' + key, value.strip()
def get_environ(self): def get_environ(self):
"""
Construct and return a new WSGI environment dictionary for a specific request.
This should begin with asking the server for the base environment
using :meth:`WSGIServer.get_environ`, and then proceed to add the
request specific values.
By the time this method is invoked the request line and request shall have
been parsed and ``self.headers`` shall be populated.
"""
env = self.server.get_environ() env = self.server.get_environ()
env['REQUEST_METHOD'] = self.command env['REQUEST_METHOD'] = self.command
env['SCRIPT_NAME'] = '' env['SCRIPT_NAME'] = ''
...@@ -932,6 +1006,14 @@ class WSGIServer(StreamServer): ...@@ -932,6 +1006,14 @@ class WSGIServer(StreamServer):
``error_log`` arguments. ``error_log`` arguments.
""" """
#: A callable taking three arguments: (socket, address, server) and returning
#: an object with a ``handle()`` method. The callable is called once for
#: each incoming socket request, as is its handle method. The handle method should not
#: return until all use of the socket is complete.
#:
#: This class uses the :class:`WSGIHandler` object as the default value. You may
#: subclass this class and set a different default value, or you may pass
#: a value to use in the ``handler_class`` keyword constructor argument.
handler_class = WSGIHandler handler_class = WSGIHandler
#: The object to which request logs will be written. #: The object to which request logs will be written.
...@@ -948,7 +1030,7 @@ class WSGIServer(StreamServer): ...@@ -948,7 +1030,7 @@ class WSGIServer(StreamServer):
'SERVER_SOFTWARE': 'gevent/%d.%d Python/%d.%d' % (gevent.version_info[:2] + sys.version_info[:2]), 'SERVER_SOFTWARE': 'gevent/%d.%d Python/%d.%d' % (gevent.version_info[:2] + sys.version_info[:2]),
'SCRIPT_NAME': '', 'SCRIPT_NAME': '',
'wsgi.version': (1, 0), 'wsgi.version': (1, 0),
'wsgi.multithread': False, 'wsgi.multithread': False, # XXX: Aren't we really, though?
'wsgi.multiprocess': False, 'wsgi.multiprocess': False,
'wsgi.run_once': False} 'wsgi.run_once': False}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment