mercurial/httpclient/socketutil.py
branchstable
changeset 29605 519bb4f9d3a4
parent 29460 a7d1532b26a1
parent 29604 db0095c83344
child 29606 59a0cbd71921
equal deleted inserted replaced
29460:a7d1532b26a1 29605:519bb4f9d3a4
     1 # Copyright 2010, Google Inc.
       
     2 # All rights reserved.
       
     3 #
       
     4 # Redistribution and use in source and binary forms, with or without
       
     5 # modification, are permitted provided that the following conditions are
       
     6 # met:
       
     7 #
       
     8 #     * Redistributions of source code must retain the above copyright
       
     9 # notice, this list of conditions and the following disclaimer.
       
    10 #     * Redistributions in binary form must reproduce the above
       
    11 # copyright notice, this list of conditions and the following disclaimer
       
    12 # in the documentation and/or other materials provided with the
       
    13 # distribution.
       
    14 #     * Neither the name of Google Inc. nor the names of its
       
    15 # contributors may be used to endorse or promote products derived from
       
    16 # this software without specific prior written permission.
       
    17 
       
    18 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
       
    19 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
       
    20 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
       
    21 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
       
    22 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
       
    23 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
       
    24 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
       
    25 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
       
    26 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
       
    27 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
       
    28 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
       
    29 """Abstraction to simplify socket use for Python < 2.6
       
    30 
       
    31 This will attempt to use the ssl module and the new
       
    32 socket.create_connection method, but fall back to the old
       
    33 methods if those are unavailable.
       
    34 """
       
    35 from __future__ import absolute_import
       
    36 
       
    37 import logging
       
    38 import socket
       
    39 
       
    40 logger = logging.getLogger(__name__)
       
    41 
       
    42 try:
       
    43     import ssl
       
    44     # make demandimporters load the module
       
    45     ssl.wrap_socket # pylint: disable=W0104
       
    46     have_ssl = True
       
    47 except ImportError:
       
    48     import httplib
       
    49     import urllib2
       
    50     have_ssl = getattr(urllib2, 'HTTPSHandler', False)
       
    51     ssl = False
       
    52 
       
    53 
       
    54 try:
       
    55     create_connection = socket.create_connection
       
    56 except AttributeError:
       
    57     def create_connection(address):
       
    58         """Backport of socket.create_connection from Python 2.6."""
       
    59         host, port = address
       
    60         msg = "getaddrinfo returns an empty list"
       
    61         sock = None
       
    62         for res in socket.getaddrinfo(host, port, 0,
       
    63                                       socket.SOCK_STREAM):
       
    64             af, socktype, proto, unused_canonname, sa = res
       
    65             try:
       
    66                 sock = socket.socket(af, socktype, proto)
       
    67                 logger.info("connect: (%s, %s)", host, port)
       
    68                 sock.connect(sa)
       
    69             except socket.error as msg:
       
    70                 logger.info('connect fail: %s %s', host, port)
       
    71                 if sock:
       
    72                     sock.close()
       
    73                 sock = None
       
    74                 continue
       
    75             break
       
    76         if not sock:
       
    77             raise socket.error(msg)
       
    78         return sock
       
    79 
       
    80 if ssl:
       
    81     wrap_socket = ssl.wrap_socket
       
    82     CERT_NONE = ssl.CERT_NONE
       
    83     CERT_OPTIONAL = ssl.CERT_OPTIONAL
       
    84     CERT_REQUIRED = ssl.CERT_REQUIRED
       
    85 else:
       
    86     class FakeSocket(httplib.FakeSocket):
       
    87         """Socket wrapper that supports SSL."""
       
    88 
       
    89         # Silence lint about this goofy backport class
       
    90         # pylint: disable=W0232,E1101,R0903,R0913,C0111
       
    91 
       
    92         # backport the behavior from Python 2.6, which is to busy wait
       
    93         # on the socket instead of anything nice. Sigh.
       
    94         # See http://bugs.python.org/issue3890 for more info.
       
    95         def recv(self, buflen=1024, flags=0):
       
    96             """ssl-aware wrapper around socket.recv
       
    97             """
       
    98             if flags != 0:
       
    99                 raise ValueError(
       
   100                     "non-zero flags not allowed in calls to recv() on %s" %
       
   101                     self.__class__)
       
   102             while True:
       
   103                 try:
       
   104                     return self._ssl.read(buflen)
       
   105                 except socket.sslerror as x:
       
   106                     if x.args[0] == socket.SSL_ERROR_WANT_READ:
       
   107                         continue
       
   108                     else:
       
   109                         raise x
       
   110 
       
   111     _PROTOCOL_SSLv23 = 2
       
   112 
       
   113     CERT_NONE = 0
       
   114     CERT_OPTIONAL = 1
       
   115     CERT_REQUIRED = 2
       
   116 
       
   117     # Disable unused-argument because we're making a dumb wrapper
       
   118     # that's like an upstream method.
       
   119     #
       
   120     # pylint: disable=W0613,R0913
       
   121     def wrap_socket(sock, keyfile=None, certfile=None,
       
   122                 server_side=False, cert_reqs=CERT_NONE,
       
   123                 ssl_version=_PROTOCOL_SSLv23, ca_certs=None,
       
   124                 do_handshake_on_connect=True,
       
   125                 suppress_ragged_eofs=True):
       
   126         """Backport of ssl.wrap_socket from Python 2.6."""
       
   127         if cert_reqs != CERT_NONE and ca_certs:
       
   128             raise CertificateValidationUnsupported(
       
   129                 'SSL certificate validation requires the ssl module'
       
   130                 '(included in Python 2.6 and later.)')
       
   131         sslob = socket.ssl(sock)
       
   132         # borrow httplib's workaround for no ssl.wrap_socket
       
   133         sock = FakeSocket(sock, sslob)
       
   134         return sock
       
   135     # pylint: enable=W0613,R0913
       
   136 
       
   137 
       
   138 class CertificateValidationUnsupported(Exception):
       
   139     """Exception raised when cert validation is requested but unavailable."""
       
   140 # no-check-code