~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/smart/request.py

  • Committer: Tarmac
  • Author(s): Vincent Ladeuil
  • Date: 2017-01-30 14:42:05 UTC
  • mfrom: (6620.1.1 trunk)
  • Revision ID: tarmac-20170130144205-r8fh2xpmiuxyozpv
Merge  2.7 into trunk including fix for bug #1657238 [r=vila]

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006, 2007 Canonical Ltd
 
1
# Copyright (C) 2006-2011 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
12
12
#
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
 
 
17
 
"""Basic server-side logic for dealing with requests."""
18
 
 
19
 
 
20
 
import tempfile
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
16
 
 
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).
 
27
"""
 
28
 
 
29
from __future__ import absolute_import
 
30
 
 
31
# XXX: The class names are a little confusing: the protocol will instantiate a
 
32
# SmartServerRequestHandler, whose dispatch_command method creates an instance
 
33
# of a SmartServerRequest subclass.
 
34
 
 
35
 
 
36
import threading
21
37
 
22
38
from bzrlib import (
23
 
    bzrdir,
 
39
    debug,
24
40
    errors,
 
41
    osutils,
25
42
    registry,
26
43
    revision,
 
44
    trace,
 
45
    urlutils,
27
46
    )
28
 
from bzrlib.bundle.serializer import write_bundle
 
47
from bzrlib.lazy_import import lazy_import
 
48
lazy_import(globals(), """
 
49
from bzrlib import bzrdir
 
50
from bzrlib.bundle import serializer
 
51
 
 
52
import tempfile
 
53
import thread
 
54
""")
 
55
 
 
56
 
 
57
jail_info = threading.local()
 
58
jail_info.transports = None
 
59
 
 
60
 
 
61
def _install_hook():
 
62
    bzrdir.BzrDir.hooks.install_named_hook(
 
63
        'pre_open', _pre_open_hook, 'checking server jail')
 
64
 
 
65
 
 
66
def _pre_open_hook(transport):
 
67
    allowed_transports = getattr(jail_info, 'transports', None)
 
68
    if allowed_transports is None:
 
69
        return
 
70
    abspath = transport.base
 
71
    for allowed_transport in allowed_transports:
 
72
        try:
 
73
            allowed_transport.relpath(abspath)
 
74
        except errors.PathNotChild:
 
75
            continue
 
76
        else:
 
77
            return
 
78
    raise errors.JailBreak(abspath)
 
79
 
 
80
 
 
81
_install_hook()
29
82
 
30
83
 
31
84
class SmartServerRequest(object):
32
 
    """Base class for request handlers."""
33
 
 
34
 
    def __init__(self, backing_transport):
 
85
    """Base class for request handlers.
 
86
 
 
87
    To define a new request, subclass this class and override the `do` method
 
88
    (and if appropriate, `do_body` as well).  Request implementors should take
 
89
    care to call `translate_client_path` and `transport_from_client_path` as
 
90
    appropriate when dealing with paths received from the client.
 
91
    """
 
92
    # XXX: rename this class to BaseSmartServerRequestHandler ?  A request
 
93
    # *handler* is a different concept to the request.
 
94
 
 
95
    def __init__(self, backing_transport, root_client_path='/', jail_root=None):
35
96
        """Constructor.
36
97
 
37
98
        :param backing_transport: the base transport to be used when performing
38
99
            this request.
 
100
        :param root_client_path: the client path that maps to the root of
 
101
            backing_transport.  This is used to interpret relpaths received
 
102
            from the client.  Clients will not be able to refer to paths above
 
103
            this root.  If root_client_path is None, then no translation will
 
104
            be performed on client paths.  Default is '/'.
 
105
        :param jail_root: if specified, the root of the BzrDir.open jail to use
 
106
            instead of backing_transport.
39
107
        """
40
108
        self._backing_transport = backing_transport
 
109
        if jail_root is None:
 
110
            jail_root = backing_transport
 
111
        self._jail_root = jail_root
 
112
        if root_client_path is not None:
 
113
            if not root_client_path.startswith('/'):
 
114
                root_client_path = '/' + root_client_path
 
115
            if not root_client_path.endswith('/'):
 
116
                root_client_path += '/'
 
117
        self._root_client_path = root_client_path
 
118
        self._body_chunks = []
41
119
 
42
120
    def _check_enabled(self):
43
121
        """Raises DisabledMethod if this method is disabled."""
45
123
 
46
124
    def do(self, *args):
47
125
        """Mandatory extension point for SmartServerRequest subclasses.
48
 
        
 
126
 
49
127
        Subclasses must implement this.
50
 
        
 
128
 
51
129
        This should return a SmartServerResponse if this command expects to
52
130
        receive no body.
53
131
        """
59
137
        It will return a SmartServerResponse if the command does not expect a
60
138
        body.
61
139
 
62
 
        :param *args: the arguments of the request.
 
140
        :param args: the arguments of the request.
63
141
        """
64
142
        self._check_enabled()
65
143
        return self.do(*args)
66
144
 
67
145
    def do_body(self, body_bytes):
68
146
        """Called if the client sends a body with the request.
69
 
        
 
147
 
 
148
        The do() method is still called, and must have returned None.
 
149
 
70
150
        Must return a SmartServerResponse.
71
151
        """
72
 
        # TODO: if a client erroneously sends a request that shouldn't have a
73
 
        # body, what to do?  Probably SmartServerRequestHandler should catch
74
 
        # this NotImplementedError and translate it into a 'bad request' error
75
 
        # to send to the client.
76
 
        raise NotImplementedError(self.do_body)
 
152
        if body_bytes != '':
 
153
            raise errors.SmartProtocolError('Request does not expect a body')
 
