~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
3245.4.46 by Andrew Bennetts
Apply John's review comments.
39
    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.
40
                      readv_body=None, body_stream=None):
3245.4.46 by Andrew Bennetts
Apply John's review comments.
41
        encoder, response_handler = self._construct_protocol(
42
            protocol_version)
3245.4.42 by Andrew Bennetts
Make _SmartClient automatically detect and use the highest protocol version compatible with the server.
43
        encoder.set_headers(self._headers)
44
        if body is not None:
3245.4.46 by Andrew Bennetts
Apply John's review comments.
45
            if readv_body is not None:
46
                raise AssertionError(
47
                    "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.
48
            if body_stream is not None:
49
                raise AssertionError(
50
                    "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.
51
            encoder.call_with_body_bytes((method, ) + args, body)
52
        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.
53
            if body_stream is not None:
54
                raise AssertionError(
55
                    "readv_body and body_stream are mutually exclusive.")
56
            encoder.call_with_body_readv_array((method, ) + args, readv_body)
57
        elif body_stream is not None:
58
            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.
59
        else:
60
            encoder.call(method, *args)
3245.4.46 by Andrew Bennetts
Apply John's review comments.
61
        return response_handler
3245.4.42 by Andrew Bennetts
Make _SmartClient automatically detect and use the highest protocol version compatible with the server.
62
3703.3.3 by Andrew Bennetts
Neater effort tests for push by using a _SmartClient.hooks['call'] hook.
63
    def _run_call_hooks(self, method, args, body, readv_body):
3703.3.4 by Andrew Bennetts
Move check to skip _SmartClient hook invocation.
64
        if not _SmartClient.hooks['call']:
65
            return
3731.2.1 by Andrew Bennetts
Show total HPSS calls (if any) on stderr when -Dhpss is active.
66
        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.
67
        for hook in _SmartClient.hooks['call']:
68
            hook(params)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
69
3245.4.42 by Andrew Bennetts
Make _SmartClient automatically detect and use the highest protocol version compatible with the server.
70
    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.
71
            body_stream=None, expect_response_body=True):
3703.3.4 by Andrew Bennetts
Move check to skip _SmartClient hook invocation.
72
        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).
73
        if self._medium._protocol_version is not None:
3245.4.46 by Andrew Bennetts
Apply John's review comments.
74
            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).
75
                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.
76
                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.
77
            return (response_handler.read_response_tuple(
78
                        expect_body=expect_response_body),
79
                    response_handler)
80
        else:
3245.4.43 by Andrew Bennetts
Improve tests for automatic detection of protocol version.
81
            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.
82
                if protocol_version == 2:
83
                    # 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.
84
                    self._medium._remember_remote_is_before((1, 6))
3245.4.46 by Andrew Bennetts
Apply John's review comments.
85
                response_handler = self._send_request(
86
                    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.
87
                    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.
88
                try:
89
                    response_tuple = response_handler.read_response_tuple(
90
                        expect_body=expect_response_body)
3245.4.43 by Andrew Bennetts
Improve tests for automatic detection of protocol version.
91
                except errors.UnexpectedProtocolVersionMarker, err:
3245.4.42 by Andrew Bennetts
Make _SmartClient automatically detect and use the highest protocol version compatible with the server.
92
                    # TODO: We could recover from this without disconnecting if
93
                    # we recognise the protocol version.
3245.4.59 by Andrew Bennetts
Various tweaks in response to Martin's review.
94
                    warning(
95
                        'Server does not understand Bazaar network protocol %d,'
3245.4.62 by Andrew Bennetts
Fix unsubstituted '%d' in reconnection warning.
96
                        ' reconnecting.  (Upgrade the server to avoid this.)'
97
                        % (protocol_version,))
3245.4.42 by Andrew Bennetts
Make _SmartClient automatically detect and use the highest protocol version compatible with the server.
98
                    self._medium.disconnect()
99
                    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.
100
                except errors.ErrorFromSmartServer:
101
                    # If we received an error reply from the server, then it
102
                    # must be ok with this protocol version.
103
                    self._medium._protocol_version = protocol_version
104
                    raise
3245.4.42 by Andrew Bennetts
Make _SmartClient automatically detect and use the highest protocol version compatible with the server.
105
                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).
106
                    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.
107
                    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.
108
            raise errors.SmartProtocolError(
109
                '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.
110
111
    def _construct_protocol(self, version):
3241.2.2 by Andrew Bennetts
Merge from bzr.dev.
112
        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.
113
        if version == 3:
114
            request_encoder = protocol.ProtocolThreeRequester(request)
115
            response_handler = message.ConventionalResponseHandler()
3245.4.42 by Andrew Bennetts
Make _SmartClient automatically detect and use the highest protocol version compatible with the server.
116
            response_proto = protocol.ProtocolThreeDecoder(
117
                response_handler, expect_version_marker=True)
3245.4.26 by Andrew Bennetts
Rename 'setProtoAndMedium' to more accurate 'setProtoAndMediumRequest', add ABCs for Requesters and ResponseHandlers.
118
            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.
119
        elif version == 2:
120
            request_encoder = protocol.SmartClientRequestProtocolTwo(request)
121
            response_handler = request_encoder
3241.2.1 by Andrew Bennetts
Dynamically choose the best client protocol version in bzrlib.smart.client.
122
        else:
3245.4.1 by Andrew Bennetts
Merge in the protocol-v3 implementation so far, integrating with the protocol negotiation in bzrlib.smart.client.
123
            request_encoder = protocol.SmartClientRequestProtocolOne(request)
124
            response_handler = request_encoder
125
        return request_encoder, response_handler
3241.2.1 by Andrew Bennetts
Dynamically choose the best client protocol version in bzrlib.smart.client.
126
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.
127
    def call(self, method, *args):
128
        """Call a method on the remote server."""
2414.1.2 by Andrew Bennetts
Deal with review comments.
129
        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).
