~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):
4205.2.1 by Andrew Bennetts
Fix BzrDir.open in non-main (and non-server-request) thread when bzrlib.smart.request's _pre_open_hook is installed.
61
    allowed_transports = getattr(jail_info, 'transports', None)
4160.2.4 by Andrew Bennetts
Use BzrDir pre_open hook to jail request code from accessing transports other than the backing transport.
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
4294.2.8 by Robert Collins
Reduce round trips pushing new branches substantially.
72
    raise errors.JailBreak(abspath)
4160.2.4 by Andrew Bennetts
Use BzrDir pre_open hook to jail request code from accessing transports other than the backing transport.
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
4760.1.1 by Andrew Bennetts
Add optional jail_root argument to SmartServerRequest and friends, and use it in the WSGI glue. Allows opening branches in shared repos via bzr+http (assuming the repo should be accessible).
89
    def __init__(self, backing_transport, root_client_path='/', jail_root=None):
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 '/'.
4760.1.1 by Andrew Bennetts
Add optional jail_root argument to SmartServerRequest and friends, and use it in the WSGI glue. Allows opening branches in shared repos via bzr+http (assuming the repo should be accessible).
99
        :param jail_root: if specified, the root of the BzrDir.open jail to use
100
            instead of backing_transport.
2402.1.2 by Andrew Bennetts
Deal with review comments.
101
        """
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.
102
        self._backing_transport = backing_transport
4760.1.1 by Andrew Bennetts
Add optional jail_root argument to SmartServerRequest and friends, and use it in the WSGI glue. Allows opening branches in shared repos via bzr+http (assuming the repo should be accessible).
103
        if jail_root is None:
104
            jail_root = backing_transport
105
        self._jail_root = jail_root
2692.1.14 by Andrew Bennetts
All WSGI tests passing, and manual testing works too.
106
        if root_client_path is not None:
107
            if not root_client_path.startswith('/'):
108
                root_client_path = '/' + root_client_path
109
            if not root_client_path.endswith('/'):
110
                root_client_path += '/'
2692.1.1 by Andrew Bennetts
Add translate_client_path method to SmartServerRequest.
111
        self._root_client_path = root_client_path
3842.3.19 by Andrew Bennetts
Tweaks suggested by review.
112
        self._body_chunks = []
2018.5.5 by Andrew Bennetts
Pass body_bytes directly to SmartServerRequest.do_body
113
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
    def _check_enabled(self):
115
        """Raises DisabledMethod if this method is disabled."""
116
        pass
117
2018.5.19 by Andrew Bennetts
Add docstrings to all the new modules, and a few other places.
118
    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)
119
        """Mandatory extension point for SmartServerRequest subclasses.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
120
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)
121
        Subclasses must implement this.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
122
2018.5.19 by Andrew Bennetts
Add docstrings to all the new modules, and a few other places.
123
        This should return a SmartServerResponse if this command expects to
124
        receive no body.
125
        """
2018.5.5 by Andrew Bennetts
Pass body_bytes directly to SmartServerRequest.do_body
126
        raise NotImplementedError(self.do)
127
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)
128
    def execute(self, *args):
129
        """Public entry point to execute this request.
130
131
        It will return a SmartServerResponse if the command does not expect a
132
        body.
133
134
        :param *args: the arguments of the request.
135
        """
136
        self._check_enabled()
137
        return self.do(*args)
138
2018.5.5 by Andrew Bennetts
Pass body_bytes directly to SmartServerRequest.do_body
139
    def do_body(self, body_bytes):
2018.5.19 by Andrew Bennetts
Add docstrings to all the new modules, and a few other places.
140
        """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.
141
142
        The do() method is still called, and must have returned None.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
143
2018.5.19 by Andrew Bennetts
Add docstrings to all the new modules, and a few other places.
144
        Must return a SmartServerResponse.
145
        """
3990.3.2 by Andrew Bennetts
Fix the do_body NotImplementedError log spam.
146
        if body_bytes != '':
3990.3.3 by Andrew Bennetts
Add a test that unexpected request bodies trigger a SmartProtocolError from request implementations.
147
            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.
148
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).
149
    def do_chunk(self, chunk_bytes):
150
        """Called with each body chunk if the request has a streamed body.
151
152
        The do() method is still called, and must have returned None.
