~bzr-pqm/bzr/bzr.dev

3224.5.8 by Andrew Bennetts
Fix failing tests.
1
# Copyright (C) 2006, 2007 Canonical Ltd
2018.5.4 by Andrew Bennetts
Split smart server VFS logic out into a new file, and start using the command pattern in the SmartServerRequestHandler.
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.4 by Andrew Bennetts
Split smart server VFS logic out into a new file, and start using the command pattern in the SmartServerRequestHandler.
16
4160.2.14 by Andrew Bennetts
Merge bzr.dev, improve NEWS entry and bzrlib.smart.request docs.
17
"""Infrastructure for server-side request handlers.
18
19
Interesting module attributes:
20
    * The request_handlers registry maps verb names to SmartServerRequest
21
      classes.
22
    * The jail_info threading.local() object is used to prevent accidental
23
      opening of BzrDirs outside of the backing transport, or any other
24
      transports placed in jail_info.transports.  The jail_info is reset on
25
      every call into a request handler (which can happen an arbitrary number
26
      of times during a request).
3195.3.4 by Andrew Bennetts
Make the general request handler dispatch version 3 events to the specific request handler (i.e. to the SmartServerRequest instance).
27
"""
2018.5.19 by Andrew Bennetts
Add docstrings to all the new modules, and a few other places.
28
4160.2.14 by Andrew Bennetts
Merge bzr.dev, improve NEWS entry and bzrlib.smart.request docs.
29
# XXX: The class names are a little confusing: the protocol will instantiate a
30
# SmartServerRequestHandler, whose dispatch_command method creates an instance
31
# of a SmartServerRequest subclass.
32
33
2018.5.6 by Andrew Bennetts
Tidy ups, and turn do_hello and do_get_bundle into command objects.
34
import tempfile
4160.2.2 by Andrew Bennetts
Add setup_jail and teardown_jail to SmartServerRequest.
35
import threading
2018.5.6 by Andrew Bennetts
Tidy ups, and turn do_hello and do_get_bundle into command objects.
36
2402.1.2 by Andrew Bennetts
Deal with review comments.
37
from bzrlib import (
38
    bzrdir,
39
    errors,
40
    registry,
41
    revision,
4064.1.2 by Andrew Bennetts
Refactor server-side error translation, improve tests.
42
    trace,
2692.1.1 by Andrew Bennetts
Add translate_client_path method to SmartServerRequest.
43
    urlutils,
2402.1.2 by Andrew Bennetts
Deal with review comments.
44
    )
3224.5.6 by Andrew Bennetts
Don't import bzrlib.bundle in bzrlib.smart.request until it's needed.
45
from bzrlib.lazy_import import lazy_import
46
lazy_import(globals(), """
47
from bzrlib.bundle import serializer
48
""")
2018.5.6 by Andrew Bennetts
Tidy ups, and turn do_hello and do_get_bundle into command objects.
49
2018.5.4 by Andrew Bennetts
Split smart server VFS logic out into a new file, and start using the command pattern in the SmartServerRequestHandler.
50
4160.2.2 by Andrew Bennetts
Add setup_jail and teardown_jail to SmartServerRequest.
51
jail_info = threading.local()
52
jail_info.transports = None
53
54
4160.2.4 by Andrew Bennetts
Use BzrDir pre_open hook to jail request code from accessing transports other than the backing transport.
55
def _install_hook():
56
    bzrdir.BzrDir.hooks.install_named_hook(
57
        'pre_open', _pre_open_hook, 'checking server jail')
58
59
60
def _pre_open_hook(transport):
61
    allowed_transports = jail_info.transports
62
    if allowed_transports is None:
63
        return
64
    abspath = transport.base
65
    for allowed_transport in allowed_transports:
66
        try:
67
            allowed_transport.relpath(abspath)
68
        except errors.PathNotChild:
69
            continue
70
        else:
71
            return
72
    raise errors.BzrError('jail break: %r' % (abspath,))
73
74
75
_install_hook()
76
77
2018.5.4 by Andrew Bennetts
Split smart server VFS logic out into a new file, and start using the command pattern in the SmartServerRequestHandler.
78
class SmartServerRequest(object):
2692.1.10 by Andrew Bennetts
More docstring polish
79
    """Base class for request handlers.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
80
2692.1.10 by Andrew Bennetts
More docstring polish
81
    To define a new request, subclass this class and override the `do` method
82
    (and if appropriate, `do_body` as well).  Request implementors should take
83
    care to call `translate_client_path` and `transport_from_client_path` as
84
    appropriate when dealing with paths received from the client.
85
    """
3195.3.2 by Andrew Bennetts
Checkpoint first rough cut of SmartServerRequestProtocolThree, this implementation reuses the _StatefulDecoder class. Plus some attempts to start tidying the smart protocol tests.
86
    # XXX: rename this class to BaseSmartServerRequestHandler ?  A request
87
    # *handler* is a different concept to the request.
2018.5.4 by Andrew Bennetts
Split smart server VFS logic out into a new file, and start using the command pattern in the SmartServerRequestHandler.
88
2692.1.1 by Andrew Bennetts
Add translate_client_path method to SmartServerRequest.
89
    def __init__(self, backing_transport, root_client_path='/'):
