/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/smart/request.py

  • Committer: Vincent Ladeuil
  • Date: 2007-11-24 14:20:59 UTC
  • mto: (3928.1.1 bzr.integration)
  • mto: This revision was merged to the branch mainline in revision 3929.
  • Revision ID: v.ladeuil+lp@free.fr-20071124142059-2114qtsgfdv8g9p1
Ssl files needed for the test https server.

* bzrlib/tests/ssl_certs/create_ssls.py: 
Script to create the ssl keys and certificates.

* bzrlib/tests/ssl_certs/server.crt: 
Server certificate signed by the certificate authority.

* bzrlib/tests/ssl_certs/server.csr: 
Server certificate signing request.

* bzrlib/tests/ssl_certs/server_without_pass.key: 
Server key usable without password.

* bzrlib/tests/ssl_certs/server_with_pass.key: 
Server key.

* bzrlib/tests/ssl_certs/ca.key: 
Certificate authority private key.

* bzrlib/tests/ssl_certs/ca.crt: 
Certificate authority certificate.

* bzrlib/tests/ssl_certs/__init__.py: 
Provide access to ssl files (keys and certificates). 

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006-2011 Canonical Ltd
 
1
# Copyright (C) 2006, 2007 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
12
12
#
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
 
 
17
 
"""Infrastructure for server-side request handlers.
18
 
 
19
 
Interesting module attributes:
20
 
    * The request_handlers registry maps verb names to SmartServerRequest
21
 
      classes.
22
 
    * The jail_info threading.local() object is used to prevent accidental
23
 
      opening of BzrDirs outside of the backing transport, or any other
24
 
      transports placed in jail_info.transports.  The jail_info is reset on
25
 
      every call into a request handler (which can happen an arbitrary number
26
 
      of times during a request).
27
 
"""
28
 
 
29
 
from __future__ import absolute_import
30
 
 
31
 
# XXX: The class names are a little confusing: the protocol will instantiate a
32
 
# SmartServerRequestHandler, whose dispatch_command method creates an instance
33
 
# of a SmartServerRequest subclass.
34
 
 
35
 
 
36
 
import threading
37
 
try:
38
 
    from _thread import get_ident
39
 
except ImportError:  # Python < 3
40
 
    from thread import get_ident
41
 
 
42
 
