~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
3431.3.11 by Andrew Bennetts
Push remote_path_from_transport logic into SmartClientMedium, removing special-casing of bzr+http from _SmartClient.
20
from bzrlib import errors
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.
21
22
2414.1.4 by Andrew Bennetts
Rename SmartClient to _SmartClient.
23
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.
24
3431.3.2 by Andrew Bennetts
Remove 'base' from _SmartClient entirely, now that the medium has it.
25
    def __init__(self, medium, headers=None):
3104.4.4 by Andrew Bennetts
Rename parameter to _SmartClient.__init__ to be less confusing.
26
        """Constructor.
27
3313.2.1 by Andrew Bennetts
Change _SmartClient's API to accept a medium and a base, rather than a _SharedConnection.
28
        :param medium: a SmartClientMedium
3104.4.4 by Andrew Bennetts
Rename parameter to _SmartClient.__init__ to be less confusing.
29
        """
3313.2.1 by Andrew Bennetts
Change _SmartClient's API to accept a medium and a base, rather than a _SharedConnection.
30
        self._medium = medium
3245.4.42 by Andrew Bennetts
Make _SmartClient automatically detect and use the highest protocol version compatible with the server.
31
        if headers is None:
32
            self._headers = {'Software version': bzrlib.__version__}
33
        else:
34
            self._headers = dict(headers)
35
3245.4.46 by Andrew Bennetts
Apply John's review comments.
36
    def _send_request(self, protocol_version, method, args, body=None,
37
                      readv_body=None):
38
        encoder, response_handler = self._construct_protocol(
39
            protocol_version)
3245.4.42 by Andrew Bennetts
Make _SmartClient automatically detect and use the highest protocol version compatible with the server.
40
        encoder.set_headers(self._headers)
41
        if body is not None:
3245.4.46 by Andrew Bennetts
Apply John's review comments.
42
            if readv_body is not None:
43
                raise AssertionError(
44
                    "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.
45
            encoder.call_with_body_bytes((method, ) + args, body)
46
        elif readv_body is not None:
47
            encoder.call_with_body_readv_array((method, ) + args,
48
                    readv_body)
49
        else:
50
            encoder.call(method, *args)
3245.4.46 by Andrew Bennetts
Apply John's review comments.
51
        return response_handler
3245.4.42 by Andrew Bennetts
Make _SmartClient automatically detect and use the highest protocol version compatible with the server.
52
53
    def _call_and_read_response(self, method, args, body=None, readv_body=None,
54
            expect_response_body=True):
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).
55
        if self._medium._protocol_version is not None:
3245.4.46 by Andrew Bennetts
Apply John's review comments.
56
            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).
57
                self._medium._protocol_version, method, args, body=body,
3245.4.46 by Andrew Bennetts
Apply John's review comments.
58
                readv_body=readv_body)
3245.4.42 by Andrew Bennetts
Make _SmartClient automatically detect and use the highest protocol version compatible with the server.
59
            return (response_handler.read_response_tuple(
60
                        expect_body=expect_response_body),
61
                    response_handler)
62
        else:
3245.4.43 by Andrew Bennetts
Improve tests for automatic detection of protocol version.
63
            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.
64
                if protocol_version == 2:
65
                    # 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.
66
                    self._medium._remember_remote_is_before((1, 6))
3245.4.46 by Andrew Bennetts
Apply John's review comments.
67
                response_handler = self._send_request(
68
                    protocol_version, method, args, body=body,
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
                try:
71
                    response_tuple = response_handler.read_response_tuple(
72
                        expect_body=expect_response_body)
3245.4.43 by Andrew Bennetts
Improve tests for automatic detection of protocol version.
73
                except errors.UnexpectedProtocolVersionMarker, err:
3245.4.42 by Andrew Bennetts
Make _SmartClient automatically detect and use the highest protocol version compatible with the server.
74
                    # TODO: We could recover from this without disconnecting if
75
                    # we recognise the protocol version.
3245.4.59 by Andrew Bennetts
Various tweaks in response to Martin's review.
76
                    warning(
77
                        'Server does not understand Bazaar network protocol %d,'
3245.4.62 by Andrew Bennetts
Fix unsubstituted '%d' in reconnection warning.
78
                        ' reconnecting.  (Upgrade the server to avoid this.)'
79
                        % (protocol_version,))
3245.4.42 by Andrew Bennetts
Make _SmartClient automatically detect and use the highest protocol version compatible with the server.
80
                    self._medium.disconnect()
81
                    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.
82
                except errors.ErrorFromSmartServer:
83
                    # If we received an error reply from the server, then it
84
                    # must be ok with this protocol version.
85
                    self._medium._protocol_version = protocol_version
86
                    raise
3245.4.42 by Andrew Bennetts
Make _SmartClient automatically detect and use the highest protocol version compatible with the server.
87
                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).