2402.1.2 by Andrew Bennetts
Deal with review comments.
90
        """Constructor.
91
92
        :param backing_transport: the base transport to be used when performing
93
            this request.
2692.1.1 by Andrew Bennetts
Add translate_client_path method to SmartServerRequest.
94
        :param root_client_path: the client path that maps to the root of
2692.1.9 by Andrew Bennetts
Docstrings for translate_client_path and transport_from_client_path.
95
            backing_transport.  This is used to interpret relpaths received
96
            from the client.  Clients will not be able to refer to paths above
2692.1.16 by Andrew Bennetts
Improve comments.
97
            this root.  If root_client_path is None, then no translation will
98
            be performed on client paths.  Default is '/'.
2402.1.2 by Andrew Bennetts
Deal with review comments.
99
        """
2018.5.4 by Andrew Bennetts
Split smart server VFS logic out into a new file, and start using the command pattern in the SmartServerRequestHandler.
100
        self._backing_transport = backing_transport
2692.1.14 by Andrew Bennetts
All WSGI tests passing, and manual testing works too.
101
        if root_client_path is not None:
102
            if not root_client_path.startswith('/'):
103
                root_client_path = '/' + root_client_path
104
            if not root_client_path.endswith('/'):
105
                root_client_path += '/'
2692.1.1 by Andrew Bennetts
Add translate_client_path method to SmartServerRequest.
106
        self._root_client_path = root_client_path
3842.3.19 by Andrew Bennetts
Tweaks suggested by review.
107
        self._body_chunks = []
2018.5.5 by Andrew Bennetts
Pass body_bytes directly to SmartServerRequest.do_body
108
2018.5.24 by Andrew Bennetts
Setting NO_SMART_VFS in environment will disable VFS methods in the smart server. (Robert Collins, John Arbash Meinel, Andrew Bennetts)
109
    def _check_enabled(self):
110
        """Raises DisabledMethod if this method is disabled."""
111
        pass
112
2018.5.19 by Andrew Bennetts
Add docstrings to all the new modules, and a few other places.
113
    def do(self, *args):
2018.5.24 by Andrew Bennetts
Setting NO_SMART_VFS in environment will disable VFS methods in the smart server. (Robert Collins, John Arbash Meinel, Andrew Bennetts)
114
        """Mandatory extension point for SmartServerRequest subclasses.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
115
2018.5.24 by Andrew Bennetts
Setting NO_SMART_VFS in environment will disable VFS methods in the smart server. (Robert Collins, John Arbash Meinel, Andrew Bennetts)
116
        Subclasses must implement this.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
117
2018.5.19 by Andrew Bennetts
Add docstrings to all the new modules, and a few other places.
118
        This should return a SmartServerResponse if this command expects to
119
        receive no body.
120
        """
2018.5.5 by Andrew Bennetts
Pass body_bytes directly to SmartServerRequest.do_body
121
        raise NotImplementedError(self.do)
122
2018.5.24 by Andrew Bennetts
Setting NO_SMART_VFS in environment will disable VFS methods in the smart server. (Robert Collins, John Arbash Meinel, Andrew Bennetts)
123
    def execute(self, *args):
124
        """Public entry point to execute this request.
125
126
        It will return a SmartServerResponse if the command does not expect a
127
        body.
128
129
        :param *args: the arguments of the request.
130
        """
131
        self._check_enabled()
132
        return self.do(*args)
133
2018.5.5 by Andrew Bennetts
Pass body_bytes directly to SmartServerRequest.do_body
134
    def do_body(self, body_bytes):
2018.5.19 by Andrew Bennetts
Add docstrings to all the new modules, and a few other places.
135
        """Called if the client sends a body with the request.
3184.1.10 by Robert Collins
Change the smart server verb for Repository.stream_revisions_chunked to use SearchResults as the request mechanism for downloads.
136
137
        The do() method is still called, and must have returned None.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
138
2018.5.19 by Andrew Bennetts
Add docstrings to all the new modules, and a few other places.
139
        Must return a SmartServerResponse.
140
        """
3990.3.2 by Andrew Bennetts
Fix the do_body NotImplementedError log spam.
141
        if body_bytes != '':
3990.3.3 by Andrew Bennetts
Add a test that unexpected request bodies trigger a SmartProtocolError from request implementations.
142
            raise errors.SmartProtocolError('Request does not expect a body')
2018.5.4 by Andrew Bennetts
Split smart server VFS logic out into a new file, and start using the command pattern in the SmartServerRequestHandler.
143
3195.3.4 by Andrew Bennetts
Make the general request handler dispatch version 3 events to the specific request handler (i.e. to the SmartServerRequest instance).
144
    def do_chunk(self, chunk_bytes):
145
        """Called with each body chunk if the request has a streamed body.
146
147
        The do() method is still called, and must have returned None.
148
        """
3842.3.19 by Andrew Bennetts
Tweaks suggested by review.
149
        self._body_chunks.append(chunk_bytes)
3195.3.4 by Andrew Bennetts
Make the general request handler dispatch version 3 events to the specific request handler (i.e. to the SmartServerRequest instance).
150
151
    def do_end(self):
