/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/message.py

  • Committer: Vincent Ladeuil
  • Date: 2012-01-18 14:09:19 UTC
  • mto: This revision was merged to the branch mainline in revision 6468.
  • Revision ID: v.ladeuil+lp@free.fr-20120118140919-rlvdrhpc0nq1lbwi
Change set/remove to require a lock for the branch config files.

This means that tests (or any plugin for that matter) do not requires an
explicit lock on the branch anymore to change a single option. This also
means the optimisation becomes "opt-in" and as such won't be as
spectacular as it may be and/or harder to get right (nothing fails
anymore).

This reduces the diff by ~300 lines.

Code/tests that were updating more than one config option is still taking
a lock to at least avoid some IOs and demonstrate the benefits through
the decreased number of hpss calls.

The duplication between BranchStack and BranchOnlyStack will be removed
once the same sharing is in place for local config files, at which point
the Stack class itself may be able to host the changes.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2008 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
16
 
 
17
from __future__ import absolute_import
 
18
 
 
19
import collections
 
20
from cStringIO import StringIO
 
21
 
 
22
from bzrlib import (
 
23
    debug,
 
24
    errors,
 
25
    )
 
26
from bzrlib.trace import mutter
 
27
 
 
28
 
 
29
class MessageHandler(object):
 
30
    """Base class for handling messages received via the smart protocol.
 
31
 
 
32
    As parts of a message are received, the corresponding PART_received method
 
33
    will be called.
 
34
    """
 
35
 
 
36
    def __init__(self):
 
37
        self.headers = None
 
38
 
 
39
    def headers_received(self, headers):
 
40
        """Called when message headers are received.
 
41
 
 
42
        This default implementation just stores them in self.headers.
 
43
        """
 
44
        self.headers = headers
 
45
 
 
46
    def byte_part_received(self, byte):
 
47
        """Called when a 'byte' part is received.
 
48
 
 
49
        Note that a 'byte' part is a message part consisting of exactly one
 
50
        byte.
 
51
        """
 
52
        raise NotImplementedError(self.byte_received)
 
53
 
 
54
    def bytes_part_received(self, bytes):
 
55
        """Called when a 'bytes' part is received.
 
56
 
 
57
        A 'bytes' message part can contain any number of bytes.  It should not
 
58
        be confused with a 'byte' part, which is always a single byte.
 
59
        """
 
60
        raise NotImplementedError(self.bytes_received)
 
61
 
 
62
    def structure_part_received(self, structure):
 
63
        """Called when a 'structure' part is received.
 
64
 
 
65
        :param structure: some structured data, which will be some combination
 
66
            of list, dict, int, and str objects.
 
67
        """
 
68
        raise NotImplementedError(self.bytes_received)
 
69
 
 
70
    def protocol_error(self, exception):
 
71
        """Called when there is a protocol decoding error.
 
72
 
 
73
        The default implementation just re-raises the exception.
 
74
        """
 
75
        raise
 
76
 
 
77
    def end_received(self):
 
78
        """Called when the end of the message is received."""
 
79
        # No-op by default.
 
80
        pass
 
81
 
 
82
 
 
83
class ConventionalRequestHandler(MessageHandler):
 
84
    """A message handler for "conventional" requests.
 
85
 
 
86
    "Conventional" is used in the sense described in
 
87
    doc/developers/network-protocol.txt: a simple message with arguments and an
 
88
    optional body.
 
89
 
 
90
    Possible states:
 
91
     * args: expecting args
 
92
     * body: expecting body (terminated by receiving a post-body status)
 
93
     * error: expecting post-body error
 
94
     * end: expecting end of message
 
95
     * nothing: finished
 
96
    """
 
97
 
 
98
    def __init__(self, request_handler, responder):
 
99
        MessageHandler.__init__(self)
 
100
        self.request_handler = request_handler
 
101
        self.responder = responder
 
102
        self.expecting = 'args'
 
103
        self._should_finish_body = False
 
104
        self._response_sent = False
 
105
 
 
106
    def protocol_error(self, exception):
 
107
        if self.responder.response_sent:
 
108
            # We can only send one response to a request, no matter how many
 
109
            # errors happen while processing it.
 
110
            return
 
111
        self.responder.send_error(exception)
 
112
 
 
113
    def byte_part_received(self, byte):
 
114
        if self.expecting == 'body':
 
115
            if byte == 'S':
 
116
                # Success.  Nothing more to come except the end of message.
 
117
                self.expecting = 'end'
 
118
            elif byte == 'E':
 
119
                # Error.  Expect an error structure.
 
120
                self.expecting = 'error'
 
121
            else:
 
122
                raise errors.SmartProtocolError(
 
123
                    'Non-success status byte in request body: %r' % (byte,))
 
124
        else:
 
125
            raise errors.SmartProtocolError(
 
126
                'Unexpected message part: byte(%r)' % (byte,))
 
