~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/smart/request.py

  • Committer: Danny van Heumen
  • Date: 2010-03-09 21:42:11 UTC
  • mto: (4634.139.5 2.0)
  • mto: This revision was merged to the branch mainline in revision 5160.
  • Revision ID: danny@dannyvanheumen.nl-20100309214211-iqh42x6qcikgd9p3
Reverted now-useless TODO list.

Show diffs side-by-side

added added

removed removed

Lines of Context:
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."""
 
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.
18
32
 
19
33
 
20
34
import tempfile
 
35
import threading
21
36
 
22
37
from bzrlib import (
23
38
    bzrdir,
24
39
    errors,
25
40
    registry,
26
41
    revision,
 
42
    trace,
 
43
    urlutils,
27
44
    )
28
 
from bzrlib.bundle.serializer import write_bundle
 
45
from bzrlib.lazy_import import lazy_import
 
46
lazy_import(globals(), """
 
47
from bzrlib.bundle import serializer
 
48
""")
 
49
 
 
50
 
 
51
jail_info = threading.local()
 
52
jail_info.transports = None
 
53
 
 
54
 
 
55
def _install_hook():
 
56
    bzrdir.BzrDir.hooks.install_named_hook(
 
57
        'pre_open', _pre_open_hook, 'checking server jail')
 
58
 
 
59
 
 
60
def _pre_open_hook(transport):
 
61
    allowed_transports = getattr(jail_info, 'transports', None)
 
62
    if allowed_transports is None:
 
63
        return
 
64
    abspath = transport.base
 
65
    for allowed_transport in allowed_transports:
 
66
        try:
 
67
            allowed_transport.relpath(abspath)
 
68
        except errors.PathNotChild:
 
69
            continue
 
70
        else:
 
71
            return
 
72
    raise errors.JailBreak(abspath)
 
73
 
 
74
 
 
75
_install_hook()
29
76
 
30
77
 
31
78
class SmartServerRequest(object):
32
 
    """Base class for request handlers."""
33
 
 
34
 
    def __init__(self, backing_transport):
 
79
    """Base class for request handlers.
 
80
 
 
81
    To define a new request, subclass this class and override the `do` method
 
82
    (and if appropriate, `do_body` as well).  Request implementors should take
 
83
    care to call `translate_client_path` and `transport_from_client_path` as
 
84
    appropriate when dealing with paths received from the client.
 
85
    """
 
86
    # XXX: rename this class to BaseSmartServerRequestHandler ?  A request
 
87
    # *handler* is a different concept to the request.
 
88
 
 
89
    def __init__(self, backing_transport, root_client_path='/'):
35
90
        """Constructor.
36
91
 
37
92
        :param backing_transport: the base transport to be used when performing
38
93
            this request.
 
94
        :param root_client_path: the client path that maps to the root of
 
95
            backing_transport.  This is used to interpret relpaths received
 
96
            from the client.  Clients will not be able to refer to paths above
 
97
            this root.  If root_client_path is None, then no translation will
 
98
            be performed on client paths.  Default is '/'.
39
99
        """
40
100
        self._backing_transport = backing_transport
 
101
        if root_client_path is not None:
 
102
            if not root_client_path.startswith('/'):
 
103
                root_client_path = '/' + root_client_path
 
104
            if not root_client_path.endswith('/'):
 
105
                root_client_path += '/'
 
106
        self._root_client_path = root_client_path
 
107
        self._body_chunks = []
41
108
 
42
109
    def _check_enabled(self):
43
110
        """Raises DisabledMethod if this method is disabled."""
45
112
 
46
113
    def do(self, *args):
47
114
        """Mandatory extension point for SmartServerRequest subclasses.
48
 
        
 
115
 
49
116
        Subclasses must implement this.
50
 
        
 
117
 
51
118
        This should return a SmartServerResponse if this command expects to
52
119
        receive no body.
53
120
        """
66
133
 
67
134
    def do_body(self, body_bytes):
68
135
        """Called if the client sends a body with the request.
