/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
77
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
78
class SmartServerRequestHandler(object):
79
    """Protocol logic for smart server.
80
    
81
    This doesn't handle serialization at all, it just processes requests and
82
    creates responses.
83
    """
84
85
    # IMPORTANT FOR IMPLEMENTORS: It is important that SmartServerRequestHandler
86
    # not contain encoding or decoding logic to allow the wire protocol to vary
87
    # from the object protocol: we will want to tweak the wire protocol separate
88
    # from the object model, and ideally we will be able to do that without
89
    # having a SmartServerRequestHandler subclass for each wire protocol, rather
90
    # just a Protocol subclass.
91
92
    # TODO: Better way of representing the body for commands that take it,
93
    # and allow it to be streamed into the server.
94
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
95
    def __init__(self, backing_transport, commands):
96
        """Constructor.
97
98
        :param backing_transport: a Transport to handle requests for.
2018.5.23 by Andrew Bennetts
Use a Registry for smart server command handlers.
99
        :param commands: a registry mapping command names to SmartServerRequest
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
100
            subclasses. e.g. bzrlib.transport.smart.vfs.vfs_commands.
101
        """
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
102
        self._backing_transport = backing_transport
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
103
        self._commands = commands
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
104
        self._body_bytes = ''
105
        self.response = None
106
        self.finished_reading = False
107
        self._command = None
108
109
    def accept_body(self, bytes):
110
        """Accept body data."""
111
112
        # TODO: This should be overriden for each command that desired body data
113
        # to handle the right format of that data, i.e. plain bytes, a bundle,
114
        # etc.  The deserialisation into that format should be done in the
115
        # Protocol object.
116
117
        # default fallback is to accumulate bytes.
118
        self._body_bytes += bytes
119
        
120
    def end_of_body(self):
121
        """No more body data will be received."""
122
        self._run_handler_code(self._command.do_body, (self._body_bytes,), {})
123
        # cannot read after this.
124
        self.finished_reading = True
125
126
    def dispatch_command(self, cmd, args):
127
        """Deprecated compatibility method.""" # XXX XXX
2018.5.23 by Andrew Bennetts
Use a Registry for smart server command handlers.
128
        try:
129
            command = self._commands.get(cmd)
130
        except LookupError:
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
131
            raise errors.SmartProtocolError("bad request %r" % (cmd,))
132
        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)
133
        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.
134
135
    def _run_handler_code(self, callable, args, kwargs):
136
        """Run some handler specific code 'callable'.
137
138
        If a result is returned, it is considered to be the commands response,
139
        and finished_reading is set true, and its assigned to self.response.
140
141
        Any exceptions caught are translated and a response object created
142
        from them.
143
        """
144
        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.
145
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
146
        if result is not None:
147
            self.response = result
148
            self.finished_reading = True
149
150
    def _call_converting_errors(self, callable, args, kwargs):
151
        """Call callable converting errors to Response objects."""
152
        # XXX: most of this error conversion is VFS-related, and thus ought to
153
        # be in SmartServerVFSRequestHandler somewhere.
154
        try:
155
            return callable(*args, **kwargs)
156
        except errors.NoSuchFile, e:
2018.5.16 by Andrew Bennetts
Move SmartServerResponse to smart/request.py, untangling more import dependencies.
157
            return SmartServerResponse(('NoSuchFile', e.path))
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
158
        except errors.FileExists, e:
2018.5.16 by Andrew Bennetts
Move SmartServerResponse to smart/request.py, untangling more import dependencies.
159
            return SmartServerResponse(('FileExists', e.path))
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
160
        except errors.DirectoryNotEmpty, e:
2018.5.16 by Andrew Bennetts
Move SmartServerResponse to smart/request.py, untangling more import dependencies.
161
            return SmartServerResponse(('DirectoryNotEmpty', e.path))
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
162
        except errors.ShortReadvError, e:
2018.5.16 by Andrew Bennetts
Move SmartServerResponse to smart/request.py, untangling more import dependencies.
163
            return SmartServerResponse(('ShortReadvError',
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
164
                e.path, str(e.offset), str(e.length), str(e.actual)))
165
        except UnicodeError, e:
166
            # If it is a DecodeError, than most likely we are starting
167
            # with a plain string
168
            str_or_unicode = e.object
169
            if isinstance(str_or_unicode, unicode):
170
                # XXX: UTF-8 might have \x01 (our seperator byte) in it.  We
171
                # should escape it somehow.
172
                val = 'u:' + str_or_unicode.encode('utf-8')
173
            else:
174
                val = 's:' + str_or_unicode.encode('base64')
175
            # This handles UnicodeEncodeError or UnicodeDecodeError
