~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-06-17 15:52:51 UTC
  • mto: (1750.1.4 http)
  • mto: This revision was merged to the branch mainline in revision 1869.
  • Revision ID: john@arbash-meinel.com-20060617155251-c445c9574fcdf6aa
Move the common Multipart stuff into plain http, and wrap pycurl response so that it matches the urllib response object.

Show diffs side-by-side

added added

removed removed

Lines of Context:
60
60
register_urlparse_netloc_protocol('http+pycurl')
61
61
 
62
62
 
 
63
class CurlResponse(object):
 
64
    """This just exists to make the curl response look like a urllib response."""
 
65
 
 
66
    def __init__(self, curl_handle):
 
67
        self._data = StringIO()
 
68
        self._header = StringIO()
 
69
        self._curl_handle = curl_handle
 
70
        self._curl_handle.setopt(pycurl.WRITEFUNCTION, self._data.write)
 
71
        self._curl_handle.setopt(pycurl.HEADERFUNCTION, self._header.write)
 
72
        
 
73
        # Will be filled out by calling update()
 
74
        self.headers = None
 
75
        self.code = None
 
76
 
 
77
        # Overload some functions used elsewhere
 
78
        self.read = self._data.read
 
79
        self.seek = self._data.seek
 
80
 
 
81
    def update(self):
 
82
        """Update self after the action has been performed."""
 
83
        self.code = self._curl_handle.getinfo(pycurl.HTTP_CODE)
 
84
        self._build_headers()
 
85
        self._data.seek(0, 0)
 
86
 
 
87
    def _build_headers(self):
 
88
        """Parse the headers into RFC822 format"""
 
89
 
 
90
        # TODO: jam 20060617 We probably don't need all of these
 
91
        #       headers, but this is what the 'curl' wrapper around
 
92
        #       pycurl does
 
93
        self._header.seek(0, 0)
 
94
        url = self._curl_handle.getinfo(pycurl.EFFECTIVE_URL)
 
95
        if url[:5] in ('http:', 'https:'):
 
96
            self._header.readline()
 
97
            m = mimetools.Message(self._header)
 
98
        else:
 
99
            m = mimetools.Message(StringIO())
 
100
        m['effective-url'] = url
 
101
        m['http-code'] = str(self._curl_handle.getinfo(pycurl.HTTP_CODE))
 
102
        # jam 20060617 These aren't used, so don't bother parsing them
 
103
        # m['total-time'] = str(self._curl_handle.getinfo(pycurl.TOTAL_TIME))
 
104
        # m['namelookup-time'] = str(self._curl_handle.getinfo(pycurl.NAMELOOKUP_TIME))
 
105
        # m['connect-time'] = str(self._curl_handle.getinfo(pycurl.CONNECT_TIME))
 
106
        # m['pretransfer-time'] = str(self._curl_handle.getinfo(pycurl.PRETRANSFER_TIME))
 
107
        # m['redirect-time'] = str(self._curl_handle.getinfo(pycurl.REDIRECT_TIME))
 
108
        # m['redirect-count'] = str(self._curl_handle.getinfo(pycurl.REDIRECT_COUNT))
 
109
        # m['size-upload'] = str(self._curl_handle.getinfo(pycurl.SIZE_UPLOAD))
 
110
        # m['size-download'] = str(self._curl_handle.getinfo(pycurl.SIZE_DOWNLOAD))
 
111
        # m['speed-upload'] = str(self._curl_handle.getinfo(pycurl.SPEED_UPLOAD))
 
112
        # m['header-size'] = str(self._curl_handle.getinfo(pycurl.HEADER_SIZE))
 
113
        # m['request-size'] = str(self._curl_handle.getinfo(pycurl.REQUEST_SIZE))
 
114
        # m['content-length-download'] = str(self._curl_handle.getinfo(pycurl.CONTENT_LENGTH_DOWNLOAD))
 
115
        # m['content-length-upload'] = str(self._curl_handle.getinfo(pycurl.CONTENT_LENGTH_UPLOAD))
 
116
 
 
117
        # Shouldn't this be 'Content-Type', I'm hoping that was already
 
118
        # parsed by the mimetools section.
 
119
        # m['content-type'] = (self._curl_handle.getinfo(pycurl.CONTENT_TYPE) or '').strip(';')
 
120
        self.headers = m
 
121
 
 
122
 
