This commit is contained in:
Ryan Williams
2010-01-11 23:49:04 -08:00
3 changed files with 81 additions and 9 deletions

View File

@@ -23,13 +23,14 @@ Linden Lab Contributors
Thanks To
---------
* AdamKG, giving the hint that invalid argument errors were introduced post-0.9.0
* 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
* Brantley Harris, reporting bug #4
* Taso Du Val, reproing an exception squelching bug, saving children's lives ;-)
* R. Tyler Ballance, bug report on tpool on Windows, help with improving corolocal module, profile performance report, suggestion use flush that fixed tpool on Windows
* R. Tyler Ballance, bug report on tpool on Windows, help with improving corolocal module, profile performance report, suggestion use flush that fixed tpool on Windows, reporting a bug in wsgi the day after it was introduced, reporting errors found in production use of spawning
* Sergey Shepelev, PEP 8 police :-), reporting bug #5
* Luci Stanescu, for reporting twisted hub bug
* Marcus Cavanaugh, for test case code that has been incredibly useful in tracking down bugs

View File

@@ -162,7 +162,7 @@ class HttpProtocol(BaseHTTPServer.BaseHTTPRequestHandler):
except greenio.SSL.ZeroReturnError:
self.raw_requestline = ''
except socket.error, e:
if getattr(e, 'errno', 0) not in BAD_SOCK:
if e[0] not in BAD_SOCK:
raise
self.raw_requestline = ''
@@ -192,7 +192,7 @@ class HttpProtocol(BaseHTTPServer.BaseHTTPRequestHandler):
self.handle_one_response()
except socket.error, e:
# Broken pipe, connection reset by peer
if getattr(e, 'errno', 0) not in BROKEN_SOCK:
if e[0] not in BROKEN_SOCK:
raise
finally:
self.server.outstanding_requests -= 1
@@ -433,6 +433,7 @@ class Server(BaseHTTPServer.HTTPServer):
def log_message(self, message):
self.log.write(message + '\n')
ACCEPT_SOCK = set((errno.EPIPE, errno.EBADF))
def server(sock, site,
log=None,
@@ -446,7 +447,9 @@ def server(sock, site,
custom_pool=None,
log_format=DEFAULT_LOG_FORMAT):
""" Start up a wsgi server handling requests from the supplied server
socket. This function loops forever.
socket. This function loops forever. The *sock* object will be closed after server exits,
but the underlying file descriptor will remain open, so if you have a dup() of *sock*,
it will remain usable.
:param sock: Server socket, must be already bound to a port and listening.
:param site: WSGI application function.
@@ -495,7 +498,7 @@ def server(sock, site,
try:
client_socket = sock.accept()
except socket.error, e:
if getattr(e, 'errno', 0) not in BAD_SOCK + BROKEN_SOCK:
if e[0] not in ACCEPT_SOCK:
raise
pool.execute_async(serv.process_request, client_socket)
except (KeyboardInterrupt, SystemExit):
@@ -503,9 +506,13 @@ def server(sock, site,
break
finally:
try:
greenio.shutdown_safe(sock)
# NOTE: It's not clear whether we want this to leave the
# socket open or close it. Use cases like Spawning want
# the underlying fd to remain open, but if we're going
# that far we might as well not bother closing sock at
# all.
sock.close()
except socket.error, e:
if getattr(e, 'errno', 0) not in BROKEN_SOCK:
if e[0] not in BROKEN_SOCK:
traceback.print_exc()

View File

@@ -1,12 +1,15 @@
import cgi
import errno
import os
import socket
import sys
from tests import skipped, LimitedTestCase
from unittest import main
from eventlet import api
from eventlet import util
from eventlet import greenio
from eventlet.green import socket as greensocket
from eventlet import wsgi
from eventlet import processes
@@ -529,7 +532,41 @@ class TestHttpd(LimitedTestCase):
self.assert_('1.2.3.4' not in self.logfile.getvalue())
self.assert_('5.6.7.8' not in self.logfile.getvalue())
self.assert_('127.0.0.1' in self.logfile.getvalue())
def test_socket_remains_open(self):
api.kill(self.killer)
server_sock = api.tcp_listener(('localhost', 0))
self.port = server_sock.getsockname()[1]
server_sock_2 = server_sock.dup()
self.killer = api.spawn(wsgi.server, server_sock_2, hello_world,
log=self.logfile)
# do a single req/response to verify it's up
sock = api.connect_tcp(('localhost', self.port))
fd = sock.makeGreenFile()
fd.write('GET / HTTP/1.0\r\nHost: localhost\r\n\r\n')
result = fd.read()
fd.close()
self.assert_(result.startswith('HTTP'), result)
self.assert_(result.endswith('hello world'))
# shut down the server and verify the server_socket fd is still open,
# but the actual socketobject passed in to wsgi.server is closed
api.kill(self.killer)
api.sleep(0.01)
try:
server_sock_2.accept()
except socket.error, exc:
self.assertEqual(exc[0], errno.EBADF)
self.killer = api.spawn(wsgi.server, server_sock, hello_world,
log=self.logfile)
sock = api.connect_tcp(('localhost', self.port))
fd = sock.makeGreenFile()
fd.write('GET / HTTP/1.0\r\nHost: localhost\r\n\r\n')
result = fd.read()
fd.close()
self.assert_(result.startswith('HTTP'), result)
self.assert_(result.endswith('hello world'))
def test_021_environ_clobbering(self):
def clobberin_time(environ, start_response):
for environ_var in ['wsgi.version', 'wsgi.url_scheme',
@@ -620,7 +657,34 @@ class TestHttpd(LimitedTestCase):
self.assertEquals(fd.read(7), 'testing')
fd.close()
def test_025_log_format(self):
def test_025_accept_errors(self):
api.kill(self.killer)
listener = greensocket.socket()
listener.bind(('localhost', 0))
# NOT calling listen, to trigger the error
self.port = listener.getsockname()[1]
self.killer = api.spawn(
wsgi.server,
listener,
self.site,
max_size=128,
log=self.logfile)
old_stderr = sys.stderr
try:
sys.stderr = self.logfile
api.sleep(0) # need to enter server loop
try:
api.connect_tcp(('localhost', self.port))
self.fail("Didn't expect to connect")
except socket.error, exc:
self.assertEquals(exc[0], errno.ECONNREFUSED)
self.assert_('Invalid argument' in self.logfile.getvalue(),
self.logfile.getvalue())
finally:
sys.stderr = old_stderr
def test_026_log_format(self):
self.spawn_server(log_format="HI %(request_line)s HI")
sock = api.connect_tcp(('localhost', self.port))
sock.sendall('GET /yo! HTTP/1.1\r\nHost: localhost\r\n\r\n')