1 # Copyright 2011-2012 GRNET S.A. All rights reserved.
3 # Redistribution and use in source and binary forms, with or
4 # without modification, are permitted provided that the following
7 # 1. Redistributions of source code must retain the above
8 # copyright notice, self.list of conditions and the following
11 # 2. Redistributions in binary form must reproduce the above
12 # copyright notice, self.list of conditions and the following
13 # disclaimer in the documentation and/or other materials
14 # provided with the distribution.
16 # THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 # OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 # USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 # POSSIBILITY OF SUCH DAMAGE.
29 # The views and conclusions contained in the software and
30 # documentation are those of the authors and should not be
31 # interpreted as representing official policies, either expressed
32 # or implied, of GRNET S.A.
36 from kamaki.clients.connection import HTTPConnectionError
37 #from .connection.request import HTTPRequest
38 from kamaki.clients.connection.kamakicon import KamakiHTTPConnection
40 sendlog = logging.getLogger('clients.send')
41 recvlog = logging.getLogger('clients.recv')
43 class ClientError(Exception):
44 def __init__(self, message, status=0, details=''):
45 super(ClientError, self).__init__(message, status, details)
46 self.message = message
48 self.details = details
52 def __init__(self, base_url, token, http_client=KamakiHTTPConnection()):
53 #def __init__(self, base_url, token, http_client=HTTPRequest()):
54 self.base_url = base_url
57 self.DATE_FORMATS = ["%a %b %d %H:%M:%S %Y",
58 "%A, %d-%b-%y %H:%M:%S GMT",
59 "%a, %d %b %Y %H:%M:%S GMT"]
60 self.http_client = http_client
62 def _raise_for_status(self, r):
63 message = "%d %s" % (r.status_code, r.status)
68 raise ClientError(message, r.status_code, details)
70 def set_header(self, name, value, iff=True):
71 """Set a header 'name':'value' provided value is not None and iff is True"""
72 if value is not None and iff:
73 self.http_client.set_header(name, value)
75 def set_param(self, name, value=None, iff=True):
77 self.http_client.set_param(name, value)
79 def set_default_header(self, name, value):
80 self.http_client.headers.setdefault(name, value)
88 """In threaded/asynchronous requests, headers and params are not safe
89 Therefore, the standard self.set_header/param system can be used only for
90 headers and params that are common for all requests. All other params and
91 headers should passes as
94 E.g. in most queries the 'X-Auth-Token' header might be the same for all, but the
95 'Range' header might be different from request to request.
99 success = kwargs.pop('success', 200)
101 data = kwargs.pop('data', None)
102 self.set_default_header('X-Auth-Token', self.token)
105 data = json.dumps(kwargs.pop('json'))
106 self.set_default_header('Content-Type', 'application/json')
108 self.set_default_header('Content-Length', unicode(len(data)))
110 self.http_client.url = self.base_url + path
111 r = self.http_client.perform_request(method, data, async_headers, async_params)
113 req = self.http_client
114 sendlog.info('%s %s', method, req.url)
115 headers = dict(req.headers)
116 headers.update(async_headers)
118 for key, val in headers.items():
119 sendlog.info('\t%s: %s', key, val)
122 sendlog.info('%s', data)
124 recvlog.info('%d %s', r.status_code, r.status)
125 for key, val in r.headers.items():
126 recvlog.info('%s: %s', key, val)
128 # recvlog.debug(r.content)
130 if success is not None:
131 # Success can either be an in or a collection
132 success = (success,) if isinstance(success, int) else success
133 if r.status_code not in success:
134 self._raise_for_status(r)
135 except Exception as err:
136 self.http_client.reset_headers()
137 self.http_client.reset_params()
138 if isinstance(err, HTTPConnectionError):
139 raise ClientError(message=err.message, status=err.status, details=err.details)
142 self.http_client.reset_headers()
143 self.http_client.reset_params()
146 def delete(self, path, **kwargs):
147 return self.request('delete', path, **kwargs)
149 def get(self, path, **kwargs):
150 return self.request('get', path, **kwargs)
152 def head(self, path, **kwargs):
153 return self.request('head', path, **kwargs)
155 def post(self, path, **kwargs):
156 return self.request('post', path, **kwargs)
158 def put(self, path, **kwargs):
159 return self.request('put', path, **kwargs)
161 def copy(self, path, **kwargs):
162 return self.request('copy', path, **kwargs)
164 def move(self, path, **kwargs):
165 return self.request('move', path, **kwargs)