152
        """Called when the end of the request has been received."""
3842.3.19 by Andrew Bennetts
Tweaks suggested by review.
153
        body_bytes = ''.join(self._body_chunks)
154
        self._body_chunks = None
3923.5.2 by Andrew Bennetts
Completely delegate handling of request body chunks to the command object. The default implementation accumulates, like the existing behaviour.
155
        return self.do_body(body_bytes)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
156
4160.2.2 by Andrew Bennetts
Add setup_jail and teardown_jail to SmartServerRequest.
157
    def setup_jail(self):
158
        jail_info.transports = [self._backing_transport]
159
160
    def teardown_jail(self):
161
        jail_info.transports = None
162
2692.1.1 by Andrew Bennetts
Add translate_client_path method to SmartServerRequest.
163
    def translate_client_path(self, client_path):
2692.1.9 by Andrew Bennetts
Docstrings for translate_client_path and transport_from_client_path.
164
        """Translate a path received from a network client into a local
165
        relpath.
166
167
        All paths received from the client *must* be translated.
168
2692.1.14 by Andrew Bennetts
All WSGI tests passing, and manual testing works too.
169
        :param client_path: the path from the client.
2692.1.9 by Andrew Bennetts
Docstrings for translate_client_path and transport_from_client_path.
170
        :returns: a relpath that may be used with self._backing_transport
2692.1.14 by Andrew Bennetts
All WSGI tests passing, and manual testing works too.
171
            (unlike the untranslated client_path, which must not be used with
172
            the backing transport).
2692.1.9 by Andrew Bennetts
Docstrings for translate_client_path and transport_from_client_path.
173
        """
2692.1.14 by Andrew Bennetts
All WSGI tests passing, and manual testing works too.
174
        if self._root_client_path is None:
175
            # no translation necessary!
176
            return client_path
2692.1.1 by Andrew Bennetts
Add translate_client_path method to SmartServerRequest.
177
        if not client_path.startswith('/'):
178
            client_path = '/' + client_path
179
        if client_path.startswith(self._root_client_path):
180
            path = client_path[len(self._root_client_path):]
181
            relpath = urlutils.joinpath('/', path)
3376.2.4 by Martin Pool
Remove every assert statement from bzrlib!
182
            if not relpath.startswith('/'):
183
                raise ValueError(relpath)
2692.1.1 by Andrew Bennetts
Add translate_client_path method to SmartServerRequest.
184
            return '.' + relpath
185
        else:
186
            raise errors.PathNotChild(client_path, self._root_client_path)
187
188
    def transport_from_client_path(self, client_path):
2692.1.9 by Andrew Bennetts
Docstrings for translate_client_path and transport_from_client_path.
189
        """Get a backing transport corresponding to the location referred to by
190
        a network client.
191
192
        :seealso: translate_client_path
193
        :returns: a transport cloned from self._backing_transport
194
        """
2692.1.1 by Andrew Bennetts
Add translate_client_path method to SmartServerRequest.
195
        relpath = self.translate_client_path(client_path)
2692.1.16 by Andrew Bennetts
Improve comments.
196
        return self._backing_transport.clone(relpath)
3195.3.4 by Andrew Bennetts
Make the general request handler dispatch version 3 events to the specific request handler (i.e. to the SmartServerRequest instance).
197
2018.5.6 by Andrew Bennetts
Tidy ups, and turn do_hello and do_get_bundle into command objects.
198
2018.5.16 by Andrew Bennetts
Move SmartServerResponse to smart/request.py, untangling more import dependencies.
199
class SmartServerResponse(object):
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
200
    """A response to a client request.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
201
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
202
    This base class should not be used. Instead use
203
    SuccessfulSmartServerResponse and FailedSmartServerResponse as appropriate.
204
    """
2018.5.16 by Andrew Bennetts
Move SmartServerResponse to smart/request.py, untangling more import dependencies.
205
2748.4.2 by Andrew Bennetts
Add protocol (version two) support for streaming bodies (using chunking) in responses.
206
    def __init__(self, args, body=None, body_stream=None):
207
        """Constructor.
208
209
        :param args: tuple of response arguments.
210
        :param body: string of a response body.
211
        :param body_stream: iterable of bytestrings to be streamed to the
212
            client.
213
        """
2018.5.16 by Andrew Bennetts
Move SmartServerResponse to smart/request.py, untangling more import dependencies.
214
        self.args = args
2748.4.2 by Andrew Bennetts
Add protocol (version two) support for streaming bodies (using chunking) in responses.
215
        if body is not None and body_stream is not None:
216
            raise errors.BzrError(
217
                "'body' and 'body_stream' are mutually exclusive.")
2018.5.16 by Andrew Bennetts
Move SmartServerResponse to smart/request.py, untangling more import dependencies.
218
        self.body = body
2748.4.2 by Andrew Bennetts
Add protocol (version two) support for streaming bodies (using chunking) in responses.
219
        self.body_stream = body_stream
2018.5.16 by Andrew Bennetts
Move SmartServerResponse to smart/request.py, untangling more import dependencies.
220
2402.1.1 by Andrew Bennetts
Use the Command pattern for handling smart server commands.
221
    def __eq__(self, other):
