/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

Merge from bzr.dev, resolving conflicts.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006-2011 Canonical Ltd
 
1
# Copyright (C) 2006 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,
45
 
    errors,
46
 
    osutils,
47
 
    registry,
48
 
    revision,
49
 
    trace,
50
 
    urlutils,
51
 
    )
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
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
16
 
 
17
"""Basic server-side logic for dealing with requests."""
 
18
 
57
19
 
58
20
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()
 
21
 
 
22
from bzrlib import bzrdir, errors, registry, revision
 
23
from bzrlib.bundle.serializer import write_bundle
96
24
 
97
25
 
98
26
class SmartServerRequest(object):
99
27
    """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
28
    """
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):
110
 
        """Constructor.
111
 
 
112
 
        :param backing_transport: the base transport to be used when performing
113
 
            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
 
        """
 
29
 
 
30
    def __init__(self, backing_transport):
122
31
        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
32
 
134
33
    def _check_enabled(self):
135
34
        """Raises DisabledMethod if this method is disabled."""
137
36
 
138
37
    def do(self, *args):
139
38
        """Mandatory extension point for SmartServerRequest subclasses.
140
 
 
 
39
        
141
40
        Subclasses must implement this.
142
 
 
 
41
        
143
42
        This should return a SmartServerResponse if this command expects to
144
43
        receive no body.
145
44
        """
151
50
        It will return a SmartServerResponse if the command does not expect a
152
51
        body.
153
52
 
154
 
        :param args: the arguments of the request.
 
53
        :param *args: the arguments of the request.
155
54
        """
156
55
        self._check_enabled()
157
56
        return self.do(*args)
158
57
 
159
58
    def do_body(self, body_bytes):
160
59
        """Called if the client sends a body with the request.
161
 
 
162
 
        The do() method is still called, and must have returned None.
163
 
 
 
60
        
164
61
        Must return a SmartServerResponse.
165
62
        """
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)
 
63
        # TODO: if a client erroneously sends a request that shouldn't have a
 
64
        # body, what to do?  Probably SmartServerRequestHandler should catch
 
65
        # this NotImplementedError and translate it into a 'bad request' error
 
66
        # to send to the client.
 
67
        raise NotImplementedError(self.do_body)
225
68
 
226
69
 
227
70
class SmartServerResponse(object):
228
 
    """A response to a client request.
229
 
 
230
 
    This base class should not be used. Instead use
231
 
    SuccessfulSmartServerResponse and FailedSmartServerResponse as appropriate.
232
 
    """
233
 
 
234
 
    def __init__(self, args, body=None, body_stream=None):
235
 
        """Constructor.
236
 
 
237
 
        :param args: tuple of response arguments.
238
 
        :param body: string of a response body.
239
 
        :param body_stream: iterable of bytestrings to be streamed to the
240
 
            client.
241
 
        """
 
71
    """Response generated by SmartServerRequestHandler."""
 
72
 
 
73
    def __init__(self, args, body=None):
242
74
        self.args = args
243
 
        if body is not None and body_stream is not None:
244
 
            raise errors.BzrError(
245
 
                "'body' and 'body_stream' are mutually exclusive.")
246
75
        self.body = body
247
 
        self.body_stream = body_stream
248
76
 
249
77
    def __eq__(self, other):
250
78
        if other is None:
251
79
            return False
252
 
        return (other.args == self.args and
253
 
                other.body == self.body and
254
 
                other.body_stream is self.body_stream)
 
80
        return other.args == self.args and other.body == self.body
255
81
 
256
82
    def __repr__(self):
257
 
        return "<%s args=%r body=%r>" % (self.__class__.__name__,
258
 
            self.args, self.body)
259
 
 
260
 
 
261
 
class FailedSmartServerResponse(SmartServerResponse):
262
 
    """A SmartServerResponse for a request which failed."""
263
 
 
264
 
    def is_successful(self):
265
 
        """FailedSmartServerResponse are not successful."""
266
 
        return False
267
 
 
268
 
 
269
 
class SuccessfulSmartServerResponse(SmartServerResponse):
270
 
    """A SmartServerResponse for a successfully completed request."""
271
 
 
272
 
    def is_successful(self):
273
 
        """SuccessfulSmartServerResponse are successful."""
274
 
        return True
 
83
        return "<SmartServerResponse args=%r body=%r>" % (self.args, self.body)
275
84
 
276
85
 
277
86
class SmartServerRequestHandler(object):
278
87
    """Protocol logic for smart server.
