/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
2018.5.4 by Andrew Bennetts
Split smart server VFS logic out into a new file, and start using the command pattern in the SmartServerRequestHandler.
1
# Copyright (C) 2006 Canonical Ltd
2
#
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
#
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
2018.5.19 by Andrew Bennetts
Add docstrings to all the new modules, and a few other places.
17
"""Basic server-side logic for dealing with requests."""
18
19
2018.5.6 by Andrew Bennetts
Tidy ups, and turn do_hello and do_get_bundle into command objects.
20
import tempfile
21
2018.5.23 by Andrew Bennetts
Use a Registry for smart server command handlers.
22
from bzrlib import bzrdir, errors, registry, revision
2018.5.6 by Andrew Bennetts
Tidy ups, and turn do_hello and do_get_bundle into command objects.
23
from bzrlib.bundle.serializer import write_bundle
24
2018.5.4 by Andrew Bennetts
Split smart server VFS logic out into a new file, and start using the command pattern in the SmartServerRequestHandler.
25
26
class SmartServerRequest(object):
27
    """Base class for request handlers.
28
    """
29
30
    def __init__(self, backing_transport):
31
        self._backing_transport = backing_transport
2018.5.5 by Andrew Bennetts
Pass body_bytes directly to SmartServerRequest.do_body
32
2018.5.24 by Andrew Bennetts
Setting NO_SMART_VFS in environment will disable VFS methods in the smart server. (Robert Collins, John Arbash Meinel, Andrew Bennetts)
33
    def _check_enabled(self):
34
        """Raises DisabledMethod if this method is disabled."""
35
        pass
36
2018.5.19 by Andrew Bennetts
Add docstrings to all the new modules, and a few other places.
37
    def do(self, *args):
2018.5.24 by Andrew Bennetts
Setting NO_SMART_VFS in environment will disable VFS methods in the smart server. (Robert Collins, John Arbash Meinel, Andrew Bennetts)
38
        """Mandatory extension point for SmartServerRequest subclasses.
39
        
40
        Subclasses must implement this.
2018.5.19 by Andrew Bennetts
Add docstrings to all the new modules, and a few other places.
41
        
42
        This should return a SmartServerResponse if this command expects to
43
        receive no body.
44
        """
2018.5.5 by Andrew Bennetts
Pass body_bytes directly to SmartServerRequest.do_body
45
        raise NotImplementedError(self.do)
46
2018.5.24 by Andrew Bennetts
Setting NO_SMART_VFS in environment will disable VFS methods in the smart server. (Robert Collins, John Arbash Meinel, Andrew Bennetts)
47
    def execute(self, *args):
48
        """Public entry point to execute this request.
49
50
        It will return a SmartServerResponse if the command does not expect a
51
        body.
52
53
        :param *args: the arguments of the request.
54
        """
55
        self._check_enabled()
56
        return self.do(*args)
57
2018.5.5 by Andrew Bennetts
Pass body_bytes directly to SmartServerRequest.do_body
58
    def do_body(self, body_bytes):
2018.5.19 by Andrew Bennetts
Add docstrings to all the new modules, and a few other places.
59
        """Called if the client sends a body with the request.
60
        
61
        Must return a SmartServerResponse.
62
        """
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.
2018.5.4 by Andrew Bennetts
Split smart server VFS logic out into a new file, and start using the command pattern in the SmartServerRequestHandler.
67
        raise NotImplementedError(self.do_body)
68
2018.5.6 by Andrew Bennetts
Tidy ups, and turn do_hello and do_get_bundle into command objects.
69
2018.5.16 by Andrew Bennetts
Move SmartServerResponse to smart/request.py, untangling more import dependencies.
70
class SmartServerResponse(object):
71
    """Response generated by SmartServerRequestHandler."""
72
73
    def __init__(self, args, body=None):
74
        self.args = args
75
        self.body = body
76
2018.6.1 by Robert Collins
Implement a BzrDir.open_branch smart server method for opening a branch without VFS.
77
    def __eq__(self, other):
2018.5.41 by Robert Collins
Fix SmartServerResponse.__eq__ to handle None.
78
        if other is None:
79
            return False
2018.6.1 by Robert Collins
Implement a BzrDir.open_branch smart server method for opening a branch without VFS.
80
        return other.args == self.args and other.body == self.body
