/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: Robert Collins
  • Date: 2007-04-19 02:27:44 UTC
  • mto: This revision was merged to the branch mainline in revision 2426.
  • Revision ID: robertc@robertcollins.net-20070419022744-pfdqz42kp1wizh43
``make docs`` now creates a man page at ``man1/bzr.1`` fixing bug 107388.
(Robert Collins)

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