/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/smart/server.py

  • Committer: Robert Collins
  • Date: 2010-05-06 11:08:10 UTC
  • mto: This revision was merged to the branch mainline in revision 5223.
  • Revision ID: robertc@robertcollins.net-20100506110810-h3j07fh5gmw54s25
Cleaner matcher matching revised unlocking protocol.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006-2011 Canonical Ltd
 
1
# Copyright (C) 2006-2010 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
22
22
import sys
23
23
import threading
24
24
 
25
 
from bzrlib.hooks import Hooks
 
25
from bzrlib.hooks import HookPoint, Hooks
26
26
from bzrlib import (
27
27
    errors,
28
28
    trace,
29
 
    transport as _mod_transport,
 
29
    transport,
30
30
)
31
 
from bzrlib.i18n import gettext
32
31
from bzrlib.lazy_import import lazy_import
33
32
lazy_import(globals(), """
34
33
from bzrlib.smart import medium
35
34
from bzrlib.transport import (
36
35
    chroot,
 
36
    get_transport,
37
37
    pathfilter,
38
38
    )
39
39
from bzrlib import (
51
51
    hooks: An instance of SmartServerHooks.
52
52
    """
53
53
 
54
 
    def __init__(self, backing_transport, root_client_path='/'):
 
54
    def __init__(self, backing_transport, host='127.0.0.1', port=0,
 
55
                 root_client_path='/'):
55
56
        """Construct a new server.
56
57
 
57
58
        To actually start it running, call either start_background_thread or
58
59
        serve.
59
60
 
60
61
        :param backing_transport: The transport to serve.
 
62
        :param host: Name of the interface to listen on.
 
63
        :param port: TCP port to listen on, or 0 to allocate a transient port.
61
64
        :param root_client_path: The client path that will correspond to root
62
65
            of backing_transport.
63
66
        """
64
 
        self.backing_transport = backing_transport
65
 
        self.root_client_path = root_client_path
66
 
 
67
 
    def start_server(self, host, port):
68
 
        """Create the server listening socket.
69
 
 
70
 
        :param host: Name of the interface to listen on.
71
 
        :param port: TCP port to listen on, or 0 to allocate a transient port.
72
 
        """
73
67
        # let connections timeout so that we get a chance to terminate
74
68
        # Keep a reference to the exceptions we want to catch because the socket
75
69
        # module's globals get set to None during interpreter shutdown.
95
89
        self.port = self._sockname[1]
96
90
        self._server_socket.listen(1)
97
91
        self._server_socket.settimeout(1)
 
92
        self.backing_transport = backing_transport
98
93
        self._started = threading.Event()
99
94
        self._stopped = threading.Event()
 
95
        self.root_client_path = root_client_path
100
96
 
101
 
    def _backing_urls(self):
 
97
    def serve(self, thread_name_suffix=''):
 
98
        self._should_terminate = False
 
99
        # for hooks we are letting code know that a server has started (and
 
100
        # later stopped).
102
101
        # There are three interesting urls:
103
102
        # The URL the server can be contacted on. (e.g. bzr://host/)
104
103
        # The URL that a commit done on the same machine as the server will
105
104
        # have within the servers space. (e.g. file:///home/user/source)
106
105
        # The URL that will be given to other hooks in the same process -
107
 
        # the URL of the backing transport itself. (e.g. filtered-36195:///)
 
106
        # the URL of the backing transport itself. (e.g. chroot+:///)
108
107
        # We need all three because:
109
108
        #  * other machines see the first
110
109
        #  * local commits on this machine should be able to be mapped to
114
113
        # The latter two urls are different aliases to the servers url,
115
114
        # so we group those in a list - as there might be more aliases
116
115
        # in the future.
117
 
        urls = [self.backing_transport.base]
 
116
        backing_urls = [self.backing_transport.base]
118
117
        try:
119
 
            urls.append(self.backing_transport.external_url())
 
118
            backing_urls.append(self.backing_transport.external_url())
120
119
        except errors.InProcessTransport:
121
120
            pass
122
 
        return urls
123
 
 
124
 
    def run_server_started_hooks(self, backing_urls=None):
125
 
        if backing_urls is None:
126
 
            backing_urls = self._backing_urls()
127
121
        for hook in SmartTCPServer.hooks['server_started']:
128
122
            hook(backing_urls, self.get_url())
129
123
        for hook in SmartTCPServer.hooks['server_started_ex']:
130
124
            hook(backing_urls, self)
131
 
 
132
 
    def run_server_stopped_hooks(self, backing_urls=None):
133
 
        if backing_urls is None:
134
 
            backing_urls = self._backing_urls()
135
 
        for hook in SmartTCPServer.hooks['server_stopped']:
136
 
            hook(backing_urls, self.get_url())
137
 
 
138
 
    def serve(self, thread_name_suffix=''):
139
 
        self._should_terminate = False
140
 
        # for hooks we are letting code know that a server has started (and
141
 
        # later stopped).
142
 
        self.run_server_started_hooks()
143
125
        self._started.set()
144
126
        try:
145
127
            try:
173
155
            except self._socket_error:
174
156
                # ignore errors on close
175
157
                pass
176
 
            self.run_server_stopped_hooks()
 
158
            for hook in SmartTCPServer.hooks['server_stopped']:
 
159
                hook(backing_urls, self.get_url())
177
160
 
178
161
    def get_url(self):
179
162
        """Return the url of the server"""
180
 
        return "bzr://%s:%s/" % (self._sockname[0], self._sockname[1])
 
163
        return "bzr://%s:%d/" % self._sockname
181
164
 
182
165
    def serve_conn(self, conn, thread_name_suffix):
183
166
        # For WIN32, where the timeout value from the listening socket
189
172
        thread_name = 'smart-server-child' + thread_name_suffix
190
173
        connection_thread = threading.Thread(
191
174
            None, handler.serve, name=thread_name)
192
 
        # FIXME: This thread is never joined, it should at least be collected
193
 
        # somewhere so that tests that want to check for leaked threads can get
194
 
        # rid of them -- vila 20100531
195
175
        connection_thread.setDaemon(True)
196
176
        connection_thread.start()
197
 
        return connection_thread
198
177
 
199
178
    def start_background_thread(self, thread_name_suffix=''):
200
179
        self._started.clear()
240
219
        These are all empty initially, because by default nothing should get
241
220
        notified.
242
221
        """
243
 
        Hooks.__init__(self, "bzrlib.smart.server", "SmartTCPServer.hooks")
244
 
        self.add_hook('server_started',
 
222
        Hooks.__init__(self)
 
223
        self.create_hook(HookPoint('server_started',
245
224
            "Called by the bzr server when it starts serving a directory. "
246
225
            "server_started is called with (backing urls, public url), "
247
226
            "where backing_url is a list of URLs giving the "
248
227
            "server-specific directory locations, and public_url is the "
249
 
            "public URL for the directory being served.", (0, 16))
250
 
        self.add_hook('server_started_ex',
 
228
            "public URL for the directory being served.", (0, 16), None))
 
229
        self.create_hook(HookPoint('server_started_ex',
251
230
            "Called by the bzr server when it starts serving a directory. "
252
231
            "server_started is called with (backing_urls, server_obj).",
253
 
            (1, 17))
254
 
        self.add_hook('server_stopped',
 
232
            (1, 17), None))
 
233
        self.create_hook(HookPoint('server_stopped',
255
234
            "Called by the bzr server when it stops serving a directory. "
256
235
            "server_stopped is called with the same parameters as the "
257
 
            "server_started hook: (backing_urls, public_url).", (0, 16))
258
 
        self.add_hook('server_exception',
259
 
            "Called by the bzr server when an exception occurs. "
260
 
            "server_exception is called with the sys.exc_info() tuple "
261
 
            "return true for the hook if the exception has been handled, "
262
 
            "in which case the server will exit normally.", (2, 4))
 
236
            "server_started hook: (backing_urls, public_url).", (0, 16), None))
263
237
 
264
238
SmartTCPServer.hooks = SmartServerHooks()
265
239
 
331
305
        chroot_server = chroot.ChrootServer(transport)
332
306
        chroot_server.start_server()
333
307
        self.cleanups.append(chroot_server.stop_server)
334
 
        transport = _mod_transport.get_transport_from_url(chroot_server.get_url())
 
308
        transport = get_transport(chroot_server.get_url())
335
309
        if self.base_path is not None:
336
310
            # Decorate the server's backing transport with a filter that can
337
311
            # expand homedirs.
338
312
            expand_userdirs = self._make_expand_userdirs_filter(transport)
339
313
            expand_userdirs.start_server()
340
314
            self.cleanups.append(expand_userdirs.stop_server)
341
 
            transport = _mod_transport.get_transport_from_url(expand_userdirs.get_url())
 
315
            transport = get_transport(expand_userdirs.get_url())
342
316
        self.transport = transport
343
317
 
344
318
    def _make_smart_server(self, host, port, inet):
350
324
                host = medium.BZR_DEFAULT_INTERFACE
351
325
            if port is None:
352
326
                port = medium.BZR_DEFAULT_PORT
353
 
            smart_server = SmartTCPServer(self.transport)
354
 
            smart_server.start_server(host, port)
355
 
            trace.note(gettext('listening on port: %s') % smart_server.port)
 
327
            smart_server = SmartTCPServer(self.transport, host=host, port=port)
 
328
            trace.note('listening on port: %s' % smart_server.port)
356
329
        self.smart_server = smart_server
357
330
 
358
331
    def _change_globals(self):
379
352
        for cleanup in reversed(self.cleanups):
380
353
            cleanup()
381
354
 
 
355
 
382
356
def serve_bzr(transport, host=None, port=None, inet=False):
383
357
    """This is the default implementation of 'bzr serve'.
384
358
    
390
364
    try:
391
365
        bzr_server.set_up(transport, host, port, inet)
392
366
        bzr_server.smart_server.serve()
393
 
    except:
394
 
        hook_caught_exception = False
395
 
        for hook in SmartTCPServer.hooks['server_exception']:
396
 
            hook_caught_exception = hook(sys.exc_info())
397
 
        if not hook_caught_exception:
398
 
            raise
399
367
    finally:
400
368
        bzr_server.tear_down()
 
369