Statistics
| Branch: | Tag: | Revision:

root / kamaki / clients / connection / kamakicon.py @ 5d16ef46

History | View | Annotate | Download (6.3 kB)

1 f364f960 Stavros Sachtouris
# Copyright 2012 GRNET S.A. All rights reserved.
2 f364f960 Stavros Sachtouris
#
3 f364f960 Stavros Sachtouris
# Redistribution and use in source and binary forms, with or
4 f364f960 Stavros Sachtouris
# without modification, are permitted provided that the following
5 f364f960 Stavros Sachtouris
# conditions are met:
6 f364f960 Stavros Sachtouris
#
7 f364f960 Stavros Sachtouris
#   1. Redistributions of source code must retain the above
8 f364f960 Stavros Sachtouris
#      copyright notice, self.list of conditions and the following
9 f364f960 Stavros Sachtouris
#      disclaimer.
10 f364f960 Stavros Sachtouris
#
11 f364f960 Stavros Sachtouris
#   2. Redistributions in binary form must reproduce the above
12 f364f960 Stavros Sachtouris
#      copyright notice, self.list of conditions and the following
13 f364f960 Stavros Sachtouris
#      disclaimer in the documentation and/or other materials
14 f364f960 Stavros Sachtouris
#      provided with the distribution.
15 f364f960 Stavros Sachtouris
#
16 f364f960 Stavros Sachtouris
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 f364f960 Stavros Sachtouris
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 f364f960 Stavros Sachtouris
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 f364f960 Stavros Sachtouris
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 f364f960 Stavros Sachtouris
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 f364f960 Stavros Sachtouris
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 f364f960 Stavros Sachtouris
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 f364f960 Stavros Sachtouris
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 f364f960 Stavros Sachtouris
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 f364f960 Stavros Sachtouris
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 f364f960 Stavros Sachtouris
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 f364f960 Stavros Sachtouris
# POSSIBILITY OF SUCH DAMAGE.
28 f364f960 Stavros Sachtouris
#
29 f364f960 Stavros Sachtouris
# The views and conclusions contained in the software and
30 f364f960 Stavros Sachtouris
# documentation are those of the authors and should not be
31 f364f960 Stavros Sachtouris
# interpreted as representing official policies, either expressed
32 f364f960 Stavros Sachtouris
# or implied, of GRNET S.A.
33 f364f960 Stavros Sachtouris
34 f364f960 Stavros Sachtouris
from urlparse import urlparse
35 b98fe305 Dionysis Zindros
from objpool.http import get_http_connection
36 b2c5b650 Stavros Sachtouris
from kamaki.clients.connection import KamakiConnection, KamakiResponse
37 b2c5b650 Stavros Sachtouris
from kamaki.clients.connection.errors import KamakiConnectionError
38 b2c5b650 Stavros Sachtouris
from kamaki.clients.connection.errors import KamakiResponseError
39 f364f960 Stavros Sachtouris
40 5b263ba2 Stavros Sachtouris
from json import loads
41 f364f960 Stavros Sachtouris
42 f364f960 Stavros Sachtouris
from time import sleep
43 f364f960 Stavros Sachtouris
from httplib import ResponseNotReady
44 f364f960 Stavros Sachtouris
45 3dabe5d2 Stavros Sachtouris
46 b2c5b650 Stavros Sachtouris
class KamakiHTTPResponse(KamakiResponse):
47 f364f960 Stavros Sachtouris
48 f364f960 Stavros Sachtouris
    def _get_response(self):
49 f364f960 Stavros Sachtouris
        if self.prefetched:
50 f364f960 Stavros Sachtouris
            return
51 5b263ba2 Stavros Sachtouris
52 5b263ba2 Stavros Sachtouris
        ready = False
53 5b263ba2 Stavros Sachtouris
        while not ready:
54 5b263ba2 Stavros Sachtouris
            try:
55 5b263ba2 Stavros Sachtouris
                r = self.request.getresponse()
56 5b263ba2 Stavros Sachtouris
            except ResponseNotReady:
57 0004301f Stavros Sachtouris
                sleep(0.001)
58 5b263ba2 Stavros Sachtouris
                continue
59 5b263ba2 Stavros Sachtouris
            break
60 f364f960 Stavros Sachtouris
        self.prefetched = True
61 f364f960 Stavros Sachtouris
        headers = {}
62 3dabe5d2 Stavros Sachtouris
        for k, v in r.getheaders():
63 3dabe5d2 Stavros Sachtouris
            headers.update({k: v})
64 f364f960 Stavros Sachtouris
        self.headers = headers
65 0004301f Stavros Sachtouris
        self.content = r.read()
