~jelmer/brz/unix-domain-sockets

« back to all changes in this revision

Viewing changes to breezy/bzr/smart/server.py

  • Committer: Jelmer Vernooij
  • Date: 2017-06-23 00:08:45 UTC
  • Revision ID: jelmer@jelmer.uk-20170623000845-mk5hobe64eicq8ol
Split SmartTCPServer further.

Show diffs side-by-side

added added

removed removed

Lines of Context:
52
52
class SmartServer(object):
53
53
    """Listens on a socket and accepts connections from smart clients.
54
54
 
55
 
    Each connection will be served by a SmartServerSocketStreamMedium running in
56
 
    a thread.
 
55
    Each connection will be served by a SmartServerSocketStreamMedium running
 
56
    in a thread.
57
57
 
58
58
    hooks: An instance of SmartServerHooks.
59
59
    """
88
88
        # we disconnect.
89
89
        self._gracefully_stopping = False
90
90
 
 
91
    def _start_server_from_socket(self, server_socket):
 
92
        # Keep a reference to the exceptions we want to catch because the socket
 
93
        # module's globals get set to None during interpreter shutdown.
 
94
        from socket import timeout as socket_timeout
 
95
        from socket import error as socket_error
 
96
        self._socket_error = socket_error
 
97
        self._socket_timeout = socket_timeout
 
98
 
 
99
        self._server_socket = server_socket
 
100
        self._sockname = self._server_socket.getsockname()
 
101
        self._server_socket.listen(1)
 
102
        self._server_socket.settimeout(self._ACCEPT_TIMEOUT)
 
103
        # Once we start accept()ing connections, we set started.
 
104
        self._started = threading.Event()
 
105
        # Once we stop accept()ing connections (and are closing the socket) we
 
106
        # set _stopped
 
107
        self._stopped = threading.Event()
 
108
        # Once we have finished waiting for all clients, etc. We set
 
109
        # _fully_stopped
 
110
        self._fully_stopped = threading.Event()
 
111
 
91
112
    def _backing_urls(self):
92
113
        # There are three interesting urls:
93
114
        # The URL the server can be contacted on. (e.g. bzr://host/)
114
135
    def run_server_started_hooks(self, backing_urls=None):
115
136
        if backing_urls is None:
116
137
            backing_urls = self._backing_urls()
117
 
        for hook in SmartTCPServer.hooks['server_started']:
 
138
        for hook in SmartServer.hooks['server_started']:
118
139
            hook(backing_urls, self.get_url())
119
 
        for hook in SmartTCPServer.hooks['server_started_ex']:
 
140
        for hook in SmartServer.hooks['server_started_ex']:
120
141
            hook(backing_urls, self)
121
142
 
122
143
    def run_server_stopped_hooks(self, backing_urls=None):
123
144
        if backing_urls is None:
124
145
            backing_urls = self._backing_urls()
125
 
        for hook in SmartTCPServer.hooks['server_stopped']:
 
146
        for hook in SmartServer.hooks['server_stopped']:
126
147
            hook(backing_urls, self.get_url())
127
148
 
128
149
    def _stop_gracefully(self):
296
317
        :param port: TCP port to listen on, or 0 to allocate a transient port.