2018.5.16 by Andrew Bennetts
Move SmartServerResponse to smart/request.py, untangling more import dependencies.
176
            return SmartServerResponse((e.__class__.__name__,
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
177
                    e.encoding, val, str(e.start), str(e.end), e.reason))
178
        except errors.TransportNotPossible, e:
179
            if e.msg == "readonly transport":
2018.5.16 by Andrew Bennetts
Move SmartServerResponse to smart/request.py, untangling more import dependencies.
180
                return SmartServerResponse(('ReadOnlyError', ))
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
181
            else:
182
                raise
183
184
2018.5.6 by Andrew Bennetts
Tidy ups, and turn do_hello and do_get_bundle into command objects.
185
class HelloRequest(SmartServerRequest):
186
    """Answer a version request with my version."""
187
188
    def do(self):
2018.5.16 by Andrew Bennetts
Move SmartServerResponse to smart/request.py, untangling more import dependencies.
189
        return SmartServerResponse(('ok', '1'))
2018.5.6 by Andrew Bennetts
Tidy ups, and turn do_hello and do_get_bundle into command objects.
190
191
192
class GetBundleRequest(SmartServerRequest):
193
194
    def do(self, path, revision_id):
195
        # open transport relative to our base
196
        t = self._backing_transport.clone(path)
197
        control, extra_path = bzrdir.BzrDir.open_containing_from_transport(t)
198
        repo = control.open_repository()
199
        tmpf = tempfile.TemporaryFile()
200
        base_revision = revision.NULL_REVISION
201
        write_bundle(repo, revision_id, base_revision, tmpf)
202
        tmpf.seek(0)
2018.5.16 by Andrew Bennetts
Move SmartServerResponse to smart/request.py, untangling more import dependencies.
203
        return SmartServerResponse((), tmpf.read())
2018.5.6 by Andrew Bennetts
Tidy ups, and turn do_hello and do_get_bundle into command objects.
204
205
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.
206
# This exists solely to help RemoteObjectHacking.  It should be removed
207
# eventually.  It should not be considered part of the real smart server
208
# protocol!
209
class ProbeDontUseRequest(SmartServerRequest):
210
211
    def do(self, path):
212
        from bzrlib.bzrdir import BzrDirFormat
213
        t = self._backing_transport.clone(path)
214
        default_format = BzrDirFormat.get_default_format()
215
        real_bzrdir = default_format.open(t, _found=True)
216
        try:
217
            real_bzrdir._format.probe_transport(t)
218
        except (errors.NotBranchError, errors.UnknownFormatError):
219
            answer = 'no'
220
        else:
221
            answer = 'yes'
222
        return SmartServerResponse((answer,))
223
224
2018.5.23 by Andrew Bennetts
Use a Registry for smart server command handlers.
225
request_handlers = registry.Registry()
226
request_handlers.register_lazy(
227
    'append', 'bzrlib.smart.vfs', 'AppendRequest')
228
request_handlers.register_lazy(
229
    'delete', 'bzrlib.smart.vfs', 'DeleteRequest')
230
request_handlers.register_lazy(
231
    'get', 'bzrlib.smart.vfs', 'GetRequest')
232
request_handlers.register_lazy(
233
    'get_bundle', 'bzrlib.smart.request', 'GetBundleRequest')
234
request_handlers.register_lazy(
235
    'has', 'bzrlib.smart.vfs', 'HasRequest')
236
request_handlers.register_lazy(
237
    'hello', 'bzrlib.smart.request', 'HelloRequest')
238
request_handlers.register_lazy(
239
    'iter_files_recursive', 'bzrlib.smart.vfs', 'IterFilesRecursive')
240
request_handlers.register_lazy(
241
    'list_dir', 'bzrlib.smart.vfs', 'ListDirRequest')
242
request_handlers.register_lazy(
243
    'mkdir', 'bzrlib.smart.vfs', 'MkdirCommand')
244
request_handlers.register_lazy(
245
    'move', 'bzrlib.smart.vfs', 'MoveCommand')
246
request_handlers.register_lazy(
247
    'put', 'bzrlib.smart.vfs', 'PutCommand')
248
request_handlers.register_lazy(
249
    'put_non_atomic', 'bzrlib.smart.vfs', 'PutNonAtomicCommand')
250
request_handlers.register_lazy(
251
    'readv', 'bzrlib.smart.vfs', 'ReadvCommand')
252
request_handlers.register_lazy(
253
    'rename', 'bzrlib.smart.vfs', 'RenameCommand')
254
request_handlers.register_lazy(
255
    'rmdir', 'bzrlib.smart.vfs', 'RmdirCommand')
256
request_handlers.register_lazy(
257
    'stat', 'bzrlib.smart.vfs', 'StatCommand')
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.
258
request_handlers.register_lazy(
259
    'probe_dont_use', 'bzrlib.smart.request', 'ProbeDontUseRequest')