222
        if other is None:
223
            return False
2748.4.2 by Andrew Bennetts
Add protocol (version two) support for streaming bodies (using chunking) in responses.
224
        return (other.args == self.args and
225
                other.body == self.body and
226
                other.body_stream is self.body_stream)
2402.1.1 by Andrew Bennetts
Use the Command pattern for handling smart server commands.
227
228
    def __repr__(self):
3691.2.6 by Martin Pool
Disable RemoteBranch stacking, but get get_stacked_on_url working, and passing back exceptions
229
        return "<%s args=%r body=%r>" % (self.__class__.__name__,
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
230
            self.args, self.body)
2402.1.1 by Andrew Bennetts
Use the Command pattern for handling smart server commands.
231
2018.5.16 by Andrew Bennetts
Move SmartServerResponse to smart/request.py, untangling more import dependencies.
232
2432.4.2 by Robert Collins
Add FailedSmartServerResponse.
233
class FailedSmartServerResponse(SmartServerResponse):
234
    """A SmartServerResponse for a request which failed."""
235
236
    def is_successful(self):
237
        """FailedSmartServerResponse are not successful."""
238
        return False
239
240
2432.4.1 by Robert Collins
Add SuccessfulSmartServerResponse.
241
class SuccessfulSmartServerResponse(SmartServerResponse):
242
    """A SmartServerResponse for a successfully completed request."""
243
244
    def is_successful(self):
245
        """SuccessfulSmartServerResponse are successful."""
246
        return True
247
2018.5.16 by Andrew Bennetts
Move SmartServerResponse to smart/request.py, untangling more import dependencies.
248
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
249
class SmartServerRequestHandler(object):
250
    """Protocol logic for smart server.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
251
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
252
    This doesn't handle serialization at all, it just processes requests and
253
    creates responses.
254
    """
255
256
    # IMPORTANT FOR IMPLEMENTORS: It is important that SmartServerRequestHandler
257
    # not contain encoding or decoding logic to allow the wire protocol to vary
258
    # from the object protocol: we will want to tweak the wire protocol separate
259
    # from the object model, and ideally we will be able to do that without
260
    # having a SmartServerRequestHandler subclass for each wire protocol, rather
261
    # just a Protocol subclass.
262
263
    # TODO: Better way of representing the body for commands that take it,
264
    # and allow it to be streamed into the server.
265
2692.1.1 by Andrew Bennetts
Add translate_client_path method to SmartServerRequest.
266
    def __init__(self, backing_transport, commands, root_client_path):
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
267
        """Constructor.
268
269
        :param backing_transport: a Transport to handle requests for.
2018.5.23 by Andrew Bennetts
Use a Registry for smart server command handlers.
270
        :param commands: a registry mapping command names to SmartServerRequest
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
271
            subclasses. e.g. bzrlib.transport.smart.vfs.vfs_commands.
272
        """
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
273
        self._backing_transport = backing_transport
2692.1.1 by Andrew Bennetts
Add translate_client_path method to SmartServerRequest.
274
        self._root_client_path = root_client_path
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
275
        self._commands = commands
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
276
        self.response = None
277
        self.finished_reading = False
278
        self._command = None
279
280
    def accept_body(self, bytes):
281
        """Accept body data."""
3923.5.2 by Andrew Bennetts
Completely delegate handling of request body chunks to the command object. The default implementation accumulates, like the existing behaviour.
282
        self._run_handler_code(self._command.do_chunk, (bytes,), {})
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
283
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
284
    def end_of_body(self):
285
        """No more body data will be received."""
3923.5.2 by Andrew Bennetts
Completely delegate handling of request body chunks to the command object. The default implementation accumulates, like the existing behaviour.
286
        self._run_handler_code(self._command.do_end, (), {})
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
287
        # cannot read after this.
288
        self.finished_reading = True
289
290
    def dispatch_command(self, cmd, args):
291
        """Deprecated compatibility method.""" # XXX XXX
2018.5.23 by Andrew Bennetts
Use a Registry for smart server command handlers.
292
        try:
293
            command = self._commands.get(cmd)
294
        except LookupError:
3245.4.29 by Andrew Bennetts
Add/tidy some comments, remove dud test_errors_are_logged test, add explicit UnknownSmartMethod to v3.
295
            raise errors.UnknownSmartMethod(cmd)
2692.1.1 by Andrew Bennetts
Add translate_client_path method to SmartServerRequest.
296
        self._command = command(self._backing_transport, self._root_client_path)
2018.5.24 by Andrew Bennetts
Setting NO_SMART_VFS in environment will disable VFS methods in the smart server. (Robert Collins, John Arbash Meinel, Andrew Bennetts)
297
        self._run_handler_code(self._command.execute, args, {})
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
298
299
    def _run_handler_code(self, callable, args, kwargs):
300
        """Run some handler specific code 'callable'.
301
302
        If a result is returned, it is considered to be the commands response,
303
        and finished_reading is set true, and its assigned to self.response.
304
305
        Any exceptions caught are translated and a response object created
306
        from them.
307
        """
308
        result = self._call_converting_errors(callable, args, kwargs)
