89
89
self._gracefully_stopping = False
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
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
107
self._stopped = threading.Event()
108
# Once we have finished waiting for all clients, etc. We set
110
self._fully_stopped = threading.Event()
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)
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())
128
149
def _stop_gracefully(self):
296
317
:param port: TCP port to listen on, or 0 to allocate a transient port.
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]
308
323
(family, socktype, proto, canonname, sockaddr) = addrs
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)
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
327
self._stopped = threading.Event()
328
# Once we have finished waiting for all clients, etc. We set
330
self._fully_stopped = threading.Event()
334
self.port = server_socket.getsockname()[1]
335
self._start_server_from_socket(server_socket)
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])
342
class SmartUnixDomainSocketServer(SmartServer):
343
"""Listens on a unix domain socket and accepts connections from smart clients.
345
Each connection will be served by a SmartServerSocketStreamMedium running in
348
hooks: An instance of SmartServerHooks.
351
def start_server(self, path):
352
"""Create the server listening socket.
354
:param path: Path of the socket to listen on
356
server_socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
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)
365
"""Return the url of the server"""
366
return "bzr+unix:///%s" % (self.path, )
337
369
class SmartServerHooks(Hooks):
338
370
"""Hooks for the smart server."""
343
375
These are all empty initially, because by default nothing should get
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), "