/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/smart/client.py

  • Committer: Andrew Bennetts
  • Date: 2008-05-16 07:15:57 UTC
  • mto: This revision was merged to the branch mainline in revision 3428.
  • Revision ID: andrew.bennetts@canonical.com-20080516071557-wenq9ita6iogeb1x
Update the protocol v3 version string to say 'bzr 1.6'.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2006-2008 Canonical Ltd
 
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
 
 
17
import urllib
 
18
 
 
19
import bzrlib
 
20
from bzrlib.smart import message, protocol
 
21
from bzrlib.trace import warning
 
22
from bzrlib import urlutils, errors
 
23
 
 
24
 
 
25
class _SmartClient(object):
 
26
 
 
27
    def __init__(self, medium, base, headers=None):
 
28
        """Constructor.
 
29
 
 
30
        :param medium: a SmartClientMedium
 
31
        :param base: a URL
 
32
        """
 
33
        self._medium = medium
 
34
        self._base = base
 
35
        if headers is None:
 
36
            self._headers = {'Software version': bzrlib.__version__}
 
37
        else:
 
38
            self._headers = dict(headers)
 
39
 
 
40
    def _send_request(self, protocol_version, method, args, body=None,
 
41
                      readv_body=None):
 
42
        encoder, response_handler = self._construct_protocol(
 
43
            protocol_version)
 
44
        encoder.set_headers(self._headers)
 
45
        if body is not None:
 
46
            if readv_body is not None:
 
47
                raise AssertionError(
 
48
                    "body and readv_body are mutually exclusive.")
 
49
            encoder.call_with_body_bytes((method, ) + args, body)
 
50
        elif readv_body is not None:
 
51
            encoder.call_with_body_readv_array((method, ) + args,
 
52
                    readv_body)
 
53
        else:
 
54
            encoder.call(method, *args)
 
55
        return response_handler
 
56
 
 
57
    def _call_and_read_response(self, method, args, body=None, readv_body=None,
 
58
            expect_response_body=True):
 
59
        if self._medium._protocol_version is not None:
 
60
            response_handler = self._send_request(
 
61
                self._medium._protocol_version, method, args, body=body,
 
62
                readv_body=readv_body)
 
63
            return (response_handler.read_response_tuple(
 
64
                        expect_body=expect_response_body),
 
65
                    response_handler)
 
66
        else:
 
67
            for protocol_version in [3, 2]:
 
68
                response_handler = self._send_request(
 
69
                    protocol_version, method, args, body=body,
 
70
                    readv_body=readv_body)
 
71
                try:
 
72
                    response_tuple = response_handler.read_response_tuple(
 
73
                        expect_body=expect_response_body)
 
74
                except errors.UnexpectedProtocolVersionMarker, err:
 
75
                    # TODO: We could recover from this without disconnecting if
 
76
                    # we recognise the protocol version.
 
77
                    warning(
 
78
                        'Server does not understand Bazaar network protocol %d,'
 
79
                        ' reconnecting.  (Upgrade the server to avoid this.)')
 
80
                    self._medium.disconnect()
 
81
                    continue
 
82
                else:
 
83
                    self._medium._protocol_version = protocol_version
 
84
                    return response_tuple, response_handler
 
85
            raise errors.SmartProtocolError(
 
86
                'Server is not a Bazaar server: ' + str(err))
 
87
 
 
88
    def _construct_protocol(self, version):
 
89
        request = self._medium.get_request()
 
90
        if version == 3:
 
91
            request_encoder = protocol.ProtocolThreeRequester(request)
 
92
            response_handler = message.ConventionalResponseHandler()
 
93
            response_proto = protocol.ProtocolThreeDecoder(
 
94
                response_handler, expect_version_marker=True)
 
95
            response_handler.setProtoAndMediumRequest(response_proto, request)
 
96
        elif version == 2:
 
97
            request_encoder = protocol.SmartClientRequestProtocolTwo(request)
 
98
            response_handler = request_encoder
 
99
        else:
 
100
            request_encoder = protocol.SmartClientRequestProtocolOne(request)
 
101
            response_handler = request_encoder
 
102
        return request_encoder, response_handler
 
103
 
 
104
    def call(self, method, *args):
 
105
        """Call a method on the remote server."""
 
106
        result, protocol = self.call_expecting_body(method, *args)
 
107
        protocol.cancel_read_body()
 
108
        return result
 
109
 
 
110
    def call_expecting_body(self, method, *args):
 
111
        """Call a method and return the result and the protocol object.
 
112
        
 
113
        The body can be read like so::
 
114
 
 
115
            result, smart_protocol = smart_client.call_expecting_body(...)
 
116
            body = smart_protocol.read_body_bytes()
 
117
        """
 
118
        return self._call_and_read_response(
 
119
            method, args, expect_response_body=True)
 
120
 
 
121
    def call_with_body_bytes(self, method, args, body):
 
122
        """Call a method on the remote server with body bytes."""
 
123
        if type(method) is not str:
 
124
            raise TypeError('method must be a byte string, not %r' % (method,))
 
125
        for arg in args:
 
126
            if type(arg) is not str:
 
127
                raise TypeError('args must be byte strings, not %r' % (args,))
 
128
        if type(body) is not str:
 
129
            raise TypeError('body must be byte string, not %r' % (body,))
 
130
        response, response_handler = self._call_and_read_response(
 
131
            method, args, body=body, expect_response_body=False)
 
132
        return response
 
133
 
 
134
    def call_with_body_bytes_expecting_body(self, method, args, body):
 
135
        """Call a method on the remote server with body bytes."""
 
136
        if type(method) is not str:
 
137
            raise TypeError('method must be a byte string, not %r' % (method,))
 
138
        for arg in args:
 
139
            if type(arg) is not str:
 
140
                raise TypeError('args must be byte strings, not %r' % (args,))
 
141
        if type(body) is not str:
 
142
            raise TypeError('body must be byte string, not %r' % (body,))
 
143
        response, response_handler = self._call_and_read_response(
 
144
            method, args, body=body, expect_response_body=True)
 
145
        return (response, response_handler)
 
146
 
 
147
    def call_with_body_readv_array(self, args, body):
 
148
        response, response_handler = self._call_and_read_response(
 
149
                args[0], args[1:], readv_body=body, expect_response_body=True)
 
150
        return (response, response_handler)
 
151
 
 
152
    def remote_path_from_transport(self, transport):
 
153
        """Convert transport into a path suitable for using in a request.
 
154
        
 
155
        Note that the resulting remote path doesn't encode the host name or
 
156
        anything but path, so it is only safe to use it in requests sent over
 
157
        the medium from the matching transport.
 
158
        """
 
159
        base = self._base
 
160
        if (base.startswith('bzr+http://') or base.startswith('bzr+https://')
 
161
            or base.startswith('http://') or base.startswith('https://')):
 
162
            medium_base = self._base
 
163
        else:
 
164
            medium_base = urlutils.join(self._base, '/')
 
165
            
 
166
        rel_url = urlutils.relative_url(medium_base, transport.base)
 
167
        return urllib.unquote(rel_url)
 
168