~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/smart/request.py

(jameinel) Allow 'bzr serve' to interpret SIGHUP as a graceful shutdown.
 (bug #795025) (John A Meinel)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006, 2007 Canonical Ltd
 
1
# Copyright (C) 2006-2010 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
12
12
#
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
 
 
17
 
"""Basic server-side logic for dealing with requests.
18
 
 
19
 
**XXX**:
20
 
 
21
 
The class names are a little confusing: the protocol will instantiate a
22
 
SmartServerRequestHandler, whose dispatch_command method creates an instance of
23
 
a SmartServerRequest subclass.
24
 
 
25
 
The request_handlers registry tracks SmartServerRequest classes (rather than
26
 
SmartServerRequestHandler).
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
16
 
 
17
"""Infrastructure for server-side request handlers.
 
18
 
 
19
Interesting module attributes:
 
20
    * The request_handlers registry maps verb names to SmartServerRequest
 
21
      classes.
 
22
    * The jail_info threading.local() object is used to prevent accidental
 
23
      opening of BzrDirs outside of the backing transport, or any other
 
24
      transports placed in jail_info.transports.  The jail_info is reset on
 
25
      every call into a request handler (which can happen an arbitrary number
 
26
      of times during a request).
27
27
"""
28
28
 
29
 
import tempfile
 
29
# 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
30
35
 
31
36
from bzrlib import (
32
37
    bzrdir,
 
38
    debug,
33
39
    errors,
 
40
    osutils,
34
41
    registry,
35
42
    revision,
 
43
    trace,
36
44
    urlutils,
37
45
    )
38
46
from bzrlib.lazy_import import lazy_import
39
47
lazy_import(globals(), """
40
48
from bzrlib.bundle import serializer
 
49
 
 
50
import tempfile
 
51
import thread
41
52
""")
42
53
 
43
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()
 
80
 
 
81
 
44
82
class SmartServerRequest(object):
45
83
    """Base class for request handlers.
46
 
    
 
84
 
47
85
    To define a new request, subclass this class and override the `do` method
48
86
    (and if appropriate, `do_body` as well).  Request implementors should take
49
87
    care to call `translate_client_path` and `transport_from_client_path` as
52
90
    # XXX: rename this class to BaseSmartServerRequestHandler ?  A request
53
91
    # *handler* is a different concept to the request.
54
92
 
55
 
    def __init__(self, backing_transport, root_client_path='/'):
 
93
    def __init__(self, backing_transport, root_client_path='/', jail_root=None):
56
94
        """Constructor.
57
95
 
58
96
        :param backing_transport: the base transport to be used when performing
62
100
            from the client.  Clients will not be able to refer to paths above
63
101
            this root.  If root_client_path is None, then no translation will
64
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.
65
105
        """
66
106
        self._backing_transport = backing_transport
 
107
        if jail_root is None:
 
108
            jail_root = backing_transport
 
109
        self._jail_root = jail_root
67
110
        if root_client_path is not None:
68
111
            if not root_client_path.startswith('/'):
69
112
                root_client_path = '/' + root_client_path
78
121
 
79
122
    def do(self, *args):
80
123
        """Mandatory extension point for SmartServerRequest subclasses.
81
 
        
 
124
 
82
125
        Subclasses must implement this.
83
 
        
 
126
 
84
127
        This should return a SmartServerResponse if this command expects to
85
128
        receive no body.
86
129
        """
92
135
        It will return a SmartServerResponse if the command does not expect a
93
136
        body.
94
137
 
95
 
        :param *args: the arguments of the request.
 
138
        :param args: the arguments of the request.
96
139
        """
97
140
        self._check_enabled()
98
141
        return self.do(*args)
101
144
        """Called if the client sends a body with the request.
102
145
 
103
146
        The do() method is still called, and must have returned None.
104
 
        
 
147
 
105
148
        Must return a SmartServerResponse.
106
149
        """
107
150
        if body_bytes != '':
119
162
        body_bytes = ''.join(self._body_chunks)
120
163
        self._body_chunks = None
121
164
        return self.do_body(body_bytes)
122
 
    
 
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
 
123
172
    def translate_client_path(self, client_path):
124
173
        """Translate a path received from a network client into a local
125
174
        relpath.
136
185
            return client_path
137
186
        if not client_path.startswith('/'):
138
187
            client_path = '/' + client_path
 
188
        if client_path + '/' == self._root_client_path:
 
189
            return '.'
139
190
        if client_path.startswith(self._root_client_path):
140
191
            path = client_path[len(self._root_client_path):]
141
192
            relpath = urlutils.joinpath('/', path)
142
193
            if not relpath.startswith('/'):
143
194
                raise ValueError(relpath)
144
 
            return '.' + relpath
 
195
            return urlutils.escape('.' + relpath)
145
196
        else:
146
197
            raise errors.PathNotChild(client_path, self._root_client_path)
147
198
 
158
209
 
159
210
class SmartServerResponse(object):
160
211
    """A response to a client request.
161
 
    
 
212
 
162
213
    This base class should not be used. Instead use
163
214
    SuccessfulSmartServerResponse and FailedSmartServerResponse as appropriate.
164
215
    """
208
259
 
209
260
class SmartServerRequestHandler(object):
210
261
    """Protocol logic for smart server.
211
 
    
 
262
 
212
263
    This doesn't handle serialization at all, it just processes requests and
213
264
    creates responses.
214
265
    """
223
274
    # TODO: Better way of representing the body for commands that take it,
224
275
    # and allow it to be streamed into the server.
225
276
 
226
 
    def __init__(self, backing_transport, commands, root_client_path):
 
277
    def __init__(self, backing_transport, commands, root_client_path,
 
278
        jail_root=None):
227
279
        """Constructor.
228
280
 
229
281
        :param backing_transport: a Transport to handle requests for.
233
285
        self._backing_transport = backing_transport
234
286
        self._root_client_path = root_client_path
235
287
        self._commands = commands
 
288
        if jail_root is None:
 
289
            jail_root = backing_transport
 
290
        self._jail_root = jail_root
236
291
        self.response = None
237
292
        self.finished_reading = False
238
293
        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))
239
315
 
240
316
    def accept_body(self, bytes):
241
317
        """Accept body data."""
 
318
        if self._command is None:
 
319
            # no active command object, so ignore the event.
 
320
            return
242
321
        self._run_handler_code(self._command.do_chunk, (bytes,), {})
243
 
        
 
322
        if 'hpss' in debug.debug_flags:
 
323
            self._trace('accept body',
 
324
                        '%d bytes' % (len(bytes),), bytes)
 
325
 
244
326
    def end_of_body(self):
245
327
        """No more body data will be received."""
246
328
        self._run_handler_code(self._command.do_end, (), {})
247
329
        # cannot read after this.
248
330
        self.finished_reading = True
249
 
 
250
 
    def dispatch_command(self, cmd, args):
251
 
        """Deprecated compatibility method.""" # XXX XXX
252
 
        try:
253
 
            command = self._commands.get(cmd)
254
 
        except LookupError:
255
 
            raise errors.UnknownSmartMethod(cmd)
256
 
        self._command = command(self._backing_transport, self._root_client_path)
257
 
        self._run_handler_code(self._command.execute, args, {})
 
331
        if 'hpss' in debug.debug_flags:
 
332
            self._trace('end of body', '', include_time=True)
258
333
 
259
334
    def _run_handler_code(self, callable, args, kwargs):
260
335
        """Run some handler specific code 'callable'.
276
351
        # XXX: most of this error conversion is VFS-related, and thus ought to
277
352
        # be in SmartServerVFSRequestHandler somewhere.
278
353
        try:
279
 
            return callable(*args, **kwargs)
280
 
        except errors.NoSuchFile, e:
281
 
            return FailedSmartServerResponse(('NoSuchFile', e.path))
282
 
        except errors.FileExists, e:
283
 
            return FailedSmartServerResponse(('FileExists', e.path))
284
 
        except errors.DirectoryNotEmpty, e:
285
 
            return FailedSmartServerResponse(('DirectoryNotEmpty', e.path))
286
 
        except errors.ShortReadvError, e:
287
 
            return FailedSmartServerResponse(('ShortReadvError',
288
 
                e.path, str(e.offset), str(e.length), str(e.actual)))
289
 
        except errors.UnstackableRepositoryFormat, e:
290
 
            return FailedSmartServerResponse(('UnstackableRepositoryFormat',
291
 
                str(e.format), e.url))
292
 
        except errors.UnstackableBranchFormat, e:
293
 
            return FailedSmartServerResponse(('UnstackableBranchFormat',
294
 
                str(e.format), e.url))
295
 
        except errors.NotStacked, e:
296
 
            return FailedSmartServerResponse(('NotStacked',))
297
 
        except UnicodeError, e:
298
 
            # If it is a DecodeError, than most likely we are starting
299
 
            # with a plain string
300
 
            str_or_unicode = e.object
301
 
            if isinstance(str_or_unicode, unicode):
302
 
                # XXX: UTF-8 might have \x01 (our protocol v1 and v2 seperator
303
 
                # byte) in it, so this encoding could cause broken responses.
304
 
                # Newer clients use protocol v3, so will be fine.
305
 
                val = 'u:' + str_or_unicode.encode('utf-8')
306
 
            else:
307
 
                val = 's:' + str_or_unicode.encode('base64')
308
 
            # This handles UnicodeEncodeError or UnicodeDecodeError
309
 
            return FailedSmartServerResponse((e.__class__.__name__,
310
 
                    e.encoding, val, str(e.start), str(e.end), e.reason))
311
 
        except errors.TransportNotPossible, e:
312
 
            if e.msg == "readonly transport":
313
 
                return FailedSmartServerResponse(('ReadOnlyError', ))
314
 
            else:
315
 
                raise
316
 
        except errors.ReadError, e:
317
 
            # cannot read the file
318
 
            return FailedSmartServerResponse(('ReadError', e.path))
319
 
        except errors.PermissionDenied, e:
320
 
            return FailedSmartServerResponse(
321
 
                ('PermissionDenied', e.path, e.extra))
 
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)
322
364
 
