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.
35 from kamaki.clients.connection import HTTPConnection, HTTPResponse, HTTPConnectionError
36 from kamaki.clients.connection.pool import ObjectPool
37 from urlparse import urlparse
39 # Add a convenience status property to the responses
41 return requests.status_codes._codes[self.status_code][0].upper()
42 requests.Response.status = property(_status)
44 class HTTPRequestsResponse(HTTPResponse):
46 def __init__(self, request=None, prefetched=False):
47 super(HTTPRequestsResponse, self).__init__(request=request, prefetched=prefetched)
49 self = request.response
51 def _get_response(self):
54 r = self.request.response
56 self.headers = r.headers
57 self.status = r.status
58 self.status_code = r.status_code
59 self.content = r.content if hasattr(r, 'content') else None
60 from json import loads
62 self.json = loads(r.content)#None if self._get_content_only else r.json
65 self.text = r.content#None if self._get_content_only else r.text
66 self.exception = r.exception if hasattr(r, 'exception') else None
67 except requests.ConnectionError as err:
68 raise HTTPConnectionError('Connection error', status=651, details=err.message)
69 except requests.HTTPError as err:
70 raise HTTPConnectionError('HTTP error', status=652, details=err.message)
71 except requests.Timeout as err:
72 raise HTTPConnectionError('Connection Timeout', status=408, details=err.message)
73 except requests.URLRequired as err:
74 raise HTTPConnectionError('Invalid URL', status=404, details=err.message)
75 except requests.RequestException as err:
76 raise HTTPConnectionError('HTTP Request error', status=700, details=err.message)
80 """requests object handles this automatically"""
81 if hasattr(self, '_pool'):
82 self._pool.pool_put(self)
85 class HTTPRequestsResponsePool(ObjectPool):
86 def __init__(self, netloc, size=POOL_SIZE):
87 super(HTTPRequestsResponsePool, self).__init__(size=size)
90 def _pool_cleanup(self, resp):
95 def key(self, full_url):
96 p = urlparse(full_url)
97 return '%s:%s:%s'%(p.scheme,p.netloc, p.port)
99 def _pool_create(self):
100 resp = HTTPRequestsResponse()
104 class HTTPRequest(HTTPConnection):
108 #Avoid certificate verification by default
111 def _get_response_object(self):
112 pool_key = HTTPRequestsResponsePool.key(self.url)
114 respool = self._pools[pool_key]
116 self._pools[pool_key] = HTTPRequestsResponsePool(pool_key)
117 respool = self._pools[pool_key]
118 return respool.pool_get()
120 def perform_request(self, method=None, url=None, params=None, headers=None, data=None):
122 Example: method='PUT' url='https://my.server:8080/path/to/service'
123 params={'update':None, 'format':'json'} headers={'X-Auth-Token':'s0m3t0k3n=='}
124 data='The data body to put to server'
125 @return an HTTPResponse which is also stored as self.response
127 if method is not None:
131 if params is not None:
133 if headers is not None:
134 self.headers = headers
136 for k,v in self.headers.items():
137 http_headers[str(k)] = str(v)
139 for i,(key, val) in enumerate(self.params.items()):
140 param_str = ('?' if i == 0 else '&') + unicode(key)
142 param_str+= '='+unicode(val)
143 self.url += param_str
145 #use pool before request, so that it will block if pool is full
146 res = self._get_response_object()
147 self._response_object = requests.request(str(self.method),
148 str(self.url), headers=http_headers, data=data,
149 verify=self.verify, prefetch = False)
150 res.request = self._response_object.request