~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
 
**XXX**:
20
 
 
21
 
The class names are a little confusing: the protocol will instantiate a
22
 
SmartServerRequestHandler, whose dispatch_command method creates an instance of
23
 
a SmartServerRequest subclass.
24
 
 
25
 
The request_handlers registry tracks SmartServerRequest classes (rather than
26
 
SmartServerRequestHandler).
 
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
27
"""
28
28
 
29
 
import tempfile
 
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
30
37
 
31
38
from bzrlib import (
32
 
    bzrdir,
 
39
    debug,
33
40
    errors,
 
41
    osutils,
34
42
    registry,
35
43
    revision,
 
44
    trace,
36
45
    urlutils,
37
46
    )
38
47
from bzrlib.lazy_import import lazy_import
39
48
lazy_import(globals(), """
 
49
from bzrlib import bzrdir
40
50
from bzrlib.bundle import serializer
 
51
 
 
52
import tempfile
 
53
import thread
41
54
""")
42
55
 
43
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()
 
82
 
 
83
 
44
84
class SmartServerRequest(object):
45
85
    """Base class for request handlers.
46
 
    
 
86
 
47
87
    To define a new request, subclass this class and override the `do` method
48
88
    (and if appropriate, `do_body` as well).  Request implementors should take
49
89
    care to call `translate_client_path` and `transport_from_client_path` as
52
92
    # XXX: rename this class to BaseSmartServerRequestHandler ?  A request
53
93
    # *handler* is a different concept to the request.
54
94
 
55
 
    def __init__(self, backing_transport, root_client_path='/'):
 
95
    def __init__(self, backing_transport, root_client_path='/', jail_root=None):
56
96
        """Constructor.
57
97
 
58
98
        :param backing_transport: the base transport to be used when performing
62
102
            from the client.  Clients will not be able to refer to paths above
63
103
            this root.  If root_client_path is None, then no translation will
64
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.
65
107
        """
66
108
        self._backing_transport = backing_transport
 
109
        if jail_root is None:
 
110
            jail_root = backing_transport
 
111
        self._jail_root = jail_root
67
112
        if root_client_path is not None:
68
113
            if not root_client_path.startswith('/'):
69
114
                root_client_path = '/' + root_client_path
70
115
            if not root_client_path.endswith('/'):
71
116
                root_client_path += '/'
72
117
        self._root_client_path = root_client_path
 
118
        self._body_chunks = []
73
119
 
74
120
    def _check_enabled(self):
75
121
        """Raises DisabledMethod if this method is disabled."""
77
123
 
78
124
    def do(self, *args):
79
125
        """Mandatory extension point for SmartServerRequest subclasses.
80
 
        
 
126
 
81
127
        Subclasses must implement this.
82
 
        
 
128
 
83
129
        This should return a SmartServerResponse if this command expects to
84
130
        receive no body.
85
131
        """
91
137
        It will return a SmartServerResponse if the command does not expect a
92
138
        body.
93
139
 
94
 
        :param *args: the arguments of the request.
 
140
        :param args: the arguments of the request.
95
141
        """
96
142
        self._check_enabled()
97
143
        return self.do(*args)
100
146
        """Called if the client sends a body with the request.
101
147
 
102
148
        The do() method is still called, and must have returned None.
103
 
        
 
149
 
104
150
        Must return a SmartServerResponse.
105
151
        """
106
 
        raise NotImplementedError(self.do_body)
 
152
        if body_bytes != '':
 
153
            raise errors.SmartProtocolError('Request does not expect a body')
107
154
 
108
155
    def do_chunk(self, chunk_bytes):
109
156
        """Called with each body chunk if the request has a streamed body.
110
157
 
111
158
        The do() method is still called, and must have returned None.
112
159
        """
113
 
        raise NotImplementedError(self.do_chunk)
 
160
        self._body_chunks.append(chunk_bytes)
114
161
 
115
162
    def do_end(self):
116
163
        """Called when the end of the request has been received."""
117
 
        pass
118
 
    
 
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
 
119
174
    def translate_client_path(self, client_path):
120
175
        """Translate a path received from a network client into a local
121
176
        relpath.
132
187
            return client_path
133
188
        if not client_path.startswith('/'):
134
189
            client_path = '/' + client_path
 
190
        if client_path + '/' == self._root_client_path:
 
191
            return '.'
135
192
        if client_path.startswith(self._root_client_path):
136
193
            path = client_path[len(self._root_client_path):]
137
194
            relpath = urlutils.joinpath('/', path)
138
195
            if not relpath.startswith('/'):
