blob: 265f64534cdf5a2b3dd1d26b56b8c9f069b9cfed [file] [log] [blame]
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import absolute_import
try:
import simplejson as json
except:
import json
from pipes import quote as pquote
from xml.dom.minidom import parseString
import os
from libcloud.common.base import (LibcloudConnection,
HttpLibResponseProxy)
from libcloud.utils.py3 import _real_unicode as u
from libcloud.utils.misc import lowercase_keys
class LoggingConnection(LibcloudConnection):
"""
Debug class to log all HTTP(s) requests as they could be made
with the curl command.
:cvar log: file-like object that logs entries are written to.
"""
protocol = 'https'
log = None
http_proxy_used = False
def _log_response(self, r):
rv = "# -------- begin %d:%d response ----------\n" % (id(self), id(r))
ht = ""
v = r.version
if r.version == 10:
v = "HTTP/1.0"
if r.version == 11:
v = "HTTP/1.1"
ht += "%s %s %s\r\n" % (v, r.status, r.reason)
body = r.read()
for h in r.getheaders():
ht += "%s: %s\r\n" % (h[0].title(), h[1])
ht += "\r\n"
headers = lowercase_keys(dict(r.getheaders()))
content_type = headers.get('content-type', None)
pretty_print = os.environ.get('LIBCLOUD_DEBUG_PRETTY_PRINT_RESPONSE',
False)
if pretty_print and content_type == 'application/json':
try:
body = json.loads(body.decode('utf-8'))
body = json.dumps(body, sort_keys=True, indent=4)
except:
# Invalid JSON or server is lying about content-type
pass
elif pretty_print and content_type == 'text/xml':
try:
elem = parseString(body.decode('utf-8'))
body = elem.toprettyxml()
except Exception:
# Invalid XML
pass
ht += u(body)
rv += ht
rv += ("\n# -------- end %d:%d response ----------\n"
% (id(self), id(r)))
return rv
def _log_curl(self, method, url, body, headers):
cmd = ["curl"]
if self.http_proxy_used:
if self.proxy_username and self.proxy_password:
proxy_url = 'http://%s:%s@%s:%s' % (self.proxy_username,
self.proxy_password,
self.proxy_host,
self.proxy_port)
else:
proxy_url = 'http://%s:%s' % (self.proxy_host,
self.proxy_port)
proxy_url = pquote(proxy_url)
cmd.extend(['--proxy', proxy_url])
cmd.extend(['-i'])
if method.lower() == 'head':
# HEAD method need special handling
cmd.extend(["--head"])
else:
cmd.extend(["-X", pquote(method)])
for h in headers:
cmd.extend(["-H", pquote("%s: %s" % (h, headers[h]))])
cert_file = getattr(self, 'cert_file', None)
if cert_file:
cmd.extend(["--cert", pquote(cert_file)])
# TODO: in python 2.6, body can be a file-like object.
if body is not None and len(body) > 0:
cmd.extend(["--data-binary", pquote(body)])
cmd.extend(["--compress"])
cmd.extend([pquote("%s%s" % (self.host, url))])
return " ".join(cmd)
def getresponse(self):
original_response = LibcloudConnection.getresponse(self)
if self.log is not None:
rv = self._log_response(HttpLibResponseProxy(original_response))
self.log.write(u(rv + "\n"))
self.log.flush()
return original_response
def request(self, method, url, body=None, headers=None, **kwargs):
headers.update({'X-LC-Request-ID': str(id(self))})
if self.log is not None:
pre = "# -------- begin %d request ----------\n" % id(self)
self.log.write(u(pre +
self._log_curl(method, url, body, headers) +
"\n"))
self.log.flush()
return LibcloudConnection.request(self, method, url, body,
headers)