69
 
        
 
136
 
 
137
        The do() method is still called, and must have returned None.
 
138
 
70
139
        Must return a SmartServerResponse.
71
140
        """
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)
 
141
        if body_bytes != '':
 
142
            raise errors.SmartProtocolError('Request does not expect a body')
 
143
 
 
144
    def do_chunk(self, chunk_bytes):
 
145
        """Called with each body chunk if the request has a streamed body.
 
146
 
 
147
        The do() method is still called, and must have returned None.
 
148
        """
 
149
        self._body_chunks.append(chunk_bytes)
 
150
 
 
151
    def do_end(self):
 
152
        """Called when the end of the request has been received."""
 
153
        body_bytes = ''.join(self._body_chunks)
 
154
        self._body_chunks = None
 
155
        return self.do_body(body_bytes)
 
156
 
 
157
    def setup_jail(self):
 
158
        jail_info.transports = [self._backing_transport]
 
159
 
 
160
    def teardown_jail(self):
 
161
        jail_info.transports = None
 
162
 
 
163
    def translate_client_path(self, client_path):
 
164
        """Translate a path received from a network client into a local
 
165
        relpath.
 
166
 
 
167
        All paths received from the client *must* be translated.
 
168
 
 
169
        :param client_path: the path from the client.
 
170
        :returns: a relpath that may be used with self._backing_transport
 
171
            (unlike the untranslated client_path, which must not be used with
 
172
            the backing transport).
 
173
        """
 
174
        if self._root_client_path is None:
 
175
            # no translation necessary!
 
176
            return client_path
 
177
        if not client_path.startswith('/'):
 
178
            client_path = '/' + client_path
 
179
        if client_path + '/' == self._root_client_path:
 
180
            return '.'
 
181
        if client_path.startswith(self._root_client_path):
 
182
            path = client_path[len(self._root_client_path):]
 
183
            relpath = urlutils.joinpath('/', path)
 
184
            if not relpath.startswith('/'):
 
185
                raise ValueError(relpath)
 
186
            return '.' + relpath
 
187
        else:
 
188
            raise errors.PathNotChild(client_path, self._root_client_path)
 
189
 
 
190
    def transport_from_client_path(self, client_path):
 
191
        """Get a backing transport corresponding to the location referred to by
 
192
        a network client.
 
193
 
 
194
        :seealso: translate_client_path
 
195
        :returns: a transport cloned from self._backing_transport
 
196
        """
 
197
        relpath = self.translate_client_path(client_path)
 
198
        return self._backing_transport.clone(relpath)
77
199
 
78
200
 
79
201
class SmartServerResponse(object):
80
202
    """A response to a client request.
81
 
    
 
203
 
82
204
    This base class should not be used. Instead use
83
205
    SuccessfulSmartServerResponse and FailedSmartServerResponse as appropriate.
84
206
    """
85
207
 
86
 
    def __init__(self, args, body=None):
 
208
    def __init__(self, args, body=None, body_stream=None):
 
209
        """Constructor.
 
210
 
 
211
        :param args: tuple of response arguments.
 
212
        :param body: string of a response body.
 
213
        :param body_stream: iterable of bytestrings to be streamed to the
 
214
            client.
 
215
        """
87
216
        self.args = args
 
217
        if body is not None and body_stream is not None:
 
218
            raise errors.BzrError(
 
219
                "'body' and 'body_stream' are mutually exclusive.")
88
220
        self.body = body
 
221
        self.body_stream = body_stream
89
222
 
90
223
    def __eq__(self, other):
91
224
        if other is None:
92
225
            return False
93
 
        return other.args == self.args and other.body == self.body
 
226
        return (other.args == self.args and
 
227
                other.body == self.body and
 
228
                other.body_stream is self.body_stream)
94
229
 
95
230
    def __repr__(self):
96
 
        return "<SmartServerResponse args=%r body=%r>" % (self.is_successful(), 
 
231
        return "<%s args=%r body=%r>" % (self.__class__.__name__,
97
232
            self.args, self.body)
98
233
 
99
234
 
115
250
 
116
251
class SmartServerRequestHandler(object):
117
252
    """Protocol logic for smart server.
