~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/smart/request.py

merge bzr.dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006-2010 Canonical Ltd
 
1
# Copyright (C) 2006, 2007 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., 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
 
# 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
 
 
34
 
import threading
 
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
35
21
 
36
22
from bzrlib import (
37
23
    bzrdir,
38
 
    debug,
39
24
    errors,
40
 
    osutils,
41
25
    registry,
42
26
    revision,
43
 
    trace,
44
 
    urlutils,
45
27
    )
46
 
from bzrlib.lazy_import import lazy_import
47
 
lazy_import(globals(), """
48
 
from bzrlib.bundle import serializer
49
 
 
50
 
import tempfile
51
 
import thread
52
 
""")
53
 
 
54
 
 
55
 
jail_info = threading.local()
56
 
jail_info.transports = None
57
 
 
58
 
 
59
 
def _install_hook():
60
 
    bzrdir.BzrDir.hooks.install_named_hook(
61
 
        'pre_open', _pre_open_hook, 'checking server jail')
62
 
 
63
 
 
64
 
def _pre_open_hook(transport):
65
 
    allowed_transports = getattr(jail_info, 'transports', None)
66
 
    if allowed_transports is None:
67
 
        return
68
 
    abspath = transport.base
69
 
    for allowed_transport in allowed_transports:
70
 
        try:
71
 
            allowed_transport.relpath(abspath)
72
 
        except errors.PathNotChild:
73
 
            continue
74
 
        else:
75
 
            return
76
 
    raise errors.JailBreak(abspath)
77
 
 
78
 
 
79
 
_install_hook()
 
28
from bzrlib.bundle.serializer import write_bundle
80
29
 
81
30
 
82
31
class SmartServerRequest(object):
83
 
    """Base class for request handlers.
84
 
 
85
 
    To define a new request, subclass this class and override the `do` method
86
 
    (and if appropriate, `do_body` as well).  Request implementors should take
87
 
    care to call `translate_client_path` and `transport_from_client_path` as
88
 
    appropriate when dealing with paths received from the client.
89
 
    """
90
 
    # XXX: rename this class to BaseSmartServerRequestHandler ?  A request
91
 
    # *handler* is a different concept to the request.
92
 
 
93
 
    def __init__(self, backing_transport, root_client_path='/', jail_root=None):
 
32
    """Base class for request handlers."""
 
33
 
 
34
    def __init__(self, backing_transport):
94
35
        """Constructor.
95
36
 
96
37
        :param backing_transport: the base transport to be used when performing
97
38
            this request.
98
 
        :param root_client_path: the client path that maps to the root of
99
 
            backing_transport.  This is used to interpret relpaths received
100
 
            from the client.  Clients will not be able to refer to paths above
101
 
            this root.  If root_client_path is None, then no translation will
102
 
            be performed on client paths.  Default is '/'.
103
 
        :param jail_root: if specified, the root of the BzrDir.open jail to use
104
 
            instead of backing_transport.
105
39
        """
106
40
        self._backing_transport = backing_transport
107
 
        if jail_root is None:
108
 
            jail_root = backing_transport
109
 
        self._jail_root = jail_root
110
 
        if root_client_path is not None:
111
 
            if not root_client_path.startswith('/'):
112
 
                root_client_path = '/' + root_client_path
113
 
            if not root_client_path.endswith('/'):
114
 
                root_client_path += '/'
115
 
        self._root_client_path = root_client_path
116
 
        self._body_chunks = []
117
41
 
118
42
    def _check_enabled(self):
119
43
        """Raises DisabledMethod if this method is disabled."""
121
45
 
122
46
    def do(self, *args):
123
47
        """Mandatory extension point for SmartServerRequest subclasses.
124
 
 
 
48
        
125
49
        Subclasses must implement this.
126
 
 
 
50
        
127
51
        This should return a SmartServerResponse if this command expects to
128
52
        receive no body.
129
53
        """
