/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
2402.1.2 by Andrew Bennetts
Deal with review comments.
1
# Copyright (C) 2006, 2007 Canonical Ltd
2400.1.5 by Andrew Bennetts
Add some missing docstrings and copyright boilerplate.
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
17
"""Basic server-side logic for dealing with requests."""
18
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
19
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
    )
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
28
from bzrlib.bundle.serializer import write_bundle
29
30
2402.1.1 by Andrew Bennetts
Use the Command pattern for handling smart server commands.
31
class SmartServerRequest(object):
2402.1.2 by Andrew Bennetts
Deal with review comments.
32
    """Base class for request handlers."""
2402.1.1 by Andrew Bennetts
Use the Command pattern for handling smart server commands.
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
        """
2402.1.1 by Andrew Bennetts
Use the Command pattern for handling smart server commands.
40
        self._backing_transport = backing_transport
41
42
    def _check_enabled(self):
43
        """Raises DisabledMethod if this method is disabled."""
44
        pass
45
46
    def do(self, *args):
47
        """Mandatory extension point for SmartServerRequest subclasses.
48
        
49
        Subclasses must implement this.
50
        
51
        This should return a SmartServerResponse if this command expects to
52
        receive no body.
53
        """
54
        raise NotImplementedError(self.do)
55
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
67
    def do_body(self, body_bytes):
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.
76
        raise NotImplementedError(self.do_body)
77
78
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
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
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
94
2432.4.2 by Robert Collins
Add FailedSmartServerResponse.
95
class FailedSmartServerResponse(SmartServerResponse):
96
    """A SmartServerResponse for a request which failed."""
97
98
    def is_successful(self):
99
        """FailedSmartServerResponse are not successful."""
100
        return False
101
102
2432.4.1 by Robert Collins
Add SuccessfulSmartServerResponse.
103
class SuccessfulSmartServerResponse(SmartServerResponse):
104
    """A SmartServerResponse for a successfully completed request."""
105
106
    def is_successful(self):
107
        """SuccessfulSmartServerResponse are successful."""
108
        return True
109
110
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
111
class SmartServerRequestHandler(object):
112
    """Protocol logic for smart server.
113
    
114
    This doesn't handle serialization at all, it just processes requests and
115
    creates responses.
116
    """
117
118
    # IMPORTANT FOR IMPLEMENTORS: It is important that SmartServerRequestHandler
119
    # not contain encoding or decoding logic to allow the wire protocol to vary
120
    # from the object protocol: we will want to tweak the wire protocol separate
121
    # from the object model, and ideally we will be able to do that without
122
    # having a SmartServerRequestHandler subclass for each wire protocol, rather
123
    # just a Protocol subclass.
124
125
    # TODO: Better way of representing the body for commands that take it,
126
    # and allow it to be streamed into the server.
2402.1.1 by Andrew Bennetts
Use the Command pattern for handling smart server commands.
127
128
    def __init__(self, backing_transport, commands):
129
        """Constructor.
130
131
        :param backing_transport: a Transport to handle requests for.
132
        :param commands: a registry mapping command names to SmartServerRequest
133
            subclasses. e.g. bzrlib.transport.smart.vfs.vfs_commands.
134
        """
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
135
        self._backing_transport = backing_transport
2402.1.1 by Andrew Bennetts
Use the Command pattern for handling smart server commands.
136
        self._commands = commands
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
137
        self._body_bytes = ''
138
        self.response = None
2402.1.1 by Andrew Bennetts
Use the Command pattern for handling smart server commands.
139
        self.finished_reading = False
140
        self._command = None
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
141
142
    def accept_body(self, bytes):
2402.1.1 by Andrew Bennetts
Use the Command pattern for handling smart server commands.
143
        """Accept body data."""
144
145
        # TODO: This should be overriden for each command that desired body data
146
        # to handle the right format of that data, i.e. plain bytes, a bundle,
147
        # etc.  The deserialisation into that format should be done in the
148
        # Protocol object.
149
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
150
        # default fallback is to accumulate bytes.
151
        self._body_bytes += bytes
152
        
153
    def end_of_body(self):
154
        """No more body data will be received."""
2402.1.1 by Andrew Bennetts
Use the Command pattern for handling smart server commands.
155
        self._run_handler_code(self._command.do_body, (self._body_bytes,), {})
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
156
        # cannot read after this.
157
        self.finished_reading = True
158
159
    def dispatch_command(self, cmd, args):
160
        """Deprecated compatibility method.""" # XXX XXX
2402.1.1 by Andrew Bennetts
Use the Command pattern for handling smart server commands.
161
        try:
162
            command = self._commands.get(cmd)
163
        except LookupError:
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
164
            raise errors.SmartProtocolError("bad request %r" % (cmd,))
2402.1.1 by Andrew Bennetts
Use the Command pattern for handling smart server commands.
165
        self._command = command(self._backing_transport)
166
        self._run_handler_code(self._command.execute, args, {})
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
167
168
    def _run_handler_code(self, callable, args, kwargs):
169
        """Run some handler specific code 'callable'.
170
171
        If a result is returned, it is considered to be the commands response,
172
        and finished_reading is set true, and its assigned to self.response.
173
174
        Any exceptions caught are translated and a response object created
175
        from them.