81
82
    def __repr__(self):
83
        return "<SmartServerResponse args=%r body=%r>" % (self.args, self.body)
84
2018.5.16 by Andrew Bennetts
Move SmartServerResponse to smart/request.py, untangling more import dependencies.
85
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
86
class SmartServerRequestHandler(object):
87
    """Protocol logic for smart server.
88
    
89
    This doesn't handle serialization at all, it just processes requests and
90
    creates responses.
91
    """
92
93
    # IMPORTANT FOR IMPLEMENTORS: It is important that SmartServerRequestHandler
94
    # not contain encoding or decoding logic to allow the wire protocol to vary
95
    # from the object protocol: we will want to tweak the wire protocol separate
96
    # from the object model, and ideally we will be able to do that without
97
    # having a SmartServerRequestHandler subclass for each wire protocol, rather
98
    # just a Protocol subclass.
99
100
    # TODO: Better way of representing the body for commands that take it,
101
    # and allow it to be streamed into the server.
102
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
103
    def __init__(self, backing_transport, commands):
104
        """Constructor.
105
106
        :param backing_transport: a Transport to handle requests for.
2018.5.23 by Andrew Bennetts
Use a Registry for smart server command handlers.
107
        :param commands: a registry mapping command names to SmartServerRequest
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
108
            subclasses. e.g. bzrlib.transport.smart.vfs.vfs_commands.
109
        """
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
110
        self._backing_transport = backing_transport
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
111
        self._commands = commands
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
112
        self._body_bytes = ''
113
        self.response = None
114
        self.finished_reading = False
115
        self._command = None
116
117
    def accept_body(self, bytes):
118
        """Accept body data."""
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
        
128
    def end_of_body(self):
129
        """No more body data will be received."""
130
        self._run_handler_code(self._command.do_body, (self._body_bytes,), {})
131
        # cannot read after this.
132
        self.finished_reading = True
133
134
    def dispatch_command(self, cmd, args):
135
        """Deprecated compatibility method.""" # XXX XXX
2018.5.23 by Andrew Bennetts
Use a Registry for smart server command handlers.
136
        try:
137
            command = self._commands.get(cmd)
138
        except LookupError:
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
139
            raise errors.SmartProtocolError("bad request %r" % (cmd,))
140
        self._command = command(self._backing_transport)
2018.5.24 by Andrew Bennetts
Setting NO_SMART_VFS in environment will disable VFS methods in the smart server. (Robert Collins, John Arbash Meinel, Andrew Bennetts)
141
        self._run_handler_code(self._command.execute, args, {})
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
142
143
    def _run_handler_code(self, callable, args, kwargs):
144
        """Run some handler specific code 'callable'.
145
146
        If a result is returned, it is considered to be the commands response,
147
        and finished_reading is set true, and its assigned to self.response.
148
149
        Any exceptions caught are translated and a response object created
150
        from them.
151
        """
152
        result = self._call_converting_errors(callable, args, kwargs)
2018.5.19 by Andrew Bennetts
Add docstrings to all the new modules, and a few other places.
153
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
154
        if result is not None:
155
            self.response = result
156
            self.finished_reading = True
157
158
    def _call_converting_errors(self, callable, args, kwargs):
159
        """Call callable converting errors to Response objects."""
160
        # XXX: most of this error conversion is VFS-related, and thus ought to
161
        # be in SmartServerVFSRequestHandler somewhere.
162
        try:
163
            return callable(*args, **kwargs)
164
        except errors.NoSuchFile, e:
2018.5.16 by Andrew Bennetts
Move SmartServerResponse to smart/request.py, untangling more import dependencies.
165
            return SmartServerResponse(('NoSuchFile', e.path))
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
166
        except errors.FileExists, e:
2018.5.16 by Andrew Bennetts
Move SmartServerResponse to smart/request.py, untangling more import dependencies.
167
            return SmartServerResponse(('FileExists', e.path))
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
168
        except errors.DirectoryNotEmpty, e:
2018.5.16 by Andrew Bennetts
Move SmartServerResponse to smart/request.py, untangling more import dependencies.
169
            return SmartServerResponse(('DirectoryNotEmpty', e.path))
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
170
        except errors.ShortReadvError, e:
2018.5.16 by Andrew Bennetts
Move SmartServerResponse to smart/request.py, untangling more import dependencies.
171
            return SmartServerResponse(('ShortReadvError',
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
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
2018.5.16 by Andrew Bennetts
Move SmartServerResponse to smart/request.py, untangling more import dependencies.
184
            return SmartServerResponse((e.__class__.__name__,
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
185
                    e.encoding, val, str(e.start), str(e.end), e.reason))
186
        except errors.TransportNotPossible, e:
187
            if e.msg == "readonly transport":
2018.5.16 by Andrew Bennetts
Move SmartServerResponse to smart/request.py, untangling more import dependencies.
188
                return SmartServerResponse(('ReadOnlyError', ))
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
189
            else:
190
                raise
191
192
2018.5.6 by Andrew Bennetts
Tidy ups, and turn do_hello and do_get_bundle into command objects.
193
class HelloRequest(SmartServerRequest):
194
    """Answer a version request with my version."""
195
196
    def do(self):
2018.5.16 by Andrew Bennetts
Move SmartServerResponse to smart/request.py, untangling more import dependencies.
197
        return SmartServerResponse(('ok', '1'))
2018.5.6 by Andrew Bennetts
Tidy ups, and turn do_hello and do_get_bundle into command objects.
198
199
200
class GetBundleRequest(SmartServerRequest):
201
202
    def do(self, path, revision_id):
203
        # open transport relative to our base
204
        t = self._backing_transport.clone(path)
205
        control, extra_path = bzrdir.BzrDir.open_containing_from_transport(t)
206
        repo = control.open_repository()
207
        tmpf = tempfile.TemporaryFile()
208
        base_revision = revision.NULL_REVISION
209
        write_bundle(repo, revision_id, base_revision, tmpf)
210
        tmpf.seek(0)
2018.5.16 by Andrew Bennetts
Move SmartServerResponse to smart/request.py, untangling more import dependencies.
211
        return SmartServerResponse((), tmpf.read())
2018.5.6 by Andrew Bennetts
Tidy ups, and turn do_hello and do_get_bundle into command objects.
212
213
2018.5.26 by Andrew Bennetts
Extract a simple SmartClient class from RemoteTransport, and a hack to avoid VFS operations when probing for a bzrdir over a smart transport.
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,))
231
232
2018.5.95 by Andrew Bennetts
Add a Transport.is_readonly remote call, let {Branch,Repository}.lock_write remote call return UnlockableTransport, and miscellaneous test fixes.
233
class SmartServerIsReadonly(SmartServerRequest):
234
    # XXX: this request method belongs somewhere else.
235
236
    def do(self):
237
        if self._backing_transport.is_readonly():
238
            answer = 'yes'
239
        else:
240
            answer = 'no'
241
        return SmartServerResponse((answer,))
242
243
2018.5.23 by Andrew Bennetts
Use a Registry for smart server command handlers.
244
request_handlers = registry.Registry()
245
request_handlers.register_lazy(
246
    'append', 'bzrlib.smart.vfs', 'AppendRequest')
247
request_handlers.register_lazy(
2018.5.59 by Robert Collins
Get BranchConfig working somewhat on RemoteBranches (Robert Collins, Vincent Ladeuil).
248
    'Branch.get_config_file', 'bzrlib.smart.branch', 'SmartServerBranchGetConfigFile')
249
request_handlers.register_lazy(
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
250
    'Branch.last_revision_info', 'bzrlib.smart.branch', 'SmartServerBranchRequestLastRevisionInfo')
251
request_handlers.register_lazy(
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
252
    'Branch.lock_write', 'bzrlib.smart.branch', 'SmartServerBranchRequestLockWrite')
253
request_handlers.register_lazy(
2018.5.38 by Robert Collins
Implement RemoteBranch.revision_history().
254
    'Branch.revision_history', 'bzrlib.smart.branch', 'SmartServerRequestRevisionHistory')
255
request_handlers.register_lazy(
2018.5.77 by Wouter van Heyst
Fix typo in request_handlers registration of Branch.set_last_revision, and test that registration
256
    'Branch.set_last_revision', 'bzrlib.smart.branch', 'SmartServerBranchRequestSetLastRevision')