139
196
                raise ValueError(relpath)
140
 
            return '.' + relpath
 
197
            return urlutils.escape('.' + relpath)
141
198
        else:
142
199
            raise errors.PathNotChild(client_path, self._root_client_path)
143
200
 
154
211
 
155
212
class SmartServerResponse(object):
156
213
    """A response to a client request.
157
 
    
 
214
 
158
215
    This base class should not be used. Instead use
159
216
    SuccessfulSmartServerResponse and FailedSmartServerResponse as appropriate.
160
217
    """
204
261
 
205
262
class SmartServerRequestHandler(object):
206
263
    """Protocol logic for smart server.
207
 
    
 
264
 
208
265
    This doesn't handle serialization at all, it just processes requests and
209
266
    creates responses.
210
267
    """
219
276
    # TODO: Better way of representing the body for commands that take it,
220
277
    # and allow it to be streamed into the server.
221
278
 
222
 
    def __init__(self, backing_transport, commands, root_client_path):
 
279
    def __init__(self, backing_transport, commands, root_client_path,
 
280
        jail_root=None):
223
281
        """Constructor.
224
282
 
225
283
        :param backing_transport: a Transport to handle requests for.
229
287
        self._backing_transport = backing_transport
230
288
        self._root_client_path = root_client_path
231
289
        self._commands = commands
232
 
        self._body_bytes = ''
 
290
        if jail_root is None:
 
291
            jail_root = backing_transport
 
292
        self._jail_root = jail_root
233
293
        self.response = None
234
294
        self.finished_reading = False
235
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))
236
317
 
237
318
    def accept_body(self, bytes):
238
319
        """Accept body data."""
239
 
 
240
 
        # TODO: This should be overriden for each command that desired body data
241
 
        # to handle the right format of that data, i.e. plain bytes, a bundle,
242
 
        # etc.  The deserialisation into that format should be done in the
243
 
        # Protocol object.
244
 
 
245
 
        # default fallback is to accumulate bytes.
246
 
        self._body_bytes += bytes
247
 
        
 
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
 
248
328
    def end_of_body(self):
249
329
        """No more body data will be received."""
250
 
        self._run_handler_code(self._command.do_body, (self._body_bytes,), {})
 
330
        self._run_handler_code(self._command.do_end, (), {})
251
331
        # cannot read after this.
252
332
        self.finished_reading = True
253
 
 
254
 
    def dispatch_command(self, cmd, args):
255
 
        """Deprecated compatibility method.""" # XXX XXX
256
 
        try:
257
 
            command = self._commands.get(cmd)
258
 
        except LookupError:
259
 
            raise errors.UnknownSmartMethod(cmd)
260
 
        self._command = command(self._backing_transport, self._root_client_path)
261
 
        self._run_handler_code(self._command.execute, args, {})
 
333
        if 'hpss' in debug.debug_flags:
 
334
            self._trace('end of body', '', include_time=True)
262
335
 
263
336
    def _run_handler_code(self, callable, args, kwargs):
264
337
        """Run some handler specific code 'callable'.
280
353
        # XXX: most of this error conversion is VFS-related, and thus ought to
281
354
        # be in SmartServerVFSRequestHandler somewhere.
282
355
        try:
283
 
            return callable(*args, **kwargs)
284
 
        except errors.NoSuchFile, e:
285
 
            return FailedSmartServerResponse(('NoSuchFile', e.path))
286
 
        except errors.FileExists, e:
287
 
            return FailedSmartServerResponse(('FileExists', e.path))
288
 
        except errors.DirectoryNotEmpty, e:
289
 
            return FailedSmartServerResponse(('DirectoryNotEmpty', e.path))
290
 
        except errors.ShortReadvError, e:
291
 
            return FailedSmartServerResponse(('ShortReadvError',
292
 
                e.path, str(e.offset), str(e.length), str(e.actual)))
293
 
        except errors.UnstackableRepositoryFormat, e:
294
 
            return FailedSmartServerResponse(('UnstackableRepositoryFormat',
295
 
                str(e.format), e.url))
296
 
        except errors.UnstackableBranchFormat, e:
297
 
            return FailedSmartServerResponse(('UnstackableBranchFormat',
298
 
                str(e.format), e.url))
299
 
        except errors.NotStacked, e:
300
 
            return FailedSmartServerResponse(('NotStacked',))
301
 
        except UnicodeError, e:
302
 
            # If it is a DecodeError, than most likely we are starting
303
 
            # with a plain string
304
 
            str_or_unicode = e.object
305
 
            if isinstance(str_or_unicode, unicode):
