/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

Merge with stacked-fixes

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006, 2007 Canonical Ltd
 
1
# Copyright (C) 2006, 2007, 2008 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
18
18
 
19
19
import errno
20
20
import socket
 
21
import sys
21
22
import threading
22
23
 
23
24
from bzrlib.hooks import Hooks
38
39
    hooks: An instance of SmartServerHooks.
39
40
    """
40
41
 
41
 
    def __init__(self, backing_transport, host='127.0.0.1', port=0):
 
42
    def __init__(self, backing_transport, host='127.0.0.1', port=0,
 
43
                 root_client_path='/'):
42
44
        """Construct a new server.
43
45
 
44
46
        To actually start it running, call either start_background_thread or
45
47
        serve.
46
48
 
 
49
        :param backing_transport: The transport to serve.
47
50
        :param host: Name of the interface to listen on.
48
51
        :param port: TCP port to listen on, or 0 to allocate a transient port.
 
52
        :param root_client_path: The client path that will correspond to root
 
53
            of backing_transport.
49
54
        """
50
55
        # let connections timeout so that we get a chance to terminate
51
56
        # Keep a reference to the exceptions we want to catch because the socket
55
60
        self._socket_error = socket_error
56
61
        self._socket_timeout = socket_timeout
57
62
        self._server_socket = socket.socket()
58
 
        self._server_socket.bind((host, port))
 
63
        # SO_REUSERADDR has a different meaning on Windows
 
64
        if sys.platform != 'win32':
 
65
            self._server_socket.setsockopt(socket.SOL_SOCKET,
 
66
                socket.SO_REUSEADDR, 1)
 
67
        try:
 
68
            self._server_socket.bind((host, port))
 
69
        except self._socket_error, message:
 
70
            raise errors.CannotBindAddress(host, port, message)
59
71
        self._sockname = self._server_socket.getsockname()
60
72
        self.port = self._sockname[1]
61
73
        self._server_socket.listen(1)
63
75
        self.backing_transport = backing_transport
64
76
        self._started = threading.Event()
65
77
        self._stopped = threading.Event()
 
78
        self.root_client_path = root_client_path
66
79
 
67
 
    def serve(self):
 
80
    def serve(self, thread_name_suffix=''):
68
81
        self._should_terminate = False
69
82
        # for hooks we are letting code know that a server has started (and
70
83
        # later stopped).
106
119
                        if e.args[0] != errno.EBADF:
107
120
                            trace.warning("listening socket error: %s", e)
108
121
                    else:
109
 
                        self.serve_conn(conn)
 
122
                        self.serve_conn(conn, thread_name_suffix)
110
123
            except KeyboardInterrupt:
111
124
                # dont log when CTRL-C'd.
112
125
                raise
129
142
        """Return the url of the server"""
130
143
        return "bzr://%s:%d/" % self._sockname
131
144
 
132
 
    def serve_conn(self, conn):
 
145
    def serve_conn(self, conn, thread_name_suffix):
133
146
        # For WIN32, where the timeout value from the listening socket
134
147
        # propogates to the newly accepted socket.
135
148
        conn.setblocking(True)
136
149
        conn.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
137
 
        handler = SmartServerSocketStreamMedium(conn, self.backing_transport)
138
 
        connection_thread = threading.Thread(None, handler.serve, name='smart-server-child')
 
150
        handler = SmartServerSocketStreamMedium(
 
151
            conn, self.backing_transport, self.root_client_path)
 
152
        thread_name = 'smart-server-child' + thread_name_suffix
 
153
        connection_thread = threading.Thread(
 
154
            None, handler.serve, name=thread_name)
139
155
        connection_thread.setDaemon(True)
140
156
        connection_thread.start()
141
157
 
142
 
    def start_background_thread(self):
 
158
    def start_background_thread(self, thread_name_suffix=''):
143
159
        self._started.clear()
144
160
        self._server_thread = threading.Thread(None,
145
 
                self.serve,
 
161
                self.serve, args=(thread_name_suffix,),
146
162
                name='server-' + self.get_url())
147
163
        self._server_thread.setDaemon(True)
148
164
        self._server_thread.start()
202
218
    This server is backed by the process's cwd.
203
219
    """
204
220
 
205
 
    def __init__(self):
 
221
    def __init__(self, thread_name_suffix=''):
206
222
        SmartTCPServer.__init__(self, None)
 
223
        self.client_path_extra = None
 
224
        self.thread_name_suffix = thread_name_suffix
207
225
        
208
226
    def get_backing_transport(self, backing_transport_server):
209
227
        """Get a backing transport from a server we are decorating."""
210
228
        return transport.get_transport(backing_transport_server.get_url())
211
229
 
212
 
    def setUp(self, backing_transport_server=None):
213
 
        """Set up server for testing"""
 
230
    def setUp(self, backing_transport_server=None,
 
231
              client_path_extra='/extra/'):
 
232
        """Set up server for testing.
 
233
        
 
234
        :param backing_transport_server: backing server to use.  If not
 
235
            specified, a LocalURLServer at the current working directory will
 
236
            be used.
 
237
        :param client_path_extra: a path segment starting with '/' to append to
 
238
            the root URL for this server.  For instance, a value of '/foo/bar/'
 
239
            will mean the root of the backing transport will be published at a
 
240
            URL like `bzr://127.0.0.1:nnnn/foo/bar/`, rather than
 
241
            `bzr://127.0.0.1:nnnn/`.  Default value is `extra`, so that tests
 
242
            by default will fail unless they do the necessary path translation.
 
243
        """
 
244
        if not client_path_extra.startswith('/'):
 
245
            raise ValueError(client_path_extra)
214
246
        from bzrlib.transport.chroot import ChrootServer
215
247
        if backing_transport_server is None:
216
248
            from bzrlib.transport.local import LocalURLServer
220
252
        self.chroot_server.setUp()
221
253
        self.backing_transport = transport.get_transport(
222
254
            self.chroot_server.get_url())
223
 
        self.start_background_thread()
 
255
        self.root_client_path = self.client_path_extra = client_path_extra
 
256
        self.start_background_thread(self.thread_name_suffix)
224
257
 
225
258
    def tearDown(self):
226
259
        self.stop_background_thread()
227
260
        self.chroot_server.tearDown()
228
261
 
 
262
    def get_url(self):
 
263
        url = super(SmartTCPServer_for_testing, self).get_url()
 
264
        return url[:-1] + self.client_path_extra
 
265
 
229
266
    def get_bogus_url(self):
230
267
        """Return a URL which will fail to connect"""
231
268
        return 'bzr://127.0.0.1:1/'
238
275
        """Get a backing transport from a server we are decorating."""
239
276
        url = 'readonly+' + backing_transport_server.get_url()
240
277
        return transport.get_transport(url)
241