~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/smart/request.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2008-03-16 14:01:20 UTC
  • mfrom: (3280.2.5 integration)
  • Revision ID: pqm@pqm.ubuntu.com-20080316140120-i3yq8yr1l66m11h7
Start 1.4 development

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., 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.
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
16
 
 
17
"""Basic server-side logic for dealing with requests."""
32
18
 
33
19
 
34
20
import tempfile
35
 
import threading
36
21
 
37
22
from bzrlib import (
38
23
    bzrdir,
39
24
    errors,
40
25
    registry,
41
26
    revision,
42
 
    trace,
43
 
    urlutils,
44
27
    )
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()
 
28
from bzrlib.bundle.serializer import write_bundle
76
29
 
77
30
 
78
31
class SmartServerRequest(object):
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='/'):
 
32
    """Base class for request handlers."""
 
33
 
 
34
    def __init__(self, backing_transport):
90
35
        """Constructor.
91
36
 
92
37
        :param backing_transport: the base transport to be used when performing
93
38
            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 '/'.
99
39
        """
100
40
        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 = []
108
41
 
109
42
    def _check_enabled(self):
110
43
        """Raises DisabledMethod if this method is disabled."""
112
45
 
113
46
    def do(self, *args):
114
47
        """Mandatory extension point for SmartServerRequest subclasses.
115
 
 
 
48
        
116
49
        Subclasses must implement this.
117
 
 
 
50
        
118
51
        This should return a SmartServerResponse if this command expects to
119
52
        receive no body.
120
53
        """
135
68
        """Called if the client sends a body with the request.
136
69
 
137
70
        The do() method is still called, and must have returned None.
138
 
 
 
71
        
139
72
        Must return a SmartServerResponse.
140
73
        """
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)
 
74
        # TODO: if a client erroneously sends a request that shouldn't have a
 
75
        # body, what to do?  Probably SmartServerRequestHandler should catch
 
76
        # this NotImplementedError and translate it into a 'bad request' error
 
77
        # to send to the client.
 
78
        raise NotImplementedError(self.do_body)
199
79
 
200
80
 
201
81
class SmartServerResponse(object):
202
82
    """A response to a client request.
203
 
 
 
83
    
204
84
    This base class should not be used. Instead use
205
85
    SuccessfulSmartServerResponse and FailedSmartServerResponse as appropriate.
206
86
    """
228
108
                other.body_stream is self.body_stream)
229
109
 
230
110
    def __repr__(self):
231
 
        return "<%s args=%r body=%r>" % (self.__class__.__name__,
232
 
            self.args, self.body)
 
111
        return ("<SmartServerResponse successful=%s args=%r body=%r>"
 
112
                % (self.is_successful(), self.args, self.body))
233
113
 
234
114
 
235
115
class FailedSmartServerResponse(SmartServerResponse):
250
130
 
251
131
class SmartServerRequestHandler(object):
252
132
    """Protocol logic for smart server.
253
 
 
 
133
    
254
134
    This doesn't handle serialization at all, it just processes requests and
255
135
    creates responses.
256
136
    """
265
145
    # TODO: Better way of representing the body for commands that take it,
266
146
    # and allow it to be streamed into the server.
267
147
 
268
 
    def __init__(self, backing_transport, commands, root_client_path):
 
148
    def __init__(self, backing_transport, commands):
269
149
        """Constructor.
270
150
 
271
151
        :param backing_transport: a Transport to handle requests for.
273
153
            subclasses. e.g. bzrlib.transport.smart.vfs.vfs_commands.
274
154
        """
275
155
        self._backing_transport = backing_transport
276
 
        self._root_client_path = root_client_path
277
156
        self._commands = commands
 
157
        self._body_bytes = ''
278
158
        self.response = None
279
159
        self.finished_reading = False
280
160
        self._command = None
281
161
 
282
162
    def accept_body(self, bytes):
283
163
        """Accept body data."""
284
 
        self._run_handler_code(self._command.do_chunk, (bytes,), {})
285
 
 
 
164
 
 
165
        # TODO: This should be overriden for each command that desired body data
 
166
        # to handle the right format of that data, i.e. plain bytes, a bundle,
 
167
        # etc.  The deserialisation into that format should be done in the
 