135
59
        It will return a SmartServerResponse if the command does not expect a
136
60
        body.
137
61
 
138
 
        :param args: the arguments of the request.
 
62
        :param *args: the arguments of the request.
139
63
        """
140
64
        self._check_enabled()
141
65
        return self.do(*args)
142
66
 
143
67
    def do_body(self, body_bytes):
144
68
        """Called if the client sends a body with the request.
145
 
 
146
 
        The do() method is still called, and must have returned None.
147
 
 
 
69
        
148
70
        Must return a SmartServerResponse.
149
71
        """
150
 
        if body_bytes != '':
151
 
            raise errors.SmartProtocolError('Request does not expect a body')
152
 
 
153
 
    def do_chunk(self, chunk_bytes):
154
 
        """Called with each body chunk if the request has a streamed body.
155
 
 
156
 
        The do() method is still called, and must have returned None.
157
 
        """
158
 
        self._body_chunks.append(chunk_bytes)
159
 
 
160
 
    def do_end(self):
161
 
        """Called when the end of the request has been received."""
162
 
        body_bytes = ''.join(self._body_chunks)
163
 
        self._body_chunks = None
164
 
        return self.do_body(body_bytes)
165
 
 
166
 
    def setup_jail(self):
167
 
        jail_info.transports = [self._jail_root]
168
 
 
169
 
    def teardown_jail(self):
170
 
        jail_info.transports = None
171
 
 
172
 
    def translate_client_path(self, client_path):
173
 
        """Translate a path received from a network client into a local
174
 
        relpath.
175
 
 
176
 
        All paths received from the client *must* be translated.
177
 
 
178
 
        :param client_path: the path from the client.
179
 
        :returns: a relpath that may be used with self._backing_transport
180
 
            (unlike the untranslated client_path, which must not be used with
181
 
            the backing transport).
182
 
        """
183
 
        if self._root_client_path is None:
184
 
            # no translation necessary!
185
 
            return client_path
186
 
        if not client_path.startswith('/'):
187
 
            client_path = '/' + client_path
188
 
        if client_path + '/' == self._root_client_path:
189
 
            return '.'
190
 
        if client_path.startswith(self._root_client_path):
191
 
            path = client_path[len(self._root_client_path):]
192
 
            relpath = urlutils.joinpath('/', path)
193
 
            if not relpath.startswith('/'):
194
 
                raise ValueError(relpath)
195
 
            return urlutils.escape('.' + relpath)
196
 
        else:
197
 
            raise errors.PathNotChild(client_path, self._root_client_path)
198
 
 
199
 
    def transport_from_client_path(self, client_path):
200
 
        """Get a backing transport corresponding to the location referred to by
201
 
        a network client.
202
 
 
203
 
        :seealso: translate_client_path
204
 
        :returns: a transport cloned from self._backing_transport
205
 
        """
206
 
        relpath = self.translate_client_path(client_path)
207
 
        return self._backing_transport.clone(relpath)
 
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)
208
77
 
209
78
 
210
79
class SmartServerResponse(object):
211
 
    """A response to a client request.
212
 
 
213
 
    This base class should not be used. Instead use
214
 
    SuccessfulSmartServerResponse and FailedSmartServerResponse as appropriate.
215
 
    """
216
 
 
217
 
    def __init__(self, args, body=None, body_stream=None):
218
 
        """Constructor.
219
 
 
220
 
        :param args: tuple of response arguments.
221
 
        :param body: string of a response body.
222
 
        :param body_stream: iterable of bytestrings to be streamed to the
223
 
            client.