130
        protocol.cancel_read_body()
131
        return result
132
2414.1.2 by Andrew Bennetts
Deal with review comments.
133
    def call_expecting_body(self, method, *args):
134
        """Call a method and return the result and the protocol object.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
135
2414.1.2 by Andrew Bennetts
Deal with review comments.
136
        The body can be read like so::
137
138
            result, smart_protocol = smart_client.call_expecting_body(...)
139
            body = smart_protocol.read_body_bytes()
140
        """
3245.4.42 by Andrew Bennetts
Make _SmartClient automatically detect and use the highest protocol version compatible with the server.
141
        return self._call_and_read_response(
142
            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.
143
144
    def call_with_body_bytes(self, method, args, body):
145
        """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.
146
        if type(method) is not str:
147
            raise TypeError('method must be a byte string, not %r' % (method,))
148
        for arg in args:
149
            if type(arg) is not str:
150
                raise TypeError('args must be byte strings, not %r' % (args,))
151
        if type(body) is not str:
152
            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.
153
        response, response_handler = self._call_and_read_response(
154
            method, args, body=body, expect_response_body=False)
155
        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).
156
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.
157
    def call_with_body_bytes_expecting_body(self, method, args, body):
158
        """Call a method on the remote server with body bytes."""
159
        if type(method) is not str:
160
            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.
161
        for arg in args:
162
            if type(arg) is not str:
163
                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.
164
        if type(body) is not str:
165
            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.
166
        response, response_handler = self._call_and_read_response(
167
            method, args, body=body, expect_response_body=True)
168
        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.
169
3245.4.24 by Andrew Bennetts
Consistently raise errors from the server as ErrorFromSmartServer exceptions.
170
    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.
171
        response, response_handler = self._call_and_read_response(
172
                args[0], args[1:], readv_body=body, expect_response_body=True)
173
        return (response, response_handler)
3245.4.24 by Andrew Bennetts
Consistently raise errors from the server as ErrorFromSmartServer exceptions.
174
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.
175
    def call_with_body_stream(self, args, stream):
176
        response, response_handler = self._call_and_read_response(
177
                args[0], args[1:], body_stream=stream,
178
                expect_response_body=False)
179
        return (response, response_handler)
180
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).
181
    def remote_path_from_transport(self, transport):
2414.1.2 by Andrew Bennetts
Deal with review comments.
182
        """Convert transport into a path suitable for using in a request.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
183
2414.1.2 by Andrew Bennetts
Deal with review comments.
184
        Note that the resulting remote path doesn't encode the host name or
185
        anything but path, so it is only safe to use it in requests sent over
186
        the medium from the matching transport.
187
        """
3431.3.11 by Andrew Bennetts
Push remote_path_from_transport logic into SmartClientMedium, removing special-casing of bzr+http from _SmartClient.
188
        return self._medium.remote_path_from_transport(transport)
3297.3.1 by Andrew Bennetts
Raise UnknownSmartMethod automatically from read_response_tuple.
189
3703.3.3 by Andrew Bennetts
Neater effort tests for push by using a _SmartClient.hooks['call'] hook.
190
191
class SmartClientHooks(hooks.Hooks):
192
193
    def __init__(self):
194
        hooks.Hooks.__init__(self)
4119.3.2 by Robert Collins
Migrate existing hooks over to the new HookPoint infrastructure.
195
        self.create_hook(hooks.HookPoint('call',
196
            "Called when the smart client is submitting a request to the "
197
            "smart server. Called with a bzrlib.smart.client.CallHookParams "
198
            "object. Streaming request bodies, and responses, are not "
199
            "accessible.", None, None))
3703.3.3 by Andrew Bennetts
Neater effort tests for push by using a _SmartClient.hooks['call'] hook.
200
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
201
3703.3.3 by Andrew Bennetts
Neater effort tests for push by using a _SmartClient.hooks['call'] hook.
202
_SmartClient.hooks = SmartClientHooks()
203
204
205
class CallHookParams(object):
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
206
3731.2.1 by Andrew Bennetts
Show total HPSS calls (if any) on stderr when -Dhpss is active.
207
    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.
208
        self.method = method
209
        self.args = args
210
        self.body = body
211
        self.readv_body = readv_body
3731.2.1 by Andrew Bennetts
Show total HPSS calls (if any) on stderr when -Dhpss is active.
212
        self.medium = medium
3703.3.3 by Andrew Bennetts
Neater effort tests for push by using a _SmartClient.hooks['call'] hook.
213
214
    def __repr__(self):
215
        attrs = dict((k, v) for (k, v) in self.__dict__.iteritems()
216
                     if v is not None)
217
        return '<%s %r>' % (self.__class__.__name__, attrs)
218
219
    def __eq__(self, other):
220
        if type(other) is not type(self):
221
            return NotImplemented
222
        return self.__dict__ == other.__dict__
223
224
    def __ne__(self, other):
225
        return not self == other