168
        # Protocol object.
 
169
 
 
170
        # default fallback is to accumulate bytes.
 
171
        self._body_bytes += bytes
 
172
        
286
173
    def end_of_body(self):
287
174
        """No more body data will be received."""
288
 
        self._run_handler_code(self._command.do_end, (), {})
 
175
        self._run_handler_code(self._command.do_body, (self._body_bytes,), {})
289
176
        # cannot read after this.
290
177
        self.finished_reading = True
291
178
 
294
181
        try:
295
182
            command = self._commands.get(cmd)
296
183
        except LookupError:
297
 
            raise errors.UnknownSmartMethod(cmd)
298
 
        self._command = command(self._backing_transport, self._root_client_path)
 
184
            raise errors.SmartProtocolError("bad request %r" % (cmd,))
 
185
        self._command = command(self._backing_transport)
299
186
        self._run_handler_code(self._command.execute, args, {})
300
187
 
301
188
    def _run_handler_code(self, callable, args, kwargs):
318
205
        # XXX: most of this error conversion is VFS-related, and thus ought to
319
206
        # be in SmartServerVFSRequestHandler somewhere.
320
207
        try:
321
 
            self._command.setup_jail()
322
 
            try:
323
 
                return callable(*args, **kwargs)
324
 
            finally:
325
 
                self._command.teardown_jail()
326
 
        except (KeyboardInterrupt, SystemExit):
327
 
            raise
328
 
        except Exception, err:
329
 
            err_struct = _translate_error(err)
330
 
            return FailedSmartServerResponse(err_struct)
331
 
 
332
 
    def headers_received(self, headers):
333
 
        # Just a no-op at the moment.
334
 
        pass
335
 
 
336
 
    def args_received(self, args):
337
 
        cmd = args[0]
338
 
        args = args[1:]
339
 
        try:
340
 
            command = self._commands.get(cmd)
341
 
        except LookupError:
342
 
            raise errors.UnknownSmartMethod(cmd)
343
 
        self._command = command(self._backing_transport)
344
 
        self._run_handler_code(self._command.execute, args, {})
345
 
 
346
 
    def end_received(self):
347
 
        self._run_handler_code(self._command.do_end, (), {})
348
 
 
349
 
    def post_body_error_received(self, error_args):
350
 
        # Just a no-op at the moment.
351
 
        pass
352
 
 
353
 
 
354
 
def _translate_error(err):
355
 
    if isinstance(err, errors.NoSuchFile):
356
 
        return ('NoSuchFile', err.path)
357
 
    elif isinstance(err, errors.FileExists):
358
 
        return ('FileExists', err.path)
359
 
    elif isinstance(err, errors.DirectoryNotEmpty):
360
 
        return ('DirectoryNotEmpty', err.path)
361
 
    elif isinstance(err, errors.ShortReadvError):
362
 
        return ('ShortReadvError', err.path, str(err.offset), str(err.length),
363
 
                str(err.actual))
364
 
    elif isinstance(err, errors.UnstackableRepositoryFormat):
365
 
        return (('UnstackableRepositoryFormat', str(err.format), err.url))
366
 
    elif isinstance(err, errors.UnstackableBranchFormat):
367
 
        return ('UnstackableBranchFormat', str(err.format), err.url)
368
 
    elif isinstance(err, errors.NotStacked):
369
 
        return ('NotStacked',)
370
 
    elif isinstance(err, UnicodeError):
371
 
        # If it is a DecodeError, than most likely we are starting
372
 
        # with a plain string
373
 
        str_or_unicode = err.object
374
 
        if isinstance(str_or_unicode, unicode):
375
 
            # XXX: UTF-8 might have \x01 (our protocol v1 and v2 seperator
376
 
            # byte) in it, so this encoding could cause broken responses.
377
 
            # Newer clients use protocol v3, so will be fine.
378
 
            val = 'u:' + str_or_unicode.encode('utf-8')
379
 
        else:
380
 
            val = 's:' + str_or_unicode.encode('base64')
381
 
        # This handles UnicodeEncodeError or UnicodeDecodeError
382
 
        return (err.__class__.__name__, err.encoding, val, str(err.start),
383
 
                str(err.end), err.reason)
384
 
    elif isinstance(err, errors.TransportNotPossible):