118
 
    
 
253
 
119
254
    This doesn't handle serialization at all, it just processes requests and
120
255
    creates responses.
121
256
    """
130
265
    # TODO: Better way of representing the body for commands that take it,
131
266
    # and allow it to be streamed into the server.
132
267
 
133
 
    def __init__(self, backing_transport, commands):
 
268
    def __init__(self, backing_transport, commands, root_client_path):
134
269
        """Constructor.
135
270
 
136
271
        :param backing_transport: a Transport to handle requests for.
138
273
            subclasses. e.g. bzrlib.transport.smart.vfs.vfs_commands.
139
274
        """
140
275
        self._backing_transport = backing_transport
 
276
        self._root_client_path = root_client_path
141
277
        self._commands = commands
142
 
        self._body_bytes = ''
143
278
        self.response = None
144
279
        self.finished_reading = False
145
280
        self._command = None
146
281
 
147
282
    def accept_body(self, bytes):
148
283
        """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
 
        
 
284
        if self._command is None:
 
285
            # no active command object, so ignore the event.
 
286
            return
 
287
        self._run_handler_code(self._command.do_chunk, (bytes,), {})
 
288
 
158
289
    def end_of_body(self):
159
290
        """No more body data will be received."""
160
 
        self._run_handler_code(self._command.do_body, (self._body_bytes,), {})
 
291
        self._run_handler_code(self._command.do_end, (), {})
161
292
        # cannot read after this.
162
293
        self.finished_reading = True
163
294
 
166
297
        try:
167
298
            command = self._commands.get(cmd)
168
299
        except LookupError:
169
 
            raise errors.SmartProtocolError("bad request %r" % (cmd,))
170
 
        self._command = command(self._backing_transport)
 
300
            raise errors.UnknownSmartMethod(cmd)
 
301
        self._command = command(self._backing_transport, self._root_client_path)
171
302
        self._run_handler_code(self._command.execute, args, {})
172
303
 
173
304
    def _run_handler_code(self, callable, args, kwargs):
190
321
        # XXX: most of this error conversion is VFS-related, and thus ought to
191
322
        # be in SmartServerVFSRequestHandler somewhere.
192
323
        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
 
324
            self._command.setup_jail()
 
325
            try:
 
326
                return callable(*args, **kwargs)
 
327
            finally:
 
328
                self._command.teardown_jail()
 
329
        except (KeyboardInterrupt, SystemExit):
 
330
            raise
 
331
        except Exception, err:
 
332
            err_struct = _translate_error(err)
 
333
            return FailedSmartServerResponse(err_struct)
 
334
 
 
335
    def headers_received(self, headers):
 
336
        # Just a no-op at the moment.
 
337
        pass
 
338
 
 
339
    def args_received(self, args):
 
340
        cmd = args[0]
 
341
        args = args[1:]
 
342
        try:
 
343
            command = self._commands.get(cmd)
 
344
        except LookupError:
 
345
            raise errors.UnknownSmartMethod(cmd)
 
346
        self._command = command(self._backing_transport)
 
347
        self._run_handler_code(self._command.execute, args, {})
 
348
 
 
349
    def end_received(self):
 
350
        if self._command is None:
 
351
            # no active command object, so ignore the event.
 
352
            return
 
353
        self._run_handler_code(self._command.do_end, (), {})
 
354
 
 
355
    def post_body_error_received(self, error_args):
 
356
        # Just a no-op at the moment.
 
357
        pass
 
358
 
 
359
 
 
360
def _translate_error(err):
 
361
    if isinstance(err, errors.NoSuchFile):
 
362
        return ('NoSuchFile', err.path)
 
363
    elif isinstance(err, errors.FileExists):
 
364
        return ('FileExists', err.path)
 
365
    elif isinstance(err, errors.DirectoryNotEmpty):
 
