##// END OF EJS Templates
httpclient: update to 938f2107d6e2 of httpplus...
Augie Fackler -
r27601:1ad9da96 default
parent child Browse files
Show More
@@ -36,6 +36,7 b' httplib, but has several additional feat'
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
40
40 # Many functions in this file have too many arguments.
41 # Many functions in this file have too many arguments.
41 # pylint: disable=R0913
42 # pylint: disable=R0913
@@ -48,8 +49,10 b' import rfc822'
48 import select
49 import select
49 import socket
50 import socket
50
51
51 import _readers
52 from . import (
52 import socketutil
53 _readers,
54 socketutil,
55 )
53
56
54 logger = logging.getLogger(__name__)
57 logger = logging.getLogger(__name__)
55
58
@@ -124,6 +127,12 b' class HTTPResponse(object):'
124 # pylint: disable=W0212
127 # pylint: disable=W0212
125 self._reader._close()
128 self._reader._close()
126
129
130 def getheader(self, header, default=None):
131 return self.headers.getheader(header, default=default)
132
133 def getheaders(self):
134 return self.headers.items()
135
127 def readline(self):
136 def readline(self):
128 """Read a single line from the response body.
137 """Read a single line from the response body.
129
138
@@ -279,6 +288,14 b' class HTTPResponse(object):'
279 # pylint: disable=W0212
288 # pylint: disable=W0212
280 self._load_response = self._reader._load
289 self._load_response = self._reader._load
281
290
291 def _foldheaders(headers):
292 """Given some headers, rework them so we can safely overwrite values.
293
294 >>> _foldheaders({'Accept-Encoding': 'wat'})
295 {'accept-encoding': ('Accept-Encoding', 'wat')}
296 """
297 return dict((k.lower(), (k, v)) for k, v in headers.iteritems())
298
282
299
283 class HTTPConnection(object):
300 class HTTPConnection(object):
284 """Connection to a single http server.
301 """Connection to a single http server.
@@ -292,7 +309,8 b' class HTTPConnection(object):'
292 def __init__(self, host, port=None, use_ssl=None, ssl_validator=None,
309 def __init__(self, host, port=None, use_ssl=None, ssl_validator=None,
293 timeout=TIMEOUT_DEFAULT,
310 timeout=TIMEOUT_DEFAULT,
294 continue_timeout=TIMEOUT_ASSUME_CONTINUE,
311 continue_timeout=TIMEOUT_ASSUME_CONTINUE,
295 proxy_hostport=None, ssl_wrap_socket=None, **ssl_opts):
312 proxy_hostport=None, proxy_headers=None,
313 ssl_wrap_socket=None, **ssl_opts):
296 """Create a new HTTPConnection.
314 """Create a new HTTPConnection.
297
315
298 Args:
316 Args:
@@ -307,6 +325,13 b' class HTTPConnection(object):'
307 "100 Continue" response. Default is TIMEOUT_ASSUME_CONTINUE.
325 "100 Continue" response. Default is TIMEOUT_ASSUME_CONTINUE.
308 proxy_hostport: Optional. Tuple of (host, port) to use as an http
326 proxy_hostport: Optional. Tuple of (host, port) to use as an http
309 proxy for the connection. Default is to not use a proxy.
327 proxy for the connection. Default is to not use a proxy.
328 proxy_headers: Optional dict of header keys and values to send to
329 a proxy when using CONNECT. For compatibility with
330 httplib, the Proxy-Authorization header may be
331 specified in headers for request(), which will clobber
332 any such header specified here if specified. Providing
333 this option and not proxy_hostport will raise an
334 ValueError.
310 ssl_wrap_socket: Optional function to use for wrapping
335 ssl_wrap_socket: Optional function to use for wrapping
311 sockets. If unspecified, the one from the ssl module will
336 sockets. If unspecified, the one from the ssl module will
312 be used if available, or something that's compatible with
337 be used if available, or something that's compatible with
@@ -330,10 +355,7 b' class HTTPConnection(object):'
330 elif use_ssl is None:
355 elif use_ssl is None:
331 use_ssl = (port == 443)
356 use_ssl = (port == 443)
332 elif port is None:
357 elif port is None:
333 if use_ssl:
358 port = (use_ssl and 443 or 80)
334 port = 443
335 else:
336 port = 80
337 self.port = port
359 self.port = port
338 if use_ssl and not socketutil.have_ssl:
360 if use_ssl and not socketutil.have_ssl:
339 raise Exception('ssl requested but unavailable on this Python')
361 raise Exception('ssl requested but unavailable on this Python')
@@ -346,13 +368,20 b' class HTTPConnection(object):'
346 self._current_response_taken = False
368 self._current_response_taken = False
347 if proxy_hostport is None:
369 if proxy_hostport is None:
348 self._proxy_host = self._proxy_port = None
370 self._proxy_host = self._proxy_port = None
371 if proxy_headers:
372 raise ValueError(
373 'proxy_headers may not be specified unless '
374 'proxy_hostport is also specified.')
375 else:
376 self._proxy_headers = {}
349 else:
377 else:
350 self._proxy_host, self._proxy_port = proxy_hostport
378 self._proxy_host, self._proxy_port = proxy_hostport
379 self._proxy_headers = _foldheaders(proxy_headers or {})
351
380
352 self.timeout = timeout
381 self.timeout = timeout
353 self.continue_timeout = continue_timeout
382 self.continue_timeout = continue_timeout
354
383
355 def _connect(self):
384 def _connect(self, proxy_headers):
356 """Connect to the host and port specified in __init__."""
385 """Connect to the host and port specified in __init__."""
357 if self.sock:
386 if self.sock:
358 return
387 return
@@ -362,10 +391,9 b' class HTTPConnection(object):'
362 sock = socketutil.create_connection((self._proxy_host,
391 sock = socketutil.create_connection((self._proxy_host,
363 self._proxy_port))
392 self._proxy_port))
364 if self.ssl:
393 if self.ssl:
365 # TODO proxy header support
366 data = self._buildheaders('CONNECT', '%s:%d' % (self.host,
394 data = self._buildheaders('CONNECT', '%s:%d' % (self.host,
367 self.port),
395 self.port),
368 {}, HTTP_VER_1_0)
396 proxy_headers, HTTP_VER_1_0)
369 sock.send(data)
397 sock.send(data)
370 sock.setblocking(0)
398 sock.setblocking(0)
371 r = self.response_class(sock, self.timeout, 'CONNECT')
399 r = self.response_class(sock, self.timeout, 'CONNECT')
@@ -468,10 +496,10 b' class HTTPConnection(object):'
468 return True
496 return True
469 return False
497 return False
470
498
471 def _reconnect(self, where):
499 def _reconnect(self, where, pheaders):
472 logger.info('reconnecting during %s', where)
500 logger.info('reconnecting during %s', where)
473 self.close()
501 self.close()
474 self._connect()
502 self._connect(pheaders)
475
503
476 def request(self, method, path, body=None, headers={},
504 def request(self, method, path, body=None, headers={},
477 expect_continue=False):
505 expect_continue=False):
@@ -492,11 +520,20 b' class HTTPConnection(object):'
492
520
493 logger.info('sending %s request for %s to %s on port %s',
521 logger.info('sending %s request for %s to %s on port %s',
494 method, path, self.host, self.port)
522 method, path, self.host, self.port)
495 hdrs = dict((k.lower(), (k, v)) for k, v in headers.iteritems())
523 hdrs = _foldheaders(headers)
496 if hdrs.get('expect', ('', ''))[1].lower() == '100-continue':
524 if hdrs.get('expect', ('', ''))[1].lower() == '100-continue':
497 expect_continue = True
525 expect_continue = True
498 elif expect_continue:
526 elif expect_continue:
499 hdrs['expect'] = ('Expect', '100-Continue')
527 hdrs['expect'] = ('Expect', '100-Continue')
528 # httplib compatibility: if the user specified a
529 # proxy-authorization header, that's actually intended for a
530 # proxy CONNECT action, not the real request, but only if
531 # we're going to use a proxy.
532 pheaders = dict(self._proxy_headers)
533 if self._proxy_host and self.ssl:
534 pa = hdrs.pop('proxy-authorization', None)
535 if pa is not None:
536 pheaders['proxy-authorization'] = pa
500
537
501 chunked = False
538 chunked = False
502 if body and HDR_CONTENT_LENGTH not in hdrs:
539 if body and HDR_CONTENT_LENGTH not in hdrs:
@@ -513,7 +550,7 b' class HTTPConnection(object):'
513 # conditions where we'll want to retry, so make a note of the
550 # conditions where we'll want to retry, so make a note of the
514 # state of self.sock
551 # state of self.sock
515 fresh_socket = self.sock is None
552 fresh_socket = self.sock is None
516 self._connect()
553 self._connect(pheaders)
517 outgoing_headers = self._buildheaders(
554 outgoing_headers = self._buildheaders(
518 method, path, hdrs, self.http_version)
555 method, path, hdrs, self.http_version)
519 response = None
556 response = None
@@ -588,7 +625,7 b' class HTTPConnection(object):'
588 logger.info(
625 logger.info(
589 'Connection appeared closed in read on first'
626 'Connection appeared closed in read on first'
590 ' request loop iteration, will retry.')
627 ' request loop iteration, will retry.')
591 self._reconnect('read')
628 self._reconnect('read', pheaders)
592 continue
629 continue
593 else:
630 else:
594 # We didn't just send the first data hunk,
631 # We didn't just send the first data hunk,
@@ -645,7 +682,7 b' class HTTPConnection(object):'
645 elif (e[0] not in (errno.ECONNRESET, errno.EPIPE)
682 elif (e[0] not in (errno.ECONNRESET, errno.EPIPE)
646 and not first):
683 and not first):
647 raise
684 raise
648 self._reconnect('write')
685 self._reconnect('write', pheaders)
649 amt = self.sock.send(out)
686 amt = self.sock.send(out)
650 logger.debug('sent %d', amt)
687 logger.debug('sent %d', amt)
651 first = False
688 first = False
@@ -664,7 +701,7 b' class HTTPConnection(object):'
664 # data at all, and in all probability the socket was
701 # data at all, and in all probability the socket was
665 # closed before the server even saw our request. Try
702 # closed before the server even saw our request. Try
666 # the request again on a fresh socket.
703 # the request again on a fresh socket.
667 logging.debug('response._select() failed during request().'
704 logger.debug('response._select() failed during request().'
668 ' Assuming request needs to be retried.')
705 ' Assuming request needs to be retried.')
669 self.sock = None
706 self.sock = None
670 # Call this method explicitly to re-try the
707 # Call this method explicitly to re-try the
@@ -31,6 +31,7 b''
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
35
35 import httplib
36 import httplib
36 import logging
37 import logging
@@ -98,11 +99,12 b' class AbstractReader(object):'
98 return result
99 return result
99
100
100 def readto(self, delimstr, blocks = None):
101 def readto(self, delimstr, blocks = None):
101 """return available data chunks up to the first one in which delimstr
102 """return available data chunks up to the first one in which
102 occurs. No data will be returned after delimstr -- the chunk in which
103 delimstr occurs. No data will be returned after delimstr --
103 it occurs will be split and the remainder pushed back onto the available
104 the chunk in which it occurs will be split and the remainder
104 data queue. If blocks is supplied chunks will be added to blocks, otherwise
105 pushed back onto the available data queue. If blocks is
105 a new list will be allocated.
106 supplied chunks will be added to blocks, otherwise a new list
107 will be allocated.
106 """
108 """
107 if blocks is None:
109 if blocks is None:
108 blocks = []
110 blocks = []
@@ -32,6 +32,8 b' This will attempt to use the ssl module '
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
36
35 import logging
37 import logging
36 import socket
38 import socket
37
39
@@ -101,9 +101,6 b''
101 mercurial/cmdutil.py not using absolute_import
101 mercurial/cmdutil.py not using absolute_import
102 mercurial/commands.py not using absolute_import
102 mercurial/commands.py not using absolute_import
103 mercurial/dispatch.py requires print_function
103 mercurial/dispatch.py requires print_function
104 mercurial/httpclient/__init__.py not using absolute_import
105 mercurial/httpclient/_readers.py not using absolute_import
106 mercurial/httpclient/socketutil.py not using absolute_import
107 mercurial/keepalive.py requires print_function
104 mercurial/keepalive.py requires print_function
108 mercurial/lsprof.py requires print_function
105 mercurial/lsprof.py requires print_function
109 mercurial/lsprofcalltree.py requires print_function
106 mercurial/lsprofcalltree.py requires print_function
General Comments 0
You need to be logged in to leave comments. Login now