mirror of
https://github.com/OpenMTC/OpenMTC.git
synced 2025-06-15 21:28:08 +00:00
328 lines
8.8 KiB
Python
328 lines
8.8 KiB
Python
'''
|
|
Created on 21.05.2011
|
|
|
|
@author: kca
|
|
'''
|
|
|
|
from base64 import b64encode
|
|
from cStringIO import StringIO
|
|
from logging import DEBUG
|
|
from socket import getservbyname
|
|
from urllib2 import quote
|
|
from urlparse import urlparse
|
|
|
|
#import vertx
|
|
|
|
from aplus import Promise
|
|
from futile import ObjectProxy
|
|
from futile.logging import LoggerMixin
|
|
from futile.net.http.exc import NetworkError, HTTPError
|
|
|
|
|
|
def compose_qs(values):
|
|
return "&".join([ "%s=%s" % (quote(k), quote(v)) for k, v in dict(values).iteritems() ])
|
|
|
|
class LoggingResponseWrapper(LoggerMixin, ObjectProxy):
|
|
def __init__(self, response, *args, **kw):
|
|
super(LoggingResponseWrapper, self).__init__(proxyobject = response, *args, **kw)
|
|
self.__buffer = StringIO()
|
|
self.__finalized = False
|
|
|
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
self.close()
|
|
|
|
def __enter__(self):
|
|
return self
|
|
|
|
def read(self, n = None):
|
|
s = self._o.read(n)
|
|
self.__buffer.write(s)
|
|
return s
|
|
|
|
def readline(self):
|
|
s = self._o.readline()
|
|
self.__buffer.write(s)
|
|
return s
|
|
|
|
def readlines(self, sizehint = None):
|
|
lines = self._o.readlines(sizehint)
|
|
self.__buffer.write(''.join(lines))
|
|
return lines
|
|
|
|
def close(self):
|
|
if self.__finalized:
|
|
self.logger.debug("%s is already finalized" % (self, ))
|
|
return
|
|
|
|
self.__finalized = True
|
|
try:
|
|
if not self._o.isclosed():
|
|
self.__buffer.write(self._o.read())
|
|
self.logger.debug("Read data:\n %s", self.__buffer.getvalue())
|
|
except:
|
|
self.logger.exception("Finalizing response failed")
|
|
finally:
|
|
self._o.close()
|
|
|
|
self.__buffer.close()
|
|
|
|
|
|
class CachingHttplibResponseWrapper(ObjectProxy, LoggerMixin):
|
|
def __init__(self, response, path, tag, last_modified, cache, *args, **kw):
|
|
super(CachingHttplibResponseWrapper, self).__init__(proxyobject = response, *args, **kw)
|
|
self.__cache = cache
|
|
self.__buffer = StringIO()
|
|
self.__path = path
|
|
self.__tag = tag
|
|
self.__last_modified = last_modified
|
|
self.__finalized = False
|
|
|
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
self.close()
|
|
|
|
def __enter__(self):
|
|
return self
|
|
|
|
def read(self, n = None):
|
|
s = self._o.read(n)
|
|
self.__buffer.write(s)
|
|
return s
|
|
|
|
def readline(self):
|
|
s = self._o.readline()
|
|
self.__buffer.write(s)
|
|
return s
|
|
|
|
def readlines(self, sizehint = None):
|
|
lines = self._o.readlines(sizehint)
|
|
self.__buffer.write(''.join(lines))
|
|
return lines
|
|
|
|
def close(self):
|
|
if self.__finalized:
|
|
self.logger.debug("%s is already finalized" % (self, ))
|
|
return
|
|
|
|
self.__finalized = True
|
|
try:
|
|
if not self._o.isclosed():
|
|
self.__buffer.write(self._o.read())
|
|
val = self.__buffer.getvalue()
|
|
self.logger.debug("Putting to cache: %s -> %s, %s\n %s", self.__path, self.__tag, self.__last_modified, val)
|
|
self.__cache[self.__path] = (self.__tag, self.__last_modified, val)
|
|
except:
|
|
self.logger.exception("Finalizing response failed")
|
|
finally:
|
|
self._o.close()
|
|
|
|
self.__buffer.close()
|
|
|
|
def __getattr__(self, name):
|
|
return getattr(self._o, name)
|
|
|
|
|
|
class closing(ObjectProxy):
|
|
def __getattr__(self, k):
|
|
return getattr(self._o, k)
|
|
|
|
def __enter__(self):
|
|
return self._o
|
|
|
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
self._o.close()
|
|
|
|
def close(self):
|
|
self._o.close()
|
|
|
|
|
|
class RestClient(LoggerMixin):
|
|
ERROR_RESPONSE_MAX = 320
|
|
|
|
get_timeout = timeout = 120.0
|
|
|
|
def __init__(self, uri, username=None, password=None, certfile=None,
|
|
keyfile=None, content_type="text/plain", headers=None,
|
|
cache=True, timeout=None, get_timeout=None,
|
|
component_name = "server", connection_manager = None,
|
|
*args, **kw):
|
|
super(RestClient, self).__init__(*args, **kw)
|
|
|
|
self.logger.debug("Creating RestClient for %s", uri)
|
|
|
|
self.timeout = timeout or self.timeout
|
|
self.get_timeout = get_timeout or timeout or self.get_timeout
|
|
|
|
if cache:
|
|
if cache is True:
|
|
from futile.caching import LRUCache
|
|
cache = LRUCache()
|
|
self.__cache = cache
|
|
|
|
if "://" not in uri:
|
|
uri = "http://" + uri
|
|
|
|
self.__content_type = content_type
|
|
self.component_name = component_name
|
|
|
|
info = urlparse(uri)
|
|
|
|
if info.scheme == "https":
|
|
if bool(certfile) ^ bool(keyfile):
|
|
raise ValueError("Must give both certfile and keyfile if any")
|
|
if certfile:
|
|
from os.path import exists
|
|
if not exists(certfile):
|
|
raise ValueError("Certificate file not found: %s" % (certfile, ))
|
|
if not exists(keyfile):
|
|
raise ValueError("Key file not found: %s" % (keyfile, ))
|
|
elif info.scheme != "http":
|
|
raise ValueError(info.scheme)
|
|
|
|
port = info.port and int(info.port) or getservbyname(info.scheme)
|
|
|
|
self.__base = info.path or ""
|
|
#if not self.__base.endswith("/"):
|
|
# self.__base += "/"
|
|
|
|
if not username:
|
|
username = info.username
|
|
|
|
if not headers:
|
|
headers = {}
|
|
|
|
headers.setdefault("Accept", "*/*")
|
|
headers["Accept-Encoding"] = "identity"
|
|
|
|
if username:
|
|
password = password or info.password or ""
|
|
headers["Authorization"] = "Basic " + b64encode("%s:%s" % (username, password))
|
|
|
|
self.__headers = headers
|
|
|
|
#if not connection_manager:
|
|
# #from SimpleConnectionManager import SimpleConnectionManager as connection_manager
|
|
# from ConnectionPoolManager import ConnectionPoolManager as connection_manager
|
|
#
|
|
# self.__connection_manager = connection_manager(host = info.hostname, port = port,
|
|
# certfile = certfile, keyfile = keyfile, force_ssl = info.scheme == "https")
|
|
#
|
|
|
|
self.client= vertx.create_http_client()
|
|
self.client.host = info.netloc.split(":")[0]
|
|
self.client.port = port
|
|
|
|
#temporary test server
|
|
#import json
|
|
#self.srv = vertx.create_http_server()
|
|
#def srv_handle(re):
|
|
# re.response.put_header("Content-Type","application/json; charset=utf-8")
|
|
# re.response.put_header("Location","locationlocation.location")
|
|
# re.response.end(json.dumps({"One":"Two"}))
|
|
#self.srv.request_handler(srv_handle)
|
|
#self.srv.listen(5000)
|
|
|
|
def request(self, method, path, data = None, headers = {}, args = None):
|
|
if isinstance(data, unicode):
|
|
data = data.encode("utf-8")
|
|
fullpath = self.__base + path
|
|
request_headers = self.__headers.copy()
|
|
|
|
if args:
|
|
fullpath += ("?" in fullpath and "&" or "?") + compose_qs(args)
|
|
|
|
if headers:
|
|
request_headers.update(headers)
|
|
|
|
if method == "GET":
|
|
timeout = self.get_timeout
|
|
try:
|
|
etag, modified, cached = self.__cache[fullpath]
|
|
if etag:
|
|
request_headers["If-None-Match"] = etag
|
|
request_headers["If-Modified-Since"] = modified
|
|
except KeyError:
|
|
request_headers.pop("If-None-Match", None)
|
|
request_headers.pop("If-Modified-Since", None)
|
|
else:
|
|
timeout = self.timeout
|
|
request_headers.setdefault("Content-Type", self.__content_type)
|
|
|
|
log_headers = request_headers
|
|
if self.logger.isEnabledFor(DEBUG) and "Authorization" in request_headers:
|
|
log_headers = request_headers.copy()
|
|
log_headers["Authorization"] = "<purged>"
|
|
|
|
if method == "GET":
|
|
self.logger.debug("%s: %s (%s)", method, fullpath, log_headers)
|
|
else:
|
|
self.logger.debug("%s: %s (%s)\n%s", method, fullpath, log_headers, repr(data))
|
|
|
|
#t = time()
|
|
promise=Promise()
|
|
try:
|
|
#response = self.__connection_manager.request(method, fullpath, data, request_headers, timeout)
|
|
|
|
def response_handler(resp):
|
|
if resp.status_code == 304:
|
|
try:
|
|
promise.fulfill(closing(StringIO(cached)))
|
|
except NameError:
|
|
promise.reject(NetworkError("Error: The %s returned 304 though no cached version is available. Request was: %s %s" % (self.component_name, method, fullpath)))
|
|
if resp.status_code < 200 or resp.status_code >= 300:
|
|
try:
|
|
promise.reject(HTTPError(msg = resp.status_message, status = resp.status_code))
|
|
except:
|
|
promise.reject(HTTPError(msg = "Http error", status = response.status))
|
|
else:
|
|
promise.fulfill(resp)
|
|
|
|
req=self.client.request(method,fullpath,response_handler)
|
|
for head,value in request_headers.items():
|
|
req.put_header(head,value)
|
|
if data:
|
|
req.chunked = True
|
|
req.write_str(data)
|
|
req.end()
|
|
|
|
except Exception as e:
|
|
print "Exception triggered: %s"%e
|
|
promise.reject(e)
|
|
|
|
return promise
|
|
|
|
#if method == "DELETE":
|
|
# try:
|
|
# self.__cache.pop(fullpath, None)
|
|
# except AttributeError:
|
|
# pass
|
|
#else:
|
|
# etag = response.getheader("Etag")
|
|
# modified = response.getheader("Last-Modified")
|
|
# if etag or modified:
|
|
# if not modified:
|
|
# modified = datetime.utcnow().strftime("%a, %d %b %Y %X GMT")
|
|
# response = CachingHttplibResponseWrapper(response, fullpath, etag, modified, self.__cache)
|
|
# elif self.logger.isEnabledFor(DEBUG):
|
|
# response = LoggingResponseWrapper(response)
|
|
|
|
|
|
|
|
|
|
def get(self, path, headers = None, args = None):
|
|
p = self.request("GET", path, headers = headers, args = args)
|
|
return p
|
|
|
|
def post(self, path, data, headers = None):
|
|
p = self.request("POST", path, data, headers)
|
|
return p
|
|
add = post
|
|
|
|
def put(self, path, data, headers = None):
|
|
p = self.request("PUT", path, data)
|
|
return p
|
|
update = put
|
|
|
|
def delete(self, path, headers = None):
|
|
p = self.request("DELETE", path, None, headers)
|
|
return p
|