279
 
 
 
88
    
280
89
    This doesn't handle serialization at all, it just processes requests and
281
90
    creates responses.
282
91
    """
291
100
    # TODO: Better way of representing the body for commands that take it,
292
101
    # and allow it to be streamed into the server.
293
102
 
294
 
    def __init__(self, backing_transport, commands, root_client_path,
295
 
        jail_root=None):
 
103
    def __init__(self, backing_transport, commands):
296
104
        """Constructor.
297
105
 
298
106
        :param backing_transport: a Transport to handle requests for.
299
107
        :param commands: a registry mapping command names to SmartServerRequest
300
 
            subclasses. e.g. breezy.transport.smart.vfs.vfs_commands.
 
108
            subclasses. e.g. bzrlib.transport.smart.vfs.vfs_commands.
301
109
        """
302
110
        self._backing_transport = backing_transport
303
 
        self._root_client_path = root_client_path
304
111
        self._commands = commands
305
 
        if jail_root is None:
306
 
            jail_root = backing_transport
307
 
        self._jail_root = jail_root
 
112
        self._body_bytes = ''
308
113
        self.response = None
309
114
        self.finished_reading = False
310
115
        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
116
 
333
117
    def accept_body(self, bytes):
334
118
        """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
 
 
 
119
 
 
120
        # TODO: This should be overriden for each command that desired body data
 
121
        # to handle the right format of that data, i.e. plain bytes, a bundle,
 
122
        # etc.  The deserialisation into that format should be done in the
 
123
        # Protocol object.
 
124
 
 
125
        # default fallback is to accumulate bytes.
 
126
        self._body_bytes += bytes
 
127
        
343
128
    def end_of_body(self):
344
129
        """No more body data will be received."""
345
 
        self._run_handler_code(self._command.do_end, (), {})
 
130
        self._run_handler_code(self._command.do_body, (self._body_bytes,), {})
346
131
        # cannot read after this.
347
132
        self.finished_reading = True
348
 
        if 'hpss' in debug.debug_flags:
349
 
            self._trace('end of body', '', include_time=True)
 
133
 
 
134
    def dispatch_command(self, cmd, args):
 
135
        """Deprecated compatibility method.""" # XXX XXX
 
136
        try:
 
137
            command = self._commands.get(cmd)
 
138
        except LookupError:
 
139
            raise errors.SmartProtocolError("bad request %r" % (cmd,))
 
140
        self._command = command(self._backing_transport)
 
141
        self._run_handler_code(self._command.execute, args, {})
350
142
 
351
143
    def _run_handler_code(self, callable, args, kwargs):
352
144
        """Run some handler specific code 'callable'.
368
160
        # XXX: most of this error conversion is VFS-related, and thus ought to
369
161
        # be in SmartServerVFSRequestHandler somewhere.
370
162
        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'))
 
163
            return callable(*args, **kwargs)
 
164
        except errors.NoSuchFile, e:
 
165
            return SmartServerResponse(('NoSuchFile', e.path))
 
166
        except errors.FileExists, e:
 
167
            return SmartServerResponse(('FileExists', e.path))
 
168
        except errors.DirectoryNotEmpty, e:
 
169
            return SmartServerResponse(('DirectoryNotEmpty', e.path))
 
170
        except errors.ShortReadvError, e:
 
171
            return SmartServerResponse(('ShortReadvError',
 
172
                e.path, str(e.offset), str(e.length), str(e.actual)))
 
173
        except UnicodeError, e:
 
174
            # If it is a DecodeError, than most likely we are starting
 
175
            # with a plain string
 
176
            str_or_unicode = e.object
 
177
            if isinstance(str_or_unicode, unicode):
 
178
                # XXX: UTF-8 might have \x01 (our seperator byte) in it.  We
 
179
                # should escape it somehow.
 
180
                val = 'u:' + str_or_unicode.encode('utf-8')
 
181
            else:
 
182
                val = 's:' + str_or_unicode.encode('base64')
 
183
            # This handles UnicodeEncodeError or UnicodeDecodeError
 
184
            return SmartServerResponse((e.__class__.__name__,
 
185
                    e.encoding, val, str(e.start), str(e.end), e.reason))
 
186
        except errors.TransportNotPossible, e:
 
187
            if e.msg == "readonly transport":
 
188
                return SmartServerResponse(('ReadOnlyError', ))
 
189
            else:
 
190
                raise
484
191
 
485
192
 
486
193
class HelloRequest(SmartServerRequest):
487
 
    """Answer a version request with the highest protocol version this server
