~bzr-pqm/bzr/bzr.dev

4763.2.4 by John Arbash Meinel
merge bzr.2.1 in preparation for NEWS entry.
1
# Copyright (C) 2006-2010 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()
2400.1.7 by Andrew Bennetts
Merge from bzr.dev.
142
        self._started.set()
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))
2400.1.7 by Andrew Bennetts
Merge from bzr.dev.
257
5622.3.10 by Jelmer Vernooij
Don't require arguments to hooks.
258
SmartTCPServer.hooks = SmartServerHooks()
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
259
2400.1.2 by Andrew Bennetts
Move SmartTCPServer classes into bzrlib/smart/server.py
260
4634.43.3 by Andrew Bennetts
Expand /~/ and /~user/ in 'bzr serve'.
261
def _local_path_for_transport(transport):
262
    """Return a local path for transport, if reasonably possible.
263
    
264
    This function works even if transport's url has a "readonly+" prefix,
265
    unlike local_path_from_url.
266
    
267
    This essentially recovers the --directory argument the user passed to "bzr
268
    serve" from the transport passed to serve_bzr.
269
    """
270
    try:
271
        base_url = transport.external_url()
272
    except (errors.InProcessTransport, NotImplementedError):
273
        return None
274
    else:
275
        # Strip readonly prefix
276
        if base_url.startswith('readonly+'):
277
            base_url = base_url[len('readonly+'):]
278
        try:
279
            return urlutils.local_path_from_url(base_url)
280
        except errors.InvalidURL:
281
            return None
282
4634.43.4 by Andrew Bennetts
Add docstring.
283
4634.43.15 by Andrew Bennetts
Rename BzrServerMaker -> BzrServerFactory.
284
class BzrServerFactory(object):
4634.43.16 by Andrew Bennetts
Docstring improvements.
285
    """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.
286
4634.43.7 by Andrew Bennetts
Add some unit tests for parts of userdir expansion.
287
    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.
288
        self.cleanups = []
289
        self.base_path = None
290
        self.backing_transport = None
291
        if userdir_expander is None:
292
            userdir_expander = os.path.expanduser
293
        self.userdir_expander = userdir_expander
4634.43.7 by Andrew Bennetts
Add some unit tests for parts of userdir expansion.
294
        if get_base_path is None:
295
            get_base_path = _local_path_for_transport
296
        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.
297
298
    def _expand_userdirs(self, path):
4634.43.4 by Andrew Bennetts
Add docstring.
299
        """Translate /~/ or /~user/ to e.g. /home/foo, using
4634.43.16 by Andrew Bennetts
Docstring improvements.
300
        self.userdir_expander (os.path.expanduser by default).
4634.43.4 by Andrew Bennetts
Add docstring.
301
302
        If the translated path would fall outside base_path, or the path does
303
        not start with ~, then no translation is applied.
304
305
        If the path is inside, it is adjusted to be relative to the base path.
306
307
        e.g. if base_path is /home, and the expanded path is /home/joe, then
308
        the translated path is joe.