297
318
        """
298
319
        # let connections timeout so that we get a chance to terminate
299
 
        # Keep a reference to the exceptions we want to catch because the socket
300
 
        # module's globals get set to None during interpreter shutdown.
301
 
        from socket import timeout as socket_timeout
302
 
        from socket import error as socket_error
303
 
        self._socket_error = socket_error
304
 
        self._socket_timeout = socket_timeout
305
320
        addrs = socket.getaddrinfo(host, port, socket.AF_UNSPEC,
306
321
            socket.SOCK_STREAM, 0, socket.AI_PASSIVE)[0]
307
322
 
308
323
        (family, socktype, proto, canonname, sockaddr) = addrs
309
324
 
310
 
        self._server_socket = socket.socket(family, socktype, proto)
 
325
        server_socket = socket.socket(family, socktype, proto)
311
326
        # SO_REUSERADDR has a different meaning on Windows
312
327
        if sys.platform != 'win32':
313
 
            self._server_socket.setsockopt(socket.SOL_SOCKET,
 
328
            server_socket.setsockopt(socket.SOL_SOCKET,
314
329
                socket.SO_REUSEADDR, 1)
315
330
        try:
316
 
            self._server_socket.bind(sockaddr)
317
 
        except self._socket_error as message:
 
331
            server_socket.bind(sockaddr)
 
332
        except socket.error as message:
318
333
            raise errors.CannotBindAddress(host, port, message)
319
 
        self._sockname = self._server_socket.getsockname()
320
 
        self.port = self._sockname[1]
321
 
        self._server_socket.listen(1)
322
 
        self._server_socket.settimeout(self._ACCEPT_TIMEOUT)
323
 
        # Once we start accept()ing connections, we set started.
324
 
        self._started = threading.Event()
325
 
        # Once we stop accept()ing connections (and are closing the socket) we
326
 
        # set _stopped
327
 
        self._stopped = threading.Event()
328
 
        # Once we have finished waiting for all clients, etc. We set
329
 
        # _fully_stopped
330
 
        self._fully_stopped = threading.Event()
 
334
        self.port = server_socket.getsockname()[1]
 
335
        self._start_server_from_socket(server_socket)
331
336
 
332
337
    def get_url(self):
333
338
        """Return the url of the server"""
334
339
        return "bzr://%s:%s/" % (self._sockname[0], self._sockname[1])
335
340
 
336
341
 
 
342
class SmartUnixDomainSocketServer(SmartServer):
 
343
    """Listens on a unix domain socket and accepts connections from smart clients.
 
344
 
 
345
    Each connection will be served by a SmartServerSocketStreamMedium running in
 
346
    a thread.
 
347
 
 
348
    hooks: An instance of SmartServerHooks.
 
349
    """
 
350
 
 
351
    def start_server(self, path):
 
352
        """Create the server listening socket.
 
353
 
 
354
        :param path: Path of the socket to listen on
 
355
        """
 
356
        server_socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
 
357
        self.path = path
 
358
        try:
 
359
            server_socket.bind(path)
 
360
        except socket.error as message:
 
361
            raise errors.CannotBindAddress('localhost', path, message)
 
362
        self._start_server_from_socket(server_socket)
 
363
 
 
364
    def get_url(self):
 
365
        """Return the url of the server"""
 
366
        return "bzr+unix:///%s" % (self.path, )
 
367
 
 
368
 
337
369
class SmartServerHooks(Hooks):
338
370
    """Hooks for the smart server."""
339
371
 
343
375
        These are all empty initially, because by default nothing should get
344
376
        notified.
345
377
        """
346
 
        Hooks.__init__(self, "breezy.bzr.smart.server", "SmartTCPServer.hooks")
 
378
        Hooks.__init__(self, "breezy.bzr.smart.server", "SmartServer.hooks")
347
379
        self.add_hook('server_started',
348
380
            "Called by the bzr server when it starts serving a directory. "
349
381
            "server_started is called with (backing urls, public url), "
364
396
            "return true for the hook if the exception has been handled, "
365
397
            "in which case the server will exit normally.", (2, 4))
366
398
 
367
 
SmartTCPServer.hooks = SmartServerHooks()
 
399
SmartServer.hooks = SmartServerHooks()
368
400
 
369
401
 
370
402
def _local_path_for_transport(transport):
508
540
        bzr_server.smart_server.serve()
509
541
    except:
510
542
        hook_caught_exception = False
511
 
        for hook in SmartTCPServer.hooks['server_exception']:
 
543
        for hook in SmartServer.hooks['server_exception']:
512
544
            hook_caught_exception = hook(sys.exc_info())
513
545
        if not hook_caught_exception:
514
546
            raise