NovaBryan/EVE_Dump
Log | Files | 📦 下载最新源码 (.zip) | Refs | README

__init__.py (51966B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\packages\httplib2\__init__.py
      2 from __future__ import generators
      3 from __future__ import print_function
      4 from __future__ import absolute_import
      5 __author__ = 'Joe Gregorio (joe@bitworking.org)'
      6 __copyright__ = 'Copyright 2006, Joe Gregorio'
      7 __contributors__ = ['Thomas Broyer (t.broyer@ltgt.net)',
      8  'James Antill',
      9  'Xavier Verges Farrero',
     10  'Jonathan Feinberg',
     11  'Blair Zajac',
     12  'Sam Ruby',
     13  'Louis Nyffenegger']
     14 __license__ = 'MIT'
     15 __version__ = '0.9.2'
     16 import re
     17 import sys
     18 import email
     19 import email.Utils
     20 import email.Message
     21 import email.FeedParser
     22 import StringIO
     23 import gzip
     24 import zlib
     25 import httplib
     26 import urlparse
     27 import urllib
     28 import base64
     29 import os
     30 import copy
     31 import calendar
     32 import time
     33 import random
     34 import errno
     35 try:
     36     from hashlib import sha1 as _sha, md5 as _md5
     37 except ImportError:
     38     import sha
     39     import md5
     40     _sha = sha.new
     41     _md5 = md5.new
     42 
     43 import hmac
     44 from gettext import gettext as _
     45 import socket
     46 try:
     47     from httplib2 import socks
     48 except ImportError:
     49     try:
     50         from . import socks
     51     except (ImportError, AttributeError):
     52         socks = None
     53 
     54 try:
     55     import ssl
     56     ssl_SSLError = ssl.SSLError
     57 
     58     def _ssl_wrap_socket(sock, key_file, cert_file, disable_validation, ca_certs):
     59         if disable_validation:
     60             cert_reqs = ssl.CERT_NONE
     61         else:
     62             cert_reqs = ssl.CERT_REQUIRED
     63         return ssl.wrap_socket(sock, keyfile=key_file, certfile=cert_file, cert_reqs=cert_reqs, ca_certs=ca_certs)
     64 
     65 
     66 except (AttributeError, ImportError):
     67     ssl_SSLError = None
     68 
     69     def _ssl_wrap_socket(sock, key_file, cert_file, disable_validation, ca_certs):
     70         if not disable_validation:
     71             raise CertificateValidationUnsupported('SSL certificate validation is not supported without the ssl module installed. To avoid this error, install the ssl module, or explicity disable validation.')
     72         ssl_sock = socket.ssl(sock, key_file, cert_file)
     73         return httplib.FakeSocket(sock, ssl_sock)
     74 
     75 
     76 if sys.version_info >= (2, 3):
     77     from .iri2uri import iri2uri
     78 else:
     79 
     80     def iri2uri(uri):
     81         return uri
     82 
     83 
     84 def has_timeout(timeout):
     85     if hasattr(socket, '_GLOBAL_DEFAULT_TIMEOUT'):
     86         return timeout is not None and timeout is not socket._GLOBAL_DEFAULT_TIMEOUT
     87     return timeout is not None
     88 
     89 
     90 __all__ = ['Http',
     91  'Response',
     92  'ProxyInfo',
     93  'HttpLib2Error',
     94  'RedirectMissingLocation',
     95  'RedirectLimit',
     96  'FailedToDecompressContent',
     97  'UnimplementedDigestAuthOptionError',
     98  'UnimplementedHmacDigestAuthOptionError',
     99  'debuglevel',
    100  'ProxiesUnavailableError']
    101 debuglevel = 0
    102 RETRIES = 2
    103 if sys.version_info < (2, 4):
    104 
    105     def sorted(seq):
    106         seq.sort()
    107         return seq
    108 
    109 
    110 def HTTPResponse__getheaders(self):
    111     if self.msg is None:
    112         raise httplib.ResponseNotReady()
    113     return self.msg.items()
    114 
    115 
    116 if not hasattr(httplib.HTTPResponse, 'getheaders'):
    117     httplib.HTTPResponse.getheaders = HTTPResponse__getheaders
    118 
    119 class HttpLib2Error(Exception):
    120     pass
    121 
    122 
    123 class HttpLib2ErrorWithResponse(HttpLib2Error):
    124 
    125     def __init__(self, desc, response, content):
    126         self.response = response
    127         self.content = content
    128         HttpLib2Error.__init__(self, desc)
    129 
    130 
    131 class RedirectMissingLocation(HttpLib2ErrorWithResponse):
    132     pass
    133 
    134 
    135 class RedirectLimit(HttpLib2ErrorWithResponse):
    136     pass
    137 
    138 
    139 class FailedToDecompressContent(HttpLib2ErrorWithResponse):
    140     pass
    141 
    142 
    143 class UnimplementedDigestAuthOptionError(HttpLib2ErrorWithResponse):
    144     pass
    145 
    146 
    147 class UnimplementedHmacDigestAuthOptionError(HttpLib2ErrorWithResponse):
    148     pass
    149 
    150 
    151 class MalformedHeader(HttpLib2Error):
    152     pass
    153 
    154 
    155 class RelativeURIError(HttpLib2Error):
    156     pass
    157 
    158 
    159 class ServerNotFoundError(HttpLib2Error):
    160     pass
    161 
    162 
    163 class ProxiesUnavailableError(HttpLib2Error):
    164     pass
    165 
    166 
    167 class CertificateValidationUnsupported(HttpLib2Error):
    168     pass
    169 
    170 
    171 class SSLHandshakeError(HttpLib2Error):
    172     pass
    173 
    174 
    175 class NotSupportedOnThisPlatform(HttpLib2Error):
    176     pass
    177 
    178 
    179 class CertificateHostnameMismatch(SSLHandshakeError):
    180 
    181     def __init__(self, desc, host, cert):
    182         HttpLib2Error.__init__(self, desc)
    183         self.host = host
    184         self.cert = cert
    185 
    186 
    187 DEFAULT_MAX_REDIRECTS = 5
    188 try:
    189     import ca_certs_locater
    190     CA_CERTS = ca_certs_locater.get()
    191 except ImportError:
    192     CA_CERTS = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'cacerts.txt')
    193 
    194 HOP_BY_HOP = ['connection',
    195  'keep-alive',
    196  'proxy-authenticate',
    197  'proxy-authorization',
    198  'te',
    199  'trailers',
    200  'transfer-encoding',
    201  'upgrade']
    202 
    203 def _get_end2end_headers(response):
    204     hopbyhop = list(HOP_BY_HOP)
    205     hopbyhop.extend([ x.strip() for x in response.get('connection', '').split(',') ])
    206     return [ header for header in response.keys() if header not in hopbyhop ]
    207 
    208 
    209 URI = re.compile('^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?')
    210 
    211 def parse_uri(uri):
    212     groups = URI.match(uri).groups()
    213     return (groups[1],
    214      groups[3],
    215      groups[4],
    216      groups[6],
    217      groups[8])
    218 
    219 
    220 def urlnorm(uri):
    221     scheme, authority, path, query, fragment = parse_uri(uri)
    222     if not scheme or not authority:
    223         raise RelativeURIError('Only absolute URIs are allowed. uri = %s' % uri)
    224     authority = authority.lower()
    225     scheme = scheme.lower()
    226     if not path:
    227         path = '/'
    228     request_uri = query and '?'.join([path, query]) or path
    229     scheme = scheme.lower()
    230     defrag_uri = scheme + '://' + authority + request_uri
    231     return (scheme,
    232      authority,
    233      request_uri,
    234      defrag_uri)
    235 
    236 
    237 re_url_scheme = re.compile('^\\w+://')
    238 re_slash = re.compile('[?/:|]+')
    239 
    240 def safename(filename):
    241     try:
    242         if re_url_scheme.match(filename):
    243             if isinstance(filename, str):
    244                 filename = filename.decode('utf-8')
    245                 filename = filename.encode('idna')
    246             else:
    247                 filename = filename.encode('idna')
    248     except UnicodeError:
    249         pass
    250 
    251     if isinstance(filename, unicode):
    252         filename = filename.encode('utf-8')
    253     filemd5 = _md5(filename).hexdigest()
    254     filename = re_url_scheme.sub('', filename)
    255     filename = re_slash.sub(',', filename)
    256     if len(filename) > 200:
    257         filename = filename[:200]
    258     return ','.join((filename, filemd5))
    259 
    260 
    261 NORMALIZE_SPACE = re.compile('(?:\\r\\n)?[ \\t]+')
    262 
    263 def _normalize_headers(headers):
    264     return dict([ (key.lower(), NORMALIZE_SPACE.sub(value, ' ').strip()) for key, value in headers.iteritems() ])
    265 
    266 
    267 def _parse_cache_control(headers):
    268     retval = {}
    269     if 'cache-control' in headers:
    270         parts = headers['cache-control'].split(',')
    271         parts_with_args = [ tuple([ x.strip().lower() for x in part.split('=', 1) ]) for part in parts if -1 != part.find('=') ]
    272         parts_wo_args = [ (name.strip().lower(), 1) for name in parts if -1 == name.find('=') ]
    273         retval = dict(parts_with_args + parts_wo_args)
    274     return retval
    275 
    276 
    277 USE_WWW_AUTH_STRICT_PARSING = 0
    278 WWW_AUTH_STRICT = re.compile('^(?:\\s*(?:,\\s*)?([^\\0-\\x1f\\x7f-\\xff()<>@,;:\\\\\\"/[\\]?={} \\t]+)\\s*=\\s*\\"?((?<=\\")(?:[^\\0-\\x08\\x0A-\\x1f\\x7f-\\xff\\\\\\"]|\\\\[\\0-\\x7f])*?(?=\\")|(?<!\\")[^\\0-\\x1f\\x7f-\\xff()<>@,;:\\\\\\"/[\\]?={} \\t]+(?!\\"))\\"?)(.*)$')
    279 WWW_AUTH_RELAXED = re.compile('^(?:\\s*(?:,\\s*)?([^ \\t\\r\\n=]+)\\s*=\\s*\\"?((?<=\\")(?:[^\\\\\\"]|\\\\.)*?(?=\\")|(?<!\\")[^ \\t\\r\\n,]+(?!\\"))\\"?)(.*)$')
    280 UNQUOTE_PAIRS = re.compile('\\\\(.)')
    281 
    282 def _parse_www_authenticate(headers, headername = 'www-authenticate'):
    283     retval = {}
    284     if headername in headers:
    285         try:
    286             authenticate = headers[headername].strip()
    287             www_auth = USE_WWW_AUTH_STRICT_PARSING and WWW_AUTH_STRICT or WWW_AUTH_RELAXED
    288             while authenticate:
    289                 if headername == 'authentication-info':
    290                     auth_scheme, the_rest = 'digest', authenticate
    291                 else:
    292                     auth_scheme, the_rest = authenticate.split(' ', 1)
    293                 match = www_auth.search(the_rest)
    294                 auth_params = {}
    295                 while match:
    296                     if match and len(match.groups()) == 3:
    297                         key, value, the_rest = match.groups()
    298                         auth_params[key.lower()] = UNQUOTE_PAIRS.sub('\\1', value)
    299                     match = www_auth.search(the_rest)
    300 
    301                 retval[auth_scheme.lower()] = auth_params
    302                 authenticate = the_rest.strip()
    303 
    304         except ValueError:
    305             raise MalformedHeader('WWW-Authenticate')
    306 
    307     return retval
    308 
    309 
    310 def _entry_disposition(response_headers, request_headers):
    311     retval = 'STALE'
    312     cc = _parse_cache_control(request_headers)
    313     cc_response = _parse_cache_control(response_headers)
    314     if 'pragma' in request_headers and request_headers['pragma'].lower().find('no-cache') != -1:
    315         retval = 'TRANSPARENT'
    316         if 'cache-control' not in request_headers:
    317             request_headers['cache-control'] = 'no-cache'
    318     elif 'no-cache' in cc:
    319         retval = 'TRANSPARENT'
    320     elif 'no-cache' in cc_response:
    321         retval = 'STALE'
    322     elif 'only-if-cached' in cc:
    323         retval = 'FRESH'
    324     elif 'date' in response_headers:
    325         date = calendar.timegm(email.Utils.parsedate_tz(response_headers['date']))
    326         now = time.time()
    327         current_age = max(0, now - date)
    328         if 'max-age' in cc_response:
    329             try:
    330                 freshness_lifetime = int(cc_response['max-age'])
    331             except ValueError:
    332                 freshness_lifetime = 0
    333 
    334         elif 'expires' in response_headers:
    335             expires = email.Utils.parsedate_tz(response_headers['expires'])
    336             if None == expires:
    337                 freshness_lifetime = 0
    338             else:
    339                 freshness_lifetime = max(0, calendar.timegm(expires) - date)
    340         else:
    341             freshness_lifetime = 0
    342         if 'max-age' in cc:
    343             try:
    344                 freshness_lifetime = int(cc['max-age'])
    345             except ValueError:
    346                 freshness_lifetime = 0
    347 
    348         if 'min-fresh' in cc:
    349             try:
    350                 min_fresh = int(cc['min-fresh'])
    351             except ValueError:
    352                 min_fresh = 0
    353 
    354             current_age += min_fresh
    355         if freshness_lifetime > current_age:
    356             retval = 'FRESH'
    357     return retval
    358 
    359 
    360 def _decompressContent(response, new_content):
    361     content = new_content
    362     try:
    363         encoding = response.get('content-encoding', None)
    364         if encoding in ('gzip', 'deflate'):
    365             if encoding == 'gzip':
    366                 content = gzip.GzipFile(fileobj=StringIO.StringIO(new_content)).read()
    367             if encoding == 'deflate':
    368                 content = zlib.decompress(content)
    369             response['content-length'] = str(len(content))
    370             response['-content-encoding'] = response['content-encoding']
    371             del response['content-encoding']
    372     except IOError:
    373         content = ''
    374         raise FailedToDecompressContent(_('Content purported to be compressed with %s but failed to decompress.') % response.get('content-encoding'), response, content)
    375 
    376     return content
    377 
    378 
    379 def _updateCache(request_headers, response_headers, content, cache, cachekey):
    380     if cachekey:
    381         cc = _parse_cache_control(request_headers)
    382         cc_response = _parse_cache_control(response_headers)
    383         if 'no-store' in cc or 'no-store' in cc_response:
    384             cache.delete(cachekey)
    385         else:
    386             info = email.Message.Message()
    387             for key, value in response_headers.iteritems():
    388                 if key not in ('status', 'content-encoding', 'transfer-encoding'):
    389                     info[key] = value
    390 
    391             vary = response_headers.get('vary', None)
    392             if vary:
    393                 vary_headers = vary.lower().replace(' ', '').split(',')
    394                 for header in vary_headers:
    395                     key = '-varied-%s' % header
    396                     try:
    397                         info[key] = request_headers[header]
    398                     except KeyError:
    399                         pass
    400 
    401             status = response_headers.status
    402             if status == 304:
    403                 status = 200
    404             status_header = 'status: %d\r\n' % status
    405             header_str = info.as_string()
    406             header_str = re.sub('\r(?!\n)|(?<!\r)\n', '\r\n', header_str)
    407             text = ''.join([status_header, header_str, content])
    408             cache.set(cachekey, text)
    409 
    410 
    411 def _cnonce():
    412     dig = _md5('%s:%s' % (time.ctime(), [ '0123456789'[random.randrange(0, 9)] for i in range(20) ])).hexdigest()
    413     return dig[:16]
    414 
    415 
    416 def _wsse_username_token(cnonce, iso_now, password):
    417     return base64.b64encode(_sha('%s%s%s' % (cnonce, iso_now, password)).digest()).strip()
    418 
    419 
    420 class Authentication(object):
    421 
    422     def __init__(self, credentials, host, request_uri, headers, response, content, http):
    423         scheme, authority, path, query, fragment = parse_uri(request_uri)
    424         self.path = path
    425         self.host = host
    426         self.credentials = credentials
    427         self.http = http
    428 
    429     def depth(self, request_uri):
    430         scheme, authority, path, query, fragment = parse_uri(request_uri)
    431         return request_uri[len(self.path):].count('/')
    432 
    433     def inscope(self, host, request_uri):
    434         scheme, authority, path, query, fragment = parse_uri(request_uri)
    435         return host == self.host and path.startswith(self.path)
    436 
    437     def request(self, method, request_uri, headers, content):
    438         pass
    439 
    440     def response(self, response, content):
    441         return False
    442 
    443 
    444 class BasicAuthentication(Authentication):
    445 
    446     def __init__(self, credentials, host, request_uri, headers, response, content, http):
    447         Authentication.__init__(self, credentials, host, request_uri, headers, response, content, http)
    448 
    449     def request(self, method, request_uri, headers, content):
    450         headers['authorization'] = 'Basic ' + base64.b64encode('%s:%s' % self.credentials).strip()
    451 
    452 
    453 class DigestAuthentication(Authentication):
    454 
    455     def __init__(self, credentials, host, request_uri, headers, response, content, http):
    456         Authentication.__init__(self, credentials, host, request_uri, headers, response, content, http)
    457         challenge = _parse_www_authenticate(response, 'www-authenticate')
    458         self.challenge = challenge['digest']
    459         qop = self.challenge.get('qop', 'auth')
    460         self.challenge['qop'] = 'auth' in [ x.strip() for x in qop.split() ] and 'auth' or None
    461         if self.challenge['qop'] is None:
    462             raise UnimplementedDigestAuthOptionError(_('Unsupported value for qop: %s.' % qop))
    463         self.challenge['algorithm'] = self.challenge.get('algorithm', 'MD5').upper()
    464         if self.challenge['algorithm'] != 'MD5':
    465             raise UnimplementedDigestAuthOptionError(_('Unsupported value for algorithm: %s.' % self.challenge['algorithm']))
    466         self.A1 = ''.join([self.credentials[0],
    467          ':',
    468          self.challenge['realm'],
    469          ':',
    470          self.credentials[1]])
    471         self.challenge['nc'] = 1
    472 
    473     def request(self, method, request_uri, headers, content, cnonce = None):
    474         H = lambda x: _md5(x).hexdigest()
    475         KD = lambda s, d: H('%s:%s' % (s, d))
    476         A2 = ''.join([method, ':', request_uri])
    477         self.challenge['cnonce'] = cnonce or _cnonce()
    478         request_digest = '"%s"' % KD(H(self.A1), '%s:%s:%s:%s:%s' % (self.challenge['nonce'],
    479          '%08x' % self.challenge['nc'],
    480          self.challenge['cnonce'],
    481          self.challenge['qop'],
    482          H(A2)))
    483         headers['authorization'] = 'Digest username="%s", realm="%s", nonce="%s", uri="%s", algorithm=%s, response=%s, qop=%s, nc=%08x, cnonce="%s"' % (self.credentials[0],
    484          self.challenge['realm'],
    485          self.challenge['nonce'],
    486          request_uri,
    487          self.challenge['algorithm'],
    488          request_digest,
    489          self.challenge['qop'],
    490          self.challenge['nc'],
    491          self.challenge['cnonce'])
    492         if self.challenge.get('opaque'):
    493             headers['authorization'] += ', opaque="%s"' % self.challenge['opaque']
    494         self.challenge['nc'] += 1
    495 
    496     def response(self, response, content):
    497         if 'authentication-info' not in response:
    498             challenge = _parse_www_authenticate(response, 'www-authenticate').get('digest', {})
    499             if 'true' == challenge.get('stale'):
    500                 self.challenge['nonce'] = challenge['nonce']
    501                 self.challenge['nc'] = 1
    502                 return True
    503         else:
    504             updated_challenge = _parse_www_authenticate(response, 'authentication-info').get('digest', {})
    505             if 'nextnonce' in updated_challenge:
    506                 self.challenge['nonce'] = updated_challenge['nextnonce']
    507                 self.challenge['nc'] = 1
    508         return False
    509 
    510 
    511 class HmacDigestAuthentication(Authentication):
    512     __author__ = 'Thomas Broyer (t.broyer@ltgt.net)'
    513 
    514     def __init__(self, credentials, host, request_uri, headers, response, content, http):
    515         Authentication.__init__(self, credentials, host, request_uri, headers, response, content, http)
    516         challenge = _parse_www_authenticate(response, 'www-authenticate')
    517         self.challenge = challenge['hmacdigest']
    518         self.challenge['reason'] = self.challenge.get('reason', 'unauthorized')
    519         if self.challenge['reason'] not in ('unauthorized', 'integrity'):
    520             self.challenge['reason'] = 'unauthorized'
    521         self.challenge['salt'] = self.challenge.get('salt', '')
    522         if not self.challenge.get('snonce'):
    523             raise UnimplementedHmacDigestAuthOptionError(_("The challenge doesn't contain a server nonce, or this one is empty."))
    524         self.challenge['algorithm'] = self.challenge.get('algorithm', 'HMAC-SHA-1')
    525         if self.challenge['algorithm'] not in ('HMAC-SHA-1', 'HMAC-MD5'):
    526             raise UnimplementedHmacDigestAuthOptionError(_('Unsupported value for algorithm: %s.' % self.challenge['algorithm']))
    527         self.challenge['pw-algorithm'] = self.challenge.get('pw-algorithm', 'SHA-1')
    528         if self.challenge['pw-algorithm'] not in ('SHA-1', 'MD5'):
    529             raise UnimplementedHmacDigestAuthOptionError(_('Unsupported value for pw-algorithm: %s.' % self.challenge['pw-algorithm']))
    530         if self.challenge['algorithm'] == 'HMAC-MD5':
    531             self.hashmod = _md5
    532         else:
    533             self.hashmod = _sha
    534         if self.challenge['pw-algorithm'] == 'MD5':
    535             self.pwhashmod = _md5
    536         else:
    537             self.pwhashmod = _sha
    538         self.key = ''.join([self.credentials[0],
    539          ':',
    540          self.pwhashmod.new(''.join([self.credentials[1], self.challenge['salt']])).hexdigest().lower(),
    541          ':',
    542          self.challenge['realm']])
    543         self.key = self.pwhashmod.new(self.key).hexdigest().lower()
    544 
    545     def request(self, method, request_uri, headers, content):
    546         keys = _get_end2end_headers(headers)
    547         keylist = ''.join([ '%s ' % k for k in keys ])
    548         headers_val = ''.join([ headers[k] for k in keys ])
    549         created = time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime())
    550         cnonce = _cnonce()
    551         request_digest = '%s:%s:%s:%s:%s' % (method,
    552          request_uri,
    553          cnonce,
    554          self.challenge['snonce'],
    555          headers_val)
    556         request_digest = hmac.new(self.key, request_digest, self.hashmod).hexdigest().lower()
    557         headers['authorization'] = 'HMACDigest username="%s", realm="%s", snonce="%s", cnonce="%s", uri="%s", created="%s", response="%s", headers="%s"' % (self.credentials[0],
    558          self.challenge['realm'],
    559          self.challenge['snonce'],
    560          cnonce,
    561          request_uri,
    562          created,
    563          request_digest,
    564          keylist)
    565 
    566     def response(self, response, content):
    567         challenge = _parse_www_authenticate(response, 'www-authenticate').get('hmacdigest', {})
    568         if challenge.get('reason') in ('integrity', 'stale'):
    569             return True
    570         return False
    571 
    572 
    573 class WsseAuthentication(Authentication):
    574 
    575     def __init__(self, credentials, host, request_uri, headers, response, content, http):
    576         Authentication.__init__(self, credentials, host, request_uri, headers, response, content, http)
    577 
    578     def request(self, method, request_uri, headers, content):
    579         headers['authorization'] = 'WSSE profile="UsernameToken"'
    580         iso_now = time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime())
    581         cnonce = _cnonce()
    582         password_digest = _wsse_username_token(cnonce, iso_now, self.credentials[1])
    583         headers['X-WSSE'] = 'UsernameToken Username="%s", PasswordDigest="%s", Nonce="%s", Created="%s"' % (self.credentials[0],
    584          password_digest,
    585          cnonce,
    586          iso_now)
    587 
    588 
    589 class GoogleLoginAuthentication(Authentication):
    590 
    591     def __init__(self, credentials, host, request_uri, headers, response, content, http):
    592         from urllib import urlencode
    593         Authentication.__init__(self, credentials, host, request_uri, headers, response, content, http)
    594         challenge = _parse_www_authenticate(response, 'www-authenticate')
    595         service = challenge['googlelogin'].get('service', 'xapi')
    596         if service == 'xapi' and request_uri.find('calendar') > 0:
    597             service = 'cl'
    598         auth = dict(Email=credentials[0], Passwd=credentials[1], service=service, source=headers['user-agent'])
    599         resp, content = self.http.request('https://www.google.com/accounts/ClientLogin', method='POST', body=urlencode(auth), headers={'Content-Type': 'application/x-www-form-urlencoded'})
    600         lines = content.split('\n')
    601         d = dict([ tuple(line.split('=', 1)) for line in lines if line ])
    602         if resp.status == 403:
    603             self.Auth = ''
    604         else:
    605             self.Auth = d['Auth']
    606 
    607     def request(self, method, request_uri, headers, content):
    608         headers['authorization'] = 'GoogleLogin Auth=' + self.Auth
    609 
    610 
    611 AUTH_SCHEME_CLASSES = {'basic': BasicAuthentication,
    612  'wsse': WsseAuthentication,
    613  'digest': DigestAuthentication,
    614  'hmacdigest': HmacDigestAuthentication,
    615  'googlelogin': GoogleLoginAuthentication}
    616 AUTH_SCHEME_ORDER = ['hmacdigest',
    617  'googlelogin',
    618  'digest',
    619  'wsse',
    620  'basic']
    621 
    622 class FileCache(object):
    623 
    624     def __init__(self, cache, safe = safename):
    625         self.cache = cache
    626         self.safe = safe
    627         if not os.path.exists(cache):
    628             os.makedirs(self.cache)
    629 
    630     def get(self, key):
    631         retval = None
    632         cacheFullPath = os.path.join(self.cache, self.safe(key))
    633         try:
    634             f = file(cacheFullPath, 'rb')
    635             retval = f.read()
    636             f.close()
    637         except IOError:
    638             pass
    639 
    640         return retval
    641 
    642     def set(self, key, value):
    643         cacheFullPath = os.path.join(self.cache, self.safe(key))
    644         f = file(cacheFullPath, 'wb')
    645         f.write(value)
    646         f.close()
    647 
    648     def delete(self, key):
    649         cacheFullPath = os.path.join(self.cache, self.safe(key))
    650         if os.path.exists(cacheFullPath):
    651             os.remove(cacheFullPath)
    652 
    653 
    654 class Credentials(object):
    655 
    656     def __init__(self):
    657         self.credentials = []
    658 
    659     def add(self, name, password, domain = ''):
    660         self.credentials.append((domain.lower(), name, password))
    661 
    662     def clear(self):
    663         self.credentials = []
    664 
    665     def iter(self, domain):
    666         for cdomain, name, password in self.credentials:
    667             if cdomain == '' or domain == cdomain:
    668                 yield (name, password)
    669 
    670 
    671 class KeyCerts(Credentials):
    672     pass
    673 
    674 
    675 class AllHosts(object):
    676     pass
    677 
    678 
    679 class ProxyInfo(object):
    680     bypass_hosts = ()
    681 
    682     def __init__(self, proxy_type, proxy_host, proxy_port, proxy_rdns = True, proxy_user = None, proxy_pass = None):
    683         self.proxy_type = proxy_type
    684         self.proxy_host = proxy_host
    685         self.proxy_port = proxy_port
    686         self.proxy_rdns = proxy_rdns
    687         self.proxy_user = proxy_user
    688         self.proxy_pass = proxy_pass
    689 
    690     def astuple(self):
    691         return (self.proxy_type,
    692          self.proxy_host,
    693          self.proxy_port,
    694          self.proxy_rdns,
    695          self.proxy_user,
    696          self.proxy_pass)
    697 
    698     def isgood(self):
    699         return self.proxy_host != None and self.proxy_port != None
    700 
    701     def applies_to(self, hostname):
    702         return not self.bypass_host(hostname)
    703 
    704     def bypass_host(self, hostname):
    705         if self.bypass_hosts is AllHosts:
    706             return True
    707         bypass = False
    708         for domain in self.bypass_hosts:
    709             if hostname.endswith(domain):
    710                 bypass = True
    711 
    712         return bypass
    713 
    714 
    715 def proxy_info_from_environment(method = 'http'):
    716     if method not in ('http', 'https'):
    717         return
    718     env_var = method + '_proxy'
    719     url = os.environ.get(env_var, os.environ.get(env_var.upper()))
    720     if not url:
    721         return
    722     pi = proxy_info_from_url(url, method)
    723     no_proxy = os.environ.get('no_proxy', os.environ.get('NO_PROXY', ''))
    724     bypass_hosts = []
    725     if no_proxy:
    726         bypass_hosts = no_proxy.split(',')
    727     if no_proxy == '*':
    728         bypass_hosts = AllHosts
    729     pi.bypass_hosts = bypass_hosts
    730     return pi
    731 
    732 
    733 def proxy_info_from_url(url, method = 'http'):
    734     url = urlparse.urlparse(url)
    735     username = None
    736     password = None
    737     port = None
    738     if '@' in url[1]:
    739         ident, host_port = url[1].split('@', 1)
    740         if ':' in ident:
    741             username, password = ident.split(':', 1)
    742         else:
    743             password = ident
    744     else:
    745         host_port = url[1]
    746     if ':' in host_port:
    747         host, port = host_port.split(':', 1)
    748     else:
    749         host = host_port
    750     if port:
    751         port = int(port)
    752     else:
    753         port = dict(https=443, http=80)[method]
    754     proxy_type = 3
    755     return ProxyInfo(proxy_type=proxy_type, proxy_host=host, proxy_port=port, proxy_user=username or None, proxy_pass=password or None)
    756 
    757 
    758 class HTTPConnectionWithTimeout(httplib.HTTPConnection):
    759 
    760     def __init__(self, host, port = None, strict = None, timeout = None, proxy_info = None):
    761         httplib.HTTPConnection.__init__(self, host, port, strict)
    762         self.timeout = timeout
    763         self.proxy_info = proxy_info
    764 
    765     def connect(self):
    766         if self.proxy_info and socks is None:
    767             raise ProxiesUnavailableError('Proxy support missing but proxy use was requested!')
    768         msg = 'getaddrinfo returns an empty list'
    769         if self.proxy_info and self.proxy_info.isgood():
    770             use_proxy = True
    771             proxy_type, proxy_host, proxy_port, proxy_rdns, proxy_user, proxy_pass = self.proxy_info.astuple()
    772             host = proxy_host
    773             port = proxy_port
    774         else:
    775             use_proxy = False
    776             host = self.host
    777             port = self.port
    778         for res in socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM):
    779             af, socktype, proto, canonname, sa = res
    780             try:
    781                 if use_proxy:
    782                     self.sock = socks.socksocket(af, socktype, proto)
    783                     self.sock.setproxy(proxy_type, proxy_host, proxy_port, proxy_rdns, proxy_user, proxy_pass)
    784                 else:
    785                     self.sock = socket.socket(af, socktype, proto)
    786                     self.sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
    787                 if has_timeout(self.timeout):
    788                     self.sock.settimeout(self.timeout)
    789                 if self.debuglevel > 0:
    790                     print('connect: (%s, %s) ************' % (self.host, self.port))
    791                     if use_proxy:
    792                         print('proxy: %s ************' % str((proxy_host,
    793                          proxy_port,
    794                          proxy_rdns,
    795                          proxy_user,
    796                          proxy_pass)))
    797                 self.sock.connect((self.host, self.port) + sa[2:])
    798             except socket.error as msg:
    799                 if self.debuglevel > 0:
    800                     print('connect fail: (%s, %s)' % (self.host, self.port))
    801                     if use_proxy:
    802                         print('proxy: %s' % str((proxy_host,
    803                          proxy_port,
    804                          proxy_rdns,
    805                          proxy_user,
    806                          proxy_pass)))
    807                 if self.sock:
    808                     self.sock.close()
    809                 self.sock = None
    810                 continue
    811 
    812             break
    813 
    814         if not self.sock:
    815             raise socket.error, msg
    816 
    817 
    818 class HTTPSConnectionWithTimeout(httplib.HTTPSConnection):
    819 
    820     def __init__(self, host, port = None, key_file = None, cert_file = None, strict = None, timeout = None, proxy_info = None, ca_certs = None, disable_ssl_certificate_validation = False):
    821         httplib.HTTPSConnection.__init__(self, host, port=port, key_file=key_file, cert_file=cert_file, strict=strict)
    822         self.timeout = timeout
    823         self.proxy_info = proxy_info
    824         if ca_certs is None:
    825             ca_certs = CA_CERTS
    826         self.ca_certs = ca_certs
    827         self.disable_ssl_certificate_validation = disable_ssl_certificate_validation
    828 
    829     def _GetValidHostsForCert(self, cert):
    830         if 'subjectAltName' in cert:
    831             return [ x[1] for x in cert['subjectAltName'] if x[0].lower() == 'dns' ]
    832         else:
    833             return [ x[0][1] for x in cert['subject'] if x[0][0].lower() == 'commonname' ]
    834 
    835     def _ValidateCertificateHostname(self, cert, hostname):
    836         hosts = self._GetValidHostsForCert(cert)
    837         for host in hosts:
    838             host_re = host.replace('.', '\\.').replace('*', '[^.]*')
    839             if re.search('^%s$' % (host_re,), hostname, re.I):
    840                 return True
    841 
    842         return False
    843 
    844     def connect(self):
    845         msg = 'getaddrinfo returns an empty list'
    846         if self.proxy_info and self.proxy_info.isgood():
    847             use_proxy = True
    848             proxy_type, proxy_host, proxy_port, proxy_rdns, proxy_user, proxy_pass = self.proxy_info.astuple()
    849             host = proxy_host
    850             port = proxy_port
    851         else:
    852             use_proxy = False
    853             host = self.host
    854             port = self.port
    855         address_info = socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM)
    856         for family, socktype, proto, canonname, sockaddr in address_info:
    857             try:
    858                 if use_proxy:
    859                     sock = socks.socksocket(family, socktype, proto)
    860                     sock.setproxy(proxy_type, proxy_host, proxy_port, proxy_rdns, proxy_user, proxy_pass)
    861                 else:
    862                     sock = socket.socket(family, socktype, proto)
    863                     sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
    864                 if has_timeout(self.timeout):
    865                     sock.settimeout(self.timeout)
    866                 sock.connect((self.host, self.port))
    867                 self.sock = _ssl_wrap_socket(sock, self.key_file, self.cert_file, self.disable_ssl_certificate_validation, self.ca_certs)
    868                 if self.debuglevel > 0:
    869                     print('connect: (%s, %s)' % (self.host, self.port))
    870                     if use_proxy:
    871                         print('proxy: %s' % str((proxy_host,
    872                          proxy_port,
    873                          proxy_rdns,
    874                          proxy_user,
    875                          proxy_pass)))
    876                 if not self.disable_ssl_certificate_validation:
    877                     cert = self.sock.getpeercert()
    878                     hostname = self.host.split(':', 0)[0]
    879                     if not self._ValidateCertificateHostname(cert, hostname):
    880                         raise CertificateHostnameMismatch('Server presented certificate that does not match host %s: %s' % (hostname, cert), hostname, cert)
    881             except ssl_SSLError as e:
    882                 if sock:
    883                     sock.close()
    884                 if self.sock:
    885                     self.sock.close()
    886                 self.sock = None
    887                 if e.errno == ssl.SSL_ERROR_SSL:
    888                     raise SSLHandshakeError(e)
    889                 else:
    890                     raise
    891             except (socket.timeout, socket.gaierror):
    892                 raise
    893             except socket.error as msg:
    894                 if self.debuglevel > 0:
    895                     print('connect fail: (%s, %s)' % (self.host, self.port))
    896                     if use_proxy:
    897                         print('proxy: %s' % str((proxy_host,
    898                          proxy_port,
    899                          proxy_rdns,
    900                          proxy_user,
    901                          proxy_pass)))
    902                 if self.sock:
    903                     self.sock.close()
    904                 self.sock = None
    905                 continue
    906 
    907             break
    908 
    909         if not self.sock:
    910             raise socket.error, msg
    911 
    912 
    913 SCHEME_TO_CONNECTION = {'http': HTTPConnectionWithTimeout,
    914  'https': HTTPSConnectionWithTimeout}
    915 try:
    916     try:
    917         from google.appengine.api import apiproxy_stub_map
    918         if apiproxy_stub_map.apiproxy.GetStub('urlfetch') is None:
    919             raise ImportError
    920         from google.appengine.api.urlfetch import fetch
    921         from google.appengine.api.urlfetch import InvalidURLError
    922     except (ImportError, AttributeError):
    923         from google3.apphosting.api import apiproxy_stub_map
    924         if apiproxy_stub_map.apiproxy.GetStub('urlfetch') is None:
    925             raise ImportError
    926         from google3.apphosting.api.urlfetch import fetch
    927         from google3.apphosting.api.urlfetch import InvalidURLError
    928 
    929     def _new_fixed_fetch(validate_certificate):
    930 
    931         def fixed_fetch(url, payload = None, method = 'GET', headers = {}, allow_truncated = False, follow_redirects = True, deadline = None):
    932             if deadline is None:
    933                 deadline = socket.getdefaulttimeout() or 5
    934             return fetch(url, payload=payload, method=method, headers=headers, allow_truncated=allow_truncated, follow_redirects=follow_redirects, deadline=deadline, validate_certificate=validate_certificate)
    935 
    936         return fixed_fetch
    937 
    938 
    939     class AppEngineHttpConnection(httplib.HTTPConnection):
    940 
    941         def __init__(self, host, port = None, key_file = None, cert_file = None, strict = None, timeout = None, proxy_info = None, ca_certs = None, disable_ssl_certificate_validation = False):
    942             httplib.HTTPConnection.__init__(self, host, port=port, strict=strict, timeout=timeout)
    943 
    944 
    945     class AppEngineHttpsConnection(httplib.HTTPSConnection):
    946 
    947         def __init__(self, host, port = None, key_file = None, cert_file = None, strict = None, timeout = None, proxy_info = None, ca_certs = None, disable_ssl_certificate_validation = False):
    948             httplib.HTTPSConnection.__init__(self, host, port=port, key_file=key_file, cert_file=cert_file, strict=strict, timeout=timeout)
    949             self._fetch = _new_fixed_fetch(not disable_ssl_certificate_validation)
    950 
    951 
    952     SCHEME_TO_CONNECTION = {'http': AppEngineHttpConnection,
    953      'https': AppEngineHttpsConnection}
    954 except (ImportError, AttributeError):
    955     pass
    956 
    957 class Http(object):
    958 
    959     def __init__(self, cache = None, timeout = None, proxy_info = proxy_info_from_environment, ca_certs = None, disable_ssl_certificate_validation = False):
    960         self.proxy_info = proxy_info
    961         self.ca_certs = ca_certs
    962         self.disable_ssl_certificate_validation = disable_ssl_certificate_validation
    963         self.connections = {}
    964         if cache and isinstance(cache, basestring):
    965             self.cache = FileCache(cache)
    966         else:
    967             self.cache = cache
    968         self.credentials = Credentials()
    969         self.certificates = KeyCerts()
    970         self.authorizations = []
    971         self.follow_redirects = True
    972         self.optimistic_concurrency_methods = ['PUT', 'PATCH']
    973         self.follow_all_redirects = False
    974         self.ignore_etag = False
    975         self.force_exception_to_status_code = False
    976         self.timeout = timeout
    977         self.forward_authorization_headers = False
    978 
    979     def __getstate__(self):
    980         state_dict = copy.copy(self.__dict__)
    981         if 'request' in state_dict:
    982             del state_dict['request']
    983         if 'connections' in state_dict:
    984             del state_dict['connections']
    985         return state_dict
    986 
    987     def __setstate__(self, state):
    988         self.__dict__.update(state)
    989         self.connections = {}
    990 
    991     def _auth_from_challenge(self, host, request_uri, headers, response, content):
    992         challenges = _parse_www_authenticate(response, 'www-authenticate')
    993         for cred in self.credentials.iter(host):
    994             for scheme in AUTH_SCHEME_ORDER:
    995                 if scheme in challenges:
    996                     yield AUTH_SCHEME_CLASSES[scheme](cred, host, request_uri, headers, response, content, self)
    997 
    998     def add_credentials(self, name, password, domain = ''):
    999         self.credentials.add(name, password, domain)
   1000 
   1001     def add_certificate(self, key, cert, domain):
   1002         self.certificates.add(key, cert, domain)
   1003 
   1004     def clear_credentials(self):
   1005         self.credentials.clear()
   1006         self.authorizations = []
   1007 
   1008     def _conn_request(self, conn, request_uri, method, body, headers):
   1009         i = 0
   1010         seen_bad_status_line = False
   1011         while i < RETRIES:
   1012             i += 1
   1013             try:
   1014                 if hasattr(conn, 'sock') and conn.sock is None:
   1015                     conn.connect()
   1016                 conn.request(method, request_uri, body, headers)
   1017             except socket.timeout:
   1018                 raise
   1019             except socket.gaierror:
   1020                 conn.close()
   1021                 raise ServerNotFoundError('Unable to find the server at %s' % conn.host)
   1022             except ssl_SSLError:
   1023                 conn.close()
   1024                 raise
   1025             except socket.error as e:
   1026                 err = 0
   1027                 if hasattr(e, 'args'):
   1028                     err = getattr(e, 'args')[0]
   1029                 else:
   1030                     err = e.errno
   1031                 if err in (errno.ENETUNREACH, errno.EADDRNOTAVAIL) and i < RETRIES:
   1032                     continue
   1033                 raise
   1034             except httplib.HTTPException:
   1035                 if hasattr(conn, 'sock') and conn.sock is None:
   1036                     if i < RETRIES - 1:
   1037                         conn.close()
   1038                         conn.connect()
   1039                         continue
   1040                     else:
   1041                         conn.close()
   1042                         raise
   1043                 if i < RETRIES - 1:
   1044                     conn.close()
   1045                     conn.connect()
   1046                     continue
   1047 
   1048             try:
   1049                 response = conn.getresponse()
   1050             except httplib.BadStatusLine:
   1051                 if not seen_bad_status_line and i == 1:
   1052                     i = 0
   1053                     seen_bad_status_line = True
   1054                     conn.close()
   1055                     conn.connect()
   1056                     continue
   1057                 else:
   1058                     conn.close()
   1059                     raise
   1060             except (socket.error, httplib.HTTPException):
   1061                 if i < RETRIES - 1:
   1062                     conn.close()
   1063                     conn.connect()
   1064                     continue
   1065                 else:
   1066                     conn.close()
   1067                     raise
   1068             else:
   1069                 content = ''
   1070                 if method == 'HEAD':
   1071                     conn.close()
   1072                 else:
   1073                     content = response.read()
   1074                 response = Response(response)
   1075                 if method != 'HEAD':
   1076                     content = _decompressContent(response, content)
   1077 
   1078             break
   1079 
   1080         return (response, content)
   1081 
   1082     def _request(self, conn, host, absolute_uri, request_uri, method, body, headers, redirections, cachekey):
   1083         auths = [ (auth.depth(request_uri), auth) for auth in self.authorizations if auth.inscope(host, request_uri) ]
   1084         auth = auths and sorted(auths)[0][1] or None
   1085         if auth:
   1086             auth.request(method, request_uri, headers, body)
   1087         response, content = self._conn_request(conn, request_uri, method, body, headers)
   1088         if auth:
   1089             if auth.response(response, body):
   1090                 auth.request(method, request_uri, headers, body)
   1091                 response, content = self._conn_request(conn, request_uri, method, body, headers)
   1092                 response._stale_digest = 1
   1093         if response.status == 401:
   1094             for authorization in self._auth_from_challenge(host, request_uri, headers, response, content):
   1095                 authorization.request(method, request_uri, headers, body)
   1096                 response, content = self._conn_request(conn, request_uri, method, body, headers)
   1097                 if response.status != 401:
   1098                     self.authorizations.append(authorization)
   1099                     authorization.response(response, body)
   1100                     break
   1101 
   1102         if self.follow_all_redirects or method in ('GET', 'HEAD') or response.status == 303:
   1103             if self.follow_redirects and response.status in (300, 301, 302, 303, 307):
   1104                 if redirections:
   1105                     if 'location' not in response and response.status != 300:
   1106                         raise RedirectMissingLocation(_('Redirected but the response is missing a Location: header.'), response, content)
   1107                     if 'location' in response:
   1108                         location = response['location']
   1109                         scheme, authority, path, query, fragment = parse_uri(location)
   1110                         if authority == None:
   1111                             response['location'] = urlparse.urljoin(absolute_uri, location)
   1112                     if response.status == 301 and method in ('GET', 'HEAD'):
   1113                         response['-x-permanent-redirect-url'] = response['location']
   1114                         if 'content-location' not in response:
   1115                             response['content-location'] = absolute_uri
   1116                         _updateCache(headers, response, content, self.cache, cachekey)
   1117                     if 'if-none-match' in headers:
   1118                         del headers['if-none-match']
   1119                     if 'if-modified-since' in headers:
   1120                         del headers['if-modified-since']
   1121                     if 'authorization' in headers and not self.forward_authorization_headers:
   1122                         del headers['authorization']
   1123                     if 'location' in response:
   1124                         location = response['location']
   1125                         old_response = copy.deepcopy(response)
   1126                         if 'content-location' not in old_response:
   1127                             old_response['content-location'] = absolute_uri
   1128                         redirect_method = method
   1129                         if response.status in (302, 303):
   1130                             redirect_method = 'GET'
   1131                             body = None
   1132                         response, content = self.request(location, method=redirect_method, body=body, headers=headers, redirections=redirections - 1)
   1133                         response.previous = old_response
   1134                 else:
   1135                     raise RedirectLimit('Redirected more times than rediection_limit allows.', response, content)
   1136             elif response.status in (200, 203) and method in ('GET', 'HEAD'):
   1137                 if 'content-location' not in response:
   1138                     response['content-location'] = absolute_uri
   1139                 _updateCache(headers, response, content, self.cache, cachekey)
   1140         return (response, content)
   1141 
   1142     def _normalize_headers(self, headers):
   1143         return _normalize_headers(headers)
   1144 
   1145     def request(self, uri, method = 'GET', body = None, headers = None, redirections = DEFAULT_MAX_REDIRECTS, connection_type = None):
   1146         try:
   1147             if headers is None:
   1148                 headers = {}
   1149             else:
   1150                 headers = self._normalize_headers(headers)
   1151             if 'user-agent' not in headers:
   1152                 headers['user-agent'] = 'Python-httplib2/%s (gzip)' % __version__
   1153             uri = iri2uri(uri)
   1154             scheme, authority, request_uri, defrag_uri = urlnorm(uri)
   1155             domain_port = authority.split(':')[0:2]
   1156             if len(domain_port) == 2 and domain_port[1] == '443' and scheme == 'http':
   1157                 scheme = 'https'
   1158                 authority = domain_port[0]
   1159             proxy_info = self._get_proxy_info(scheme, authority)
   1160             conn_key = scheme + ':' + authority
   1161             if conn_key in self.connections:
   1162                 conn = self.connections[conn_key]
   1163             else:
   1164                 if not connection_type:
   1165                     connection_type = SCHEME_TO_CONNECTION[scheme]
   1166                 certs = list(self.certificates.iter(authority))
   1167                 if scheme == 'https':
   1168                     if certs:
   1169                         conn = self.connections[conn_key] = connection_type(authority, key_file=certs[0][0], cert_file=certs[0][1], timeout=self.timeout, proxy_info=proxy_info, ca_certs=self.ca_certs, disable_ssl_certificate_validation=self.disable_ssl_certificate_validation)
   1170                     else:
   1171                         conn = self.connections[conn_key] = connection_type(authority, timeout=self.timeout, proxy_info=proxy_info, ca_certs=self.ca_certs, disable_ssl_certificate_validation=self.disable_ssl_certificate_validation)
   1172                 else:
   1173                     conn = self.connections[conn_key] = connection_type(authority, timeout=self.timeout, proxy_info=proxy_info)
   1174                 conn.set_debuglevel(debuglevel)
   1175             if 'range' not in headers and 'accept-encoding' not in headers:
   1176                 headers['accept-encoding'] = 'gzip, deflate'
   1177             info = email.Message.Message()
   1178             cached_value = None
   1179             if self.cache:
   1180                 cachekey = defrag_uri.encode('utf-8')
   1181                 cached_value = self.cache.get(cachekey)
   1182                 if cached_value:
   1183                     try:
   1184                         info, content = cached_value.split('\r\n\r\n', 1)
   1185                         feedparser = email.FeedParser.FeedParser()
   1186                         feedparser.feed(info)
   1187                         info = feedparser.close()
   1188                         feedparser._parse = None
   1189                     except (IndexError, ValueError):
   1190                         self.cache.delete(cachekey)
   1191                         cachekey = None
   1192                         cached_value = None
   1193 
   1194             else:
   1195                 cachekey = None
   1196             if method in self.optimistic_concurrency_methods and self.cache and 'etag' in info and not self.ignore_etag and 'if-match' not in headers:
   1197                 headers['if-match'] = info['etag']
   1198             if method not in ('GET', 'HEAD') and self.cache and cachekey:
   1199                 self.cache.delete(cachekey)
   1200             if method in ('GET', 'HEAD') and 'vary' in info:
   1201                 vary = info['vary']
   1202                 vary_headers = vary.lower().replace(' ', '').split(',')
   1203                 for header in vary_headers:
   1204                     key = '-varied-%s' % header
   1205                     value = info[key]
   1206                     if headers.get(header, None) != value:
   1207                         cached_value = None
   1208                         break
   1209 
   1210             if cached_value and method in ('GET', 'HEAD') and self.cache and 'range' not in headers:
   1211                 if '-x-permanent-redirect-url' in info:
   1212                     if redirections <= 0:
   1213                         raise RedirectLimit('Redirected more times than rediection_limit allows.', {}, '')
   1214                     response, new_content = self.request(info['-x-permanent-redirect-url'], method='GET', headers=headers, redirections=redirections - 1)
   1215                     response.previous = Response(info)
   1216                     response.previous.fromcache = True
   1217                 else:
   1218                     entry_disposition = _entry_disposition(info, headers)
   1219                     if entry_disposition == 'FRESH':
   1220                         if not cached_value:
   1221                             info['status'] = '504'
   1222                             content = ''
   1223                         response = Response(info)
   1224                         if cached_value:
   1225                             response.fromcache = True
   1226                         return (response, content)
   1227                     if entry_disposition == 'STALE':
   1228                         if 'etag' in info and not self.ignore_etag and 'if-none-match' not in headers:
   1229                             headers['if-none-match'] = info['etag']
   1230                         if 'last-modified' in info and 'last-modified' not in headers:
   1231                             headers['if-modified-since'] = info['last-modified']
   1232                     elif entry_disposition == 'TRANSPARENT':
   1233                         pass
   1234                     response, new_content = self._request(conn, authority, uri, request_uri, method, body, headers, redirections, cachekey)
   1235                 if response.status == 304 and method == 'GET':
   1236                     for key in _get_end2end_headers(response):
   1237                         info[key] = response[key]
   1238 
   1239                     merged_response = Response(info)
   1240                     if hasattr(response, '_stale_digest'):
   1241                         merged_response._stale_digest = response._stale_digest
   1242                     _updateCache(headers, merged_response, content, self.cache, cachekey)
   1243                     response = merged_response
   1244                     response.status = 200
   1245                     response.fromcache = True
   1246                 elif response.status == 200:
   1247                     content = new_content
   1248                 else:
   1249                     self.cache.delete(cachekey)
   1250                     content = new_content
   1251             else:
   1252                 cc = _parse_cache_control(headers)
   1253                 if 'only-if-cached' in cc:
   1254                     info['status'] = '504'
   1255                     response = Response(info)
   1256                     content = ''
   1257                 else:
   1258                     response, content = self._request(conn, authority, uri, request_uri, method, body, headers, redirections, cachekey)
   1259         except Exception as e:
   1260             if self.force_exception_to_status_code:
   1261                 if isinstance(e, HttpLib2ErrorWithResponse):
   1262                     response = e.response
   1263                     content = e.content
   1264                     response.status = 500
   1265                     response.reason = str(e)
   1266                 elif isinstance(e, socket.timeout):
   1267                     content = 'Request Timeout'
   1268                     response = Response({'content-type': 'text/plain',
   1269                      'status': '408',
   1270                      'content-length': len(content)})
   1271                     response.reason = 'Request Timeout'
   1272                 else:
   1273                     content = str(e)
   1274                     response = Response({'content-type': 'text/plain',
   1275                      'status': '400',
   1276                      'content-length': len(content)})
   1277                     response.reason = 'Bad Request'
   1278             else:
   1279                 raise
   1280 
   1281         return (response, content)
   1282 
   1283     def _get_proxy_info(self, scheme, authority):
   1284         hostname, port = urllib.splitport(authority)
   1285         proxy_info = self.proxy_info
   1286         if callable(proxy_info):
   1287             proxy_info = proxy_info(scheme)
   1288         if hasattr(proxy_info, 'applies_to') and not proxy_info.applies_to(hostname):
   1289             proxy_info = None
   1290         return proxy_info
   1291 
   1292 
   1293 class Response(dict):
   1294     fromcache = False
   1295     version = 11
   1296     status = 200
   1297     reason = 'Ok'
   1298     previous = None
   1299 
   1300     def __init__(self, info):
   1301         if isinstance(info, httplib.HTTPResponse):
   1302             for key, value in info.getheaders():
   1303                 self[key.lower()] = value
   1304 
   1305             self.status = info.status
   1306             self['status'] = str(self.status)
   1307             self.reason = info.reason
   1308             self.version = info.version
   1309         elif isinstance(info, email.Message.Message):
   1310             for key, value in info.items():
   1311                 self[key.lower()] = value
   1312 
   1313             self.status = int(self['status'])
   1314         else:
   1315             for key, value in info.iteritems():
   1316                 self[key.lower()] = value
   1317 
   1318             self.status = int(self.get('status', self.status))
   1319             self.reason = self.get('reason', self.reason)
   1320 
   1321     def __getattr__(self, name):
   1322         if name == 'dict':
   1323             return self
   1324         raise AttributeError, name