~bzr-pqm/bzr/bzr.dev

5909.2.2 by Jonathan Riddell
remove debugging
1
# Copyright (C) 2006-2011 Canonical Ltd
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
2
#
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
#
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
4183.7.1 by Sabin Iacob
update FSF mailing address
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
16
2018.5.19 by Andrew Bennetts
Add docstrings to all the new modules, and a few other places.
17
"""Server for smart-server protocol."""
18
2400.1.7 by Andrew Bennetts
Merge from bzr.dev.
19
import errno
4634.43.3 by Andrew Bennetts
Expand /~/ and /~user/ in 'bzr serve'.
20
import os.path
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
21
import socket
3374.1.1 by Martin Pool
Set SO_REUSEADDR on server sockets (#164288)
22
import sys
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
23
import threading
24
5622.3.2 by Jelmer Vernooij
Add more lazily usable hook points.
25
from bzrlib.hooks import Hooks
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
26
from bzrlib import (
2634.1.1 by Robert Collins
(robertc) Reinstate the accidentally backed out external_url patch.
27
    errors,
2018.5.15 by Andrew Bennetts
Tidy some imports, and bugs introduced when adding server.py
28
    trace,
5609.9.1 by Martin
Blindly change all users of get_transport to address the function via the transport module
29
    transport as _mod_transport,
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
30
)
3224.5.5 by Andrew Bennetts
Don't import bzrlib.smart.medium from bzrlib.smart.server until it's needed. This helps the bzr-dbus plugin import faster.
31
from bzrlib.lazy_import import lazy_import
32
lazy_import(globals(), """
33
from bzrlib.smart import medium
4634.43.5 by Andrew Bennetts
Refactor bzr_serve so that building the backing_transport, constructing the server object, and actually starting the server are separate functions/methods.
34
from bzrlib.transport import (
35
    chroot,
36
    pathfilter,
37
    )
4634.43.3 by Andrew Bennetts
Expand /~/ and /~user/ in 'bzr serve'.
38
from bzrlib import (
39
    urlutils,
40
    )
3224.5.5 by Andrew Bennetts
Don't import bzrlib.smart.medium from bzrlib.smart.server until it's needed. This helps the bzr-dbus plugin import faster.
41
""")
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
42
43
44
class SmartTCPServer(object):
45
    """Listens on a TCP socket and accepts connections from smart clients.
2018.5.139 by Andrew Bennetts
Merge from bzr.dev, resolving conflicts.
46
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
47
    Each connection will be served by a SmartServerSocketStreamMedium running in
2018.5.139 by Andrew Bennetts
Merge from bzr.dev, resolving conflicts.
48
    a thread.
2400.1.7 by Andrew Bennetts
Merge from bzr.dev.
49
50
    hooks: An instance of SmartServerHooks.
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
51
    """
52
5247.3.36 by Vincent Ladeuil
Start refactoring the smart server to control which thread it runs in.
53
    def __init__(self, backing_transport, root_client_path='/'):
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
54
        """Construct a new server.
55
56
        To actually start it running, call either start_background_thread or
57
        serve.
58
2692.1.14 by Andrew Bennetts
All WSGI tests passing, and manual testing works too.
59
        :param backing_transport: The transport to serve.
5247.3.36 by Vincent Ladeuil
Start refactoring the smart server to control which thread it runs in.
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
5247.3.48 by Vincent Ladeuil
Fixed as per lifeless suggestion.
66
    def start_server(self, host, port):
5247.3.36 by Vincent Ladeuil
Start refactoring the smart server to control which thread it runs in.
67
        """Create the server listening socket.
68
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
69
        :param host: Name of the interface to listen on.
70
        :param port: TCP port to listen on, or 0 to allocate a transient port.
71
        """
2400.1.7 by Andrew Bennetts
Merge from bzr.dev.
72
        # let connections timeout so that we get a chance to terminate
73
        # Keep a reference to the exceptions we want to catch because the socket
74
        # module's globals get set to None during interpreter shutdown.
75
        from socket import timeout as socket_timeout
76
        from socket import error as socket_error
77
        self._socket_error = socket_error
78
        self._socket_timeout = socket_timeout
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
79
        addrs = socket.getaddrinfo(host, port, socket.AF_UNSPEC,
3711.2.2 by Jelmer Vernooij
Avoid using AI_ADDRCONFIG since it's not portable.
80
            socket.SOCK_STREAM, 0, socket.AI_PASSIVE)[0]
3665.4.1 by Jelmer Vernooij
Support IPv6 in the smart server.
81
82
        (family, socktype, proto, canonname, sockaddr) = addrs
83
84
        self._server_socket = socket.socket(family, socktype, proto)
3374.1.1 by Martin Pool
Set SO_REUSEADDR on server sockets (#164288)
85
        # SO_REUSERADDR has a different meaning on Windows
86
        if sys.platform != 'win32':
87
            self._server_socket.setsockopt(socket.SOL_SOCKET,
88
                socket.SO_REUSEADDR, 1)
3365.1.1 by Andrea Corbellini
Handle errors raised by socket.bind() (bug 200575)
89
        try:
3665.4.1 by Jelmer Vernooij
Support IPv6 in the smart server.
90
            self._server_socket.bind(sockaddr)
3365.1.1 by Andrea Corbellini
Handle errors raised by socket.bind() (bug 200575)
91
        except self._socket_error, message:
92
            raise errors.CannotBindAddress(host, port, message)
2400.1.7 by Andrew Bennetts
Merge from bzr.dev.
93
        self._sockname = self._server_socket.getsockname()
94
        self.port = self._sockname[1]
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
95
        self._server_socket.listen(1)
96
        self._server_socket.settimeout(1)
2400.1.7 by Andrew Bennetts
Merge from bzr.dev.
97
        self._started = threading.Event()
98
        self._stopped = threading.Event()
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
99
5247.3.36 by Vincent Ladeuil
Start refactoring the smart server to control which thread it runs in.
100
    def _backing_urls(self):
2634.1.1 by Robert Collins
(robertc) Reinstate the accidentally backed out external_url patch.
101
        # There are three interesting urls:
102
        # The URL the server can be contacted on. (e.g. bzr://host/)
103
        # The URL that a commit done on the same machine as the server will
104
        # have within the servers space. (e.g. file:///home/user/source)
105
        # The URL that will be given to other hooks in the same process -
5699.5.1 by Max Bowsher
Expunge a few lingering references to ChrootTransportDecorator and the chroot+
106
        # the URL of the backing transport itself. (e.g. filtered-36195:///)
2634.1.1 by Robert Collins
(robertc) Reinstate the accidentally backed out external_url patch.
107
        # We need all three because:
108
        #  * other machines see the first
109
        #  * local commits on this machine should be able to be mapped to
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
110
        #    this server
2634.1.1 by Robert Collins
(robertc) Reinstate the accidentally backed out external_url patch.
111
        #  * commits the server does itself need to be mapped across to this
112
        #    server.
113
        # The latter two urls are different aliases to the servers url,
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
114
        # so we group those in a list - as there might be more aliases
2634.1.1 by Robert Collins
(robertc) Reinstate the accidentally backed out external_url patch.
115
        # in the future.
5247.3.36 by Vincent Ladeuil
Start refactoring the smart server to control which thread it runs in.
116
        urls = [self.backing_transport.base]
2634.1.1 by Robert Collins
(robertc) Reinstate the accidentally backed out external_url patch.
117
        try:
5247.3.36 by Vincent Ladeuil
Start refactoring the smart server to control which thread it runs in.
118
            urls.append(self.backing_transport.external_url())
2634.1.1 by Robert Collins
(robertc) Reinstate the accidentally backed out external_url patch.
119
        except errors.InProcessTransport:
120
            pass
5247.3.36 by Vincent Ladeuil
Start refactoring the smart server to control which thread it runs in.
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()
2400.1.7 by Andrew Bennetts
Merge from bzr.dev.
126
        for hook in SmartTCPServer.hooks['server_started']:
2634.1.1 by Robert Collins
(robertc) Reinstate the accidentally backed out external_url patch.
127
            hook(backing_urls, self.get_url())
4544.1.2 by Andrew Bennetts
Add test that would catch the lack of ChrootServer in cmd_serve.
128
        for hook in SmartTCPServer.hooks['server_started_ex']:
4544.1.3 by Andrew Bennetts
Pass backing_urls to the new server_started_ex hook.
129
            hook(backing_urls, self)
5247.3.36 by Vincent Ladeuil
Start refactoring the smart server to control which thread it runs in.
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()
5929.2.2 by Vincent Ladeuil
Tsk, tsk, that's not the issue at all, the race is in the test framework.
142
        self._started.set()
2400.1.7 by Andrew Bennetts
Merge from bzr.dev.
143
        try:
144
            try:
145
                while not self._should_terminate:
146
                    try:
147
                        conn, client_addr = self._server_socket.accept()
148
                    except self._socket_timeout:
149
                        # just check if we're asked to stop
150
                        pass
151
                    except self._socket_error, e:
152
                        # if the socket is closed by stop_background_thread
153
                        # we might get a EBADF here, any other socket errors
154
                        # should get logged.
155
                        if e.args[0] != errno.EBADF:
156
                            trace.warning("listening socket error: %s", e)
157
                    else:
5011.3.10 by Andrew Bennetts
Try a bit harder to stop a SmartTCPServer sooner when _should_terminate has been set.
158
                        if self._should_terminate:
159
                            break
3245.4.28 by Andrew Bennetts
Remove another XXX, and include test ID in smart server thread names.
160
                        self.serve_conn(conn, thread_name_suffix)
2400.1.7 by Andrew Bennetts
Merge from bzr.dev.
161
            except KeyboardInterrupt:
162
                # dont log when CTRL-C'd.
163
                raise
164
            except Exception, e:
4695.5.5 by Martin Pool
Unhandled smart-server exceptions are reported using generic report_exception
165
                trace.report_exception(sys.exc_info(), sys.stderr)
2400.1.7 by Andrew Bennetts
Merge from bzr.dev.
166
                raise
167
        finally:
168
            self._stopped.set()
169
            try:
170
                # ensure the server socket is closed.
171
                self._server_socket.close()
172
            except self._socket_error:
173
                # ignore errors on close
174
                pass
5247.3.36 by Vincent Ladeuil
Start refactoring the smart server to control which thread it runs in.
175
            self.run_server_stopped_hooks()
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
176
177
    def get_url(self):
178
        """Return the url of the server"""
5611.1.3 by Jelmer Vernooij
review comments from Vincent.
179
        return "bzr://%s:%s/" % (self._sockname[0], self._sockname[1])
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
180
3245.4.28 by Andrew Bennetts
Remove another XXX, and include test ID in smart server thread names.
181
    def serve_conn(self, conn, thread_name_suffix):
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
182
        # For WIN32, where the timeout value from the listening socket
4031.3.1 by Frank Aspell
Fixing various typos
183
        # propagates to the newly accepted socket.
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
184
        conn.setblocking(True)
185
        conn.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
3224.5.5 by Andrew Bennetts
Don't import bzrlib.smart.medium from bzrlib.smart.server until it's needed. This helps the bzr-dbus plugin import faster.
186
        handler = medium.SmartServerSocketStreamMedium(
2692.1.11 by Andrew Bennetts
Improve test coverage by making SmartTCPServer_for_testing by default create a server that does not serve the backing transport's root at its own root. This mirrors the way most HTTP smart servers are configured.
187
            conn, self.backing_transport, self.root_client_path)
3245.4.28 by Andrew Bennetts
Remove another XXX, and include test ID in smart server thread names.
188
        thread_name = 'smart-server-child' + thread_name_suffix
189
        connection_thread = threading.Thread(
190
            None, handler.serve, name=thread_name)
5247.2.9 by Vincent Ladeuil
The smart server leaks one thread (daemonic) for each connection.
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
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
194
        connection_thread.setDaemon(True)
195
        connection_thread.start()
4731.2.8 by Vincent Ladeuil
Collect and shutdown clients for SmartTCPServer_for_testing.
196
        return connection_thread
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
197
3245.4.28 by Andrew Bennetts
Remove another XXX, and include test ID in smart server thread names.
198
    def start_background_thread(self, thread_name_suffix=''):
2400.1.7 by Andrew Bennetts
Merge from bzr.dev.
199
        self._started.clear()
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
200
        self._server_thread = threading.Thread(None,
3245.4.28 by Andrew Bennetts
Remove another XXX, and include test ID in smart server thread names.
201
                self.serve, args=(thread_name_suffix,),
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
202
                name='server-' + self.get_url())
203
        self._server_thread.setDaemon(True)
204
        self._server_thread.start()
2400.1.7 by Andrew Bennetts
Merge from bzr.dev.
205
        self._started.wait()
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
206
207
    def stop_background_thread(self):
2400.1.7 by Andrew Bennetts
Merge from bzr.dev.
208
        self._stopped.clear()
209
        # tell the main loop to quit on the next iteration.
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
210
        self._should_terminate = True
2400.1.7 by Andrew Bennetts
Merge from bzr.dev.
211
        # close the socket - gives error to connections from here on in,
212
        # rather than a connection reset error to connections made during
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
213
        # the period between setting _should_terminate = True and
2400.1.7 by Andrew Bennetts
Merge from bzr.dev.
214
        # the current request completing/aborting. It may also break out the
215
        # main loop if it was currently in accept() (on some platforms).
216
        try:
217
            self._server_socket.close()
218
        except self._socket_error:
219
            # ignore errors on close
220
            pass
221
        if not self._stopped.isSet():
222
            # server has not stopped (though it may be stopping)
223
            # its likely in accept(), so give it a connection
224
            temp_socket = socket.socket()
225
            temp_socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
226
            if not temp_socket.connect_ex(self._sockname):
227
                # and close it immediately: we dont choose to send any requests.
228
                temp_socket.close()
229
        self._stopped.wait()
230
        self._server_thread.join()
231
232
233
class SmartServerHooks(Hooks):
234
    """Hooks for the smart server."""
235
5622.3.10 by Jelmer Vernooij
Don't require arguments to hooks.
236
    def __init__(self):
2400.1.7 by Andrew Bennetts
Merge from bzr.dev.
237
        """Create the default hooks.
238
239
        These are all empty initially, because by default nothing should get
240
        notified.
241
        """
5622.3.10 by Jelmer Vernooij
Don't require arguments to hooks.
242
        Hooks.__init__(self, "bzrlib.smart.server", "SmartTCPServer.hooks")
5622.3.2 by Jelmer Vernooij
Add more lazily usable hook points.
243
        self.add_hook('server_started',
4119.3.2 by Robert Collins
Migrate existing hooks over to the new HookPoint infrastructure.
244
            "Called by the bzr server when it starts serving a directory. "
245
            "server_started is called with (backing urls, public url), "
246
            "where backing_url is a list of URLs giving the "
247
            "server-specific directory locations, and public_url is the "
5622.3.2 by Jelmer Vernooij
Add more lazily usable hook points.
248
            "public URL for the directory being served.", (0, 16))
249
        self.add_hook('server_started_ex',
4544.1.2 by Andrew Bennetts
Add test that would catch the lack of ChrootServer in cmd_serve.
250
            "Called by the bzr server when it starts serving a directory. "
4544.1.3 by Andrew Bennetts
Pass backing_urls to the new server_started_ex hook.
251
            "server_started is called with (backing_urls, server_obj).",
5622.3.2 by Jelmer Vernooij
Add more lazily usable hook points.
252
            (1, 17))
253
        self.add_hook('server_stopped',
4119.3.2 by Robert Collins
Migrate existing hooks over to the new HookPoint infrastructure.
254
            "Called by the bzr server when it stops serving a directory. "
255
            "server_stopped is called with the same parameters as the "
5622.3.2 by Jelmer Vernooij
Add more lazily usable hook points.
256
            "server_started hook: (backing_urls, public_url).", (0, 16))
5909.2.1 by Jonathan Riddell
add hook for exceptions in the smart server
257
        self.add_hook('server_exception',
258
            "Called by the bzr server when an exception occurs. "
259
            "server_exception is called with the sys.exc_info() tuple "
260
            "return true for the hook if the exception has been handled, "
261
            "in which case the server will exit normally.", (2, 4))
2400.1.7 by Andrew Bennetts
Merge from bzr.dev.
262
5622.3.10 by Jelmer Vernooij
Don't require arguments to hooks.
263
SmartTCPServer.hooks = SmartServerHooks()
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
264
5909.2.5 by Jonathan Riddell
split test into two tests
265
4634.43.3 by Andrew Bennetts
Expand /~/ and /~user/ in 'bzr serve'.
266
def _local_path_for_transport(transport):
267
    """Return a local path for transport, if reasonably possible.
268
    
269
    This function works even if transport's url has a "readonly+" prefix,
270
    unlike local_path_from_url.
271
    
272
    This essentially recovers the --directory argument the user passed to "bzr
273
    serve" from the transport passed to serve_bzr.
274
    """
275
    try:
276
        base_url = transport.external_url()
277
    except (errors.InProcessTransport, NotImplementedError):
278
        return None
279
    else:
280
        # Strip readonly prefix
281
        if base_url.startswith('readonly+'):
282
            base_url = base_url[len('readonly+'):]
283
        try:
284
            return urlutils.local_path_from_url(base_url)
285
        except errors.InvalidURL:
286
            return None
287
4634.43.4 by Andrew Bennetts
Add docstring.
288
4634.43.15 by Andrew Bennetts
Rename BzrServerMaker -> BzrServerFactory.
289
class BzrServerFactory(object):
4634.43.16 by Andrew Bennetts
Docstring improvements.
290
    """Helper class for serve_bzr."""
4634.43.6 by Andrew Bennetts
Refactor bzr_serve more so that it is possible to use a function other than os.path.expanduser for the userdir expansion.
291
4634.43.7 by Andrew Bennetts
Add some unit tests for parts of userdir expansion.
292
    def __init__(self, userdir_expander=None, get_base_path=None):
4634.43.6 by Andrew Bennetts
Refactor bzr_serve more so that it is possible to use a function other than os.path.expanduser for the userdir expansion.
293
        self.cleanups = []
294
        self.base_path = None
295
        self.backing_transport = None
296
        if userdir_expander is None:
297
            userdir_expander = os.path.expanduser
298
        self.userdir_expander = userdir_expander
4634.43.7 by Andrew Bennetts
Add some unit tests for parts of userdir expansion.
299
        if get_base_path is None:
300
            get_base_path = _local_path_for_transport
301
        self.get_base_path = get_base_path
4634.43.6 by Andrew Bennetts
Refactor bzr_serve more so that it is possible to use a function other than os.path.expanduser for the userdir expansion.
302
303
    def _expand_userdirs(self, path):
4634.43.4 by Andrew Bennetts
Add docstring.
304
        """Translate /~/ or /~user/ to e.g. /home/foo, using
4634.43.16 by Andrew Bennetts
Docstring improvements.
305
        self.userdir_expander (os.path.expanduser by default).
4634.43.4 by Andrew Bennetts
Add docstring.
306
307
        If the translated path would fall outside base_path, or the path does
308
        not start with ~, then no translation is applied.
309
310
        If the path is inside, it is adjusted to be relative to the base path.
311
312
        e.g. if base_path is /home, and the expanded path is /home/joe, then
313
        the translated path is joe.
314
        """
4634.43.3 by Andrew Bennetts
Expand /~/ and /~user/ in 'bzr serve'.
315
        result = path
316
        if path.startswith('~'):
4634.43.6 by Andrew Bennetts
Refactor bzr_serve more so that it is possible to use a function other than os.path.expanduser for the userdir expansion.
317
            expanded = self.userdir_expander(path)
4634.43.3 by Andrew Bennetts
Expand /~/ and /~user/ in 'bzr serve'.
318
            if not expanded.endswith('/'):
319
                expanded += '/'
4634.43.6 by Andrew Bennetts
Refactor bzr_serve more so that it is possible to use a function other than os.path.expanduser for the userdir expansion.
320
            if expanded.startswith(self.base_path):
321
                result = expanded[len(self.base_path):]
4634.43.3 by Andrew Bennetts
Expand /~/ and /~user/ in 'bzr serve'.
322
        return result
4634.43.6 by Andrew Bennetts
Refactor bzr_serve more so that it is possible to use a function other than os.path.expanduser for the userdir expansion.
323
324
    def _make_expand_userdirs_filter(self, transport):
325
        return pathfilter.PathFilteringServer(transport, self._expand_userdirs)
4634.43.5 by Andrew Bennetts
Refactor bzr_serve so that building the backing_transport, constructing the server object, and actually starting the server are separate functions/methods.
326
327
    def _make_backing_transport(self, transport):
328
        """Chroot transport, and decorate with userdir expander."""
4634.43.7 by Andrew Bennetts
Add some unit tests for parts of userdir expansion.
329
        self.base_path = self.get_base_path(transport)
4634.43.5 by Andrew Bennetts
Refactor bzr_serve so that building the backing_transport, constructing the server object, and actually starting the server are separate functions/methods.
330
        chroot_server = chroot.ChrootServer(transport)
4934.3.3 by Martin Pool
Rename Server.setUp to Server.start_server
331
        chroot_server.start_server()
4934.3.1 by Martin Pool
Rename Server.tearDown to .stop_server
332
        self.cleanups.append(chroot_server.stop_server)
5609.9.1 by Martin
Blindly change all users of get_transport to address the function via the transport module
333
        transport = _mod_transport.get_transport(chroot_server.get_url())
4634.43.6 by Andrew Bennetts
Refactor bzr_serve more so that it is possible to use a function other than os.path.expanduser for the userdir expansion.
334
        if self.base_path is not None:
4634.43.3 by Andrew Bennetts
Expand /~/ and /~user/ in 'bzr serve'.
335
            # Decorate the server's backing transport with a filter that can
336
            # expand homedirs.
4634.43.6 by Andrew Bennetts
Refactor bzr_serve more so that it is possible to use a function other than os.path.expanduser for the userdir expansion.
337
            expand_userdirs = self._make_expand_userdirs_filter(transport)
4934.3.3 by Martin Pool
Rename Server.setUp to Server.start_server
338
            expand_userdirs.start_server()
4934.3.1 by Martin Pool
Rename Server.tearDown to .stop_server
339
            self.cleanups.append(expand_userdirs.stop_server)
5609.9.1 by Martin
Blindly change all users of get_transport to address the function via the transport module
340
            transport = _mod_transport.get_transport(expand_userdirs.get_url())
4634.43.5 by Andrew Bennetts
Refactor bzr_serve so that building the backing_transport, constructing the server object, and actually starting the server are separate functions/methods.
341
        self.transport = transport
342
343
    def _make_smart_server(self, host, port, inet):
4634.43.3 by Andrew Bennetts
Expand /~/ and /~user/ in 'bzr serve'.
344
        if inet:
345
            smart_server = medium.SmartServerPipeStreamMedium(
4634.43.5 by Andrew Bennetts
Refactor bzr_serve so that building the backing_transport, constructing the server object, and actually starting the server are separate functions/methods.
346
                sys.stdin, sys.stdout, self.transport)
4634.43.3 by Andrew Bennetts
Expand /~/ and /~user/ in 'bzr serve'.
347
        else:
348
            if host is None:
349
                host = medium.BZR_DEFAULT_INTERFACE
350
            if port is None:
351
                port = medium.BZR_DEFAULT_PORT
5247.3.36 by Vincent Ladeuil
Start refactoring the smart server to control which thread it runs in.
352
            smart_server = SmartTCPServer(self.transport)
5247.3.48 by Vincent Ladeuil
Fixed as per lifeless suggestion.
353
            smart_server.start_server(host, port)
4634.43.3 by Andrew Bennetts
Expand /~/ and /~user/ in 'bzr serve'.
354
            trace.note('listening on port: %s' % smart_server.port)
4634.43.5 by Andrew Bennetts
Refactor bzr_serve so that building the backing_transport, constructing the server object, and actually starting the server are separate functions/methods.
355
        self.smart_server = smart_server
356
357
    def _change_globals(self):
358
        from bzrlib import lockdir, ui
4634.43.3 by Andrew Bennetts
Expand /~/ and /~user/ in 'bzr serve'.
359
        # For the duration of this server, no UI output is permitted. note
360
        # that this may cause problems with blackbox tests. This should be
361
        # changed with care though, as we dont want to use bandwidth sending
362
        # progress over stderr to smart server clients!
363
        old_factory = ui.ui_factory
364
        old_lockdir_timeout = lockdir._DEFAULT_TIMEOUT_SECONDS
365
        def restore_default_ui_factory_and_lockdir_timeout():
366
            ui.ui_factory = old_factory
367
            lockdir._DEFAULT_TIMEOUT_SECONDS = old_lockdir_timeout
4634.43.5 by Andrew Bennetts
Refactor bzr_serve so that building the backing_transport, constructing the server object, and actually starting the server are separate functions/methods.
368
        self.cleanups.append(restore_default_ui_factory_and_lockdir_timeout)
4370.4.6 by Jelmer Vernooij
Move server protocol registry to bzrlib.transport.
369
        ui.ui_factory = ui.SilentUIFactory()
370
        lockdir._DEFAULT_TIMEOUT_SECONDS = 0
4634.43.5 by Andrew Bennetts
Refactor bzr_serve so that building the backing_transport, constructing the server object, and actually starting the server are separate functions/methods.
371
4634.43.19 by Andrew Bennetts
Rename BzrServerFactory's setUp/tearDown to set_up/tear_down; this isn't a TestCase (or transport Server), so we should not use camelCase names.
372
    def set_up(self, transport, host, port, inet):
4634.43.5 by Andrew Bennetts
Refactor bzr_serve so that building the backing_transport, constructing the server object, and actually starting the server are separate functions/methods.
373
        self._make_backing_transport(transport)
374
        self._make_smart_server(host, port, inet)
375
        self._change_globals()
376
4634.43.19 by Andrew Bennetts
Rename BzrServerFactory's setUp/tearDown to set_up/tear_down; this isn't a TestCase (or transport Server), so we should not use camelCase names.
377
    def tear_down(self):
4634.43.9 by Andrew Bennetts
Add BzrServerMaker.tearDown, and call it from tests.
378
        for cleanup in reversed(self.cleanups):
379
            cleanup()
380
4634.43.5 by Andrew Bennetts
Refactor bzr_serve so that building the backing_transport, constructing the server object, and actually starting the server are separate functions/methods.
381
def serve_bzr(transport, host=None, port=None, inet=False):
4634.43.16 by Andrew Bennetts
Docstring improvements.
382
    """This is the default implementation of 'bzr serve'.
383
    
384
    It creates a TCP or pipe smart server on 'transport, and runs it.  The
385
    transport will be decorated with a chroot and pathfilter (using
386
    os.path.expanduser).
387
    """
4634.43.15 by Andrew Bennetts
Rename BzrServerMaker -> BzrServerFactory.
388
    bzr_server = BzrServerFactory()
4634.43.5 by Andrew Bennetts
Refactor bzr_serve so that building the backing_transport, constructing the server object, and actually starting the server are separate functions/methods.
389
    try:
4634.43.19 by Andrew Bennetts
Rename BzrServerFactory's setUp/tearDown to set_up/tear_down; this isn't a TestCase (or transport Server), so we should not use camelCase names.
390
        bzr_server.set_up(transport, host, port, inet)
4634.43.5 by Andrew Bennetts
Refactor bzr_serve so that building the backing_transport, constructing the server object, and actually starting the server are separate functions/methods.
391
        bzr_server.smart_server.serve()
5909.2.1 by Jonathan Riddell
add hook for exceptions in the smart server
392
    except:
393
        hook_caught_exception = False
394
        for hook in SmartTCPServer.hooks['server_exception']:
395
            hook_caught_exception = hook(sys.exc_info())
396
        if not hook_caught_exception:
397
            raise
4370.4.6 by Jelmer Vernooij
Move server protocol registry to bzrlib.transport.
398
    finally:
4634.43.19 by Andrew Bennetts
Rename BzrServerFactory's setUp/tearDown to set_up/tear_down; this isn't a TestCase (or transport Server), so we should not use camelCase names.
399
        bzr_server.tear_down()