~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transport/smart.py

  • Committer: Robert Collins
  • Date: 2007-03-25 08:59:56 UTC
  • mto: (2376.3.1 integration)
  • mto: This revision was merged to the branch mainline in revision 2401.
  • Revision ID: robertc@robertcollins.net-20070325085956-my8jv7cifqzyltyz
New SmartServer hooks facility. There are two initial hooks documented
in bzrlib.transport.smart.SmartServerHooks. The two initial hooks allow
plugins to execute code upon server startup and shutdown.
(Robert Collins).

SmartServer in standalone mode will now close its listening socket
when it stops, rather than waiting for garbage collection. This primarily
fixes test suite hangs when a test tries to connect to a shutdown server.
It may also help improve behaviour when dealing with a server running
on a specific port (rather than dynamically assigned ports).
(Robert Collins)

Show diffs side-by-side

added added

removed removed

Lines of Context:
212
212
    urlutils,
213
213
    )
214
214
from bzrlib.bundle.serializer import write_bundle
 
215
from bzrlib.hooks import Hooks
215
216
try:
216
217
    from bzrlib.transport import ssh
217
218
except errors.ParamikoNotPresent:
820
821
 
821
822
 
822
823
class SmartTCPServer(object):
823
 
    """Listens on a TCP socket and accepts connections from smart clients"""
 
824
    """Listens on a TCP socket and accepts connections from smart clients
 
825
 
 
826
    hooks: An instance of SmartServerHooks.
 
827
    """
824
828
 
825
829
    def __init__(self, backing_transport, host='127.0.0.1', port=0):
826
830
        """Construct a new server.
833
837
        """
834
838
        self._server_socket = socket.socket()
835
839
        self._server_socket.bind((host, port))
836
 
        self.port = self._server_socket.getsockname()[1]
 
840
        self._sockname = self._server_socket.getsockname()
 
841
        self.port = self._sockname[1]
837
842
        self._server_socket.listen(1)
838
843
        self._server_socket.settimeout(1)
839
844
        self.backing_transport = backing_transport
845
850
        from socket import timeout as socket_timeout
846
851
        from socket import error as socket_error
847
852
        self._should_terminate = False
848
 
        while not self._should_terminate:
 
853
        for hook in SmartTCPServer.hooks['server_started']:
 
854
            hook(self.backing_transport.base, self.get_url())
 
855
        try:
 
856
            while not self._should_terminate:
 
857
                try:
 
858
                    self.accept_and_serve()
 
859
                except socket_timeout:
 
860
                    # just check if we're asked to stop
 
861
                    pass
 
862
                except socket_error, e:
 
863
                    trace.warning("client disconnected: %s", e)
 
864
                    pass
 
865
        finally:
849
866
            try:
850
 
                self.accept_and_serve()
851
 
            except socket_timeout:
852
 
                # just check if we're asked to stop
853
 
                pass
854
 
            except socket_error, e:
855
 
                trace.warning("client disconnected: %s", e)
856
 
                pass
 
867
                self._server_socket.close()
 
868
            except socket_error:
 
869
                # ignore errors on close
 
870
                pass
 
871
            for hook in SmartTCPServer.hooks['server_stopped']:
 
872
                hook(self.backing_transport.base, self.get_url())
857
873
 
858
874
    def get_url(self):
859
875
        """Return the url of the server"""
860
 
        return "bzr://%s:%d/" % self._server_socket.getsockname()
 
876
        return "bzr://%s:%d/" % self._sockname
861
877
 
862
878
    def accept_and_serve(self):
863
879
        conn, client_addr = self._server_socket.accept()
887
903
        ## self._server_thread.join()
888
904
 
889
905
 
 
906
class SmartServerHooks(Hooks):
 
907
    """Hooks for the smart server."""
 
908
 
 
909
    def __init__(self):
 
910
        """Create the default hooks.
 
911
 
 
912
        These are all empty initially, because by default nothing should get
 
913
        notified.
 
914
        """
 
915
        Hooks.__init__(self)
 
916
        # Introduced in 0.16:
 
917
        # invoked whenever the server starts serving a directory.
 
918
        # The api signature is (backing url, public url).
 
919
        self['server_started'] = []
 
920
        # Introduced in 0.16:
 
921
        # invoked whenever the server stops serving a directory.
 
922
        # The api signature is (backing url, public url).
 
923
        self['server_stopped'] = []
 
924
 
 
925
SmartTCPServer.hooks = SmartServerHooks()
 
926
 
 
927
 
890
928
class SmartTCPServer_for_testing(SmartTCPServer):
891
929
    """Server suitable for use by transport tests.
892
930