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