""" koji.compatrequests ~~~~~~~~~~~~~~~~~~~ This module contains a *very limited* partial implemention of the requests module that is based on the older codepaths in koji. It only provides the bits that koji needs. """ import httplib import urlparse import urllib import sys import ssl.SSLCommon try: from ssl import ssl as pyssl except ImportError: # pragma: no cover pass class Session(object): def __init__(self): self.connection = None def post(self, url, data=None, headers=None, stream=None, verify=None, cert=None, timeout=None): uri = urlparse.urlsplit(url) if uri[3]: handler = "%s?%s" % (uri[2], uri[3]) else: handler = uri[2] cnx = self.get_connection(uri, cert, verify, timeout) #cnx.set_debuglevel(1) cnx.putrequest('POST', handler) if headers: for k in headers: cnx.putheader(k, headers[k]) cnx.endheaders() if data is not None: cnx.send(data) response = cnx.getresponse() return Response(self, response) def get_connection(self, uri, cert, verify, timeout): scheme = uri[0] host, port = urllib.splitport(uri[1]) key = (scheme, host, cert, verify, timeout) #if self.connection and self.opts.get('keepalive'): if self.connection: # XXX honor keepalive if key == self.connection[0]: cnx = self.connection[1] if getattr(cnx, 'sock', None): return cnx # Otherwise we make a new one default_port = 80 certs = {} if isinstance(verify, basestring): certs['peer_ca_cert'] = verify if cert: certs['key_and_cert'] = cert ctx = ssl.SSLCommon.CreateSSLContext(certs) cnxOpts = {'ssl_context' : ctx} cnxClass = ssl.SSLCommon.PlgHTTPSConnection default_port = 443 elif scheme == 'https': cnxOpts = {} if verify: if sys.version_info[:3] >= (2, 7, 9): try: proto = pyssl.PROTOCOL_TLS except AttributeError: proto = pyssl.PROTOCOL_SSLv23 ctx = pyssl.SSLContext(proto) ctx.load_verify_locations(cafile=verify) ctx.verify_mode = pyssl.CERT_REQUIRED cnxOpts['context'] = ctx else: cnxOpts['cert_file'] = verify elif verify is None: # not specified, leave as default pass elif sys.version_info[:3] >= (2, 7, 9): # no verify ctx = pyssl._create_unverified_context() cnxOpts['context'] = ctx cnxClass = httplib.HTTPSConnection default_port = 443 elif scheme == 'http': cnxOpts = {} cnxClass = httplib.HTTPConnection else: raise IOError, "unsupported protocol: %s" % scheme timeout_compat = False if timeout: if sys.version_info[:3] < (2, 6, 0) and 'ssl_context' not in cnxOpts: timeout_compat = True else: cnxOpts['timeout'] = timeout # no need to close connection port = (port and int(port) or default_port) cnx = cnxClass(host, port, **cnxOpts) self.connection = (key, cnx) if timeout_compat: # in python < 2.6 httplib does not support the timeout option # but socket supports it since 2.3 cnx.connect() cnx.sock.settimeout(timeout) return cnx def close(self): if self.connection: self.connection[1].close() self.connection = None class Response(object): def __init__(self, session, response): self.session = session self.response = response def iter_content(self, blocksize=8192): # should we check this in Session.post()? # should we even check this here? if self.response.status != 200: if (self.response.getheader("content-length", 0)): self.response.read() # XXX wrong exception raise Exception("Server status: %s" % self.response.status) while True: chunk = self.response.read(blocksize) if not chunk: break yield chunk def close(self): self.response.close()