153
        """
3842.3.19 by Andrew Bennetts
Tweaks suggested by review.
154
        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).
155
156
    def do_end(self):
157
        """Called when the end of the request has been received."""
3842.3.19 by Andrew Bennetts
Tweaks suggested by review.
158
        body_bytes = ''.join(self._body_chunks)
159
        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.
160
        return self.do_body(body_bytes)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
161
4160.2.2 by Andrew Bennetts
Add setup_jail and teardown_jail to SmartServerRequest.
162
    def setup_jail(self):
4760.1.1 by Andrew Bennetts
Add optional jail_root argument to SmartServerRequest and friends, and use it in the WSGI glue. Allows opening branches in shared repos via bzr+http (assuming the repo should be accessible).
163
        jail_info.transports = [self._jail_root]
4160.2.2 by Andrew Bennetts
Add setup_jail and teardown_jail to SmartServerRequest.
164
165
    def teardown_jail(self):
166
        jail_info.transports = None
167
2692.1.1 by Andrew Bennetts
Add translate_client_path method to SmartServerRequest.
168
    def translate_client_path(self, client_path):
2692.1.9 by Andrew Bennetts
Docstrings for translate_client_path and transport_from_client_path.
169
        """Translate a path received from a network client into a local
170
        relpath.
171
172
        All paths received from the client *must* be translated.
173
2692.1.14 by Andrew Bennetts
All WSGI tests passing, and manual testing works too.
174
        :param client_path: the path from the client.
2692.1.9 by Andrew Bennetts
Docstrings for translate_client_path and transport_from_client_path.
175
        :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.
176
            (unlike the untranslated client_path, which must not be used with
177
            the backing transport).
2692.1.9 by Andrew Bennetts
Docstrings for translate_client_path and transport_from_client_path.
178
        """
2692.1.14 by Andrew Bennetts
All WSGI tests passing, and manual testing works too.
179
        if self._root_client_path is None:
180
            # no translation necessary!
181
            return client_path
2692.1.1 by Andrew Bennetts
Add translate_client_path method to SmartServerRequest.
182
        if not client_path.startswith('/'):
183
            client_path = '/' + client_path
4294.2.1 by Robert Collins
Move directory checking for bzr push options into Branch.create_clone_on_transport.
184
        if client_path + '/' == self._root_client_path:
185
            return '.'
2692.1.1 by Andrew Bennetts
Add translate_client_path method to SmartServerRequest.
186
        if client_path.startswith(self._root_client_path):
187
            path = client_path[len(self._root_client_path):]
188
            relpath = urlutils.joinpath('/', path)
3376.2.4 by Martin Pool
Remove every assert statement from bzrlib!
189
            if not relpath.startswith('/'):
190
                raise ValueError(relpath)
4760.2.1 by Michael Hudson
very simple fix
191
            return urlutils.escape('.' + relpath)
2692.1.1 by Andrew Bennetts
Add translate_client_path method to SmartServerRequest.
192
        else:
193
            raise errors.PathNotChild(client_path, self._root_client_path)
194
195
    def transport_from_client_path(self, client_path):
2692.1.9 by Andrew Bennetts
Docstrings for translate_client_path and transport_from_client_path.
196
        """Get a backing transport corresponding to the location referred to by
197
        a network client.
198
199
        :seealso: translate_client_path
200
        :returns: a transport cloned from self._backing_transport
201
        """
2692.1.1 by Andrew Bennetts
Add translate_client_path method to SmartServerRequest.
202
        relpath = self.translate_client_path(client_path)
2692.1.16 by Andrew Bennetts
Improve comments.
203
        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).
204
2018.5.6 by Andrew Bennetts
Tidy ups, and turn do_hello and do_get_bundle into command objects.
205
2018.5.16 by Andrew Bennetts
Move SmartServerResponse to smart/request.py, untangling more import dependencies.
206
class SmartServerResponse(object):
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
207
    """A response to a client request.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
208
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
209
    This base class should not be used. Instead use
210
    SuccessfulSmartServerResponse and FailedSmartServerResponse as appropriate.
211
    """
2018.5.16 by Andrew Bennetts
Move SmartServerResponse to smart/request.py, untangling more import dependencies.
212
2748.4.2 by Andrew Bennetts
Add protocol (version two) support for streaming bodies (using chunking) in responses.
213
    def __init__(self, args, body=None, body_stream=None):
214
        """Constructor.
215
216
        :param args: tuple of response arguments.
