~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
6379.6.7 by Jelmer Vernooij
Move importing from future until after doc string, otherwise the doc string will disappear.
17
"""Server for smart-server protocol."""
18
6379.6.3 by Jelmer Vernooij
Use absolute_import.
19
from __future__ import absolute_import
20
2400.1.7 by Andrew Bennetts
Merge from bzr.dev.
21
import errno
4634.43.3 by Andrew Bennetts
Expand /~/ and /~user/ in 'bzr serve'.
22
import os.path
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
23
import socket
3374.1.1 by Martin Pool
Set SO_REUSEADDR on server sockets (#164288)
24
import sys
6133.4.54 by John Arbash Meinel
Teach the SmartTCPServer to wait gracefully for the client threads to exit.
25
import time
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
26
import threading
27
5622.3.2 by Jelmer Vernooij
Add more lazily usable hook points.
28
from bzrlib.hooks import Hooks
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
29
from bzrlib import (
2634.1.1 by Robert Collins
(robertc) Reinstate the accidentally backed out external_url patch.
30
    errors,
2018.5.15 by Andrew Bennetts
Tidy some imports, and bugs introduced when adding server.py
31
    trace,
5609.9.1 by Martin
Blindly change all users of get_transport to address the function via the transport module
32
    transport as _mod_transport,
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
33
)
6138.3.4 by Jonathan Riddell
add gettext() to uses of trace.note()
34
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.
35
from bzrlib.lazy_import import lazy_import
36
lazy_import(globals(), """
6133.4.44 by John Arbash Meinel
Move the code into bzrlib.smart.signals.
37
from bzrlib.smart import (
38
    medium,
39
    signals,
40
    )
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.
41
from bzrlib.transport import (
42
    chroot,
43
    pathfilter,
44
    )
4634.43.3 by Andrew Bennetts
Expand /~/ and /~user/ in 'bzr serve'.
45
from bzrlib import (
6133.4.20 by John Arbash Meinel
Import 'config' from the right location.
46
    config,
4634.43.3 by Andrew Bennetts
Expand /~/ and /~user/ in 'bzr serve'.
47
    urlutils,
48
    )
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.
49
""")
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
50
51
52
class SmartTCPServer(object):
53
    """Listens on a TCP socket and accepts connections from smart clients.
2018.5.139 by Andrew Bennetts
Merge from bzr.dev, resolving conflicts.
54
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
55
    Each connection will be served by a SmartServerSocketStreamMedium running in
2018.5.139 by Andrew Bennetts
Merge from bzr.dev, resolving conflicts.
56
    a thread.
2400.1.7 by Andrew Bennetts
Merge from bzr.dev.
57
58
    hooks: An instance of SmartServerHooks.
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
59
    """
60
6133.4.34 by John Arbash Meinel
get the blackbox tests passing.
61
    # This is the timeout on the socket we use .accept() on. It is exposed here
62
    # so the test suite can set it faster. (It thread.interrupt_main() will not
63
    # fire a KeyboardInterrupt during socket.accept)
64
    _ACCEPT_TIMEOUT = 1.0
6133.4.54 by John Arbash Meinel
Teach the SmartTCPServer to wait gracefully for the client threads to exit.
65
    _SHUTDOWN_POLL_TIMEOUT = 1.0
66
    _LOG_WAITING_TIMEOUT = 10.0
67
68
    _timer = time.time
6133.4.34 by John Arbash Meinel
get the blackbox tests passing.
69
6133.4.19 by John Arbash Meinel
Make SmartTCPServer take a client_timeout parameter, which it passes down to the Medium.
70
    def __init__(self, backing_transport, root_client_path='/',
71
                 client_timeout=None):
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
72
        """Construct a new server.
73
74
        To actually start it running, call either start_background_thread or
75
        serve.
76
2692.1.14 by Andrew Bennetts
All WSGI tests passing, and manual testing works too.
77
        :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.
78
        :param root_client_path: The client path that will correspond to root
79
            of backing_transport.
6133.4.19 by John Arbash Meinel
Make SmartTCPServer take a client_timeout parameter, which it passes down to the Medium.
80
        :param client_timeout: See SmartServerSocketStreamMedium's timeout
81
            parameter.
5247.3.36 by Vincent Ladeuil
Start refactoring the smart server to control which thread it runs in.
82
        """
83
        self.backing_transport = backing_transport
84
        self.root_client_path = root_client_path
6133.4.19 by John Arbash Meinel
Make SmartTCPServer take a client_timeout parameter, which it passes down to the Medium.
85
        self._client_timeout = client_timeout
6133.4.50 by John Arbash Meinel
Track the active connections, add a polling function to notice when they are stopped.
86
        self._active_connections = []
6133.4.54 by John Arbash Meinel
Teach the SmartTCPServer to wait gracefully for the client threads to exit.
87
        # This is set to indicate we want to wait for clients to finish before
88
        # we disconnect.
89
        self._gracefully_stopping = False
5247.3.36 by Vincent Ladeuil
Start refactoring the smart server to control which thread it runs in.
90
5247.3.48 by Vincent Ladeuil
Fixed as per lifeless suggestion.
91
    def start_server(self, host, port):
5247.3.36 by Vincent Ladeuil
Start refactoring the smart server to control which thread it runs in.
92
        """Create the server listening socket.
93
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
94
        :param host: Name of the interface to listen on.
95
        :param port: TCP port to listen on, or 0 to allocate a transient port.
96
        """
2400.1.7 by Andrew Bennetts
Merge from bzr.dev.
97
        # let connections timeout so that we get a chance to terminate
98
        # Keep a reference to the exceptions we want to catch because the socket
99
        # module's globals get set to None during interpreter shutdown.
100
        from socket import timeout as socket_timeout
101
        from socket import error as socket_error
102
        self._socket_error = socket_error
103
        self._socket_timeout = socket_timeout
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
104
        addrs = socket.getaddrinfo(host, port, socket.AF_UNSPEC,
3711.2.2 by Jelmer Vernooij
Avoid using AI_ADDRCONFIG since it's not portable.
105
            socket.SOCK_STREAM, 0, socket.AI_PASSIVE)[0]
3665.4.1 by Jelmer Vernooij
Support IPv6 in the smart server.
106
107
        (family, socktype, proto, canonname, sockaddr) = addrs
108
109
        self._server_socket = socket.socket(family, socktype, proto)
3374.1.1 by Martin Pool
Set SO_REUSEADDR on server sockets (#164288)
110
        # SO_REUSERADDR has a different meaning on Windows
111
        if sys.platform != 'win32':
112
            self._server_socket.setsockopt(socket.SOL_SOCKET,
113
                socket.SO_REUSEADDR, 1)
3365.1.1 by Andrea Corbellini
Handle errors raised by socket.bind() (bug 200575)
114
        try:
3665.4.1 by Jelmer Vernooij
Support IPv6 in the smart server.
115
            self._server_socket.bind(sockaddr)
3365.1.1 by Andrea Corbellini
Handle errors raised by socket.bind() (bug 200575)
116
        except self._socket_error, message:
117
            raise errors.CannotBindAddress(host, port, message)
2400.1.7 by Andrew Bennetts
Merge from bzr.dev.
118
        self._sockname = self._server_socket.getsockname()
119
        self.port = self._sockname[1]
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
120
        self._server_socket.listen(1)
6133.4.34 by John Arbash Meinel
get the blackbox tests passing.
121
        self._server_socket.settimeout(self._ACCEPT_TIMEOUT)
6133.4.54 by John Arbash Meinel
Teach the SmartTCPServer to wait gracefully for the client threads to exit.
122
        # Once we start accept()ing connections, we set started.
2400.1.7 by Andrew Bennetts
Merge from bzr.dev.
123
        self._started = threading.Event()
6133.4.54 by John Arbash Meinel
Teach the SmartTCPServer to wait gracefully for the client threads to exit.
124
        # Once we stop accept()ing connections (and are closing the socket) we
125
        # set _stopped
2400.1.7 by Andrew Bennetts
Merge from bzr.dev.
126
        self._stopped = threading.Event()
6133.4.54 by John Arbash Meinel
Teach the SmartTCPServer to wait gracefully for the client threads to exit.
127
        # Once we have finished waiting for all clients, etc. We set
128
        # _fully_stopped
129
        self._fully_stopped = threading.Event()
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
130
5247.3.36 by Vincent Ladeuil
Start refactoring the smart server to control which thread it runs in.
131
    def _backing_urls(self):
2634.1.1 by Robert Collins
(robertc) Reinstate the accidentally backed out external_url patch.
132
        # There are three interesting urls:
133
        # The URL the server can be contacted on. (e.g. bzr://host/)
134
        # The URL that a commit done on the same machine as the server will
135
        # have within the servers space. (e.g. file:///home/user/source)
136
        # 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+
137
        # 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.
138
        # We need all three because:
139
        #  * other machines see the first
140
        #  * 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
141
        #    this server
2634.1.1 by Robert Collins
(robertc) Reinstate the accidentally backed out external_url patch.
142
        #  * commits the server does itself need to be mapped across to this
143
        #    server.
144
        # The latter two urls are different aliases to the servers url,
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
145
        # 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.
146
        # in the future.
5247.3.36 by Vincent Ladeuil
Start refactoring the smart server to control which thread it runs in.
147
        urls = [self.backing_transport.base]
2634.1.1 by Robert Collins
(robertc) Reinstate the accidentally backed out external_url patch.
148
        try:
5247.3.36 by Vincent Ladeuil
Start refactoring the smart server to control which thread it runs in.
149
            urls.append(self.backing_transport.external_url())
2634.1.1 by Robert Collins
(robertc) Reinstate the accidentally backed out external_url patch.
150
        except errors.InProcessTransport:
151
            pass
5247.3.36 by Vincent Ladeuil
Start refactoring the smart server to control which thread it runs in.
152
        return urls
153
154
    def run_server_started_hooks(self, backing_urls=None):
155
        if backing_urls is None:
156
            backing_urls = self._backing_urls()
2400.1.7 by Andrew Bennetts
Merge from bzr.dev.
157
        for hook in SmartTCPServer.hooks['server_started']:
2634.1.1 by Robert Collins
(robertc) Reinstate the accidentally backed out external_url patch.
158
            hook(backing_urls, self.get_url())
4544.1.2 by Andrew Bennetts
Add test that would catch the lack of ChrootServer in cmd_serve.
159
        for hook in SmartTCPServer.hooks['server_started_ex']:
4544.1.3 by Andrew Bennetts
Pass backing_urls to the new server_started_ex hook.
160
            hook(backing_urls, self)
5247.3.36 by Vincent Ladeuil
Start refactoring the smart server to control which thread it runs in.
161
162
    def run_server_stopped_hooks(self, backing_urls=None):
163
        if backing_urls is None:
164
            backing_urls = self._backing_urls()
165
        for hook in SmartTCPServer.hooks['server_stopped']:
166
            hook(backing_urls, self.get_url())
167
6133.4.43 by John Arbash Meinel
Initial implementation of SIGHUP for bzr serve. bug #795025.
168
    def _stop_gracefully(self):
6133.4.65 by John Arbash Meinel
Use gettext on the new trace.note/warning lines.
169
        trace.note(gettext('Requested to stop gracefully'))
6133.4.43 by John Arbash Meinel
Initial implementation of SIGHUP for bzr serve. bug #795025.
170
        self._should_terminate = True
6133.4.54 by John Arbash Meinel
Teach the SmartTCPServer to wait gracefully for the client threads to exit.
171
        self._gracefully_stopping = True
6133.4.55 by John Arbash Meinel
Now we've implemented the basic structure.
172
        for handler, _ in self._active_connections:
173
            handler._stop_gracefully()
6133.4.54 by John Arbash Meinel
Teach the SmartTCPServer to wait gracefully for the client threads to exit.
174
175
    def _wait_for_clients_to_disconnect(self):
176
        self._poll_active_connections()
177
        if not self._active_connections:
178
            return
6133.4.65 by John Arbash Meinel
Use gettext on the new trace.note/warning lines.
179
        trace.note(gettext('Waiting for %d client(s) to finish')
6133.4.54 by John Arbash Meinel
Teach the SmartTCPServer to wait gracefully for the client threads to exit.
180
                   % (len(self._active_connections),))
181
        t_next_log = self._timer() + self._LOG_WAITING_TIMEOUT
182
        while self._active_connections:
183
            now = self._timer()
184
            if now >= t_next_log:
6133.4.65 by John Arbash Meinel
Use gettext on the new trace.note/warning lines.
185
                trace.note(gettext('Still waiting for %d client(s) to finish')
6133.4.54 by John Arbash Meinel
Teach the SmartTCPServer to wait gracefully for the client threads to exit.
186
                           % (len(self._active_connections),))
187
                t_next_log = now + self._LOG_WAITING_TIMEOUT
188
            self._poll_active_connections(self._SHUTDOWN_POLL_TIMEOUT)
6133.4.43 by John Arbash Meinel
Initial implementation of SIGHUP for bzr serve. bug #795025.
189
5247.3.36 by Vincent Ladeuil
Start refactoring the smart server to control which thread it runs in.
190
    def serve(self, thread_name_suffix=''):
6133.4.47 by John Arbash Meinel
Testing that 'bzr serve' actually installs SIGHUP and responds to it showed some problems.
191
        # Note: There is a temptation to do
192
        #       signals.register_on_hangup(id(self), self._stop_gracefully)
193
        #       However, that creates a temporary object which is a bound
194
        #       method. signals._on_sighup is a WeakKeyDictionary so it
195
        #       immediately gets garbage collected, because nothing else
196
        #       references it. Instead, we need to keep a real reference to the
197
        #       bound method for the lifetime of the serve() function.
198
        stop_gracefully = self._stop_gracefully
199
        signals.register_on_hangup(id(self), stop_gracefully)
5247.3.36 by Vincent Ladeuil
Start refactoring the smart server to control which thread it runs in.
200
        self._should_terminate = False
201
        # for hooks we are letting code know that a server has started (and
202
        # later stopped).
203
        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.
204
        self._started.set()
2400.1.7 by Andrew Bennetts
Merge from bzr.dev.
205
        try:
206
            try:
207
                while not self._should_terminate:
208
                    try:
209
                        conn, client_addr = self._server_socket.accept()
210
                    except self._socket_timeout:
211
                        # just check if we're asked to stop
212
                        pass
213
                    except self._socket_error, e:
214
                        # if the socket is closed by stop_background_thread
6133.4.66 by John Arbash Meinel
Some cleanups
215
                        # we might get a EBADF here, or if we get a signal we
216
                        # can get EINTR, any other socket errors should get
217
                        # logged.
6133.4.43 by John Arbash Meinel
Initial implementation of SIGHUP for bzr serve. bug #795025.
218
                        if e.args[0] not in (errno.EBADF, errno.EINTR):
6133.4.65 by John Arbash Meinel
Use gettext on the new trace.note/warning lines.
219
                            trace.warning(gettext("listening socket error: %s")
220
                                          % (e,))
2400.1.7 by Andrew Bennetts
Merge from bzr.dev.
221
                    else:
5011.3.10 by Andrew Bennetts
Try a bit harder to stop a SmartTCPServer sooner when _should_terminate has been set.
222
                        if self._should_terminate:
6133.4.57 by John Arbash Meinel
If someone does manage to connect while we are shutting down, close the connection.
223
                            conn.close()
5011.3.10 by Andrew Bennetts
Try a bit harder to stop a SmartTCPServer sooner when _should_terminate has been set.
224
                            break
3245.4.28 by Andrew Bennetts
Remove another XXX, and include test ID in smart server thread names.
225
                        self.serve_conn(conn, thread_name_suffix)
6133.4.52 by John Arbash Meinel
Implement basic testing that the smart server tracks client connections,
226
                    # Cleanout any threads that have finished processing.
6133.4.53 by John Arbash Meinel
Refactor the code a bit. Now we can ensure that the active loop
227
                    self._poll_active_connections()
2400.1.7 by Andrew Bennetts
Merge from bzr.dev.
228
            except KeyboardInterrupt:
229
                # dont log when CTRL-C'd.
230
                raise
231
            except Exception, e:
4695.5.5 by Martin Pool
Unhandled smart-server exceptions are reported using generic report_exception
232
                trace.report_exception(sys.exc_info(), sys.stderr)
2400.1.7 by Andrew Bennetts
Merge from bzr.dev.
233
                raise
234
        finally:
235
            try:
236
                # ensure the server socket is closed.
237
                self._server_socket.close()
238
            except self._socket_error:
239
                # ignore errors on close
240
                pass
6133.4.55 by John Arbash Meinel
Now we've implemented the basic structure.
241
            self._stopped.set()
242
            signals.unregister_on_hangup(id(self))
5247.3.36 by Vincent Ladeuil
Start refactoring the smart server to control which thread it runs in.
243
            self.run_server_stopped_hooks()
6133.4.54 by John Arbash Meinel
Teach the SmartTCPServer to wait gracefully for the client threads to exit.
244
        if self._gracefully_stopping:
245
            self._wait_for_clients_to_disconnect()
246
        self._fully_stopped.set()
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
247
248
    def get_url(self):
249
        """Return the url of the server"""
5611.1.3 by Jelmer Vernooij
review comments from Vincent.
250
        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.
251
6133.4.24 by John Arbash Meinel
Change the name from _create_handler to _make_handler, to match similar code in the file.
252
    def _make_handler(self, conn):
6133.4.19 by John Arbash Meinel
Make SmartTCPServer take a client_timeout parameter, which it passes down to the Medium.
253
        return medium.SmartServerSocketStreamMedium(
254
            conn, self.backing_transport, self.root_client_path,
255
            timeout=self._client_timeout)
256
6133.4.50 by John Arbash Meinel
Track the active connections, add a polling function to notice when they are stopped.
257
    def _poll_active_connections(self, timeout=0.0):
258
        """Check to see if any active connections have finished.
259
260
        This will iterate through self._active_connections, and update any
261
        connections that are finished.
262
263
        :param timeout: The timeout to pass to thread.join(). By default, we
264
            set it to 0, so that we don't hang if threads are not done yet.
265
        :return: None
266
        """
267
        still_active = []
6133.4.55 by John Arbash Meinel
Now we've implemented the basic structure.
268
        for handler, thread in self._active_connections:
6133.4.50 by John Arbash Meinel
Track the active connections, add a polling function to notice when they are stopped.
269
            thread.join(timeout)
270
            if thread.isAlive():
6133.4.55 by John Arbash Meinel
Now we've implemented the basic structure.
271
                still_active.append((handler, thread))
6133.4.50 by John Arbash Meinel
Track the active connections, add a polling function to notice when they are stopped.
272
        self._active_connections = still_active
273
3245.4.28 by Andrew Bennetts
Remove another XXX, and include test ID in smart server thread names.
274
    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.
275
        # For WIN32, where the timeout value from the listening socket
4031.3.1 by Frank Aspell
Fixing various typos
276
        # propagates to the newly accepted socket.
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
277
        conn.setblocking(True)
278
        conn.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
3245.4.28 by Andrew Bennetts
Remove another XXX, and include test ID in smart server thread names.
279
        thread_name = 'smart-server-child' + thread_name_suffix
6133.4.24 by John Arbash Meinel
Change the name from _create_handler to _make_handler, to match similar code in the file.
280
        handler = self._make_handler(conn)
3245.4.28 by Andrew Bennetts
Remove another XXX, and include test ID in smart server thread names.
281
        connection_thread = threading.Thread(
282
            None, handler.serve, name=thread_name)
6133.4.55 by John Arbash Meinel
Now we've implemented the basic structure.
283
        self._active_connections.append((handler, connection_thread))
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
284
        connection_thread.setDaemon(True)
285
        connection_thread.start()
4731.2.8 by Vincent Ladeuil
Collect and shutdown clients for SmartTCPServer_for_testing.
286
        return connection_thread
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
287
3245.4.28 by Andrew Bennetts
Remove another XXX, and include test ID in smart server thread names.
288
    def start_background_thread(self, thread_name_suffix=''):
2400.1.7 by Andrew Bennetts
Merge from bzr.dev.
289
        self._started.clear()
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
290
        self._server_thread = threading.Thread(None,
3245.4.28 by Andrew Bennetts
Remove another XXX, and include test ID in smart server thread names.
291
                self.serve, args=(thread_name_suffix,),
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
292
                name='server-' + self.get_url())
293
        self._server_thread.setDaemon(True)
294
        self._server_thread.start()
2400.1.7 by Andrew Bennetts
Merge from bzr.dev.
295
        self._started.wait()
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
296
297
    def stop_background_thread(self):
2400.1.7 by Andrew Bennetts
Merge from bzr.dev.
298
        self._stopped.clear()
299
        # 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.
300
        self._should_terminate = True
2400.1.7 by Andrew Bennetts
Merge from bzr.dev.
301
        # close the socket - gives error to connections from here on in,
302
        # rather than a connection reset error to connections made during
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
303
        # the period between setting _should_terminate = True and
2400.1.7 by Andrew Bennetts
Merge from bzr.dev.
304
        # the current request completing/aborting. It may also break out the
305
        # main loop if it was currently in accept() (on some platforms).
306
        try:
307
            self._server_socket.close()
308
        except self._socket_error:
309
            # ignore errors on close
310
            pass
311
        if not self._stopped.isSet():
312
            # server has not stopped (though it may be stopping)
313
            # its likely in accept(), so give it a connection
314
            temp_socket = socket.socket()
315
            temp_socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
316
            if not temp_socket.connect_ex(self._sockname):
317
                # and close it immediately: we dont choose to send any requests.
318
                temp_socket.close()
319
        self._stopped.wait()
320
        self._server_thread.join()
321
322
323
class SmartServerHooks(Hooks):
324
    """Hooks for the smart server."""
325
5622.3.10 by Jelmer Vernooij
Don't require arguments to hooks.
326
    def __init__(self):
2400.1.7 by Andrew Bennetts
Merge from bzr.dev.
327
        """Create the default hooks.
328
329
        These are all empty initially, because by default nothing should get
330
        notified.
331
        """
5622.3.10 by Jelmer Vernooij
Don't require arguments to hooks.
332
        Hooks.__init__(self, "bzrlib.smart.server", "SmartTCPServer.hooks")
5622.3.2 by Jelmer Vernooij
Add more lazily usable hook points.
333
        self.add_hook('server_started',
4119.3.2 by Robert Collins
Migrate existing hooks over to the new HookPoint infrastructure.
334
            "Called by the bzr server when it starts serving a directory. "
335
            "server_started is called with (backing urls, public url), "
336
            "where backing_url is a list of URLs giving the "
337
            "server-specific directory locations, and public_url is the "
5622.3.2 by Jelmer Vernooij
Add more lazily usable hook points.
338
            "public URL for the directory being served.", (0, 16))
339
        self.add_hook('server_started_ex',
4544.1.2 by Andrew Bennetts
Add test that would catch the lack of ChrootServer in cmd_serve.
340
            "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.
341
            "server_started is called with (backing_urls, server_obj).",
5622.3.2 by Jelmer Vernooij
Add more lazily usable hook points.
342
            (1, 17))
343
        self.add_hook('server_stopped',
4119.3.2 by Robert Collins
Migrate existing hooks over to the new HookPoint infrastructure.
344
            "Called by the bzr server when it stops serving a directory. "
345
            "server_stopped is called with the same parameters as the "
5622.3.2 by Jelmer Vernooij
Add more lazily usable hook points.
346
            "server_started hook: (backing_urls, public_url).", (0, 16))
5909.2.1 by Jonathan Riddell
add hook for exceptions in the smart server
347
        self.add_hook('server_exception',
348
            "Called by the bzr server when an exception occurs. "
349
            "server_exception is called with the sys.exc_info() tuple "
350
            "return true for the hook if the exception has been handled, "
351
            "in which case the server will exit normally.", (2, 4))
2400.1.7 by Andrew Bennetts
Merge from bzr.dev.
352
5622.3.10 by Jelmer Vernooij
Don't require arguments to hooks.
353
SmartTCPServer.hooks = SmartServerHooks()
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
354
5909.2.5 by Jonathan Riddell
split test into two tests
355
4634.43.3 by Andrew Bennetts
Expand /~/ and /~user/ in 'bzr serve'.
356
def _local_path_for_transport(transport):
357
    """Return a local path for transport, if reasonably possible.
358
    
359
    This function works even if transport's url has a "readonly+" prefix,
360
    unlike local_path_from_url.
361
    
362
    This essentially recovers the --directory argument the user passed to "bzr
363
    serve" from the transport passed to serve_bzr.
364
    """
365
    try:
366
        base_url = transport.external_url()
367
    except (errors.InProcessTransport, NotImplementedError):
368
        return None
369
    else:
370
        # Strip readonly prefix
371
        if base_url.startswith('readonly+'):
372
            base_url = base_url[len('readonly+'):]
373
        try:
374
            return urlutils.local_path_from_url(base_url)
375
        except errors.InvalidURL:
376
            return None
377
4634.43.4 by Andrew Bennetts
Add docstring.
378
4634.43.15 by Andrew Bennetts
Rename BzrServerMaker -> BzrServerFactory.
379
class BzrServerFactory(object):
4634.43.16 by Andrew Bennetts
Docstring improvements.
380
    """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.
381
4634.43.7 by Andrew Bennetts
Add some unit tests for parts of userdir expansion.
382
    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.
383
        self.cleanups = []
384
        self.base_path = None
385
        self.backing_transport = None
386
        if userdir_expander is None:
387
            userdir_expander = os.path.expanduser
388
        self.userdir_expander = userdir_expander
4634.43.7 by Andrew Bennetts
Add some unit tests for parts of userdir expansion.
389
        if get_base_path is None:
390
            get_base_path = _local_path_for_transport
391
        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.
392
393
    def _expand_userdirs(self, path):
4634.43.4 by Andrew Bennetts
Add docstring.
394
        """Translate /~/ or /~user/ to e.g. /home/foo, using
4634.43.16 by Andrew Bennetts
Docstring improvements.
395
        self.userdir_expander (os.path.expanduser by default).
4634.43.4 by Andrew Bennetts
Add docstring.
396
397
        If the translated path would fall outside base_path, or the path does
398
        not start with ~, then no translation is applied.
399
400
        If the path is inside, it is adjusted to be relative to the base path.
401
402
        e.g. if base_path is /home, and the expanded path is /home/joe, then
403
        the translated path is joe.
404
        """
4634.43.3 by Andrew Bennetts
Expand /~/ and /~user/ in 'bzr serve'.
405
        result = path
406
        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.
407
            expanded = self.userdir_expander(path)
4634.43.3 by Andrew Bennetts
Expand /~/ and /~user/ in 'bzr serve'.
408
            if not expanded.endswith('/'):
409
                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.
410
            if expanded.startswith(self.base_path):
411
                result = expanded[len(self.base_path):]
4634.43.3 by Andrew Bennetts
Expand /~/ and /~user/ in 'bzr serve'.
412
        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.
413
414
    def _make_expand_userdirs_filter(self, transport):
415
        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.
416
417
    def _make_backing_transport(self, transport):
418
        """Chroot transport, and decorate with userdir expander."""
4634.43.7 by Andrew Bennetts
Add some unit tests for parts of userdir expansion.
419
        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.
420
        chroot_server = chroot.ChrootServer(transport)
4934.3.3 by Martin Pool
Rename Server.setUp to Server.start_server
421
        chroot_server.start_server()
4934.3.1 by Martin Pool
Rename Server.tearDown to .stop_server
422
        self.cleanups.append(chroot_server.stop_server)
6039.1.5 by Jelmer Vernooij
Add get_transport_from_url and get_transport_from_path functions.
423
        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.
424
        if self.base_path is not None:
4634.43.3 by Andrew Bennetts
Expand /~/ and /~user/ in 'bzr serve'.
425
            # Decorate the server's backing transport with a filter that can
426
            # 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.
427
            expand_userdirs = self._make_expand_userdirs_filter(transport)
4934.3.3 by Martin Pool
Rename Server.setUp to Server.start_server
428
            expand_userdirs.start_server()
4934.3.1 by Martin Pool
Rename Server.tearDown to .stop_server
429
            self.cleanups.append(expand_userdirs.stop_server)
6039.1.5 by Jelmer Vernooij
Add get_transport_from_url and get_transport_from_path functions.
430
            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.
431
        self.transport = transport
432
6133.4.61 by John Arbash Meinel
Expose infrastructure so that we can test the Inet portion of the server.
433
    def _get_stdin_stdout(self):
434
        return sys.stdin, sys.stdout
435
6133.4.29 by John Arbash Meinel
Expose --client-timeout to the command line, pass it through the layers.
436
    def _make_smart_server(self, host, port, inet, timeout):
437
        if timeout is None:
438
            c = config.GlobalStack()
6133.4.34 by John Arbash Meinel
get the blackbox tests passing.
439
            timeout = c.get('serve.client_timeout')
4634.43.3 by Andrew Bennetts
Expand /~/ and /~user/ in 'bzr serve'.
440
        if inet:
6133.4.61 by John Arbash Meinel
Expose infrastructure so that we can test the Inet portion of the server.
441
            stdin, stdout = self._get_stdin_stdout()
4634.43.3 by Andrew Bennetts
Expand /~/ and /~user/ in 'bzr serve'.
442
            smart_server = medium.SmartServerPipeStreamMedium(
6133.4.61 by John Arbash Meinel
Expose infrastructure so that we can test the Inet portion of the server.
443
                stdin, stdout, self.transport, timeout=timeout)
4634.43.3 by Andrew Bennetts
Expand /~/ and /~user/ in 'bzr serve'.
444
        else:
445
            if host is None:
446
                host = medium.BZR_DEFAULT_INTERFACE
447
            if port is None:
448
                port = medium.BZR_DEFAULT_PORT
6133.4.21 by John Arbash Meinel
I'm trying to make sure that 'bzr serve' supports the serve.client_timeout code.
449
            smart_server = SmartTCPServer(self.transport,
450
                                          client_timeout=timeout)
5247.3.48 by Vincent Ladeuil
Fixed as per lifeless suggestion.
451
            smart_server.start_server(host, port)
6138.3.4 by Jonathan Riddell
add gettext() to uses of trace.note()
452
            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.
453
        self.smart_server = smart_server
454
455
    def _change_globals(self):
456
        from bzrlib import lockdir, ui
4634.43.3 by Andrew Bennetts
Expand /~/ and /~user/ in 'bzr serve'.
457
        # For the duration of this server, no UI output is permitted. note
458
        # that this may cause problems with blackbox tests. This should be
459
        # changed with care though, as we dont want to use bandwidth sending
460
        # progress over stderr to smart server clients!
461
        old_factory = ui.ui_factory
462
        old_lockdir_timeout = lockdir._DEFAULT_TIMEOUT_SECONDS
463
        def restore_default_ui_factory_and_lockdir_timeout():
464
            ui.ui_factory = old_factory
465
            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.
466
        self.cleanups.append(restore_default_ui_factory_and_lockdir_timeout)
4370.4.6 by Jelmer Vernooij
Move server protocol registry to bzrlib.transport.
467
        ui.ui_factory = ui.SilentUIFactory()
468
        lockdir._DEFAULT_TIMEOUT_SECONDS = 0
6133.4.60 by John Arbash Meinel
serve_bzr wasn't properly cleaning up the new _on_sighup dict, etc.
469
        orig = signals.install_sighup_handler()
470
        def restore_signals():
471
            signals.restore_sighup_handler(orig)
472
        self.cleanups.append(restore_signals)
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.
473
6133.4.29 by John Arbash Meinel
Expose --client-timeout to the command line, pass it through the layers.
474
    def set_up(self, transport, host, port, inet, 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.
475
        self._make_backing_transport(transport)
6133.4.29 by John Arbash Meinel
Expose --client-timeout to the command line, pass it through the layers.
476
        self._make_smart_server(host, port, inet, 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.
477
        self._change_globals()
478
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.
479
    def tear_down(self):
4634.43.9 by Andrew Bennetts
Add BzrServerMaker.tearDown, and call it from tests.
480
        for cleanup in reversed(self.cleanups):
481
            cleanup()
482
6133.4.60 by John Arbash Meinel
serve_bzr wasn't properly cleaning up the new _on_sighup dict, etc.
483
6133.4.29 by John Arbash Meinel
Expose --client-timeout to the command line, pass it through the layers.
484
def serve_bzr(transport, host=None, port=None, inet=False, timeout=None):
4634.43.16 by Andrew Bennetts
Docstring improvements.
485
    """This is the default implementation of 'bzr serve'.
6133.4.15 by John Arbash Meinel
Start working on exposing timeout as a configuration item.
486
4634.43.16 by Andrew Bennetts
Docstring improvements.
487
    It creates a TCP or pipe smart server on 'transport, and runs it.  The
488
    transport will be decorated with a chroot and pathfilter (using
489
    os.path.expanduser).
490
    """
4634.43.15 by Andrew Bennetts
Rename BzrServerMaker -> BzrServerFactory.
491
    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.
492
    try:
6133.4.29 by John Arbash Meinel
Expose --client-timeout to the command line, pass it through the layers.
493
        bzr_server.set_up(transport, host, port, inet, 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.
494
        bzr_server.smart_server.serve()
5909.2.1 by Jonathan Riddell
add hook for exceptions in the smart server
495
    except:
496
        hook_caught_exception = False
497
        for hook in SmartTCPServer.hooks['server_exception']:
498
            hook_caught_exception = hook(sys.exc_info())
499
        if not hook_caught_exception:
500
            raise
4370.4.6 by Jelmer Vernooij
Move server protocol registry to bzrlib.transport.
501
    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.
502
        bzr_server.tear_down()