~bzr-pqm/bzr/bzr.dev

2018.5.157 by Andrew Bennetts
Remove unnecessary trivial divergences from bzr.dev.
1
# Copyright (C) 2006, 2007 Canonical Ltd
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.
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
2402.1.2 by Andrew Bennetts
Deal with review comments.
22
from bzrlib import (
23
    bzrdir,
24
    errors,
25
    registry,
26
    revision,
27
    )
2018.5.6 by Andrew Bennetts
Tidy ups, and turn do_hello and do_get_bundle into command objects.
28
from bzrlib.bundle.serializer import write_bundle
29
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.
30
31
class SmartServerRequest(object):
2402.1.2 by Andrew Bennetts
Deal with review comments.
32
    """Base class for request handlers."""
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.
33
34
    def __init__(self, backing_transport):
2402.1.2 by Andrew Bennetts
Deal with review comments.
35
        """Constructor.
36
37
        :param backing_transport: the base transport to be used when performing
38
            this request.
39
        """
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.
40
        self._backing_transport = backing_transport
2018.5.5 by Andrew Bennetts
Pass body_bytes directly to SmartServerRequest.do_body
41
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)
42
    def _check_enabled(self):
43
        """Raises DisabledMethod if this method is disabled."""
44
        pass
45
2018.5.19 by Andrew Bennetts
Add docstrings to all the new modules, and a few other places.
46
    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)
47
        """Mandatory extension point for SmartServerRequest subclasses.
48
        
49
        Subclasses must implement this.
2018.5.19 by Andrew Bennetts
Add docstrings to all the new modules, and a few other places.
50
        
51
        This should return a SmartServerResponse if this command expects to
52
        receive no body.
53
        """
2018.5.5 by Andrew Bennetts
Pass body_bytes directly to SmartServerRequest.do_body
54
        raise NotImplementedError(self.do)
55
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)
56
    def execute(self, *args):
57
        """Public entry point to execute this request.
58
59
        It will return a SmartServerResponse if the command does not expect a
60
        body.
61
62
        :param *args: the arguments of the request.
63
        """
64
        self._check_enabled()
65
        return self.do(*args)
66
2018.5.5 by Andrew Bennetts
Pass body_bytes directly to SmartServerRequest.do_body
67
    def do_body(self, body_bytes):
2018.5.19 by Andrew Bennetts
Add docstrings to all the new modules, and a few other places.
68
        """Called if the client sends a body with the request.
69
        
70
        Must return a SmartServerResponse.
71
        """
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.
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.
76
        raise NotImplementedError(self.do_body)
77
2018.5.6 by Andrew Bennetts
Tidy ups, and turn do_hello and do_get_bundle into command objects.
78
2018.5.16 by Andrew Bennetts
Move SmartServerResponse to smart/request.py, untangling more import dependencies.
79
class SmartServerResponse(object):
80
    """Response generated by SmartServerRequestHandler."""
81
82
    def __init__(self, args, body=None):
83
        self.args = args
84
        self.body = body
85
2402.1.1 by Andrew Bennetts
Use the Command pattern for handling smart server commands.
86
    def __eq__(self, other):
87
        if other is None:
88
            return False
89
        return other.args == self.args and other.body == self.body
90
91
    def __repr__(self):
92
        return "<SmartServerResponse args=%r body=%r>" % (self.args, self.body)
93
2018.5.16 by Andrew Bennetts
Move SmartServerResponse to smart/request.py, untangling more import dependencies.
94
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
95
class SmartServerRequestHandler(object):
96
    """Protocol logic for smart server.
97
    
98
    This doesn't handle serialization at all, it just processes requests and
99
    creates responses.
100
    """
101
102
    # IMPORTANT FOR IMPLEMENTORS: It is important that SmartServerRequestHandler
103
    # not contain encoding or decoding logic to allow the wire protocol to vary
104
    # from the object protocol: we will want to tweak the wire protocol separate
105
    # from the object model, and ideally we will be able to do that without
