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