/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
3374.1.1 by Martin Pool
Set SO_REUSEADDR on server sockets (#164288)
1
# Copyright (C) 2006, 2007, 2008 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
4119.3.2 by Robert Collins
Migrate existing hooks over to the new HookPoint infrastructure.
25
from bzrlib.hooks import HookPoint, 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,
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
29
    transport,
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
    get_transport,
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
2692.1.14 by Andrew Bennetts
All WSGI tests passing, and manual testing works too.
54
    def __init__(self, backing_transport, host='127.0.0.1', port=0,
55
                 root_client_path='/'):
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
56
        """Construct a new server.
57
58
        To actually start it running, call either start_background_thread or
59
        serve.
60
2692.1.14 by Andrew Bennetts
All WSGI tests passing, and manual testing works too.
61
        :param backing_transport: The transport to serve.
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
62
        :param host: Name of the interface to listen on.
63
        :param port: TCP port to listen on, or 0 to allocate a transient port.
2692.1.14 by Andrew Bennetts
All WSGI tests passing, and manual testing works too.
64
        :param root_client_path: The client path that will correspond to root
65
            of backing_transport.
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
66
        """
2400.1.7 by Andrew Bennetts
Merge from bzr.dev.
67
        # let connections timeout so that we get a chance to terminate
68
        # Keep a reference to the exceptions we want to catch because the socket
69
        # module's globals get set to None during interpreter shutdown.
70
        from socket import timeout as socket_timeout
71
        from socket import error as socket_error
72
        self._socket_error = socket_error
73
        self._socket_timeout = socket_timeout
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
74
        addrs = socket.getaddrinfo(host, port, socket.AF_UNSPEC,
3711.2.2 by Jelmer Vernooij
Avoid using AI_ADDRCONFIG since it's not portable.
75
            socket.SOCK_STREAM, 0, socket.AI_PASSIVE)[0]
3665.4.1 by Jelmer Vernooij
Support IPv6 in the smart server.
76
77
        (family, socktype, proto, canonname, sockaddr) = addrs
78
79
        self._server_socket = socket.socket(family, socktype, proto)
3374.1.1 by Martin Pool
Set SO_REUSEADDR on server sockets (#164288)
80
        # SO_REUSERADDR has a different meaning on Windows
81
        if sys.platform != 'win32':
82
            self._server_socket.setsockopt(socket.SOL_SOCKET,
83
                socket.SO_REUSEADDR, 1)
3365.1.1 by Andrea Corbellini
Handle errors raised by socket.bind() (bug 200575)
84
        try:
3665.4.1 by Jelmer Vernooij
Support IPv6 in the smart server.
85
            self._server_socket.bind(sockaddr)
3365.1.1 by Andrea Corbellini
Handle errors raised by socket.bind() (bug 200575)
86
        except self._socket_error, message:
87
            raise errors.CannotBindAddress(host, port, message)
2400.1.7 by Andrew Bennetts
Merge from bzr.dev.
88
        self._sockname = self._server_socket.getsockname()
89
        self.port = self._sockname[1]
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
90
        self._server_socket.listen(1)
91
        self._server_socket.settimeout(1)
92
        self.backing_transport = backing_transport
2400.1.7 by Andrew Bennetts
Merge from bzr.dev.
93
        self._started = threading.Event()
94
        self._stopped = threading.Event()
2692.1.14 by Andrew Bennetts
All WSGI tests passing, and manual testing works too.
95
        self.root_client_path = root_client_path
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
96
3245.4.28 by Andrew Bennetts
Remove another XXX, and include test ID in smart server thread names.
97
    def serve(self, thread_name_suffix=''):
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
98
        self._should_terminate = False
2634.1.1 by Robert Collins
(robertc) Reinstate the accidentally backed out external_url patch.
99
        # for hooks we are letting code know that a server has started (and
100
        # later stopped).
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 -
106
        # the URL of the backing transport itself. (e.g. chroot+:///)
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.
116
        backing_urls = [self.backing_transport.base]
117
        try:
118
            backing_urls.append(self.backing_transport.external_url())
119
        except errors.InProcessTransport:
120
            pass
2400.1.7 by Andrew Bennetts
Merge from bzr.dev.
121
        for hook in SmartTCPServer.hooks['server_started']:
2634.1.1 by Robert Collins
(robertc) Reinstate the accidentally backed out external_url patch.
122
            hook(backing_urls, self.get_url())
4544.1.2 by Andrew Bennetts
Add test that would catch the lack of ChrootServer in cmd_serve.
123
        for hook in SmartTCPServer.hooks['server_started_ex']:
4544.1.3 by Andrew Bennetts
Pass backing_urls to the new server_started_ex hook.
124
            hook(backing_urls, self)
2400.1.7 by Andrew Bennetts
Merge from bzr.dev.
125
        self._started.set()
126
        try:
127
            try:
128
                while not self._should_terminate:
129
                    try:
130
                        conn, client_addr = self._server_socket.accept()
131
                    except self._socket_timeout:
132
                        # just check if we're asked to stop
133
                        pass
134
                    except self._socket_error, e:
135
                        # if the socket is closed by stop_background_thread
136
                        # we might get a EBADF here, any other socket errors
137
                        # should get logged.
138
                        if e.args[0] != errno.EBADF:
139
                            trace.warning("listening socket error: %s", e)
140
                    else:
3245.4.28 by Andrew Bennetts
Remove another XXX, and include test ID in smart server thread names.
141
                        self.serve_conn(conn, thread_name_suffix)
2400.1.7 by Andrew Bennetts
Merge from bzr.dev.
142
            except KeyboardInterrupt:
143
                # dont log when CTRL-C'd.
144
                raise
145
            except Exception, e:
4695.5.5 by Martin Pool
Unhandled smart-server exceptions are reported using generic report_exception
146
                trace.report_exception(sys.exc_info(), sys.stderr)
2400.1.7 by Andrew Bennetts
Merge from bzr.dev.
147
                raise
148
        finally:
149
            self._stopped.set()
150
            try:
151
                # ensure the server socket is closed.
152
                self._server_socket.close()
153
            except self._socket_error:
154
                # ignore errors on close
155
                pass
156
            for hook in SmartTCPServer.hooks['server_stopped']:
2634.1.1 by Robert Collins
(robertc) Reinstate the accidentally backed out external_url patch.
157
                hook(backing_urls, self.get_url())
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
158
159
    def get_url(self):
160
        """Return the url of the server"""
2400.1.7 by Andrew Bennetts
Merge from bzr.dev.
161
        return "bzr://%s:%d/" % self._sockname
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
162
3245.4.28 by Andrew Bennetts
Remove another XXX, and include test ID in smart server thread names.
163
    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.
164
        # For WIN32, where the timeout value from the listening socket
4031.3.1 by Frank Aspell
Fixing various typos
165
        # propagates to the newly accepted socket.
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
166
        conn.setblocking(True)
167
        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.
168
        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.
169
            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.
170
        thread_name = 'smart-server-child' + thread_name_suffix
171
        connection_thread = threading.Thread(
172
            None, handler.serve, name=thread_name)
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
173
        connection_thread.setDaemon(True)
174
        connection_thread.start()
4731.2.8 by Vincent Ladeuil
Collect and shutdown clients for SmartTCPServer_for_testing.
175
        return connection_thread
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
176
3245.4.28 by Andrew Bennetts
Remove another XXX, and include test ID in smart server thread names.
177
    def start_background_thread(self, thread_name_suffix=''):
2400.1.7 by Andrew Bennetts
Merge from bzr.dev.
178
        self._started.clear()
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
179
        self._server_thread = threading.Thread(None,
3245.4.28 by Andrew Bennetts
Remove another XXX, and include test ID in smart server thread names.
180
                self.serve, args=(thread_name_suffix,),
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
181
                name='server-' + self.get_url())
182
        self._server_thread.setDaemon(True)
183
        self._server_thread.start()
2400.1.7 by Andrew Bennetts
Merge from bzr.dev.
184
        self._started.wait()
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
185
186
    def stop_background_thread(self):
2400.1.7 by Andrew Bennetts
Merge from bzr.dev.
187
        self._stopped.clear()
188
        # 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.
189
        self._should_terminate = True
2400.1.7 by Andrew Bennetts
Merge from bzr.dev.
190
        # close the socket - gives error to connections from here on in,
191
        # rather than a connection reset error to connections made during
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
192
        # the period between setting _should_terminate = True and
2400.1.7 by Andrew Bennetts
Merge from bzr.dev.
193
        # the current request completing/aborting. It may also break out the
194
        # main loop if it was currently in accept() (on some platforms).
195
        try:
196
            self._server_socket.close()
197
        except self._socket_error:
198
            # ignore errors on close
199
            pass
200
        if not self._stopped.isSet():
201
            # server has not stopped (though it may be stopping)
202
            # its likely in accept(), so give it a connection
203
            temp_socket = socket.socket()
204
            temp_socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
205
            if not temp_socket.connect_ex(self._sockname):
206
                # and close it immediately: we dont choose to send any requests.
207
                temp_socket.close()
208
        self._stopped.wait()
209
        self._server_thread.join()
210
211
212
class SmartServerHooks(Hooks):
213
    """Hooks for the smart server."""
214
215
    def __init__(self):
216
        """Create the default hooks.
217
218
        These are all empty initially, because by default nothing should get
219
        notified.
220
        """
221
        Hooks.__init__(self)
4119.3.2 by Robert Collins
Migrate existing hooks over to the new HookPoint infrastructure.
222
        self.create_hook(HookPoint('server_started',
223
            "Called by the bzr server when it starts serving a directory. "
224
            "server_started is called with (backing urls, public url), "
225
            "where backing_url is a list of URLs giving the "
226
            "server-specific directory locations, and public_url is the "
227
            "public URL for the directory being served.", (0, 16), None))
4544.1.2 by Andrew Bennetts
Add test that would catch the lack of ChrootServer in cmd_serve.
228
        self.create_hook(HookPoint('server_started_ex',
229
            "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.
230
            "server_started is called with (backing_urls, server_obj).",
231
            (1, 17), None))
4119.3.2 by Robert Collins
Migrate existing hooks over to the new HookPoint infrastructure.
232
        self.create_hook(HookPoint('server_stopped',
233
            "Called by the bzr server when it stops serving a directory. "
234
            "server_stopped is called with the same parameters as the "
235
            "server_started hook: (backing_urls, public_url).", (0, 16), None))
2400.1.7 by Andrew Bennetts
Merge from bzr.dev.
236
237
SmartTCPServer.hooks = SmartServerHooks()
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
238
2400.1.2 by Andrew Bennetts
Move SmartTCPServer classes into bzrlib/smart/server.py
239
240
class SmartTCPServer_for_testing(SmartTCPServer):
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
241
    """Server suitable for use by transport tests.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
242
2400.1.2 by Andrew Bennetts
Move SmartTCPServer classes into bzrlib/smart/server.py
243
    This server is backed by the process's cwd.
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
244
    """
245
3245.4.28 by Andrew Bennetts
Remove another XXX, and include test ID in smart server thread names.
246
    def __init__(self, thread_name_suffix=''):
2018.5.42 by Robert Collins
Various hopefully improvements, but wsgi is broken, handing over to spiv :).
247
        SmartTCPServer.__init__(self, None)
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.
248
        self.client_path_extra = None
3245.4.28 by Andrew Bennetts
Remove another XXX, and include test ID in smart server thread names.
249
        self.thread_name_suffix = thread_name_suffix
4731.2.8 by Vincent Ladeuil
Collect and shutdown clients for SmartTCPServer_for_testing.
250
        # We collect the sockets/threads used by the clients so we can
251
        # close/join them when shutting down
252
        self.clients = []
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
253
2400.1.2 by Andrew Bennetts
Move SmartTCPServer classes into bzrlib/smart/server.py
254
    def get_backing_transport(self, backing_transport_server):
255
        """Get a backing transport from a server we are decorating."""
2018.5.104 by Andrew Bennetts
Completely rework chrooted transports.
256
        return transport.get_transport(backing_transport_server.get_url())
2400.1.2 by Andrew Bennetts
Move SmartTCPServer classes into bzrlib/smart/server.py
257
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.
258
    def setUp(self, backing_transport_server=None,
259
              client_path_extra='/extra/'):
260
        """Set up server for testing.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
261
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.
262
        :param backing_transport_server: backing server to use.  If not
263
            specified, a LocalURLServer at the current working directory will
264
            be used.
265
        :param client_path_extra: a path segment starting with '/' to append to
266
            the root URL for this server.  For instance, a value of '/foo/bar/'
267
            will mean the root of the backing transport will be published at a
268
            URL like `bzr://127.0.0.1:nnnn/foo/bar/`, rather than
269
            `bzr://127.0.0.1:nnnn/`.  Default value is `extra`, so that tests
270
            by default will fail unless they do the necessary path translation.
271
        """
3376.2.4 by Martin Pool
Remove every assert statement from bzrlib!
272
        if not client_path_extra.startswith('/'):
273
            raise ValueError(client_path_extra)
2018.5.104 by Andrew Bennetts
Completely rework chrooted transports.
274
        from bzrlib.transport.chroot import ChrootServer
2018.5.47 by Andrew Bennetts
Make SmartTCPServer_for_Testing.setUp's backing_transport_server argument optional.
275
        if backing_transport_server is None:
276
            from bzrlib.transport.local import LocalURLServer
277
            backing_transport_server = LocalURLServer()
2018.5.104 by Andrew Bennetts
Completely rework chrooted transports.
278
        self.chroot_server = ChrootServer(
279
            self.get_backing_transport(backing_transport_server))
280
        self.chroot_server.setUp()
281
        self.backing_transport = transport.get_transport(
282
            self.chroot_server.get_url())
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.
283
        self.root_client_path = self.client_path_extra = client_path_extra
3245.4.28 by Andrew Bennetts
Remove another XXX, and include test ID in smart server thread names.
284
        self.start_background_thread(self.thread_name_suffix)
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
285
4731.2.8 by Vincent Ladeuil
Collect and shutdown clients for SmartTCPServer_for_testing.
286
    def serve_conn(self, conn, thread_name_suffix):
287
        conn_thread = super(SmartTCPServer_for_testing, self).serve_conn(
288
            conn, thread_name_suffix)
289
        self.clients.append((conn, conn_thread))
290
        return conn_thread
291
292
    def shutdown_client(self, client_socket):
293
        """Properly shutdown a client socket.
294
295
        Under some circumstances (as in bug #383920), we need to force the
296
        shutdown as python delays it until gc occur otherwise and the client
297
        may hang.
298
299
        This should be called only when no other thread is trying to use the
300
        socket.
301
        """
302
        try:
303
            # The request process has been completed, the thread is about to
304
            # die, let's shutdown the socket if we can.
305
            client_socket.shutdown(socket.SHUT_RDWR)
306
        except (socket.error, select.error), e:
307
            if e[0] in (errno.EBADF, errno.ENOTCONN):
308
                # Right, the socket is already down
309
                pass
310
            else:
311
                raise
312
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
313
    def tearDown(self):
314
        self.stop_background_thread()
4731.2.8 by Vincent Ladeuil
Collect and shutdown clients for SmartTCPServer_for_testing.
315
        # Let's close all our pending clients too
316
        for sock, thread in self.clients:
317
            self.shutdown_client(sock)
318
            thread.join()
319
            del thread
320
        self.clients = []
2018.5.104 by Andrew Bennetts
Completely rework chrooted transports.
321
        self.chroot_server.tearDown()
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
322
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.
323
    def get_url(self):
324
        url = super(SmartTCPServer_for_testing, self).get_url()
325
        return url[:-1] + self.client_path_extra
326
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
327
    def get_bogus_url(self):
328
        """Return a URL which will fail to connect"""
329
        return 'bzr://127.0.0.1:1/'
330
331
2018.5.42 by Robert Collins
Various hopefully improvements, but wsgi is broken, handing over to spiv :).
332
class ReadonlySmartTCPServer_for_testing(SmartTCPServer_for_testing):
333
    """Get a readonly server for testing."""