385
 
        if err.msg == "readonly transport":
386
 
            return ('ReadOnlyError', )
387
 
    elif isinstance(err, errors.ReadError):
388
 
        # cannot read the file
389
 
        return ('ReadError', err.path)
390
 
    elif isinstance(err, errors.PermissionDenied):
391
 
        return ('PermissionDenied', err.path, err.extra)
392
 
    elif isinstance(err, errors.TokenMismatch):
393
 
        return ('TokenMismatch', err.given_token, err.lock_token)
394
 
    elif isinstance(err, errors.LockContention):
395
 
        return ('LockContention', err.lock, err.msg)
396
 
    # Unserialisable error.  Log it, and return a generic error
397
 
    trace.log_exception_quietly()
398
 
    return ('error', str(err))
 
208
            return callable(*args, **kwargs)
 
209
        except errors.NoSuchFile, e:
 
210
            return FailedSmartServerResponse(('NoSuchFile', e.path))
 
211
        except errors.FileExists, e:
 
212
            return FailedSmartServerResponse(('FileExists', e.path))
 
213
        except errors.DirectoryNotEmpty, e:
 
214
            return FailedSmartServerResponse(('DirectoryNotEmpty', e.path))
 
215
        except errors.ShortReadvError, e:
 
216
            return FailedSmartServerResponse(('ShortReadvError',
 
217
                e.path, str(e.offset), str(e.length), str(e.actual)))
 
218
        except UnicodeError, e:
 
219
            # If it is a DecodeError, than most likely we are starting
 
220
            # with a plain string
 
221
            str_or_unicode = e.object
 
222
            if isinstance(str_or_unicode, unicode):
 
223
                # XXX: UTF-8 might have \x01 (our seperator byte) in it.  We
 
224
                # should escape it somehow.
 
225
                val = 'u:' + str_or_unicode.encode('utf-8')
 
226
            else:
 
227
                val = 's:' + str_or_unicode.encode('base64')
 
228
            # This handles UnicodeEncodeError or UnicodeDecodeError
 
229
            return FailedSmartServerResponse((e.__class__.__name__,
 
230
                    e.encoding, val, str(e.start), str(e.end), e.reason))
 
231
        except errors.TransportNotPossible, e:
 
232
            if e.msg == "readonly transport":
 
233
                return FailedSmartServerResponse(('ReadOnlyError', ))
 
234
            else:
 
235
                raise
399
236
 
400
237
 
401
238
class HelloRequest(SmartServerRequest):
412
249
 
413
250
    def do(self, path, revision_id):
414
251
        # open transport relative to our base
415
 
        t = self.transport_from_client_path(path)
 
252
        t = self._backing_transport.clone(path)
416
253
        control, extra_path = bzrdir.BzrDir.open_containing_from_transport(t)
417
254
        repo = control.open_repository()
418
255
        tmpf = tempfile.TemporaryFile()
419
256
        base_revision = revision.NULL_REVISION
420
 
        serializer.write_bundle(repo, revision_id, base_revision, tmpf)
 
257
        write_bundle(repo, revision_id, base_revision, tmpf)
421
258
        tmpf.seek(0)
422
259
        return SuccessfulSmartServerResponse((), tmpf.read())
423
260
 
437
274
request_handlers.register_lazy(
438
275
    'append', 'bzrlib.smart.vfs', 'AppendRequest')
439
276
request_handlers.register_lazy(
440
 
    'Branch.get_config_file', 'bzrlib.smart.branch',
441
 
    'SmartServerBranchGetConfigFile')
442
 
request_handlers.register_lazy(
443
 
    'Branch.get_parent', 'bzrlib.smart.branch', 'SmartServerBranchGetParent')
444
 
request_handlers.register_lazy(
445
 
    'Branch.get_tags_bytes', 'bzrlib.smart.branch',
446
 
    'SmartServerBranchGetTagsBytes')
447
 
request_handlers.register_lazy(
448
 
    'Branch.get_stacked_on_url', 'bzrlib.smart.branch', 'SmartServerBranchRequestGetStackedOnURL')
 
277
    'Branch.get_config_file', 'bzrlib.smart.branch', 'SmartServerBranchGetConfigFile')