2018.5.19 by Andrew Bennetts
Add docstrings to all the new modules, and a few other places.
309
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
310
        if result is not None:
311
            self.response = result
312
            self.finished_reading = True
313
314
    def _call_converting_errors(self, callable, args, kwargs):
315
        """Call callable converting errors to Response objects."""
316
        # XXX: most of this error conversion is VFS-related, and thus ought to
317
        # be in SmartServerVFSRequestHandler somewhere.
318
        try:
4160.2.2 by Andrew Bennetts
Add setup_jail and teardown_jail to SmartServerRequest.
319
            self._command.setup_jail()
320
            try:
321
                return callable(*args, **kwargs)
322
            finally:
323
                self._command.teardown_jail()
4064.1.2 by Andrew Bennetts
Refactor server-side error translation, improve tests.
324
        except (KeyboardInterrupt, SystemExit):
325
            raise
326
        except Exception, err:
327
            err_struct = _translate_error(err)
328
            return FailedSmartServerResponse(err_struct)
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
329
3195.3.2 by Andrew Bennetts
Checkpoint first rough cut of SmartServerRequestProtocolThree, this implementation reuses the _StatefulDecoder class. Plus some attempts to start tidying the smart protocol tests.
330
    def headers_received(self, headers):
3245.4.33 by Andrew Bennetts
Remove unused no_body_received method on SmartServerRequestHandler.
331
        # Just a no-op at the moment.
3195.3.2 by Andrew Bennetts
Checkpoint first rough cut of SmartServerRequestProtocolThree, this implementation reuses the _StatefulDecoder class. Plus some attempts to start tidying the smart protocol tests.
332
        pass
333
334
    def args_received(self, args):
3195.3.4 by Andrew Bennetts
Make the general request handler dispatch version 3 events to the specific request handler (i.e. to the SmartServerRequest instance).
335
        cmd = args[0]
336
        args = args[1:]
337
        try:
338
            command = self._commands.get(cmd)
339
        except LookupError:
3245.4.48 by Andrew Bennetts
raise UnknownSmartMethod from dispatch_command.
340
            raise errors.UnknownSmartMethod(cmd)
3195.3.4 by Andrew Bennetts
Make the general request handler dispatch version 3 events to the specific request handler (i.e. to the SmartServerRequest instance).
341
        self._command = command(self._backing_transport)
342
        self._run_handler_code(self._command.execute, args, {})
3195.3.2 by Andrew Bennetts
Checkpoint first rough cut of SmartServerRequestProtocolThree, this implementation reuses the _StatefulDecoder class. Plus some attempts to start tidying the smart protocol tests.
343
3195.3.4 by Andrew Bennetts
Make the general request handler dispatch version 3 events to the specific request handler (i.e. to the SmartServerRequest instance).
344
    def end_received(self):
345
        self._run_handler_code(self._command.do_end, (), {})
3195.3.2 by Andrew Bennetts
Checkpoint first rough cut of SmartServerRequestProtocolThree, this implementation reuses the _StatefulDecoder class. Plus some attempts to start tidying the smart protocol tests.
346
3923.5.4 by Andrew Bennetts
Allow a request's body part(s) to be followed by an error.
347
    def post_body_error_received(self, error_args):
348
        # Just a no-op at the moment.
349
        pass
350
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
351
4064.1.2 by Andrew Bennetts
Refactor server-side error translation, improve tests.
352
def _translate_error(err):
353
    if isinstance(err, errors.NoSuchFile):
354
        return ('NoSuchFile', err.path)
355
    elif isinstance(err, errors.FileExists):
356
        return ('FileExists', err.path)
357
    elif isinstance(err, errors.DirectoryNotEmpty):
358
        return ('DirectoryNotEmpty', err.path)
359
    elif isinstance(err, errors.ShortReadvError):
360
        return ('ShortReadvError', err.path, str(err.offset), str(err.length),
361
                str(err.actual))
362
    elif isinstance(err, errors.UnstackableRepositoryFormat):
363
        return (('UnstackableRepositoryFormat', str(err.format), err.url))
364
    elif isinstance(err, errors.UnstackableBranchFormat):
365
        return ('UnstackableBranchFormat', str(err.format), err.url)
366
    elif isinstance(err, errors.NotStacked):
367
        return ('NotStacked',)
368
    elif isinstance(err, UnicodeError):
369
        # If it is a DecodeError, than most likely we are starting
370
        # with a plain string
371
        str_or_unicode = err.object
372
        if isinstance(str_or_unicode, unicode):
373
            # XXX: UTF-8 might have \x01 (our protocol v1 and v2 seperator
374
            # byte) in it, so this encoding could cause broken responses.
375
            # Newer clients use protocol v3, so will be fine.
376
            val = 'u:' + str_or_unicode.encode('utf-8')
377
        else:
378
            val = 's:' + str_or_unicode.encode('base64')
379
        # This handles UnicodeEncodeError or UnicodeDecodeError
380
        return (err.__class__.__name__, err.encoding, val, str(err.start),
381
                str(err.end), err.reason)
382
    elif isinstance(err, errors.TransportNotPossible):
383
        if err.msg == "readonly transport":
384
            return ('ReadOnlyError', )