224
 
        """
 
80
    """Response generated by SmartServerRequestHandler."""
 
81
 
 
82
    def __init__(self, args, body=None):
225
83
        self.args = args
226
 
        if body is not None and body_stream is not None:
227
 
            raise errors.BzrError(
228
 
                "'body' and 'body_stream' are mutually exclusive.")
229
84
        self.body = body
230
 
        self.body_stream = body_stream
231
85
 
232
86
    def __eq__(self, other):
233
87
        if other is None:
234
88
            return False
235
 
        return (other.args == self.args and
236
 
                other.body == self.body and
237
 
                other.body_stream is self.body_stream)
 
89
        return other.args == self.args and other.body == self.body
238
90
 
239
91
    def __repr__(self):
240
 
        return "<%s args=%r body=%r>" % (self.__class__.__name__,
241
 
            self.args, self.body)
242
 
 
243
 
 
244
 
class FailedSmartServerResponse(SmartServerResponse):
245
 
    """A SmartServerResponse for a request which failed."""
246
 
 
247
 
    def is_successful(self):
248
 
        """FailedSmartServerResponse are not successful."""
249
 
        return False
250
 
 
251
 
 
252
 
class SuccessfulSmartServerResponse(SmartServerResponse):
253
 
    """A SmartServerResponse for a successfully completed request."""
254
 
 
255
 
    def is_successful(self):
256
 
        """SuccessfulSmartServerResponse are successful."""
257
 
        return True
 
92
        return "<SmartServerResponse args=%r body=%r>" % (self.args, self.body)
258
93
 
259
94
 
260
95
class SmartServerRequestHandler(object):
261
96
    """Protocol logic for smart server.
262
 
 
 
97
    
263
98
    This doesn't handle serialization at all, it just processes requests and
264
99
    creates responses.
265
100
    """
274
109
    # TODO: Better way of representing the body for commands that take it,
275
110
    # and allow it to be streamed into the server.
276
111
 
277
 
    def __init__(self, backing_transport, commands, root_client_path,
278
 
        jail_root=None):
 
112
    def __init__(self, backing_transport, commands):
279
113
        """Constructor.
280
114
 
281
115
        :param backing_transport: a Transport to handle requests for.
283
117
            subclasses. e.g. bzrlib.transport.smart.vfs.vfs_commands.
284
118
        """
285
119
        self._backing_transport = backing_transport
286
 
        self._root_client_path = root_client_path
287
120
        self._commands = commands
288
 
        if jail_root is None:
289
 
            jail_root = backing_transport
290
 
        self._jail_root = jail_root
 
121
        self._body_bytes = ''
291
122
        self.response = None
292
123
        self.finished_reading = False
293
124
        self._command = None
294
 
        if 'hpss' in debug.debug_flags:
295
 
            self._request_start_time = osutils.timer_func()
296
 
            self._thread_id = thread.get_ident()
297
 
 
298
 
    def _trace(self, action, message, extra_bytes=None, include_time=False):
299
 
        # It is a bit of a shame that this functionality overlaps with that of 
300
 
        # ProtocolThreeRequester._trace. However, there is enough difference
301
 
        # that just putting it in a helper doesn't help a lot. And some state
302
 
        # is taken from the instance.
303
 
        if include_time:
304
 
            t = '%5.3fs ' % (osutils.timer_func() - self._request_start_time)
305
 
        else:
306
 
            t = ''
307
 
        if extra_bytes is None:
308
 
            extra = ''
309
 
        else:
310
 
            extra = ' ' + repr(extra_bytes[:40])
311
 
            if len(extra) > 33:
312
 
                extra = extra[:29] + extra[-1] + '...'
313
 
        trace.mutter('%12s: [%s] %s%s%s'
314
 
                     % (action, self._thread_id, t, message, extra))
315
125
 
316
126
    def accept_body(self, bytes):
317
127
        """Accept body data."""
318
 
        if self._command is None:
319
 
            # no active command object, so ignore the event.
320
 
            return
321
 
        self._run_handler_code(self._command.do_chunk, (bytes,), {})
322
 
        if 'hpss' in debug.debug_flags:
323
 
            self._trace('accept body',
324
 
                        '%d bytes' % (len(bytes),), bytes)
325
 
 
 
128
 
 
129
        # TODO: This should be overriden for each command that desired body data
 
130
        # to handle the right format of that data, i.e. plain bytes, a bundle,
 
131
        # etc.  The deserialisation into that format should be done in the
 
132
        # Protocol object.
 
133
 
 
134
        # default fallback is to accumulate bytes.
 
135
        self._body_bytes += bytes
 
136
        
326
137
    def end_of_body(self):
327
138
        """No more body data will be received."""
328
 
        self._run_handler_code(self._command.do_end, (), {})
 
139
        self._run_handler_code(self._command.do_body, (self._body_bytes,), {})
329
140
        # cannot read after this.
330
141
        self.finished_reading = True
331
 
        if 'hpss' in debug.debug_flags:
332
 
            self._trace('end of body', '', include_time=True)
 
142
 
 
143
    def dispatch_command(self, cmd, args):
 
144
        """Deprecated compatibility method.""" # XXX XXX
 
145
        try:
 
146
            command = self._commands.get(cmd)
 
147
        except LookupError:
 
148
            raise errors.SmartProtocolError("bad request %r" % (cmd,))
 
149
        self._command = command(self._backing_transport)
 
150
        self._run_handler_code(self._command.execute, args, {})
333
151
 
334
152
    def _run_handler_code(self, callable, args, kwargs):
335
153
        """Run some handler specific code 'callable'.