106
    # having a SmartServerRequestHandler subclass for each wire protocol, rather
107
    # just a Protocol subclass.
108
109
    # TODO: Better way of representing the body for commands that take it,
110
    # and allow it to be streamed into the server.
111
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
112
    def __init__(self, backing_transport, commands):
113
        """Constructor.
114
115
        :param backing_transport: a Transport to handle requests for.
2018.5.23 by Andrew Bennetts
Use a Registry for smart server command handlers.
116
        :param commands: a registry mapping command names to SmartServerRequest
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
117
            subclasses. e.g. bzrlib.transport.smart.vfs.vfs_commands.
118
        """
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
119
        self._backing_transport = backing_transport
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
120
        self._commands = commands
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
121
        self._body_bytes = ''
122
        self.response = None
123
        self.finished_reading = False
124
        self._command = None
125
126
    def accept_body(self, bytes):
127
        """Accept body data."""
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
        
137
    def end_of_body(self):
138
        """No more body data will be received."""
139
        self._run_handler_code(self._command.do_body, (self._body_bytes,), {})
140
        # cannot read after this.
141
        self.finished_reading = True
142
143
    def dispatch_command(self, cmd, args):
144
        """Deprecated compatibility method.""" # XXX XXX
2018.5.23 by Andrew Bennetts
Use a Registry for smart server command handlers.
145
        try:
146
            command = self._commands.get(cmd)
147
        except LookupError:
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
148
            raise errors.SmartProtocolError("bad request %r" % (cmd,))
149
        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)
150
        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.
151
152
    def _run_handler_code(self, callable, args, kwargs):
153
        """Run some handler specific code 'callable'.
154
155
        If a result is returned, it is considered to be the commands response,
156
        and finished_reading is set true, and its assigned to self.response.
157
158
        Any exceptions caught are translated and a response object created
159
        from them.
160
        """
161
        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.
162
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
163
        if result is not None:
164
            self.response = result
165
            self.finished_reading = True
166
167
    def _call_converting_errors(self, callable, args, kwargs):
168
        """Call callable converting errors to Response objects."""
169
        # XXX: most of this error conversion is VFS-related, and thus ought to
170
        # be in SmartServerVFSRequestHandler somewhere.
171
        try:
172
            return callable(*args, **kwargs)
173
        except errors.NoSuchFile, e:
2018.5.16 by Andrew Bennetts
Move SmartServerResponse to smart/request.py, untangling more import dependencies.
174
            return SmartServerResponse(('NoSuchFile', e.path))
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
175
        except errors.FileExists, e:
2018.5.16 by Andrew Bennetts
Move SmartServerResponse to smart/request.py, untangling more import dependencies.
176
            return SmartServerResponse(('FileExists', e.path))
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
177
        except errors.DirectoryNotEmpty, e:
2018.5.16 by Andrew Bennetts
Move SmartServerResponse to smart/request.py, untangling more import dependencies.
178
            return SmartServerResponse(('DirectoryNotEmpty', e.path))
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
179
        except errors.ShortReadvError, e:
2018.5.16 by Andrew Bennetts
Move SmartServerResponse to smart/request.py, untangling more import dependencies.
180
            return SmartServerResponse(('ShortReadvError',
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
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
2018.5.16 by Andrew Bennetts
Move SmartServerResponse to smart/request.py, untangling more import dependencies.
193
            return SmartServerResponse((e.__class__.__name__,
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
194
                    e.encoding, val, str(e.start), str(e.end), e.reason))
195
        except errors.TransportNotPossible, e:
196
            if e.msg == "readonly transport":
2018.5.16 by Andrew Bennetts
Move SmartServerResponse to smart/request.py, untangling more import dependencies.
197
                return SmartServerResponse(('ReadOnlyError', ))
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
198
            else:
199
                raise
200
201
2018.5.6 by Andrew Bennetts
Tidy ups, and turn do_hello and do_get_bundle into command objects.
202
class HelloRequest(SmartServerRequest):
2432.2.6 by Andrew Bennetts
Improve HelloRequest's docstring.
203
    """Answer a version request with the highest protocol version this server
204
    supports.
205
    """
2018.5.6 by Andrew Bennetts
Tidy ups, and turn do_hello and do_get_bundle into command objects.
206
207
    def do(self):
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
208
        return SmartServerResponse(('ok', '2'))
2018.5.6 by Andrew Bennetts
Tidy ups, and turn do_hello and do_get_bundle into command objects.
209
210
211
class GetBundleRequest(SmartServerRequest):
2402.1.2 by Andrew Bennetts
Deal with review comments.
212
    """Get a bundle of from the null revision to the specified revision."""
2018.5.6 by Andrew Bennetts
Tidy ups, and turn do_hello and do_get_bundle into command objects.
213
214
    def do(self, path, revision_id):
215
        # open transport relative to our base
216
        t = self._backing_transport.clone(path)
217
        control, extra_path = bzrdir.BzrDir.open_containing_from_transport(t)
218
        repo = control.open_repository()
219
        tmpf = tempfile.TemporaryFile()
220
        base_revision = revision.NULL_REVISION
221
        write_bundle(repo, revision_id, base_revision, tmpf)
222
        tmpf.seek(0)
2018.5.16 by Andrew Bennetts
Move SmartServerResponse to smart/request.py, untangling more import dependencies.
223
        return SmartServerResponse((), tmpf.read())
2018.5.6 by Andrew Bennetts
Tidy ups, and turn do_hello and do_get_bundle into command objects.
224
225
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.
226
class SmartServerIsReadonly(SmartServerRequest):
227
    # XXX: this request method belongs somewhere else.
228
229
    def do(self):
230
        if self._backing_transport.is_readonly():
231
            answer = 'yes'
232
        else:
233
            answer = 'no'
234
        return SmartServerResponse((answer,))
235
236
2018.5.23 by Andrew Bennetts
Use a Registry for smart server command handlers.
237
request_handlers = registry.Registry()
238
request_handlers.register_lazy(
239
    'append', 'bzrlib.smart.vfs', 'AppendRequest')
240
request_handlers.register_lazy(
2018.5.59 by Robert Collins
Get BranchConfig working somewhat on RemoteBranches (Robert Collins, Vincent Ladeuil).
241
    'Branch.get_config_file', 'bzrlib.smart.branch', 'SmartServerBranchGetConfigFile')
242
request_handlers.register_lazy(
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
243
    'Branch.last_revision_info', 'bzrlib.smart.branch', 'SmartServerBranchRequestLastRevisionInfo')
244
request_handlers.register_lazy(
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
245
    'Branch.lock_write', 'bzrlib.smart.branch', 'SmartServerBranchRequestLockWrite')
246
request_handlers.register_lazy(
2018.5.38 by Robert Collins
Implement RemoteBranch.revision_history().
247
    'Branch.revision_history', 'bzrlib.smart.branch', 'SmartServerRequestRevisionHistory')
248
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
249
    '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.
250
request_handlers.register_lazy(
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
251
    'Branch.unlock', 'bzrlib.smart.branch', 'SmartServerBranchRequestUnlock')
252
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.
253
    'BzrDir.find_repository', 'bzrlib.smart.bzrdir', 'SmartServerRequestFindRepository')
254
request_handlers.register_lazy(
2018.5.42 by Robert Collins
Various hopefully improvements, but wsgi is broken, handing over to spiv :).
255
    'BzrDirFormat.initialize', 'bzrlib.smart.bzrdir', 'SmartServerRequestInitializeBzrDir')
256
request_handlers.register_lazy(
2018.6.1 by Robert Collins
Implement a BzrDir.open_branch smart server method for opening a branch without VFS.
257
    'BzrDir.open_branch', 'bzrlib.smart.bzrdir', 'SmartServerRequestOpenBranch')
