~bzr-pqm/bzr/bzr.dev

3245.4.42 by Andrew Bennetts
Make _SmartClient automatically detect and use the highest protocol version compatible with the server.
1
# Copyright (C) 2006-2008 Canonical Ltd
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.
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
4183.7.1 by Sabin Iacob
update FSF mailing address
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
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.
16
3245.4.42 by Andrew Bennetts
Make _SmartClient automatically detect and use the highest protocol version compatible with the server.
17
import bzrlib
3195.3.17 by Andrew Bennetts
Some tests now passing using protocol 3.
18
from bzrlib.smart import message, protocol
3245.4.59 by Andrew Bennetts
Various tweaks in response to Martin's review.
19
from bzrlib.trace import warning
3703.3.3 by Andrew Bennetts
Neater effort tests for push by using a _SmartClient.hooks['call'] hook.
20
from bzrlib import (
21
    errors,
22
    hooks,
23
    )
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.
24
25
2414.1.4 by Andrew Bennetts
Rename SmartClient to _SmartClient.
26
class _SmartClient(object):
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.
27
3431.3.2 by Andrew Bennetts
Remove 'base' from _SmartClient entirely, now that the medium has it.
28
    def __init__(self, medium, headers=None):
3104.4.4 by Andrew Bennetts
Rename parameter to _SmartClient.__init__ to be less confusing.
29
        """Constructor.
30
3313.2.1 by Andrew Bennetts
Change _SmartClient's API to accept a medium and a base, rather than a _SharedConnection.
31
        :param medium: a SmartClientMedium
3104.4.4 by Andrew Bennetts
Rename parameter to _SmartClient.__init__ to be less confusing.
32
        """
3313.2.1 by Andrew Bennetts
Change _SmartClient's API to accept a medium and a base, rather than a _SharedConnection.
33
        self._medium = medium
3245.4.42 by Andrew Bennetts
Make _SmartClient automatically detect and use the highest protocol version compatible with the server.
34
        if headers is None:
35
            self._headers = {'Software version': bzrlib.__version__}
36
        else:
37
            self._headers = dict(headers)
38
4964.2.2 by Martin Pool
Add SmartClient repr
39
    def __repr__(self):
40
        return '%s(%r)' % (self.__class__.__name__, self._medium)
41
3245.4.46 by Andrew Bennetts
Apply John's review comments.
42
    def _send_request(self, protocol_version, method, args, body=None,
3842.3.4 by Andrew Bennetts
TestStacking.test_fetch_copies_from_stacked_on now passes using the VersionedFile.insert_record_stream RPC; lots of debugging cruft needs removal though.
43
                      readv_body=None, body_stream=None):
3245.4.46 by Andrew Bennetts
Apply John's review comments.
44
        encoder, response_handler = self._construct_protocol(
45
            protocol_version)
3245.4.42 by Andrew Bennetts
Make _SmartClient automatically detect and use the highest protocol version compatible with the server.
46
        encoder.set_headers(self._headers)
47
        if body is not None:
3245.4.46 by Andrew Bennetts
Apply John's review comments.
48
            if readv_body is not None:
49
                raise AssertionError(
50
                    "body and readv_body are mutually exclusive.")
3842.3.4 by Andrew Bennetts
TestStacking.test_fetch_copies_from_stacked_on now passes using the VersionedFile.insert_record_stream RPC; lots of debugging cruft needs removal though.
51
            if body_stream is not None:
52
                raise AssertionError(
53
                    "body and body_stream are mutually exclusive.")
3245.4.42 by Andrew Bennetts
Make _SmartClient automatically detect and use the highest protocol version compatible with the server.
54
            encoder.call_with_body_bytes((method, ) + args, body)
55
        elif readv_body is not None:
3842.3.4 by Andrew Bennetts
TestStacking.test_fetch_copies_from_stacked_on now passes using the VersionedFile.insert_record_stream RPC; lots of debugging cruft needs removal though.
56
            if body_stream is not None:
57
                raise AssertionError(
58
                    "readv_body and body_stream are mutually exclusive.")
59
            encoder.call_with_body_readv_array((method, ) + args, readv_body)
60
        elif body_stream is not None:
61
            encoder.call_with_body_stream((method, ) + args, body_stream)
3245.4.42 by Andrew Bennetts
Make _SmartClient automatically detect and use the highest protocol version compatible with the server.
62
        else:
63
            encoder.call(method, *args)
3245.4.46 by Andrew Bennetts
Apply John's review comments.
64
        return response_handler
3245.4.42 by Andrew Bennetts
Make _SmartClient automatically detect and use the highest protocol version compatible with the server.
65
3703.3.3 by Andrew Bennetts
Neater effort tests for push by using a _SmartClient.hooks['call'] hook.
66
    def _run_call_hooks(self, method, args, body, readv_body):
3703.3.4 by Andrew Bennetts
Move check to skip _SmartClient hook invocation.
67
        if not _SmartClient.hooks['call']:
68
            return
3731.2.1 by Andrew Bennetts
Show total HPSS calls (if any) on stderr when -Dhpss is active.
69
        params = CallHookParams(method, args, body, readv_body, self._medium)
3703.3.3 by Andrew Bennetts
Neater effort tests for push by using a _SmartClient.hooks['call'] hook.
70
        for hook in _SmartClient.hooks['call']:
71
            hook(params)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
72
3245.4.42 by Andrew Bennetts
Make _SmartClient automatically detect and use the highest protocol version compatible with the server.
73
    def _call_and_read_response(self, method, args, body=None, readv_body=None,
3842.3.4 by Andrew Bennetts
TestStacking.test_fetch_copies_from_stacked_on now passes using the VersionedFile.insert_record_stream RPC; lots of debugging cruft needs removal though.
74
            body_stream=None, expect_response_body=True):
3703.3.4 by Andrew Bennetts
Move check to skip _SmartClient hook invocation.
75
        self._run_call_hooks(method, args, body, readv_body)
3245.4.47 by Andrew Bennetts
Don't automatically send 'hello' requests from RemoteBzrDirFormat.probe_transport unless we have to (i.e. the transport is HTTP).
76
        if self._medium._protocol_version is not None:
3245.4.46 by Andrew Bennetts
Apply John's review comments.
77
            response_handler = self._send_request(
3245.4.47 by Andrew Bennetts
Don't automatically send 'hello' requests from RemoteBzrDirFormat.probe_transport unless we have to (i.e. the transport is HTTP).
78
                self._medium._protocol_version, method, args, body=body,
3842.3.4 by Andrew Bennetts
TestStacking.test_fetch_copies_from_stacked_on now passes using the VersionedFile.insert_record_stream RPC; lots of debugging cruft needs removal though.
79
                readv_body=readv_body, body_stream=body_stream)
3245.4.42 by Andrew Bennetts
Make _SmartClient automatically detect and use the highest protocol version compatible with the server.
80
            return (response_handler.read_response_tuple(
81
                        expect_body=expect_response_body),
82
                    response_handler)
83
        else:
3245.4.43 by Andrew Bennetts
Improve tests for automatic detection of protocol version.
84
            for protocol_version in [3, 2]:
3453.4.4 by Andrew Bennetts
Small optimisation: don't bother trying RPCs from >= 1.6 if the server doesn't support protocol v3.
85
                if protocol_version == 2:
86
                    # If v3 doesn't work, the remote side is older than 1.6.
3453.4.9 by Andrew Bennetts
Rename _remote_is_not to _remember_remote_is_before.
87
                    self._medium._remember_remote_is_before((1, 6))
3245.4.46 by Andrew Bennetts
Apply John's review comments.
88
                response_handler = self._send_request(
89
                    protocol_version, method, args, body=body,
3842.3.4 by Andrew Bennetts
TestStacking.test_fetch_copies_from_stacked_on now passes using the VersionedFile.insert_record_stream RPC; lots of debugging cruft needs removal though.
90
                    readv_body=readv_body, body_stream=body_stream)
