95
98
elif code in (200, 302): # "ok", "found"
98
self._raise_curl_connection_error(curl)
100
101
self._raise_curl_http_error(curl)
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)
108
return self._get_full(relpath)
110
def _setup_get_request(self, curl, relpath):
111
"""Do the common setup stuff for making a request
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)
117
data: file that will be filled with the body
118
header: file that will be filled with the headers
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)
128
curl.setopt(pycurl.WRITEFUNCTION, data.write)
129
curl.setopt(pycurl.HEADERFUNCTION, header.write)
131
return abspath, data, header
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)
139
code = curl.getinfo(pycurl.HTTP_CODE)
143
raise NoSuchFile(abspath)
145
raise errors.InvalidHttpResponse(abspath,
146
'Expected a 200 or 404 response, not: %s' % code)
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
109
curl = self._base_curl
110
if ranges is not None:
111
curl = self._range_curl
113
abspath = self._real_abspath(relpath)
114
curl.setopt(pycurl.URL, abspath)
115
self._set_curl_options(curl)
116
curl.setopt(pycurl.NOBODY, 0)
120
self._curl_handle.setopt(pycurl.WRITEFUNCTION, data.write)
121
self._curl_handle.setopt(pycurl.HEADERFUNCTION, header.write)
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)
163
curl.setopt(pycurl.RANGE,
164
self.range_header(ranges, tail_amount))
125
165
self._curl_perform(curl)
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)
131
171
def _raise_curl_connection_error(self, curl):
132
172
curl_errno = curl.getinfo(pycurl.OS_ERRNO)