Statistics
| Branch: | Tag: | Revision:

root / kamaki / clients / connection / kamakicon.py @ 0238c167

History | View | Annotate | Download (5 kB)

1
# Copyright 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
from urlparse import urlparse
35
#from .pool.http import get_http_connection
36
from synnefo.lib.pool.http import get_http_connection
37
from kamaki.clients.connection import HTTPConnection, HTTPResponse
38
from kamaki.clients.connection.errors import HTTPConnectionError
39
from kamaki.clients.connection.errors import HTTPResponseError
40
from socket import gaierror
41

    
42
from json import loads
43

    
44
from time import sleep
45
from httplib import ResponseNotReady
46

    
47

    
48
class KamakiHTTPResponse(HTTPResponse):
49

    
50
    def _get_response(self):
51
        if self.prefetched:
52
            return
53

    
54
        ready = False
55
        while not ready:
56
            try:
57
                r = self.request.getresponse()
58
            except ResponseNotReady:
59
                sleep(0.001)
60
                continue
61
            break
62
        self.prefetched = True
63
        headers = {}
64
        for k, v in r.getheaders():
65
            headers.update({k: v})
66
        self.headers = headers
67
        self.content = r.read()
68
        self.status_code = r.status
69
        self.status = r.reason
70
        self.request.close()
71

    
72
    @property
73
    def text(self):
74
        self._get_response()
75
        return self._content
76

    
77
    @text.setter
78
    def test(self, v):
79
        pass
80

    
81
    @property
82
    def json(self):
83
        self._get_response()
84
        try:
85
            return loads(self._content)
86
        except ValueError as err:
87
            HTTPResponseError('Response not formated in JSON - %s' % err)
88

    
89
    @json.setter
90
    def json(self, v):
91
        pass
92

    
93
    def release(self):
94
        if not self.prefetched:
95
            self.request.close()
96

    
97

    
98
class KamakiHTTPConnection(HTTPConnection):
99

    
100
    def _retrieve_connection_info(self, extra_params={}):
101
        """ return (scheme, netloc, url?with&params) """
102
        if self.url:
103
            url = self.url if self.url[-1] == '/' else (self.url + '/')
104
        else:
105
            url = 'http://127.0.0.1'
106
        if self.path:
107
            url += self.path[1:] if self.path[0] == '/' else self.path
108
        params = dict(self.params)
109
        for k, v in extra_params.items():
110
            params[k] = v
111
        for i, (key, val) in enumerate(params.items()):
112
            param_str = ('?' if i == 0 else '&') + unicode(key)
113
            if val is not None:
114
                param_str += '=' + unicode(val)
115
            url += param_str
116

    
117
        parsed = urlparse(url)
118
        self.url = url
119
        self.path = parsed.path if parsed.path else '/'
120
        self.path += '?%s' % parsed.query if parsed.query else ''
121
        return (parsed.scheme, parsed.netloc)
122

    
123
    def perform_request(self,
124
        method=None,
125
        data=None,
126
        async_headers={},
127
        async_params={}):
128
        (scheme, netloc) = self._retrieve_connection_info(
129
            extra_params=async_params)
130
        headers = dict(self.headers)
131
        for k, v in async_headers.items():
132
            headers[k] = v
133

    
134
        #de-unicode headers to prepare them for http
135
        http_headers = {}
136
        for k, v in headers.items():
137
            http_headers[str(k)] = str(v)
138

    
139
        #get connection from pool
140
        conn = get_http_connection(netloc=netloc, scheme=scheme)
141
        try:
142
            #Be carefull, all non-body variables should not be unicode
143
            conn.request(method=str(method.upper()),
144
                url=str(self.path),
145
                headers=http_headers,
146
                body=data)
147
        except Exception as err:
148
            conn.close()
149
            if isinstance(err, gaierror):
150
                raise HTTPConnectionError(
151
                    'Cannot connect to %s - %s' % (self.url, err))
152
            raise
153
        return KamakiHTTPResponse(conn)