323
365
    def headers_received(self, headers):
324
366
        # Just a no-op at the moment.
325
 
        pass
 
367
        if 'hpss' in debug.debug_flags:
 
368
            self._trace('headers', repr(headers))
326
369
 
327
370
    def args_received(self, args):
328
371
        cmd = args[0]
330
373
        try:
331
374
            command = self._commands.get(cmd)
332
375
        except LookupError:
 
376
            if 'hpss' in debug.debug_flags:
 
377
                self._trace('hpss unknown request', 
 
378
                            cmd, repr(args)[1:-1])
333
379
            raise errors.UnknownSmartMethod(cmd)
334
 
        self._command = command(self._backing_transport)
 
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)
335
390
        self._run_handler_code(self._command.execute, args, {})
336
391
 
337
392
    def end_received(self):
 
393
        if self._command is None:
 
394
            # no active command object, so ignore the event.
 
395
            return
338
396
        self._run_handler_code(self._command.do_end, (), {})
 
397
        if 'hpss' in debug.debug_flags:
 
398
            self._trace('end', '', include_time=True)
339
399
 
340
400
    def post_body_error_received(self, error_args):
341
401
        # Just a no-op at the moment.
342
402
        pass
343
403
 
344
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))
 
457
 
 
458
 
345
459
class HelloRequest(SmartServerRequest):
346
460
    """Answer a version request with the highest protocol version this server
347
461
    supports.
381
495
request_handlers.register_lazy(
382
496
    'append', 'bzrlib.smart.vfs', 'AppendRequest')
383
497
request_handlers.register_lazy(
384
 
    'Branch.get_config_file', 'bzrlib.smart.branch', 'SmartServerBranchGetConfigFile')
 
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')
385
511
request_handlers.register_lazy(
386
512
    'Branch.get_stacked_on_url', 'bzrlib.smart.branch', 'SmartServerBranchRequestGetStackedOnURL')
387
513
request_handlers.register_lazy(
388
514
    'Branch.last_revision_info', 'bzrlib.smart.branch', 'SmartServerBranchRequestLastRevisionInfo')
389
515
request_handlers.register_lazy(
390
516
    'Branch.lock_write', 'bzrlib.smart.branch', 'SmartServerBranchRequestLockWrite')
391
 
request_handlers.register_lazy(
392
 
    'Branch.revision_history', 'bzrlib.smart.branch', 'SmartServerRequestRevisionHistory')
393
 
request_handlers.register_lazy(
394
 
    'Branch.set_last_revision', 'bzrlib.smart.branch', 'SmartServerBranchRequestSetLastRevision')
 
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')
395
525
request_handlers.register_lazy(
396
526
    'Branch.set_last_revision_info', 'bzrlib.smart.branch',
397
527
    'SmartServerBranchRequestSetLastRevisionInfo')
399
529
    'Branch.set_last_revision_ex', 'bzrlib.smart.branch',
400
530
    'SmartServerBranchRequestSetLastRevisionEx')
401
531
request_handlers.register_lazy(
 
532
    'Branch.set_parent_location', 'bzrlib.smart.branch',
 
533
    'SmartServerBranchRequestSetParentLocation')
 
534
request_handlers.register_lazy(
402
535
    'Branch.unlock', 'bzrlib.smart.branch', 'SmartServerBranchRequestUnlock')
403
536
request_handlers.register_lazy(
404
 
    'BzrDir.find_repository', 'bzrlib.smart.bzrdir', 'SmartServerRequestFindRepositoryV1')
405
 
request_handlers.register_lazy(
406
 
    'BzrDir.find_repositoryV2', 'bzrlib.smart.bzrdir', 'SmartServerRequestFindRepositoryV2')
407
 
request_handlers.register_lazy(
408
 
    'BzrDirFormat.initialize', 'bzrlib.smart.bzrdir', 'SmartServerRequestInitializeBzrDir')
409
 
request_handlers.register_lazy(
410
 
    'BzrDir.open_branch', 'bzrlib.smart.bzrdir', 'SmartServerRequestOpenBranch')
 
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')
411
576
request_handlers.register_lazy(
412
577
    'delete', 'bzrlib.smart.vfs', 'DeleteRequest')
413
578
request_handlers.register_lazy(
448
613
request_handlers.register_lazy(
449
614
    'Repository.has_revision', 'bzrlib.smart.repository', 'SmartServerRequestHasRevision')
450
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(
451
622
    'Repository.is_shared', 'bzrlib.smart.repository', 'SmartServerRepositoryIsShared')
452
623
request_handlers.register_lazy(
453
624
    'Repository.lock_write', 'bzrlib.smart.repository', 'SmartServerRepositoryLockWrite')
454
625
request_handlers.register_lazy(
 
626
    'Repository.set_make_working_trees', 'bzrlib.smart.repository',
 
627
    'SmartServerRepositorySetMakeWorkingTrees')
 
628
request_handlers.register_lazy(
455
629
    'Repository.unlock', 'bzrlib.smart.repository', 'SmartServerRepositoryUnlock')
456
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(
457
640
    'Repository.tarball', 'bzrlib.smart.repository',
458
641
    'SmartServerRepositoryTarball')
459
642
request_handlers.register_lazy(
462
645
    'stat', 'bzrlib.smart.vfs', 'StatRequest')
463
646
request_handlers.register_lazy(
464
647
    'Transport.is_readonly', 'bzrlib.smart.request', 'SmartServerIsReadonly')
465
 
request_handlers.register_lazy(
466
 
    'BzrDir.open', 'bzrlib.smart.bzrdir', 'SmartServerRequestOpenBzrDir')