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