2018.12.3 by Andrew Bennetts
Add a Branch.set_last_revision smart method, and make RemoteBranch.set_revision_history use it.
257
request_handlers.register_lazy(
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
258
    'Branch.unlock', 'bzrlib.smart.branch', 'SmartServerBranchRequestUnlock')
259
request_handlers.register_lazy(
2018.5.34 by Robert Collins
Get test_remote.BasicRemoteObjectTests.test_open_remote_branch passing by implementing a remote method BzrDir.find_repository.
260
    'BzrDir.find_repository', 'bzrlib.smart.bzrdir', 'SmartServerRequestFindRepository')
261
request_handlers.register_lazy(
2018.5.42 by Robert Collins
Various hopefully improvements, but wsgi is broken, handing over to spiv :).
262
    'BzrDirFormat.initialize', 'bzrlib.smart.bzrdir', 'SmartServerRequestInitializeBzrDir')
263
request_handlers.register_lazy(
2018.6.1 by Robert Collins
Implement a BzrDir.open_branch smart server method for opening a branch without VFS.
264
    'BzrDir.open_branch', 'bzrlib.smart.bzrdir', 'SmartServerRequestOpenBranch')
265
request_handlers.register_lazy(
2018.5.23 by Andrew Bennetts
Use a Registry for smart server command handlers.
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(
2018.5.37 by Andrew Bennetts
Make sure all the request handlers in bzrlib/smart/vfs.py have consistent names.
276
    'iter_files_recursive', 'bzrlib.smart.vfs', 'IterFilesRecursiveRequest')
2018.5.23 by Andrew Bennetts
Use a Registry for smart server command handlers.
277
request_handlers.register_lazy(
278
    'list_dir', 'bzrlib.smart.vfs', 'ListDirRequest')
279
request_handlers.register_lazy(
2018.5.37 by Andrew Bennetts
Make sure all the request handlers in bzrlib/smart/vfs.py have consistent names.
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')
2018.10.2 by v.ladeuil+lp at free
gather_stats server side and request registration
291
request_handlers.register_lazy('Repository.gather_stats',
292
                               'bzrlib.smart.repository',
293
                               'SmartServerRepositoryGatherStats')
2018.5.37 by Andrew Bennetts
Make sure all the request handlers in bzrlib/smart/vfs.py have consistent names.
294
request_handlers.register_lazy(
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
295
    'Repository.get_revision_graph', 'bzrlib.smart.repository', 'SmartServerRepositoryGetRevisionGraph')
296
request_handlers.register_lazy(
2018.5.40 by Robert Collins
Implement a remote Repository.has_revision method.
297
    'Repository.has_revision', 'bzrlib.smart.repository', 'SmartServerRequestHasRevision')
298
request_handlers.register_lazy(
2018.5.57 by Robert Collins
Implement RemoteRepository.is_shared (Robert Collins, Vincent Ladeuil).
299
    'Repository.is_shared', 'bzrlib.smart.repository', 'SmartServerRepositoryIsShared')
300
request_handlers.register_lazy(
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
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(
2018.5.37 by Andrew Bennetts
Make sure all the request handlers in bzrlib/smart/vfs.py have consistent names.
305
    'rmdir', 'bzrlib.smart.vfs', 'RmdirRequest')
306
request_handlers.register_lazy(
307
    'stat', 'bzrlib.smart.vfs', 'StatRequest')
2018.5.26 by Andrew Bennetts
Extract a simple SmartClient class from RemoteTransport, and a hack to avoid VFS operations when probing for a bzrdir over a smart transport.
308
request_handlers.register_lazy(
2018.5.95 by Andrew Bennetts
Add a Transport.is_readonly remote call, let {Branch,Repository}.lock_write remote call return UnlockableTransport, and miscellaneous test fixes.
309
    'Transport.is_readonly', 'bzrlib.smart.request', 'SmartServerIsReadonly')
310
request_handlers.register_lazy(
2018.5.26 by Andrew Bennetts
Extract a simple SmartClient class from RemoteTransport, and a hack to avoid VFS operations when probing for a bzrdir over a smart transport.
311
    'probe_dont_use', 'bzrlib.smart.request', 'ProbeDontUseRequest')