385
    elif isinstance(err, errors.ReadError):
386
        # cannot read the file
387
        return ('ReadError', err.path)
388
    elif isinstance(err, errors.PermissionDenied):
389
        return ('PermissionDenied', err.path, err.extra)
4144.3.1 by Andrew Bennetts
Add Repository.insert_stream_locked server-side implementation, plus tests for server-side _translate_error.
390
    elif isinstance(err, errors.TokenMismatch):
391
        return ('TokenMismatch', err.given_token, err.lock_token)
392
    elif isinstance(err, errors.LockContention):
393
        return ('LockContention', err.lock, err.msg)
4064.1.2 by Andrew Bennetts
Refactor server-side error translation, improve tests.
394
    # Unserialisable error.  Log it, and return a generic error
395
    trace.log_exception_quietly()
396
    return ('error', str(err))
397
398
2018.5.6 by Andrew Bennetts
Tidy ups, and turn do_hello and do_get_bundle into command objects.
399
class HelloRequest(SmartServerRequest):
2432.2.6 by Andrew Bennetts
Improve HelloRequest's docstring.
400
    """Answer a version request with the highest protocol version this server
401
    supports.
402
    """
2018.5.6 by Andrew Bennetts
Tidy ups, and turn do_hello and do_get_bundle into command objects.
403
404
    def do(self):
3245.4.59 by Andrew Bennetts
Various tweaks in response to Martin's review.
405
        return SuccessfulSmartServerResponse(('ok', '2'))
2018.5.6 by Andrew Bennetts
Tidy ups, and turn do_hello and do_get_bundle into command objects.
406
407
408
class GetBundleRequest(SmartServerRequest):
2402.1.2 by Andrew Bennetts
Deal with review comments.
409
    """Get a bundle of from the null revision to the specified revision."""
2018.5.6 by Andrew Bennetts
Tidy ups, and turn do_hello and do_get_bundle into command objects.
410
411
    def do(self, path, revision_id):
412
        # open transport relative to our base
2692.1.7 by Andrew Bennetts
Translate path in GetBundleRequest too.
413
        t = self.transport_from_client_path(path)
2018.5.6 by Andrew Bennetts
Tidy ups, and turn do_hello and do_get_bundle into command objects.
414
        control, extra_path = bzrdir.BzrDir.open_containing_from_transport(t)
415
        repo = control.open_repository()
416
        tmpf = tempfile.TemporaryFile()
417
        base_revision = revision.NULL_REVISION
3224.5.6 by Andrew Bennetts
Don't import bzrlib.bundle in bzrlib.smart.request until it's needed.
418
        serializer.write_bundle(repo, revision_id, base_revision, tmpf)
2018.5.6 by Andrew Bennetts
Tidy ups, and turn do_hello and do_get_bundle into command objects.
419
        tmpf.seek(0)
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
420
        return SuccessfulSmartServerResponse((), tmpf.read())
2018.5.6 by Andrew Bennetts
Tidy ups, and turn do_hello and do_get_bundle into command objects.
421
422
2018.5.95 by Andrew Bennetts
Add a Transport.is_readonly remote call, let {Branch,Repository}.lock_write remote call return UnlockableTransport, and miscellaneous test fixes.
423
class SmartServerIsReadonly(SmartServerRequest):
424
    # XXX: this request method belongs somewhere else.
425
426
    def do(self):
427
        if self._backing_transport.is_readonly():
428
            answer = 'yes'
429
        else:
430
            answer = 'no'
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
431
        return SuccessfulSmartServerResponse((answer,))
2018.5.95 by Andrew Bennetts
Add a Transport.is_readonly remote call, let {Branch,Repository}.lock_write remote call return UnlockableTransport, and miscellaneous test fixes.
432
433
2018.5.23 by Andrew Bennetts
Use a Registry for smart server command handlers.
434
request_handlers = registry.Registry()
435
request_handlers.register_lazy(
436
    'append', 'bzrlib.smart.vfs', 'AppendRequest')
437
request_handlers.register_lazy(
4084.2.1 by Robert Collins
Make accessing a branch.tags.get_tag_dict use a smart[er] method rather than VFS calls and real objects.
438
    'Branch.get_config_file', 'bzrlib.smart.branch',
439
    'SmartServerBranchGetConfigFile')
2018.5.59 by Robert Collins
Get BranchConfig working somewhat on RemoteBranches (Robert Collins, Vincent Ladeuil).
440
request_handlers.register_lazy(
4078.2.1 by Robert Collins
Add a Branch.get_parent remote call for RemoteBranch.
441
    'Branch.get_parent', 'bzrlib.smart.branch', 'SmartServerBranchGetParent')
442
request_handlers.register_lazy(
4084.2.1 by Robert Collins
Make accessing a branch.tags.get_tag_dict use a smart[er] method rather than VFS calls and real objects.
443
    'Branch.get_tags_bytes', 'bzrlib.smart.branch',
444
    'SmartServerBranchGetTagsBytes')
445
request_handlers.register_lazy(
3691.2.5 by Martin Pool
Add Branch.get_stacked_on_url rpc and tests for same
446
    'Branch.get_stacked_on_url', 'bzrlib.smart.branch', 'SmartServerBranchRequestGetStackedOnURL')