366
        return ('DirectoryNotEmpty', err.path)
 
367
    elif isinstance(err, errors.IncompatibleRepositories):
 
368
        return ('IncompatibleRepositories', str(err.source), str(err.target),
 
369
            str(err.details))
 
370
    elif isinstance(err, errors.ShortReadvError):
 
371
        return ('ShortReadvError', err.path, str(err.offset), str(err.length),
 
372
                str(err.actual))
 
373
    elif isinstance(err, errors.UnstackableRepositoryFormat):
 
374
        return (('UnstackableRepositoryFormat', str(err.format), err.url))
 
375
    elif isinstance(err, errors.UnstackableBranchFormat):
 
376
        return ('UnstackableBranchFormat', str(err.format), err.url)
 
377
    elif isinstance(err, errors.NotStacked):
 
378
        return ('NotStacked',)
 
379
    elif isinstance(err, UnicodeError):
 
380
        # If it is a DecodeError, than most likely we are starting
 
381
        # with a plain string
 
382
        str_or_unicode = err.object
 
383
        if isinstance(str_or_unicode, unicode):
 
384
            # XXX: UTF-8 might have \x01 (our protocol v1 and v2 seperator
 
385
            # byte) in it, so this encoding could cause broken responses.
 
386
            # Newer clients use protocol v3, so will be fine.
 
387
            val = 'u:' + str_or_unicode.encode('utf-8')
 
388
        else:
 
389
            val = 's:' + str_or_unicode.encode('base64')
 
390
        # This handles UnicodeEncodeError or UnicodeDecodeError
 
391
        return (err.__class__.__name__, err.encoding, val, str(err.start),
 
392
                str(err.end), err.reason)
 
393
    elif isinstance(err, errors.TransportNotPossible):
 
394
        if err.msg == "readonly transport":
 
395
            return ('ReadOnlyError', )
 
396
    elif isinstance(err, errors.ReadError):
 
397
        # cannot read the file
 
398
        return ('ReadError', err.path)
 
399
    elif isinstance(err, errors.PermissionDenied):
 
400
        return ('PermissionDenied', err.path, err.extra)
 
401
    elif isinstance(err, errors.TokenMismatch):
 
402
        return ('TokenMismatch', err.given_token, err.lock_token)
 
403
    elif isinstance(err, errors.LockContention):
 
404
        return ('LockContention',)
 
405
    # Unserialisable error.  Log it, and return a generic error
 
406
    trace.log_exception_quietly()
 
407
    return ('error', str(err))
221
408
 
222
409
 
223
410
class HelloRequest(SmartServerRequest):
234
421
 
235
422
    def do(self, path, revision_id):
236
423
        # open transport relative to our base
237
 
        t = self._backing_transport.clone(path)
 
424
        t = self.transport_from_client_path(path)
238
425
        control, extra_path = bzrdir.BzrDir.open_containing_from_transport(t)
239
426
        repo = control.open_repository()
240
427
        tmpf = tempfile.TemporaryFile()
241
428
        base_revision = revision.NULL_REVISION
242
 
        write_bundle(repo, revision_id, base_revision, tmpf)
 
429
        serializer.write_bundle(repo, revision_id, base_revision, tmpf)
243
430
        tmpf.seek(0)
244
431
        return SuccessfulSmartServerResponse((), tmpf.read())
245
432
 
259
446
request_handlers.register_lazy(
260
447
    'append', 'bzrlib.smart.vfs', 'AppendRequest')
261
448
request_handlers.register_lazy(
262
 
    'Branch.get_config_file', 'bzrlib.smart.branch', 'SmartServerBranchGetConfigFile')
 
449
    'Branch.get_config_file', 'bzrlib.smart.branch',
 
450
    'SmartServerBranchGetConfigFile')
 
451
request_handlers.register_lazy(
 
452
    'Branch.get_parent', 'bzrlib.smart.branch', 'SmartServerBranchGetParent')
 
453
request_handlers.register_lazy(
 
454
    'Branch.get_tags_bytes', 'bzrlib.smart.branch',
 
455
    'SmartServerBranchGetTagsBytes')
 