449
278
request_handlers.register_lazy(
450
279
    'Branch.last_revision_info', 'bzrlib.smart.branch', 'SmartServerBranchRequestLastRevisionInfo')
451
280
request_handlers.register_lazy(
452
281
    'Branch.lock_write', 'bzrlib.smart.branch', 'SmartServerBranchRequestLockWrite')
453
 
request_handlers.register_lazy( 'Branch.revision_history',
454
 
    'bzrlib.smart.branch', 'SmartServerRequestRevisionHistory')
455
 
request_handlers.register_lazy( 'Branch.set_config_option',
456
 
    'bzrlib.smart.branch', 'SmartServerBranchRequestSetConfigOption')
457
 
request_handlers.register_lazy( 'Branch.set_last_revision',
458
 
    'bzrlib.smart.branch', 'SmartServerBranchRequestSetLastRevision')
459
 
request_handlers.register_lazy(
460
 
    'Branch.set_last_revision_info', 'bzrlib.smart.branch',
461
 
    'SmartServerBranchRequestSetLastRevisionInfo')
462
 
request_handlers.register_lazy(
463
 
    'Branch.set_last_revision_ex', 'bzrlib.smart.branch',
464
 
    'SmartServerBranchRequestSetLastRevisionEx')
465
 
request_handlers.register_lazy(
466
 
    'Branch.set_parent_location', 'bzrlib.smart.branch',
467
 
    'SmartServerBranchRequestSetParentLocation')
 
282
request_handlers.register_lazy(
 
283
    'Branch.revision_history', 'bzrlib.smart.branch', 'SmartServerRequestRevisionHistory')
 
284
request_handlers.register_lazy(
 
285
    'Branch.set_last_revision', 'bzrlib.smart.branch', 'SmartServerBranchRequestSetLastRevision')
468
286
request_handlers.register_lazy(
469
287
    'Branch.unlock', 'bzrlib.smart.branch', 'SmartServerBranchRequestUnlock')
470
288
request_handlers.register_lazy(
471
 
    'BzrDir.cloning_metadir', 'bzrlib.smart.bzrdir',
472
 
    'SmartServerBzrDirRequestCloningMetaDir')
473
 
request_handlers.register_lazy(
474
 
    'BzrDir.create_branch', 'bzrlib.smart.bzrdir',
475
 
    'SmartServerRequestCreateBranch')
476
 
request_handlers.register_lazy(
477
 
    'BzrDir.create_repository', 'bzrlib.smart.bzrdir',
478
 
    'SmartServerRequestCreateRepository')
479
 
request_handlers.register_lazy(
480
 
    'BzrDir.find_repository', 'bzrlib.smart.bzrdir',
481
 
    'SmartServerRequestFindRepositoryV1')
482
 
request_handlers.register_lazy(
483
 
    'BzrDir.find_repositoryV2', 'bzrlib.smart.bzrdir',
484
 
    'SmartServerRequestFindRepositoryV2')
485
 
request_handlers.register_lazy(
486
 
    'BzrDir.find_repositoryV3', 'bzrlib.smart.bzrdir',
487
 
    'SmartServerRequestFindRepositoryV3')
488
 
request_handlers.register_lazy(
489
 
    'BzrDir.get_config_file', 'bzrlib.smart.bzrdir',
490
 
    'SmartServerBzrDirRequestConfigFile')
491
 
request_handlers.register_lazy(
492
 
    'BzrDirFormat.initialize', 'bzrlib.smart.bzrdir',
493
 
    'SmartServerRequestInitializeBzrDir')
494
 
request_handlers.register_lazy(
495
 
    'BzrDirFormat.initialize_ex', 'bzrlib.smart.bzrdir',
496
 
    'SmartServerRequestBzrDirInitializeEx')
497
 
request_handlers.register_lazy(
498
 
    'BzrDir.open', 'bzrlib.smart.bzrdir', 'SmartServerRequestOpenBzrDir')
499
 
request_handlers.register_lazy(
500
 
    'BzrDir.open_branch', 'bzrlib.smart.bzrdir',
501
 
    'SmartServerRequestOpenBranch')
502
 
request_handlers.register_lazy(
503
 
    'BzrDir.open_branchV2', 'bzrlib.smart.bzrdir',
504
 
    'SmartServerRequestOpenBranchV2')
 