154
 
 
155
    def do_chunk(self, chunk_bytes):
 
156
        """Called with each body chunk if the request has a streamed body.
 
157
 
 
158
        The do() method is still called, and must have returned None.
 
159
        """
 
160
        self._body_chunks.append(chunk_bytes)
 
161
 
 
162
    def do_end(self):
 
163
        """Called when the end of the request has been received."""
 
164
        body_bytes = ''.join(self._body_chunks)
 
165
        self._body_chunks = None
 
166
        return self.do_body(body_bytes)
 
167
 
 
168
    def setup_jail(self):
 
169
        jail_info.transports = [self._jail_root]
 
170
 
 
171
    def teardown_jail(self):
 
172
        jail_info.transports = None
 
173
 
 
174
    def translate_client_path(self, client_path):
 
175
        """Translate a path received from a network client into a local
 
176
        relpath.
 
177
 
 
178
        All paths received from the client *must* be translated.
 
179
 
 
180
        :param client_path: the path from the client.
 
181
        :returns: a relpath that may be used with self._backing_transport
 
182
            (unlike the untranslated client_path, which must not be used with
 
183
            the backing transport).
 
184
        """
 
185
        if self._root_client_path is None:
 
186
            # no translation necessary!
 
187
            return client_path
 
188
        if not client_path.startswith('/'):
 
189
            client_path = '/' + client_path
 
190
        if client_path + '/' == self._root_client_path:
 
191
            return '.'
 
192
        if client_path.startswith(self._root_client_path):
 
193
            path = client_path[len(self._root_client_path):]
 
194
            relpath = urlutils.joinpath('/', path)
 
195
            if not relpath.startswith('/'):
 
196
                raise ValueError(relpath)
 
197
            return urlutils.escape('.' + relpath)
 
198
        else:
 
199
            raise errors.PathNotChild(client_path, self._root_client_path)
 
200
 
 
201
    def transport_from_client_path(self, client_path):
 
202
        """Get a backing transport corresponding to the location referred to by
 
203
        a network client.
 
204
 
 
205
        :seealso: translate_client_path
 
206
        :returns: a transport cloned from self._backing_transport
 
207
        """
 
208
        relpath = self.translate_client_path(client_path)
 
209
        return self._backing_transport.clone(relpath)
77
210
 
78
211
 
79
212
class SmartServerResponse(object):
80
213
    """A response to a client request.
81
 
    
 
214
 
82
215
    This base class should not be used. Instead use
83
216
    SuccessfulSmartServerResponse and FailedSmartServerResponse as appropriate.
84
217
    """
85
218
 
86
 
    def __init__(self, args, body=None):
 
219
    def __init__(self, args, body=None, body_stream=None):
 
220
        """Constructor.
 
221
 
 
222
        :param args: tuple of response arguments.
 
223
        :param body: string of a response body.
 
224
        :param body_stream: iterable of bytestrings to be streamed to the
 
225
            client.
 
226
        """
87
227
        self.args = args
 
228
        if body is not None and body_stream is not None:
 
229
            raise errors.BzrError(
 
230
                "'body' and 'body_stream' are mutually exclusive.")
88
231
        self.body = body
 
232
        self.body_stream = body_stream
89
233
 
90
234
    def __eq__(self, other):
91
235
        if other is None:
92
236
            return False
93
 
        return other.args == self.args and other.body == self.body
 
237
        return (other.args == self.args and
 
238
                other.body == self.body and
 
239
                other.body_stream is self.body_stream)
94
240
 
95
241
    def __repr__(self):