66 f364f960 Stavros Sachtouris
        self.status_code = r.status
67 f364f960 Stavros Sachtouris
        self.status = r.reason
68 5b263ba2 Stavros Sachtouris
        self.request.close()
69 f364f960 Stavros Sachtouris
70 3dabe5d2 Stavros Sachtouris
    @property
71 f364f960 Stavros Sachtouris
    def text(self):
72 b4c8b916 Stavros Sachtouris
        """
73 b4c8b916 Stavros Sachtouris
        :returns: (str) content
74 b4c8b916 Stavros Sachtouris
        """
75 5b263ba2 Stavros Sachtouris
        self._get_response()
76 5f7882af Stavros Sachtouris
        return unicode(self._content)
77 3dabe5d2 Stavros Sachtouris
78 f364f960 Stavros Sachtouris
    @text.setter
79 8dd20bc7 Stavros Sachtouris
    def text(self, v):
80 f364f960 Stavros Sachtouris
        pass
81 f364f960 Stavros Sachtouris
82 3dabe5d2 Stavros Sachtouris
    @property
83 f364f960 Stavros Sachtouris
    def json(self):
84 b4c8b916 Stavros Sachtouris
        """
85 b4c8b916 Stavros Sachtouris
        :returns: (dict) the json-formated content
86 b4c8b916 Stavros Sachtouris

87 b2c5b650 Stavros Sachtouris
        :raises KamakiResponseError: if content is not json formated
88 b4c8b916 Stavros Sachtouris
        """
89 5b263ba2 Stavros Sachtouris
        self._get_response()
90 f364f960 Stavros Sachtouris
        try:
91 f364f960 Stavros Sachtouris
            return loads(self._content)
92 f364f960 Stavros Sachtouris
        except ValueError as err:
93 b2c5b650 Stavros Sachtouris
            KamakiResponseError('Response not formated in JSON - %s' % err)
94 3dabe5d2 Stavros Sachtouris
95 f364f960 Stavros Sachtouris
    @json.setter
96 f364f960 Stavros Sachtouris
    def json(self, v):
97 f364f960 Stavros Sachtouris
        pass
98 f364f960 Stavros Sachtouris
99 5b263ba2 Stavros Sachtouris
    def release(self):
100 b4c8b916 Stavros Sachtouris
        """ Release the connection. Should always be called if the response
101 b4c8b916 Stavros Sachtouris
        content hasn't been used.
102 b4c8b916 Stavros Sachtouris
        """
103 5b263ba2 Stavros Sachtouris
        if not self.prefetched:
104 5b263ba2 Stavros Sachtouris
            self.request.close()
105 f364f960 Stavros Sachtouris
106 f364f960 Stavros Sachtouris
107 b2c5b650 Stavros Sachtouris
class KamakiHTTPConnection(KamakiConnection):
108 f364f960 Stavros Sachtouris
109 9a7efb0d Stavros Sachtouris
    def _retrieve_connection_info(self, extra_params={}):
110 05208859 Stavros Sachtouris
        """
111 05208859 Stavros Sachtouris
        :param extra_params: (dict) key:val for url parameters
112 05208859 Stavros Sachtouris

113 05208859 Stavros Sachtouris
        :returns: (scheme, netloc, url?with&params)
114 05208859 Stavros Sachtouris
        """
115 7d91734c Stavros Sachtouris
        if self.url:
116 7d91734c Stavros Sachtouris
            url = self.url if self.url[-1] == '/' else (self.url + '/')
117 7d91734c Stavros Sachtouris
        else:
118 7e5415a4 Stavros Sachtouris
            url = 'http://127.0.0.1'
119 7d91734c Stavros Sachtouris
        if self.path:
120 7d91734c Stavros Sachtouris
            url += self.path[1:] if self.path[0] == '/' else self.path
121 0004301f Stavros Sachtouris
        params = dict(self.params)
122 5d16ef46 Stavros Sachtouris
        params.update(extra_params)
123 3dabe5d2 Stavros Sachtouris
        for i, (key, val) in enumerate(params.items()):
124 5d16ef46 Stavros Sachtouris
            url += '%s%s%s' % (
125 5d16ef46 Stavros Sachtouris
                '&' if i else '?',
126 5d16ef46 Stavros Sachtouris
                key,
127 5d16ef46 Stavros Sachtouris
                '=%s' % val if val else '')
128 f364f960 Stavros Sachtouris
129 7d91734c Stavros Sachtouris
        parsed = urlparse(url)
130 5b263ba2 Stavros Sachtouris
        self.url = url
131 5d16ef46 Stavros Sachtouris
        self.path = parsed.path or '/'