127
 
 
128
    def structure_part_received(self, structure):
 
129
        if self.expecting == 'args':
 
130
            self._args_received(structure)
 
131
        elif self.expecting == 'error':
 
132
            self._error_received(structure)
 
133
        else:
 
134
            raise errors.SmartProtocolError(
 
135
                'Unexpected message part: structure(%r)' % (structure,))
 
136
 
 
137
    def _args_received(self, args):
 
138
        self.expecting = 'body'
 
139
        self.request_handler.args_received(args)
 
140
        if self.request_handler.finished_reading:
 
141
            self._response_sent = True
 
142
            self.responder.send_response(self.request_handler.response)
 
143
            self.expecting = 'end'
 
144
 
 
145
    def _error_received(self, error_args):
 
146
        self.expecting = 'end'
 
147
        self.request_handler.post_body_error_received(error_args)
 
148
 
 
149
    def bytes_part_received(self, bytes):
 
150
        if self.expecting == 'body':
 
151
            self._should_finish_body = True
 
152
            self.request_handler.accept_body(bytes)
 
153
        else:
 
154
            raise errors.SmartProtocolError(
 
155
                'Unexpected message part: bytes(%r)' % (bytes,))
 
156
 
 
157
    def end_received(self):
 
158
        if self.expecting not in ['body', 'end']:
 
159
            raise errors.SmartProtocolError(
 
160
                'End of message received prematurely (while expecting %s)'
 
161
                % (self.expecting,))
 
162
        self.expecting = 'nothing'
 
163
        self.request_handler.end_received()
 
164
        if not self.request_handler.finished_reading:
 
165
            raise errors.SmartProtocolError(
 
166
                "Complete conventional request was received, but request "
 
167
                "handler has not finished reading.")
 
168
        if not self._response_sent:
 
169
            self.responder.send_response(self.request_handler.response)
 
170
 
 
171
 
 
172
class ResponseHandler(object):
 
173
    """Abstract base class for an object that handles a smart response."""
 
174
 
 
175
    def read_response_tuple(self, expect_body=False):
 
176
        """Reads and returns the response tuple for the current request.
 
177
 
 
178
        :keyword expect_body: a boolean indicating if a body is expected in the
 
179
            response.  Some protocol versions needs this information to know
 
180
            when a response is finished.  If False, read_body_bytes should
 
181
            *not* be called afterwards.  Defaults to False.
 
182
        :returns: tuple of response arguments.
 
183
        """
 
184
        raise NotImplementedError(self.read_response_tuple)
 
185
 
 
186
    def read_body_bytes(self, count=-1):
 
187
        """Read and return some bytes from the body.
 
188
 
 
189
        :param count: if specified, read up to this many bytes.  By default,
 
190
            reads the entire body.
 
191
        :returns: str of bytes from the response body.
 
192
        """
 
193
        raise NotImplementedError(self.read_body_bytes)
 
194
 
 
195
    def read_streamed_body(self):
 
196
        """Returns an iterable that reads and returns a series of body chunks.
 
197
        """
 
198
        raise NotImplementedError(self.read_streamed_body)
 
199
 
 
200
    def cancel_read_body(self):
 
201
        """Stop expecting a body for this response.
 
202
 
 
203
        If expect_body was passed to read_response_tuple, this cancels that
 
204
        expectation (and thus finishes reading the response, allowing a new
 
205
        request to be issued).  This is useful if a response turns out to be an
 
206
        error rather than a normal result with a body.
 
207
        """
 
208
        raise NotImplementedError(self.cancel_read_body)
 
209
 
 
210
 
 
211
class ConventionalResponseHandler(MessageHandler, ResponseHandler):
 
212
 
 
213
    def __init__(self):
 
214
        MessageHandler.__init__(self)
 
215
        self.status = None
 
216
        self.args = None
 
217
        self._bytes_parts = collections.deque()
 
218
        self._body_started = False
 
219
        self._body_stream_status = None
 
220
        self._body = None
 
221
        self._body_error_args = None
 
222
        self.finished_reading = False
 
223
 
 
224
    def setProtoAndMediumRequest(self, protocol_decoder, medium_request):
 
225
        self._protocol_decoder = protocol_decoder
 
226
        self._medium_request = medium_request
 
227
 
 
228
    def byte_part_received(self, byte):
 
229
        if byte not in ['E', 'S']:
 
230
            raise errors.SmartProtocolError(
 
231
                'Unknown response status: %r' % (byte,))
 
232
        if self._body_started:
 
233
            if self._body_stream_status is not None:
 
234
                raise errors.SmartProtocolError(
 
235
                    'Unexpected byte part received: %r' % (byte,))
 
236
            self._body_stream_status = byte
 
237
        else:
 
238
            if self.status is not None:
 
239
                raise errors.SmartProtocolError(
 
240
                    'Unexpected byte part received: %r' % (byte,))
 
241
            self.status = byte
 
242
 
 
243
    def bytes_part_received(self, bytes):
 