63
123
class PyCurlTransport(HttpTransportBase):
64
124
    """http client transport using pycurl
65
125
 
112
172
            curl = self._range_curl
113
173
 
114
174
        abspath = self._real_abspath(relpath)
115
 
        sio = StringIO()
116
 
        header_sio = StringIO()
117
175
        curl.setopt(pycurl.URL, abspath)
118
176
        self._set_curl_options(curl)
119
 
        curl.setopt(pycurl.WRITEFUNCTION, sio.write)
120
177
        curl.setopt(pycurl.NOBODY, 0)
121
 
        curl.setopt(pycurl.HEADERFUNCTION, header_sio.write)
 
178
 
 
179
        response = CurlResponse(curl)
122
180
 
123
181
        if ranges is not None:
124
 
            assert len(ranges) == 1
125
 
            # multiple ranges not supported yet because we can't decode the
126
 
            # response
127
 
            curl.setopt(pycurl.RANGE, '%d-%d' % ranges[0])
 
182
            curl.setopt(pycurl.RANGE, self._range_header(ranges))
128
183
        self._curl_perform(curl)
129
 
        info = self._get_header_info(header_sio, curl)
130
 
        code = info['http-code']
131
 
        if code == '404':
132
 
            raise NoSuchFile(abspath)
133
 
        elif code == '200':
134
 
            sio.seek(0)
135
 
            return code, sio
136
 
        elif code == '206' and (ranges is not None):
137
 
            sio.seek(0)
138
 
            return code, sio
139
 
        elif code == 0:
 
184
        response.update()
 
185
        if response.code == 0:
140
186
            self._raise_curl_connection_error(curl)
141
 
        else:
142
 
            self._raise_curl_http_error(curl)
143
 
 
144
 
    def _get_header_info(self, header, curl_handle):
145
 
        """Parse the headers into RFC822 format"""
146
 
 
147
 
        # TODO: jam 20060617 We probably don't need all of these
148
 
        #       headers, but this is what the 'curl' wrapper around
149
 
        #       pycurl does
150
 
        header.seek(0, 0)
151
 
        url = curl_handle.getinfo(pycurl.EFFECTIVE_URL)
152
 
        if url[:5] in ('http:', 'https:'):
153
 
            header.readline()
154
 
            m = mimetools.Message(header)
155
 
        else:
156
 
            m = mimetools.Message(StringIO())
157
 
        m['effective-url'] = url
158
 
        m['http-code'] = str(curl_handle.getinfo(pycurl.HTTP_CODE))
159
 
        m['total-time'] = str(curl_handle.getinfo(pycurl.TOTAL_TIME))
160
 
        m['namelookup-time'] = str(curl_handle.getinfo(pycurl.NAMELOOKUP_TIME))
161
 
        m['connect-time'] = str(curl_handle.getinfo(pycurl.CONNECT_TIME))
162
 
        m['pretransfer-time'] = str(curl_handle.getinfo(pycurl.PRETRANSFER_TIME))
163
 
        m['redirect-time'] = str(curl_handle.getinfo(pycurl.REDIRECT_TIME))
164
 
        m['redirect-count'] = str(curl_handle.getinfo(pycurl.REDIRECT_COUNT))
165
 
        m['size-upload'] = str(curl_handle.getinfo(pycurl.SIZE_UPLOAD))
166
 
        m['size-download'] = str(curl_handle.getinfo(pycurl.SIZE_DOWNLOAD))
167
 
        m['speed-upload'] = str(curl_handle.getinfo(pycurl.SPEED_UPLOAD))
168
 
        m['header-size'] = str(curl_handle.getinfo(pycurl.HEADER_SIZE))
169
 
        m['request-size'] = str(curl_handle.getinfo(pycurl.REQUEST_SIZE))
170
 
        m['content-length-download'] = str(curl_handle.getinfo(pycurl.CONTENT_LENGTH_DOWNLOAD))
171
 
        m['content-length-upload'] = str(curl_handle.getinfo(pycurl.CONTENT_LENGTH_UPLOAD))
172
 
        m['content-type'] = (curl_handle.getinfo(pycurl.CONTENT_TYPE) or '').strip(';')
173
 
        return m
174
 
 
 
187
        return response.code, self._handle_response(relpath, response)
175
188
 
176
189
    def _raise_curl_connection_error(self, curl):
177
190
        curl_errno = curl.getinfo(pycurl.OS_ERRNO)