/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: Vincent Ladeuil
  • Date: 2008-05-17 17:53:47 UTC
  • mto: (3430.2.1 trunk)
  • mto: This revision was merged to the branch mainline in revision 3431.
  • Revision ID: v.ladeuil+lp@free.fr-20080517175347-our2rugcwl67zgv5
Fix bug #229076 by fixing header names before sending the request.

* bzrlib/transport/http/_urllib2_wrappers.py:
(AbstractHTTPHandler.do_open): Title header names before sending
the request or some exotic servers or proxies may choke.

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
                        % (protocol_version,))
 
81
                    self._medium.disconnect()
 
82
                    continue
 
83
                else:
 
84
                    self._medium._protocol_version = protocol_version
 
85
                    return response_tuple, response_handler
 
86
            raise errors.SmartProtocolError(
 
87
                'Server is not a Bazaar server: ' + str(err))
 
88
 
 
89
    def _construct_protocol(self, version):
 
90
        request = self._medium.get_request()
 
91
        if version == 3:
 
92
            request_encoder = protocol.ProtocolThreeRequester(request)
 
93
            response_handler = message.ConventionalResponseHandler()
 
94
            response_proto = protocol.ProtocolThreeDecoder(
 
95
                response_handler, expect_version_marker=True)
 
96
            response_handler.setProtoAndMediumRequest(response_proto, request)
 
97
        elif version == 2:
 
98
            request_encoder = protocol.SmartClientRequestProtocolTwo(request)
 
99
            response_handler = request_encoder
 
100
        else:
 
101
            request_encoder = protocol.SmartClientRequestProtocolOne(request)
 
102
            response_handler = request_encoder
 
103
        return request_encoder, response_handler
 
104
 
 
105
    def call(self, method, *args):
 
106
        """Call a method on the remote server."""
 
107
        result, protocol = self.call_expecting_body(method, *args)
 
108
        protocol.cancel_read_body()
 
109
        return result
 
110
 
 
111
    def call_expecting_body(self, method, *args):
 
112
        """Call a method and return the result and the protocol object.
 
113
        
 
114
        The body can be read like so::
 
115
 
 
116
            result, smart_protocol = smart_client.call_expecting_body(...)
 
117
            body = smart_protocol.read_body_bytes()
 
118
        """
 
119
        return self._call_and_read_response(
 
120
            method, args, expect_response_body=True)
 
121
 
 
122
    def call_with_body_bytes(self, method, args, body):
 
123
        """Call a method on the remote server with body bytes."""
 
124
        if type(method) is not str:
 
125
            raise TypeError('method must be a byte string, not %r' % (method,))
 
126
        for arg in args:
 
127
            if type(arg) is not str:
 
128
                raise TypeError('args must be byte strings, not %r' % (args,))
 
129
        if type(body) is not str:
 
130
            raise TypeError('body must be byte string, not %r' % (body,))
 
131
        response, response_handler = self._call_and_read_response(
 
132
            method, args, body=body, expect_response_body=False)
 
133
        return response
 
134
 
 
135
    def call_with_body_bytes_expecting_body(self, method, args, body):
 
136
        """Call a method on the remote server with body bytes."""
 
137
        if type(method) is not str:
 
138
            raise TypeError('method must be a byte string, not %r' % (method,))
 
139
        for arg in args:
 
140
            if type(arg) is not str:
 
141
                raise TypeError('args must be byte strings, not %r' % (args,))
 
142
        if type(body) is not str:
 
143
            raise TypeError('body must be byte string, not %r' % (body,))
 
144
        response, response_handler = self._call_and_read_response(
 
145
            method, args, body=body, expect_response_body=True)
 
146
        return (response, response_handler)
 
147
 
 
148
    def call_with_body_readv_array(self, args, body):
 
149
        response, response_handler = self._call_and_read_response(
 
150
                args[0], args[1:], readv_body=body, expect_response_body=True)
 
151
        return (response, response_handler)
 
152
 
 
153
    def remote_path_from_transport(self, transport):
 
154
        """Convert transport into a path suitable for using in a request.
 
155
        
 
156
        Note that the resulting remote path doesn't encode the host name or
 
157
        anything but path, so it is only safe to use it in requests sent over
 
158
        the medium from the matching transport.
 
159
        """
 
160
        base = self._base
 
161
        if (base.startswith('bzr+http://') or base.startswith('bzr+https://')
 
162
            or base.startswith('http://') or base.startswith('https://')):
 
163
            medium_base = self._base
 
164
        else:
 
165
            medium_base = urlutils.join(self._base, '/')
 
166
            
 
167
        rel_url = urlutils.relative_url(medium_base, transport.base)
 
168
        return urllib.unquote(rel_url)
 
169