351
169
        # XXX: most of this error conversion is VFS-related, and thus ought to
352
170
        # be in SmartServerVFSRequestHandler somewhere.
353
171
        try:
354
 
            self._command.setup_jail()
355
 
            try:
356
 
                return callable(*args, **kwargs)
357
 
            finally:
358
 
                self._command.teardown_jail()
359
 
        except (KeyboardInterrupt, SystemExit):
360
 
            raise
361
 
        except Exception, err:
362
 
            err_struct = _translate_error(err)
363
 
            return FailedSmartServerResponse(err_struct)
364
 
 
365
 
    def headers_received(self, headers):
366
 
        # Just a no-op at the moment.
367
 
        if 'hpss' in debug.debug_flags:
368
 
            self._trace('headers', repr(headers))
369
 
 
370
 
    def args_received(self, args):
371
 
        cmd = args[0]
372
 
        args = args[1:]
373
 
        try:
374
 
            command = self._commands.get(cmd)
375
 
        except LookupError:
376
 
            if 'hpss' in debug.debug_flags:
377
 
                self._trace('hpss unknown request', 
378
 
                            cmd, repr(args)[1:-1])
379
 
            raise errors.UnknownSmartMethod(cmd)
380
 
        if 'hpss' in debug.debug_flags:
381
 
            from bzrlib.smart import vfs
382
 
            if issubclass(command, vfs.VfsRequest):
383
 
                action = 'hpss vfs req'
384
 
            else:
385
 
                action = 'hpss request'
386
 
            self._trace(action, 
387
 
                        '%s %s' % (cmd, repr(args)[1:-1]))
388
 
        self._command = command(
389
 
            self._backing_transport, self._root_client_path, self._jail_root)
390
 
        self._run_handler_code(self._command.execute, args, {})
391
 
 
392
 
    def end_received(self):
393
 
        if self._command is None:
394
 
            # no active command object, so ignore the event.
395
 
            return
396
 
        self._run_handler_code(self._command.do_end, (), {})
397
 
        if 'hpss' in debug.debug_flags:
398
 
            self._trace('end', '', include_time=True)
399
 
 
400
 
    def post_body_error_received(self, error_args):
401
 
        # Just a no-op at the moment.
402
 
        pass
403
 
 
404
 
 
405
 
def _translate_error(err):
406
 
    if isinstance(err, errors.NoSuchFile):
407
 
        return ('NoSuchFile', err.path)
408
 
    elif isinstance(err, errors.FileExists):