96
 
        return "<SmartServerResponse args=%r body=%r>" % (self.is_successful(), 
 
242
        return "<%s args=%r body=%r>" % (self.__class__.__name__,
97
243
            self.args, self.body)
98
244
 
99
245
 
115
261
 
116
262
class SmartServerRequestHandler(object):
117
263
    """Protocol logic for smart server.
118
 
    
 
264
 
119
265
    This doesn't handle serialization at all, it just processes requests and
120
266
    creates responses.
121
267
    """
130
276
    # TODO: Better way of representing the body for commands that take it,
131
277
    # and allow it to be streamed into the server.
132
278
 
133
 
    def __init__(self, backing_transport, commands):
 
279
    def __init__(self, backing_transport, commands, root_client_path,
 
280
        jail_root=None):
134
281
        """Constructor.
135
282
 
136
283
        :param backing_transport: a Transport to handle requests for.
138
285
            subclasses. e.g. bzrlib.transport.smart.vfs.vfs_commands.
139
286
        """
140
287
        self._backing_transport = backing_transport
 
288
        self._root_client_path = root_client_path
141
289
        self._commands = commands
142
 
        self._body_bytes = ''
 
290
        if jail_root is None:
 
291
            jail_root = backing_transport
 
292
        self._jail_root = jail_root
143
293
        self.response = None
144
294
        self.finished_reading = False
145
295
        self._command = None
 
296
        if 'hpss' in debug.debug_flags:
 
297
            self._request_start_time = osutils.timer_func()
 
298
            self._thread_id = thread.get_ident()
 
299
 
 
300
    def _trace(self, action, message, extra_bytes=None, include_time=False):
 
301
        # It is a bit of a shame that this functionality overlaps with that of 
 
302
        # ProtocolThreeRequester._trace. However, there is enough difference
 
303
        # that just putting it in a helper doesn't help a lot. And some state
 
304
        # is taken from the instance.
 
305
        if include_time:
 
306
            t = '%5.3fs ' % (osutils.timer_func() - self._request_start_time)
 
307
        else:
 
308
            t = ''
 
309
        if extra_bytes is None:
 
310
            extra = ''
 
311
        else:
 
312
            extra = ' ' + repr(extra_bytes[:40])
 
313
            if len(extra) > 33:
 
314
                extra = extra[:29] + extra[-1] + '...'
 
315
        trace.mutter('%12s: [%s] %s%s%s'
 
316
                     % (action, self._thread_id, t, message, extra))
146
317
 
147
318
    def accept_body(self, bytes):
148
319
        """Accept body data."""
149
 
 
150
 
        # TODO: This should be overriden for each command that desired body data
151
 
        # to handle the right format of that data, i.e. plain bytes, a bundle,
152
 
        # etc.  The deserialisation into that format should be done in the
153
 
        # Protocol object.
154
 
 
155
 
        # default fallback is to accumulate bytes.
156
 
        self._body_bytes += bytes
157
 
        
 
320
        if self._command is None:
 
321
            # no active command object, so ignore the event.
 
322
            return
 
323
        self._run_handler_code(self._command.do_chunk, (bytes,), {})
 
324
        if 'hpss' in debug.debug_flags:
 
325
            self._trace('accept body',
 
326
                        '%d bytes' % (len(bytes),), bytes)
 
327
 
158
328
    def end_of_body(self):
159
329
        """No more body data will be received."""
160
 
        self._run_handler_code(self._command.do_body, (self._body_bytes,), {})
 
330
        self._run_handler_code(self._command.do_end, (), {})
161
331
        # cannot read after this.
162
332
        self.finished_reading = True
163
 
 
164
 
    def dispatch_command(self, cmd, args):
165
 
        """Deprecated compatibility method.""" # XXX XXX
166
 
        try:
167
 
            command = self._commands.get(cmd)
168
 
        except LookupError:
169
 
            raise errors.SmartProtocolError("bad request %r" % (cmd,))
170
 
        self._command = command(self._backing_transport)
171
 
        self._run_handler_code(self._command.execute, args, {})
 
333
        if 'hpss' in debug.debug_flags:
 
334
            self._trace('end of body', '', include_time=True)
172
335
 
173
336
    def _run_handler_code(self, callable, args, kwargs):
174
337
        """Run some handler specific code 'callable'.
190
353
        # XXX: most of this error conversion is VFS-related, and thus ought to
191
354
        # be in SmartServerVFSRequestHandler somewhere.
192
355
        try:
193
 
            return callable(*args, **kwargs)
194
 
        except errors.NoSuchFile, e:
195
 
            return FailedSmartServerResponse(('NoSuchFile', e.path))
196
 
        except errors.FileExists, e:
197
 
            return FailedSmartServerResponse(('FileExists', e.path))
198
 
        except errors.DirectoryNotEmpty, e:
199
 
            return FailedSmartServerResponse(('DirectoryNotEmpty', e.path))
200
 
        except errors.ShortReadvError, e:
201
 
            return FailedSmartServerResponse(('ShortReadvError',
202
 
                e.path, str(e.offset), str(e.length), str(e.actual)))
203
 
        except UnicodeError, e:
204
 
            # If it is a DecodeError, than most likely we are starting
205
 
            # with a plain string
206
 
            str_or_unicode = e.object
207
 
            if isinstance(str_or_unicode, unicode):
208
 
                # XXX: UTF-8 might have \x01 (our seperator byte) in it.  We
209
 
                # should escape it somehow.
210
 
                val = 'u:' + str_or_unicode.encode('utf-8')
211
 
            else:
212
 
                val = 's:' + str_or_unicode.encode('base64')
213
 
            # This handles UnicodeEncodeError or UnicodeDecodeError
214
 
            return FailedSmartServerResponse((e.__class__.__name__,
215
 
                    e.encoding, val, str(e.start), str(e.end), e.reason))
216
 
        except errors.TransportNotPossible, e:
217
 
            if e.msg == "readonly transport":
218
 
                return FailedSmartServerResponse(('ReadOnlyError', ))
219
 
            else:
220
 
                raise
 
356
            self._command.setup_jail()
 
357
            try:
 
358
                return callable(*args, **kwargs)
 
359
            finally:
 
360
                self._command.teardown_jail()
 
361
        except (KeyboardInterrupt, SystemExit):
 
362
            raise
 
363
        except Exception, err:
 
364
            err_struct = _translate_error(err)
 
365
            return FailedSmartServerResponse(err_struct)
 
366
 
 
367
    def headers_received(self, headers):
 
368
        # Just a no-op at the moment.
 
369
        if 'hpss' in debug.debug_flags:
 
370
            self._trace('headers', repr(headers))
 
371
 
 
372
    def args_received(self, args):
 
373
        cmd = args[0]
 
374
        args = args[1:]
 
375
        try:
 
376
            command = self._commands.get(cmd)
 
377
        except LookupError:
 
378
            if 'hpss' in debug.debug_flags:
 
379
                self._trace('hpss unknown request', 
 
380
                            cmd, repr(args)[1:-1])
 
381
            raise errors.UnknownSmartMethod(cmd)
 
382
        if 'hpss' in debug.debug_flags:
 
383
            from bzrlib.smart import vfs
 
384
            if issubclass(command, vfs.VfsRequest):
 
385
                action = 'hpss vfs req'
 
386
            else:
 
387
                action = 'hpss request'
 
388
            self._trace(action, 
 
389
                        '%s %s' % (cmd, repr(args)[1:-1]))
 
390
        self._command = command(
 
391
            self._backing_transport, self._root_client_path, self._jail_root)
 
392
        self._run_handler_code(self._command.execute, args, {})
 
393
 
 
394
    def end_received(self):
 
395
        if self._command is None:
 
396
            # no active command object, so ignore the event.
 
397
            return
 
398
        self._run_handler_code(self._command.do_end, (), {})
 
399
        if 'hpss' in debug.debug_flags:
 
400
            self._trace('end', '', include_time=True)
 
401
 
 
402
    def post_body_error_received(self, error_args):
 
403
        # Just a no-op at the moment.
 
404
        pass
 
405
 
 
406
 
 
407
def _translate_error(err):
 
408
    if isinstance(err, errors.NoSuchFile):
 
409
        return ('NoSuchFile', err.path)
 
410
    elif isinstance(err, errors.FileExists):
 
411
        return ('FileExists', err.path)
 
412
    elif isinstance(err, errors.DirectoryNotEmpty):
 
413
        return ('DirectoryNotEmpty', err.path)
 
414
    elif isinstance(err, errors.IncompatibleRepositories):
 
415
        return ('IncompatibleRepositories', str(err.source), str(err.target),
 
416
            str(err.details))
 
417
    elif isinstance(err, errors.ShortReadvError):
 
418
        return ('ShortReadvError', err.path, str(err.offset), str(err.length),
 
419
                str(err.actual))
 
420
    elif isinstance(err, errors.RevisionNotPresent):
 
421
        return ('RevisionNotPresent', err.revision_id, err.file_id)
 
422
    elif isinstance(err, errors.UnstackableRepositoryFormat):
 
423
        return (('UnstackableRepositoryFormat', str(err.format), err.url))
 
424
    elif isinstance(err, errors.UnstackableBranchFormat):
 
425
        return ('UnstackableBranchFormat', str(err.format), err.url)
 
426
    elif isinstance(err, errors.NotStacked):
 
427
        return ('NotStacked',)
 
428
    elif isinstance(err, errors.BzrCheckError):
 
429
        return ('BzrCheckError', err.msg)
 
430
    elif isinstance(err, UnicodeError):
 
431
        # If it is a DecodeError, than most likely we are starting
 
432
        # with a plain string
 
433
        str_or_unicode = err.object
 
434
        if isinstance(str_or_unicode, unicode):
 
435
            # XXX: UTF-8 might have \x01 (our protocol v1 and v2 seperator
 
436
            # byte) in it, so this encoding could cause broken responses.
 
437
            # Newer clients use protocol v3, so will be fine.
 
438
            val = 'u:' + str_or_unicode.encode('utf-8')
 
439
        else:
 
440
            val = 's:' + str_or_unicode.encode('base64')
 
441
        # This handles UnicodeEncodeError or UnicodeDecodeError
 
442
        return (err.__class__.__name__, err.encoding, val, str(err.start),
 
443
                str(err.end), err.reason)
 
444
    elif isinstance(err, errors.TransportNotPossible):
 
445
        if err.msg == "readonly transport":
 
446
            return ('ReadOnlyError', )
 
447
    elif isinstance(err, errors.ReadError):
 
448
        # cannot read the file
 
449
        return ('ReadError', err.path)
 
450
    elif isinstance(err, errors.PermissionDenied):
 
451
        return ('PermissionDenied', err.path, err.extra)
 
452
    elif isinstance(err, errors.TokenMismatch):
 
453
        return ('TokenMismatch', err.given_token, err.lock_token)
 
454
    elif isinstance(err, errors.LockContention):
 
455
        return ('LockContention',)
 
456
    elif isinstance(err, MemoryError):
 
457
        # GZ 2011-02-24: Copy bzrlib.trace -Dmem_dump functionality here?
 
458
        return ('MemoryError',)
 
459
    # Unserialisable error.  Log it, and return a generic error
 
460
    trace.log_exception_quietly()
 
461
    return ('error', trace._qualified_exception_name(err.__class__, True),
 
462
        str(err))
221
463
 
222
464
 
223
465
class HelloRequest(SmartServerRequest):
234
476
 
235
477
    def do(self, path, revision_id):
236
478
        # open transport relative to our base
237
 
        t = self._backing_transport.clone(path)
 
479
        t = self.transport_from_client_path(path)
238
480
        control, extra_path = bzrdir.BzrDir.open_containing_from_transport(t)
239
481
        repo = control.open_repository()
240
482
        tmpf = tempfile.TemporaryFile()
241
483
        base_revision = revision.NULL_REVISION
242
 
        write_bundle(repo, revision_id, base_revision, tmpf)
 
484
        serializer.write_bundle(repo, revision_id, base_revision, tmpf)
243
485
        tmpf.seek(0)
244
486
        return SuccessfulSmartServerResponse((), tmpf.read())
245
487
 
255
497
        return SuccessfulSmartServerResponse((answer,))
256
498
 
257
499
 
 
500
# In the 'info' attribute, we store whether this request is 'safe' to retry if
 
501
# we get a disconnect while reading the response. It can have the values:
 
502
#   read    This is purely a read request, so retrying it is perfectly ok.
 
503
#   idem    An idempotent write request. Something like 'put' where if you put
 
504
#           the same bytes twice you end up with the same final bytes.
 
505
#   semi    This is a request that isn't strictly idempotent, but doesn't
 
506
#           result in corruption if it is retried. This is for things like
 
507
#           'lock' and 'unlock'. If you call lock, it updates the disk
 
508
#           structure. If you fail to read the response, you won't be able to
 
509
#           use the lock, because you don't have the lock token. Calling lock
 
510
#           again will fail, because the lock is already taken. However, we
 
511
#           can't tell if the server received our request or not. If it didn't,
 
512
#           then retrying the request is fine, as it will actually do what we
 
513
#           want. If it did, we will interrupt the current operation, but we
 
514
#           are no worse off than interrupting the current operation because of
 
515
#           a ConnectionReset.
 
516
#   semivfs Similar to semi, but specific to a Virtual FileSystem request.
 
517
#   stream  This is a request that takes a stream that cannot be restarted if
 
518
#           consumed. This request is 'safe' in that if we determine the
 
519
#           connection is closed before we consume the stream, we can try
 
520
#           again.
 
521
#   mutate  State is updated in a way that replaying that request results in a
 
522
#           different state. For example 'append' writes more bytes to a given
 
523
#           file. If append succeeds, it moves the file pointer.
258
524
request_handlers = registry.Registry()
259
525
request_handlers.register_lazy(
260
 
    'append', 'bzrlib.smart.vfs', 'AppendRequest')
261
 
request_handlers.register_lazy(
262
 
    'Branch.get_config_file', 'bzrlib.smart.branch', 'SmartServerBranchGetConfigFile')
263
 
request_handlers.register_lazy(
264
 
    'Branch.last_revision_info', 'bzrlib.smart.branch', 'SmartServerBranchRequestLastRevisionInfo')
265
 
request_handlers.register_lazy(
266
 
    'Branch.lock_write', 'bzrlib.smart.branch', 'SmartServerBranchRequestLockWrite')
267
 
request_handlers.register_lazy(
268
 
    'Branch.revision_history', 'bzrlib.smart.branch', 'SmartServerRequestRevisionHistory')
269
 
request_handlers.register_lazy(
270
 
    'Branch.set_last_revision', 'bzrlib.smart.branch', 'SmartServerBranchRequestSetLastRevision')
271
 
request_handlers.register_lazy(
272
 
    'Branch.unlock', 'bzrlib.smart.branch', 'SmartServerBranchRequestUnlock')
273
 
request_handlers.register_lazy(
274
 
    'BzrDir.find_repository', 'bzrlib.smart.bzrdir', 'SmartServerRequestFindRepository')
275
 
request_handlers.register_lazy(
276
 
    'BzrDirFormat.initialize', 'bzrlib.smart.bzrdir', 'SmartServerRequestInitializeBzrDir')
277
 
request_handlers.register_lazy(
278
 
    'BzrDir.open_branch', 'bzrlib.smart.bzrdir', 'SmartServerRequestOpenBranch')
279
 
request_handlers.register_lazy(
280
 
    'delete', 'bzrlib.smart.vfs', 'DeleteRequest')
281
 
request_handlers.register_lazy(
282
 
    'get', 'bzrlib.smart.vfs', 'GetRequest')
283
 
request_handlers.register_lazy(
284
 
    'get_bundle', 'bzrlib.smart.request', 'GetBundleRequest')
285
 
request_handlers.register_lazy(
286
 
    'has', 'bzrlib.smart.vfs', 'HasRequest')
287
 
request_handlers.register_lazy(
288
 
    'hello', 'bzrlib.smart.request', 'HelloRequest')
289
 
request_handlers.register_lazy(
290
 
    'iter_files_recursive', 'bzrlib.smart.vfs', 'IterFilesRecursiveRequest')
291
 
request_handlers.register_lazy(
292
 
    'list_dir', 'bzrlib.smart.vfs', 'ListDirRequest')
293
 
request_handlers.register_lazy(
294
 
    'mkdir', 'bzrlib.smart.vfs', 'MkdirRequest')
295
 
request_handlers.register_lazy(
296
 
    'move', 'bzrlib.smart.vfs', 'MoveRequest')
297
 
request_handlers.register_lazy(
298
 
    'put', 'bzrlib.smart.vfs', 'PutRequest')
299
 
request_handlers.register_lazy(
300
 
    'put_non_atomic', 'bzrlib.smart.vfs', 'PutNonAtomicRequest')
301
 
request_handlers.register_lazy(
302
 
    'readv', 'bzrlib.smart.vfs', 'ReadvRequest')
303
 
request_handlers.register_lazy(
304
 
    'rename', 'bzrlib.smart.vfs', 'RenameRequest')
305
 
request_handlers.register_lazy('Repository.gather_stats',
306
 
                               'bzrlib.smart.repository',
307
 
                               'SmartServerRepositoryGatherStats')
308
 
request_handlers.register_lazy(
309
 
    'Repository.get_revision_graph', 'bzrlib.smart.repository', 'SmartServerRepositoryGetRevisionGraph')
310
 
request_handlers.register_lazy(
311
 
    'Repository.has_revision', 'bzrlib.smart.repository', 'SmartServerRequestHasRevision')
312
 
request_handlers.register_lazy(
313
 
    'Repository.is_shared', 'bzrlib.smart.repository', 'SmartServerRepositoryIsShared')
314
 
request_handlers.register_lazy(
315
 
    'Repository.lock_write', 'bzrlib.smart.repository', 'SmartServerRepositoryLockWrite')
316
 
request_handlers.register_lazy(
317
 
    'Repository.unlock', 'bzrlib.smart.repository', 'SmartServerRepositoryUnlock')
 
526
    'append', 'bzrlib.smart.vfs', 'AppendRequest', info='mutate')
 
527
request_handlers.register_lazy(
 
528
    'Branch.break_lock', 'bzrlib.smart.branch',
 
529
    'SmartServerBranchBreakLock', info='idem')
 
530
request_handlers.register_lazy(
 
531
    'Branch.get_config_file', 'bzrlib.smart.branch',
 
532
    'SmartServerBranchGetConfigFile', info='read')
 
533
request_handlers.register_lazy(
 
534
    'Branch.get_parent', 'bzrlib.smart.branch', 'SmartServerBranchGetParent',
 
535
    info='read')
 
536
request_handlers.register_lazy(
 
537
    'Branch.put_config_file', 'bzrlib.smart.branch',
 
538
    'SmartServerBranchPutConfigFile', info='idem')
 
539
request_handlers.register_lazy(
 
540
    'Branch.get_tags_bytes', 'bzrlib.smart.branch',
 
541
    'SmartServerBranchGetTagsBytes', info='read')
 
542
request_handlers.register_lazy(
 
543
    'Branch.set_tags_bytes', 'bzrlib.smart.branch',
 
544
    'SmartServerBranchSetTagsBytes', info='idem')
 
545
request_handlers.register_lazy(
 
546
    'Branch.heads_to_fetch', 'bzrlib.smart.branch',
 
547
    'SmartServerBranchHeadsToFetch', info='read')
 
548
request_handlers.register_lazy(
 
549
    'Branch.get_stacked_on_url', 'bzrlib.smart.branch',
 
550
    'SmartServerBranchRequestGetStackedOnURL', info='read')
 
551
request_handlers.register_lazy(
 
552
    'Branch.get_physical_lock_status', 'bzrlib.smart.branch',
 
553
    'SmartServerBranchRequestGetPhysicalLockStatus', info='read')
 
554
request_handlers.register_lazy(
 
555
    'Branch.last_revision_info', 'bzrlib.smart.branch',
 
556
    'SmartServerBranchRequestLastRevisionInfo', info='read')
 
557
request_handlers.register_lazy(
 
558
    'Branch.lock_write', 'bzrlib.smart.branch',
 
559
    'SmartServerBranchRequestLockWrite', info='semi')
 
560
request_handlers.register_lazy(
 
561
    'Branch.revision_history', 'bzrlib.smart.branch',
 
562
    'SmartServerRequestRevisionHistory', info='read')
 
563
request_handlers.register_lazy(
 
564
    'Branch.set_config_option', 'bzrlib.smart.branch',
 
565
    'SmartServerBranchRequestSetConfigOption', info='idem')
 
566
request_handlers.register_lazy(
 
567
    'Branch.set_config_option_dict', 'bzrlib.smart.branch',
 
568
    'SmartServerBranchRequestSetConfigOptionDict', info='idem')
 
569
request_handlers.register_lazy(
 
570
    'Branch.set_last_revision', 'bzrlib.smart.branch',
 
571
    'SmartServerBranchRequestSetLastRevision', info='idem')
 
572
request_handlers.register_lazy(
 
573
    'Branch.set_last_revision_info', 'bzrlib.smart.branch',
 
574
    'SmartServerBranchRequestSetLastRevisionInfo', info='idem')
 
575
request_handlers.register_lazy(
 
576
    'Branch.set_last_revision_ex', 'bzrlib.smart.branch',
 
577
    'SmartServerBranchRequestSetLastRevisionEx', info='idem')
 
578
request_handlers.register_lazy(
 
579
    'Branch.set_parent_location', 'bzrlib.smart.branch',
 
580
    'SmartServerBranchRequestSetParentLocation', info='idem')
 
581
request_handlers.register_lazy(
 
582
    'Branch.unlock', 'bzrlib.smart.branch',
 
583
    'SmartServerBranchRequestUnlock', info='semi')
 
584
request_handlers.register_lazy(
 
585
    'Branch.revision_id_to_revno', 'bzrlib.smart.branch',
 
586
    'SmartServerBranchRequestRevisionIdToRevno', info='read')
 
587
request_handlers.register_lazy(
 
588
    'BzrDir.checkout_metadir', 'bzrlib.smart.bzrdir',
 
589
    'SmartServerBzrDirRequestCheckoutMetaDir', info='read')
 
590
request_handlers.register_lazy(
 
591
    'BzrDir.cloning_metadir', 'bzrlib.smart.bzrdir',
 
592
    'SmartServerBzrDirRequestCloningMetaDir', info='read')
 
593
request_handlers.register_lazy(
 
594
    'BzrDir.create_branch', 'bzrlib.smart.bzrdir',
 
595
    'SmartServerRequestCreateBranch', info='semi')
 
596
request_handlers.register_lazy(
 
597
    'BzrDir.create_repository', 'bzrlib.smart.bzrdir',
 
598
    'SmartServerRequestCreateRepository', info='semi')
 
599
request_handlers.register_lazy(
 
600
    'BzrDir.find_repository', 'bzrlib.smart.bzrdir',
 
601
    'SmartServerRequestFindRepositoryV1', info='read')
 
602
request_handlers.register_lazy(
 
603
    'BzrDir.find_repositoryV2', 'bzrlib.smart.bzrdir',
 
604
    'SmartServerRequestFindRepositoryV2', info='read')
 
605
request_handlers.register_lazy(
 
606
    'BzrDir.find_repositoryV3', 'bzrlib.smart.bzrdir',
 
607
    'SmartServerRequestFindRepositoryV3', info='read')
 
608
request_handlers.register_lazy(
 
609
    'BzrDir.get_branches', 'bzrlib.smart.bzrdir',
 
610
    'SmartServerBzrDirRequestGetBranches', info='read')
 
611
request_handlers.register_lazy(
 
612
    'BzrDir.get_config_file', 'bzrlib.smart.bzrdir',
 
613
    'SmartServerBzrDirRequestConfigFile', info='read')
 
614
request_handlers.register_lazy(
 
615
    'BzrDir.destroy_branch', 'bzrlib.smart.bzrdir',
 
616
    'SmartServerBzrDirRequestDestroyBranch', info='semi')
 
617
request_handlers.register_lazy(
 
618
    'BzrDir.destroy_repository', 'bzrlib.smart.bzrdir',
 
619
    'SmartServerBzrDirRequestDestroyRepository', info='semi')
 
620
request_handlers.register_lazy(
 
621
    'BzrDir.has_workingtree', 'bzrlib.smart.bzrdir',
 
622
    'SmartServerBzrDirRequestHasWorkingTree', info='read')
 
623
request_handlers.register_lazy(
 
624
    'BzrDirFormat.initialize', 'bzrlib.smart.bzrdir',
 
625
    'SmartServerRequestInitializeBzrDir', info='semi')
 
626
request_handlers.register_lazy(
 
627
    'BzrDirFormat.initialize_ex_1.16', 'bzrlib.smart.bzrdir',
 
628
    'SmartServerRequestBzrDirInitializeEx', info='semi')
 
629
request_handlers.register_lazy(
 
630
    'BzrDir.open', 'bzrlib.smart.bzrdir', 'SmartServerRequestOpenBzrDir',
 
631
    info='read')
 
632
request_handlers.register_lazy(
 
633
    'BzrDir.open_2.1', 'bzrlib.smart.bzrdir',
 
634
    'SmartServerRequestOpenBzrDir_2_1', info='read')
 
635
request_handlers.register_lazy(
 
636
    'BzrDir.open_branch', 'bzrlib.smart.bzrdir',
 
637
    'SmartServerRequestOpenBranch', info='read')
 
638
request_handlers.register_lazy(
 
639
    'BzrDir.open_branchV2', 'bzrlib.smart.bzrdir',
 
640
    'SmartServerRequestOpenBranchV2', info='read')
 
641
request_handlers.register_lazy(
 
642
    'BzrDir.open_branchV3', 'bzrlib.smart.bzrdir',
 
643
    'SmartServerRequestOpenBranchV3', info='read')
 
644
request_handlers.register_lazy(
 
645
    'delete', 'bzrlib.smart.vfs', 'DeleteRequest', info='semivfs')
 
646
request_handlers.register_lazy(
 
647
    'get', 'bzrlib.smart.vfs', 'GetRequest', info='read')
 
648
request_handlers.register_lazy(
 
649
    'get_bundle', 'bzrlib.smart.request', 'GetBundleRequest', info='read')
 
650
request_handlers.register_lazy(
 
651
    'has', 'bzrlib.smart.vfs', 'HasRequest', info='read')
 
652
request_handlers.register_lazy(
 
653
    'hello', 'bzrlib.smart.request', 'HelloRequest', info='read')
 
654
request_handlers.register_lazy(
 
655
    'iter_files_recursive', 'bzrlib.smart.vfs', 'IterFilesRecursiveRequest',
 
656
    info='read')
 
657
request_handlers.register_lazy(
 
658
    'list_dir', 'bzrlib.smart.vfs', 'ListDirRequest', info='read')
 
659
request_handlers.register_lazy(
 
660
    'mkdir', 'bzrlib.smart.vfs', 'MkdirRequest', info='semivfs')
 
661
request_handlers.register_lazy(
 
662
    'move', 'bzrlib.smart.vfs', 'MoveRequest', info='semivfs')
 
663
request_handlers.register_lazy(
 
664
    'put', 'bzrlib.smart.vfs', 'PutRequest', info='idem')
 
665
request_handlers.register_lazy(
 
666
    'put_non_atomic', 'bzrlib.smart.vfs', 'PutNonAtomicRequest', info='idem')
 
667
request_handlers.register_lazy(
 
668
    'readv', 'bzrlib.smart.vfs', 'ReadvRequest', info='read')
 
669
request_handlers.register_lazy(
 
670
    'rename', 'bzrlib.smart.vfs', 'RenameRequest', info='semivfs')
 
671
request_handlers.register_lazy(
 
672
    'Repository.add_signature_text', 'bzrlib.smart.repository',
 
673
    'SmartServerRepositoryAddSignatureText', info='idem')
 
674
request_handlers.register_lazy(
 
675
    'Repository.all_revision_ids', 'bzrlib.smart.repository',
 
676
    'SmartServerRepositoryAllRevisionIds', info='read')
 
677
request_handlers.register_lazy(
 
678
    'PackRepository.autopack', 'bzrlib.smart.packrepository',
 
679
    'SmartServerPackRepositoryAutopack', info='idem')
 
680
request_handlers.register_lazy(
 
681
    'Repository.break_lock', 'bzrlib.smart.repository',
 
682
    'SmartServerRepositoryBreakLock', info='idem')
 
683
request_handlers.register_lazy(
 
684
    'Repository.gather_stats', 'bzrlib.smart.repository',
 
685
    'SmartServerRepositoryGatherStats', info='read')
 
686
request_handlers.register_lazy(
 
687
    'Repository.get_parent_map', 'bzrlib.smart.repository',
 
688
    'SmartServerRepositoryGetParentMap', info='read')
 
689
request_handlers.register_lazy(
 
690
    'Repository.get_revision_graph', 'bzrlib.smart.repository',
 
691
    'SmartServerRepositoryGetRevisionGraph', info='read')
 
692
request_handlers.register_lazy(
 
693
    'Repository.get_revision_signature_text', 'bzrlib.smart.repository',
 
694
    'SmartServerRepositoryGetRevisionSignatureText', info='read')
 
695
request_handlers.register_lazy(
 
696
    'Repository.has_revision', 'bzrlib.smart.repository',
 
697
    'SmartServerRequestHasRevision', info='read')
 
698
request_handlers.register_lazy(
 
699
    'Repository.has_signature_for_revision_id', 'bzrlib.smart.repository',
 
700
    'SmartServerRequestHasSignatureForRevisionId', info='read')
 
701
request_handlers.register_lazy(
 
702
    'Repository.insert_stream', 'bzrlib.smart.repository',
 
703
    'SmartServerRepositoryInsertStream', info='stream')
 
704
request_handlers.register_lazy(
 
705
    'Repository.insert_stream_1.19', 'bzrlib.smart.repository',
 
706
    'SmartServerRepositoryInsertStream_1_19', info='stream')
 
707
request_handlers.register_lazy(
 
708
    'Repository.insert_stream_locked', 'bzrlib.smart.repository',
 
709
    'SmartServerRepositoryInsertStreamLocked', info='stream')
 
710
request_handlers.register_lazy(
 
711
    'Repository.is_shared', 'bzrlib.smart.repository',
 
712
    'SmartServerRepositoryIsShared', info='read')
 
713
request_handlers.register_lazy(
 
714
    'Repository.iter_files_bytes', 'bzrlib.smart.repository',
 
715
    'SmartServerRepositoryIterFilesBytes', info='read')
 
716
request_handlers.register_lazy(
 
717
    'Repository.lock_write', 'bzrlib.smart.repository',
 
718
    'SmartServerRepositoryLockWrite', info='semi')
 
719
request_handlers.register_lazy(
 
720
    'Repository.make_working_trees', 'bzrlib.smart.repository',
 
721
    'SmartServerRepositoryMakeWorkingTrees', info='read')
 
722
request_handlers.register_lazy(
 
723
    'Repository.set_make_working_trees', 'bzrlib.smart.repository',
 
724
    'SmartServerRepositorySetMakeWorkingTrees', info='idem')
 
725
request_handlers.register_lazy(
 
726
    'Repository.unlock', 'bzrlib.smart.repository',
 
727
    'SmartServerRepositoryUnlock', info='semi')
 
728
request_handlers.register_lazy(
 
729
    'Repository.get_physical_lock_status', 'bzrlib.smart.repository',
 
730
    'SmartServerRepositoryGetPhysicalLockStatus', info='read')
 
731
request_handlers.register_lazy(
 
732
    'Repository.get_rev_id_for_revno', 'bzrlib.smart.repository',
 
733
    'SmartServerRepositoryGetRevIdForRevno', info='read')
 
734
request_handlers.register_lazy(
 
735
    'Repository.get_stream', 'bzrlib.smart.repository',
 
736
    'SmartServerRepositoryGetStream', info='read')
 
737
request_handlers.register_lazy(
 
738
    'Repository.get_stream_1.19', 'bzrlib.smart.repository',
 
739
    'SmartServerRepositoryGetStream_1_19', info='read')
 
740
request_handlers.register_lazy(
 
741
    'Repository.iter_revisions', 'bzrlib.smart.repository',
 
742
    'SmartServerRepositoryIterRevisions', info='read')
 
743
request_handlers.register_lazy(
 
744
    'Repository.pack', 'bzrlib.smart.repository',
 
745
    'SmartServerRepositoryPack', info='idem')
 
746
request_handlers.register_lazy(
 
747
    'Repository.start_write_group', 'bzrlib.smart.repository',
 
748
    'SmartServerRepositoryStartWriteGroup', info='semi')
 
749
request_handlers.register_lazy(
 
750
    'Repository.commit_write_group', 'bzrlib.smart.repository',
 
751
    'SmartServerRepositoryCommitWriteGroup', info='semi')
 
752
request_handlers.register_lazy(
 
753
    'Repository.abort_write_group', 'bzrlib.smart.repository',
 
754
    'SmartServerRepositoryAbortWriteGroup', info='semi')
 
755
request_handlers.register_lazy(
 
756
    'Repository.check_write_group', 'bzrlib.smart.repository',
 
757
    'SmartServerRepositoryCheckWriteGroup', info='read')
 
758
request_handlers.register_lazy(
 
759
    'Repository.reconcile', 'bzrlib.smart.repository',
 
760
    'SmartServerRepositoryReconcile', info='idem')
318
761
request_handlers.register_lazy(
319
762
    'Repository.tarball', 'bzrlib.smart.repository',
320
 
    'SmartServerRepositoryTarball')
321
 
request_handlers.register_lazy(
322
 
    'rmdir', 'bzrlib.smart.vfs', 'RmdirRequest')
323
 
request_handlers.register_lazy(
324
 
    'stat', 'bzrlib.smart.vfs', 'StatRequest')
325
 
request_handlers.register_lazy(
326
 
    'Transport.is_readonly', 'bzrlib.smart.request', 'SmartServerIsReadonly')
327
 
request_handlers.register_lazy(
328
 
    'BzrDir.open', 'bzrlib.smart.bzrdir', 'SmartServerRequestOpenBzrDir')
 
763
    'SmartServerRepositoryTarball', info='read')
 
764
request_handlers.register_lazy(
 
765
    'VersionedFileRepository.get_serializer_format', 'bzrlib.smart.repository',
 
766
    'SmartServerRepositoryGetSerializerFormat', info='read')
 
767
request_handlers.register_lazy(
 
768
    'VersionedFileRepository.get_inventories', 'bzrlib.smart.repository',
 
769
    'SmartServerRepositoryGetInventories', info='read')
 
770
request_handlers.register_lazy(
 
771
    'rmdir', 'bzrlib.smart.vfs', 'RmdirRequest', info='semivfs')
 
772
request_handlers.register_lazy(
 
773
    'stat', 'bzrlib.smart.vfs', 'StatRequest', info='read')
 
774
request_handlers.register_lazy(
 
775
    'Transport.is_readonly', 'bzrlib.smart.request',
 
776
    'SmartServerIsReadonly', info='read')