334
335
    def get_backing_transport(self, backing_transport_server):
336
        """Get a backing transport from a server we are decorating."""
2018.5.104 by Andrew Bennetts
Completely rework chrooted transports.
337
        url = 'readonly+' + backing_transport_server.get_url()
2018.5.42 by Robert Collins
Various hopefully improvements, but wsgi is broken, handing over to spiv :).
338
        return transport.get_transport(url)
3453.5.1 by Andrew Bennetts
Add {bzrdir,repository,branch}_implementations tests for Remote objects using protocol v2 and pre-1.6 RPCs.
339
340
341
class SmartTCPServer_for_testing_v2_only(SmartTCPServer_for_testing):
342
    """A variation of SmartTCPServer_for_testing that limits the client to
343
    using RPCs in protocol v2 (i.e. bzr <= 1.5).
344
    """
345
346
    def get_url(self):
347
        url = super(SmartTCPServer_for_testing_v2_only, self).get_url()
348
        url = 'bzr-v2://' + url[len('bzr://'):]
349
        return url
350
351
352
class ReadonlySmartTCPServer_for_testing_v2_only(SmartTCPServer_for_testing_v2_only):
353
    """Get a readonly server for testing."""
354
355
    def get_backing_transport(self, backing_transport_server):
356
        """Get a backing transport from a server we are decorating."""