3245.4.42 by Andrew Bennetts
Make _SmartClient automatically detect and use the highest protocol version compatible with the server.
91
                try:
92
                    response_tuple = response_handler.read_response_tuple(
93
                        expect_body=expect_response_body)
3245.4.43 by Andrew Bennetts
Improve tests for automatic detection of protocol version.
94
                except errors.UnexpectedProtocolVersionMarker, err:
3245.4.42 by Andrew Bennetts
Make _SmartClient automatically detect and use the highest protocol version compatible with the server.
95
                    # TODO: We could recover from this without disconnecting if
96
                    # we recognise the protocol version.
3245.4.59 by Andrew Bennetts
Various tweaks in response to Martin's review.
97
                    warning(
98
                        'Server does not understand Bazaar network protocol %d,'
3245.4.62 by Andrew Bennetts
Fix unsubstituted '%d' in reconnection warning.
99
                        ' reconnecting.  (Upgrade the server to avoid this.)'
100
                        % (protocol_version,))
3245.4.42 by Andrew Bennetts
Make _SmartClient automatically detect and use the highest protocol version compatible with the server.
101
                    self._medium.disconnect()
102
                    continue
3461.2.1 by Andrew Bennetts
Avoid unnecessary reconnections to old servers when the first HPSS is an error in the right protocol version.
103
                except errors.ErrorFromSmartServer:
104
                    # If we received an error reply from the server, then it
105
                    # must be ok with this protocol version.
106
                    self._medium._protocol_version = protocol_version
107
                    raise
3245.4.42 by Andrew Bennetts
Make _SmartClient automatically detect and use the highest protocol version compatible with the server.
108
                else:
3245.4.47 by Andrew Bennetts
Don't automatically send 'hello' requests from RemoteBzrDirFormat.probe_transport unless we have to (i.e. the transport is HTTP).
109
                    self._medium._protocol_version = protocol_version
3245.4.42 by Andrew Bennetts
Make _SmartClient automatically detect and use the highest protocol version compatible with the server.
110
                    return response_tuple, response_handler
3245.4.44 by Andrew Bennetts
Remove auto-detection of protocol v1; it's complex and there's a high risk of false positives. Also remove unused mock object.
111
            raise errors.SmartProtocolError(
112
                'Server is not a Bazaar server: ' + str(err))
3245.4.42 by Andrew Bennetts
Make _SmartClient automatically detect and use the highest protocol version compatible with the server.
113
114
    def _construct_protocol(self, version):
3241.2.2 by Andrew Bennetts
Merge from bzr.dev.
115
        request = self._medium.get_request()
3245.4.1 by Andrew Bennetts
Merge in the protocol-v3 implementation so far, integrating with the protocol negotiation in bzrlib.smart.client.
116
        if version == 3:
117
            request_encoder = protocol.ProtocolThreeRequester(request)
118
            response_handler = message.ConventionalResponseHandler()
3245.4.42 by Andrew Bennetts
Make _SmartClient automatically detect and use the highest protocol version compatible with the server.
119
            response_proto = protocol.ProtocolThreeDecoder(
120
                response_handler, expect_version_marker=True)
3245.4.26 by Andrew Bennetts
Rename 'setProtoAndMedium' to more accurate 'setProtoAndMediumRequest', add ABCs for Requesters and ResponseHandlers.
121
            response_handler.setProtoAndMediumRequest(response_proto, request)
3245.4.1 by Andrew Bennetts
Merge in the protocol-v3 implementation so far, integrating with the protocol negotiation in bzrlib.smart.client.
122
        elif version == 2:
123
            request_encoder = protocol.SmartClientRequestProtocolTwo(request)
124
            response_handler = request_encoder
3241.2.1 by Andrew Bennetts
Dynamically choose the best client protocol version in bzrlib.smart.client.
125
        else:
3245.4.1 by Andrew Bennetts
Merge in the protocol-v3 implementation so far, integrating with the protocol negotiation in bzrlib.smart.client.
126
            request_encoder = protocol.SmartClientRequestProtocolOne(request)
127
            response_handler = request_encoder
128
        return request_encoder, response_handler
3241.2.1 by Andrew Bennetts
Dynamically choose the best client protocol version in bzrlib.smart.client.
129
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.
130
    def call(self, method, *args):
131
        """Call a method on the remote server."""
2414.1.2 by Andrew Bennetts
Deal with review comments.
132
        result, protocol = self.call_expecting_body(method, *args)
2414.1.1 by Andrew Bennetts
Add some unicode-related tests from the hpss branch, and a few other nits (also from the hpss branch).
133
        protocol.cancel_read_body()
134
        return result
135
2414.1.2 by Andrew Bennetts
Deal with review comments.
136
    def call_expecting_body(self, method, *args):
137
        """Call a method and return the result and the protocol object.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
138
2414.1.2 by Andrew Bennetts
Deal with review comments.
139
        The body can be read like so::
140
141
            result, smart_protocol = smart_client.call_expecting_body(...)
142
            body = smart_protocol.read_body_bytes()
143
        """
3245.4.42 by Andrew Bennetts
Make _SmartClient automatically detect and use the highest protocol version compatible with the server.
144
        return self._call_and_read_response(
145
            method, args, expect_response_body=True)
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.
146
147
    def call_with_body_bytes(self, method, args, body):
148
        """Call a method on the remote server with body bytes."""
2018.5.131 by Andrew Bennetts
Be strict about unicode passed to transport.put_{bytes,file} and SmartClient.call_with_body_bytes, fixing part of TestLockableFiles_RemoteLockDir.test_read_write.
149
        if type(method) is not str:
150
            raise TypeError('method must be a byte string, not %r' % (method,))
151
        for arg in args:
152
            if type(arg) is not str:
153
                raise TypeError('args must be byte strings, not %r' % (args,))
154
        if type(body) is not str:
155
            raise TypeError('body must be byte string, not %r' % (body,))
3245.4.42 by Andrew Bennetts
Make _SmartClient automatically detect and use the highest protocol version compatible with the server.
156
        response, response_handler = self._call_and_read_response(
157
            method, args, body=body, expect_response_body=False)
158
        return response
2414.1.1 by Andrew Bennetts
Add some unicode-related tests from the hpss branch, and a few other nits (also from the hpss branch).
159
3184.1.10 by Robert Collins
Change the smart server verb for Repository.stream_revisions_chunked to use SearchResults as the request mechanism for downloads.
160
    def call_with_body_bytes_expecting_body(self, method, args, body):
161
        """Call a method on the remote server with body bytes."""
162
        if type(method) is not str:
163
            raise TypeError('method must be a byte string, not %r' % (method,))
3842.3.2 by Andrew Bennetts
Revert the RemoteVersionedFiles.get_parent_map implementation, leaving just the skeleton of RemoteVersionedFiles.
164
        for arg in args:
165
            if type(arg) is not str:
166
                raise TypeError('args must be byte strings, not %r' % (args,))
3184.1.10 by Robert Collins
Change the smart server verb for Repository.stream_revisions_chunked to use SearchResults as the request mechanism for downloads.
167
        if type(body) is not str:
168
            raise TypeError('body must be byte string, not %r' % (body,))
3245.4.42 by Andrew Bennetts
Make _SmartClient automatically detect and use the highest protocol version compatible with the server.
169
        response, response_handler = self._call_and_read_response(
170
            method, args, body=body, expect_response_body=True)
171
        return (response, response_handler)
3184.1.10 by Robert Collins
Change the smart server verb for Repository.stream_revisions_chunked to use SearchResults as the request mechanism for downloads.
172
3245.4.24 by Andrew Bennetts
Consistently raise errors from the server as ErrorFromSmartServer exceptions.
173
    def call_with_body_readv_array(self, args, body):
