62
34
def call_expecting_body(self, method, *args):
63
35
"""Call a method and return the result and the protocol object.
65
37
The body can be read like so::
67
39
result, smart_protocol = smart_client.call_expecting_body(...)
68
40
body = smart_protocol.read_body_bytes()
70
return self._call_and_read_response(
71
method, args, expect_response_body=True)
42
request = self._medium.get_request()
43
smart_protocol = protocol.SmartClientRequestProtocolOne(request)
44
smart_protocol.call(method, *args)
45
return smart_protocol.read_response_tuple(expect_body=True), smart_protocol
73
47
def call_with_body_bytes(self, method, args, body):
74
48
"""Call a method on the remote server with body bytes."""
75
if not isinstance(method, bytes):
76
raise TypeError('method must be a byte string, not %r' % (method,))
78
if not isinstance(arg, bytes):
79
raise TypeError('args must be byte strings, not %r' % (args,))
80
if not isinstance(body, bytes):
81
raise TypeError('body must be byte string, not %r' % (body,))
82
response, response_handler = self._call_and_read_response(
83
method, args, body=body, expect_response_body=False)
86
def call_with_body_bytes_expecting_body(self, method, args, body):
87
"""Call a method on the remote server with body bytes."""
88
if not isinstance(method, bytes):
89
raise TypeError('method must be a byte string, not %r' % (method,))
91
if not isinstance(arg, bytes):
92
raise TypeError('args must be byte strings, not %r' % (args,))
93
if not isinstance(body, bytes):
94
raise TypeError('body must be byte string, not %r' % (body,))
95
response, response_handler = self._call_and_read_response(
96
method, args, body=body, expect_response_body=True)
97
return (response, response_handler)
99
def call_with_body_readv_array(self, args, body):
100
response, response_handler = self._call_and_read_response(
101
args[0], args[1:], readv_body=body, expect_response_body=True)
102
return (response, response_handler)
104
def call_with_body_stream(self, args, stream):
105
response, response_handler = self._call_and_read_response(
106
args[0], args[1:], body_stream=stream,
107
expect_response_body=False)
108
return (response, response_handler)
49
if type(method) is not str:
50
raise TypeError('method must be a byte string, not %r' % (method,))
52
if type(arg) is not str:
53
raise TypeError('args must be byte strings, not %r' % (args,))
54
if type(body) is not str:
55
raise TypeError('body must be byte string, not %r' % (body,))
56
request = self._medium.get_request()
57
smart_protocol = protocol.SmartClientRequestProtocolOne(request)
58
smart_protocol.call_with_body_bytes((method, ) + args, body)
59
return smart_protocol.read_response_tuple()
110
61
def remote_path_from_transport(self, transport):
111
62
"""Convert transport into a path suitable for using in a request.
113
64
Note that the resulting remote path doesn't encode the host name or
114
65
anything but path, so it is only safe to use it in requests sent over
115
66
the medium from the matching transport.
117
return self._medium.remote_path_from_transport(transport).encode('utf-8')
120
class _SmartClientRequest(object):
121
"""Encapsulate the logic for a single request.
123
This class handles things like reconnecting and sending the request a
124
second time when the connection is reset in the middle. It also handles the
125
multiple requests that get made if we don't know what protocol the server
128
Generally, you build up one of these objects, passing in the arguments that
129
you want to send to the server, and then use 'call_and_read_response' to
130
get the response from the server.
133
def __init__(self, client, method, args, body=None, readv_body=None,
134
body_stream=None, expect_response_body=True):
139
self.readv_body = readv_body
140
self.body_stream = body_stream
141
self.expect_response_body = expect_response_body
143
def call_and_read_response(self):
144
"""Send the request to the server, and read the initial response.
146
This doesn't read all of the body content of the response, instead it
147
returns (response_tuple, response_handler). response_tuple is the 'ok',
148
or 'error' information, and 'response_handler' can be used to get the
151
self._run_call_hooks()
152
protocol_version = self.client._medium._protocol_version
153
if protocol_version is None:
154
return self._call_determining_protocol_version()
156
return self._call(protocol_version)
158
def _is_safe_to_send_twice(self):
159
"""Check if the current method is re-entrant safe."""
160
if self.body_stream is not None or 'noretry' in debug.debug_flags:
161
# We can't restart a body stream that has already been consumed.
163
request_type = _mod_request.request_handlers.get_info(self.method)
164
if request_type in ('read', 'idem', 'semi'):
166
# If we have gotten this far, 'stream' cannot be retried, because we
167
# already consumed the local stream.
168
if request_type in ('semivfs', 'mutate', 'stream'):
170
trace.mutter('Unknown request type: %s for method %s'
171
% (request_type, self.method))
174
def _run_call_hooks(self):
175
if not _SmartClient.hooks['call']:
177
params = CallHookParams(self.method, self.args, self.body,
178
self.readv_body, self.client._medium)
179
for hook in _SmartClient.hooks['call']:
182
def _call(self, protocol_version):
183
"""We know the protocol version.
185
So this just sends the request, and then reads the response. This is
186
where the code will be to retry requests if the connection is closed.
188
response_handler = self._send(protocol_version)
190
response_tuple = response_handler.read_response_tuple(
191
expect_body=self.expect_response_body)
192
except errors.ConnectionReset as e:
193
self.client._medium.reset()
194
if not self._is_safe_to_send_twice():
196
trace.warning('ConnectionReset reading response for %r, retrying'
198
trace.log_exception_quietly()
199
encoder, response_handler = self._construct_protocol(
201
self._send_no_retry(encoder)
202
response_tuple = response_handler.read_response_tuple(
203
expect_body=self.expect_response_body)
204
return (response_tuple, response_handler)
206
def _call_determining_protocol_version(self):
207
"""Determine what protocol the remote server supports.
209
We do this by placing a request in the most recent protocol, and
210
handling the UnexpectedProtocolVersionMarker from the server.
213
for protocol_version in [3, 2]:
214
if protocol_version == 2:
215
# If v3 doesn't work, the remote side is older than 1.6.
216
self.client._medium._remember_remote_is_before((1, 6))
218
response_tuple, response_handler = self._call(protocol_version)
219
except errors.UnexpectedProtocolVersionMarker as err:
220
# TODO: We could recover from this without disconnecting if
221
# we recognise the protocol version.
223
'Server does not understand Bazaar network protocol %d,'
224
' reconnecting. (Upgrade the server to avoid this.)'
225
% (protocol_version,))
226
self.client._medium.disconnect()
229
except errors.ErrorFromSmartServer:
230
# If we received an error reply from the server, then it
231
# must be ok with this protocol version.
232
self.client._medium._protocol_version = protocol_version
235
self.client._medium._protocol_version = protocol_version
236
return response_tuple, response_handler
237
raise errors.SmartProtocolError(
238
'Server is not a Bazaar server: ' + str(last_err))
240
def _construct_protocol(self, version):
241
"""Build the encoding stack for a given protocol version."""
242
request = self.client._medium.get_request()
244
request_encoder = protocol.ProtocolThreeRequester(request)
245
response_handler = message.ConventionalResponseHandler()
246
response_proto = protocol.ProtocolThreeDecoder(
247
response_handler, expect_version_marker=True)
248
response_handler.setProtoAndMediumRequest(response_proto, request)
250
request_encoder = protocol.SmartClientRequestProtocolTwo(request)
251
response_handler = request_encoder
253
request_encoder = protocol.SmartClientRequestProtocolOne(request)
254
response_handler = request_encoder
255
return request_encoder, response_handler
257
def _send(self, protocol_version):
258
"""Encode the request, and send it to the server.
260
This will retry a request if we get a ConnectionReset while sending the
261
request to the server. (Unless we have a body_stream that we have
262
already started consuming, since we can't restart body_streams)
264
:return: response_handler as defined by _construct_protocol
266
encoder, response_handler = self._construct_protocol(protocol_version)
268
self._send_no_retry(encoder)
269
except errors.ConnectionReset as e:
270
# If we fail during the _send_no_retry phase, then we can
271
# be confident that the server did not get our request, because we
272
# haven't started waiting for the reply yet. So try the request
273
# again. We only issue a single retry, because if the connection
274
# really is down, there is no reason to loop endlessly.
276
# Connection is dead, so close our end of it.
277
self.client._medium.reset()
278
if (('noretry' in debug.debug_flags) or
279
(self.body_stream is not None and
280
encoder.body_stream_started)):
281
# We can't restart a body_stream that has been partially
282
# consumed, so we don't retry.
283
# Note: We don't have to worry about
284
# SmartClientRequestProtocolOne or Two, because they don't
285
# support client-side body streams.
287
trace.warning('ConnectionReset calling %r, retrying'
289
trace.log_exception_quietly()
290
encoder, response_handler = self._construct_protocol(
292
self._send_no_retry(encoder)
293
return response_handler
295
def _send_no_retry(self, encoder):
296
"""Just encode the request and try to send it."""
297
encoder.set_headers(self.client._headers)
298
if self.body is not None:
299
if self.readv_body is not None:
300
raise AssertionError(
301
"body and readv_body are mutually exclusive.")
302
if self.body_stream is not None:
303
raise AssertionError(
304
"body and body_stream are mutually exclusive.")
305
encoder.call_with_body_bytes(
306
(self.method, ) + self.args, self.body)
307
elif self.readv_body is not None:
308
if self.body_stream is not None:
309
raise AssertionError(
310
"readv_body and body_stream are mutually exclusive.")
311
encoder.call_with_body_readv_array((self.method, ) + self.args,
313
elif self.body_stream is not None:
314
encoder.call_with_body_stream((self.method, ) + self.args,
317
encoder.call(self.method, *self.args)
320
class SmartClientHooks(hooks.Hooks):
323
hooks.Hooks.__init__(
324
self, "breezy.bzr.smart.client", "_SmartClient.hooks")
325
self.add_hook('call',
326
"Called when the smart client is submitting a request to the "
327
"smart server. Called with a breezy.bzr.smart.client.CallHookParams "
328
"object. Streaming request bodies, and responses, are not "
332
_SmartClient.hooks = SmartClientHooks()
335
class CallHookParams(object):
337
def __init__(self, method, args, body, readv_body, medium):
341
self.readv_body = readv_body
345
attrs = dict((k, v) for k, v in self.__dict__.items()
347
return '<%s %r>' % (self.__class__.__name__, attrs)
349
def __eq__(self, other):
350
if not isinstance(other, type(self)):
351
return NotImplemented
352
return self.__dict__ == other.__dict__
354
def __ne__(self, other):
355
return not self == other
68
return unescape(urlparse(transport.base)[2]).encode('utf8')