409
 
        return ('FileExists', err.path)
410
 
    elif isinstance(err, errors.DirectoryNotEmpty):
411
 
        return ('DirectoryNotEmpty', err.path)
412
 
    elif isinstance(err, errors.IncompatibleRepositories):
413
 
        return ('IncompatibleRepositories', str(err.source), str(err.target),
414
 
            str(err.details))
415
 
    elif isinstance(err, errors.ShortReadvError):
416
 
        return ('ShortReadvError', err.path, str(err.offset), str(err.length),
417
 
                str(err.actual))
418
 
    elif isinstance(err, errors.UnstackableRepositoryFormat):
419
 
        return (('UnstackableRepositoryFormat', str(err.format), err.url))
420
 
    elif isinstance(err, errors.UnstackableBranchFormat):
421
 
        return ('UnstackableBranchFormat', str(err.format), err.url)
422
 
    elif isinstance(err, errors.NotStacked):
423
 
        return ('NotStacked',)
424
 
    elif isinstance(err, UnicodeError):
425
 
        # If it is a DecodeError, than most likely we are starting
426
 
        # with a plain string
427
 
        str_or_unicode = err.object
428
 
        if isinstance(str_or_unicode, unicode):
429
 
            # XXX: UTF-8 might have \x01 (our protocol v1 and v2 seperator
430
 
            # byte) in it, so this encoding could cause broken responses.
431
 
            # Newer clients use protocol v3, so will be fine.
432
 
            val = 'u:' + str_or_unicode.encode('utf-8')
433
 
        else:
434
 
            val = 's:' + str_or_unicode.encode('base64')
435
 
        # This handles UnicodeEncodeError or UnicodeDecodeError
436
 
        return (err.__class__.__name__, err.encoding, val, str(err.start),
437
 
                str(err.end), err.reason)
438
 
    elif isinstance(err, errors.TransportNotPossible):
439
 
        if err.msg == "readonly transport":
440
 
            return ('ReadOnlyError', )
441
 
    elif isinstance(err, errors.ReadError):
442
 
        # cannot read the file
443
 
        return ('ReadError', err.path)
444
 
    elif isinstance(err, errors.PermissionDenied):
445
 
        return ('PermissionDenied', err.path, err.extra)
446
 
    elif isinstance(err, errors.TokenMismatch):
447
 
        return ('TokenMismatch', err.given_token, err.lock_token)
448
 
    elif isinstance(err, errors.LockContention):
449
 
        return ('LockContention',)
450
 
    elif isinstance(err, MemoryError):
451
 
        # GZ 2011-02-24: Copy bzrlib.trace -Dmem_dump functionality here?
452
 
        return ('MemoryError',)
453
 
    # Unserialisable error.  Log it, and return a generic error
454
 
    trace.log_exception_quietly()
455
 
    return ('error', trace._qualified_exception_name(err.__class__, True),
456
 
        str(err))
 
172
            return callable(*args, **kwargs)
 
173
        except errors.NoSuchFile, e:
 
174
            return SmartServerResponse(('NoSuchFile', e.path))
 
175
        except errors.FileExists, e:
 
176
            return SmartServerResponse(('FileExists', e.path))
 
177
        except errors.DirectoryNotEmpty, e:
 
178
            return SmartServerResponse(('DirectoryNotEmpty', e.path))
 
179
        except errors.ShortReadvError, e:
 
180
            return SmartServerResponse(('ShortReadvError',
 
181
                e.path, str(e.offset), str(e.length), str(e.actual)))
 
182
        except UnicodeError, e:
 
183
            # If it is a DecodeError, than most likely we are starting
 
184
            # with a plain string
 
185
            str_or_unicode = e.object
 
186
            if isinstance(str_or_unicode, unicode):
 
187
                # XXX: UTF-8 might have \x01 (our seperator byte) in it.  We
 
188
                # should escape it somehow.
 