3245.4.42 by Andrew Bennetts
Make _SmartClient automatically detect and use the highest protocol version compatible with the server.
174
        response, response_handler = self._call_and_read_response(
175
                args[0], args[1:], readv_body=body, expect_response_body=True)
176
        return (response, response_handler)
3245.4.24 by Andrew Bennetts
Consistently raise errors from the server as ErrorFromSmartServer exceptions.
177
3842.3.4 by Andrew Bennetts
TestStacking.test_fetch_copies_from_stacked_on now passes using the VersionedFile.insert_record_stream RPC; lots of debugging cruft needs removal though.
178
    def call_with_body_stream(self, args, stream):
179
        response, response_handler = self._call_and_read_response(
180
                args[0], args[1:], body_stream=stream,
181
                expect_response_body=False)
182
        return (response, response_handler)
183
2414.1.1 by Andrew Bennetts
Add some unicode-related tests from the hpss branch, and a few other nits (also from the hpss branch).
184
    def remote_path_from_transport(self, transport):
2414.1.2 by Andrew Bennetts
Deal with review comments.
185
        """Convert transport into a path suitable for using in a request.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
186
2414.1.2 by Andrew Bennetts
Deal with review comments.
187
        Note that the resulting remote path doesn't encode the host name or
188
        anything but path, so it is only safe to use it in requests sent over
189
        the medium from the matching transport.
190
        """
3431.3.11 by Andrew Bennetts
Push remote_path_from_transport logic into SmartClientMedium, removing special-casing of bzr+http from _SmartClient.
191
        return self._medium.remote_path_from_transport(transport)
3297.3.1 by Andrew Bennetts
Raise UnknownSmartMethod automatically from read_response_tuple.
192
3703.3.3 by Andrew Bennetts
Neater effort tests for push by using a _SmartClient.hooks['call'] hook.
193
194
class SmartClientHooks(hooks.Hooks):
195
196
    def __init__(self):
197
        hooks.Hooks.__init__(self)
4119.3.2 by Robert Collins
Migrate existing hooks over to the new HookPoint infrastructure.
198
        self.create_hook(hooks.HookPoint('call',
199
            "Called when the smart client is submitting a request to the "
200
            "smart server. Called with a bzrlib.smart.client.CallHookParams "
201
            "object. Streaming request bodies, and responses, are not "
202
            "accessible.", None, None))
3703.3.3 by Andrew Bennetts
Neater effort tests for push by using a _SmartClient.hooks['call'] hook.
203
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
204
3703.3.3 by Andrew Bennetts
Neater effort tests for push by using a _SmartClient.hooks['call'] hook.
205
_SmartClient.hooks = SmartClientHooks()
206
207
208
class CallHookParams(object):
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
209
3731.2.1 by Andrew Bennetts
Show total HPSS calls (if any) on stderr when -Dhpss is active.
210
    def __init__(self, method, args, body, readv_body, medium):
3703.3.3 by Andrew Bennetts
Neater effort tests for push by using a _SmartClient.hooks['call'] hook.
211
        self.method = method
212
        self.args = args
213
        self.body = body
214
        self.readv_body = readv_body
3731.2.1 by Andrew Bennetts
Show total HPSS calls (if any) on stderr when -Dhpss is active.
215
        self.medium = medium
3703.3.3 by Andrew Bennetts
Neater effort tests for push by using a _SmartClient.hooks['call'] hook.
216
217
    def __repr__(self):
218
        attrs = dict((k, v) for (k, v) in self.__dict__.iteritems()
219
                     if v is not None)
220
        return '<%s %r>' % (self.__class__.__name__, attrs)
221
222
    def __eq__(self, other):
223
        if type(other) is not type(self):
224
            return NotImplemented
225
        return self.__dict__ == other.__dict__
226
227
    def __ne__(self, other):
228
        return not self == other