~bzr-pqm/bzr/bzr.dev

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