189
                val = 'u:' + str_or_unicode.encode('utf-8')
 
190
            else:
 
191
                val = 's:' + str_or_unicode.encode('base64')
 
192
            # This handles UnicodeEncodeError or UnicodeDecodeError
 
193
            return SmartServerResponse((e.__class__.__name__,
 
194
                    e.encoding, val, str(e.start), str(e.end), e.reason))
 
195
        except errors.TransportNotPossible, e:
 
196
            if e.msg == "readonly transport":
 
197
                return SmartServerResponse(('ReadOnlyError', ))
 
198
            else:
 
199
                raise
457
200
 
458
201
 
459
202
class HelloRequest(SmartServerRequest):
460
 
    """Answer a version request with the highest protocol version this server
461
 
    supports.
462
 
    """
 
203
    """Answer a version request with my version."""
463
204
 
464
205
    def do(self):
465
 
        return SuccessfulSmartServerResponse(('ok', '2'))
 
206
        return SmartServerResponse(('ok', '1'))
466
207
 
467
208
 
468
209
class GetBundleRequest(SmartServerRequest):
470
211
 
471
212
    def do(self, path, revision_id):
472
213
        # open transport relative to our base
473
 
        t = self.transport_from_client_path(path)
 
214
        t = self._backing_transport.clone(path)
474
215
        control, extra_path = bzrdir.BzrDir.open_containing_from_transport(t)
475
216
        repo = control.open_repository()
476
217
        tmpf = tempfile.TemporaryFile()
477
218
        base_revision = revision.NULL_REVISION
478
 
        serializer.write_bundle(repo, revision_id, base_revision, tmpf)
 
219
        write_bundle(repo, revision_id, base_revision, tmpf)
479
220
        tmpf.seek(0)
480
 
        return SuccessfulSmartServerResponse((), tmpf.read())
 
221
        return SmartServerResponse((), tmpf.read())
481
222
 
482
223
 
483
224
class SmartServerIsReadonly(SmartServerRequest):
488
229
            answer = 'yes'
489
230
        else:
490
231
            answer = 'no'
491
 
        return SuccessfulSmartServerResponse((answer,))
 
232
        return SmartServerResponse((answer,))
492
233
 
493
234
 
494
235
request_handlers = registry.Registry()
495
236
request_handlers.register_lazy(
496
237
    'append', 'bzrlib.smart.vfs', 'AppendRequest')
497
238
request_handlers.register_lazy(
498
 
    'Branch.get_config_file', 'bzrlib.smart.branch',
499
 
    'SmartServerBranchGetConfigFile')
500
 
request_handlers.register_lazy(
501
 
    'Branch.get_parent', 'bzrlib.smart.branch', 'SmartServerBranchGetParent')
502
 
request_handlers.register_lazy(
503
 
    'Branch.get_tags_bytes', 'bzrlib.smart.branch',
504
 
    'SmartServerBranchGetTagsBytes')
505
 
request_handlers.register_lazy(
506
 
    'Branch.set_tags_bytes', 'bzrlib.smart.branch',
507
 
    'SmartServerBranchSetTagsBytes')
508
 
request_handlers.register_lazy(
509
 
    'Branch.heads_to_fetch', 'bzrlib.smart.branch',
510
 
    'SmartServerBranchHeadsToFetch')
511
 
request_handlers.register_lazy(
512
 
    'Branch.get_stacked_on_url', 'bzrlib.smart.branch', 'SmartServerBranchRequestGetStackedOnURL')
513
 
request_handlers.register_lazy(
514
 
    'Branch.last_revision_info', 'bzrlib.smart.branch', 'SmartServerBranchRequestLastRevisionInfo')
515
 
request_handlers.register_lazy(
516
 
    'Branch.lock_write', 'bzrlib.smart.branch', 'SmartServerBranchRequestLockWrite')
517
 