306
 
                # XXX: UTF-8 might have \x01 (our protocol v1 and v2 seperator
307
 
                # byte) in it, so this encoding could cause broken responses.
308
 
                # Newer clients use protocol v3, so will be fine.
309
 
                val = 'u:' + str_or_unicode.encode('utf-8')
310
 
            else:
311
 
                val = 's:' + str_or_unicode.encode('base64')
312
 
            # This handles UnicodeEncodeError or UnicodeDecodeError
313
 
            return FailedSmartServerResponse((e.__class__.__name__,
314
 
                    e.encoding, val, str(e.start), str(e.end), e.reason))
315
 
        except errors.TransportNotPossible, e:
316
 
            if e.msg == "readonly transport":
317
 
                return FailedSmartServerResponse(('ReadOnlyError', ))
318
 
            else:
319
 
                raise
320
 
        except errors.ReadError, e:
321
 
            # cannot read the file
322
 
            return FailedSmartServerResponse(('ReadError', e.path))
323
 
        except errors.PermissionDenied, e:
324
 
            return FailedSmartServerResponse(
325
 
                ('PermissionDenied', e.path, e.extra))
 
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)
326
366
 
327
367
    def headers_received(self, headers):
328
368
        # Just a no-op at the moment.
329
 
        pass
 
369
        if 'hpss' in debug.debug_flags:
 
370
            self._trace('headers', repr(headers))
330
371
 
331
372
    def args_received(self, args):
332
373
        cmd = args[0]
334
375
        try:
335
376
            command = self._commands.get(cmd)
336
377
        except LookupError:
 
378
            if 'hpss' in debug.debug_flags:
 
379
                self._trace('hpss unknown request', 
 
380
                            cmd, repr(args)[1:-1])
337
381
            raise errors.UnknownSmartMethod(cmd)
338
 
        self._command = command(self._backing_transport)
 
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)
339
392
        self._run_handler_code(self._command.execute, args, {})
340
393
 
341
 
    def prefixed_body_received(self, body_bytes):
342
 
        """No more body data will be received."""
343
 
        self._run_handler_code(self._command.do_body, (body_bytes,), {})
344
 
        # cannot read after this.
345
 
        self.finished_reading = True
346
 
 
347
 
    def body_chunk_received(self, chunk_bytes):
348
 
        self._run_handler_code(self._command.do_chunk, (chunk_bytes,), {})
349
 
 
350
394
    def end_received(self):
 
395
        if self._command is None:
 
396
            # no active command object, so ignore the event.
 
397
            return
351
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))
352
463
 
353
464
 
354
465
class HelloRequest(SmartServerRequest):
386
497
        return SuccessfulSmartServerResponse((answer,))
387
498
 
388
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.
389
524
request_handlers = registry.Registry()
390
525
request_handlers.register_lazy(
391
 
    'append', 'bzrlib.smart.vfs', 'AppendRequest')
392
 
request_handlers.register_lazy(
393
 
    'Branch.get_config_file', 'bzrlib.smart.branch', 'SmartServerBranchGetConfigFile')
394
 
request_handlers.register_lazy(
395
 
    'Branch.get_stacked_on_url', 'bzrlib.smart.branch', 'SmartServerBranchRequestGetStackedOnURL')
396
 
request_handlers.register_lazy(
397
 
    'Branch.last_revision_info', 'bzrlib.smart.branch', 'SmartServerBranchRequestLastRevisionInfo')
398
 
request_handlers.register_lazy(
399
 
    'Branch.lock_write', 'bzrlib.smart.branch', 'SmartServerBranchRequestLockWrite')
400
 
request_handlers.register_lazy(
401
 
    'Branch.revision_history', 'bzrlib.smart.branch', 'SmartServerRequestRevisionHistory')
402
 
request_handlers.register_lazy(
403
 
    'Branch.set_last_revision', 'bzrlib.smart.branch', 'SmartServerBranchRequestSetLastRevision')
 
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')
404
572
request_handlers.register_lazy(
405
573
    'Branch.set_last_revision_info', 'bzrlib.smart.branch',
406
 
    'SmartServerBranchRequestSetLastRevisionInfo')
 
574
    'SmartServerBranchRequestSetLastRevisionInfo', info='idem')
407
575
request_handlers.register_lazy(
408
576
    'Branch.set_last_revision_ex', 'bzrlib.smart.branch',
409
 
    'SmartServerBranchRequestSetLastRevisionEx')
410
 
request_handlers.register_lazy(
411
 
    'Branch.unlock', 'bzrlib.smart.branch', 'SmartServerBranchRequestUnlock')