456
request_handlers.register_lazy(
 
457
    'Branch.set_tags_bytes', 'bzrlib.smart.branch',
 
458
    'SmartServerBranchSetTagsBytes')
 
459
request_handlers.register_lazy(
 
460
    'Branch.get_stacked_on_url', 'bzrlib.smart.branch', 'SmartServerBranchRequestGetStackedOnURL')
263
461
request_handlers.register_lazy(
264
462
    'Branch.last_revision_info', 'bzrlib.smart.branch', 'SmartServerBranchRequestLastRevisionInfo')
265
463
request_handlers.register_lazy(
266
464
    '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')
 
465
request_handlers.register_lazy( 'Branch.revision_history',
 
466
    'bzrlib.smart.branch', 'SmartServerRequestRevisionHistory')
 
467
request_handlers.register_lazy( 'Branch.set_config_option',
 
468
    'bzrlib.smart.branch', 'SmartServerBranchRequestSetConfigOption')
 
469
request_handlers.register_lazy( 'Branch.set_last_revision',
 
470
    'bzrlib.smart.branch', 'SmartServerBranchRequestSetLastRevision')
 
471
request_handlers.register_lazy(
 
472
    'Branch.set_last_revision_info', 'bzrlib.smart.branch',
 
473
    'SmartServerBranchRequestSetLastRevisionInfo')
 
474
request_handlers.register_lazy(
 
475
    'Branch.set_last_revision_ex', 'bzrlib.smart.branch',
 
476
    'SmartServerBranchRequestSetLastRevisionEx')
 
477
request_handlers.register_lazy(
 
478
    'Branch.set_parent_location', 'bzrlib.smart.branch',
 
479
    'SmartServerBranchRequestSetParentLocation')
271
480
request_handlers.register_lazy(
272
481
    'Branch.unlock', 'bzrlib.smart.branch', 'SmartServerBranchRequestUnlock')
273
482
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')
 
483
    'BzrDir.cloning_metadir', 'bzrlib.smart.bzrdir',
 
484
    'SmartServerBzrDirRequestCloningMetaDir')
 
485
request_handlers.register_lazy(
 
486
    'BzrDir.create_branch', 'bzrlib.smart.bzrdir',
 
487
    'SmartServerRequestCreateBranch')
 
488
request_handlers.register_lazy(
 
489
    'BzrDir.create_repository', 'bzrlib.smart.bzrdir',
 
490
    'SmartServerRequestCreateRepository')
 
491
request_handlers.register_lazy(
 
492
    'BzrDir.find_repository', 'bzrlib.smart.bzrdir',
 
493
    'SmartServerRequestFindRepositoryV1')
 
494
request_handlers.register_lazy(
 
495
    'BzrDir.find_repositoryV2', 'bzrlib.smart.bzrdir',
 
496
    'SmartServerRequestFindRepositoryV2')
 
497
request_handlers.register_lazy(
 
498
    'BzrDir.find_repositoryV3', 'bzrlib.smart.bzrdir',
 
499
    'SmartServerRequestFindRepositoryV3')
 
500
request_handlers.register_lazy(
 
501
    'BzrDir.get_config_file', 'bzrlib.smart.bzrdir',
 
502
    'SmartServerBzrDirRequestConfigFile')
 
503
request_handlers.register_lazy(
 
504
    'BzrDirFormat.initialize', 'bzrlib.smart.bzrdir',
 
505
    'SmartServerRequestInitializeBzrDir')
 
506
request_handlers.register_lazy(
 
507
    'BzrDirFormat.initialize_ex_1.16', 'bzrlib.smart.bzrdir',
 
508
    'SmartServerRequestBzrDirInitializeEx')
 
509
request_handlers.register_lazy(
 
510
    'BzrDir.open', 'bzrlib.smart.bzrdir', 'SmartServerRequestOpenBzrDir')
 
