##// END OF EJS Templates
httpclient: upgrade to fe8c09e4db64 of httpplus
Augie Fackler -
r19182:fae47eca default
parent child Browse files
Show More
@@ -37,6 +37,9 b' httplib, but has several additional feat'
37 * implements ssl inline instead of in a different class
37 * implements ssl inline instead of in a different class
38 """
38 """
39
39
40 # Many functions in this file have too many arguments.
41 # pylint: disable=R0913
42
40 import cStringIO
43 import cStringIO
41 import errno
44 import errno
42 import httplib
45 import httplib
@@ -117,6 +120,8 b' class HTTPResponse(object):'
117
120
118 def _close(self):
121 def _close(self):
119 if self._reader is not None:
122 if self._reader is not None:
123 # We're a friend of the reader class here.
124 # pylint: disable=W0212
120 self._reader._close()
125 self._reader._close()
121
126
122 def readline(self):
127 def readline(self):
@@ -137,6 +142,7 b' class HTTPResponse(object):'
137 return ''.join(blocks)
142 return ''.join(blocks)
138
143
139 def read(self, length=None):
144 def read(self, length=None):
145 """Read data from the response body."""
140 # if length is None, unbounded read
146 # if length is None, unbounded read
141 while (not self.complete() # never select on a finished read
147 while (not self.complete() # never select on a finished read
142 and (not length # unbounded, so we wait for complete()
148 and (not length # unbounded, so we wait for complete()
@@ -150,7 +156,8 b' class HTTPResponse(object):'
150 return r
156 return r
151
157
152 def _select(self):
158 def _select(self):
153 r, _, _ = select.select([self.sock], [], [], self._timeout)
159 r, unused_write, unused_err = select.select(
160 [self.sock], [], [], self._timeout)
154 if not r:
161 if not r:
155 # socket was not readable. If the response is not
162 # socket was not readable. If the response is not
156 # complete, raise a timeout.
163 # complete, raise a timeout.
@@ -170,13 +177,16 b' class HTTPResponse(object):'
170 # raise an exception if this is an invalid situation.
177 # raise an exception if this is an invalid situation.
171 if not data:
178 if not data:
172 if self._reader:
179 if self._reader:
180 # We're a friend of the reader class here.
181 # pylint: disable=W0212
173 self._reader._close()
182 self._reader._close()
174 return False
183 return False
175 else:
184 else:
176 self._load_response(data)
185 self._load_response(data)
177 return True
186 return True
178
187
179 def _load_response(self, data):
188 # This method gets replaced by _load later, which confuses pylint.
189 def _load_response(self, data): # pylint: disable=E0202
180 # Being here implies we're not at the end of the headers yet,
190 # Being here implies we're not at the end of the headers yet,
181 # since at the end of this method if headers were completely
191 # since at the end of this method if headers were completely
182 # loaded we replace this method with the load() method of the
192 # loaded we replace this method with the load() method of the
@@ -201,7 +211,7 b' class HTTPResponse(object):'
201
211
202 # handle 100-continue response
212 # handle 100-continue response
203 hdrs, body = self.raw_response.split(self._end_headers, 1)
213 hdrs, body = self.raw_response.split(self._end_headers, 1)
204 http_ver, status = hdrs.split(' ', 1)
214 unused_http_ver, status = hdrs.split(' ', 1)
205 if status.startswith('100'):
215 if status.startswith('100'):
206 self.raw_response = body
216 self.raw_response = body
207 self.continued = True
217 self.continued = True
@@ -260,9 +270,13 b' class HTTPResponse(object):'
260 self.will_close = True
270 self.will_close = True
261
271
262 if body:
272 if body:
273 # We're a friend of the reader class here.
274 # pylint: disable=W0212
263 self._reader._load(body)
275 self._reader._load(body)
264 logger.debug('headers complete')
276 logger.debug('headers complete')
265 self.headers = headers
277 self.headers = headers
278 # We're a friend of the reader class here.
279 # pylint: disable=W0212
266 self._load_response = self._reader._load
280 self._load_response = self._reader._load
267
281
268
282
@@ -335,9 +349,9 b' class HTTPConnection(object):'
335 self._proxy_port))
349 self._proxy_port))
336 if self.ssl:
350 if self.ssl:
337 # TODO proxy header support
351 # TODO proxy header support
338 data = self.buildheaders('CONNECT', '%s:%d' % (self.host,
352 data = self._buildheaders('CONNECT', '%s:%d' % (self.host,
339 self.port),
353 self.port),
340 {}, HTTP_VER_1_0)
354 {}, HTTP_VER_1_0)
341 sock.send(data)
355 sock.send(data)
342 sock.setblocking(0)
356 sock.setblocking(0)
343 r = self.response_class(sock, self.timeout, 'CONNECT')
357 r = self.response_class(sock, self.timeout, 'CONNECT')
@@ -345,6 +359,9 b' class HTTPConnection(object):'
345 'Timed out waiting for CONNECT response from proxy')
359 'Timed out waiting for CONNECT response from proxy')
346 while not r.complete():
360 while not r.complete():
347 try:
361 try:
362 # We're a friend of the response class, so let
363 # us use the private attribute.
364 # pylint: disable=W0212
348 if not r._select():
365 if not r._select():
349 if not r.complete():
366 if not r.complete():
350 raise timeout_exc
367 raise timeout_exc
@@ -376,7 +393,7 b' class HTTPConnection(object):'
376 sock.setblocking(0)
393 sock.setblocking(0)
377 self.sock = sock
394 self.sock = sock
378
395
379 def buildheaders(self, method, path, headers, http_ver):
396 def _buildheaders(self, method, path, headers, http_ver):
380 if self.ssl and self.port == 443 or self.port == 80:
397 if self.ssl and self.port == 443 or self.port == 80:
381 # default port for protocol, so leave it out
398 # default port for protocol, so leave it out
382 hdrhost = self.host
399 hdrhost = self.host
@@ -437,6 +454,11 b' class HTTPConnection(object):'
437 return True
454 return True
438 return False
455 return False
439
456
457 def _reconnect(self, where):
458 logger.info('reconnecting during %s', where)
459 self.close()
460 self._connect()
461
440 def request(self, method, path, body=None, headers={},
462 def request(self, method, path, body=None, headers={},
441 expect_continue=False):
463 expect_continue=False):
442 """Send a request to the server.
464 """Send a request to the server.
@@ -474,16 +496,11 b' class HTTPConnection(object):'
474 raise BadRequestData('body has no __len__() nor read()')
496 raise BadRequestData('body has no __len__() nor read()')
475
497
476 self._connect()
498 self._connect()
477 outgoing_headers = self.buildheaders(
499 outgoing_headers = self._buildheaders(
478 method, path, hdrs, self.http_version)
500 method, path, hdrs, self.http_version)
479 response = None
501 response = None
480 first = True
502 first = True
481
503
482 def reconnect(where):
483 logger.info('reconnecting during %s', where)
484 self.close()
485 self._connect()
486
487 while ((outgoing_headers or body)
504 while ((outgoing_headers or body)
488 and not (response and response.complete())):
505 and not (response and response.complete())):
489 select_timeout = self.timeout
506 select_timeout = self.timeout
@@ -523,14 +540,17 b' class HTTPConnection(object):'
523 except socket.sslerror, e:
540 except socket.sslerror, e:
524 if e.args[0] != socket.SSL_ERROR_WANT_READ:
541 if e.args[0] != socket.SSL_ERROR_WANT_READ:
525 raise
542 raise
526 logger.debug(
543 logger.debug('SSL_ERROR_WANT_READ while sending '
527 'SSL_ERROR_WANT_READ while sending data, retrying...')
544 'data, retrying...')
528 continue
545 continue
529 if not data:
546 if not data:
530 logger.info('socket appears closed in read')
547 logger.info('socket appears closed in read')
531 self.sock = None
548 self.sock = None
532 self._current_response = None
549 self._current_response = None
533 if response is not None:
550 if response is not None:
551 # We're a friend of the response class, so let
552 # us use the private attribute.
553 # pylint: disable=W0212
534 response._close()
554 response._close()
535 # This if/elif ladder is a bit subtle,
555 # This if/elif ladder is a bit subtle,
536 # comments in each branch should help.
556 # comments in each branch should help.
@@ -550,7 +570,7 b' class HTTPConnection(object):'
550 logger.info(
570 logger.info(
551 'Connection appeared closed in read on first'
571 'Connection appeared closed in read on first'
552 ' request loop iteration, will retry.')
572 ' request loop iteration, will retry.')
553 reconnect('read')
573 self._reconnect('read')
554 continue
574 continue
555 else:
575 else:
556 # We didn't just send the first data hunk,
576 # We didn't just send the first data hunk,
@@ -563,7 +583,11 b' class HTTPConnection(object):'
563 'response was missing or incomplete!')
583 'response was missing or incomplete!')
564 logger.debug('read %d bytes in request()', len(data))
584 logger.debug('read %d bytes in request()', len(data))
565 if response is None:
585 if response is None:
566 response = self.response_class(r[0], self.timeout, method)
586 response = self.response_class(
587 r[0], self.timeout, method)
588 # We're a friend of the response class, so let us
589 # use the private attribute.
590 # pylint: disable=W0212
567 response._load_response(data)
591 response._load_response(data)
568 # Jump to the next select() call so we load more
592 # Jump to the next select() call so we load more
569 # data if the server is still sending us content.
593 # data if the server is still sending us content.
@@ -576,6 +600,8 b' class HTTPConnection(object):'
576 if w and out:
600 if w and out:
577 try:
601 try:
578 if getattr(out, 'read', False):
602 if getattr(out, 'read', False):
603 # pylint guesses the type of out incorrectly here
604 # pylint: disable=E1103
579 data = out.read(OUTGOING_BUFFER_SIZE)
605 data = out.read(OUTGOING_BUFFER_SIZE)
580 if not data:
606 if not data:
581 continue
607 continue
@@ -599,14 +625,10 b' class HTTPConnection(object):'
599 elif (e[0] not in (errno.ECONNRESET, errno.EPIPE)
625 elif (e[0] not in (errno.ECONNRESET, errno.EPIPE)
600 and not first):
626 and not first):
601 raise
627 raise
602 reconnect('write')
628 self._reconnect('write')
603 amt = self.sock.send(out)
629 amt = self.sock.send(out)
604 logger.debug('sent %d', amt)
630 logger.debug('sent %d', amt)
605 first = False
631 first = False
606 # stash data we think we sent in case the socket breaks
607 # when we read from it
608 if was_first:
609 sent_data = out[:amt]
610 if out is body:
632 if out is body:
611 body = out[amt:]
633 body = out[amt:]
612 else:
634 else:
@@ -616,7 +638,6 b' class HTTPConnection(object):'
616 # the whole request
638 # the whole request
617 if response is None:
639 if response is None:
618 response = self.response_class(self.sock, self.timeout, method)
640 response = self.response_class(self.sock, self.timeout, method)
619 complete = response.complete()
620 data_left = bool(outgoing_headers or body)
641 data_left = bool(outgoing_headers or body)
621 if data_left:
642 if data_left:
622 logger.info('stopped sending request early, '
643 logger.info('stopped sending request early, '
@@ -629,10 +650,14 b' class HTTPConnection(object):'
629 self._current_response = response
650 self._current_response = response
630
651
631 def getresponse(self):
652 def getresponse(self):
653 """Returns the response to the most recent request."""
632 if self._current_response is None:
654 if self._current_response is None:
633 raise httplib.ResponseNotReady()
655 raise httplib.ResponseNotReady()
634 r = self._current_response
656 r = self._current_response
635 while r.headers is None:
657 while r.headers is None:
658 # We're a friend of the response class, so let us use the
659 # private attribute.
660 # pylint: disable=W0212
636 if not r._select() and not r.complete():
661 if not r._select() and not r.complete():
637 raise _readers.HTTPRemoteClosedError()
662 raise _readers.HTTPRemoteClosedError()
638 if r.will_close:
663 if r.will_close:
@@ -33,7 +33,6 b' have any clients outside of httpplus.'
33 """
33 """
34
34
35 import httplib
35 import httplib
36 import itertools
37 import logging
36 import logging
38
37
39 logger = logging.getLogger(__name__)
38 logger = logging.getLogger(__name__)
@@ -59,33 +58,35 b' class AbstractReader(object):'
59 self._done_chunks = []
58 self._done_chunks = []
60 self.available_data = 0
59 self.available_data = 0
61
60
62 def addchunk(self, data):
61 def _addchunk(self, data):
63 self._done_chunks.append(data)
62 self._done_chunks.append(data)
64 self.available_data += len(data)
63 self.available_data += len(data)
65
64
66 def pushchunk(self, data):
65 def _pushchunk(self, data):
67 self._done_chunks.insert(0, data)
66 self._done_chunks.insert(0, data)
68 self.available_data += len(data)
67 self.available_data += len(data)
69
68
70 def popchunk(self):
69 def _popchunk(self):
71 b = self._done_chunks.pop(0)
70 b = self._done_chunks.pop(0)
72 self.available_data -= len(b)
71 self.available_data -= len(b)
73
72
74 return b
73 return b
75
74
76 def done(self):
75 def done(self):
76 """Returns true if the response body is entirely read."""
77 return self._finished
77 return self._finished
78
78
79 def read(self, amt):
79 def read(self, amt):
80 """Read amt bytes from the response body."""
80 if self.available_data < amt and not self._finished:
81 if self.available_data < amt and not self._finished:
81 raise ReadNotReady()
82 raise ReadNotReady()
82 blocks = []
83 blocks = []
83 need = amt
84 need = amt
84 while self._done_chunks:
85 while self._done_chunks:
85 b = self.popchunk()
86 b = self._popchunk()
86 if len(b) > need:
87 if len(b) > need:
87 nb = b[:need]
88 nb = b[:need]
88 self.pushchunk(b[need:])
89 self._pushchunk(b[need:])
89 b = nb
90 b = nb
90 blocks.append(b)
91 blocks.append(b)
91 need -= len(b)
92 need -= len(b)
@@ -107,11 +108,11 b' class AbstractReader(object):'
107 blocks = []
108 blocks = []
108
109
109 while self._done_chunks:
110 while self._done_chunks:
110 b = self.popchunk()
111 b = self._popchunk()
111 i = b.find(delimstr) + len(delimstr)
112 i = b.find(delimstr) + len(delimstr)
112 if i:
113 if i:
113 if i < len(b):
114 if i < len(b):
114 self.pushchunk(b[i:])
115 self._pushchunk(b[i:])
115 blocks.append(b[:i])
116 blocks.append(b[:i])
116 break
117 break
117 else:
118 else:
@@ -154,8 +155,9 b' class AbstractSimpleReader(AbstractReade'
154 if data:
155 if data:
155 assert not self._finished, (
156 assert not self._finished, (
156 'tried to add data (%r) to a closed reader!' % data)
157 'tried to add data (%r) to a closed reader!' % data)
157 logger.debug('%s read an additional %d data', self.name, len(data))
158 logger.debug('%s read an additional %d data',
158 self.addchunk(data)
159 self.name, len(data)) # pylint: disable=E1101
160 self._addchunk(data)
159
161
160
162
161 class CloseIsEndReader(AbstractSimpleReader):
163 class CloseIsEndReader(AbstractSimpleReader):
@@ -172,7 +174,7 b' class ContentLengthReader(AbstractSimple'
172 name = 'content-length'
174 name = 'content-length'
173
175
174 def __init__(self, amount):
176 def __init__(self, amount):
175 AbstractReader.__init__(self)
177 AbstractSimpleReader.__init__(self)
176 self._amount = amount
178 self._amount = amount
177 if amount == 0:
179 if amount == 0:
178 self._finished = True
180 self._finished = True
@@ -199,7 +201,8 b' class ChunkedReader(AbstractReader):'
199 logger.debug('chunked read an additional %d data', len(data))
201 logger.debug('chunked read an additional %d data', len(data))
200 position = 0
202 position = 0
201 if self._leftover_data:
203 if self._leftover_data:
202 logger.debug('chunked reader trying to finish block from leftover data')
204 logger.debug(
205 'chunked reader trying to finish block from leftover data')
203 # TODO: avoid this string concatenation if possible
206 # TODO: avoid this string concatenation if possible
204 data = self._leftover_data + data
207 data = self._leftover_data + data
205 position = self._leftover_skip_amt
208 position = self._leftover_skip_amt
@@ -224,6 +227,6 b' class ChunkedReader(AbstractReader):'
224 self._finished = True
227 self._finished = True
225 logger.debug('closing chunked reader due to chunk of length 0')
228 logger.debug('closing chunked reader due to chunk of length 0')
226 return
229 return
227 self.addchunk(data[block_start:block_start + amt])
230 self._addchunk(data[block_start:block_start + amt])
228 position = block_start + amt + len(self._eol)
231 position = block_start + amt + len(self._eol)
229 # no-check-code
232 # no-check-code
@@ -39,7 +39,8 b' logger = logging.getLogger(__name__)'
39
39
40 try:
40 try:
41 import ssl
41 import ssl
42 ssl.wrap_socket # make demandimporters load the module
42 # make demandimporters load the module
43 ssl.wrap_socket # pylint: disable=W0104
43 have_ssl = True
44 have_ssl = True
44 except ImportError:
45 except ImportError:
45 import httplib
46 import httplib
@@ -52,12 +53,13 b' try:'
52 create_connection = socket.create_connection
53 create_connection = socket.create_connection
53 except AttributeError:
54 except AttributeError:
54 def create_connection(address):
55 def create_connection(address):
56 """Backport of socket.create_connection from Python 2.6."""
55 host, port = address
57 host, port = address
56 msg = "getaddrinfo returns an empty list"
58 msg = "getaddrinfo returns an empty list"
57 sock = None
59 sock = None
58 for res in socket.getaddrinfo(host, port, 0,
60 for res in socket.getaddrinfo(host, port, 0,
59 socket.SOCK_STREAM):
61 socket.SOCK_STREAM):
60 af, socktype, proto, _canonname, sa = res
62 af, socktype, proto, unused_canonname, sa = res
61 try:
63 try:
62 sock = socket.socket(af, socktype, proto)
64 sock = socket.socket(af, socktype, proto)
63 logger.info("connect: (%s, %s)", host, port)
65 logger.info("connect: (%s, %s)", host, port)
@@ -80,8 +82,11 b' if ssl:'
80 CERT_REQUIRED = ssl.CERT_REQUIRED
82 CERT_REQUIRED = ssl.CERT_REQUIRED
81 else:
83 else:
82 class FakeSocket(httplib.FakeSocket):
84 class FakeSocket(httplib.FakeSocket):
83 """Socket wrapper that supports SSL.
85 """Socket wrapper that supports SSL."""
84 """
86
87 # Silence lint about this goofy backport class
88 # pylint: disable=W0232,E1101,R0903,R0913,C0111
89
85 # backport the behavior from Python 2.6, which is to busy wait
90 # backport the behavior from Python 2.6, which is to busy wait
86 # on the socket instead of anything nice. Sigh.
91 # on the socket instead of anything nice. Sigh.
87 # See http://bugs.python.org/issue3890 for more info.
92 # See http://bugs.python.org/issue3890 for more info.
@@ -107,11 +112,16 b' else:'
107 CERT_OPTIONAL = 1
112 CERT_OPTIONAL = 1
108 CERT_REQUIRED = 2
113 CERT_REQUIRED = 2
109
114
115 # Disable unused-argument because we're making a dumb wrapper
116 # that's like an upstream method.
117 #
118 # pylint: disable=W0613,R0913
110 def wrap_socket(sock, keyfile=None, certfile=None,
119 def wrap_socket(sock, keyfile=None, certfile=None,
111 server_side=False, cert_reqs=CERT_NONE,
120 server_side=False, cert_reqs=CERT_NONE,
112 ssl_version=_PROTOCOL_SSLv23, ca_certs=None,
121 ssl_version=_PROTOCOL_SSLv23, ca_certs=None,
113 do_handshake_on_connect=True,
122 do_handshake_on_connect=True,
114 suppress_ragged_eofs=True):
123 suppress_ragged_eofs=True):
124 """Backport of ssl.wrap_socket from Python 2.6."""
115 if cert_reqs != CERT_NONE and ca_certs:
125 if cert_reqs != CERT_NONE and ca_certs:
116 raise CertificateValidationUnsupported(
126 raise CertificateValidationUnsupported(
117 'SSL certificate validation requires the ssl module'
127 'SSL certificate validation requires the ssl module'
@@ -120,6 +130,7 b' else:'
120 # borrow httplib's workaround for no ssl.wrap_socket
130 # borrow httplib's workaround for no ssl.wrap_socket
121 sock = FakeSocket(sock, sslob)
131 sock = FakeSocket(sock, sslob)
122 return sock
132 return sock
133 # pylint: enable=W0613,R0913
123
134
124
135
125 class CertificateValidationUnsupported(Exception):
136 class CertificateValidationUnsupported(Exception):
General Comments 0
You need to be logged in to leave comments. Login now