289
    'BzrDir.find_repository', 'bzrlib.smart.bzrdir', 'SmartServerRequestFindRepositoryV1')
 
290
request_handlers.register_lazy(
 
291
    'BzrDir.find_repositoryV2', 'bzrlib.smart.bzrdir', 'SmartServerRequestFindRepositoryV2')
 
292
request_handlers.register_lazy(
 
293
    'BzrDirFormat.initialize', 'bzrlib.smart.bzrdir', 'SmartServerRequestInitializeBzrDir')
 
294
request_handlers.register_lazy(
 
295
    'BzrDir.open_branch', 'bzrlib.smart.bzrdir', 'SmartServerRequestOpenBranch')
505
296
request_handlers.register_lazy(
506
297
    'delete', 'bzrlib.smart.vfs', 'DeleteRequest')
507
298
request_handlers.register_lazy(
528
319
    'readv', 'bzrlib.smart.vfs', 'ReadvRequest')
529
320
request_handlers.register_lazy(
530
321
    'rename', 'bzrlib.smart.vfs', 'RenameRequest')
531
 
request_handlers.register_lazy(
532
 
    'PackRepository.autopack', 'bzrlib.smart.packrepository',
533
 
    'SmartServerPackRepositoryAutopack')
534
322
request_handlers.register_lazy('Repository.gather_stats',
535
323
                               'bzrlib.smart.repository',
536
324
                               'SmartServerRepositoryGatherStats')
538
326
                               'bzrlib.smart.repository',
539
327
                               'SmartServerRepositoryGetParentMap')
540
328
request_handlers.register_lazy(
 
329
    'Repository.stream_knit_data_for_revisions',
 
330
    'bzrlib.smart.repository',
 
331
    'SmartServerRepositoryStreamKnitDataForRevisions')
 
332
request_handlers.register_lazy(
 
333
    'Repository.stream_revisions_chunked',
 
334
    'bzrlib.smart.repository',
 
335
    'SmartServerRepositoryStreamRevisionsChunked')
 
336
request_handlers.register_lazy(
541
337
    'Repository.get_revision_graph', 'bzrlib.smart.repository', 'SmartServerRepositoryGetRevisionGraph')
542
338
request_handlers.register_lazy(
543
339
    'Repository.has_revision', 'bzrlib.smart.repository', 'SmartServerRequestHasRevision')
544
340
request_handlers.register_lazy(
545
 
    'Repository.insert_stream', 'bzrlib.smart.repository', 'SmartServerRepositoryInsertStream')
546
 
request_handlers.register_lazy(
547
 
    'Repository.insert_stream_locked', 'bzrlib.smart.repository', 'SmartServerRepositoryInsertStreamLocked')
548
 
request_handlers.register_lazy(
549
341
    'Repository.is_shared', 'bzrlib.smart.repository', 'SmartServerRepositoryIsShared')
550
342
request_handlers.register_lazy(
551
343
    'Repository.lock_write', 'bzrlib.smart.repository', 'SmartServerRepositoryLockWrite')
552
344
request_handlers.register_lazy(
553
 
    'Repository.set_make_working_trees', 'bzrlib.smart.repository',
554
 
    'SmartServerRepositorySetMakeWorkingTrees')
555
 
request_handlers.register_lazy(
556
345
    'Repository.unlock', 'bzrlib.smart.repository', 'SmartServerRepositoryUnlock')
557
346
request_handlers.register_lazy(
558
 
    'Repository.get_stream', 'bzrlib.smart.repository',
559
 
    'SmartServerRepositoryGetStream')
560
 
request_handlers.register_lazy(
561
347
    'Repository.tarball', 'bzrlib.smart.repository',
562
348
    'SmartServerRepositoryTarball')
563
349
request_handlers.register_lazy(
566
352
    'stat', 'bzrlib.smart.vfs', 'StatRequest')
567
353
request_handlers.register_lazy(
568
354
    'Transport.is_readonly', 'bzrlib.smart.request', 'SmartServerIsReadonly')
 
355
request_handlers.register_lazy(
 
356
    'BzrDir.open', 'bzrlib.smart.bzrdir', 'SmartServerRequestOpenBzrDir')