258
request_handlers.register_lazy(
2018.5.23 by Andrew Bennetts
Use a Registry for smart server command handlers.
259
    'delete', 'bzrlib.smart.vfs', 'DeleteRequest')
260
request_handlers.register_lazy(
261
    'get', 'bzrlib.smart.vfs', 'GetRequest')
262
request_handlers.register_lazy(
263
    'get_bundle', 'bzrlib.smart.request', 'GetBundleRequest')
264
request_handlers.register_lazy(
265
    'has', 'bzrlib.smart.vfs', 'HasRequest')
266
request_handlers.register_lazy(
267
    'hello', 'bzrlib.smart.request', 'HelloRequest')
268
request_handlers.register_lazy(
2018.5.37 by Andrew Bennetts
Make sure all the request handlers in bzrlib/smart/vfs.py have consistent names.
269
    'iter_files_recursive', 'bzrlib.smart.vfs', 'IterFilesRecursiveRequest')
2018.5.23 by Andrew Bennetts
Use a Registry for smart server command handlers.
270
request_handlers.register_lazy(
271
    'list_dir', 'bzrlib.smart.vfs', 'ListDirRequest')
272
request_handlers.register_lazy(
2018.5.37 by Andrew Bennetts
Make sure all the request handlers in bzrlib/smart/vfs.py have consistent names.
273
    'mkdir', 'bzrlib.smart.vfs', 'MkdirRequest')
274
request_handlers.register_lazy(
275
    'move', 'bzrlib.smart.vfs', 'MoveRequest')
276
request_handlers.register_lazy(
277
    'put', 'bzrlib.smart.vfs', 'PutRequest')
278
request_handlers.register_lazy(
279
    'put_non_atomic', 'bzrlib.smart.vfs', 'PutNonAtomicRequest')
280
request_handlers.register_lazy(
281
    'readv', 'bzrlib.smart.vfs', 'ReadvRequest')
282
request_handlers.register_lazy(
283
    'rename', 'bzrlib.smart.vfs', 'RenameRequest')
2018.10.2 by v.ladeuil+lp at free
gather_stats server side and request registration
284
request_handlers.register_lazy('Repository.gather_stats',
285
                               'bzrlib.smart.repository',
286
                               'SmartServerRepositoryGatherStats')
2018.5.37 by Andrew Bennetts
Make sure all the request handlers in bzrlib/smart/vfs.py have consistent names.
287
request_handlers.register_lazy(
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
288
    'Repository.get_revision_graph', 'bzrlib.smart.repository', 'SmartServerRepositoryGetRevisionGraph')
289
request_handlers.register_lazy(
2018.5.40 by Robert Collins
Implement a remote Repository.has_revision method.
290
    'Repository.has_revision', 'bzrlib.smart.repository', 'SmartServerRequestHasRevision')
291
request_handlers.register_lazy(
2018.5.57 by Robert Collins
Implement RemoteRepository.is_shared (Robert Collins, Vincent Ladeuil).
292
    'Repository.is_shared', 'bzrlib.smart.repository', 'SmartServerRepositoryIsShared')
293
request_handlers.register_lazy(
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
294
    'Repository.lock_write', 'bzrlib.smart.repository', 'SmartServerRepositoryLockWrite')
295
request_handlers.register_lazy(
296
    'Repository.unlock', 'bzrlib.smart.repository', 'SmartServerRepositoryUnlock')
297
request_handlers.register_lazy(
2018.5.37 by Andrew Bennetts
Make sure all the request handlers in bzrlib/smart/vfs.py have consistent names.
298
    'rmdir', 'bzrlib.smart.vfs', 'RmdirRequest')
299
request_handlers.register_lazy(
300
    '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.
301
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.
302
    'Transport.is_readonly', 'bzrlib.smart.request', 'SmartServerIsReadonly')
303
request_handlers.register_lazy(
2018.5.163 by Andrew Bennetts
Deal with various review comments from Robert.
304
    'BzrDir.open', 'bzrlib.smart.bzrdir', 'SmartServerRequestOpenBzrDir')