~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/smart/client.py

  • Committer: John Arbash Meinel
  • Date: 2008-05-29 19:46:01 UTC
  • mfrom: (3456 +trunk)
  • mto: This revision was merged to the branch mainline in revision 3459.
  • Revision ID: john@arbash-meinel.com-20080529194601-r2gpmk536xin9c4a
merge bzr.dev, put the NEWS entry in the right place

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006 Canonical Ltd
 
1
# Copyright (C) 2006-2008 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
17
 
import urllib
18
 
from urlparse import urlparse
19
 
 
20
 
from bzrlib.smart import protocol
21
 
from bzrlib import urlutils
 
17
import bzrlib
 
18
from bzrlib.smart import message, protocol
 
19
from bzrlib.trace import warning
 
20
from bzrlib import errors
22
21
 
23
22
 
24
23
class _SmartClient(object):
25
24
 
26
 
    def __init__(self, shared_connection):
 
25
    def __init__(self, medium, headers=None):
27
26
        """Constructor.
28
27
 
29
 
        :param shared_connection: a bzrlib.transport._SharedConnection
 
28
        :param medium: a SmartClientMedium
30
29
        """
31
 
        self._shared_connection = shared_connection
32
 
 
33
 
    def get_smart_medium(self):
34
 
        return self._shared_connection.connection
 
30
        self._medium = medium
 
31
        if headers is None:
 
32
            self._headers = {'Software version': bzrlib.__version__}
 
33
        else:
 
34
            self._headers = dict(headers)
 
35
 
 
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)
 
40
        encoder.set_headers(self._headers)
 
41
        if body is not None:
 
42
            if readv_body is not None:
 
43
                raise AssertionError(
 
44
                    "body and readv_body are mutually exclusive.")
 
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)
 
51
        return response_handler
 
52
 
 
53
    def _call_and_read_response(self, method, args, body=None, readv_body=None,
 
54
            expect_response_body=True):
 
55
        if self._medium._protocol_version is not None:
 
56
            response_handler = self._send_request(
 
57
                self._medium._protocol_version, method, args, body=body,
 
58
                readv_body=readv_body)
 
59
            return (response_handler.read_response_tuple(
 
60
                        expect_body=expect_response_body),
 
61
                    response_handler)
 
62
        else:
 
63
            for protocol_version in [3, 2]:
 
64
                response_handler = self._send_request(
 
65
                    protocol_version, method, args, body=body,
 
66
                    readv_body=readv_body)
 
67
                try:
 
68
                    response_tuple = response_handler.read_response_tuple(
 
69
                        expect_body=expect_response_body)
 
70
                except errors.UnexpectedProtocolVersionMarker, err:
 
71
                    # TODO: We could recover from this without disconnecting if
 
72
                    # we recognise the protocol version.
 
73
                    warning(
 
74
                        'Server does not understand Bazaar network protocol %d,'
 
75
                        ' reconnecting.  (Upgrade the server to avoid this.)'
 
76
                        % (protocol_version,))
 
77
                    self._medium.disconnect()
 
78
                    continue
 
79
                else:
 
80
                    self._medium._protocol_version = protocol_version
 
81
                    return response_tuple, response_handler
 
82
            raise errors.SmartProtocolError(
 
83
                'Server is not a Bazaar server: ' + str(err))
 
84
 
 
85
    def _construct_protocol(self, version):
 
86
        request = self._medium.get_request()
 
87
        if version == 3:
 
88
            request_encoder = protocol.ProtocolThreeRequester(request)
 
89
            response_handler = message.ConventionalResponseHandler()
 
90
            response_proto = protocol.ProtocolThreeDecoder(
 
91
                response_handler, expect_version_marker=True)
 
92
            response_handler.setProtoAndMediumRequest(response_proto, request)
 
93
        elif version == 2:
 
94
            request_encoder = protocol.SmartClientRequestProtocolTwo(request)
 
95
            response_handler = request_encoder
 
96
        else:
 
97
            request_encoder = protocol.SmartClientRequestProtocolOne(request)
 
98
            response_handler = request_encoder
 
99
        return request_encoder, response_handler
35
100
 
36
101
    def call(self, method, *args):
37
102
        """Call a method on the remote server."""
47
112
            result, smart_protocol = smart_client.call_expecting_body(...)
48
113
            body = smart_protocol.read_body_bytes()
49
114
        """
50
 
        request = self.get_smart_medium().get_request()
51
 
        smart_protocol = protocol.SmartClientRequestProtocolTwo(request)
52
 
        smart_protocol.call(method, *args)
53
 
        return smart_protocol.read_response_tuple(expect_body=True), smart_protocol
 
115
        return self._call_and_read_response(
 
116
            method, args, expect_response_body=True)
54
117
 
55
118
    def call_with_body_bytes(self, method, args, body):
56
119
        """Call a method on the remote server with body bytes."""
61
124
                raise TypeError('args must be byte strings, not %r' % (args,))
62
125
        if type(body) is not str:
63
126
            raise TypeError('body must be byte string, not %r' % (body,))
64
 
        request = self.get_smart_medium().get_request()
65
 
        smart_protocol = protocol.SmartClientRequestProtocolOne(request)
66
 
        smart_protocol.call_with_body_bytes((method, ) + args, body)
67
 
        return smart_protocol.read_response_tuple()
 
127
        response, response_handler = self._call_and_read_response(
 
128
            method, args, body=body, expect_response_body=False)
 
129
        return response
68
130
 
69
131
    def call_with_body_bytes_expecting_body(self, method, args, body):
70
132
        """Call a method on the remote server with body bytes."""
75
137
                raise TypeError('args must be byte strings, not %r' % (args,))
76
138
        if type(body) is not str:
77
139
            raise TypeError('body must be byte string, not %r' % (body,))
78
 
        request = self.get_smart_medium().get_request()
79
 
        smart_protocol = protocol.SmartClientRequestProtocolTwo(request)
80
 
        smart_protocol.call_with_body_bytes((method, ) + args, body)
81
 
        return smart_protocol.read_response_tuple(expect_body=True), smart_protocol
 
140
        response, response_handler = self._call_and_read_response(
 
141
            method, args, body=body, expect_response_body=True)
 
142
        return (response, response_handler)
 
143
 
 
144
    def call_with_body_readv_array(self, args, body):
 
145
        response, response_handler = self._call_and_read_response(
 
146
                args[0], args[1:], readv_body=body, expect_response_body=True)
 
147
        return (response, response_handler)
82
148
 
83
149
    def remote_path_from_transport(self, transport):
84
150
        """Convert transport into a path suitable for using in a request.
87
153
        anything but path, so it is only safe to use it in requests sent over
88
154
        the medium from the matching transport.
89
155
        """
90
 
        base = self._shared_connection.base
91
 
        if base.startswith('bzr+http://') or base.startswith('bzr+https://'):
92
 
            medium_base = self._shared_connection.base
93
 
        else:
94
 
            medium_base = urlutils.join(self._shared_connection.base, '/')
95
 
            
96
 
        rel_url = urlutils.relative_url(medium_base, transport.base)
97
 
        return urllib.unquote(rel_url)
 
156
        return self._medium.remote_path_from_transport(transport)
 
157