357
        url = 'readonly+' + backing_transport_server.get_url()
358
        return transport.get_transport(url)
359
360
4634.43.3 by Andrew Bennetts
Expand /~/ and /~user/ in 'bzr serve'.
361
def _local_path_for_transport(transport):
362
    """Return a local path for transport, if reasonably possible.
363
    
364
    This function works even if transport's url has a "readonly+" prefix,
365
    unlike local_path_from_url.
366
    
367
    This essentially recovers the --directory argument the user passed to "bzr
368
    serve" from the transport passed to serve_bzr.
369
    """
370
    try:
371
        base_url = transport.external_url()
372
    except (errors.InProcessTransport, NotImplementedError):
373
        return None
374
    else:
375
        # Strip readonly prefix
376
        if base_url.startswith('readonly+'):
377
            base_url = base_url[len('readonly+'):]
378
        try:
379
            return urlutils.local_path_from_url(base_url)
380
        except errors.InvalidURL:
381
            return None
382
4634.43.4 by Andrew Bennetts
Add docstring.
383
4634.43.15 by Andrew Bennetts
Rename BzrServerMaker -> BzrServerFactory.
384
class BzrServerFactory(object):
4634.43.16 by Andrew Bennetts
Docstring improvements.
385
    """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.
386
4634.43.7 by Andrew Bennetts
Add some unit tests for parts of userdir expansion.
387
    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.
388
        self.cleanups = []
389
        self.base_path = None
390
        self.backing_transport = None
391
        if userdir_expander is None:
392
            userdir_expander = os.path.expanduser
393
        self.userdir_expander = userdir_expander
4634.43.7 by Andrew Bennetts
Add some unit tests for parts of userdir expansion.
394
        if get_base_path is None:
395
            get_base_path = _local_path_for_transport
396
        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.
397
398
    def _expand_userdirs(self, path):
4634.43.4 by Andrew Bennetts
Add docstring.
399
        """Translate /~/ or /~user/ to e.g. /home/foo, using