request_handlers.register_lazy( 'Branch.revision_history',
518
 
    'bzrlib.smart.branch', 'SmartServerRequestRevisionHistory')
519
 
request_handlers.register_lazy( 'Branch.set_config_option',
520
 
    'bzrlib.smart.branch', 'SmartServerBranchRequestSetConfigOption')
521
 
request_handlers.register_lazy( 'Branch.set_config_option_dict',
522
 
    'bzrlib.smart.branch', 'SmartServerBranchRequestSetConfigOptionDict')
523
 
request_handlers.register_lazy( 'Branch.set_last_revision',
524
 
    'bzrlib.smart.branch', 'SmartServerBranchRequestSetLastRevision')
525
 
request_handlers.register_lazy(
526
 
    'Branch.set_last_revision_info', 'bzrlib.smart.branch',
527
 
    'SmartServerBranchRequestSetLastRevisionInfo')
528
 
request_handlers.register_lazy(
529
 
    'Branch.set_last_revision_ex', 'bzrlib.smart.branch',
530
 
    'SmartServerBranchRequestSetLastRevisionEx')
531
 
request_handlers.register_lazy(
532
 
    'Branch.set_parent_location', 'bzrlib.smart.branch',
533
 
    'SmartServerBranchRequestSetParentLocation')
534
 
request_handlers.register_lazy(
535
 
    'Branch.unlock', 'bzrlib.smart.branch', 'SmartServerBranchRequestUnlock')
536
 
request_handlers.register_lazy(
537
 
    'BzrDir.cloning_metadir', 'bzrlib.smart.bzrdir',
538
 
    'SmartServerBzrDirRequestCloningMetaDir')
539
 
request_handlers.register_lazy(
540
 
    'BzrDir.create_branch', 'bzrlib.smart.bzrdir',
541
 
    'SmartServerRequestCreateBranch')
542
 
request_handlers.register_lazy(
543
 
    'BzrDir.create_repository', 'bzrlib.smart.bzrdir',
544
 
    'SmartServerRequestCreateRepository')
545
 
request_handlers.register_lazy(
546
 
    'BzrDir.find_repository', 'bzrlib.smart.bzrdir',
547
 
    'SmartServerRequestFindRepositoryV1')
548
 
request_handlers.register_lazy(
549
 
    'BzrDir.find_repositoryV2', 'bzrlib.smart.bzrdir',
550
 
    'SmartServerRequestFindRepositoryV2')
551
 
request_handlers.register_lazy(
552
 
    'BzrDir.find_repositoryV3', 'bzrlib.smart.bzrdir',
553
 
    'SmartServerRequestFindRepositoryV3')
554
 
request_handlers.register_lazy(
555
 
    'BzrDir.get_config_file', 'bzrlib.smart.bzrdir',
556
 
    'SmartServerBzrDirRequestConfigFile')
557
 
request_handlers.register_lazy(
558
 
    'BzrDirFormat.initialize', 'bzrlib.smart.bzrdir',
559
 
    'SmartServerRequestInitializeBzrDir')
560
 
request_handlers.register_lazy(
561
 
    'BzrDirFormat.initialize_ex_1.16', 'bzrlib.smart.bzrdir',
562
 
    'SmartServerRequestBzrDirInitializeEx')
563
 
request_handlers.register_lazy(
564
 
    'BzrDir.open', 'bzrlib.smart.bzrdir', 'SmartServerRequestOpenBzrDir')
565
 
request_handlers.register_lazy(
566
 
    'BzrDir.open_2.1', 'bzrlib.smart.bzrdir', 'SmartServerRequestOpenBzrDir_2_1')
567
 
request_handlers.register_lazy(
568
 
    'BzrDir.open_branch', 'bzrlib.smart.bzrdir',
569
 
    'SmartServerRequestOpenBranch')
570
 
request_handlers.register_lazy(
571
 
    'BzrDir.open_branchV2', 'bzrlib.smart.bzrdir',
572
 
    'SmartServerRequestOpenBranchV2')
