##// END OF EJS Templates
httpclient: update to upstream revision 2995635573d2...
Augie Fackler -
r29131:8a66eda4 default
parent child Browse files
Show More
@@ -1,768 +1,842 b''
1 # Copyright 2010, Google Inc.
1 # Copyright 2010, Google Inc.
2 # All rights reserved.
2 # All rights reserved.
3 #
3 #
4 # Redistribution and use in source and binary forms, with or without
4 # Redistribution and use in source and binary forms, with or without
5 # modification, are permitted provided that the following conditions are
5 # modification, are permitted provided that the following conditions are
6 # met:
6 # met:
7 #
7 #
8 # * Redistributions of source code must retain the above copyright
8 # * Redistributions of source code must retain the above copyright
9 # notice, this list of conditions and the following disclaimer.
9 # notice, this list of conditions and the following disclaimer.
10 # * Redistributions in binary form must reproduce the above
10 # * Redistributions in binary form must reproduce the above
11 # copyright notice, this list of conditions and the following disclaimer
11 # copyright notice, this list of conditions and the following disclaimer
12 # in the documentation and/or other materials provided with the
12 # in the documentation and/or other materials provided with the
13 # distribution.
13 # distribution.
14 # * Neither the name of Google Inc. nor the names of its
14 # * Neither the name of Google Inc. nor the names of its
15 # contributors may be used to endorse or promote products derived from
15 # contributors may be used to endorse or promote products derived from
16 # this software without specific prior written permission.
16 # this software without specific prior written permission.
17
17
18 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 """Improved HTTP/1.1 client library
29 """Improved HTTP/1.1 client library
30
30
31 This library contains an HTTPConnection which is similar to the one in
31 This library contains an HTTPConnection which is similar to the one in
32 httplib, but has several additional features:
32 httplib, but has several additional features:
33
33
34 * supports keepalives natively
34 * supports keepalives natively
35 * uses select() to block for incoming data
35 * uses select() to block for incoming data
36 * notices when the server responds early to a request
36 * notices when the server responds early to a request
37 * implements ssl inline instead of in a different class
37 * implements ssl inline instead of in a different class
38 """
38 """
39 from __future__ import absolute_import
39 from __future__ import absolute_import
40
40
41 # Many functions in this file have too many arguments.
41 # Many functions in this file have too many arguments.
42 # pylint: disable=R0913
42 # pylint: disable=R0913
43
43
44 import cStringIO
45 import errno
44 import errno
46 import httplib
45 import inspect
47 import logging
46 import logging
48 import rfc822
47 import rfc822
49 import select
48 import select
50 import socket
49 import socket
51
50
51 try:
52 import cStringIO as io
53 io.StringIO
54 except ImportError:
55 import io
56
57 try:
58 import httplib
59 httplib.HTTPException
60 except ImportError:
61 import http.client as httplib
62
52 from . import (
63 from . import (
53 _readers,
64 _readers,
54 socketutil,
65 socketutil,
55 )
66 )
56
67
57 logger = logging.getLogger(__name__)
68 logger = logging.getLogger(__name__)
58
69
59 __all__ = ['HTTPConnection', 'HTTPResponse']
70 __all__ = ['HTTPConnection', 'HTTPResponse']
60
71
61 HTTP_VER_1_0 = 'HTTP/1.0'
72 HTTP_VER_1_0 = 'HTTP/1.0'
62 HTTP_VER_1_1 = 'HTTP/1.1'
73 HTTP_VER_1_1 = 'HTTP/1.1'
63
74
64 OUTGOING_BUFFER_SIZE = 1 << 15
75 OUTGOING_BUFFER_SIZE = 1 << 15
65 INCOMING_BUFFER_SIZE = 1 << 20
76 INCOMING_BUFFER_SIZE = 1 << 20
66
77
67 HDR_ACCEPT_ENCODING = 'accept-encoding'
78 HDR_ACCEPT_ENCODING = 'accept-encoding'
68 HDR_CONNECTION_CTRL = 'connection'
79 HDR_CONNECTION_CTRL = 'connection'
69 HDR_CONTENT_LENGTH = 'content-length'
80 HDR_CONTENT_LENGTH = 'content-length'
70 HDR_XFER_ENCODING = 'transfer-encoding'
81 HDR_XFER_ENCODING = 'transfer-encoding'
71
82
72 XFER_ENCODING_CHUNKED = 'chunked'
83 XFER_ENCODING_CHUNKED = 'chunked'
73
84
74 CONNECTION_CLOSE = 'close'
85 CONNECTION_CLOSE = 'close'
75
86
76 EOL = '\r\n'
87 EOL = '\r\n'
77 _END_HEADERS = EOL * 2
88 _END_HEADERS = EOL * 2
78
89
79 # Based on some searching around, 1 second seems like a reasonable
90 # Based on some searching around, 1 second seems like a reasonable
80 # default here.
91 # default here.
81 TIMEOUT_ASSUME_CONTINUE = 1
92 TIMEOUT_ASSUME_CONTINUE = 1
82 TIMEOUT_DEFAULT = None
93 TIMEOUT_DEFAULT = None
83
94
84
95
85 class HTTPResponse(object):
96 class HTTPResponse(object):
86 """Response from an HTTP server.
97 """Response from an HTTP server.
87
98
88 The response will continue to load as available. If you need the
99 The response will continue to load as available. If you need the
89 complete response before continuing, check the .complete() method.
100 complete response before continuing, check the .complete() method.
90 """
101 """
91 def __init__(self, sock, timeout, method):
102 def __init__(self, sock, timeout, method):
92 self.sock = sock
103 self.sock = sock
93 self.method = method
104 self.method = method
94 self.raw_response = ''
105 self.raw_response = ''
95 self._headers_len = 0
106 self._headers_len = 0
96 self.headers = None
107 self.headers = None
97 self.will_close = False
108 self.will_close = False
98 self.status_line = ''
109 self.status_line = ''
99 self.status = None
110 self.status = None
100 self.continued = False
111 self.continued = False
101 self.http_version = None
112 self.http_version = None
102 self.reason = None
113 self.reason = None
103 self._reader = None
114 self._reader = None
104
115
105 self._read_location = 0
116 self._read_location = 0
106 self._eol = EOL
117 self._eol = EOL
107
118
108 self._timeout = timeout
119 self._timeout = timeout
109
120
110 @property
121 @property
111 def _end_headers(self):
122 def _end_headers(self):
112 return self._eol * 2
123 return self._eol * 2
113
124
114 def complete(self):
125 def complete(self):
115 """Returns true if this response is completely loaded.
126 """Returns true if this response is completely loaded.
116
127
117 Note that if this is a connection where complete means the
128 Note that if this is a connection where complete means the
118 socket is closed, this will nearly always return False, even
129 socket is closed, this will nearly always return False, even
119 in cases where all the data has actually been loaded.
130 in cases where all the data has actually been loaded.
120 """
131 """
121 if self._reader:
132 if self._reader:
122 return self._reader.done()
133 return self._reader.done()
123
134
124 def _close(self):
135 def _close(self):
125 if self._reader is not None:
136 if self._reader is not None:
126 # We're a friend of the reader class here.
137 # We're a friend of the reader class here.
127 # pylint: disable=W0212
138 # pylint: disable=W0212
128 self._reader._close()
139 self._reader._close()
129
140
130 def getheader(self, header, default=None):
141 def getheader(self, header, default=None):
131 return self.headers.getheader(header, default=default)
142 return self.headers.getheader(header, default=default)
132
143
133 def getheaders(self):
144 def getheaders(self):
134 return self.headers.items()
145 return self.headers.items()
135
146
136 def readline(self):
147 def readline(self):
137 """Read a single line from the response body.
148 """Read a single line from the response body.
138
149
139 This may block until either a line ending is found or the
150 This may block until either a line ending is found or the
140 response is complete.
151 response is complete.
141 """
152 """
142 blocks = []
153 blocks = []
143 while True:
154 while True:
144 self._reader.readto('\n', blocks)
155 self._reader.readto('\n', blocks)
145
156
146 if blocks and blocks[-1][-1] == '\n' or self.complete():
157 if blocks and blocks[-1][-1] == '\n' or self.complete():
147 break
158 break
148
159
149 self._select()
160 self._select()
150
161
151 return ''.join(blocks)
162 return ''.join(blocks)
152
163
153 def read(self, length=None):
164 def read(self, length=None):
154 """Read data from the response body."""
165 """Read data from the response body."""
155 # if length is None, unbounded read
166 # if length is None, unbounded read
156 while (not self.complete() # never select on a finished read
167 while (not self.complete() # never select on a finished read
157 and (not length # unbounded, so we wait for complete()
168 and (not length # unbounded, so we wait for complete()
158 or length > self._reader.available_data)):
169 or length > self._reader.available_data)):
159 self._select()
170 self._select()
160 if not length:
171 if not length:
161 length = self._reader.available_data
172 length = self._reader.available_data
162 r = self._reader.read(length)
173 r = self._reader.read(length)
163 if self.complete() and self.will_close:
174 if self.complete() and self.will_close:
164 self.sock.close()
175 self.sock.close()
165 return r
176 return r
166
177
167 def _select(self):
178 def _select(self):
168 r, unused_write, unused_err = select.select(
179 r, unused_write, unused_err = select.select(
169 [self.sock], [], [], self._timeout)
180 [self.sock], [], [], self._timeout)
170 if not r:
181 if not r:
171 # socket was not readable. If the response is not
182 # socket was not readable. If the response is not
172 # complete, raise a timeout.
183 # complete, raise a timeout.
173 if not self.complete():
184 if not self.complete():
174 logger.info('timed out with timeout of %s', self._timeout)
185 logger.info('timed out with timeout of %s', self._timeout)
175 raise HTTPTimeoutException('timeout reading data')
186 raise HTTPTimeoutException('timeout reading data')
176 try:
187 try:
177 data = self.sock.recv(INCOMING_BUFFER_SIZE)
188 data = self.sock.recv(INCOMING_BUFFER_SIZE)
178 except socket.sslerror as e:
189 except socket.sslerror as e:
179 if e.args[0] != socket.SSL_ERROR_WANT_READ:
190 if e.args[0] != socket.SSL_ERROR_WANT_READ:
180 raise
191 raise
181 logger.debug('SSL_ERROR_WANT_READ in _select, should retry later')
192 logger.debug('SSL_ERROR_WANT_READ in _select, should retry later')
182 return True
193 return True
183 logger.debug('response read %d data during _select', len(data))
194 logger.debug('response read %d data during _select', len(data))
184 # If the socket was readable and no data was read, that means
195 # If the socket was readable and no data was read, that means
185 # the socket was closed. Inform the reader (if any) so it can
196 # the socket was closed. Inform the reader (if any) so it can
186 # raise an exception if this is an invalid situation.
197 # raise an exception if this is an invalid situation.
187 if not data:
198 if not data:
188 if self._reader:
199 if self._reader:
189 # We're a friend of the reader class here.
200 # We're a friend of the reader class here.
190 # pylint: disable=W0212
201 # pylint: disable=W0212
191 self._reader._close()
202 self._reader._close()
192 return False
203 return False
193 else:
204 else:
194 self._load_response(data)
205 self._load_response(data)
195 return True
206 return True
196
207
197 # This method gets replaced by _load later, which confuses pylint.
208 # This method gets replaced by _load later, which confuses pylint.
198 def _load_response(self, data): # pylint: disable=E0202
209 def _load_response(self, data): # pylint: disable=E0202
199 # Being here implies we're not at the end of the headers yet,
210 # Being here implies we're not at the end of the headers yet,
200 # since at the end of this method if headers were completely
211 # since at the end of this method if headers were completely
201 # loaded we replace this method with the load() method of the
212 # loaded we replace this method with the load() method of the
202 # reader we created.
213 # reader we created.
203 self.raw_response += data
214 self.raw_response += data
204 # This is a bogus server with bad line endings
215 # This is a bogus server with bad line endings
205 if self._eol not in self.raw_response:
216 if self._eol not in self.raw_response:
206 for bad_eol in ('\n', '\r'):
217 for bad_eol in ('\n', '\r'):
207 if (bad_eol in self.raw_response
218 if (bad_eol in self.raw_response
208 # verify that bad_eol is not the end of the incoming data
219 # verify that bad_eol is not the end of the incoming data
209 # as this could be a response line that just got
220 # as this could be a response line that just got
210 # split between \r and \n.
221 # split between \r and \n.
211 and (self.raw_response.index(bad_eol) <
222 and (self.raw_response.index(bad_eol) <
212 (len(self.raw_response) - 1))):
223 (len(self.raw_response) - 1))):
213 logger.info('bogus line endings detected, '
224 logger.info('bogus line endings detected, '
214 'using %r for EOL', bad_eol)
225 'using %r for EOL', bad_eol)
215 self._eol = bad_eol
226 self._eol = bad_eol
216 break
227 break
217 # exit early if not at end of headers
228 # exit early if not at end of headers
218 if self._end_headers not in self.raw_response or self.headers:
229 if self._end_headers not in self.raw_response or self.headers:
219 return
230 return
220
231
221 # handle 100-continue response
232 # handle 100-continue response
222 hdrs, body = self.raw_response.split(self._end_headers, 1)
233 hdrs, body = self.raw_response.split(self._end_headers, 1)
223 unused_http_ver, status = hdrs.split(' ', 1)
234 unused_http_ver, status = hdrs.split(' ', 1)
224 if status.startswith('100'):
235 if status.startswith('100'):
225 self.raw_response = body
236 self.raw_response = body
226 self.continued = True
237 self.continued = True
227 logger.debug('continue seen, setting body to %r', body)
238 logger.debug('continue seen, setting body to %r', body)
228 return
239 return
229
240
230 # arriving here means we should parse response headers
241 # arriving here means we should parse response headers
231 # as all headers have arrived completely
242 # as all headers have arrived completely
232 hdrs, body = self.raw_response.split(self._end_headers, 1)
243 hdrs, body = self.raw_response.split(self._end_headers, 1)
233 del self.raw_response
244 del self.raw_response
234 if self._eol in hdrs:
245 if self._eol in hdrs:
235 self.status_line, hdrs = hdrs.split(self._eol, 1)
246 self.status_line, hdrs = hdrs.split(self._eol, 1)
236 else:
247 else:
237 self.status_line = hdrs
248 self.status_line = hdrs
238 hdrs = ''
249 hdrs = ''
239 # TODO HTTP < 1.0 support
250 # TODO HTTP < 1.0 support
240 (self.http_version, self.status,
251 (self.http_version, self.status,
241 self.reason) = self.status_line.split(' ', 2)
252 self.reason) = self.status_line.split(' ', 2)
242 self.status = int(self.status)
253 self.status = int(self.status)
243 if self._eol != EOL:
254 if self._eol != EOL:
244 hdrs = hdrs.replace(self._eol, '\r\n')
255 hdrs = hdrs.replace(self._eol, '\r\n')
245 headers = rfc822.Message(cStringIO.StringIO(hdrs))
256 headers = rfc822.Message(io.StringIO(hdrs))
246 content_len = None
257 content_len = None
247 if HDR_CONTENT_LENGTH in headers:
258 if HDR_CONTENT_LENGTH in headers:
248 content_len = int(headers[HDR_CONTENT_LENGTH])
259 content_len = int(headers[HDR_CONTENT_LENGTH])
249 if self.http_version == HTTP_VER_1_0:
260 if self.http_version == HTTP_VER_1_0:
250 self.will_close = True
261 self.will_close = True
251 elif HDR_CONNECTION_CTRL in headers:
262 elif HDR_CONNECTION_CTRL in headers:
252 self.will_close = (
263 self.will_close = (
253 headers[HDR_CONNECTION_CTRL].lower() == CONNECTION_CLOSE)
264 headers[HDR_CONNECTION_CTRL].lower() == CONNECTION_CLOSE)
254 if (HDR_XFER_ENCODING in headers
265 if (HDR_XFER_ENCODING in headers
255 and headers[HDR_XFER_ENCODING].lower() == XFER_ENCODING_CHUNKED):
266 and headers[HDR_XFER_ENCODING].lower() == XFER_ENCODING_CHUNKED):
256 self._reader = _readers.ChunkedReader(self._eol)
267 self._reader = _readers.ChunkedReader(self._eol)
257 logger.debug('using a chunked reader')
268 logger.debug('using a chunked reader')
258 else:
269 else:
259 # HEAD responses are forbidden from returning a body, and
270 # HEAD responses are forbidden from returning a body, and
260 # it's implausible for a CONNECT response to use
271 # it's implausible for a CONNECT response to use
261 # close-is-end logic for an OK response.
272 # close-is-end logic for an OK response.
262 if (self.method == 'HEAD' or
273 if (self.method == 'HEAD' or
263 (self.method == 'CONNECT' and content_len is None)):
274 (self.method == 'CONNECT' and content_len is None)):
264 content_len = 0
275 content_len = 0
265 if content_len is not None:
276 if content_len is not None:
266 logger.debug('using a content-length reader with length %d',
277 logger.debug('using a content-length reader with length %d',
267 content_len)
278 content_len)
268 self._reader = _readers.ContentLengthReader(content_len)
279 self._reader = _readers.ContentLengthReader(content_len)
269 else:
280 else:
270 # Response body had no length specified and is not
281 # Response body had no length specified and is not
271 # chunked, so the end of the body will only be
282 # chunked, so the end of the body will only be
272 # identifiable by the termination of the socket by the
283 # identifiable by the termination of the socket by the
273 # server. My interpretation of the spec means that we
284 # server. My interpretation of the spec means that we
274 # are correct in hitting this case if
285 # are correct in hitting this case if
275 # transfer-encoding, content-length, and
286 # transfer-encoding, content-length, and
276 # connection-control were left unspecified.
287 # connection-control were left unspecified.
277 self._reader = _readers.CloseIsEndReader()
288 self._reader = _readers.CloseIsEndReader()
278 logger.debug('using a close-is-end reader')
289 logger.debug('using a close-is-end reader')
279 self.will_close = True
290 self.will_close = True
280
291
281 if body:
292 if body:
282 # We're a friend of the reader class here.
293 # We're a friend of the reader class here.
283 # pylint: disable=W0212
294 # pylint: disable=W0212
284 self._reader._load(body)
295 self._reader._load(body)
285 logger.debug('headers complete')
296 logger.debug('headers complete')
286 self.headers = headers
297 self.headers = headers
287 # We're a friend of the reader class here.
298 # We're a friend of the reader class here.
288 # pylint: disable=W0212
299 # pylint: disable=W0212
289 self._load_response = self._reader._load
300 self._load_response = self._reader._load
290
301
291 def _foldheaders(headers):
302 def _foldheaders(headers):
292 """Given some headers, rework them so we can safely overwrite values.
303 """Given some headers, rework them so we can safely overwrite values.
293
304
294 >>> _foldheaders({'Accept-Encoding': 'wat'})
305 >>> _foldheaders({'Accept-Encoding': 'wat'})
295 {'accept-encoding': ('Accept-Encoding', 'wat')}
306 {'accept-encoding': ('Accept-Encoding', 'wat')}
296 """
307 """
297 return dict((k.lower(), (k, v)) for k, v in headers.iteritems())
308 return dict((k.lower(), (k, v)) for k, v in headers.iteritems())
298
309
310 try:
311 inspect.signature
312 def _handlesarg(func, arg):
313 """ Try to determine if func accepts arg
314
315 If it takes arg, return True
316 If it happens to take **args, then it could do anything:
317 * It could throw a different TypeError, just for fun
318 * It could throw an ArgumentError or anything else
319 * It could choose not to throw an Exception at all
320 ... return 'unknown'
321
322 Otherwise, return False
323 """
324 params = inspect.signature(func).parameters
325 if arg in params:
326 return True
327 for p in params:
328 if params[p].kind == inspect._ParameterKind.VAR_KEYWORD:
329 return 'unknown'
330 return False
331 except AttributeError:
332 def _handlesarg(func, arg):
333 """ Try to determine if func accepts arg
334
335 If it takes arg, return True
336 If it happens to take **args, then it could do anything:
337 * It could throw a different TypeError, just for fun
338 * It could throw an ArgumentError or anything else
339 * It could choose not to throw an Exception at all
340 ... return 'unknown'
341
342 Otherwise, return False
343 """
344 spec = inspect.getargspec(func)
345 if arg in spec.args:
346 return True
347 if spec.keywords:
348 return 'unknown'
349 return False
299
350
300 class HTTPConnection(object):
351 class HTTPConnection(object):
301 """Connection to a single http server.
352 """Connection to a single http server.
302
353
303 Supports 100-continue and keepalives natively. Uses select() for
354 Supports 100-continue and keepalives natively. Uses select() for
304 non-blocking socket operations.
355 non-blocking socket operations.
305 """
356 """
306 http_version = HTTP_VER_1_1
357 http_version = HTTP_VER_1_1
307 response_class = HTTPResponse
358 response_class = HTTPResponse
308
359
309 def __init__(self, host, port=None, use_ssl=None, ssl_validator=None,
360 def __init__(self, host, port=None, use_ssl=None, ssl_validator=None,
310 timeout=TIMEOUT_DEFAULT,
361 timeout=TIMEOUT_DEFAULT,
311 continue_timeout=TIMEOUT_ASSUME_CONTINUE,
362 continue_timeout=TIMEOUT_ASSUME_CONTINUE,
312 proxy_hostport=None, proxy_headers=None,
363 proxy_hostport=None, proxy_headers=None,
313 ssl_wrap_socket=None, **ssl_opts):
364 ssl_wrap_socket=None, **ssl_opts):
314 """Create a new HTTPConnection.
365 """Create a new HTTPConnection.
315
366
316 Args:
367 Args:
317 host: The host to which we'll connect.
368 host: The host to which we'll connect.
318 port: Optional. The port over which we'll connect. Default 80 for
369 port: Optional. The port over which we'll connect. Default 80 for
319 non-ssl, 443 for ssl.
370 non-ssl, 443 for ssl.
320 use_ssl: Optional. Whether to use ssl. Defaults to False if port is
371 use_ssl: Optional. Whether to use ssl. Defaults to False if port is
321 not 443, true if port is 443.
372 not 443, true if port is 443.
322 ssl_validator: a function(socket) to validate the ssl cert
373 ssl_validator: a function(socket) to validate the ssl cert
323 timeout: Optional. Connection timeout, default is TIMEOUT_DEFAULT.
374 timeout: Optional. Connection timeout, default is TIMEOUT_DEFAULT.
324 continue_timeout: Optional. Timeout for waiting on an expected
375 continue_timeout: Optional. Timeout for waiting on an expected
325 "100 Continue" response. Default is TIMEOUT_ASSUME_CONTINUE.
376 "100 Continue" response. Default is TIMEOUT_ASSUME_CONTINUE.
326 proxy_hostport: Optional. Tuple of (host, port) to use as an http
377 proxy_hostport: Optional. Tuple of (host, port) to use as an http
327 proxy for the connection. Default is to not use a proxy.
378 proxy for the connection. Default is to not use a proxy.
328 proxy_headers: Optional dict of header keys and values to send to
379 proxy_headers: Optional dict of header keys and values to send to
329 a proxy when using CONNECT. For compatibility with
380 a proxy when using CONNECT. For compatibility with
330 httplib, the Proxy-Authorization header may be
381 httplib, the Proxy-Authorization header may be
331 specified in headers for request(), which will clobber
382 specified in headers for request(), which will clobber
332 any such header specified here if specified. Providing
383 any such header specified here if specified. Providing
333 this option and not proxy_hostport will raise an
384 this option and not proxy_hostport will raise an
334 ValueError.
385 ValueError.
335 ssl_wrap_socket: Optional function to use for wrapping
386 ssl_wrap_socket: Optional function to use for wrapping
336 sockets. If unspecified, the one from the ssl module will
387 sockets. If unspecified, the one from the ssl module will
337 be used if available, or something that's compatible with
388 be used if available, or something that's compatible with
338 it if on a Python older than 2.6.
389 it if on a Python older than 2.6.
339
390
340 Any extra keyword arguments to this function will be provided
391 Any extra keyword arguments to this function will be provided
341 to the ssl_wrap_socket method. If no ssl
392 to the ssl_wrap_socket method. If no ssl
342 """
393 """
343 if port is None and host.count(':') == 1 or ']:' in host:
394 if port is None and host.count(':') == 1 or ']:' in host:
344 host, port = host.rsplit(':', 1)
395 host, port = host.rsplit(':', 1)
345 port = int(port)
396 port = int(port)
346 if '[' in host:
397 if '[' in host:
347 host = host[1:-1]
398 host = host[1:-1]
348 if ssl_wrap_socket is not None:
399 if ssl_wrap_socket is not None:
349 self._ssl_wrap_socket = ssl_wrap_socket
400 _wrap_socket = ssl_wrap_socket
350 else:
401 else:
351 self._ssl_wrap_socket = socketutil.wrap_socket
402 _wrap_socket = socketutil.wrap_socket
403 call_wrap_socket = None
404 handlesubar = _handlesarg(_wrap_socket, 'server_hostname')
405 if handlesubar is True:
406 # supports server_hostname
407 call_wrap_socket = _wrap_socket
408 handlesnobar = _handlesarg(_wrap_socket, 'serverhostname')
409 if handlesnobar is True and handlesubar is not True:
410 # supports serverhostname
411 def call_wrap_socket(sock, server_hostname=None, **ssl_opts):
412 return _wrap_socket(sock, serverhostname=server_hostname,
413 **ssl_opts)
414 if handlesubar is False and handlesnobar is False:
415 # does not support either
416 def call_wrap_socket(sock, server_hostname=None, **ssl_opts):
417 return _wrap_socket(sock, **ssl_opts)
418 if call_wrap_socket is None:
419 # we assume it takes **args
420 def call_wrap_socket(sock, **ssl_opts):
421 if 'server_hostname' in ssl_opts:
422 ssl_opts['serverhostname'] = ssl_opts['server_hostname']
423 return _wrap_socket(sock, **ssl_opts)
424 self._ssl_wrap_socket = call_wrap_socket
352 if use_ssl is None and port is None:
425 if use_ssl is None and port is None:
353 use_ssl = False
426 use_ssl = False
354 port = 80
427 port = 80
355 elif use_ssl is None:
428 elif use_ssl is None:
356 use_ssl = (port == 443)
429 use_ssl = (port == 443)
357 elif port is None:
430 elif port is None:
358 port = (use_ssl and 443 or 80)
431 port = (use_ssl and 443 or 80)
359 self.port = port
432 self.port = port
360 if use_ssl and not socketutil.have_ssl:
433 if use_ssl and not socketutil.have_ssl:
361 raise Exception('ssl requested but unavailable on this Python')
434 raise Exception('ssl requested but unavailable on this Python')
362 self.ssl = use_ssl
435 self.ssl = use_ssl
363 self.ssl_opts = ssl_opts
436 self.ssl_opts = ssl_opts
364 self._ssl_validator = ssl_validator
437 self._ssl_validator = ssl_validator
365 self.host = host
438 self.host = host
366 self.sock = None
439 self.sock = None
367 self._current_response = None
440 self._current_response = None
368 self._current_response_taken = False
441 self._current_response_taken = False
369 if proxy_hostport is None:
442 if proxy_hostport is None:
370 self._proxy_host = self._proxy_port = None
443 self._proxy_host = self._proxy_port = None
371 if proxy_headers:
444 if proxy_headers:
372 raise ValueError(
445 raise ValueError(
373 'proxy_headers may not be specified unless '
446 'proxy_headers may not be specified unless '
374 'proxy_hostport is also specified.')
447 'proxy_hostport is also specified.')
375 else:
448 else:
376 self._proxy_headers = {}
449 self._proxy_headers = {}
377 else:
450 else:
378 self._proxy_host, self._proxy_port = proxy_hostport
451 self._proxy_host, self._proxy_port = proxy_hostport
379 self._proxy_headers = _foldheaders(proxy_headers or {})
452 self._proxy_headers = _foldheaders(proxy_headers or {})
380
453
381 self.timeout = timeout
454 self.timeout = timeout
382 self.continue_timeout = continue_timeout
455 self.continue_timeout = continue_timeout
383
456
384 def _connect(self, proxy_headers):
457 def _connect(self, proxy_headers):
385 """Connect to the host and port specified in __init__."""
458 """Connect to the host and port specified in __init__."""
386 if self.sock:
459 if self.sock:
387 return
460 return
388 if self._proxy_host is not None:
461 if self._proxy_host is not None:
389 logger.info('Connecting to http proxy %s:%s',
462 logger.info('Connecting to http proxy %s:%s',
390 self._proxy_host, self._proxy_port)
463 self._proxy_host, self._proxy_port)
391 sock = socketutil.create_connection((self._proxy_host,
464 sock = socketutil.create_connection((self._proxy_host,
392 self._proxy_port))
465 self._proxy_port))
393 if self.ssl:
466 if self.ssl:
394 data = self._buildheaders('CONNECT', '%s:%d' % (self.host,
467 data = self._buildheaders('CONNECT', '%s:%d' % (self.host,
395 self.port),
468 self.port),
396 proxy_headers, HTTP_VER_1_0)
469 proxy_headers, HTTP_VER_1_0)
397 sock.send(data)
470 sock.send(data)
398 sock.setblocking(0)
471 sock.setblocking(0)
399 r = self.response_class(sock, self.timeout, 'CONNECT')
472 r = self.response_class(sock, self.timeout, 'CONNECT')
400 timeout_exc = HTTPTimeoutException(
473 timeout_exc = HTTPTimeoutException(
401 'Timed out waiting for CONNECT response from proxy')
474 'Timed out waiting for CONNECT response from proxy')
402 while not r.complete():
475 while not r.complete():
403 try:
476 try:
404 # We're a friend of the response class, so let
477 # We're a friend of the response class, so let
405 # us use the private attribute.
478 # us use the private attribute.
406 # pylint: disable=W0212
479 # pylint: disable=W0212
407 if not r._select():
480 if not r._select():
408 if not r.complete():
481 if not r.complete():
409 raise timeout_exc
482 raise timeout_exc
410 except HTTPTimeoutException:
483 except HTTPTimeoutException:
411 # This raise/except pattern looks goofy, but
484 # This raise/except pattern looks goofy, but
412 # _select can raise the timeout as well as the
485 # _select can raise the timeout as well as the
413 # loop body. I wish it wasn't this convoluted,
486 # loop body. I wish it wasn't this convoluted,
414 # but I don't have a better solution
487 # but I don't have a better solution
415 # immediately handy.
488 # immediately handy.
416 raise timeout_exc
489 raise timeout_exc
417 if r.status != 200:
490 if r.status != 200:
418 raise HTTPProxyConnectFailedException(
491 raise HTTPProxyConnectFailedException(
419 'Proxy connection failed: %d %s' % (r.status,
492 'Proxy connection failed: %d %s' % (r.status,
420 r.read()))
493 r.read()))
421 logger.info('CONNECT (for SSL) to %s:%s via proxy succeeded.',
494 logger.info('CONNECT (for SSL) to %s:%s via proxy succeeded.',
422 self.host, self.port)
495 self.host, self.port)
423 else:
496 else:
424 sock = socketutil.create_connection((self.host, self.port))
497 sock = socketutil.create_connection((self.host, self.port))
425 if self.ssl:
498 if self.ssl:
426 # This is the default, but in the case of proxied SSL
499 # This is the default, but in the case of proxied SSL
427 # requests the proxy logic above will have cleared
500 # requests the proxy logic above will have cleared
428 # blocking mode, so re-enable it just to be safe.
501 # blocking mode, so re-enable it just to be safe.
429 sock.setblocking(1)
502 sock.setblocking(1)
430 logger.debug('wrapping socket for ssl with options %r',
503 logger.debug('wrapping socket for ssl with options %r',
431 self.ssl_opts)
504 self.ssl_opts)
432 sock = self._ssl_wrap_socket(sock, **self.ssl_opts)
505 sock = self._ssl_wrap_socket(sock, server_hostname=self.host,
506 **self.ssl_opts)
433 if self._ssl_validator:
507 if self._ssl_validator:
434 self._ssl_validator(sock)
508 self._ssl_validator(sock)
435 sock.setblocking(0)
509 sock.setblocking(0)
436 self.sock = sock
510 self.sock = sock
437
511
438 def _buildheaders(self, method, path, headers, http_ver):
512 def _buildheaders(self, method, path, headers, http_ver):
439 if self.ssl and self.port == 443 or self.port == 80:
513 if self.ssl and self.port == 443 or self.port == 80:
440 # default port for protocol, so leave it out
514 # default port for protocol, so leave it out
441 hdrhost = self.host
515 hdrhost = self.host
442 else:
516 else:
443 # include nonstandard port in header
517 # include nonstandard port in header
444 if ':' in self.host: # must be IPv6
518 if ':' in self.host: # must be IPv6
445 hdrhost = '[%s]:%d' % (self.host, self.port)
519 hdrhost = '[%s]:%d' % (self.host, self.port)
446 else:
520 else:
447 hdrhost = '%s:%d' % (self.host, self.port)
521 hdrhost = '%s:%d' % (self.host, self.port)
448 if self._proxy_host and not self.ssl:
522 if self._proxy_host and not self.ssl:
449 # When talking to a regular http proxy we must send the
523 # When talking to a regular http proxy we must send the
450 # full URI, but in all other cases we must not (although
524 # full URI, but in all other cases we must not (although
451 # technically RFC 2616 says servers must accept our
525 # technically RFC 2616 says servers must accept our
452 # request if we screw up, experimentally few do that
526 # request if we screw up, experimentally few do that
453 # correctly.)
527 # correctly.)
454 assert path[0] == '/', 'path must start with a /'
528 assert path[0] == '/', 'path must start with a /'
455 path = 'http://%s%s' % (hdrhost, path)
529 path = 'http://%s%s' % (hdrhost, path)
456 outgoing = ['%s %s %s%s' % (method, path, http_ver, EOL)]
530 outgoing = ['%s %s %s%s' % (method, path, http_ver, EOL)]
457 headers['host'] = ('Host', hdrhost)
531 headers['host'] = ('Host', hdrhost)
458 headers[HDR_ACCEPT_ENCODING] = (HDR_ACCEPT_ENCODING, 'identity')
532 headers[HDR_ACCEPT_ENCODING] = (HDR_ACCEPT_ENCODING, 'identity')
459 for hdr, val in headers.itervalues():
533 for hdr, val in headers.itervalues():
460 outgoing.append('%s: %s%s' % (hdr, val, EOL))
534 outgoing.append('%s: %s%s' % (hdr, val, EOL))
461 outgoing.append(EOL)
535 outgoing.append(EOL)
462 return ''.join(outgoing)
536 return ''.join(outgoing)
463
537
464 def close(self):
538 def close(self):
465 """Close the connection to the server.
539 """Close the connection to the server.
466
540
467 This is a no-op if the connection is already closed. The
541 This is a no-op if the connection is already closed. The
468 connection may automatically close if requested by the server
542 connection may automatically close if requested by the server
469 or required by the nature of a response.
543 or required by the nature of a response.
470 """
544 """
471 if self.sock is None:
545 if self.sock is None:
472 return
546 return
473 self.sock.close()
547 self.sock.close()
474 self.sock = None
548 self.sock = None
475 logger.info('closed connection to %s on %s', self.host, self.port)
549 logger.info('closed connection to %s on %s', self.host, self.port)
476
550
477 def busy(self):
551 def busy(self):
478 """Returns True if this connection object is currently in use.
552 """Returns True if this connection object is currently in use.
479
553
480 If a response is still pending, this will return True, even if
554 If a response is still pending, this will return True, even if
481 the request has finished sending. In the future,
555 the request has finished sending. In the future,
482 HTTPConnection may transparently juggle multiple connections
556 HTTPConnection may transparently juggle multiple connections
483 to the server, in which case this will be useful to detect if
557 to the server, in which case this will be useful to detect if
484 any of those connections is ready for use.
558 any of those connections is ready for use.
485 """
559 """
486 cr = self._current_response
560 cr = self._current_response
487 if cr is not None:
561 if cr is not None:
488 if self._current_response_taken:
562 if self._current_response_taken:
489 if cr.will_close:
563 if cr.will_close:
490 self.sock = None
564 self.sock = None
491 self._current_response = None
565 self._current_response = None
492 return False
566 return False
493 elif cr.complete():
567 elif cr.complete():
494 self._current_response = None
568 self._current_response = None
495 return False
569 return False
496 return True
570 return True
497 return False
571 return False
498
572
499 def _reconnect(self, where, pheaders):
573 def _reconnect(self, where, pheaders):
500 logger.info('reconnecting during %s', where)
574 logger.info('reconnecting during %s', where)
501 self.close()
575 self.close()
502 self._connect(pheaders)
576 self._connect(pheaders)
503
577
504 def request(self, method, path, body=None, headers={},
578 def request(self, method, path, body=None, headers={},
505 expect_continue=False):
579 expect_continue=False):
506 """Send a request to the server.
580 """Send a request to the server.
507
581
508 For increased flexibility, this does not return the response
582 For increased flexibility, this does not return the response
509 object. Future versions of HTTPConnection that juggle multiple
583 object. Future versions of HTTPConnection that juggle multiple
510 sockets will be able to send (for example) 5 requests all at
584 sockets will be able to send (for example) 5 requests all at
511 once, and then let the requests arrive as data is
585 once, and then let the requests arrive as data is
512 available. Use the `getresponse()` method to retrieve the
586 available. Use the `getresponse()` method to retrieve the
513 response.
587 response.
514 """
588 """
515 if self.busy():
589 if self.busy():
516 raise httplib.CannotSendRequest(
590 raise httplib.CannotSendRequest(
517 'Can not send another request before '
591 'Can not send another request before '
518 'current response is read!')
592 'current response is read!')
519 self._current_response_taken = False
593 self._current_response_taken = False
520
594
521 logger.info('sending %s request for %s to %s on port %s',
595 logger.info('sending %s request for %s to %s on port %s',
522 method, path, self.host, self.port)
596 method, path, self.host, self.port)
523 hdrs = _foldheaders(headers)
597 hdrs = _foldheaders(headers)
524 if hdrs.get('expect', ('', ''))[1].lower() == '100-continue':
598 if hdrs.get('expect', ('', ''))[1].lower() == '100-continue':
525 expect_continue = True
599 expect_continue = True
526 elif expect_continue:
600 elif expect_continue:
527 hdrs['expect'] = ('Expect', '100-Continue')
601 hdrs['expect'] = ('Expect', '100-Continue')
528 # httplib compatibility: if the user specified a
602 # httplib compatibility: if the user specified a
529 # proxy-authorization header, that's actually intended for a
603 # proxy-authorization header, that's actually intended for a
530 # proxy CONNECT action, not the real request, but only if
604 # proxy CONNECT action, not the real request, but only if
531 # we're going to use a proxy.
605 # we're going to use a proxy.
532 pheaders = dict(self._proxy_headers)
606 pheaders = dict(self._proxy_headers)
533 if self._proxy_host and self.ssl:
607 if self._proxy_host and self.ssl:
534 pa = hdrs.pop('proxy-authorization', None)
608 pa = hdrs.pop('proxy-authorization', None)
535 if pa is not None:
609 if pa is not None:
536 pheaders['proxy-authorization'] = pa
610 pheaders['proxy-authorization'] = pa
537
611
538 chunked = False
612 chunked = False
539 if body and HDR_CONTENT_LENGTH not in hdrs:
613 if body and HDR_CONTENT_LENGTH not in hdrs:
540 if getattr(body, '__len__', False):
614 if getattr(body, '__len__', False):
541 hdrs[HDR_CONTENT_LENGTH] = (HDR_CONTENT_LENGTH, len(body))
615 hdrs[HDR_CONTENT_LENGTH] = (HDR_CONTENT_LENGTH, len(body))
542 elif getattr(body, 'read', False):
616 elif getattr(body, 'read', False):
543 hdrs[HDR_XFER_ENCODING] = (HDR_XFER_ENCODING,
617 hdrs[HDR_XFER_ENCODING] = (HDR_XFER_ENCODING,
544 XFER_ENCODING_CHUNKED)
618 XFER_ENCODING_CHUNKED)
545 chunked = True
619 chunked = True
546 else:
620 else:
547 raise BadRequestData('body has no __len__() nor read()')
621 raise BadRequestData('body has no __len__() nor read()')
548
622
549 # If we're reusing the underlying socket, there are some
623 # If we're reusing the underlying socket, there are some
550 # conditions where we'll want to retry, so make a note of the
624 # conditions where we'll want to retry, so make a note of the
551 # state of self.sock
625 # state of self.sock
552 fresh_socket = self.sock is None
626 fresh_socket = self.sock is None
553 self._connect(pheaders)
627 self._connect(pheaders)
554 outgoing_headers = self._buildheaders(
628 outgoing_headers = self._buildheaders(
555 method, path, hdrs, self.http_version)
629 method, path, hdrs, self.http_version)
556 response = None
630 response = None
557 first = True
631 first = True
558
632
559 while ((outgoing_headers or body)
633 while ((outgoing_headers or body)
560 and not (response and response.complete())):
634 and not (response and response.complete())):
561 select_timeout = self.timeout
635 select_timeout = self.timeout
562 out = outgoing_headers or body
636 out = outgoing_headers or body
563 blocking_on_continue = False
637 blocking_on_continue = False
564 if expect_continue and not outgoing_headers and not (
638 if expect_continue and not outgoing_headers and not (
565 response and (response.headers or response.continued)):
639 response and (response.headers or response.continued)):
566 logger.info(
640 logger.info(
567 'waiting up to %s seconds for'
641 'waiting up to %s seconds for'
568 ' continue response from server',
642 ' continue response from server',
569 self.continue_timeout)
643 self.continue_timeout)
570 select_timeout = self.continue_timeout
644 select_timeout = self.continue_timeout
571 blocking_on_continue = True
645 blocking_on_continue = True
572 out = False
646 out = False
573 if out:
647 if out:
574 w = [self.sock]
648 w = [self.sock]
575 else:
649 else:
576 w = []
650 w = []
577 r, w, x = select.select([self.sock], w, [], select_timeout)
651 r, w, x = select.select([self.sock], w, [], select_timeout)
578 # if we were expecting a 100 continue and it's been long
652 # if we were expecting a 100 continue and it's been long
579 # enough, just go ahead and assume it's ok. This is the
653 # enough, just go ahead and assume it's ok. This is the
580 # recommended behavior from the RFC.
654 # recommended behavior from the RFC.
581 if r == w == x == []:
655 if r == w == x == []:
582 if blocking_on_continue:
656 if blocking_on_continue:
583 expect_continue = False
657 expect_continue = False
584 logger.info('no response to continue expectation from '
658 logger.info('no response to continue expectation from '
585 'server, optimistically sending request body')
659 'server, optimistically sending request body')
586 else:
660 else:
587 raise HTTPTimeoutException('timeout sending data')
661 raise HTTPTimeoutException('timeout sending data')
588 was_first = first
662 was_first = first
589
663
590 # incoming data
664 # incoming data
591 if r:
665 if r:
592 try:
666 try:
593 try:
667 try:
594 data = r[0].recv(INCOMING_BUFFER_SIZE)
668 data = r[0].recv(INCOMING_BUFFER_SIZE)
595 except socket.sslerror as e:
669 except socket.sslerror as e:
596 if e.args[0] != socket.SSL_ERROR_WANT_READ:
670 if e.args[0] != socket.SSL_ERROR_WANT_READ:
597 raise
671 raise
598 logger.debug('SSL_ERROR_WANT_READ while sending '
672 logger.debug('SSL_ERROR_WANT_READ while sending '
599 'data, retrying...')
673 'data, retrying...')
600 continue
674 continue
601 if not data:
675 if not data:
602 logger.info('socket appears closed in read')
676 logger.info('socket appears closed in read')
603 self.sock = None
677 self.sock = None
604 self._current_response = None
678 self._current_response = None
605 if response is not None:
679 if response is not None:
606 # We're a friend of the response class, so let
680 # We're a friend of the response class, so let
607 # us use the private attribute.
681 # us use the private attribute.
608 # pylint: disable=W0212
682 # pylint: disable=W0212
609 response._close()
683 response._close()
610 # This if/elif ladder is a bit subtle,
684 # This if/elif ladder is a bit subtle,
611 # comments in each branch should help.
685 # comments in each branch should help.
612 if response is not None and response.complete():
686 if response is not None and response.complete():
613 # Server responded completely and then
687 # Server responded completely and then
614 # closed the socket. We should just shut
688 # closed the socket. We should just shut
615 # things down and let the caller get their
689 # things down and let the caller get their
616 # response.
690 # response.
617 logger.info('Got an early response, '
691 logger.info('Got an early response, '
618 'aborting remaining request.')
692 'aborting remaining request.')
619 break
693 break
620 elif was_first and response is None:
694 elif was_first and response is None:
621 # Most likely a keepalive that got killed
695 # Most likely a keepalive that got killed
622 # on the server's end. Commonly happens
696 # on the server's end. Commonly happens
623 # after getting a really large response
697 # after getting a really large response
624 # from the server.
698 # from the server.
625 logger.info(
699 logger.info(
626 'Connection appeared closed in read on first'
700 'Connection appeared closed in read on first'
627 ' request loop iteration, will retry.')
701 ' request loop iteration, will retry.')
628 self._reconnect('read', pheaders)
702 self._reconnect('read', pheaders)
629 continue
703 continue
630 else:
704 else:
631 # We didn't just send the first data hunk,
705 # We didn't just send the first data hunk,
632 # and either have a partial response or no
706 # and either have a partial response or no
633 # response at all. There's really nothing
707 # response at all. There's really nothing
634 # meaningful we can do here.
708 # meaningful we can do here.
635 raise HTTPStateError(
709 raise HTTPStateError(
636 'Connection appears closed after '
710 'Connection appears closed after '
637 'some request data was written, but the '
711 'some request data was written, but the '
638 'response was missing or incomplete!')
712 'response was missing or incomplete!')
639 logger.debug('read %d bytes in request()', len(data))
713 logger.debug('read %d bytes in request()', len(data))
640 if response is None:
714 if response is None:
641 response = self.response_class(
715 response = self.response_class(
642 r[0], self.timeout, method)
716 r[0], self.timeout, method)
643 # We're a friend of the response class, so let us
717 # We're a friend of the response class, so let us
644 # use the private attribute.
718 # use the private attribute.
645 # pylint: disable=W0212
719 # pylint: disable=W0212
646 response._load_response(data)
720 response._load_response(data)
647 # Jump to the next select() call so we load more
721 # Jump to the next select() call so we load more
648 # data if the server is still sending us content.
722 # data if the server is still sending us content.
649 continue
723 continue
650 except socket.error as e:
724 except socket.error as e:
651 if e[0] != errno.EPIPE and not was_first:
725 if e[0] != errno.EPIPE and not was_first:
652 raise
726 raise
653
727
654 # outgoing data
728 # outgoing data
655 if w and out:
729 if w and out:
656 try:
730 try:
657 if getattr(out, 'read', False):
731 if getattr(out, 'read', False):
658 # pylint guesses the type of out incorrectly here
732 # pylint guesses the type of out incorrectly here
659 # pylint: disable=E1103
733 # pylint: disable=E1103
660 data = out.read(OUTGOING_BUFFER_SIZE)
734 data = out.read(OUTGOING_BUFFER_SIZE)
661 if not data:
735 if not data:
662 continue
736 continue
663 if len(data) < OUTGOING_BUFFER_SIZE:
737 if len(data) < OUTGOING_BUFFER_SIZE:
664 if chunked:
738 if chunked:
665 body = '0' + EOL + EOL
739 body = '0' + EOL + EOL
666 else:
740 else:
667 body = None
741 body = None
668 if chunked:
742 if chunked:
669 out = hex(len(data))[2:] + EOL + data + EOL
743 out = hex(len(data))[2:] + EOL + data + EOL
670 else:
744 else:
671 out = data
745 out = data
672 amt = w[0].send(out)
746 amt = w[0].send(out)
673 except socket.error as e:
747 except socket.error as e:
674 if e[0] == socket.SSL_ERROR_WANT_WRITE and self.ssl:
748 if e[0] == socket.SSL_ERROR_WANT_WRITE and self.ssl:
675 # This means that SSL hasn't flushed its buffer into
749 # This means that SSL hasn't flushed its buffer into
676 # the socket yet.
750 # the socket yet.
677 # TODO: find a way to block on ssl flushing its buffer
751 # TODO: find a way to block on ssl flushing its buffer
678 # similar to selecting on a raw socket.
752 # similar to selecting on a raw socket.
679 continue
753 continue
680 if e[0] == errno.EWOULDBLOCK or e[0] == errno.EAGAIN:
754 if e[0] == errno.EWOULDBLOCK or e[0] == errno.EAGAIN:
681 continue
755 continue
682 elif (e[0] not in (errno.ECONNRESET, errno.EPIPE)
756 elif (e[0] not in (errno.ECONNRESET, errno.EPIPE)
683 and not first):
757 and not first):
684 raise
758 raise
685 self._reconnect('write', pheaders)
759 self._reconnect('write', pheaders)
686 amt = self.sock.send(out)
760 amt = self.sock.send(out)
687 logger.debug('sent %d', amt)
761 logger.debug('sent %d', amt)
688 first = False
762 first = False
689 if out is body:
763 if out is body:
690 body = out[amt:]
764 body = out[amt:]
691 else:
765 else:
692 outgoing_headers = out[amt:]
766 outgoing_headers = out[amt:]
693
767
694 # close if the server response said to or responded before eating
768 # close if the server response said to or responded before eating
695 # the whole request
769 # the whole request
696 if response is None:
770 if response is None:
697 response = self.response_class(self.sock, self.timeout, method)
771 response = self.response_class(self.sock, self.timeout, method)
698 if not fresh_socket:
772 if not fresh_socket:
699 if not response._select():
773 if not response._select():
700 # This means the response failed to get any response
774 # This means the response failed to get any response
701 # data at all, and in all probability the socket was
775 # data at all, and in all probability the socket was
702 # closed before the server even saw our request. Try
776 # closed before the server even saw our request. Try
703 # the request again on a fresh socket.
777 # the request again on a fresh socket.
704 logger.debug('response._select() failed during request().'
778 logger.debug('response._select() failed during request().'
705 ' Assuming request needs to be retried.')
779 ' Assuming request needs to be retried.')
706 self.sock = None
780 self.sock = None
707 # Call this method explicitly to re-try the
781 # Call this method explicitly to re-try the
708 # request. We don't use self.request() because
782 # request. We don't use self.request() because
709 # some tools (notably Mercurial) expect to be able
783 # some tools (notably Mercurial) expect to be able
710 # to subclass and redefine request(), and they
784 # to subclass and redefine request(), and they
711 # don't have the same argspec as we do.
785 # don't have the same argspec as we do.
712 #
786 #
713 # TODO restructure sending of requests to avoid
787 # TODO restructure sending of requests to avoid
714 # this recursion
788 # this recursion
715 return HTTPConnection.request(
789 return HTTPConnection.request(
716 self, method, path, body=body, headers=headers,
790 self, method, path, body=body, headers=headers,
717 expect_continue=expect_continue)
791 expect_continue=expect_continue)
718 data_left = bool(outgoing_headers or body)
792 data_left = bool(outgoing_headers or body)
719 if data_left:
793 if data_left:
720 logger.info('stopped sending request early, '
794 logger.info('stopped sending request early, '
721 'will close the socket to be safe.')
795 'will close the socket to be safe.')
722 response.will_close = True
796 response.will_close = True
723 if response.will_close:
797 if response.will_close:
724 # The socket will be closed by the response, so we disown
798 # The socket will be closed by the response, so we disown
725 # the socket
799 # the socket
726 self.sock = None
800 self.sock = None
727 self._current_response = response
801 self._current_response = response
728
802
729 def getresponse(self):
803 def getresponse(self):
730 """Returns the response to the most recent request."""
804 """Returns the response to the most recent request."""
731 if self._current_response is None:
805 if self._current_response is None:
732 raise httplib.ResponseNotReady()
806 raise httplib.ResponseNotReady()
733 r = self._current_response
807 r = self._current_response
734 while r.headers is None:
808 while r.headers is None:
735 # We're a friend of the response class, so let us use the
809 # We're a friend of the response class, so let us use the
736 # private attribute.
810 # private attribute.
737 # pylint: disable=W0212
811 # pylint: disable=W0212
738 if not r._select() and not r.complete():
812 if not r._select() and not r.complete():
739 raise _readers.HTTPRemoteClosedError()
813 raise _readers.HTTPRemoteClosedError()
740 if r.will_close:
814 if r.will_close:
741 self.sock = None
815 self.sock = None
742 self._current_response = None
816 self._current_response = None
743 elif r.complete():
817 elif r.complete():
744 self._current_response = None
818 self._current_response = None
745 else:
819 else:
746 self._current_response_taken = True
820 self._current_response_taken = True
747 return r
821 return r
748
822
749
823
750 class HTTPTimeoutException(httplib.HTTPException):
824 class HTTPTimeoutException(httplib.HTTPException):
751 """A timeout occurred while waiting on the server."""
825 """A timeout occurred while waiting on the server."""
752
826
753
827
754 class BadRequestData(httplib.HTTPException):
828 class BadRequestData(httplib.HTTPException):
755 """Request body object has neither __len__ nor read."""
829 """Request body object has neither __len__ nor read."""
756
830
757
831
758 class HTTPProxyConnectFailedException(httplib.HTTPException):
832 class HTTPProxyConnectFailedException(httplib.HTTPException):
759 """Connecting to the HTTP proxy failed."""
833 """Connecting to the HTTP proxy failed."""
760
834
761
835
762 class HTTPStateError(httplib.HTTPException):
836 class HTTPStateError(httplib.HTTPException):
763 """Invalid internal state encountered."""
837 """Invalid internal state encountered."""
764
838
765 # Forward this exception type from _readers since it needs to be part
839 # Forward this exception type from _readers since it needs to be part
766 # of the public API.
840 # of the public API.
767 HTTPRemoteClosedError = _readers.HTTPRemoteClosedError
841 HTTPRemoteClosedError = _readers.HTTPRemoteClosedError
768 # no-check-code
842 # no-check-code
@@ -1,234 +1,239 b''
1 # Copyright 2011, Google Inc.
1 # Copyright 2011, Google Inc.
2 # All rights reserved.
2 # All rights reserved.
3 #
3 #
4 # Redistribution and use in source and binary forms, with or without
4 # Redistribution and use in source and binary forms, with or without
5 # modification, are permitted provided that the following conditions are
5 # modification, are permitted provided that the following conditions are
6 # met:
6 # met:
7 #
7 #
8 # * Redistributions of source code must retain the above copyright
8 # * Redistributions of source code must retain the above copyright
9 # notice, this list of conditions and the following disclaimer.
9 # notice, this list of conditions and the following disclaimer.
10 # * Redistributions in binary form must reproduce the above
10 # * Redistributions in binary form must reproduce the above
11 # copyright notice, this list of conditions and the following disclaimer
11 # copyright notice, this list of conditions and the following disclaimer
12 # in the documentation and/or other materials provided with the
12 # in the documentation and/or other materials provided with the
13 # distribution.
13 # distribution.
14 # * Neither the name of Google Inc. nor the names of its
14 # * Neither the name of Google Inc. nor the names of its
15 # contributors may be used to endorse or promote products derived from
15 # contributors may be used to endorse or promote products derived from
16 # this software without specific prior written permission.
16 # this software without specific prior written permission.
17
17
18 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 """Reader objects to abstract out different body response types.
29 """Reader objects to abstract out different body response types.
30
30
31 This module is package-private. It is not expected that these will
31 This module is package-private. It is not expected that these will
32 have any clients outside of httpplus.
32 have any clients outside of httpplus.
33 """
33 """
34 from __future__ import absolute_import
34 from __future__ import absolute_import
35
35
36 import httplib
36 try:
37 import httplib
38 httplib.HTTPException
39 except ImportError:
40 import http.client as httplib
41
37 import logging
42 import logging
38
43
39 logger = logging.getLogger(__name__)
44 logger = logging.getLogger(__name__)
40
45
41
46
42 class ReadNotReady(Exception):
47 class ReadNotReady(Exception):
43 """Raised when read() is attempted but not enough data is loaded."""
48 """Raised when read() is attempted but not enough data is loaded."""
44
49
45
50
46 class HTTPRemoteClosedError(httplib.HTTPException):
51 class HTTPRemoteClosedError(httplib.HTTPException):
47 """The server closed the remote socket in the middle of a response."""
52 """The server closed the remote socket in the middle of a response."""
48
53
49
54
50 class AbstractReader(object):
55 class AbstractReader(object):
51 """Abstract base class for response readers.
56 """Abstract base class for response readers.
52
57
53 Subclasses must implement _load, and should implement _close if
58 Subclasses must implement _load, and should implement _close if
54 it's not an error for the server to close their socket without
59 it's not an error for the server to close their socket without
55 some termination condition being detected during _load.
60 some termination condition being detected during _load.
56 """
61 """
57 def __init__(self):
62 def __init__(self):
58 self._finished = False
63 self._finished = False
59 self._done_chunks = []
64 self._done_chunks = []
60 self.available_data = 0
65 self.available_data = 0
61
66
62 def _addchunk(self, data):
67 def _addchunk(self, data):
63 self._done_chunks.append(data)
68 self._done_chunks.append(data)
64 self.available_data += len(data)
69 self.available_data += len(data)
65
70
66 def _pushchunk(self, data):
71 def _pushchunk(self, data):
67 self._done_chunks.insert(0, data)
72 self._done_chunks.insert(0, data)
68 self.available_data += len(data)
73 self.available_data += len(data)
69
74
70 def _popchunk(self):
75 def _popchunk(self):
71 b = self._done_chunks.pop(0)
76 b = self._done_chunks.pop(0)
72 self.available_data -= len(b)
77 self.available_data -= len(b)
73
78
74 return b
79 return b
75
80
76 def done(self):
81 def done(self):
77 """Returns true if the response body is entirely read."""
82 """Returns true if the response body is entirely read."""
78 return self._finished
83 return self._finished
79
84
80 def read(self, amt):
85 def read(self, amt):
81 """Read amt bytes from the response body."""
86 """Read amt bytes from the response body."""
82 if self.available_data < amt and not self._finished:
87 if self.available_data < amt and not self._finished:
83 raise ReadNotReady()
88 raise ReadNotReady()
84 blocks = []
89 blocks = []
85 need = amt
90 need = amt
86 while self._done_chunks:
91 while self._done_chunks:
87 b = self._popchunk()
92 b = self._popchunk()
88 if len(b) > need:
93 if len(b) > need:
89 nb = b[:need]
94 nb = b[:need]
90 self._pushchunk(b[need:])
95 self._pushchunk(b[need:])
91 b = nb
96 b = nb
92 blocks.append(b)
97 blocks.append(b)
93 need -= len(b)
98 need -= len(b)
94 if need == 0:
99 if need == 0:
95 break
100 break
96 result = ''.join(blocks)
101 result = ''.join(blocks)
97 assert len(result) == amt or (self._finished and len(result) < amt)
102 assert len(result) == amt or (self._finished and len(result) < amt)
98
103
99 return result
104 return result
100
105
101 def readto(self, delimstr, blocks = None):
106 def readto(self, delimstr, blocks = None):
102 """return available data chunks up to the first one in which
107 """return available data chunks up to the first one in which
103 delimstr occurs. No data will be returned after delimstr --
108 delimstr occurs. No data will be returned after delimstr --
104 the chunk in which it occurs will be split and the remainder
109 the chunk in which it occurs will be split and the remainder
105 pushed back onto the available data queue. If blocks is
110 pushed back onto the available data queue. If blocks is
106 supplied chunks will be added to blocks, otherwise a new list
111 supplied chunks will be added to blocks, otherwise a new list
107 will be allocated.
112 will be allocated.
108 """
113 """
109 if blocks is None:
114 if blocks is None:
110 blocks = []
115 blocks = []
111
116
112 while self._done_chunks:
117 while self._done_chunks:
113 b = self._popchunk()
118 b = self._popchunk()
114 i = b.find(delimstr) + len(delimstr)
119 i = b.find(delimstr) + len(delimstr)
115 if i:
120 if i:
116 if i < len(b):
121 if i < len(b):
117 self._pushchunk(b[i:])
122 self._pushchunk(b[i:])
118 blocks.append(b[:i])
123 blocks.append(b[:i])
119 break
124 break
120 else:
125 else:
121 blocks.append(b)
126 blocks.append(b)
122
127
123 return blocks
128 return blocks
124
129
125 def _load(self, data): # pragma: no cover
130 def _load(self, data): # pragma: no cover
126 """Subclasses must implement this.
131 """Subclasses must implement this.
127
132
128 As data is available to be read out of this object, it should
133 As data is available to be read out of this object, it should
129 be placed into the _done_chunks list. Subclasses should not
134 be placed into the _done_chunks list. Subclasses should not
130 rely on data remaining in _done_chunks forever, as it may be
135 rely on data remaining in _done_chunks forever, as it may be
131 reaped if the client is parsing data as it comes in.
136 reaped if the client is parsing data as it comes in.
132 """
137 """
133 raise NotImplementedError
138 raise NotImplementedError
134
139
135 def _close(self):
140 def _close(self):
136 """Default implementation of close.
141 """Default implementation of close.
137
142
138 The default implementation assumes that the reader will mark
143 The default implementation assumes that the reader will mark
139 the response as finished on the _finished attribute once the
144 the response as finished on the _finished attribute once the
140 entire response body has been read. In the event that this is
145 entire response body has been read. In the event that this is
141 not true, the subclass should override the implementation of
146 not true, the subclass should override the implementation of
142 close (for example, close-is-end responses have to set
147 close (for example, close-is-end responses have to set
143 self._finished in the close handler.)
148 self._finished in the close handler.)
144 """
149 """
145 if not self._finished:
150 if not self._finished:
146 raise HTTPRemoteClosedError(
151 raise HTTPRemoteClosedError(
147 'server appears to have closed the socket mid-response')
152 'server appears to have closed the socket mid-response')
148
153
149
154
150 class AbstractSimpleReader(AbstractReader):
155 class AbstractSimpleReader(AbstractReader):
151 """Abstract base class for simple readers that require no response decoding.
156 """Abstract base class for simple readers that require no response decoding.
152
157
153 Examples of such responses are Connection: Close (close-is-end)
158 Examples of such responses are Connection: Close (close-is-end)
154 and responses that specify a content length.
159 and responses that specify a content length.
155 """
160 """
156 def _load(self, data):
161 def _load(self, data):
157 if data:
162 if data:
158 assert not self._finished, (
163 assert not self._finished, (
159 'tried to add data (%r) to a closed reader!' % data)
164 'tried to add data (%r) to a closed reader!' % data)
160 logger.debug('%s read an additional %d data',
165 logger.debug('%s read an additional %d data',
161 self.name, len(data)) # pylint: disable=E1101
166 self.name, len(data)) # pylint: disable=E1101
162 self._addchunk(data)
167 self._addchunk(data)
163
168
164
169
165 class CloseIsEndReader(AbstractSimpleReader):
170 class CloseIsEndReader(AbstractSimpleReader):
166 """Reader for responses that specify Connection: Close for length."""
171 """Reader for responses that specify Connection: Close for length."""
167 name = 'close-is-end'
172 name = 'close-is-end'
168
173
169 def _close(self):
174 def _close(self):
170 logger.info('Marking close-is-end reader as closed.')
175 logger.info('Marking close-is-end reader as closed.')
171 self._finished = True
176 self._finished = True
172
177
173
178
174 class ContentLengthReader(AbstractSimpleReader):
179 class ContentLengthReader(AbstractSimpleReader):
175 """Reader for responses that specify an exact content length."""
180 """Reader for responses that specify an exact content length."""
176 name = 'content-length'
181 name = 'content-length'
177
182
178 def __init__(self, amount):
183 def __init__(self, amount):
179 AbstractSimpleReader.__init__(self)
184 AbstractSimpleReader.__init__(self)
180 self._amount = amount
185 self._amount = amount
181 if amount == 0:
186 if amount == 0:
182 self._finished = True
187 self._finished = True
183 self._amount_seen = 0
188 self._amount_seen = 0
184
189
185 def _load(self, data):
190 def _load(self, data):
186 AbstractSimpleReader._load(self, data)
191 AbstractSimpleReader._load(self, data)
187 self._amount_seen += len(data)
192 self._amount_seen += len(data)
188 if self._amount_seen >= self._amount:
193 if self._amount_seen >= self._amount:
189 self._finished = True
194 self._finished = True
190 logger.debug('content-length read complete')
195 logger.debug('content-length read complete')
191
196
192
197
193 class ChunkedReader(AbstractReader):
198 class ChunkedReader(AbstractReader):
194 """Reader for chunked transfer encoding responses."""
199 """Reader for chunked transfer encoding responses."""
195 def __init__(self, eol):
200 def __init__(self, eol):
196 AbstractReader.__init__(self)
201 AbstractReader.__init__(self)
197 self._eol = eol
202 self._eol = eol
198 self._leftover_skip_amt = 0
203 self._leftover_skip_amt = 0
199 self._leftover_data = ''
204 self._leftover_data = ''
200
205
201 def _load(self, data):
206 def _load(self, data):
202 assert not self._finished, 'tried to add data to a closed reader!'
207 assert not self._finished, 'tried to add data to a closed reader!'
203 logger.debug('chunked read an additional %d data', len(data))
208 logger.debug('chunked read an additional %d data', len(data))
204 position = 0
209 position = 0
205 if self._leftover_data:
210 if self._leftover_data:
206 logger.debug(
211 logger.debug(
207 'chunked reader trying to finish block from leftover data')
212 'chunked reader trying to finish block from leftover data')
208 # TODO: avoid this string concatenation if possible
213 # TODO: avoid this string concatenation if possible
209 data = self._leftover_data + data
214 data = self._leftover_data + data
210 position = self._leftover_skip_amt
215 position = self._leftover_skip_amt
211 self._leftover_data = ''
216 self._leftover_data = ''
212 self._leftover_skip_amt = 0
217 self._leftover_skip_amt = 0
213 datalen = len(data)
218 datalen = len(data)
214 while position < datalen:
219 while position < datalen:
215 split = data.find(self._eol, position)
220 split = data.find(self._eol, position)
216 if split == -1:
221 if split == -1:
217 self._leftover_data = data
222 self._leftover_data = data
218 self._leftover_skip_amt = position
223 self._leftover_skip_amt = position
219 return
224 return
220 amt = int(data[position:split], base=16)
225 amt = int(data[position:split], base=16)
221 block_start = split + len(self._eol)
226 block_start = split + len(self._eol)
222 # If the whole data chunk plus the eol trailer hasn't
227 # If the whole data chunk plus the eol trailer hasn't
223 # loaded, we'll wait for the next load.
228 # loaded, we'll wait for the next load.
224 if block_start + amt + len(self._eol) > len(data):
229 if block_start + amt + len(self._eol) > len(data):
225 self._leftover_data = data
230 self._leftover_data = data
226 self._leftover_skip_amt = position
231 self._leftover_skip_amt = position
227 return
232 return
228 if amt == 0:
233 if amt == 0:
229 self._finished = True
234 self._finished = True
230 logger.debug('closing chunked reader due to chunk of length 0')
235 logger.debug('closing chunked reader due to chunk of length 0')
231 return
236 return
232 self._addchunk(data[block_start:block_start + amt])
237 self._addchunk(data[block_start:block_start + amt])
233 position = block_start + amt + len(self._eol)
238 position = block_start + amt + len(self._eol)
234 # no-check-code
239 # no-check-code
@@ -1,140 +1,141 b''
1 # Copyright 2010, Google Inc.
1 # Copyright 2010, Google Inc.
2 # All rights reserved.
2 # All rights reserved.
3 #
3 #
4 # Redistribution and use in source and binary forms, with or without
4 # Redistribution and use in source and binary forms, with or without
5 # modification, are permitted provided that the following conditions are
5 # modification, are permitted provided that the following conditions are
6 # met:
6 # met:
7 #
7 #
8 # * Redistributions of source code must retain the above copyright
8 # * Redistributions of source code must retain the above copyright
9 # notice, this list of conditions and the following disclaimer.
9 # notice, this list of conditions and the following disclaimer.
10 # * Redistributions in binary form must reproduce the above
10 # * Redistributions in binary form must reproduce the above
11 # copyright notice, this list of conditions and the following disclaimer
11 # copyright notice, this list of conditions and the following disclaimer
12 # in the documentation and/or other materials provided with the
12 # in the documentation and/or other materials provided with the
13 # distribution.
13 # distribution.
14 # * Neither the name of Google Inc. nor the names of its
14 # * Neither the name of Google Inc. nor the names of its
15 # contributors may be used to endorse or promote products derived from
15 # contributors may be used to endorse or promote products derived from
16 # this software without specific prior written permission.
16 # this software without specific prior written permission.
17
17
18 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 """Abstraction to simplify socket use for Python < 2.6
29 """Abstraction to simplify socket use for Python < 2.6
30
30
31 This will attempt to use the ssl module and the new
31 This will attempt to use the ssl module and the new
32 socket.create_connection method, but fall back to the old
32 socket.create_connection method, but fall back to the old
33 methods if those are unavailable.
33 methods if those are unavailable.
34 """
34 """
35 from __future__ import absolute_import
35 from __future__ import absolute_import
36
36
37 import logging
37 import logging
38 import socket
38 import socket
39
39
40 logger = logging.getLogger(__name__)
40 logger = logging.getLogger(__name__)
41
41
42 try:
42 try:
43 import ssl
43 import ssl
44 # make demandimporters load the module
44 # make demandimporters load the module
45 ssl.wrap_socket # pylint: disable=W0104
45 ssl.wrap_socket # pylint: disable=W0104
46 have_ssl = True
46 have_ssl = True
47 except ImportError:
47 except ImportError:
48 import httplib
48 import httplib
49 import urllib2
49 import urllib2
50 have_ssl = getattr(urllib2, 'HTTPSHandler', False)
50 have_ssl = getattr(urllib2, 'HTTPSHandler', False)
51 ssl = False
51 ssl = False
52
52
53
53
54 try:
54 try:
55 create_connection = socket.create_connection
55 create_connection = socket.create_connection
56 except AttributeError:
56 except AttributeError:
57 def create_connection(address):
57 def create_connection(address):
58 """Backport of socket.create_connection from Python 2.6."""
58 """Backport of socket.create_connection from Python 2.6."""
59 host, port = address
59 host, port = address
60 msg = "getaddrinfo returns an empty list"
60 msg = "getaddrinfo returns an empty list"
61 sock = None
61 sock = None
62 for res in socket.getaddrinfo(host, port, 0,
62 for res in socket.getaddrinfo(host, port, 0,
63 socket.SOCK_STREAM):
63 socket.SOCK_STREAM):
64 af, socktype, proto, unused_canonname, sa = res
64 af, socktype, proto, unused_canonname, sa = res
65 try:
65 try:
66 sock = socket.socket(af, socktype, proto)
66 sock = socket.socket(af, socktype, proto)
67 logger.info("connect: (%s, %s)", host, port)
67 logger.info("connect: (%s, %s)", host, port)
68 sock.connect(sa)
68 sock.connect(sa)
69 except socket.error as msg:
69 except socket.error as msg:
70 logger.info('connect fail: %s %s', host, port)
70 logger.info('connect fail: %s %s', host, port)
71 if sock:
71 if sock:
72 sock.close()
72 sock.close()
73 sock = None
73 sock = None
74 continue
74 continue
75 break
75 break
76 if not sock:
76 if not sock:
77 raise socket.error(msg)
77 raise socket.error(msg)
78 return sock
78 return sock
79
79
80 if ssl:
80 if ssl:
81 wrap_socket = ssl.wrap_socket
81 wrap_socket = ssl.wrap_socket
82 CERT_NONE = ssl.CERT_NONE
82 CERT_NONE = ssl.CERT_NONE
83 CERT_OPTIONAL = ssl.CERT_OPTIONAL
83 CERT_OPTIONAL = ssl.CERT_OPTIONAL
84 CERT_REQUIRED = ssl.CERT_REQUIRED
84 CERT_REQUIRED = ssl.CERT_REQUIRED
85 else:
85 else:
86 class FakeSocket(httplib.FakeSocket):
86 class FakeSocket(httplib.FakeSocket):
87 """Socket wrapper that supports SSL."""
87 """Socket wrapper that supports SSL."""
88
88
89 # Silence lint about this goofy backport class
89 # Silence lint about this goofy backport class
90 # pylint: disable=W0232,E1101,R0903,R0913,C0111
90 # pylint: disable=W0232,E1101,R0903,R0913,C0111
91
91
92 # backport the behavior from Python 2.6, which is to busy wait
92 # backport the behavior from Python 2.6, which is to busy wait
93 # on the socket instead of anything nice. Sigh.
93 # on the socket instead of anything nice. Sigh.
94 # See http://bugs.python.org/issue3890 for more info.
94 # See http://bugs.python.org/issue3890 for more info.
95 def recv(self, buflen=1024, flags=0):
95 def recv(self, buflen=1024, flags=0):
96 """ssl-aware wrapper around socket.recv
96 """ssl-aware wrapper around socket.recv
97 """
97 """
98 if flags != 0:
98 if flags != 0:
99 raise ValueError(
99 raise ValueError(
100 "non-zero flags not allowed in calls to recv() on %s" %
100 "non-zero flags not allowed in calls to recv() on %s" %
101 self.__class__)
101 self.__class__)
102 while True:
102 while True:
103 try:
103 try:
104 return self._ssl.read(buflen)
104 return self._ssl.read(buflen)
105 except socket.sslerror as x:
105 except socket.sslerror as x:
106 if x.args[0] == socket.SSL_ERROR_WANT_READ:
106 if x.args[0] == socket.SSL_ERROR_WANT_READ:
107 continue
107 continue
108 else:
108 else:
109 raise x
109 raise x
110
110
111 _PROTOCOL_SSLv23 = 2
111 _PROTOCOL_SSLv23 = 2
112
112
113 CERT_NONE = 0
113 CERT_NONE = 0
114 CERT_OPTIONAL = 1
114 CERT_OPTIONAL = 1
115 CERT_REQUIRED = 2
115 CERT_REQUIRED = 2
116
116
117 # Disable unused-argument because we're making a dumb wrapper
117 # Disable unused-argument because we're making a dumb wrapper
118 # that's like an upstream method.
118 # that's like an upstream method.
119 #
119 #
120 # pylint: disable=W0613,R0913
120 # pylint: disable=W0613,R0913
121 def wrap_socket(sock, keyfile=None, certfile=None,
121 def wrap_socket(sock, keyfile=None, certfile=None,
122 server_side=False, cert_reqs=CERT_NONE,
122 server_side=False, cert_reqs=CERT_NONE,
123 ssl_version=_PROTOCOL_SSLv23, ca_certs=None,
123 ssl_version=_PROTOCOL_SSLv23, ca_certs=None,
124 do_handshake_on_connect=True,
124 do_handshake_on_connect=True,
125 suppress_ragged_eofs=True):
125 suppress_ragged_eofs=True,
126 server_hostname=None):
126 """Backport of ssl.wrap_socket from Python 2.6."""
127 """Backport of ssl.wrap_socket from Python 2.6."""
127 if cert_reqs != CERT_NONE and ca_certs:
128 if cert_reqs != CERT_NONE and ca_certs:
128 raise CertificateValidationUnsupported(
129 raise CertificateValidationUnsupported(
129 'SSL certificate validation requires the ssl module'
130 'SSL certificate validation requires the ssl module'
130 '(included in Python 2.6 and later.)')
131 '(included in Python 2.6 and later.)')
131 sslob = socket.ssl(sock)
132 sslob = socket.ssl(sock)
132 # borrow httplib's workaround for no ssl.wrap_socket
133 # borrow httplib's workaround for no ssl.wrap_socket
133 sock = FakeSocket(sock, sslob)
134 sock = FakeSocket(sock, sslob)
134 return sock
135 return sock
135 # pylint: enable=W0613,R0913
136 # pylint: enable=W0613,R0913
136
137
137
138
138 class CertificateValidationUnsupported(Exception):
139 class CertificateValidationUnsupported(Exception):
139 """Exception raised when cert validation is requested but unavailable."""
140 """Exception raised when cert validation is requested but unavailable."""
140 # no-check-code
141 # no-check-code
General Comments 0
You need to be logged in to leave comments. Login now