217
        :param body: string of a response body.
218
        :param body_stream: iterable of bytestrings to be streamed to the
219
            client.
220
        """
2018.5.16 by Andrew Bennetts
Move SmartServerResponse to smart/request.py, untangling more import dependencies.
221
        self.args = args
2748.4.2 by Andrew Bennetts
Add protocol (version two) support for streaming bodies (using chunking) in responses.
222
        if body is not None and body_stream is not None:
223
            raise errors.BzrError(
224
                "'body' and 'body_stream' are mutually exclusive.")
2018.5.16 by Andrew Bennetts
Move SmartServerResponse to smart/request.py, untangling more import dependencies.
225
        self.body = body
2748.4.2 by Andrew Bennetts
Add protocol (version two) support for streaming bodies (using chunking) in responses.
226
        self.body_stream = body_stream
2018.5.16 by Andrew Bennetts
Move SmartServerResponse to smart/request.py, untangling more import dependencies.
227
2402.1.1 by Andrew Bennetts
Use the Command pattern for handling smart server commands.
228
    def __eq__(self, other):
229
        if other is None:
230
            return False
2748.4.2 by Andrew Bennetts
Add protocol (version two) support for streaming bodies (using chunking) in responses.
231
        return (other.args == self.args and
232
                other.body == self.body and
233
                other.body_stream is self.body_stream)
2402.1.1 by Andrew Bennetts
Use the Command pattern for handling smart server commands.
234
235
    def __repr__(self):
3691.2.6 by Martin Pool
Disable RemoteBranch stacking, but get get_stacked_on_url working, and passing back exceptions
236
        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.
237
            self.args, self.body)
2402.1.1 by Andrew Bennetts
Use the Command pattern for handling smart server commands.
238
2018.5.16 by Andrew Bennetts
Move SmartServerResponse to smart/request.py, untangling more import dependencies.
239
2432.4.2 by Robert Collins
Add FailedSmartServerResponse.
240
class FailedSmartServerResponse(SmartServerResponse):
241
    """A SmartServerResponse for a request which failed."""
242
243
    def is_successful(self):
244
        """FailedSmartServerResponse are not successful."""
245
        return False
246
247
2432.4.1 by Robert Collins
Add SuccessfulSmartServerResponse.
248
class SuccessfulSmartServerResponse(SmartServerResponse):
249
    """A SmartServerResponse for a successfully completed request."""
250
251
    def is_successful(self):
252
        """SuccessfulSmartServerResponse are successful."""
253
        return True
254
2018.5.16 by Andrew Bennetts
Move SmartServerResponse to smart/request.py, untangling more import dependencies.
255
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
256
class SmartServerRequestHandler(object):
257
    """Protocol logic for smart server.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
258
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
259
    This doesn't handle serialization at all, it just processes requests and
260
    creates responses.
261
    """
262
263
    # IMPORTANT FOR IMPLEMENTORS: It is important that SmartServerRequestHandler
264
    # not contain encoding or decoding logic to allow the wire protocol to vary
265
    # from the object protocol: we will want to tweak the wire protocol separate
266
    # from the object model, and ideally we will be able to do that without
267
    # having a SmartServerRequestHandler subclass for each wire protocol, rather
268
    # just a Protocol subclass.
269
270
    # TODO: Better way of representing the body for commands that take it,
271
    # and allow it to be streamed into the server.
272
4760.1.1 by Andrew Bennetts
Add optional jail_root argument to SmartServerRequest and friends, and use it in the WSGI glue. Allows opening branches in shared repos via bzr+http (assuming the repo should be accessible).
273
    def __init__(self, backing_transport, commands, root_client_path,
4760.1.4 by Andrew Bennetts
Make jail_root param of SmartServerRequestHandler optional for convenience of tests.
274
        jail_root=None):
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
275
        """Constructor.
276
277
        :param backing_transport: a Transport to handle requests for.
2018.5.23 by Andrew Bennetts
Use a Registry for smart server command handlers.
278
        :param commands: a registry mapping command names to SmartServerRequest
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
279
            subclasses. e.g. bzrlib.transport.smart.vfs.vfs_commands.
