Mercurial > hg
comparison mercurial/httpclient/__init__.py @ 27601:1ad9da968a2e
httpclient: update to 938f2107d6e2 of httpplus
This enhances proxy support in httpclient a little bit, though I don't
know that we used that functionality at all. It also switches httpplus
to using absolute_import.
author | Augie Fackler <augie@google.com> |
---|---|
date | Thu, 31 Dec 2015 13:19:20 -0500 |
parents | 328739ea70c3 |
children | 86db5cb55d46 |
comparison
equal
deleted
inserted
replaced
27600:cfb26146a8cd | 27601:1ad9da968a2e |
---|---|
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 | 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 |
42 | 43 |
43 import cStringIO | 44 import cStringIO |
46 import logging | 47 import logging |
47 import rfc822 | 48 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 |
56 __all__ = ['HTTPConnection', 'HTTPResponse'] | 59 __all__ = ['HTTPConnection', 'HTTPResponse'] |
57 | 60 |
121 def _close(self): | 124 def _close(self): |
122 if self._reader is not None: | 125 if self._reader is not None: |
123 # We're a friend of the reader class here. | 126 # We're a friend of the reader class here. |
124 # pylint: disable=W0212 | 127 # pylint: disable=W0212 |
125 self._reader._close() | 128 self._reader._close() |
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() | |
126 | 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 |
130 This may block until either a line ending is found or the | 139 This may block until either a line ending is found or the |
277 self.headers = headers | 286 self.headers = headers |
278 # We're a friend of the reader class here. | 287 # We're a friend of the reader class here. |
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. |
285 | 302 |
286 Supports 100-continue and keepalives natively. Uses select() for | 303 Supports 100-continue and keepalives natively. Uses select() for |
290 response_class = HTTPResponse | 307 response_class = HTTPResponse |
291 | 308 |
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: |
299 host: The host to which we'll connect. | 317 host: The host to which we'll connect. |
300 port: Optional. The port over which we'll connect. Default 80 for | 318 port: Optional. The port over which we'll connect. Default 80 for |
305 timeout: Optional. Connection timeout, default is TIMEOUT_DEFAULT. | 323 timeout: Optional. Connection timeout, default is TIMEOUT_DEFAULT. |
306 continue_timeout: Optional. Timeout for waiting on an expected | 324 continue_timeout: Optional. Timeout for waiting on an expected |
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 |
313 it if on a Python older than 2.6. | 338 it if on a Python older than 2.6. |
314 | 339 |
328 use_ssl = False | 353 use_ssl = False |
329 port = 80 | 354 port = 80 |
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') |
340 self.ssl = use_ssl | 362 self.ssl = use_ssl |
341 self.ssl_opts = ssl_opts | 363 self.ssl_opts = ssl_opts |
344 self.sock = None | 366 self.sock = None |
345 self._current_response = None | 367 self._current_response = None |
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 |
359 if self._proxy_host is not None: | 388 if self._proxy_host is not None: |
360 logger.info('Connecting to http proxy %s:%s', | 389 logger.info('Connecting to http proxy %s:%s', |
361 self._proxy_host, self._proxy_port) | 390 self._proxy_host, self._proxy_port) |
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') |
372 timeout_exc = HTTPTimeoutException( | 400 timeout_exc = HTTPTimeoutException( |
373 'Timed out waiting for CONNECT response from proxy') | 401 'Timed out waiting for CONNECT response from proxy') |
466 self._current_response = None | 494 self._current_response = None |
467 return False | 495 return False |
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): |
478 """Send a request to the server. | 506 """Send a request to the server. |
479 | 507 |
490 'current response is read!') | 518 'current response is read!') |
491 self._current_response_taken = False | 519 self._current_response_taken = False |
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: |
503 if getattr(body, '__len__', False): | 540 if getattr(body, '__len__', False): |
504 hdrs[HDR_CONTENT_LENGTH] = (HDR_CONTENT_LENGTH, len(body)) | 541 hdrs[HDR_CONTENT_LENGTH] = (HDR_CONTENT_LENGTH, len(body)) |
511 | 548 |
512 # If we're reusing the underlying socket, there are some | 549 # If we're reusing the underlying socket, there are some |
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 |
520 first = True | 557 first = True |
521 | 558 |
586 # after getting a really large response | 623 # after getting a really large response |
587 # from the server. | 624 # from the server. |
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, |
595 # and either have a partial response or no | 632 # and either have a partial response or no |
596 # response at all. There's really nothing | 633 # response at all. There's really nothing |
643 if e[0] == errno.EWOULDBLOCK or e[0] == errno.EAGAIN: | 680 if e[0] == errno.EWOULDBLOCK or e[0] == errno.EAGAIN: |
644 continue | 681 continue |
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 |
652 if out is body: | 689 if out is body: |
653 body = out[amt:] | 690 body = out[amt:] |
662 if not response._select(): | 699 if not response._select(): |
663 # This means the response failed to get any response | 700 # This means the response failed to get any response |
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 |
671 # request. We don't use self.request() because | 708 # request. We don't use self.request() because |
672 # some tools (notably Mercurial) expect to be able | 709 # some tools (notably Mercurial) expect to be able |
673 # to subclass and redefine request(), and they | 710 # to subclass and redefine request(), and they |