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