Statistics
| Branch: | Tag: | Revision:

root / kamaki / clients / connection / request.py @ c270fe96

History | View | Annotate | Download (5.3 kB)

1
# Copyright 2011-2012 GRNET S.A. All rights reserved.
2
#
3
# Redistribution and use in source and binary forms, with or
4
# without modification, are permitted provided that the following
5
# conditions are met:
6
#
7
#   1. Redistributions of source code must retain the above
8
#      copyright notice, self.list of conditions and the following
9
#      disclaimer.
10
#
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.
15
#
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.
28
#
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.
33

    
34
import requests
35
from kamaki.clients.connection import HTTPConnection, HTTPResponse, HTTPConnectionError
36
from kamaki.clients.connection.pool import ObjectPool
37
from urlparse import urlparse
38

    
39
# Add a convenience status property to the responses
40
def _status(self):
41
        return requests.status_codes._codes[self.status_code][0].upper()
42
requests.Response.status = property(_status)
43

    
44
class HTTPRequestsResponse(HTTPResponse):
45

    
46
        def __init__(self, request=None, prefetched=False):
47
                super(HTTPRequestsResponse, self).__init__(request=request, prefetched=prefetched)
48
                if prefetched:
49
                        self = request.response
50

    
51
        def _get_response(self):
52
                if self.prefetched:
53
                        return
54
                r = self.request.response
55
                try:
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
61
                        try:
62
                                self.json = loads(r.content)#None if self._get_content_only else r.json
63
                        except ValueError:
64
                                self.json = None
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)
77
                self.prefetched=True
78

    
79
        def release(self):
80
                """requests object handles this automatically"""
81
                if hasattr(self, '_pool'):
82
                        self._pool.pool_put(self)
83

    
84
POOL_SIZE=8
85
class HTTPRequestsResponsePool(ObjectPool):
86
        def __init__(self, netloc, size=POOL_SIZE):
87
                super(HTTPRequestsResponsePool, self).__init__(size=size)
88
                self.netloc = netloc
89

    
90
        def _pool_cleanup(self, resp):
91
                resp._get_response()
92
                return True
93

    
94
        @classmethod
95
        def key(self, full_url):
96
                p = urlparse(full_url)
97
                return '%s:%s:%s'%(p.scheme,p.netloc, p.port)
98

    
99
        def _pool_create(self):
100
                resp = HTTPRequestsResponse()
101
                resp._pool = self
102
                return resp
103

    
104
class HTTPRequest(HTTPConnection):
105

    
106
        _pools = {}
107

    
108
        #Avoid certificate verification by default
109
        verify = False
110

    
111
        def _get_response_object(self):
112
                pool_key = HTTPRequestsResponsePool.key(self.url)
113
                try:
114
                        respool = self._pools[pool_key]
115
                except KeyError:
116
                        self._pools[pool_key] = HTTPRequestsResponsePool(pool_key)
117
                        respool = self._pools[pool_key]
118
                return respool.pool_get()
119

    
120
        def perform_request(self, method=None, url=None, params=None, headers=None, data=None):
121
                """perform a request
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
126
                """
127
                if method is not None:
128
                        self.method = method
129
                if url is not None:
130
                        self.url = url
131
                if params is not None:
132
                        self.params = params
133
                if headers is not None:
134
                        self.headers = headers
135
                http_headers = {}
136
                for k,v in self.headers.items():
137
                        http_headers[str(k)] = str(v)
138

    
139
                for i,(key, val) in enumerate(self.params.items()):
140
                        param_str = ('?' if i == 0 else '&') + unicode(key) 
141
                        if val is not None:
142
                                param_str+= '='+unicode(val)
143
                        self.url += param_str
144

    
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
151
                return res