~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/smart/request.py

  • Committer: Andrew Bennetts
  • Date: 2009-02-11 07:06:42 UTC
  • mto: This revision was merged to the branch mainline in revision 4002.
  • Revision ID: andrew.bennetts@canonical.com-20090211070642-987ocatad8vcpo9n
Remove some cruft from test_smart_transport.

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