244
        self._body_started = True
 
245
        self._bytes_parts.append(bytes)
 
246
 
 
247
    def structure_part_received(self, structure):
 
248
        if type(structure) is not tuple:
 
249
            raise errors.SmartProtocolError(
 
250
                'Args structure is not a sequence: %r' % (structure,))
 
251
        if not self._body_started:
 
252
            if self.args is not None:
 
253
                raise errors.SmartProtocolError(
 
254
                    'Unexpected structure received: %r (already got %r)'
 
255
                    % (structure, self.args))
 
256
            self.args = structure
 
257
        else:
 
258
            if self._body_stream_status != 'E':
 
259
                raise errors.SmartProtocolError(
 
260
                    'Unexpected structure received after body: %r'
 
261
                    % (structure,))
 
262
            self._body_error_args = structure
 
263
 
 
264
    def _wait_for_response_args(self):
 
265
        while self.args is None and not self.finished_reading:
 
266
            self._read_more()
 
267
 
 
268
    def _wait_for_response_end(self):
 
269
        while not self.finished_reading:
 
270
            self._read_more()
 
271
 
 
272
    def _read_more(self):
 
273
        next_read_size = self._protocol_decoder.next_read_size()
 
274
        if next_read_size == 0:
 
275
            # a complete request has been read.
 
276
            self.finished_reading = True
 
277
            self._medium_request.finished_reading()
 
278
            return
 
279
        bytes = self._medium_request.read_bytes(next_read_size)
 
280
        if bytes == '':
 
281
            # end of file encountered reading from server
 
282
            if 'hpss' in debug.debug_flags:
 
283
                mutter(
 
284
                    'decoder state: buf[:10]=%r, state_accept=%s',
 
285
                    self._protocol_decoder._get_in_buffer()[:10],
 
286
                    self._protocol_decoder.state_accept.__name__)
 
287
            raise errors.ConnectionReset(
 
288
                "Unexpected end of message. "
 
289
                "Please check connectivity and permissions, and report a bug "
 
290
                "if problems persist.")
 
291
        self._protocol_decoder.accept_bytes(bytes)
 
292
 
 
293
    def protocol_error(self, exception):
 
294
        # Whatever the error is, we're done with this request.
 
295
        self.finished_reading = True
 
296
        self._medium_request.finished_reading()
 
297
        raise
 
298
 
 
299
    def read_response_tuple(self, expect_body=False):
 
300
        """Read a response tuple from the wire."""
 
301
        self._wait_for_response_args()
 
302
        if not expect_body:
 
303
            self._wait_for_response_end()
 
304
        if 'hpss' in debug.debug_flags:
 
305
            mutter('   result:   %r', self.args)
 
306
        if self.status == 'E':
 
307
            self._wait_for_response_end()
 
308
            _raise_smart_server_error(self.args)
 
309
        return tuple(self.args)
 
310
 
 
311
    def read_body_bytes(self, count=-1):
 
312
        """Read bytes from the body, decoding into a byte stream.
 
313
 
 
314
        We read all bytes at once to ensure we've checked the trailer for
 
315
        errors, and then feed the buffer back as read_body_bytes is called.
 
316
 
 
317
        Like the builtin file.read in Python, a count of -1 (the default) means
 
318
        read the entire body.
 
319
        """
 
320
        # TODO: we don't necessarily need to buffer the full request if count
 
321
        # != -1.  (2008/04/30, Andrew Bennetts)
 
322
        if self._body is None:
 
323
            self._wait_for_response_end()
 
324
            body_bytes = ''.join(self._bytes_parts)
 
325
            if 'hpss' in debug.debug_flags:
 
326
                mutter('              %d body bytes read', len(body_bytes))
 
327
            self._body = StringIO(body_bytes)
 
328
            self._bytes_parts = None
 
329
        return self._body.read(count)
 
330
 
 
331
    def read_streamed_body(self):
 
332
        while not self.finished_reading:
 
333
            while self._bytes_parts:
 
334
                bytes_part = self._bytes_parts.popleft()
 
335
                if 'hpssdetail' in debug.debug_flags:
 
336
                    mutter('              %d byte part read', len(bytes_part))
 
337
                yield bytes_part
 
338
            self._read_more()
 
339
        if self._body_stream_status == 'E':
 
340
            _raise_smart_server_error(self._body_error_args)
 
341
 
 
342
    def cancel_read_body(self):
 
343
        self._wait_for_response_end()
 
344
 
 
345
 
 
346
def _raise_smart_server_error(error_tuple):
 
347
    """Raise exception based on tuple received from smart server
 
348
 
 
349
    Specific error translation is handled by bzrlib.remote._translate_error
 
350
    """
 
351
    if error_tuple[0] == 'UnknownMethod':
 
352
        raise errors.UnknownSmartMethod(error_tuple[1])
 
353
    raise errors.ErrorFromSmartServer(error_tuple)