488
 
    supports.
489
 
    """
 
194
    """Answer a version request with my version."""
490
195
 
491
196
    def do(self):
492
 
        return SuccessfulSmartServerResponse((b'ok', b'2'))
 
197
        return SmartServerResponse(('ok', '1'))
493
198
 
494
199
 
495
200
class GetBundleRequest(SmartServerRequest):
496
 
    """Get a bundle of from the null revision to the specified revision."""
497
201
 
498
202
    def do(self, path, revision_id):
499
203
        # open transport relative to our base
500
 
        t = self.transport_from_client_path(path)
 
204
        t = self._backing_transport.clone(path)
501
205
        control, extra_path = bzrdir.BzrDir.open_containing_from_transport(t)
502
206
        repo = control.open_repository()
503
207
        tmpf = tempfile.TemporaryFile()
504
208
        base_revision = revision.NULL_REVISION
505
 
        serializer.write_bundle(repo, revision_id, base_revision, tmpf)
 
209
        write_bundle(repo, revision_id, base_revision, tmpf)
506
210
        tmpf.seek(0)
507
 
        return SuccessfulSmartServerResponse((), tmpf.read())
 
211
        return SmartServerResponse((), tmpf.read())
 
212
 
 
213
 
 
214
# This exists solely to help RemoteObjectHacking.  It should be removed
 
215
# eventually.  It should not be considered part of the real smart server
 
216
# protocol!
 
217
class ProbeDontUseRequest(SmartServerRequest):
 
218
 
 
219
    def do(self, path):
 
220
        from bzrlib.bzrdir import BzrDirFormat
 
221
        t = self._backing_transport.clone(path)
 
222
        default_format = BzrDirFormat.get_default_format()
 
223
        real_bzrdir = default_format.open(t, _found=True)
 
224
        try:
 
225
            real_bzrdir._format.probe_transport(t)
 
226
        except (errors.NotBranchError, errors.UnknownFormatError):
 
227
            answer = 'no'
 
228
        else:
 
229
            answer = 'yes'
 
230
        return SmartServerResponse((answer,))
508
231
 
509
232
 
510
233
class SmartServerIsReadonly(SmartServerRequest):
512
235
 
513
236
    def do(self):
514
237
        if self._backing_transport.is_readonly():
515
 
            answer = b'yes'
 
238
            answer = 'yes'
516
239
        else:
517
 
            answer = b'no'
518
 
        return SuccessfulSmartServerResponse((answer,))
519
 
 
520
 
 
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.
 
240
            answer = 'no'
 
241
        return SmartServerResponse((answer,))
 
242
 
 
243
 
545
244
request_handlers = registry.Registry()
546
245
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')
 
246
    'append', 'bzrlib.smart.vfs', 'AppendRequest')
 
247
request_handlers.register_lazy(
 
248
    'Branch.get_config_file', 'bzrlib.smart.branch', 'SmartServerBranchGetConfigFile')
 
249
request_handlers.register_lazy(
 
250
    'Branch.last_revision_info', 'bzrlib.smart.branch', 'SmartServerBranchRequestLastRevisionInfo')
 
251
request_handlers.register_lazy(
 
252
    'Branch.lock_write', 'bzrlib.smart.branch', 'SmartServerBranchRequestLockWrite')
 
253
request_handlers.register_lazy(
 
254
    'Branch.revision_history', 'bzrlib.smart.branch', 'SmartServerRequestRevisionHistory')
 
255
request_handlers.register_lazy(
 
256
    'Branch.set_last_revision', 'bzrlib.smart.branch', 'SmartServerBranchRequestSetLastRevision')
 
257
request_handlers.register_lazy(
 
258
    'Branch.unlock', 'bzrlib.smart.branch', 'SmartServerBranchRequestUnlock')
 
259
request_handlers.register_lazy(
 
260
    'BzrDir.find_repository', 'bzrlib.smart.bzrdir', 'SmartServerRequestFindRepository')
 
261
request_handlers.register_lazy(
 
262
    'BzrDirFormat.initialize', 'bzrlib.smart.bzrdir', 'SmartServerRequestInitializeBzrDir')
 
263
request_handlers.register_lazy(
 
264
    'BzrDir.open_branch', 'bzrlib.smart.bzrdir', 'SmartServerRequestOpenBranch')
 
265
request_handlers.register_lazy(
 
266
    'delete', 'bzrlib.smart.vfs', 'DeleteRequest')
 
267
request_handlers.register_lazy(
 
268
    'get', 'bzrlib.smart.vfs', 'GetRequest')
 
269
request_handlers.register_lazy(
 
270
    'get_bundle', 'bzrlib.smart.request', 'GetBundleRequest')
 
271
request_handlers.register_lazy(
 
272
    'has', 'bzrlib.smart.vfs', 'HasRequest')
 
273
request_handlers.register_lazy(
 
274
    'hello', 'bzrlib.smart.request', 'HelloRequest')
 
275
request_handlers.register_lazy(
 
276
    'iter_files_recursive', 'bzrlib.smart.vfs', 'IterFilesRecursiveRequest')
 
277
request_handlers.register_lazy(
 
278
    'list_dir', 'bzrlib.smart.vfs', 'ListDirRequest')
 
279
request_handlers.register_lazy(
 
280
    'mkdir', 'bzrlib.smart.vfs', 'MkdirRequest')
 
281
request_handlers.register_lazy(
 
282
    'move', 'bzrlib.smart.vfs', 'MoveRequest')
 
283
request_handlers.register_lazy(
 
284
    'put', 'bzrlib.smart.vfs', 'PutRequest')
 
285
request_handlers.register_lazy(
 
286
    'put_non_atomic', 'bzrlib.smart.vfs', 'PutNonAtomicRequest')
 
287
request_handlers.register_lazy(
 
288
    'readv', 'bzrlib.smart.vfs', 'ReadvRequest')
 
289
request_handlers.register_lazy(
 
290
    'rename', 'bzrlib.smart.vfs', 'RenameRequest')
 
291
request_handlers.register_lazy('Repository.gather_stats',
 
292
                               'bzrlib.smart.repository',
 
293
                               'SmartServerRepositoryGatherStats')
 
294
request_handlers.register_lazy(
 
295
    'Repository.get_revision_graph', 'bzrlib.smart.repository', 'SmartServerRepositoryGetRevisionGraph')
 
296
request_handlers.register_lazy(
 
297
    'Repository.has_revision', 'bzrlib.smart.repository', 'SmartServerRequestHasRevision')
 
298
request_handlers.register_lazy(
 
299
    'Repository.is_shared', 'bzrlib.smart.repository', 'SmartServerRepositoryIsShared')
 
300
request_handlers.register_lazy(
 
301
    'Repository.lock_write', 'bzrlib.smart.repository', 'SmartServerRepositoryLockWrite')
 
302
request_handlers.register_lazy(
 
303
    'Repository.unlock', 'bzrlib.smart.repository', 'SmartServerRepositoryUnlock')
 
304
request_handlers.register_lazy(
 
305
    'rmdir', 'bzrlib.smart.vfs', 'RmdirRequest')
 
306
request_handlers.register_lazy(
 
307
    'stat', 'bzrlib.smart.vfs', 'StatRequest')
 
308
request_handlers.register_lazy(
 
309
    'Transport.is_readonly', 'bzrlib.smart.request', 'SmartServerIsReadonly')
 
310
request_handlers.register_lazy(
 
311
    'probe_dont_use', 'bzrlib.smart.request', 'ProbeDontUseRequest')