4634.43.16 by Andrew Bennetts
Docstring improvements.
400
        self.userdir_expander (os.path.expanduser by default).
4634.43.4 by Andrew Bennetts
Add docstring.
401
402
        If the translated path would fall outside base_path, or the path does
403
        not start with ~, then no translation is applied.
404
405
        If the path is inside, it is adjusted to be relative to the base path.
406
407
        e.g. if base_path is /home, and the expanded path is /home/joe, then
408
        the translated path is joe.
409
        """
4634.43.3 by Andrew Bennetts
Expand /~/ and /~user/ in 'bzr serve'.
410
        result = path
411
        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.
412
            expanded = self.userdir_expander(path)
4634.43.3 by Andrew Bennetts
Expand /~/ and /~user/ in 'bzr serve'.
413
            if not expanded.endswith('/'):
414
                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.
415
            if expanded.startswith(self.base_path):
416
                result = expanded[len(self.base_path):]
4634.43.3 by Andrew Bennetts
Expand /~/ and /~user/ in 'bzr serve'.
417
        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.
418
419
    def _make_expand_userdirs_filter(self, transport):
420
        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.
421
422
    def _make_backing_transport(self, transport):
423
        """Chroot transport, and decorate with userdir expander."""
4634.43.7 by Andrew Bennetts
Add some unit tests for parts of userdir expansion.
424
        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.
425
        chroot_server = chroot.ChrootServer(transport)
4634.43.3 by Andrew Bennetts
Expand /~/ and /~user/ in 'bzr serve'.
426
        chroot_server.setUp()
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.
427
        self.cleanups.append(chroot_server.tearDown)
4634.43.3 by Andrew Bennetts
Expand /~/ and /~user/ in 'bzr serve'.
428
        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.
429
        if self.base_path is not None:
4634.43.3 by Andrew Bennetts
Expand /~/ and /~user/ in 'bzr serve'.
430
            # Decorate the server's backing transport with a filter that can
431
            # 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.
432
            expand_userdirs = self._make_expand_userdirs_filter(transport)
4634.43.3 by Andrew Bennetts
Expand /~/ and /~user/ in 'bzr serve'.
433
            expand_userdirs.setUp()
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.
434
            self.cleanups.append(expand_userdirs.tearDown)
4634.43.3 by Andrew Bennetts
Expand /~/ and /~user/ in 'bzr serve'.
435
            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.
436
        self.transport = transport
437
438
    def _make_smart_server(self, host, port, inet):
4634.43.3 by Andrew Bennetts
Expand /~/ and /~user/ in 'bzr serve'.
439
        if inet:
440
            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.
441
                sys.stdin, sys.stdout, self.transport)
4634.43.3 by Andrew Bennetts
Expand /~/ and /~user/ in 'bzr serve'.
442
        else:
443
            if host is None:
444
                host = medium.BZR_DEFAULT_INTERFACE
445
            if port is None:
446
                port = medium.BZR_DEFAULT_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.
447
            smart_server = SmartTCPServer(self.transport, host=host, port=port)
4634.43.3 by Andrew Bennetts
Expand /~/ and /~user/ in 'bzr serve'.
448
            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.
449
        self.smart_server = smart_server
450
451
    def _change_globals(self):
452
        from bzrlib import lockdir, ui
4634.43.3 by Andrew Bennetts
Expand /~/ and /~user/ in 'bzr serve'.
453
        # For the duration of this server, no UI output is permitted. note
454
        # that this may cause problems with blackbox tests. This should be
455
        # changed with care though, as we dont want to use bandwidth sending
456
        # progress over stderr to smart server clients!
457
        old_factory = ui.ui_factory
458
        old_lockdir_timeout = lockdir._DEFAULT_TIMEOUT_SECONDS
459
        def restore_default_ui_factory_and_lockdir_timeout():
460
            ui.ui_factory = old_factory
461
            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.
462
        self.cleanups.append(restore_default_ui_factory_and_lockdir_timeout)
4370.4.6 by Jelmer Vernooij
Move server protocol registry to bzrlib.transport.
463
        ui.ui_factory = ui.SilentUIFactory()
464
        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.
465
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.
466
    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.
467
        self._make_backing_transport(transport)
468
        self._make_smart_server(host, port, inet)
469
        self._change_globals()
470
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.
471
    def tear_down(self):
4634.43.9 by Andrew Bennetts
Add BzrServerMaker.tearDown, and call it from tests.
472
        for cleanup in reversed(self.cleanups):
473
            cleanup()
474
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.
475
476
def serve_bzr(transport, host=None, port=None, inet=False):
4634.43.16 by Andrew Bennetts
Docstring improvements.
477
    """This is the default implementation of 'bzr serve'.
478
    
479
    It creates a TCP or pipe smart server on 'transport, and runs it.  The
480
    transport will be decorated with a chroot and pathfilter (using
481
    os.path.expanduser).
482
    """
4634.43.15 by Andrew Bennetts
Rename BzrServerMaker -> BzrServerFactory.
483
    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.
484
    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.
485
        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.
486
        bzr_server.smart_server.serve()
4370.4.6 by Jelmer Vernooij
Move server protocol registry to bzrlib.transport.
487
    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.
488
        bzr_server.tear_down()
4370.4.6 by Jelmer Vernooij
Move server protocol registry to bzrlib.transport.
489