from ... import (
43
 
    branch as _mod_branch,
44
 
    debug,
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
16
 
 
17
"""Basic server-side logic for dealing with requests."""
 
18
 
 
19
 
 
20
import tempfile
 
21
 
 
22
from bzrlib import (
 
23
    bzrdir,
45
24
    errors,
46
 
    osutils,
47
25
    registry,
48
26
    revision,
49
 
    trace,
50
 
    urlutils,
51
27
    )
52
 
from ...sixish import text_type
53
 
from ...lazy_import import lazy_import
54
 
lazy_import(globals(), """
55
 
from breezy.bzr import bzrdir
56
 
from breezy.bundle import serializer
57
 
 
58
 
import tempfile
59
 
""")
60
 
 
61
 
 
62
 
jail_info = threading.local()
63
 
jail_info.transports = None
64
 
 
65
 
 
66
 
class DisabledMethod(errors.InternalBzrError):
67
 
 
68
 
    _fmt = "The smart server method '%(class_name)s' is disabled."
69
 
 
70
 
    def __init__(self, class_name):
71
 
        errors.BzrError.__init__(self)
72
 
        self.class_name = class_name
73
 
 
74
 
 
75
 
def _install_hook():
76
 
    bzrdir.BzrDir.hooks.install_named_hook(
77
 
        'pre_open', _pre_open_hook, 'checking server jail')
78
 
 
79
 
 
80
 
def _pre_open_hook(transport):
81
 
    allowed_transports = getattr(jail_info, 'transports', None)
82
 
    if allowed_transports is None:
83
 
        return
84
 
    abspath = transport.base
85
 
    for allowed_transport in allowed_transports:
86
 
        try:
87
 
            allowed_transport.relpath(abspath)
88
 
        except errors.PathNotChild:
89
 
            continue
90
 
        else:
91
 
            return
92
 
    raise errors.JailBreak(abspath)
93
 
 
94
 
 
95
 
_install_hook()
 
28
from bzrlib.bundle.serializer import write_bundle
96
29
 
97
30
 
98
31
class SmartServerRequest(object):
99
 
    """Base class for request handlers.
100
 
 
101
 
    To define a new request, subclass this class and override the `do` method
102
 
    (and if appropriate, `do_body` as well).  Request implementors should take
103
 
    care to call `translate_client_path` and `transport_from_client_path` as
104
 
    appropriate when dealing with paths received from the client.
105
 
    """
106
 
    # XXX: rename this class to BaseSmartServerRequestHandler ?  A request
107
 
    # *handler* is a different concept to the request.
108
 
 
109
 
    def __init__(self, backing_transport, root_client_path='/', jail_root=None):
 
32
    """Base class for request handlers."""
 
33
 
 
34
    def __init__(self, backing_transport):
110
35
        """Constructor.
111
36
 
112
37
        :param backing_transport: the base transport to be used when performing
113
38
            this request.
114
 
        :param root_client_path: the client path that maps to the root of
115
 
            backing_transport.  This is used to interpret relpaths received
116
 
            from the client.  Clients will not be able to refer to paths above
117
 
            this root.  If root_client_path is None, then no translation will
118
 
            be performed on client paths.  Default is '/'.
119
 
        :param jail_root: if specified, the root of the BzrDir.open jail to use
120
 
            instead of backing_transport.
121
39
        """
122
40
        self._backing_transport = backing_transport
123
 
        if jail_root is None:
124
 
            jail_root = backing_transport
125
 
        self._jail_root = jail_root
126
 
        if root_client_path is not None:
127
 
            if not root_client_path.startswith('/'):
128
 
                root_client_path = '/' + root_client_path
129
 
            if not root_client_path.endswith('/'):
130
 
                root_client_path += '/'
131
 
        self._root_client_path = root_client_path
132
 
        self._body_chunks = []
133
41
 
134
42
    def _check_enabled(self):
135
43
        """Raises DisabledMethod if this method is disabled."""
137
45
 
138
46
    def do(self, *args):
139
47
        """Mandatory extension point for SmartServerRequest subclasses.
140
 
 
 
48
        
141
49
        Subclasses must implement this.
142
 
 
 
50
        
143
51
        This should return a SmartServerResponse if this command expects to
144
52
        receive no body.
145
53
        """
151
59
        It will return a SmartServerResponse if the command does not expect a
152
60
        body.
153
61
 
154
 
        :param args: the arguments of the request.
 
62
        :param *args: the arguments of the request.
155
63
        """
156
64
        self._check_enabled()
157
65
        return self.do(*args)
158
66
 
159
67
    def do_body(self, body_bytes):
160
68
        """Called if the client sends a body with the request.
161
 
 
162
 
        The do() method is still called, and must have returned None.
163
 
 
 
69
        
164
70
        Must return a SmartServerResponse.
165
71
        """
166
 
        if body_bytes != b'':
167
 
            raise errors.SmartProtocolError('Request does not expect a body')
168
 
 
169
 
    def do_chunk(self, chunk_bytes):
170
 
        """Called with each body chunk if the request has a streamed body.
171
 
 
172
 
        The do() method is still called, and must have returned None.
173
 
        """
174
 
        self._body_chunks.append(chunk_bytes)
175
 
 
176
 
    def do_end(self):
177
 
        """Called when the end of the request has been received."""
178
 
        body_bytes = b''.join(self._body_chunks)
179
 
        self._body_chunks = None
180
 
        return self.do_body(body_bytes)
181
 
 
182
 
    def setup_jail(self):
183
 
        jail_info.transports = [self._jail_root]
184
 
 
185
 
    def teardown_jail(self):
186
 
        jail_info.transports = None
187
 
 
188
 
    def translate_client_path(self, client_path):
189
 
        """Translate a path received from a network client into a local
190
 
        relpath.
191
 
 
192
 
        All paths received from the client *must* be translated.
193
 
 
194
 
        :param client_path: the path from the client.
195
 
        :returns: a relpath that may be used with self._backing_transport
196
 
            (unlike the untranslated client_path, which must not be used with
197
 
            the backing transport).
198
 
        """
199
 
        client_path = client_path.decode('utf-8')
200
 
        if self._root_client_path is None:
201
 
            # no translation necessary!
202
 
            return client_path
203
 
        if not client_path.startswith('/'):
204
 
            client_path = '/' + client_path
205
 
        if client_path + '/' == self._root_client_path:
206
 
            return '.'
207
 
        if client_path.startswith(self._root_client_path):
208
 
            path = client_path[len(self._root_client_path):]
209
 
            relpath = urlutils.joinpath('/', path)
210
 
            if not relpath.startswith('/'):
211
 
                raise ValueError(relpath)
212
 
            return urlutils.escape('.' + relpath)
213
 
        else:
214
 
            raise errors.PathNotChild(client_path, self._root_client_path)
215
 
 
216
 
    def transport_from_client_path(self, client_path):
217
 
        """Get a backing transport corresponding to the location referred to by
218
 
        a network client.
219
 
 
220
 
        :seealso: translate_client_path
221
 
        :returns: a transport cloned from self._backing_transport
222
 
        """
223
 
        relpath = self.translate_client_path(client_path)
224
 
        return self._backing_transport.clone(relpath)
 
72
        # TODO: if a client erroneously sends a request that shouldn't have a
 
73
        # body, what to do?  Probably SmartServerRequestHandler should catch
 
74
        # this NotImplementedError and translate it into a 'bad request' error
 
75
        # to send to the client.
 
76
        raise NotImplementedError(self.do_body)
225
77
 
226
78
 
227
79
class SmartServerResponse(object):
228
80
    """A response to a client request.
229
 
 
 
81
    
230
82
    This base class should not be used. Instead use
231
83
    SuccessfulSmartServerResponse and FailedSmartServerResponse as appropriate.
232
84
    """
254
106
                other.body_stream is self.body_stream)
255
107
 
256
108
    def __repr__(self):
257
 
        return "<%s args=%r body=%r>" % (self.__class__.__name__,
258
 
            self.args, self.body)
 
109
        return "<SmartServerResponse %r args=%r body=%r>" % (
 
110
            self.is_successful(), self.args, self.body)
259
111
 
260
112
 
261
113
class FailedSmartServerResponse(SmartServerResponse):
276
128
 
277
129
class SmartServerRequestHandler(object):
278
130
    """Protocol logic for smart server.
279
 
 
 
131
    
280
132
    This doesn't handle serialization at all, it just processes requests and
281
133
    creates responses.
282
134
    """
291
143
    # TODO: Better way of representing the body for commands that take it,
292
144
    # and allow it to be streamed into the server.
293
145
 
294
 
    def __init__(self, backing_transport, commands, root_client_path,
295
 
        jail_root=None):
 
146
    def __init__(self, backing_transport, commands):
296
147
        """Constructor.
297
148
 
298
149
        :param backing_transport: a Transport to handle requests for.
299
150
        :param commands: a registry mapping command names to SmartServerRequest
300
 
            subclasses. e.g. breezy.transport.smart.vfs.vfs_commands.
 
151
            subclasses. e.g. bzrlib.transport.smart.vfs.vfs_commands.
301
152
        """
302
153
        self._backing_transport = backing_transport
303
 
        self._root_client_path = root_client_path
304
154
        self._commands = commands
305
 
        if jail_root is None:
306
 
            jail_root = backing_transport
307
 
        self._jail_root = jail_root
 
155
        self._body_bytes = ''
308
156
        self.response = None
309
157
        self.finished_reading = False
310
158
        self._command = None
311
 
        if 'hpss' in debug.debug_flags:
312
 
            self._request_start_time = osutils.timer_func()
313
 
            self._thread_id = get_ident()
314
 
 
315
 
    def _trace(self, action, message, extra_bytes=None, include_time=False):
316
 
        # It is a bit of a shame that this functionality overlaps with that of
317
 
        # ProtocolThreeRequester._trace. However, there is enough difference
318
 
        # that just putting it in a helper doesn't help a lot. And some state
319
 
        # is taken from the instance.
320
 
        if include_time:
321
 
            t = '%5.3fs ' % (osutils.timer_func() - self._request_start_time)
322
 
        else:
323
 
            t = ''
324
 
        if extra_bytes is None:
325
 
            extra = ''
326
 
        else:
327
 
            extra = ' ' + repr(extra_bytes[:40])
328
 
            if len(extra) > 33:
329
 
                extra = extra[:29] + extra[-1] + '...'
330
 
        trace.mutter('%12s: [%s] %s%s%s'
331
 
                     % (action, self._thread_id, t, message, extra))
332
159
 
333
160
    def accept_body(self, bytes):
334
161
        """Accept body data."""
335
 
        if self._command is None:
336
 
            # no active command object, so ignore the event.
337
 
            return
338
 
        self._run_handler_code(self._command.do_chunk, (bytes,), {})
339
 
        if 'hpss' in debug.debug_flags:
340
 
            self._trace('accept body',
341
 
                        '%d bytes' % (len(bytes),), bytes)
342
 
 
 
162
 
 
163
        # TODO: This should be overriden for each command that desired body data
 
164
        # to handle the right format of that data, i.e. plain bytes, a bundle,
 
165
        # etc.  The deserialisation into that format should be done in the
 
166
        # Protocol object.
 
167
 
 
168
        # default fallback is to accumulate bytes.
 
169
        self._body_bytes += bytes
 
170
        
343
171
    def end_of_body(self):
344
172
        """No more body data will be received."""
345
 
        self._run_handler_code(self._command.do_end, (), {})
 
173
        self._run_handler_code(self._command.do_body, (self._body_bytes,), {})
346
174
        # cannot read after this.
347
175
        self.finished_reading = True
348
 
        if 'hpss' in debug.debug_flags:
349
 
            self._trace('end of body', '', include_time=True)
 
176
 
 
177
    def dispatch_command(self, cmd, args):
 
178
        """Deprecated compatibility method.""" # XXX XXX
 
179
        try:
 
180
            command = self._commands.get(cmd)
 
181
        except LookupError:
 
182
            raise errors.SmartProtocolError("bad request %r" % (cmd,))
 
183
        self._command = command(self._backing_transport)
 
184
        self._run_handler_code(self._command.execute, args, {})
350
185
 
351
186
    def _run_handler_code(self, callable, args, kwargs):
352
187
        """Run some handler specific code 'callable'.
368
203
        # XXX: most of this error conversion is VFS-related, and thus ought to
369
204
        # be in SmartServerVFSRequestHandler somewhere.
370
205
        try:
371
 
            self._command.setup_jail()
372
 
            try:
373
 
                return callable(*args, **kwargs)
374
 
            finally:
375
 
                self._command.teardown_jail()
376
 
        except (KeyboardInterrupt, SystemExit):
377
 
            raise
378
 
        except Exception as err:
379
 
            err_struct = _translate_error(err)
380
 
            return FailedSmartServerResponse(err_struct)
381
 
 
382
 
    def headers_received(self, headers):
383
 
        # Just a no-op at the moment.
384
 
        if 'hpss' in debug.debug_flags:
385
 
            self._trace('headers', repr(headers))
386
 
 
387
 
    def args_received(self, args):
388
 
        cmd = args[0]
389
 
        args = args[1:]
390
 
        try:
391
 
            command = self._commands.get(cmd)
392
 
        except LookupError:
393
 
            if 'hpss' in debug.debug_flags:
394
 
                self._trace('hpss unknown request',
395
 
                            cmd, repr(args)[1:-1])
396
 
            raise errors.UnknownSmartMethod(cmd)
397
 
        if 'hpss' in debug.debug_flags:
398
 
            from . import vfs
399
 
            if issubclass(command, vfs.VfsRequest):
400
 
                action = 'hpss vfs req'
401
 
            else:
402
 
                action = 'hpss request'
403
 
            self._trace(action, '%s %s' % (cmd, repr(args)[1:-1]))
404
 
        self._command = command(
405
 
            self._backing_transport, self._root_client_path, self._jail_root)
406
 
        self._run_handler_code(self._command.execute, args, {})
407
 
 
408
 
    def end_received(self):
409
 
        if self._command is None:
410
 
            # no active command object, so ignore the event.
411
 
            return
412
 
        self._run_handler_code(self._command.do_end, (), {})
413
 
        if 'hpss' in debug.debug_flags:
414
 
            self._trace('end', '', include_time=True)
415
 
 
416
 
    def post_body_error_received(self, error_args):
417
 
        # Just a no-op at the moment.
418
 
        pass
419
 
 
420
 
 
421
 
def _translate_error(err):
422
 
    if isinstance(err, errors.NoSuchFile):
423
 
        return (b'NoSuchFile', err.path.encode('utf-8'))
424
 
    elif isinstance(err, errors.FileExists):
425
 
        return (b'FileExists', err.path.encode('utf-8'))
426
 
    elif isinstance(err, errors.DirectoryNotEmpty):
427
 
        return (b'DirectoryNotEmpty', err.path.encode('utf-8'))
428
 
    elif isinstance(err, errors.IncompatibleRepositories):
429
 
        return (b'IncompatibleRepositories', str(err.source), str(err.target),
430
 
            str(err.details))
431
 
    elif isinstance(err, errors.ShortReadvError):
432
 
        return (b'ShortReadvError', err.path.encode('utf-8'),
433
 
                str(err.offset).encode('ascii'),
434
 
                str(err.length).encode('ascii'),
435
 
                str(err.actual).encode('ascii'))
436
 
    elif isinstance(err, errors.RevisionNotPresent):
437
 
        return (b'RevisionNotPresent', err.revision_id, err.file_id)
438
 
    elif isinstance(err, errors.UnstackableRepositoryFormat):
439
 
        return ((b'UnstackableRepositoryFormat',
440
 
            str(err.format).encode('utf-8'), err.url.encode('utf-8')))
441
 
    elif isinstance(err, _mod_branch.UnstackableBranchFormat):
442
 
        return (b'UnstackableBranchFormat', str(err.format).encode('utf-8'),
443
 
                err.url.encode('utf-8'))
444
 
    elif isinstance(err, errors.NotStacked):
445
 
        return (b'NotStacked',)
446
 
    elif isinstance(err, errors.BzrCheckError):
447
 
        return (b'BzrCheckError', err.msg.encode('utf-8'))
448
 
    elif isinstance(err, UnicodeError):
449
 
        # If it is a DecodeError, than most likely we are starting
450
 
        # with a plain string
451
 
        str_or_unicode = err.object
452
 
        if isinstance(str_or_unicode, text_type):
453
 
            # XXX: UTF-8 might have \x01 (our protocol v1 and v2 seperator
454
 
            # byte) in it, so this encoding could cause broken responses.
455
 
            # Newer clients use protocol v3, so will be fine.
456
 
            val = 'u:' + str_or_unicode.encode('utf-8')
457
 
        else:
458
 
            val = 's:' + str_or_unicode.encode('base64')
459
 
        # This handles UnicodeEncodeError or UnicodeDecodeError
460
 
        return (err.__class__.__name__, err.encoding, val, str(err.start),
461
 
                str(err.end), err.reason)
462
 
    elif isinstance(err, errors.TransportNotPossible):
463
 
        if err.msg == "readonly transport":
464
 
            return (b'ReadOnlyError', )
465
 
    elif isinstance(err, errors.ReadError):
466
 
        # cannot read the file
467
 
        return (b'ReadError', err.path)
468
 
    elif isinstance(err, errors.PermissionDenied):
469
 
        return (b'PermissionDenied', err.path.encode('utf-8'), err.extra.encode('utf-8'))
470
 
    elif isinstance(err, errors.TokenMismatch):
471
 
        return (b'TokenMismatch', err.given_token, err.lock_token)
472
 
    elif isinstance(err, errors.LockContention):
473
 
        return (b'LockContention',)
474
 
    elif isinstance(err, errors.GhostRevisionsHaveNoRevno):
475
 
        return (b'GhostRevisionsHaveNoRevno', err.revision_id, err.ghost_revision_id)
476
 
    elif isinstance(err, MemoryError):
477
 
        # GZ 2011-02-24: Copy breezy.trace -Dmem_dump functionality here?
478
 
        return (b'MemoryError',)
479
 
    # Unserialisable error.  Log it, and return a generic error
480
 
    trace.log_exception_quietly()
481
 
    return (b'error',
482
 
            trace._qualified_exception_name(err.__class__, True).encode('utf-8'),
483
 
            str(err).encode('utf-8'))
 
206
            return callable(*args, **kwargs)
 
207
        except errors.NoSuchFile, e:
 
208
            return FailedSmartServerResponse(('NoSuchFile', e.path))
 
209
        except errors.FileExists, e:
 
210
            return FailedSmartServerResponse(('FileExists', e.path))
 
211
        except errors.DirectoryNotEmpty, e:
 
212
            return FailedSmartServerResponse(('DirectoryNotEmpty', e.path))
 
213
        except errors.ShortReadvError, e:
 
214
            return FailedSmartServerResponse(('ShortReadvError',
 
215
                e.path, str(e.offset), str(e.length), str(e.actual)))
 
216
        except UnicodeError, e:
 
217
            # If it is a DecodeError, than most likely we are starting
 
218
            # with a plain string
 
219
            str_or_unicode = e.object
 
220
            if isinstance(str_or_unicode, unicode):
 
221
                # XXX: UTF-8 might have \x01 (our seperator byte) in it.  We
 
222
                # should escape it somehow.
 
223
                val = 'u:' + str_or_unicode.encode('utf-8')
 
224
            else:
 
225
                val = 's:' + str_or_unicode.encode('base64')
 
226
            # This handles UnicodeEncodeError or UnicodeDecodeError
 
227
            return FailedSmartServerResponse((e.__class__.__name__,
 
228
                    e.encoding, val, str(e.start), str(e.end), e.reason))
 
229
        except errors.TransportNotPossible, e:
 
230
            if e.msg == "readonly transport":
 
231
                return FailedSmartServerResponse(('ReadOnlyError', ))
 
232
            else:
 
233
                raise
484
234
 
485
235
 
486
236
class HelloRequest(SmartServerRequest):
489
239
    """
490
240
 
491
241
    def do(self):
492
 
        return SuccessfulSmartServerResponse((b'ok', b'2'))
 
242
        return SuccessfulSmartServerResponse(('ok', '2'))
493
243
 
494
244
 
495
245
class GetBundleRequest(SmartServerRequest):
497
247
 
498
248
    def do(self, path, revision_id):
499
249
        # open transport relative to our base
500
 
        t = self.transport_from_client_path(path)
 
250
        t = self._backing_transport.clone(path)
501
251
        control, extra_path = bzrdir.BzrDir.open_containing_from_transport(t)
502
252
        repo = control.open_repository()
503
253
        tmpf = tempfile.TemporaryFile()
504
254
        base_revision = revision.NULL_REVISION
505
 
        serializer.write_bundle(repo, revision_id, base_revision, tmpf)
 
255
        write_bundle(repo, revision_id, base_revision, tmpf)
506
256
        tmpf.seek(0)
507
257
        return SuccessfulSmartServerResponse((), tmpf.read())
508
258
 
512
262
 
513
263
    def do(self):
514
264
        if self._backing_transport.is_readonly():
515
 
            answer = b'yes'
 
265
            answer = 'yes'
516
266
        else:
517
 
            answer = b'no'
 
267
            answer = 'no'
518
268
        return SuccessfulSmartServerResponse((answer,))
519
269
 
520
270
 
521
 
# In the 'info' attribute, we store whether this request is 'safe' to retry if
522
 
# we get a disconnect while reading the response. It can have the values:
523
 
#   read    This is purely a read request, so retrying it is perfectly ok.
524
 
#   idem    An idempotent write request. Something like 'put' where if you put
525
 
#           the same bytes twice you end up with the same final bytes.
526
 
#   semi    This is a request that isn't strictly idempotent, but doesn't
527
 
#           result in corruption if it is retried. This is for things like
528
 
#           'lock' and 'unlock'. If you call lock, it updates the disk
529
 
#           structure. If you fail to read the response, you won't be able to
530
 
#           use the lock, because you don't have the lock token. Calling lock
531
 
#           again will fail, because the lock is already taken. However, we
532
 
#           can't tell if the server received our request or not. If it didn't,
533
 
#           then retrying the request is fine, as it will actually do what we
534
 
#           want. If it did, we will interrupt the current operation, but we
535
 
#           are no worse off than interrupting the current operation because of
536
 
#           a ConnectionReset.
537
 
#   semivfs Similar to semi, but specific to a Virtual FileSystem request.
538
 
#   stream  This is a request that takes a stream that cannot be restarted if
539
 
#           consumed. This request is 'safe' in that if we determine the
540
 
#           connection is closed before we consume the stream, we can try
541
 
#           again.
542
 
#   mutate  State is updated in a way that replaying that request results in a
543
 
#           different state. For example 'append' writes more bytes to a given
544
 
#           file. If append succeeds, it moves the file pointer.
545
271
request_handlers = registry.Registry()
546
272
request_handlers.register_lazy(
547
 
    b'append', 'breezy.bzr.smart.vfs', 'AppendRequest', info='mutate')
548
 
request_handlers.register_lazy(
549
 
    b'Branch.break_lock', 'breezy.bzr.smart.branch',
550
 
    'SmartServerBranchBreakLock', info='idem')
551
 
request_handlers.register_lazy(
552
 
    b'Branch.get_config_file', 'breezy.bzr.smart.branch',
553
 
    'SmartServerBranchGetConfigFile', info='read')
554
 
request_handlers.register_lazy(
555
 
    b'Branch.get_parent', 'breezy.bzr.smart.branch', 'SmartServerBranchGetParent',
556
 
    info='read')
557
 
request_handlers.register_lazy(
558
 
    b'Branch.put_config_file', 'breezy.bzr.smart.branch',
559
 
    'SmartServerBranchPutConfigFile', info='idem')
560
 
request_handlers.register_lazy(
561
 
    b'Branch.get_tags_bytes', 'breezy.bzr.smart.branch',
562
 
    'SmartServerBranchGetTagsBytes', info='read')
563
 
request_handlers.register_lazy(
564
 
    b'Branch.set_tags_bytes', 'breezy.bzr.smart.branch',
565
 
    'SmartServerBranchSetTagsBytes', info='idem')
566
 
request_handlers.register_lazy(
567
 
    b'Branch.heads_to_fetch', 'breezy.bzr.smart.branch',
568
 
    'SmartServerBranchHeadsToFetch', info='read')
569
 
request_handlers.register_lazy(
570
 
    b'Branch.get_stacked_on_url', 'breezy.bzr.smart.branch',
571
 
    'SmartServerBranchRequestGetStackedOnURL', info='read')
572
 
request_handlers.register_lazy(
573
 
    b'Branch.get_physical_lock_status', 'breezy.bzr.smart.branch',
574
 
    'SmartServerBranchRequestGetPhysicalLockStatus', info='read')
575
 
request_handlers.register_lazy(
576
 
    b'Branch.last_revision_info', 'breezy.bzr.smart.branch',
577
 
    'SmartServerBranchRequestLastRevisionInfo', info='read')
578
 
request_handlers.register_lazy(
579
 
    b'Branch.lock_write', 'breezy.bzr.smart.branch',
580
 
    'SmartServerBranchRequestLockWrite', info='semi')
581
 
request_handlers.register_lazy(
582
 
    b'Branch.revision_history', 'breezy.bzr.smart.branch',
583
 
    'SmartServerRequestRevisionHistory', info='read')
584
 
request_handlers.register_lazy(
585
 
    b'Branch.set_config_option', 'breezy.bzr.smart.branch',
586
 
    'SmartServerBranchRequestSetConfigOption', info='idem')
587
 
request_handlers.register_lazy(
588
 
    b'Branch.set_config_option_dict', 'breezy.bzr.smart.branch',
589
 
    'SmartServerBranchRequestSetConfigOptionDict', info='idem')
590
 
request_handlers.register_lazy(
591
 
    b'Branch.set_last_revision', 'breezy.bzr.smart.branch',
592
 
    'SmartServerBranchRequestSetLastRevision', info='idem')
593
 
request_handlers.register_lazy(
594
 
    b'Branch.set_last_revision_info', 'breezy.bzr.smart.branch',
595
 
    'SmartServerBranchRequestSetLastRevisionInfo', info='idem')
596
 
request_handlers.register_lazy(
597
 
    b'Branch.set_last_revision_ex', 'breezy.bzr.smart.branch',
598
 
    'SmartServerBranchRequestSetLastRevisionEx', info='idem')
599
 
request_handlers.register_lazy(
600
 
    b'Branch.set_parent_location', 'breezy.bzr.smart.branch',
601
 
    'SmartServerBranchRequestSetParentLocation', info='idem')
602
 
request_handlers.register_lazy(
603
 
    b'Branch.unlock', 'breezy.bzr.smart.branch',
604
 
    'SmartServerBranchRequestUnlock', info='semi')
605
 
request_handlers.register_lazy(
606
 
    b'Branch.revision_id_to_revno', 'breezy.bzr.smart.branch',
607
 
    'SmartServerBranchRequestRevisionIdToRevno', info='read')
608
 
request_handlers.register_lazy(
609
 
    b'BzrDir.checkout_metadir', 'breezy.bzr.smart.bzrdir',
610
 
    'SmartServerBzrDirRequestCheckoutMetaDir', info='read')
611
 
request_handlers.register_lazy(
612
 
    b'BzrDir.cloning_metadir', 'breezy.bzr.smart.bzrdir',
613
 
    'SmartServerBzrDirRequestCloningMetaDir', info='read')
614
 
request_handlers.register_lazy(
615
 
    b'BzrDir.create_branch', 'breezy.bzr.smart.bzrdir',
616
 
    'SmartServerRequestCreateBranch', info='semi')
617
 
request_handlers.register_lazy(
618
 
    b'BzrDir.create_repository', 'breezy.bzr.smart.bzrdir',
619
 
    'SmartServerRequestCreateRepository', info='semi')
620
 
request_handlers.register_lazy(
621
 
    b'BzrDir.find_repository', 'breezy.bzr.smart.bzrdir',
622
 
    'SmartServerRequestFindRepositoryV1', info='read')
623
 
request_handlers.register_lazy(
624
 
    b'BzrDir.find_repositoryV2', 'breezy.bzr.smart.bzrdir',
625
 
    'SmartServerRequestFindRepositoryV2', info='read')
626
 
request_handlers.register_lazy(
627
 
    b'BzrDir.find_repositoryV3', 'breezy.bzr.smart.bzrdir',
628
 
    'SmartServerRequestFindRepositoryV3', info='read')
629
 
request_handlers.register_lazy(
630
 
    b'BzrDir.get_branches', 'breezy.bzr.smart.bzrdir',
631
 
    'SmartServerBzrDirRequestGetBranches', info='read')
632
 
request_handlers.register_lazy(
633
 
    b'BzrDir.get_config_file', 'breezy.bzr.smart.bzrdir',
634
 
    'SmartServerBzrDirRequestConfigFile', info='read')
635
 
request_handlers.register_lazy(
636
 
    b'BzrDir.destroy_branch', 'breezy.bzr.smart.bzrdir',
637
 
    'SmartServerBzrDirRequestDestroyBranch', info='semi')
638
 
request_handlers.register_lazy(
639
 
    b'BzrDir.destroy_repository', 'breezy.bzr.smart.bzrdir',
640
 
    'SmartServerBzrDirRequestDestroyRepository', info='semi')
641
 
request_handlers.register_lazy(
642
 
    b'BzrDir.has_workingtree', 'breezy.bzr.smart.bzrdir',
643
 
    'SmartServerBzrDirRequestHasWorkingTree', info='read')
644
 
request_handlers.register_lazy(
645
 
    b'BzrDirFormat.initialize', 'breezy.bzr.smart.bzrdir',
646
 
    'SmartServerRequestInitializeBzrDir', info='semi')
647
 
request_handlers.register_lazy(
648
 
    b'BzrDirFormat.initialize_ex_1.16', 'breezy.bzr.smart.bzrdir',
649
 
    'SmartServerRequestBzrDirInitializeEx', info='semi')
650
 
request_handlers.register_lazy(
651
 
    b'BzrDir.open', 'breezy.bzr.smart.bzrdir', 'SmartServerRequestOpenBzrDir',
652
 
    info='read')
653
 
request_handlers.register_lazy(
654
 
    b'BzrDir.open_2.1', 'breezy.bzr.smart.bzrdir',
655
 
    'SmartServerRequestOpenBzrDir_2_1', info='read')
656
 
request_handlers.register_lazy(
657
 
    b'BzrDir.open_branch', 'breezy.bzr.smart.bzrdir',
658
 
    'SmartServerRequestOpenBranch', info='read')
659
 
request_handlers.register_lazy(
660
 
    b'BzrDir.open_branchV2', 'breezy.bzr.smart.bzrdir',
661
 
    'SmartServerRequestOpenBranchV2', info='read')
662
 
request_handlers.register_lazy(
663
 
    b'BzrDir.open_branchV3', 'breezy.bzr.smart.bzrdir',
664
 
    'SmartServerRequestOpenBranchV3', info='read')
665
 
request_handlers.register_lazy(
666
 
    b'delete', 'breezy.bzr.smart.vfs', 'DeleteRequest', info='semivfs')
667
 
request_handlers.register_lazy(
668
 
    b'get', 'breezy.bzr.smart.vfs', 'GetRequest', info='read')
669
 
request_handlers.register_lazy(
670
 
    b'get_bundle', 'breezy.bzr.smart.request', 'GetBundleRequest', info='read')
671
 
request_handlers.register_lazy(
672
 
    b'has', 'breezy.bzr.smart.vfs', 'HasRequest', info='read')
673
 
request_handlers.register_lazy(
674
 
    b'hello', 'breezy.bzr.smart.request', 'HelloRequest', info='read')
675
 
request_handlers.register_lazy(
676
 
    b'iter_files_recursive', 'breezy.bzr.smart.vfs', 'IterFilesRecursiveRequest',
677
 
    info='read')
678
 
request_handlers.register_lazy(
679
 
    b'list_dir', 'breezy.bzr.smart.vfs', 'ListDirRequest', info='read')
680
 
request_handlers.register_lazy(
681
 
    b'mkdir', 'breezy.bzr.smart.vfs', 'MkdirRequest', info='semivfs')
682
 
request_handlers.register_lazy(
683
 
    b'move', 'breezy.bzr.smart.vfs', 'MoveRequest', info='semivfs')
684
 
request_handlers.register_lazy(
685
 
    b'put', 'breezy.bzr.smart.vfs', 'PutRequest', info='idem')
686
 
request_handlers.register_lazy(
687
 
    b'put_non_atomic', 'breezy.bzr.smart.vfs', 'PutNonAtomicRequest', info='idem')
688
 
request_handlers.register_lazy(
689
 
    b'readv', 'breezy.bzr.smart.vfs', 'ReadvRequest', info='read')
690
 
request_handlers.register_lazy(
691
 
    b'rename', 'breezy.bzr.smart.vfs', 'RenameRequest', info='semivfs')
692
 
request_handlers.register_lazy(
693
 
    b'Repository.add_signature_text', 'breezy.bzr.smart.repository',
694
 
    'SmartServerRepositoryAddSignatureText', info='idem')
695
 
request_handlers.register_lazy(
696
 
    b'Repository.annotate_file_revision', 'breezy.bzr.smart.repository',
697
 
    'SmartServerRepositoryAnnotateFileRevision', info='read')
698
 
request_handlers.register_lazy(
699
 
    b'Repository.all_revision_ids', 'breezy.bzr.smart.repository',
700
 
    'SmartServerRepositoryAllRevisionIds', info='read')
701
 
request_handlers.register_lazy(
702
 
    b'PackRepository.autopack', 'breezy.bzr.smart.packrepository',
703
 
    'SmartServerPackRepositoryAutopack', info='idem')
704
 
request_handlers.register_lazy(
705
 
    b'Repository.break_lock', 'breezy.bzr.smart.repository',
706
 
    'SmartServerRepositoryBreakLock', info='idem')
707
 
request_handlers.register_lazy(
708
 
    b'Repository.gather_stats', 'breezy.bzr.smart.repository',
709
 
    'SmartServerRepositoryGatherStats', info='read')
710
 
request_handlers.register_lazy(
711
 
    b'Repository.get_parent_map', 'breezy.bzr.smart.repository',
712
 
    'SmartServerRepositoryGetParentMap', info='read')
713
 
request_handlers.register_lazy(
714
 
    b'Repository.get_revision_graph', 'breezy.bzr.smart.repository',
715
 
    'SmartServerRepositoryGetRevisionGraph', info='read')
716
 
request_handlers.register_lazy(
717
 
    b'Repository.get_revision_signature_text', 'breezy.bzr.smart.repository',
718
 
    'SmartServerRepositoryGetRevisionSignatureText', info='read')
719
 
request_handlers.register_lazy(
720
 
    b'Repository.has_revision', 'breezy.bzr.smart.repository',
721
 
    'SmartServerRequestHasRevision', info='read')
722
 
request_handlers.register_lazy(
723
 
    b'Repository.has_signature_for_revision_id', 'breezy.bzr.smart.repository',
724
 
    'SmartServerRequestHasSignatureForRevisionId', info='read')
725
 
request_handlers.register_lazy(
726
 
    b'Repository.insert_stream', 'breezy.bzr.smart.repository',
727
 
    'SmartServerRepositoryInsertStream', info='stream')
728
 
request_handlers.register_lazy(
729
 
    b'Repository.insert_stream_1.19', 'breezy.bzr.smart.repository',
730
 
    'SmartServerRepositoryInsertStream_1_19', info='stream')
731
 
request_handlers.register_lazy(
732
 
    b'Repository.insert_stream_locked', 'breezy.bzr.smart.repository',
733
 
    'SmartServerRepositoryInsertStreamLocked', info='stream')
734
 
request_handlers.register_lazy(
735
 
    b'Repository.is_shared', 'breezy.bzr.smart.repository',
736
 
    'SmartServerRepositoryIsShared', info='read')
737
 
request_handlers.register_lazy(
738
 
    b'Repository.iter_files_bytes', 'breezy.bzr.smart.repository',
739
 
    'SmartServerRepositoryIterFilesBytes', info='read')
740
 
request_handlers.register_lazy(
741
 
    b'Repository.lock_write', 'breezy.bzr.smart.repository',
742
 
    'SmartServerRepositoryLockWrite', info='semi')
743
 
request_handlers.register_lazy(
744
 
    b'Repository.make_working_trees', 'breezy.bzr.smart.repository',
745
 
    'SmartServerRepositoryMakeWorkingTrees', info='read')
746
 
request_handlers.register_lazy(
747
 
    b'Repository.set_make_working_trees', 'breezy.bzr.smart.repository',
748
 
    'SmartServerRepositorySetMakeWorkingTrees', info='idem')
749
 
request_handlers.register_lazy(
750
 
    b'Repository.unlock', 'breezy.bzr.smart.repository',
751
 
    'SmartServerRepositoryUnlock', info='semi')
752
 
request_handlers.register_lazy(
753
 
    b'Repository.get_physical_lock_status', 'breezy.bzr.smart.repository',
754
 
    'SmartServerRepositoryGetPhysicalLockStatus', info='read')
755
 
request_handlers.register_lazy(
756
 
    b'Repository.get_rev_id_for_revno', 'breezy.bzr.smart.repository',
757
 
    'SmartServerRepositoryGetRevIdForRevno', info='read')
758
 
request_handlers.register_lazy(
759
 
    b'Repository.get_stream', 'breezy.bzr.smart.repository',
760
 
    'SmartServerRepositoryGetStream', info='read')
761
 
request_handlers.register_lazy(
762
 
    b'Repository.get_stream_1.19', 'breezy.bzr.smart.repository',
763
 
    'SmartServerRepositoryGetStream_1_19', info='read')
764
 
request_handlers.register_lazy(
765
 
    b'Repository.get_stream_for_missing_keys', 'breezy.bzr.smart.repository',
766
 
    'SmartServerRepositoryGetStreamForMissingKeys', info='read')
767
 
request_handlers.register_lazy(
768
 
    b'Repository.iter_revisions', 'breezy.bzr.smart.repository',
769
 
    'SmartServerRepositoryIterRevisions', info='read')
770
 
request_handlers.register_lazy(
771
 
    b'Repository.pack', 'breezy.bzr.smart.repository',
772
 
    'SmartServerRepositoryPack', info='idem')
773
 
request_handlers.register_lazy(
774
 
    b'Repository.start_write_group', 'breezy.bzr.smart.repository',
775
 
    'SmartServerRepositoryStartWriteGroup', info='semi')
776
 
request_handlers.register_lazy(
777
 
    b'Repository.commit_write_group', 'breezy.bzr.smart.repository',
778
 
    'SmartServerRepositoryCommitWriteGroup', info='semi')
779
 
request_handlers.register_lazy(
780
 
    b'Repository.abort_write_group', 'breezy.bzr.smart.repository',
781
 
    'SmartServerRepositoryAbortWriteGroup', info='semi')
782
 
request_handlers.register_lazy(
783
 
    b'Repository.check_write_group', 'breezy.bzr.smart.repository',
784
 
    'SmartServerRepositoryCheckWriteGroup', info='read')
785
 
request_handlers.register_lazy(
786
 
    b'Repository.reconcile', 'breezy.bzr.smart.repository',
787
 
    'SmartServerRepositoryReconcile', info='idem')
788
 
request_handlers.register_lazy(
789
 
    b'Repository.revision_archive', 'breezy.bzr.smart.repository',
790
 
    'SmartServerRepositoryRevisionArchive', info='read')
791
 
request_handlers.register_lazy(
792
 
    b'Repository.tarball', 'breezy.bzr.smart.repository',
793
 
    'SmartServerRepositoryTarball', info='read')
794
 
request_handlers.register_lazy(
795
 
    b'VersionedFileRepository.get_serializer_format', 'breezy.bzr.smart.repository',
796
 
    'SmartServerRepositoryGetSerializerFormat', info='read')
797
 
request_handlers.register_lazy(
798
 
    b'VersionedFileRepository.get_inventories', 'breezy.bzr.smart.repository',
799
 
    'SmartServerRepositoryGetInventories', info='read')
800
 
request_handlers.register_lazy(
801
 
    b'rmdir', 'breezy.bzr.smart.vfs', 'RmdirRequest', info='semivfs')
802
 
request_handlers.register_lazy(
803
 
    b'stat', 'breezy.bzr.smart.vfs', 'StatRequest', info='read')
804
 
request_handlers.register_lazy(
805
 
    b'Transport.is_readonly', 'breezy.bzr.smart.request',
806
 
    'SmartServerIsReadonly', info='read')
 
273
    'append', 'bzrlib.smart.vfs', 'AppendRequest')
 
274
request_handlers.register_lazy(
 
275
    'Branch.get_config_file', 'bzrlib.smart.branch', 'SmartServerBranchGetConfigFile')
 
276
request_handlers.register_lazy(
 
277
    'Branch.last_revision_info', 'bzrlib.smart.branch', 'SmartServerBranchRequestLastRevisionInfo')
 
278
request_handlers.register_lazy(
 
279
    'Branch.lock_write', 'bzrlib.smart.branch', 'SmartServerBranchRequestLockWrite')
 
280
request_handlers.register_lazy(
 
281
    'Branch.revision_history', 'bzrlib.smart.branch', 'SmartServerRequestRevisionHistory')
 
282
request_handlers.register_lazy(
 
283
    'Branch.set_last_revision', 'bzrlib.smart.branch', 'SmartServerBranchRequestSetLastRevision')
 
284
request_handlers.register_lazy(
 
285
    'Branch.unlock', 'bzrlib.smart.branch', 'SmartServerBranchRequestUnlock')
 
286
request_handlers.register_lazy(
 
287
    'BzrDir.find_repository', 'bzrlib.smart.bzrdir', 'SmartServerRequestFindRepository')
 
288
request_handlers.register_lazy(
 
289
    'BzrDirFormat.initialize', 'bzrlib.smart.bzrdir', 'SmartServerRequestInitializeBzrDir')
 
290
request_handlers.register_lazy(
 
291
    'BzrDir.open_branch', 'bzrlib.smart.bzrdir', 'SmartServerRequestOpenBranch')
 
292
request_handlers.register_lazy(
 
293
    'delete', 'bzrlib.smart.vfs', 'DeleteRequest')
 
294
request_handlers.register_lazy(
 
295
    'get', 'bzrlib.smart.vfs', 'GetRequest')
 
296
request_handlers.register_lazy(
 
297
    'get_bundle', 'bzrlib.smart.request', 'GetBundleRequest')
 
298
request_handlers.register_lazy(
 
299
    'has', 'bzrlib.smart.vfs', 'HasRequest')
 
300
request_handlers.register_lazy(
 
301
    'hello', 'bzrlib.smart.request', 'HelloRequest')
 
302
request_handlers.register_lazy(
 
303
    'iter_files_recursive', 'bzrlib.smart.vfs', 'IterFilesRecursiveRequest')
 
304
request_handlers.register_lazy(
 
305
    'list_dir', 'bzrlib.smart.vfs', 'ListDirRequest')
 
306
request_handlers.register_lazy(
 
307
    'mkdir', 'bzrlib.smart.vfs', 'MkdirRequest')
 
308
request_handlers.register_lazy(
 
309
    'move', 'bzrlib.smart.vfs', 'MoveRequest')
 
310
request_handlers.register_lazy(
 
311
    'put', 'bzrlib.smart.vfs', 'PutRequest')
 
312
request_handlers.register_lazy(
 
313
    'put_non_atomic', 'bzrlib.smart.vfs', 'PutNonAtomicRequest')
 
314
request_handlers.register_lazy(
 
315
    'readv', 'bzrlib.smart.vfs', 'ReadvRequest')
 
316
request_handlers.register_lazy(
 
317
    'rename', 'bzrlib.smart.vfs', 'RenameRequest')
 
318
request_handlers.register_lazy('Repository.gather_stats',
 
319
                               'bzrlib.smart.repository',
 
320
                               'SmartServerRepositoryGatherStats')
 
321
request_handlers.register_lazy(
 
322
    'Repository.stream_knit_data_for_revisions', 'bzrlib.smart.repository',
 
323
    'SmartServerRepositoryStreamKnitDataForRevisions')
 
324
request_handlers.register_lazy(
 
325
    'Repository.get_revision_graph', 'bzrlib.smart.repository', 'SmartServerRepositoryGetRevisionGraph')
 
326
request_handlers.register_lazy(
 
327
    'Repository.has_revision', 'bzrlib.smart.repository', 'SmartServerRequestHasRevision')
 
328
request_handlers.register_lazy(
 
329
    'Repository.is_shared', 'bzrlib.smart.repository', 'SmartServerRepositoryIsShared')
 
330
request_handlers.register_lazy(
 
331
    'Repository.lock_write', 'bzrlib.smart.repository', 'SmartServerRepositoryLockWrite')
 
332
request_handlers.register_lazy(
 
333
    'Repository.unlock', 'bzrlib.smart.repository', 'SmartServerRepositoryUnlock')
 
334
request_handlers.register_lazy(
 
335
    'Repository.tarball', 'bzrlib.smart.repository',
 
336
    'SmartServerRepositoryTarball')
 
337
request_handlers.register_lazy(
 
338
    'rmdir', 'bzrlib.smart.vfs', 'RmdirRequest')
 
339
request_handlers.register_lazy(
 
340
    'stat', 'bzrlib.smart.vfs', 'StatRequest')
 
341
request_handlers.register_lazy(
 
342
    'Transport.is_readonly', 'bzrlib.smart.request', 'SmartServerIsReadonly')
 
343
request_handlers.register_lazy(
 
344
    'BzrDir.open', 'bzrlib.smart.bzrdir', 'SmartServerRequestOpenBzrDir')