diff --git a/AUTHORS b/AUTHORS index efe4a6f..8a210e9 100644 --- a/AUTHORS +++ b/AUTHORS @@ -23,6 +23,7 @@ Linden Lab Contributors Thanks To --------- +* Michael Barton, 100-continue patch, content-length bugfixes for wsgi * gholt, wsgi patches for accepting a custom pool, and returning 400 if content-length is invalid * Luke Tucker, bug report regarding wsgi + webob * Chuck Thier, reporting a bug in processes.py diff --git a/doc/real_index.html b/doc/real_index.html index c4b98a5..f3b0bd7 100644 --- a/doc/real_index.html +++ b/doc/real_index.html @@ -22,9 +22,7 @@

Eventlet is a networking library written in Python. It achieves high scalability by using non-blocking io while at the same time retaining high programmer usability by using coroutines to make the non-blocking io operations appear blocking at the source code level.

-

Documentation

- -API Documentation +

API Documentation

Installation

@@ -47,10 +45,16 @@ easy_install eventlet

Development

-

"root" repository

+

trunk repository

We use Mercurial for our source control, hosted by BitBucket. It's easy to branch off the main repository and contribute patches, tests, and documentation back upstream.

+

Bugs

+ +

Bug Report Form

+ +

No registration is required. Please be sure to report bugs as effectively as possible, to ensure that we understand and act on them quickly.

+

Web Crawler ExampleΒΆ

This is a simple web “crawler” that fetches a bunch of urls using a coroutine pool. It has as much concurrency (i.e. pages being fetched simultaneously) as coroutines in the pool.

@@ -78,9 +82,13 @@ easy_install eventlet for waiter in waiters: waiter.wait()
+ +

Stats

+ +
@@ -92,6 +100,7 @@ easy_install eventlet diff --git a/eventlet/db_pool.py b/eventlet/db_pool.py index 1b1f731..6be0a1c 100644 --- a/eventlet/db_pool.py +++ b/eventlet/db_pool.py @@ -297,14 +297,14 @@ class GenericConnectionWrapper(object): def character_set_name(self,*args, **kwargs): return self._base.character_set_name(*args, **kwargs) def close(self,*args, **kwargs): return self._base.close(*args, **kwargs) def commit(self,*args, **kwargs): return self._base.commit(*args, **kwargs) - def cursor(self, cursorclass=None, **kwargs): return self._base.cursor(cursorclass, **kwargs) + def cursor(self, *args, **kwargs): return self._base.cursor(*args, **kwargs) def dump_debug_info(self,*args, **kwargs): return self._base.dump_debug_info(*args, **kwargs) def errno(self,*args, **kwargs): return self._base.errno(*args, **kwargs) def error(self,*args, **kwargs): return self._base.error(*args, **kwargs) - def errorhandler(self, conn, curs, errcls, errval): return self._base.errorhandler(conn, curs, errcls, errval) - def literal(self, o): return self._base.literal(o) - def set_character_set(self, charset): return self._base.set_character_set(charset) - def set_sql_mode(self, sql_mode): return self._base.set_sql_mode(sql_mode) + def errorhandler(self, *args, **kwargs): return self._base.errorhandler(*args, **kwargs) + def literal(self, *args, **kwargs): return self._base.literal(*args, **kwargs) + def set_character_set(self, *args, **kwargs): return self._base.set_character_set(*args, **kwargs) + def set_sql_mode(self, *args, **kwargs): return self._base.set_sql_mode(*args, **kwargs) def show_warnings(self): return self._base.show_warnings() def warning_count(self): return self._base.warning_count() def ping(self,*args, **kwargs): return self._base.ping(*args, **kwargs) @@ -315,7 +315,7 @@ class GenericConnectionWrapper(object): def server_capabilities(self,*args, **kwargs): return self._base.server_capabilities(*args, **kwargs) def shutdown(self,*args, **kwargs): return self._base.shutdown(*args, **kwargs) def sqlstate(self,*args, **kwargs): return self._base.sqlstate(*args, **kwargs) - def stat(self,*args, **kwargs): return self._base.stat(*args, **kwargs) + def stat(self, *args, **kwargs): return self._base.stat(*args, **kwargs) def store_result(self,*args, **kwargs): return self._base.store_result(*args, **kwargs) def string_literal(self,*args, **kwargs): return self._base.string_literal(*args, **kwargs) def thread_id(self,*args, **kwargs): return self._base.thread_id(*args, **kwargs) diff --git a/eventlet/wsgi.py b/eventlet/wsgi.py index b6db8da..ef2c62f 100644 --- a/eventlet/wsgi.py +++ b/eventlet/wsgi.py @@ -297,8 +297,10 @@ class HttpProtocol(BaseHTTPServer.BaseHTTPRequestHandler): if hasattr(result, 'close'): result.close() if self.environ['eventlet.input'].position < self.environ.get('CONTENT_LENGTH', 0): - ## Read and discard body - self.environ['eventlet.input'].read() + ## Read and discard body if there was no pending 100-continue + if not self.environ['eventlet.input'].wfile: + while self.environ['eventlet.input'].read(MINIMUM_CHUNK_SIZE): + pass finish = time.time() self.server.log_message('%s - - [%s] "%s" %s %s %.6f' % ( diff --git a/tests/wsgi_test.py b/tests/wsgi_test.py index 024df3a..cde6ee1 100644 --- a/tests/wsgi_test.py +++ b/tests/wsgi_test.py @@ -585,6 +585,30 @@ class TestHttpd(LimitedTestCase): self.assert_('400 Bad Request' in result) self.assert_('500' not in result) + def test_024_expect_100_continue(self): + def wsgi_app(environ, start_response): + if int(environ['CONTENT_LENGTH']) > 1024: + start_response('417 Expectation Failed', [('Content-Length', '7')]) + return ['failure'] + else: + text = environ['wsgi.input'].read() + start_response('200 OK', [('Content-Length', str(len(text)))]) + return [text] + self.site.application = wsgi_app + sock = api.connect_tcp(('localhost', self.port)) + fd = sock.makeGreenFile() + fd.write('PUT / HTTP/1.1\r\nHost: localhost\r\nContent-length: 1025\r\nExpect: 100-continue\r\n\r\n') + result = fd.readuntil('\r\n\r\n') + self.assert_(result.startswith('HTTP/1.1 417 Expectation Failed')) + self.assertEquals(fd.read(7), 'failure') + fd.write('PUT / HTTP/1.1\r\nHost: localhost\r\nContent-length: 7\r\nExpect: 100-continue\r\n\r\ntesting') + result = fd.readuntil('\r\n\r\n') + self.assert_(result.startswith('HTTP/1.1 100 Continue')) + result = fd.readuntil('\r\n\r\n') + self.assert_(result.startswith('HTTP/1.1 200 OK')) + self.assertEquals(fd.read(7), 'testing') + fd.close() + if __name__ == '__main__': main()