280
        """
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
281
        self._backing_transport = backing_transport
2692.1.1 by Andrew Bennetts
Add translate_client_path method to SmartServerRequest.
282
        self._root_client_path = root_client_path
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
283
        self._commands = commands
4760.1.4 by Andrew Bennetts
Make jail_root param of SmartServerRequestHandler optional for convenience of tests.
284
        if jail_root is None:
285
            jail_root = backing_transport
4760.1.1 by Andrew Bennetts
Add optional jail_root argument to SmartServerRequest and friends, and use it in the WSGI glue. Allows opening branches in shared repos via bzr+http (assuming the repo should be accessible).
286
        self._jail_root = jail_root
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
287
        self.response = None
288
        self.finished_reading = False
289
        self._command = None
290
291
    def accept_body(self, bytes):
292
        """Accept body data."""
4475.2.2 by Andrew Bennetts
Don't try call do_chunk or do_end if there's no active command (e.g. while consuming a request for an unknown method)
293
        if self._command is None:
294
            # no active command object, so ignore the event.
295
            return
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.
296
        self._run_handler_code(self._command.do_chunk, (bytes,), {})
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
297
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
298
    def end_of_body(self):
299
        """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.
300
        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.
301
        # cannot read after this.
302
        self.finished_reading = True
303
304
    def _run_handler_code(self, callable, args, kwargs):
305
        """Run some handler specific code 'callable'.
306
307
        If a result is returned, it is considered to be the commands response,
308
        and finished_reading is set true, and its assigned to self.response.
309
310
        Any exceptions caught are translated and a response object created
311
        from them.
312
        """
313
        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.
314
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
315
        if result is not None:
316
            self.response = result
317
            self.finished_reading = True
318
319
    def _call_converting_errors(self, callable, args, kwargs):
320
        """Call callable converting errors to Response objects."""
321
        # XXX: most of this error conversion is VFS-related, and thus ought to
322
        # be in SmartServerVFSRequestHandler somewhere.
323
        try:
4160.2.2 by Andrew Bennetts
Add setup_jail and teardown_jail to SmartServerRequest.
324
            self._command.setup_jail()
325
            try:
326
                return callable(*args, **kwargs)
327
            finally:
328
                self._command.teardown_jail()
4064.1.2 by Andrew Bennetts
Refactor server-side error translation, improve tests.
329
        except (KeyboardInterrupt, SystemExit):
330
            raise
331
        except Exception, err:
332
            err_struct = _translate_error(err)
333
            return FailedSmartServerResponse(err_struct)
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
334
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.
335
    def headers_received(self, headers):
3245.4.33 by Andrew Bennetts
Remove unused no_body_received method on SmartServerRequestHandler.
336
        # 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.
337
        pass
338
339
    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).
340
        cmd = args[0]
341
        args = args[1:]
342
        try:
343
            command = self._commands.get(cmd)
344
        except LookupError:
3245.4.48 by Andrew Bennetts
raise UnknownSmartMethod from dispatch_command.
345
            raise errors.UnknownSmartMethod(cmd)
4634.6.30 by Andrew Bennetts
Remove SmartServerRequest.dispatch_command, fix SmartServerRequest.args_received.
346
        self._command = command(
4760.1.1 by Andrew Bennetts
Add optional jail_root argument to SmartServerRequest and friends, and use it in the WSGI glue. Allows opening branches in shared repos via bzr+http (assuming the repo should be accessible).
347
            self._backing_transport, self._root_client_path, self._jail_root)
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).
348
        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.
349
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).
350
    def end_received(self):
4475.2.2 by Andrew Bennetts
Don't try call do_chunk or do_end if there's no active command (e.g. while consuming a request for an unknown method)
351
        if self._command is None:
352
            # no active command object, so ignore the event.
353
            return
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).
354
        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.
355
3923.5.4 by Andrew Bennetts
Allow a request's body part(s) to be followed by an error.
356
    def post_body_error_received(self, error_args):
357
        # Just a no-op at the moment.
358
        pass
359
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
360
4064.1.2 by Andrew Bennetts
Refactor server-side error translation, improve tests.
361
def _translate_error(err):
362
    if isinstance(err, errors.NoSuchFile):
363
        return ('NoSuchFile', err.path)
364
    elif isinstance(err, errors.FileExists):
365
        return ('FileExists', err.path)
366
    elif isinstance(err, errors.DirectoryNotEmpty):
367
        return ('DirectoryNotEmpty', err.path)
4650.2.2 by Robert Collins
Serialize IncompatibleRepositories from the server too.
368
    elif isinstance(err, errors.IncompatibleRepositories):