511
request_handlers.register_lazy(
 
512
    'BzrDir.open_branch', 'bzrlib.smart.bzrdir',
 
513
    'SmartServerRequestOpenBranch')
 
514
request_handlers.register_lazy(
 
515
    'BzrDir.open_branchV2', 'bzrlib.smart.bzrdir',
 
516
    'SmartServerRequestOpenBranchV2')
279
517
request_handlers.register_lazy(
280
518
    'delete', 'bzrlib.smart.vfs', 'DeleteRequest')
281
519
request_handlers.register_lazy(
302
540
    'readv', 'bzrlib.smart.vfs', 'ReadvRequest')
303
541
request_handlers.register_lazy(
304
542
    'rename', 'bzrlib.smart.vfs', 'RenameRequest')
 
543
request_handlers.register_lazy(
 
544
    'PackRepository.autopack', 'bzrlib.smart.packrepository',
 
545
    'SmartServerPackRepositoryAutopack')
305
546
request_handlers.register_lazy('Repository.gather_stats',
306
547
                               'bzrlib.smart.repository',
307
548
                               'SmartServerRepositoryGatherStats')
 
549
request_handlers.register_lazy('Repository.get_parent_map',
 
550
                               'bzrlib.smart.repository',
 
551
                               'SmartServerRepositoryGetParentMap')
308
552
request_handlers.register_lazy(
309
553
    'Repository.get_revision_graph', 'bzrlib.smart.repository', 'SmartServerRepositoryGetRevisionGraph')
310
554
request_handlers.register_lazy(
311
555
    'Repository.has_revision', 'bzrlib.smart.repository', 'SmartServerRequestHasRevision')
312
556
request_handlers.register_lazy(
 
557
    'Repository.insert_stream', 'bzrlib.smart.repository', 'SmartServerRepositoryInsertStream')
 
558
request_handlers.register_lazy(
 
559
    'Repository.insert_stream_1.19', 'bzrlib.smart.repository', 'SmartServerRepositoryInsertStream_1_19')
 
560
request_handlers.register_lazy(
 
561
    'Repository.insert_stream_locked', 'bzrlib.smart.repository', 'SmartServerRepositoryInsertStreamLocked')
 
562
request_handlers.register_lazy(
313
563
    'Repository.is_shared', 'bzrlib.smart.repository', 'SmartServerRepositoryIsShared')
314
564
request_handlers.register_lazy(
315
565
    'Repository.lock_write', 'bzrlib.smart.repository', 'SmartServerRepositoryLockWrite')
316
566
request_handlers.register_lazy(
 
567
    'Repository.set_make_working_trees', 'bzrlib.smart.repository',
 
568
    'SmartServerRepositorySetMakeWorkingTrees')
 
569
request_handlers.register_lazy(
317
570
    'Repository.unlock', 'bzrlib.smart.repository', 'SmartServerRepositoryUnlock')
318
571
request_handlers.register_lazy(
 
572
    'Repository.get_rev_id_for_revno', 'bzrlib.smart.repository',
 
573
    'SmartServerRepositoryGetRevIdForRevno')
 
574
request_handlers.register_lazy(
 
575
    'Repository.get_stream', 'bzrlib.smart.repository',
 
576
    'SmartServerRepositoryGetStream')
 
577
request_handlers.register_lazy(
 
578
    'Repository.get_stream_1.19', 'bzrlib.smart.repository',
 
579
    'SmartServerRepositoryGetStream_1_19')
 
580
request_handlers.register_lazy(
319
581
    'Repository.tarball', 'bzrlib.smart.repository',
320
582
    'SmartServerRepositoryTarball')
321
583
request_handlers.register_lazy(
324
586
    'stat', 'bzrlib.smart.vfs', 'StatRequest')
325
587
request_handlers.register_lazy(
326
588
    'Transport.is_readonly', 'bzrlib.smart.request', 'SmartServerIsReadonly')
327
 
request_handlers.register_lazy(
328
 
    'BzrDir.open', 'bzrlib.smart.bzrdir', 'SmartServerRequestOpenBzrDir')