I had to merge some refactoring done for bug #392127 so that the 2.4 band-aids stay smaller.
I changed the server.tearDown() into server.shutdown() as that better matches the SocketServer.py methods names.
I'm not particularly happy with having to override server_bind() twice, but that will be solved
with #392127 where the inheritance scheme is a bit different.
I may need to make connect_socket() a bit more reusable but I also plan to make more
test_server bits reusable so that could wait too.
Here is the differential diff (but it's ~200 lines while the full one is ~250):
=== modified file 'bzrlib/tests/http_server.py'
--- bzrlib/tests/http_server.py 2009-10-07 10:24:04 +0000
+++ bzrlib/tests/http_server.py 2009-10-08 08:25:53 +0000
@@ -317,27 +317,70 @@
# the tests cases. self.test_case_server = test_case_server self._home_dir = test_case_server._home_dir
-
- def tearDown(self):
- """Called to clean-up the server.
-
- Since the server may be (surely is, even) in a blocking listen, we
- shutdown its socket before closing it.
- """
- # The server is listening for a last connection, let's give it:
- try:
- fake_conn = socket.create_connection(self.server_address)
- fake_conn.close()
- except socket.error, e:
- # But ignore connection errors as the point is to unblock the
- # server thread, it may happen that it's not blocked or even not
- # started (when something went wrong during test case setup
- # leading to self.setUp() *not* being called but self.tearDown()
- # still being called)
- pass
-
-
-class TestingHTTPServer(SocketServer.TCPServer, TestingHTTPServerMixin):
+ self.serving = False
+ self.is_shut_down = threading.Event()
+
+ def serve(self):
+ self.serving = True
+ self.is_shut_down.clear()
+ while self.serving:
+ try:
+ # Really a connection but the python framework is generic and
+ # call them requests
+ self.handle_request()
+ except socket.timeout:
+ pass
+ except (socket.error, select.error), e:
+ if e[0] == errno.EBADF:
+ # Starting with python-2.6, handle_request may raise socket
+ # or select exceptions when the server is shut down as we
+ # do.
+ pass
+ else:
+ raise
+ # Let's close the listening socket
+ self.server_close()
+ self.is_shut_down.set()
+
+ def connect_socket(self):
+ msg = "getaddrinfo returns an empty list"
+ for res in socket.getaddrinfo(*self.server_address):
+ af, socktype, proto, canonname, sa = res
+ sock = None
+ try:
+ sock = socket.socket(af, socktype, proto)
+ sock.connect(sa)
+ return sock
+
+ except socket.error, msg:
+ if sock is not None:
+ sock.close()
+ raise socket.error, msg
+
+ def shutdown(self):
+ """Stops the serve() loop.
+
+ Blocks until the loop has finished. This must be called while serve()
+ is running in another thread, or it will deadlock.
+ """
+ if not self.serving:
+ return
+ self.serving = False
+ # The server is listening for a last connection, let's give it:
+ try:
+ fake_conn = self.connect_socket()
+ fake_conn.close()
+ except socket.error, e:
+ # But ignore connection errors as the point is to unblock the
+ # server thread, it may happen that it's not blocked or even not
+ # started (when something went wrong during test case setup
+ # leading to self.setUp() *not* being called but self.tearDown()
+ # still being called)
+ pass
+ self.is_shut_down.wait()
+
+
+class TestingHTTPServer(TestingHTTPServerMixin, SocketServer.TCPServer):
class HttpServer(transport.Server):
"""A test server for http transports.
@@ -445,18 +499,18 @@ raise httplib.UnknownProtocol(proto_vers)
else: self._httpd = self.create_httpd(serv_cls, rhandler)
- host, self.port = self._httpd.socket.getsockname()
+ # Ensure we get the right port
+ host, self.port = self._httpd.server_address
return self._httpd
def _http_start(self):
"""Server thread main entry point. """
- self._http_running = False
+ server = None
try:
try:
- httpd = self._get_httpd()
+ server = self._get_httpd() self._http_base_url = '%s://%s:%s/' % (self._url_protocol, self.host, self.port)
- self._http_running = True except:
# Whatever goes wrong, we save the exception for the main
# thread. Note that since we are running in a thread, no signal
@@ -469,24 +523,8 @@
# From now on, exceptions are taken care of by the
# SocketServer.BaseServer or the request handler.
- while self._http_running:
- try:
- # Really an HTTP connection but the python framework is generic
- # and call them requests
- httpd.handle_request()
- except socket.timeout:
- pass
- except (socket.error, select.error), e:
- if e[0] == errno.EBADF:
- # Starting with python-2.6, handle_request may raise socket
- # or select exceptions when the server is shut down (as we
- # do).
- pass
- else:
- raise
- if self._httpd is not None:
- # Let the server properly close the listening socket
- self._httpd.server_close()
+ if server is not None:
+ server.serve()
I had to merge some refactoring done for bug #392127 so that the 2.4 band-aids stay smaller.
I changed the server.tearDown() into server.shutdown() as that better matches the SocketServer.py methods names.
I'm not particularly happy with having to override server_bind() twice, but that will be solved
with #392127 where the inheritance scheme is a bit different.
I may need to make connect_socket() a bit more reusable but I also plan to make more
test_server bits reusable so that could wait too.
Here is the differential diff (but it's ~200 lines while the full one is ~250): tests/http_ server. py' tests/http_ server. py 2009-10-07 10:24:04 +0000 tests/http_ server. py 2009-10-08 08:25:53 +0000
self. test_case_ server = test_case_server
self. _home_dir = test_case_ server. _home_dir create_ connection( self.server_ address) er(SocketServer .TCPServer, TestingHTTPServ erMixin) : shut_down. clear() request( ) shut_down. set() socket( self): getaddrinfo( *self.server_ address) : socket( ) shut_down. wait() er(TestingHTTPS erverMixin, SocketServer. TCPServer) :
=== modified file 'bzrlib/
--- bzrlib/
+++ bzrlib/
@@ -317,27 +317,70 @@
# the tests cases.
-
- def tearDown(self):
- """Called to clean-up the server.
-
- Since the server may be (surely is, even) in a blocking listen, we
- shutdown its socket before closing it.
- """
- # The server is listening for a last connection, let's give it:
- try:
- fake_conn = socket.
- fake_conn.close()
- except socket.error, e:
- # But ignore connection errors as the point is to unblock the
- # server thread, it may happen that it's not blocked or even not
- # started (when something went wrong during test case setup
- # leading to self.setUp() *not* being called but self.tearDown()
- # still being called)
- pass
-
-
-class TestingHTTPServ
+ self.serving = False
+ self.is_shut_down = threading.Event()
+
+ def serve(self):
+ self.serving = True
+ self.is_
+ while self.serving:
+ try:
+ # Really a connection but the python framework is generic and
+ # call them requests
+ self.handle_
+ except socket.timeout:
+ pass
+ except (socket.error, select.error), e:
+ if e[0] == errno.EBADF:
+ # Starting with python-2.6, handle_request may raise socket
+ # or select exceptions when the server is shut down as we
+ # do.
+ pass
+ else:
+ raise
+ # Let's close the listening socket
+ self.server_close()
+ self.is_
+
+ def connect_
+ msg = "getaddrinfo returns an empty list"
+ for res in socket.
+ af, socktype, proto, canonname, sa = res
+ sock = None
+ try:
+ sock = socket.socket(af, socktype, proto)
+ sock.connect(sa)
+ return sock
+
+ except socket.error, msg:
+ if sock is not None:
+ sock.close()
+ raise socket.error, msg
+
+ def shutdown(self):
+ """Stops the serve() loop.
+
+ Blocks until the loop has finished. This must be called while serve()
+ is running in another thread, or it will deadlock.
+ """
+ if not self.serving:
+ return
+ self.serving = False
+ # The server is listening for a last connection, let's give it:
+ try:
+ fake_conn = self.connect_
+ fake_conn.close()
+ except socket.error, e:
+ # But ignore connection errors as the point is to unblock the
+ # server thread, it may happen that it's not blocked or even not
+ # started (when something went wrong during test case setup
+ # leading to self.setUp() *not* being called but self.tearDown()
+ # still being called)
+ pass
+ self.is_
+
+
+class TestingHTTPServ
def __init__(self, server_address, request_ handler_ class,
test_ case_server) :
SocketServer. TCPServer. __init_ _(self, server_address,
request_ handler_ class)
@@ -345,9 +388,15 @@
- gHTTPServer( SocketServer. ThreadingTCPSer ver, erMixin) : TCPServer. server_ bind(self) getsockname( ) gHTTPServer( TestingHTTPServ erMixin, ThreadingTCPSer ver,
-class TestingThreadin
- TestingHTTPServ
+ def server_bind(self):
+ SocketServer.
+ if sys.version < (2, 5):
+ self.server_address = self.socket.
+
+
+class TestingThreadin
+ SocketServer.
+ ):
"""A threading HTTP test server for HTTP 1.1.
Since tests can initiate several concurrent connections to the same http
raise
@@ -382,6 +431,11 @@
else:
+ def server_bind(self): ThreadingTCPSer ver.server_ bind(self) getsockname( )
+ SocketServer.
+ if sys.version < (2, 5):
+ self.server_address = self.socket.
+
class HttpServer( transport. Server) :
raise httplib. UnknownProtocol (proto_ vers)
self. _httpd = self.create_ httpd(serv_ cls, rhandler) socket. getsockname( ) server_ address
"""A test server for http transports.
@@ -445,18 +499,18 @@
else:
- host, self.port = self._httpd.
+ # Ensure we get the right port
+ host, self.port = self._httpd.
return self._httpd
def _http_start(self):
self. _http_base_ url = '%s://%s:%s/' % (self._ url_protocol,
self.host, self.port)
except:
"""Server thread main entry point. """
- self._http_running = False
+ server = None
try:
try:
- httpd = self._get_httpd()
+ server = self._get_httpd()
- self._http_running = True
# Whatever goes wrong, we save the exception for the main
# thread. Note that since we are running in a thread, no signal
@@ -469,24 +523,8 @@
# From now on, exceptions are taken care of by the BaseServer or the request handler. request( ) server_ close()
# SocketServer.
- while self._http_running:
- try:
- # Really an HTTP connection but the python framework is generic
- # and call them requests
- httpd.handle_
- except socket.timeout:
- pass
- except (socket.error, select.error), e:
- if e[0] == errno.EBADF:
- # Starting with python-2.6, handle_request may raise socket
- # or select exceptions when the server is shut down (as we
- # do).
- pass
- else:
- raise
- if self._httpd is not None:
- # Let the server properly close the listening socket
- self._httpd.
+ if server is not None:
+ server.serve()
def _get_remote_ url(self, path): os.path. sep) transport. Server. tearDown. """
self. _http_running = False tearDown( ) shutdown( )
self. _http_thread. join()
path_parts = path.split(
@@ -543,7 +581,7 @@
def tearDown(self):
"""See bzrlib.
- self._httpd.
+ self._httpd.
def get_url(self):
=== modified file 'bzrlib/ tests/test_ http.py' tests/test_ http.py 2009-09-17 11:54:41 +0000 tests/test_ http.py 2009-10-08 07:25:52 +0000 HttpServer( )
server. setUp() (server. _http_running) (server. _httpd is not None) (server. _httpd. serving)
server. tearDown( ) e(server. _http_running) e(server. _httpd. serving)
--- bzrlib/
+++ bzrlib/
@@ -325,10 +325,11 @@
server = http_server.
try:
- self.assertTrue
+ self.assertTrue
+ self.assertTrue
finally:
- self.assertFals
+ self.assertFals
def test_create_ http_server_ one_zero( self): neZero( http_server. TestingHTTPRequ estHandler) :
class RequestHandlerO