447
request_handlers.register_lazy(
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
448
    'Branch.last_revision_info', 'bzrlib.smart.branch', 'SmartServerBranchRequestLastRevisionInfo')
449
request_handlers.register_lazy(
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
450
    'Branch.lock_write', 'bzrlib.smart.branch', 'SmartServerBranchRequestLockWrite')
451
request_handlers.register_lazy(
2018.5.38 by Robert Collins
Implement RemoteBranch.revision_history().
452
    'Branch.revision_history', 'bzrlib.smart.branch', 'SmartServerRequestRevisionHistory')
453
request_handlers.register_lazy(
2018.5.77 by Wouter van Heyst
Fix typo in request_handlers registration of Branch.set_last_revision, and test that registration
454
    'Branch.set_last_revision', 'bzrlib.smart.branch', 'SmartServerBranchRequestSetLastRevision')
2018.12.3 by Andrew Bennetts
Add a Branch.set_last_revision smart method, and make RemoteBranch.set_revision_history use it.
455
request_handlers.register_lazy(
2892.2.1 by Andrew Bennetts
Add Branch.set_last_revision_info smart method, and make the RemoteBranch client use it.
456
    'Branch.set_last_revision_info', 'bzrlib.smart.branch',
457
    'SmartServerBranchRequestSetLastRevisionInfo')
458
request_handlers.register_lazy(
3441.5.25 by Andrew Bennetts
Rename Branch.set_last_revision_descendant verb to Branch.set_last_revision_ex. It's a cop out, but at least it's not misleading.
459
    'Branch.set_last_revision_ex', 'bzrlib.smart.branch',
460
    'SmartServerBranchRequestSetLastRevisionEx')
3441.5.6 by Andrew Bennetts
Greatly simplify RemoteBranch.update_revisions. Still needs more tests.
461
request_handlers.register_lazy(
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
462
    'Branch.unlock', 'bzrlib.smart.branch', 'SmartServerBranchRequestUnlock')
463
request_handlers.register_lazy(
4070.2.3 by Robert Collins
Get BzrDir.cloning_metadir working.
464
    'BzrDir.cloning_metadir', 'bzrlib.smart.bzrdir',
465
    'SmartServerBzrDirRequestCloningMetaDir')
466
request_handlers.register_lazy(
4084.2.1 by Robert Collins
Make accessing a branch.tags.get_tag_dict use a smart[er] method rather than VFS calls and real objects.
467
    'BzrDir.create_branch', 'bzrlib.smart.bzrdir',
468
    'SmartServerRequestCreateBranch')
469
request_handlers.register_lazy(
470
    'BzrDir.create_repository', 'bzrlib.smart.bzrdir',
471
    'SmartServerRequestCreateRepository')
472
request_handlers.register_lazy(
473
    'BzrDir.find_repository', 'bzrlib.smart.bzrdir',
474
    'SmartServerRequestFindRepositoryV1')
475
request_handlers.register_lazy(
476
    'BzrDir.find_repositoryV2', 'bzrlib.smart.bzrdir',
477
    'SmartServerRequestFindRepositoryV2')
478
request_handlers.register_lazy(
479
    'BzrDir.find_repositoryV3', 'bzrlib.smart.bzrdir',
480
    'SmartServerRequestFindRepositoryV3')
481
request_handlers.register_lazy(
482
    'BzrDirFormat.initialize', 'bzrlib.smart.bzrdir',
483
    'SmartServerRequestInitializeBzrDir')
484
request_handlers.register_lazy(
485
    'BzrDir.open_branch', 'bzrlib.smart.bzrdir',
486
    'SmartServerRequestOpenBranch')
487
request_handlers.register_lazy(
488
    'BzrDir.open_branchV2', 'bzrlib.smart.bzrdir',
489
    'SmartServerRequestOpenBranchV2')
2018.6.1 by Robert Collins
Implement a BzrDir.open_branch smart server method for opening a branch without VFS.
490
request_handlers.register_lazy(
2018.5.23 by Andrew Bennetts
Use a Registry for smart server command handlers.
491
    'delete', 'bzrlib.smart.vfs', 'DeleteRequest')
492
request_handlers.register_lazy(
493
    'get', 'bzrlib.smart.vfs', 'GetRequest')
494
request_handlers.register_lazy(
495
    'get_bundle', 'bzrlib.smart.request', 'GetBundleRequest')
496
request_handlers.register_lazy(
497
    'has', 'bzrlib.smart.vfs', 'HasRequest')
498
request_handlers.register_lazy(
499
    'hello', 'bzrlib.smart.request', 'HelloRequest')
500
request_handlers.register_lazy(
2018.5.37 by Andrew Bennetts
Make sure all the request handlers in bzrlib/smart/vfs.py have consistent names.
501
    'iter_files_recursive', 'bzrlib.smart.vfs', 'IterFilesRecursiveRequest')
2018.5.23 by Andrew Bennetts
Use a Registry for smart server command handlers.
502
request_handlers.register_lazy(
503
    'list_dir', 'bzrlib.smart.vfs', 'ListDirRequest')
