~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transport/http/_pycurl.py

  • Committer: John Arbash Meinel
  • Date: 2006-07-13 18:26:21 UTC
  • mto: This revision was merged to the branch mainline in revision 1869.
  • Revision ID: john@arbash-meinel.com-20060713182621-10fc8ce282741455
Fix up the http transports so that tests pass with the new configuration.

Show diffs side-by-side

added added

removed removed

Lines of Context:
26
26
import os
27
27
from StringIO import StringIO
28
28
 
 
29
from bzrlib import errors
29
30
import bzrlib
30
31
from bzrlib.errors import (TransportNotPossible, NoSuchFile,
31
32
                           TransportError, ConnectionError,
32
33
                           DependencyNotPresent)
33
34
from bzrlib.trace import mutter
34
35
from bzrlib.transport import register_urlparse_netloc_protocol
35
 
from bzrlib.transport.http import HttpTransportBase, HttpServer
 
36
from bzrlib.transport.http import (HttpTransportBase, HttpServer,
 
37
                                   response, _extract_headers)
36
38
 
37
39
try:
38
40
    import pycurl
87
89
        curl.setopt(pycurl.FOLLOWLOCATION, 1) # follow redirect responses
88
90
        self._set_curl_options(curl)
89
91
        # don't want the body - ie just do a HEAD request
 
92
        # This means "NO BODY" not 'nobody'
90
93
        curl.setopt(pycurl.NOBODY, 1)
91
94
        self._curl_perform(curl)
92
95
        code = curl.getinfo(pycurl.HTTP_CODE)
94
97
            return False
95
98
        elif code in (200, 302): # "ok", "found"
96
99
            return True
97
 
        elif code == 0:
98
 
            self._raise_curl_connection_error(curl)
99
100
        else:
100
101
            self._raise_curl_http_error(curl)
101
102
        
102
103
    def _get(self, relpath, ranges, tail_amount=0):
 
104
        # This just switches based on the type of request
 
105
        if ranges is not None or tail_amount not in (0, None):
 
106
            return self._get_ranged(relpath, ranges, tail_amount=tail_amount)
 
107
        else:
 
108
            return self._get_full(relpath)
 
109
    
 
110
    def _setup_get_request(self, curl, relpath):
 
111
        """Do the common setup stuff for making a request
 
112
 
 
113
        :param curl: The curl object to place the request on
 
114
        :param relpath: The relative path that we want to get
 
115
        :return: (abspath, data, header) 
 
116
                 abspath: full url
 
117
                 data: file that will be filled with the body
 
118
                 header: file that will be filled with the headers
 
119
        """
 
120
        abspath = self._real_abspath(relpath)
 
121
        curl.setopt(pycurl.URL, abspath)
 
122
        self._set_curl_options(curl)
 
123
        # This means "NO BODY" not 'nobody'
 
124
        curl.setopt(pycurl.NOBODY, 0)
 
125
 
 
126
        data = StringIO()
 
127
        header = StringIO()
 
128
        curl.setopt(pycurl.WRITEFUNCTION, data.write)
 
129
        curl.setopt(pycurl.HEADERFUNCTION, header.write)
 
130
 
 
131
        return abspath, data, header
 
132
 
 
133
    def _get_full(self, relpath):
 
134
        """Make a request for the entire file"""
 
135
        curl = self._base_curl
 
136
        abspath, data, header = self._setup_get_request(curl, relpath)
 
137
        self._curl_perform(curl)
 
138
 
 
139
        code = curl.getinfo(pycurl.HTTP_CODE)
 
140
        data.seek(0)
 
141
 
 
142
        if code == 404:
 
143
            raise NoSuchFile(abspath)
 
144
        if code != 200:
 
145
            raise errors.InvalidHttpResponse(abspath,
 
146
                'Expected a 200 or 404 response, not: %s' % code)
 
147
 
 
148
        return code, data
 
149
 
 
150
    def _get_ranged(self, relpath, ranges, tail_amount):
 
151
        """Make a request for just part of the file."""
 
152
        # We would like to re-use the same curl object for 
 
153
        # full requests and partial requests
103
154
        # Documentation says 'Pass in NULL to disable the use of ranges'
104
155
        # None is the closest we have, but at least with pycurl 7.13.1
105
156
        # It raises an 'invalid arguments' response
106
 
        #curl.setopt(pycurl.RANGE, None)
107
 
        # So instead we hack around this by using a separate object for
108
 
        # range requests
109
 
        curl = self._base_curl
110
 
        if ranges is not None:
111
 
            curl = self._range_curl
112
 
 
113
 
        abspath = self._real_abspath(relpath)
114
 
        curl.setopt(pycurl.URL, abspath)
115
 
        self._set_curl_options(curl)
116
 
        curl.setopt(pycurl.NOBODY, 0)
117
 
 
118
 
        data = StringIO()
119
 
        header = StringIO()
120
 
        self._curl_handle.setopt(pycurl.WRITEFUNCTION, data.write)
121
 
        self._curl_handle.setopt(pycurl.HEADERFUNCTION, header.write)
122
 
 
123
 
        if ranges is not None or tail_amount:
124
 
            curl.setopt(pycurl.RANGE, self._range_header(ranges, tail_amount))
 
157
        # curl.setopt(pycurl.RANGE, None)
 
158
        # curl.unsetopt(pycurl.RANGE) doesn't support the RANGE parameter
 
159
        # So instead we hack around this by using a separate objects
 
160
        curl = self._range_curl
 
161
        abspath, data, header = self._setup_get_request(curl, relpath)
 
162
 
 
163
        curl.setopt(pycurl.RANGE,
 
164
                                self.range_header(ranges, tail_amount))
125
165
        self._curl_perform(curl)
126
 
        response.update()
127
 
        if response.code == 0:
128
 
            self._raise_curl_connection_error(curl)
129
 
        return response.code, self._handle_response(relpath, response)
 
166
        code = curl.getinfo(pycurl.HTTP_CODE)
 
167
        headers = _extract_headers(header, skip_first=True)
 
168
        # handle_response will raise NoSuchFile, etc based on the response code
 
169
        return code, response.handle_response(abspath, code, headers, data)
130
170
 
131
171
    def _raise_curl_connection_error(self, curl):
132
172
        curl_errno = curl.getinfo(pycurl.OS_ERRNO)
137
177
    def _raise_curl_http_error(self, curl):
138
178
        code = curl.getinfo(pycurl.HTTP_CODE)
139
179
        url = curl.getinfo(pycurl.EFFECTIVE_URL)
140
 
        raise TransportError('http error %d probing for %s' %
141
 
                             (code, url))
 
180
        raise errors.InvalidHttpResponse(url, 'http error code %d' % (code,))
142
181
 
143
182
    def _set_curl_options(self, curl):
144
183
        """Set options for all requests"""
162
201
        except pycurl.error, e:
163
202
            # XXX: There seem to be no symbolic constants for these values.
164
203
            if e[0] == 6:
 
204
                # TODO: jam 20060713 This should really be a ConnectionError
 
205
                #       rather than NoSuchFile if we fail to connect.
165
206
                # couldn't resolve host
166
207
                raise NoSuchFile(curl.getinfo(pycurl.EFFECTIVE_URL), e)
 
208
            elif e[0] == 7:
 
209
                self._raise_curl_connection_error(curl)
 
210
            # jam 20060713 The code didn't use to re-raise the exception here
 
211
            # but that seemed bogus
 
212
            raise
167
213
 
168
214
 
169
215
class HttpServer_PyCurl(HttpServer):