176
        """
177
        result = self._call_converting_errors(callable, args, kwargs)
2402.1.1 by Andrew Bennetts
Use the Command pattern for handling smart server commands.
178
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
179
        if result is not None:
180
            self.response = result
181
            self.finished_reading = True
182
183
    def _call_converting_errors(self, callable, args, kwargs):
184
        """Call callable converting errors to Response objects."""
2402.1.1 by Andrew Bennetts
Use the Command pattern for handling smart server commands.
185
        # XXX: most of this error conversion is VFS-related, and thus ought to
186
        # be in SmartServerVFSRequestHandler somewhere.
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
187
        try:
188
            return callable(*args, **kwargs)
189
        except errors.NoSuchFile, e:
190
            return SmartServerResponse(('NoSuchFile', e.path))
191
        except errors.FileExists, e:
192
            return SmartServerResponse(('FileExists', e.path))
193
        except errors.DirectoryNotEmpty, e:
194
            return SmartServerResponse(('DirectoryNotEmpty', e.path))
195
        except errors.ShortReadvError, e:
196
            return SmartServerResponse(('ShortReadvError',
197
                e.path, str(e.offset), str(e.length), str(e.actual)))
198
        except UnicodeError, e:
199
            # If it is a DecodeError, than most likely we are starting
200
            # with a plain string
201
            str_or_unicode = e.object
202
            if isinstance(str_or_unicode, unicode):
203
                # XXX: UTF-8 might have \x01 (our seperator byte) in it.  We
204
                # should escape it somehow.
205
                val = 'u:' + str_or_unicode.encode('utf-8')
206
            else:
207
                val = 's:' + str_or_unicode.encode('base64')
208
            # This handles UnicodeEncodeError or UnicodeDecodeError
209
            return SmartServerResponse((e.__class__.__name__,
210
                    e.encoding, val, str(e.start), str(e.end), e.reason))
211
        except errors.TransportNotPossible, e:
212
            if e.msg == "readonly transport":
213
                return SmartServerResponse(('ReadOnlyError', ))
214
            else:
215
                raise
216
217
2402.1.1 by Andrew Bennetts
Use the Command pattern for handling smart server commands.
218
class HelloRequest(SmartServerRequest):
219
    """Answer a version request with my version."""
220
221
    def do(self):
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
222
        return SmartServerResponse(('ok', '2'))
2402.1.1 by Andrew Bennetts
Use the Command pattern for handling smart server commands.
223
224
225
class GetBundleRequest(SmartServerRequest):
2402.1.2 by Andrew Bennetts
Deal with review comments.
226
    """Get a bundle of from the null revision to the specified revision."""
2402.1.1 by Andrew Bennetts
Use the Command pattern for handling smart server commands.
227
228
    def do(self, path, revision_id):
229
        # open transport relative to our base
230
        t = self._backing_transport.clone(path)
231
        control, extra_path = bzrdir.BzrDir.open_containing_from_transport(t)
232
        repo = control.open_repository()
233
        tmpf = tempfile.TemporaryFile()
234
        base_revision = revision.NULL_REVISION
235
        write_bundle(repo, revision_id, base_revision, tmpf)
236
        tmpf.seek(0)
237
        return SmartServerResponse((), tmpf.read())
238
239
240
class SmartServerIsReadonly(SmartServerRequest):
241
    # XXX: this request method belongs somewhere else.
242
243
    def do(self):
244
        if self._backing_transport.is_readonly():
245
            answer = 'yes'
246
        else:
247
            answer = 'no'
248
        return SmartServerResponse((answer,))
249
250
251
request_handlers = registry.Registry()
252
request_handlers.register_lazy(
253
    'append', 'bzrlib.smart.vfs', 'AppendRequest')
254
request_handlers.register_lazy(
255
    'delete', 'bzrlib.smart.vfs', 'DeleteRequest')
256
request_handlers.register_lazy(
257
    'get', 'bzrlib.smart.vfs', 'GetRequest')
258
request_handlers.register_lazy(
259
    'get_bundle', 'bzrlib.smart.request', 'GetBundleRequest')
260
request_handlers.register_lazy(
261
    'has', 'bzrlib.smart.vfs', 'HasRequest')
262
request_handlers.register_lazy(
263
    'hello', 'bzrlib.smart.request', 'HelloRequest')
264
request_handlers.register_lazy(
265
    'iter_files_recursive', 'bzrlib.smart.vfs', 'IterFilesRecursiveRequest')
266
request_handlers.register_lazy(
267
    'list_dir', 'bzrlib.smart.vfs', 'ListDirRequest')
268
request_handlers.register_lazy(
269
    'mkdir', 'bzrlib.smart.vfs', 'MkdirRequest')
270
request_handlers.register_lazy(
271
    'move', 'bzrlib.smart.vfs', 'MoveRequest')
272
request_handlers.register_lazy(
273
    'put', 'bzrlib.smart.vfs', 'PutRequest')
274
request_handlers.register_lazy(
275
    'put_non_atomic', 'bzrlib.smart.vfs', 'PutNonAtomicRequest')
276
request_handlers.register_lazy(
277
    'readv', 'bzrlib.smart.vfs', 'ReadvRequest')
278
request_handlers.register_lazy(
279
    'rename', 'bzrlib.smart.vfs', 'RenameRequest')
280
request_handlers.register_lazy(
281
    'rmdir', 'bzrlib.smart.vfs', 'RmdirRequest')
282
request_handlers.register_lazy(
283
    'stat', 'bzrlib.smart.vfs', 'StatRequest')