573
 
request_handlers.register_lazy(
574
 
    'BzrDir.open_branchV3', 'bzrlib.smart.bzrdir',
575
 
    'SmartServerRequestOpenBranchV3')
576
 
request_handlers.register_lazy(
577
239
    'delete', 'bzrlib.smart.vfs', 'DeleteRequest')
578
240
request_handlers.register_lazy(
579
241
    'get', 'bzrlib.smart.vfs', 'GetRequest')
600
262
request_handlers.register_lazy(
601
263
    'rename', 'bzrlib.smart.vfs', 'RenameRequest')
602
264
request_handlers.register_lazy(
603
 
    'PackRepository.autopack', 'bzrlib.smart.packrepository',
604
 
    'SmartServerPackRepositoryAutopack')
605
 
request_handlers.register_lazy('Repository.gather_stats',
606
 
                               'bzrlib.smart.repository',
607
 
                               'SmartServerRepositoryGatherStats')
608
 
request_handlers.register_lazy('Repository.get_parent_map',
609
 
                               'bzrlib.smart.repository',
610
 
                               'SmartServerRepositoryGetParentMap')
611
 
request_handlers.register_lazy(
612
 
    'Repository.get_revision_graph', 'bzrlib.smart.repository', 'SmartServerRepositoryGetRevisionGraph')
613
 
request_handlers.register_lazy(
614
 
    'Repository.has_revision', 'bzrlib.smart.repository', 'SmartServerRequestHasRevision')
615
 
request_handlers.register_lazy(
616
 
    'Repository.insert_stream', 'bzrlib.smart.repository', 'SmartServerRepositoryInsertStream')
617
 
request_handlers.register_lazy(
618
 
    'Repository.insert_stream_1.19', 'bzrlib.smart.repository', 'SmartServerRepositoryInsertStream_1_19')
619
 
request_handlers.register_lazy(
620
 
    'Repository.insert_stream_locked', 'bzrlib.smart.repository', 'SmartServerRepositoryInsertStreamLocked')
621
 
request_handlers.register_lazy(
622
 
    'Repository.is_shared', 'bzrlib.smart.repository', 'SmartServerRepositoryIsShared')
623
 
request_handlers.register_lazy(
624
 
    'Repository.lock_write', 'bzrlib.smart.repository', 'SmartServerRepositoryLockWrite')
625
 
request_handlers.register_lazy(
626
 
    'Repository.set_make_working_trees', 'bzrlib.smart.repository',
627
 
    'SmartServerRepositorySetMakeWorkingTrees')
628
 
request_handlers.register_lazy(
629
 
    'Repository.unlock', 'bzrlib.smart.repository', 'SmartServerRepositoryUnlock')
630
 
request_handlers.register_lazy(
631
 
    'Repository.get_rev_id_for_revno', 'bzrlib.smart.repository',
632
 
    'SmartServerRepositoryGetRevIdForRevno')
633
 
request_handlers.register_lazy(
634
 
    'Repository.get_stream', 'bzrlib.smart.repository',
635
 
    'SmartServerRepositoryGetStream')
636
 
request_handlers.register_lazy(
637
 
    'Repository.get_stream_1.19', 'bzrlib.smart.repository',
638
 
    'SmartServerRepositoryGetStream_1_19')
639
 
request_handlers.register_lazy(
640
 
    'Repository.tarball', 'bzrlib.smart.repository',
641
 
    'SmartServerRepositoryTarball')
642
 
request_handlers.register_lazy(
643
265
    'rmdir', 'bzrlib.smart.vfs', 'RmdirRequest')
644
266
request_handlers.register_lazy(
645
267
    'stat', 'bzrlib.smart.vfs', 'StatRequest')
646
 
request_handlers.register_lazy(
647
 
    'Transport.is_readonly', 'bzrlib.smart.request', 'SmartServerIsReadonly')