369
        return ('IncompatibleRepositories', str(err.source), str(err.target),
370
            str(err.details))
4064.1.2 by Andrew Bennetts
Refactor server-side error translation, improve tests.
371
    elif isinstance(err, errors.ShortReadvError):
372
        return ('ShortReadvError', err.path, str(err.offset), str(err.length),
373
                str(err.actual))
374
    elif isinstance(err, errors.UnstackableRepositoryFormat):
375
        return (('UnstackableRepositoryFormat', str(err.format), err.url))
376
    elif isinstance(err, errors.UnstackableBranchFormat):
377
        return ('UnstackableBranchFormat', str(err.format), err.url)
378
    elif isinstance(err, errors.NotStacked):
379
        return ('NotStacked',)
380
    elif isinstance(err, UnicodeError):
381
        # If it is a DecodeError, than most likely we are starting
382
        # with a plain string
383
        str_or_unicode = err.object
384
        if isinstance(str_or_unicode, unicode):
385
            # XXX: UTF-8 might have \x01 (our protocol v1 and v2 seperator
386
            # byte) in it, so this encoding could cause broken responses.
387
            # Newer clients use protocol v3, so will be fine.
388
            val = 'u:' + str_or_unicode.encode('utf-8')
389
        else:
390
            val = 's:' + str_or_unicode.encode('base64')
391
        # This handles UnicodeEncodeError or UnicodeDecodeError
392
        return (err.__class__.__name__, err.encoding, val, str(err.start),
393
                str(err.end), err.reason)
394
    elif isinstance(err, errors.TransportNotPossible):
395
        if err.msg == "readonly transport":
396
            return ('ReadOnlyError', )
397
    elif isinstance(err, errors.ReadError):
398
        # cannot read the file
399
        return ('ReadError', err.path)
400
    elif isinstance(err, errors.PermissionDenied):
401
        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.
402
    elif isinstance(err, errors.TokenMismatch):
403
        return ('TokenMismatch', err.given_token, err.lock_token)
404
    elif isinstance(err, errors.LockContention):
4556.2.6 by Andrew Bennetts
More conservative fix for LockContention serialisation.
405
        return ('LockContention',)
4064.1.2 by Andrew Bennetts
Refactor server-side error translation, improve tests.
406
    # Unserialisable error.  Log it, and return a generic error
407
    trace.log_exception_quietly()
408
    return ('error', str(err))
409
410
2018.5.6 by Andrew Bennetts
Tidy ups, and turn do_hello and do_get_bundle into command objects.
411
class HelloRequest(SmartServerRequest):
2432.2.6 by Andrew Bennetts
Improve HelloRequest's docstring.
412
    """Answer a version request with the highest protocol version this server
413
    supports.
414
    """
2018.5.6 by Andrew Bennetts
Tidy ups, and turn do_hello and do_get_bundle into command objects.
415
416
    def do(self):
3245.4.59 by Andrew Bennetts
Various tweaks in response to Martin's review.
417
        return SuccessfulSmartServerResponse(('ok', '2'))