132 5d16ef46 Stavros Sachtouris
        if parsed.query:
133 5d16ef46 Stavros Sachtouris
            self.path += '?%s' % parsed.query
134 5b263ba2 Stavros Sachtouris
        return (parsed.scheme, parsed.netloc)
135 f364f960 Stavros Sachtouris
136 24ff0a35 Stavros Sachtouris
    def perform_request(
137 2005b18e Stavros Sachtouris
            self,
138 2005b18e Stavros Sachtouris
            method=None, data=None, async_headers={}, async_params={}):
139 5f7882af Stavros Sachtouris
        """
140 5f7882af Stavros Sachtouris
        :param method: (str) http method ('get', 'post', etc.)
141 5f7882af Stavros Sachtouris

142 5f7882af Stavros Sachtouris
        :param data: (binary object)
143 5f7882af Stavros Sachtouris

144 5f7882af Stavros Sachtouris
        :param async_headers: (dict) key:val headers that are used only for one
145 5f7882af Stavros Sachtouris
            request instance as opposed to self.headers, which remain to be
146 5f7882af Stavros Sachtouris
            used by following or parallel requests
147 5f7882af Stavros Sachtouris

148 5f7882af Stavros Sachtouris
        :param async_params: (dict) key:val url parameters that are used only
149 5f7882af Stavros Sachtouris
            for one request instance as opposed to self.params, which remain to
150 5f7882af Stavros Sachtouris
            be used by following or parallel requests
151 5f7882af Stavros Sachtouris

152 e0214780 Stavros Sachtouris
        :returns: (KamakiHTTPResponse) a response object
153 5f7882af Stavros Sachtouris

154 b2c5b650 Stavros Sachtouris
        :raises KamakiConnectionError: Connection failures
155 5f7882af Stavros Sachtouris
        """
156 5d16ef46 Stavros Sachtouris
        (scheme, netloc) = self._retrieve_connection_info(async_params)
157 0004301f Stavros Sachtouris
        headers = dict(self.headers)
158 3dabe5d2 Stavros Sachtouris
        for k, v in async_headers.items():
159 9a7efb0d Stavros Sachtouris
            headers[k] = v
160 3ad34f11 Stavros Sachtouris
161 3ad34f11 Stavros Sachtouris
        #de-unicode headers to prepare them for http
162 3ad34f11 Stavros Sachtouris
        http_headers = {}
163 3dabe5d2 Stavros Sachtouris
        for k, v in headers.items():
164 3ad34f11 Stavros Sachtouris
            http_headers[str(k)] = str(v)
165 3ad34f11 Stavros Sachtouris
166 1785ad41 Stavros Sachtouris
        #get connection from pool
167 a03ade9e Stavros Sachtouris
        try:
168 a03ade9e Stavros Sachtouris
            conn = get_http_connection(netloc=netloc, scheme=scheme)
169 a03ade9e Stavros Sachtouris
        except ValueError as ve:
170 b2c5b650 Stavros Sachtouris
            raise KamakiConnectionError(
171 a03ade9e Stavros Sachtouris
                'Cannot establish connection to %s %s' % (self.url, ve),
172 a03ade9e Stavros Sachtouris
                errno=-1)
173 f364f960 Stavros Sachtouris
        try:
174 3ad34f11 Stavros Sachtouris
            #Be carefull, all non-body variables should not be unicode
175 24ff0a35 Stavros Sachtouris
            conn.request(
176 24ff0a35 Stavros Sachtouris
                method=str(method.upper()),
177 7d91734c Stavros Sachtouris
                url=str(self.path),
178 3ad34f11 Stavros Sachtouris
                headers=http_headers,
179 3ad34f11 Stavros Sachtouris
                body=data)
180 67469d65 Stavros Sachtouris
        except IOError as ioe:
181 b2c5b650 Stavros Sachtouris
            raise KamakiConnectionError(
182 67469d65 Stavros Sachtouris
                'Cannot connect to %s: %s' % (self.url, ioe.strerror),
183 67469d65 Stavros Sachtouris
                errno=ioe.errno)
184 b9d07587 Stavros Sachtouris
        except Exception as err:
185 1f417830 Stavros Sachtouris
            from traceback import format_stack
186 1f417830 Stavros Sachtouris
            from kamaki.clients import recvlog
187 1f417830 Stavros Sachtouris
            recvlog.debug('\n'.join(['%s' % type(err)] + format_stack()))
188 f364f960 Stavros Sachtouris
            conn.close()
189 f364f960 Stavros Sachtouris
            raise
190 e0214780 Stavros Sachtouris
        return KamakiHTTPResponse(conn)