Files
python-glanceclient/glanceclient/common/http.py
T
Brian Waldon da360462a5 Wrap image data in iterator
This is establishing the API for a future optimization. We want to
be able to offer true socket-level caching, but can't do that with
httplib2 right now. For now, we will just fake the optimization
by returning an iterator over the image body, which happens to already
be fully loaded into a string.

Change-Id: I2d36e3cdd45b26d7c7c27ba050bf6a4b5765df6c
2012-07-11 19:55:02 -07:00

144 lines
4.5 KiB
Python

"""
OpenStack Client interface. Handles the REST calls and responses.
"""
import copy
import logging
import os
import StringIO
import urlparse
import httplib2
try:
import json
except ImportError:
import simplejson as json
# Python 2.5 compat fix
if not hasattr(urlparse, 'parse_qsl'):
import cgi
urlparse.parse_qsl = cgi.parse_qsl
from glanceclient.common import exceptions
logger = logging.getLogger(__name__)
USER_AGENT = 'python-glanceclient'
CHUNKSIZE = 1024 * 64 # 64kB
class HTTPClient(httplib2.Http):
def __init__(self, endpoint, token=None, timeout=600, insecure=False):
super(HTTPClient, self).__init__(timeout=timeout)
self.endpoint = endpoint
self.auth_token = token
# httplib2 overrides
self.force_exception_to_status_code = True
self.disable_ssl_certificate_validation = insecure
def http_log(self, args, kwargs, resp, body):
if os.environ.get('GLANCECLIENT_DEBUG', False):
ch = logging.StreamHandler()
logger.setLevel(logging.DEBUG)
logger.addHandler(ch)
elif not logger.isEnabledFor(logging.DEBUG):
return
string_parts = ['curl -i']
for element in args:
if element in ('GET', 'POST'):
string_parts.append(' -X %s' % element)
else:
string_parts.append(' %s' % element)
for element in kwargs['headers']:
header = ' -H "%s: %s"' % (element, kwargs['headers'][element])
string_parts.append(header)
logger.debug("REQ: %s\n" % "".join(string_parts))
if 'raw_body' in kwargs:
logger.debug("REQ BODY (RAW): %s\n" % (kwargs['raw_body']))
if 'body' in kwargs:
logger.debug("REQ BODY: %s\n" % (kwargs['body']))
logger.debug("RESP: %s\nRESP BODY: %s\n", resp, body)
def _http_request(self, url, method, **kwargs):
""" Send an http request with the specified characteristics.
Wrapper around httplib2.Http.request to handle tasks such as
setting headers, JSON encoding/decoding, and error handling.
"""
url = self.endpoint + url
# Copy the kwargs so we can reuse the original in case of redirects
kwargs['headers'] = copy.deepcopy(kwargs.get('headers', {}))
kwargs['headers'].setdefault('User-Agent', USER_AGENT)
if self.auth_token:
kwargs['headers'].setdefault('X-Auth-Token', self.auth_token)
resp, body = super(HTTPClient, self).request(url, method, **kwargs)
self.http_log((url, method,), kwargs, resp, body)
if 400 <= resp.status < 600:
logger.exception("Request returned failure status.")
raise exceptions.from_response(resp, body)
elif resp.status in (301, 302, 305):
# Redirected. Reissue the request to the new location.
return self._http_request(resp['location'], method, **kwargs)
#NOTE(bcwaldon): body has been loaded to a string at this point,
# but we want to move to a world where it can be read from a
# socket-level cache. This is here until we can do that.
body_iter = ResponseBodyIterator(StringIO.StringIO(body))
return resp, body_iter
def json_request(self, method, url, **kwargs):
kwargs.setdefault('headers', {})
kwargs['headers'].setdefault('Content-Type', 'application/json')
if 'body' in kwargs:
kwargs['body'] = json.dumps(kwargs['body'])
resp, body_iter = self._http_request(url, method, **kwargs)
body = ''.join([chunk for chunk in body_iter])
if body:
try:
body = json.loads(body)
except ValueError:
logger.debug("Could not decode JSON from body: %s" % body)
else:
logger.debug("No body was returned.")
body = None
return resp, body
def raw_request(self, method, url, **kwargs):
kwargs.setdefault('headers', {})
kwargs['headers'].setdefault('Content-Type',
'application/octet-stream')
return self._http_request(url, method, **kwargs)
class ResponseBodyIterator(object):
"""A class that acts as an iterator over an HTTP response."""
def __init__(self, resp):
self.resp = resp
def __iter__(self):
while True:
yield self.next()
def next(self):
chunk = self.resp.read(CHUNKSIZE)
if chunk:
return chunk
else:
raise StopIteration()