##// END OF EJS Templates
httpclient: update to 54868ef054d2 of httpplus...
Augie Fackler -
r29442:456609cb default
parent child Browse files
Show More
@@ -40,13 +40,15 b' from __future__ import absolute_import'
40 40
41 41 # Many functions in this file have too many arguments.
42 42 # pylint: disable=R0913
43
43 import email
44 import email.message
44 45 import errno
45 46 import inspect
46 47 import logging
47 import rfc822
48 48 import select
49 49 import socket
50 import ssl
51 import sys
50 52
51 53 try:
52 54 import cStringIO as io
@@ -62,15 +64,14 b' except ImportError:'
62 64
63 65 from . import (
64 66 _readers,
65 socketutil,
66 67 )
67 68
68 69 logger = logging.getLogger(__name__)
69 70
70 71 __all__ = ['HTTPConnection', 'HTTPResponse']
71 72
72 HTTP_VER_1_0 = 'HTTP/1.0'
73 HTTP_VER_1_1 = 'HTTP/1.1'
73 HTTP_VER_1_0 = b'HTTP/1.0'
74 HTTP_VER_1_1 = b'HTTP/1.1'
74 75
75 76 OUTGOING_BUFFER_SIZE = 1 << 15
76 77 INCOMING_BUFFER_SIZE = 1 << 20
@@ -84,7 +85,7 b" XFER_ENCODING_CHUNKED = 'chunked'"
84 85
85 86 CONNECTION_CLOSE = 'close'
86 87
87 EOL = '\r\n'
88 EOL = b'\r\n'
88 89 _END_HEADERS = EOL * 2
89 90
90 91 # Based on some searching around, 1 second seems like a reasonable
@@ -92,6 +93,57 b" EOL = '\\r\\n'"
92 93 TIMEOUT_ASSUME_CONTINUE = 1
93 94 TIMEOUT_DEFAULT = None
94 95
96 if sys.version_info > (3, 0):
97 _unicode = str
98 else:
99 _unicode = unicode
100
101 def _ensurebytes(data):
102 if not isinstance(data, (_unicode, bytes)):
103 data = str(data)
104 if not isinstance(data, bytes):
105 try:
106 return data.encode('latin-1')
107 except UnicodeEncodeError as err:
108 raise UnicodeEncodeError(
109 err.encoding,
110 err.object,
111 err.start,
112 err.end,
113 '%r is not valid Latin-1 Use .encode("utf-8") '
114 'if sending as utf-8 is desired.' % (
115 data[err.start:err.end],))
116 return data
117
118 class _CompatMessage(email.message.Message):
119 """Workaround for rfc822.Message and email.message.Message API diffs."""
120
121 @classmethod
122 def from_string(cls, s):
123 if sys.version_info > (3, 0):
124 # Python 3 can't decode headers from bytes, so we have to
125 # trust RFC 2616 and decode the headers as iso-8859-1
126 # bytes.
127 s = s.decode('iso-8859-1')
128 headers = email.message_from_string(s, _class=_CompatMessage)
129 # Fix multi-line headers to match httplib's behavior from
130 # Python 2.x, since email.message.Message handles them in
131 # slightly different ways.
132 if sys.version_info < (3, 0):
133 new = []
134 for h, v in headers._headers:
135 if '\r\n' in v:
136 v = '\n'.join([' ' + x.lstrip() for x in v.split('\r\n')])[1:]
137 new.append((h, v))
138 headers._headers = new
139 return headers
140
141 def getheaders(self, key):
142 return self.get_all(key)
143
144 def getheader(self, key, default=None):
145 return self.get(key, failobj=default)
146
95 147
96 148 class HTTPResponse(object):
97 149 """Response from an HTTP server.
@@ -102,11 +154,11 b' class HTTPResponse(object):'
102 154 def __init__(self, sock, timeout, method):
103 155 self.sock = sock
104 156 self.method = method
105 self.raw_response = ''
157 self.raw_response = b''
106 158 self._headers_len = 0
107 159 self.headers = None
108 160 self.will_close = False
109 self.status_line = ''
161 self.status_line = b''
110 162 self.status = None
111 163 self.continued = False
112 164 self.http_version = None
@@ -142,6 +194,10 b' class HTTPResponse(object):'
142 194 return self.headers.getheader(header, default=default)
143 195
144 196 def getheaders(self):
197 if sys.version_info < (3, 0):
198 return [(k.lower(), v) for k, v in self.headers.items()]
199 # Starting in Python 3, headers aren't lowercased before being
200 # returned here.
145 201 return self.headers.items()
146 202
147 203 def readline(self):
@@ -152,14 +208,14 b' class HTTPResponse(object):'
152 208 """
153 209 blocks = []
154 210 while True:
155 self._reader.readto('\n', blocks)
211 self._reader.readto(b'\n', blocks)
156 212
157 if blocks and blocks[-1][-1] == '\n' or self.complete():
213 if blocks and blocks[-1][-1:] == b'\n' or self.complete():
158 214 break
159 215
160 216 self._select()
161 217
162 return ''.join(blocks)
218 return b''.join(blocks)
163 219
164 220 def read(self, length=None):
165 221 """Read data from the response body."""
@@ -186,8 +242,8 b' class HTTPResponse(object):'
186 242 raise HTTPTimeoutException('timeout reading data')
187 243 try:
188 244 data = self.sock.recv(INCOMING_BUFFER_SIZE)
189 except socket.sslerror as e:
190 if e.args[0] != socket.SSL_ERROR_WANT_READ:
245 except ssl.SSLError as e:
246 if e.args[0] != ssl.SSL_ERROR_WANT_READ:
191 247 raise
192 248 logger.debug('SSL_ERROR_WANT_READ in _select, should retry later')
193 249 return True
@@ -214,7 +270,7 b' class HTTPResponse(object):'
214 270 self.raw_response += data
215 271 # This is a bogus server with bad line endings
216 272 if self._eol not in self.raw_response:
217 for bad_eol in ('\n', '\r'):
273 for bad_eol in (b'\n', b'\r'):
218 274 if (bad_eol in self.raw_response
219 275 # verify that bad_eol is not the end of the incoming data
220 276 # as this could be a response line that just got
@@ -231,8 +287,8 b' class HTTPResponse(object):'
231 287
232 288 # handle 100-continue response
233 289 hdrs, body = self.raw_response.split(self._end_headers, 1)
234 unused_http_ver, status = hdrs.split(' ', 1)
235 if status.startswith('100'):
290 unused_http_ver, status = hdrs.split(b' ', 1)
291 if status.startswith(b'100'):
236 292 self.raw_response = body
237 293 self.continued = True
238 294 logger.debug('continue seen, setting body to %r', body)
@@ -246,14 +302,14 b' class HTTPResponse(object):'
246 302 self.status_line, hdrs = hdrs.split(self._eol, 1)
247 303 else:
248 304 self.status_line = hdrs
249 hdrs = ''
305 hdrs = b''
250 306 # TODO HTTP < 1.0 support
251 307 (self.http_version, self.status,
252 self.reason) = self.status_line.split(' ', 2)
308 self.reason) = self.status_line.split(b' ', 2)
253 309 self.status = int(self.status)
254 310 if self._eol != EOL:
255 hdrs = hdrs.replace(self._eol, '\r\n')
256 headers = rfc822.Message(io.StringIO(hdrs))
311 hdrs = hdrs.replace(self._eol, b'\r\n')
312 headers = _CompatMessage.from_string(hdrs)
257 313 content_len = None
258 314 if HDR_CONTENT_LENGTH in headers:
259 315 content_len = int(headers[HDR_CONTENT_LENGTH])
@@ -270,8 +326,8 b' class HTTPResponse(object):'
270 326 # HEAD responses are forbidden from returning a body, and
271 327 # it's implausible for a CONNECT response to use
272 328 # close-is-end logic for an OK response.
273 if (self.method == 'HEAD' or
274 (self.method == 'CONNECT' and content_len is None)):
329 if (self.method == b'HEAD' or
330 (self.method == b'CONNECT' and content_len is None)):
275 331 content_len = 0
276 332 if content_len is not None:
277 333 logger.debug('using a content-length reader with length %d',
@@ -305,7 +361,7 b' def _foldheaders(headers):'
305 361 >>> _foldheaders({'Accept-Encoding': 'wat'})
306 362 {'accept-encoding': ('Accept-Encoding', 'wat')}
307 363 """
308 return dict((k.lower(), (k, v)) for k, v in headers.iteritems())
364 return dict((k.lower(), (k, v)) for k, v in headers.items())
309 365
310 366 try:
311 367 inspect.signature
@@ -391,15 +447,16 b' class HTTPConnection(object):'
391 447 Any extra keyword arguments to this function will be provided
392 448 to the ssl_wrap_socket method. If no ssl
393 449 """
394 if port is None and host.count(':') == 1 or ']:' in host:
395 host, port = host.rsplit(':', 1)
450 host = _ensurebytes(host)
451 if port is None and host.count(b':') == 1 or b']:' in host:
452 host, port = host.rsplit(b':', 1)
396 453 port = int(port)
397 if '[' in host:
454 if b'[' in host:
398 455 host = host[1:-1]
399 456 if ssl_wrap_socket is not None:
400 457 _wrap_socket = ssl_wrap_socket
401 458 else:
402 _wrap_socket = socketutil.wrap_socket
459 _wrap_socket = ssl.wrap_socket
403 460 call_wrap_socket = None
404 461 handlesubar = _handlesarg(_wrap_socket, 'server_hostname')
405 462 if handlesubar is True:
@@ -430,8 +487,6 b' class HTTPConnection(object):'
430 487 elif port is None:
431 488 port = (use_ssl and 443 or 80)
432 489 self.port = port
433 if use_ssl and not socketutil.have_ssl:
434 raise Exception('ssl requested but unavailable on this Python')
435 490 self.ssl = use_ssl
436 491 self.ssl_opts = ssl_opts
437 492 self._ssl_validator = ssl_validator
@@ -461,15 +516,15 b' class HTTPConnection(object):'
461 516 if self._proxy_host is not None:
462 517 logger.info('Connecting to http proxy %s:%s',
463 518 self._proxy_host, self._proxy_port)
464 sock = socketutil.create_connection((self._proxy_host,
465 self._proxy_port))
519 sock = socket.create_connection((self._proxy_host,
520 self._proxy_port))
466 521 if self.ssl:
467 data = self._buildheaders('CONNECT', '%s:%d' % (self.host,
468 self.port),
522 data = self._buildheaders(b'CONNECT', b'%s:%d' % (self.host,
523 self.port),
469 524 proxy_headers, HTTP_VER_1_0)
470 525 sock.send(data)
471 526 sock.setblocking(0)
472 r = self.response_class(sock, self.timeout, 'CONNECT')
527 r = self.response_class(sock, self.timeout, b'CONNECT')
473 528 timeout_exc = HTTPTimeoutException(
474 529 'Timed out waiting for CONNECT response from proxy')
475 530 while not r.complete():
@@ -494,7 +549,7 b' class HTTPConnection(object):'
494 549 logger.info('CONNECT (for SSL) to %s:%s via proxy succeeded.',
495 550 self.host, self.port)
496 551 else:
497 sock = socketutil.create_connection((self.host, self.port))
552 sock = socket.create_connection((self.host, self.port))
498 553 if self.ssl:
499 554 # This is the default, but in the case of proxied SSL
500 555 # requests the proxy logic above will have cleared
@@ -515,25 +570,26 b' class HTTPConnection(object):'
515 570 hdrhost = self.host
516 571 else:
517 572 # include nonstandard port in header
518 if ':' in self.host: # must be IPv6
519 hdrhost = '[%s]:%d' % (self.host, self.port)
573 if b':' in self.host: # must be IPv6
574 hdrhost = b'[%s]:%d' % (self.host, self.port)
520 575 else:
521 hdrhost = '%s:%d' % (self.host, self.port)
576 hdrhost = b'%s:%d' % (self.host, self.port)
522 577 if self._proxy_host and not self.ssl:
523 578 # When talking to a regular http proxy we must send the
524 579 # full URI, but in all other cases we must not (although
525 580 # technically RFC 2616 says servers must accept our
526 581 # request if we screw up, experimentally few do that
527 582 # correctly.)
528 assert path[0] == '/', 'path must start with a /'
529 path = 'http://%s%s' % (hdrhost, path)
530 outgoing = ['%s %s %s%s' % (method, path, http_ver, EOL)]
531 headers['host'] = ('Host', hdrhost)
583 assert path[0:1] == b'/', 'path must start with a /'
584 path = b'http://%s%s' % (hdrhost, path)
585 outgoing = [b'%s %s %s%s' % (method, path, http_ver, EOL)]
586 headers[b'host'] = (b'Host', hdrhost)
532 587 headers[HDR_ACCEPT_ENCODING] = (HDR_ACCEPT_ENCODING, 'identity')
533 for hdr, val in headers.itervalues():
534 outgoing.append('%s: %s%s' % (hdr, val, EOL))
588 for hdr, val in sorted((_ensurebytes(h), _ensurebytes(v))
589 for h, v in headers.values()):
590 outgoing.append(b'%s: %s%s' % (hdr, val, EOL))
535 591 outgoing.append(EOL)
536 return ''.join(outgoing)
592 return b''.join(outgoing)
537 593
538 594 def close(self):
539 595 """Close the connection to the server.
@@ -586,6 +642,8 b' class HTTPConnection(object):'
586 642 available. Use the `getresponse()` method to retrieve the
587 643 response.
588 644 """
645 method = _ensurebytes(method)
646 path = _ensurebytes(path)
589 647 if self.busy():
590 648 raise httplib.CannotSendRequest(
591 649 'Can not send another request before '
@@ -594,11 +652,26 b' class HTTPConnection(object):'
594 652
595 653 logger.info('sending %s request for %s to %s on port %s',
596 654 method, path, self.host, self.port)
655
597 656 hdrs = _foldheaders(headers)
598 if hdrs.get('expect', ('', ''))[1].lower() == '100-continue':
657 # Figure out headers that have to be computed from the request
658 # body.
659 chunked = False
660 if body and HDR_CONTENT_LENGTH not in hdrs:
661 if getattr(body, '__len__', False):
662 hdrs[HDR_CONTENT_LENGTH] = (HDR_CONTENT_LENGTH,
663 b'%d' % len(body))
664 elif getattr(body, 'read', False):
665 hdrs[HDR_XFER_ENCODING] = (HDR_XFER_ENCODING,
666 XFER_ENCODING_CHUNKED)
667 chunked = True
668 else:
669 raise BadRequestData('body has no __len__() nor read()')
670 # Figure out expect-continue header
671 if hdrs.get('expect', ('', ''))[1].lower() == b'100-continue':
599 672 expect_continue = True
600 673 elif expect_continue:
601 hdrs['expect'] = ('Expect', '100-Continue')
674 hdrs['expect'] = (b'Expect', b'100-Continue')
602 675 # httplib compatibility: if the user specified a
603 676 # proxy-authorization header, that's actually intended for a
604 677 # proxy CONNECT action, not the real request, but only if
@@ -608,25 +681,15 b' class HTTPConnection(object):'
608 681 pa = hdrs.pop('proxy-authorization', None)
609 682 if pa is not None:
610 683 pheaders['proxy-authorization'] = pa
611
612 chunked = False
613 if body and HDR_CONTENT_LENGTH not in hdrs:
614 if getattr(body, '__len__', False):
615 hdrs[HDR_CONTENT_LENGTH] = (HDR_CONTENT_LENGTH, len(body))
616 elif getattr(body, 'read', False):
617 hdrs[HDR_XFER_ENCODING] = (HDR_XFER_ENCODING,
618 XFER_ENCODING_CHUNKED)
619 chunked = True
620 else:
621 raise BadRequestData('body has no __len__() nor read()')
684 # Build header data
685 outgoing_headers = self._buildheaders(
686 method, path, hdrs, self.http_version)
622 687
623 688 # If we're reusing the underlying socket, there are some
624 689 # conditions where we'll want to retry, so make a note of the
625 690 # state of self.sock
626 691 fresh_socket = self.sock is None
627 692 self._connect(pheaders)
628 outgoing_headers = self._buildheaders(
629 method, path, hdrs, self.http_version)
630 693 response = None
631 694 first = True
632 695
@@ -666,8 +729,8 b' class HTTPConnection(object):'
666 729 try:
667 730 try:
668 731 data = r[0].recv(INCOMING_BUFFER_SIZE)
669 except socket.sslerror as e:
670 if e.args[0] != socket.SSL_ERROR_WANT_READ:
732 except ssl.SSLError as e:
733 if e.args[0] != ssl.SSL_ERROR_WANT_READ:
671 734 raise
672 735 logger.debug('SSL_ERROR_WANT_READ while sending '
673 736 'data, retrying...')
@@ -736,16 +799,20 b' class HTTPConnection(object):'
736 799 continue
737 800 if len(data) < OUTGOING_BUFFER_SIZE:
738 801 if chunked:
739 body = '0' + EOL + EOL
802 body = b'0' + EOL + EOL
740 803 else:
741 804 body = None
742 805 if chunked:
743 out = hex(len(data))[2:] + EOL + data + EOL
806 # This encode is okay because we know
807 # hex() is building us only 0-9 and a-f
808 # digits.
809 asciilen = hex(len(data))[2:].encode('ascii')
810 out = asciilen + EOL + data + EOL
744 811 else:
745 812 out = data
746 813 amt = w[0].send(out)
747 814 except socket.error as e:
748 if e[0] == socket.SSL_ERROR_WANT_WRITE and self.ssl:
815 if e[0] == ssl.SSL_ERROR_WANT_WRITE and self.ssl:
749 816 # This means that SSL hasn't flushed its buffer into
750 817 # the socket yet.
751 818 # TODO: find a way to block on ssl flushing its buffer
@@ -764,6 +831,7 b' class HTTPConnection(object):'
764 831 body = out[amt:]
765 832 else:
766 833 outgoing_headers = out[amt:]
834 # End of request-sending loop.
767 835
768 836 # close if the server response said to or responded before eating
769 837 # the whole request
@@ -98,7 +98,7 b' class AbstractReader(object):'
98 98 need -= len(b)
99 99 if need == 0:
100 100 break
101 result = ''.join(blocks)
101 result = b''.join(blocks)
102 102 assert len(result) == amt or (self._finished and len(result) < amt)
103 103
104 104 return result
@@ -17,4 +17,3 b' New errors are not allowed. Warnings are'
17 17 Skipping i18n/polib.py it has no-che?k-code (glob)
18 18 Skipping mercurial/httpclient/__init__.py it has no-che?k-code (glob)
19 19 Skipping mercurial/httpclient/_readers.py it has no-che?k-code (glob)
20 Skipping mercurial/httpclient/socketutil.py it has no-che?k-code (glob)
1 NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now