88
                    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.
89
                    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.
90
            raise errors.SmartProtocolError(
91
                '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.
92
93
    def _construct_protocol(self, version):
3241.2.2 by Andrew Bennetts
Merge from bzr.dev.
94
        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.
95
        if version == 3:
96
            request_encoder = protocol.ProtocolThreeRequester(request)
97
            response_handler = message.ConventionalResponseHandler()
3245.4.42 by Andrew Bennetts
Make _SmartClient automatically detect and use the highest protocol version compatible with the server.
98
            response_proto = protocol.ProtocolThreeDecoder(
99
                response_handler, expect_version_marker=True)
3245.4.26 by Andrew Bennetts
Rename 'setProtoAndMedium' to more accurate 'setProtoAndMediumRequest', add ABCs for Requesters and ResponseHandlers.
100
            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.
101
        elif version == 2:
102
            request_encoder = protocol.SmartClientRequestProtocolTwo(request)
103
            response_handler = request_encoder
3241.2.1 by Andrew Bennetts
Dynamically choose the best client protocol version in bzrlib.smart.client.
104
        else:
3245.4.1 by Andrew Bennetts
Merge in the protocol-v3 implementation so far, integrating with the protocol negotiation in bzrlib.smart.client.
105
            request_encoder = protocol.SmartClientRequestProtocolOne(request)
106
            response_handler = request_encoder
107
        return request_encoder, response_handler
3241.2.1 by Andrew Bennetts
Dynamically choose the best client protocol version in bzrlib.smart.client.
108
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.
109
    def call(self, method, *args):
110
        """Call a method on the remote server."""
2414.1.2 by Andrew Bennetts
Deal with review comments.
111
        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).
112
        protocol.cancel_read_body()
113
        return result
114
2414.1.2 by Andrew Bennetts
Deal with review comments.
115
    def call_expecting_body(self, method, *args):
116
        """Call a method and return the result and the protocol object.
117
        
118
        The body can be read like so::
119
120
            result, smart_protocol = smart_client.call_expecting_body(...)
121
            body = smart_protocol.read_body_bytes()
122
        """
3245.4.42 by Andrew Bennetts
Make _SmartClient automatically detect and use the highest protocol version compatible with the server.
123
        return self._call_and_read_response(
124
            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.
125
126
    def call_with_body_bytes(self, method, args, body):
127
        """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.
128
        if type(method) is not str:
129
            raise TypeError('method must be a byte string, not %r' % (method,))
130
        for arg in args:
131
            if type(arg) is not str:
132
                raise TypeError('args must be byte strings, not %r' % (args,))
133
        if type(body) is not str:
134
            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.
135
        response, response_handler = self._call_and_read_response(
136
            method, args, body=body, expect_response_body=False)
137
        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).
138
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.
139
    def call_with_body_bytes_expecting_body(self, method, args, body):
140
        """Call a method on the remote server with body bytes."""
141
        if type(method) is not str:
142
            raise TypeError('method must be a byte string, not %r' % (method,))
143
        for arg in args:
144
            if type(arg) is not str:
145
                raise TypeError('args must be byte strings, not %r' % (args,))
146
        if type(body) is not str:
147
            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.
148
        response, response_handler = self._call_and_read_response(
149
            method, args, body=body, expect_response_body=True)
150
        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.
151
3245.4.24 by Andrew Bennetts
Consistently raise errors from the server as ErrorFromSmartServer exceptions.
152
    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.
153
        response, response_handler = self._call_and_read_response(
154
                args[0], args[1:], readv_body=body, expect_response_body=True)
155
        return (response, response_handler)
3245.4.24 by Andrew Bennetts
Consistently raise errors from the server as ErrorFromSmartServer exceptions.
156
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).
157
    def remote_path_from_transport(self, transport):
2414.1.2 by Andrew Bennetts
Deal with review comments.
158
        """Convert transport into a path suitable for using in a request.
159
        
160
        Note that the resulting remote path doesn't encode the host name or
161
        anything but path, so it is only safe to use it in requests sent over
162
        the medium from the matching transport.
163
        """
3431.3.11 by Andrew Bennetts
Push remote_path_from_transport logic into SmartClientMedium, removing special-casing of bzr+http from _SmartClient.
164
        return self._medium.remote_path_from_transport(transport)
3297.3.1 by Andrew Bennetts
Raise UnknownSmartMethod automatically from read_response_tuple.
165