2018.5.6 by Andrew Bennetts
Tidy ups, and turn do_hello and do_get_bundle into command objects.
418
419
420
class GetBundleRequest(SmartServerRequest):
2402.1.2 by Andrew Bennetts
Deal with review comments.
421
    """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.
422
423
    def do(self, path, revision_id):
424
        # open transport relative to our base
2692.1.7 by Andrew Bennetts
Translate path in GetBundleRequest too.
425
        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.
426
        control, extra_path = bzrdir.BzrDir.open_containing_from_transport(t)
427
        repo = control.open_repository()
428
        tmpf = tempfile.TemporaryFile()
429
        base_revision = revision.NULL_REVISION
3224.5.6 by Andrew Bennetts
Don't import bzrlib.bundle in bzrlib.smart.request until it's needed.
430
        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.
431
        tmpf.seek(0)
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
432
        return SuccessfulSmartServerResponse((), tmpf.read())
2018.5.6 by Andrew Bennetts
Tidy ups, and turn do_hello and do_get_bundle into command objects.
433
434
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.
435
class SmartServerIsReadonly(SmartServerRequest):
436
    # XXX: this request method belongs somewhere else.
437
438
    def do(self):
439
        if self._backing_transport.is_readonly():
440
            answer = 'yes'
441
        else:
442
            answer = 'no'
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
443
        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.
444
445
2018.5.23 by Andrew Bennetts
Use a Registry for smart server command handlers.
446
request_handlers = registry.Registry()
447
request_handlers.register_lazy(
448
    'append', 'bzrlib.smart.vfs', 'AppendRequest')
449
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.
450
    'Branch.get_config_file', 'bzrlib.smart.branch',
451
    'SmartServerBranchGetConfigFile')
2018.5.59 by Robert Collins
Get BranchConfig working somewhat on RemoteBranches (Robert Collins, Vincent Ladeuil).
452
request_handlers.register_lazy(
4078.2.1 by Robert Collins
Add a Branch.get_parent remote call for RemoteBranch.
453
    'Branch.get_parent', 'bzrlib.smart.branch', 'SmartServerBranchGetParent')
454
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.
455
    'Branch.get_tags_bytes', 'bzrlib.smart.branch',
456
    'SmartServerBranchGetTagsBytes')
457
request_handlers.register_lazy(
4556.2.1 by Andrew Bennetts
Add Branch.set_tags_bytes RPC, with HPSS call count acceptance test. Also fixes serialisation of LockDir, and uses external_url() in LockDir's repr and contention message.
458
    'Branch.set_tags_bytes', 'bzrlib.smart.branch',
459
    'SmartServerBranchSetTagsBytes')
460
request_handlers.register_lazy(
3691.2.5 by Martin Pool
Add Branch.get_stacked_on_url rpc and tests for same
461
    'Branch.get_stacked_on_url', 'bzrlib.smart.branch', 'SmartServerBranchRequestGetStackedOnURL')
462
request_handlers.register_lazy(
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
463
    'Branch.last_revision_info', 'bzrlib.smart.branch', 'SmartServerBranchRequestLastRevisionInfo')
464
request_handlers.register_lazy(
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
465
    'Branch.lock_write', 'bzrlib.smart.branch', 'SmartServerBranchRequestLockWrite')
4226.2.1 by Robert Collins
Set branch config options via a smart method.
466
request_handlers.register_lazy( 'Branch.revision_history',
467
    'bzrlib.smart.branch', 'SmartServerRequestRevisionHistory')
468
request_handlers.register_lazy( 'Branch.set_config_option',
469
    'bzrlib.smart.branch', 'SmartServerBranchRequestSetConfigOption')
470
request_handlers.register_lazy( 'Branch.set_last_revision',
471
    '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.
472
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.
473
    'Branch.set_last_revision_info', 'bzrlib.smart.branch',
474
    'SmartServerBranchRequestSetLastRevisionInfo')
475
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.
476
    'Branch.set_last_revision_ex', 'bzrlib.smart.branch',
477
    'SmartServerBranchRequestSetLastRevisionEx')
3441.5.6 by Andrew Bennetts
Greatly simplify RemoteBranch.update_revisions. Still needs more tests.
478
request_handlers.register_lazy(
4288.1.7 by Robert Collins
Add new remote server verb Branch.set_parent_location, dropping roundtrips further on push operations.
479
    'Branch.set_parent_location', 'bzrlib.smart.branch',
480
    'SmartServerBranchRequestSetParentLocation')
481
request_handlers.register_lazy(
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
482
    'Branch.unlock', 'bzrlib.smart.branch', 'SmartServerBranchRequestUnlock')
483
request_handlers.register_lazy(
4070.2.3 by Robert Collins
Get BzrDir.cloning_metadir working.
484
    'BzrDir.cloning_metadir', 'bzrlib.smart.bzrdir',
485
    'SmartServerBzrDirRequestCloningMetaDir')
486
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.
487
    'BzrDir.create_branch', 'bzrlib.smart.bzrdir',
488
    'SmartServerRequestCreateBranch')
489
request_handlers.register_lazy(
490
    'BzrDir.create_repository', 'bzrlib.smart.bzrdir',
491
    'SmartServerRequestCreateRepository')
492
request_handlers.register_lazy(
493
    'BzrDir.find_repository', 'bzrlib.smart.bzrdir',
494
    'SmartServerRequestFindRepositoryV1')
495
request_handlers.register_lazy(
496
    'BzrDir.find_repositoryV2', 'bzrlib.smart.bzrdir',
497
    'SmartServerRequestFindRepositoryV2')
498
request_handlers.register_lazy(
499
    'BzrDir.find_repositoryV3', 'bzrlib.smart.bzrdir',
500
    'SmartServerRequestFindRepositoryV3')
501
request_handlers.register_lazy(
4288.1.2 by Robert Collins
Create a server verb for doing BzrDir.get_config()
502
    'BzrDir.get_config_file', 'bzrlib.smart.bzrdir',
503
    'SmartServerBzrDirRequestConfigFile')
504
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.
505
    'BzrDirFormat.initialize', 'bzrlib.smart.bzrdir',
506
    'SmartServerRequestInitializeBzrDir')
507
request_handlers.register_lazy(
4436.1.1 by Andrew Bennetts
Rename BzrDirFormat.initialize_ex verb to BzrDirFormat.initialize_ex_1.16.
508
    'BzrDirFormat.initialize_ex_1.16', 'bzrlib.smart.bzrdir',
4294.2.7 by Robert Collins
Start building up a BzrDir.initialize_ex verb for the smart server.
509
    'SmartServerRequestBzrDirInitializeEx')
510
request_handlers.register_lazy(
4288.1.2 by Robert Collins
Create a server verb for doing BzrDir.get_config()
511
    'BzrDir.open', 'bzrlib.smart.bzrdir', 'SmartServerRequestOpenBzrDir')
512
request_handlers.register_lazy(
4634.47.3 by Andrew Bennetts
Add a BzrDir.open_2.1 verb that indicates if there is a workingtree present. Removes the last 2 VFS calls from incremental pushes.
513
    'BzrDir.open_2.1', 'bzrlib.smart.bzrdir', 'SmartServerRequestOpenBzrDir_2_1')
514
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.
515
    'BzrDir.open_branch', 'bzrlib.smart.bzrdir',
516
    'SmartServerRequestOpenBranch')
517
request_handlers.register_lazy(
518
    'BzrDir.open_branchV2', 'bzrlib.smart.bzrdir',
519
    'SmartServerRequestOpenBranchV2')
2018.6.1 by Robert Collins
Implement a BzrDir.open_branch smart server method for opening a branch without VFS.
520
request_handlers.register_lazy(
2018.5.23 by Andrew Bennetts
Use a Registry for smart server command handlers.
521
    'delete', 'bzrlib.smart.vfs', 'DeleteRequest')
522
request_handlers.register_lazy(
523
    'get', 'bzrlib.smart.vfs', 'GetRequest')
524
request_handlers.register_lazy(
525
    'get_bundle', 'bzrlib.smart.request', 'GetBundleRequest')
526
request_handlers.register_lazy(
527
    'has', 'bzrlib.smart.vfs', 'HasRequest')
528
request_handlers.register_lazy(
529
    'hello', 'bzrlib.smart.request', 'HelloRequest')
530
request_handlers.register_lazy(
2018.5.37 by Andrew Bennetts
Make sure all the request handlers in bzrlib/smart/vfs.py have consistent names.
531
    'iter_files_recursive', 'bzrlib.smart.vfs', 'IterFilesRecursiveRequest')
2018.5.23 by Andrew Bennetts
Use a Registry for smart server command handlers.
532
request_handlers.register_lazy(
533
    'list_dir', 'bzrlib.smart.vfs', 'ListDirRequest')
534
request_handlers.register_lazy(
2018.5.37 by Andrew Bennetts
Make sure all the request handlers in bzrlib/smart/vfs.py have consistent names.
535
    'mkdir', 'bzrlib.smart.vfs', 'MkdirRequest')
536
request_handlers.register_lazy(
537
    'move', 'bzrlib.smart.vfs', 'MoveRequest')
538
request_handlers.register_lazy(
539
    'put', 'bzrlib.smart.vfs', 'PutRequest')
540
request_handlers.register_lazy(
541
    'put_non_atomic', 'bzrlib.smart.vfs', 'PutNonAtomicRequest')
542
request_handlers.register_lazy(
543
    'readv', 'bzrlib.smart.vfs', 'ReadvRequest')
544
request_handlers.register_lazy(
545
    'rename', 'bzrlib.smart.vfs', 'RenameRequest')
3452.2.2 by Andrew Bennetts
Experimental PackRepository.{check_references,autopack} RPCs.
546
request_handlers.register_lazy(
547
    'PackRepository.autopack', 'bzrlib.smart.packrepository',
548
    'SmartServerPackRepositoryAutopack')
2018.10.2 by v.ladeuil+lp at free
gather_stats server side and request registration
549
request_handlers.register_lazy('Repository.gather_stats',
550
                               'bzrlib.smart.repository',
551
                               'SmartServerRepositoryGatherStats')
3842.3.3 by Andrew Bennetts
Remove RPC registration for VersionedFiles.get_parent_map, not Repository.get_parent_map.
552
request_handlers.register_lazy('Repository.get_parent_map',
553
                               'bzrlib.smart.repository',
554
                               'SmartServerRepositoryGetParentMap')
2018.5.37 by Andrew Bennetts
Make sure all the request handlers in bzrlib/smart/vfs.py have consistent names.
555
request_handlers.register_lazy(
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
556
    'Repository.get_revision_graph', 'bzrlib.smart.repository', 'SmartServerRepositoryGetRevisionGraph')
557
request_handlers.register_lazy(
2018.5.40 by Robert Collins
Implement a remote Repository.has_revision method.
558
    'Repository.has_revision', 'bzrlib.smart.repository', 'SmartServerRequestHasRevision')
559
request_handlers.register_lazy(
4022.1.6 by Robert Collins
Cherrypick and polish the RemoteSink for streaming push.
560
    'Repository.insert_stream', 'bzrlib.smart.repository', 'SmartServerRepositoryInsertStream')
561
request_handlers.register_lazy(
4476.3.82 by Andrew Bennetts
Mention another bug fix in NEWS, and update verb name, comments, and NEWS additions for landing on 1.19 rather than 1.18.
562
    'Repository.insert_stream_1.19', 'bzrlib.smart.repository', 'SmartServerRepositoryInsertStream_1_19')
4476.3.15 by Andrew Bennetts
Partially working fallback for pre-1.17 servers.
563
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.
564
    'Repository.insert_stream_locked', 'bzrlib.smart.repository', 'SmartServerRepositoryInsertStreamLocked')
565
request_handlers.register_lazy(
2018.5.57 by Robert Collins
Implement RemoteRepository.is_shared (Robert Collins, Vincent Ladeuil).
566
    'Repository.is_shared', 'bzrlib.smart.repository', 'SmartServerRepositoryIsShared')
567
request_handlers.register_lazy(
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
568
    'Repository.lock_write', 'bzrlib.smart.repository', 'SmartServerRepositoryLockWrite')
569
request_handlers.register_lazy(
4017.3.4 by Robert Collins
Create a verb for Repository.set_make_working_trees.
570
    'Repository.set_make_working_trees', 'bzrlib.smart.repository',
571
    'SmartServerRepositorySetMakeWorkingTrees')
572
request_handlers.register_lazy(
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
573
    'Repository.unlock', 'bzrlib.smart.repository', 'SmartServerRepositoryUnlock')
574
request_handlers.register_lazy(
4419.2.4 by Andrew Bennetts
Add Repository.get_rev_id_for_revno RPC, removes VFS calls from 'pull -r 123' case.
575
    'Repository.get_rev_id_for_revno', 'bzrlib.smart.repository',
576
    'SmartServerRepositoryGetRevIdForRevno')
577
request_handlers.register_lazy(
4060.1.5 by Robert Collins
Verb change name requested by Andrew.
578
    'Repository.get_stream', 'bzrlib.smart.repository',
579
    'SmartServerRepositoryGetStream')
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
580
request_handlers.register_lazy(
4476.3.82 by Andrew Bennetts
Mention another bug fix in NEWS, and update verb name, comments, and NEWS additions for landing on 1.19 rather than 1.18.
581
    'Repository.get_stream_1.19', 'bzrlib.smart.repository',
582
    'SmartServerRepositoryGetStream_1_19')
4476.3.29 by Andrew Bennetts
Add Repository.get_stream_1.18 verb.
583
request_handlers.register_lazy(
2018.18.1 by Martin Pool
Add stub Repository.tarball smart method
584
    'Repository.tarball', 'bzrlib.smart.repository',
585
    'SmartServerRepositoryTarball')
586
request_handlers.register_lazy(
2018.5.37 by Andrew Bennetts
Make sure all the request handlers in bzrlib/smart/vfs.py have consistent names.
587
    'rmdir', 'bzrlib.smart.vfs', 'RmdirRequest')
588
request_handlers.register_lazy(
589
    '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.
590
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.
591
    'Transport.is_readonly', 'bzrlib.smart.request', 'SmartServerIsReadonly')