412
 
request_handlers.register_lazy(
413
 
    'BzrDir.find_repository', 'bzrlib.smart.bzrdir', 'SmartServerRequestFindRepositoryV1')
414
 
request_handlers.register_lazy(
415
 
    'BzrDir.find_repositoryV2', 'bzrlib.smart.bzrdir', 'SmartServerRequestFindRepositoryV2')
416
 
request_handlers.register_lazy(
417
 
    'BzrDirFormat.initialize', 'bzrlib.smart.bzrdir', 'SmartServerRequestInitializeBzrDir')
418
 
request_handlers.register_lazy(
419
 
    'BzrDir.open_branch', 'bzrlib.smart.bzrdir', 'SmartServerRequestOpenBranch')
420
 
request_handlers.register_lazy(
421
 
    'delete', 'bzrlib.smart.vfs', 'DeleteRequest')
422
 
request_handlers.register_lazy(
423
 
    'get', 'bzrlib.smart.vfs', 'GetRequest')
424
 
request_handlers.register_lazy(
425
 
    'get_bundle', 'bzrlib.smart.request', 'GetBundleRequest')
426
 
request_handlers.register_lazy(
427
 
    'has', 'bzrlib.smart.vfs', 'HasRequest')
428
 
request_handlers.register_lazy(
429
 
    'hello', 'bzrlib.smart.request', 'HelloRequest')
430
 
request_handlers.register_lazy(
431
 
    'iter_files_recursive', 'bzrlib.smart.vfs', 'IterFilesRecursiveRequest')
432
 
request_handlers.register_lazy(
433
 
    'list_dir', 'bzrlib.smart.vfs', 'ListDirRequest')
434
 
request_handlers.register_lazy(
435
 
    'mkdir', 'bzrlib.smart.vfs', 'MkdirRequest')
436
 
request_handlers.register_lazy(
437
 
    'move', 'bzrlib.smart.vfs', 'MoveRequest')
438
 
request_handlers.register_lazy(
439
 
    'put', 'bzrlib.smart.vfs', 'PutRequest')
440
 
request_handlers.register_lazy(
441
 
    'put_non_atomic', 'bzrlib.smart.vfs', 'PutNonAtomicRequest')
442
 
request_handlers.register_lazy(
443
 
    'readv', 'bzrlib.smart.vfs', 'ReadvRequest')
444
 
request_handlers.register_lazy(
445
 
    'rename', 'bzrlib.smart.vfs', 'RenameRequest')
 
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')
446
677
request_handlers.register_lazy(
447
678
    'PackRepository.autopack', 'bzrlib.smart.packrepository',
448
 
    'SmartServerPackRepositoryAutopack')
449
 
request_handlers.register_lazy('Repository.gather_stats',
450
 
                               'bzrlib.smart.repository',
451
 
                               'SmartServerRepositoryGatherStats')
452
 
request_handlers.register_lazy('Repository.get_parent_map',
453
 
                               'bzrlib.smart.repository',
454
 
                               'SmartServerRepositoryGetParentMap')
455
 
request_handlers.register_lazy(
456
 
    'Repository.get_revision_graph', 'bzrlib.smart.repository', 'SmartServerRepositoryGetRevisionGraph')
457
 
request_handlers.register_lazy(
458
 
    'Repository.has_revision', 'bzrlib.smart.repository', 'SmartServerRequestHasRevision')
459
 
request_handlers.register_lazy(
460
 
    'Repository.is_shared', 'bzrlib.smart.repository', 'SmartServerRepositoryIsShared')
461
 
request_handlers.register_lazy(
462
 
    'Repository.lock_write', 'bzrlib.smart.repository', 'SmartServerRepositoryLockWrite')
463
 
request_handlers.register_lazy(
464
 
    'Repository.unlock', 'bzrlib.smart.repository', 'SmartServerRepositoryUnlock')
 
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')
465
761
request_handlers.register_lazy(
466
762
    'Repository.tarball', 'bzrlib.smart.repository',
467
 
    'SmartServerRepositoryTarball')
468
 
request_handlers.register_lazy(
469
 
    'rmdir', 'bzrlib.smart.vfs', 'RmdirRequest')
470
 
request_handlers.register_lazy(
471
 
    'stat', 'bzrlib.smart.vfs', 'StatRequest')
472
 
request_handlers.register_lazy(
473
 
    'Transport.is_readonly', 'bzrlib.smart.request', 'SmartServerIsReadonly')
474
 
request_handlers.register_lazy(
475
 
    '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')