~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/smart/server.py

  • Committer: Jonathan Riddell
  • Date: 2011-05-16 10:05:25 UTC
  • mto: This revision was merged to the branch mainline in revision 5869.
  • Revision ID: jriddell@canonical.com-20110516100525-7q23m5opdnl4qg41
start adding licences

Show diffs side-by-side

added added

removed removed

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