Merge
This commit is contained in:
@@ -21,5 +21,41 @@ To launch a wsgi server, simply create a socket and call :func:`eventlet.wsgi.se
|
||||
You can find a slightly more elaborate version of this code in the file
|
||||
``examples/wsgi.py``.
|
||||
|
||||
Non-Standard Extension to Support Post Hooks
|
||||
--------------------------------------------
|
||||
Eventlet's WSGI server supports a non-standard extension to the WSGI
|
||||
specification where :samp:`env['eventlet.posthooks']` contains an array of
|
||||
`post hooks` that will be called after fully sending a response. Each post hook
|
||||
is a tuple of :samp:`(func, args, kwargs)` and the `func` will be called with
|
||||
the WSGI environment dictionary, followed by the `args` and then the `kwargs`
|
||||
in the post hook.
|
||||
|
||||
For example::
|
||||
|
||||
from eventlet import wsgi
|
||||
import eventlet
|
||||
|
||||
def hook(env, arg1, arg2, kwarg3=None, kwarg4=None):
|
||||
print 'Hook called: %s %s %s %s %s' % (env, arg1, arg2, kwarg3, kwarg4)
|
||||
|
||||
def hello_world(env, start_response):
|
||||
env['eventlet.posthooks'].append(
|
||||
(hook, ('arg1', 'arg2'), {'kwarg3': 3, 'kwarg4': 4}))
|
||||
start_response('200 OK', [('Content-Type', 'text/plain')])
|
||||
return ['Hello, World!\r\n']
|
||||
|
||||
wsgi.server(eventlet.listen(('', 8090)), hello_world)
|
||||
|
||||
The above code will print the WSGI environment and the other passed function
|
||||
arguments for every request processed.
|
||||
|
||||
Post hooks are useful when code needs to be executed after a response has been
|
||||
fully sent to the client (or when the client disconnects early). One example is
|
||||
for more accurate logging of bandwidth used, as client disconnects use less
|
||||
bandwidth than the actual Content-Length.
|
||||
|
||||
API
|
||||
---
|
||||
|
||||
.. automodule:: eventlet.wsgi
|
||||
:members:
|
||||
|
@@ -376,6 +376,9 @@ class HttpProtocol(BaseHTTPServer.BaseHTTPRequestHandler):
|
||||
pass
|
||||
finish = time.time()
|
||||
|
||||
for hook, args, kwargs in self.environ['eventlet.posthooks']:
|
||||
hook(self.environ, *args, **kwargs)
|
||||
|
||||
self.server.log_message(self.server.log_format % dict(
|
||||
client_ip=self.get_client_ip(),
|
||||
date_time=self.log_date_time_string(),
|
||||
@@ -442,6 +445,7 @@ class HttpProtocol(BaseHTTPServer.BaseHTTPRequestHandler):
|
||||
env['wsgi.input'] = env['eventlet.input'] = Input(
|
||||
self.rfile, length, wfile=wfile, wfile_line=wfile_line,
|
||||
chunked_input=chunked)
|
||||
env['eventlet.posthooks'] = []
|
||||
|
||||
return env
|
||||
|
||||
|
@@ -818,6 +818,60 @@ class TestHttpd(_TestBase):
|
||||
pass # TODO: should test with OpenSSL
|
||||
greenthread.kill(g)
|
||||
|
||||
def test_029_posthooks(self):
|
||||
posthook1_count = [0]
|
||||
posthook2_count = [0]
|
||||
def posthook1(env, value, multiplier=1):
|
||||
self.assertEquals(env['local.test'], 'test_029_posthooks')
|
||||
posthook1_count[0] += value * multiplier
|
||||
def posthook2(env, value, divisor=1):
|
||||
self.assertEquals(env['local.test'], 'test_029_posthooks')
|
||||
posthook2_count[0] += value / divisor
|
||||
|
||||
def one_posthook_app(env, start_response):
|
||||
env['local.test'] = 'test_029_posthooks'
|
||||
if 'eventlet.posthooks' not in env:
|
||||
start_response('500 eventlet.posthooks not supported',
|
||||
[('Content-Type', 'text/plain')])
|
||||
else:
|
||||
env['eventlet.posthooks'].append(
|
||||
(posthook1, (2,), {'multiplier': 3}))
|
||||
start_response('200 OK', [('Content-Type', 'text/plain')])
|
||||
yield ''
|
||||
self.site.application = one_posthook_app
|
||||
sock = eventlet.connect(('localhost', self.port))
|
||||
fp = sock.makefile('rw')
|
||||
fp.write('GET / HTTP/1.1\r\nHost: localhost\r\n\r\n')
|
||||
fp.flush()
|
||||
self.assertEquals(fp.readline(), 'HTTP/1.1 200 OK\r\n')
|
||||
fp.close()
|
||||
sock.close()
|
||||
self.assertEquals(posthook1_count[0], 6)
|
||||
self.assertEquals(posthook2_count[0], 0)
|
||||
|
||||
def two_posthook_app(env, start_response):
|
||||
env['local.test'] = 'test_029_posthooks'
|
||||
if 'eventlet.posthooks' not in env:
|
||||
start_response('500 eventlet.posthooks not supported',
|
||||
[('Content-Type', 'text/plain')])
|
||||
else:
|
||||
env['eventlet.posthooks'].append(
|
||||
(posthook1, (4,), {'multiplier': 5}))
|
||||
env['eventlet.posthooks'].append(
|
||||
(posthook2, (100,), {'divisor': 4}))
|
||||
start_response('200 OK', [('Content-Type', 'text/plain')])
|
||||
yield ''
|
||||
self.site.application = two_posthook_app
|
||||
sock = eventlet.connect(('localhost', self.port))
|
||||
fp = sock.makefile('rw')
|
||||
fp.write('GET / HTTP/1.1\r\nHost: localhost\r\n\r\n')
|
||||
fp.flush()
|
||||
self.assertEquals(fp.readline(), 'HTTP/1.1 200 OK\r\n')
|
||||
fp.close()
|
||||
sock.close()
|
||||
self.assertEquals(posthook1_count[0], 26)
|
||||
self.assertEquals(posthook2_count[0], 25)
|
||||
|
||||
def test_zero_length_chunked_response(self):
|
||||
def zero_chunked_app(env, start_response):
|
||||
start_response('200 OK', [('Content-type', 'text/plain')])
|
||||
|
Reference in New Issue
Block a user