309
        """
4634.43.3 by Andrew Bennetts
Expand /~/ and /~user/ in 'bzr serve'.
310
        result = path
311
        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.
312
            expanded = self.userdir_expander(path)
4634.43.3 by Andrew Bennetts
Expand /~/ and /~user/ in 'bzr serve'.
313
            if not expanded.endswith('/'):
314
                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.
315
            if expanded.startswith(self.base_path):
316
                result = expanded[len(self.base_path):]
4634.43.3 by Andrew Bennetts
Expand /~/ and /~user/ in 'bzr serve'.
317
        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.
318
319
    def _make_expand_userdirs_filter(self, transport):
320
        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.
321
322
    def _make_backing_transport(self, transport):
323
        """Chroot transport, and decorate with userdir expander."""
4634.43.7 by Andrew Bennetts
Add some unit tests for parts of userdir expansion.
324
        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.
325
        chroot_server = chroot.ChrootServer(transport)
4934.3.3 by Martin Pool
Rename Server.setUp to Server.start_server
326
        chroot_server.start_server()
4934.3.1 by Martin Pool
Rename Server.tearDown to .stop_server
327
        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
328
        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.
329
        if self.base_path is not None:
4634.43.3 by Andrew Bennetts
Expand /~/ and /~user/ in 'bzr serve'.
330
            # Decorate the server's backing transport with a filter that can
331
            # 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.
332
            expand_userdirs = self._make_expand_userdirs_filter(transport)
4934.3.3 by Martin Pool
Rename Server.setUp to Server.start_server
333
            expand_userdirs.start_server()
4934.3.1 by Martin Pool
Rename Server.tearDown to .stop_server
334
            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
335
            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.
336
        self.transport = transport
337
338
    def _make_smart_server(self, host, port, inet):
4634.43.3 by Andrew Bennetts
Expand /~/ and /~user/ in 'bzr serve'.
339
        if inet:
340
            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.
341
                sys.stdin, sys.stdout, self.transport)
4634.43.3 by Andrew Bennetts
Expand /~/ and /~user/ in 'bzr serve'.
342
        else:
343
            if host is None:
344
                host = medium.BZR_DEFAULT_INTERFACE
345
            if port is None:
346
                port = medium.BZR_DEFAULT_PORT
5247.3.36 by Vincent Ladeuil
Start refactoring the smart server to control which thread it runs in.
347
            smart_server = SmartTCPServer(self.transport)
5247.3.48 by Vincent Ladeuil
Fixed as per lifeless suggestion.
348
            smart_server.start_server(host, port)
4634.43.3 by Andrew Bennetts
Expand /~/ and /~user/ in 'bzr serve'.
349
            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.
350
        self.smart_server = smart_server
351
352
    def _change_globals(self):
353
        from bzrlib import lockdir, ui
4634.43.3 by Andrew Bennetts
Expand /~/ and /~user/ in 'bzr serve'.
354
        # For the duration of this server, no UI output is permitted. note
355
        # that this may cause problems with blackbox tests. This should be
356
        # changed with care though, as we dont want to use bandwidth sending
357
        # progress over stderr to smart server clients!
358
        old_factory = ui.ui_factory
359
        old_lockdir_timeout = lockdir._DEFAULT_TIMEOUT_SECONDS
360
        def restore_default_ui_factory_and_lockdir_timeout():
361
            ui.ui_factory = old_factory
362
            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.
363
        self.cleanups.append(restore_default_ui_factory_and_lockdir_timeout)
4370.4.6 by Jelmer Vernooij
Move server protocol registry to bzrlib.transport.
364
        ui.ui_factory = ui.SilentUIFactory()
365
        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.
366
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.
367
    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.
368
        self._make_backing_transport(transport)
369
        self._make_smart_server(host, port, inet)
370
        self._change_globals()
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 tear_down(self):
4634.43.9 by Andrew Bennetts
Add BzrServerMaker.tearDown, and call it from tests.
373
        for cleanup in reversed(self.cleanups):
374
            cleanup()
375
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.
376
377
def serve_bzr(transport, host=None, port=None, inet=False):
4634.43.16 by Andrew Bennetts
Docstring improvements.
378
    """This is the default implementation of 'bzr serve'.
379
    
380
    It creates a TCP or pipe smart server on 'transport, and runs it.  The
381
    transport will be decorated with a chroot and pathfilter (using
382
    os.path.expanduser).
383
    """
4634.43.15 by Andrew Bennetts
Rename BzrServerMaker -> BzrServerFactory.
384
    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.
385
    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.
386
        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.
387
        bzr_server.smart_server.serve()
4370.4.6 by Jelmer Vernooij
Move server protocol registry to bzrlib.transport.
388
    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.
389
        bzr_server.tear_down()
4370.4.6 by Jelmer Vernooij
Move server protocol registry to bzrlib.transport.
390