504
request_handlers.register_lazy(
2018.5.37 by Andrew Bennetts
Make sure all the request handlers in bzrlib/smart/vfs.py have consistent names.
505
    'mkdir', 'bzrlib.smart.vfs', 'MkdirRequest')
506
request_handlers.register_lazy(
507
    'move', 'bzrlib.smart.vfs', 'MoveRequest')
508
request_handlers.register_lazy(
509
    'put', 'bzrlib.smart.vfs', 'PutRequest')
510
request_handlers.register_lazy(
511
    'put_non_atomic', 'bzrlib.smart.vfs', 'PutNonAtomicRequest')
512
request_handlers.register_lazy(
513
    'readv', 'bzrlib.smart.vfs', 'ReadvRequest')
514
request_handlers.register_lazy(
515
    'rename', 'bzrlib.smart.vfs', 'RenameRequest')
3452.2.2 by Andrew Bennetts
Experimental PackRepository.{check_references,autopack} RPCs.
516
request_handlers.register_lazy(
517
    'PackRepository.autopack', 'bzrlib.smart.packrepository',
518
    'SmartServerPackRepositoryAutopack')
2018.10.2 by v.ladeuil+lp at free
gather_stats server side and request registration
519
request_handlers.register_lazy('Repository.gather_stats',
520
                               'bzrlib.smart.repository',
521
                               'SmartServerRepositoryGatherStats')
3842.3.3 by Andrew Bennetts
Remove RPC registration for VersionedFiles.get_parent_map, not Repository.get_parent_map.
522
request_handlers.register_lazy('Repository.get_parent_map',
523
                               'bzrlib.smart.repository',
524
                               'SmartServerRepositoryGetParentMap')
2018.5.37 by Andrew Bennetts
Make sure all the request handlers in bzrlib/smart/vfs.py have consistent names.
525
request_handlers.register_lazy(
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
526
    'Repository.get_revision_graph', 'bzrlib.smart.repository', 'SmartServerRepositoryGetRevisionGraph')
527
request_handlers.register_lazy(
2018.5.40 by Robert Collins
Implement a remote Repository.has_revision method.
528
    'Repository.has_revision', 'bzrlib.smart.repository', 'SmartServerRequestHasRevision')
529
request_handlers.register_lazy(
4022.1.6 by Robert Collins
Cherrypick and polish the RemoteSink for streaming push.
530
    'Repository.insert_stream', 'bzrlib.smart.repository', 'SmartServerRepositoryInsertStream')
531
request_handlers.register_lazy(
4144.3.1 by Andrew Bennetts
Add Repository.insert_stream_locked server-side implementation, plus tests for server-side _translate_error.
532
    'Repository.insert_stream_locked', 'bzrlib.smart.repository', 'SmartServerRepositoryInsertStreamLocked')
533
request_handlers.register_lazy(
2018.5.57 by Robert Collins
Implement RemoteRepository.is_shared (Robert Collins, Vincent Ladeuil).
534
    'Repository.is_shared', 'bzrlib.smart.repository', 'SmartServerRepositoryIsShared')
535
request_handlers.register_lazy(
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
536
    'Repository.lock_write', 'bzrlib.smart.repository', 'SmartServerRepositoryLockWrite')
537
request_handlers.register_lazy(
4017.3.4 by Robert Collins
Create a verb for Repository.set_make_working_trees.
538
    'Repository.set_make_working_trees', 'bzrlib.smart.repository',
539
    'SmartServerRepositorySetMakeWorkingTrees')
540
request_handlers.register_lazy(
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
541
    'Repository.unlock', 'bzrlib.smart.repository', 'SmartServerRepositoryUnlock')
542
request_handlers.register_lazy(
4060.1.5 by Robert Collins
Verb change name requested by Andrew.
543
    'Repository.get_stream', 'bzrlib.smart.repository',
544
    'SmartServerRepositoryGetStream')
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
545
request_handlers.register_lazy(
2018.18.1 by Martin Pool
Add stub Repository.tarball smart method
546
    'Repository.tarball', 'bzrlib.smart.repository',
547
    'SmartServerRepositoryTarball')
548
request_handlers.register_lazy(
2018.5.37 by Andrew Bennetts
Make sure all the request handlers in bzrlib/smart/vfs.py have consistent names.
549
    'rmdir', 'bzrlib.smart.vfs', 'RmdirRequest')
550
request_handlers.register_lazy(
551
    'stat', 'bzrlib.smart.vfs', 'StatRequest')
2018.5.26 by Andrew Bennetts
Extract a simple SmartClient class from RemoteTransport, and a hack to avoid VFS operations when probing for a bzrdir over a smart transport.
552
request_handlers.register_lazy(
2018.5.95 by Andrew Bennetts
Add a Transport.is_readonly remote call, let {Branch,Repository}.lock_write remote call return UnlockableTransport, and miscellaneous test fixes.
553
    'Transport.is_readonly', 'bzrlib.smart.request', 'SmartServerIsReadonly')
554
request_handlers.register_lazy(
2018.5.163 by Andrew Bennetts
Deal with various review comments from Robert.
555
    'BzrDir.open', 'bzrlib.smart.bzrdir', 'SmartServerRequestOpenBzrDir')