Statistics
| Branch: | Tag: | Revision:

root / kamaki / clients / connection / kamakicon.py @ 58ddc630

History | View | Annotate | Download (6.9 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 9a8892d8 Stavros Sachtouris
from urllib2 import quote
36 b98fe305 Dionysis Zindros
from objpool.http import get_http_connection
37 58ddc630 Stavros Sachtouris
from traceback import format_stack
38 58ddc630 Stavros Sachtouris
39 b2c5b650 Stavros Sachtouris
from kamaki.clients.connection import KamakiConnection, KamakiResponse
40 b2c5b650 Stavros Sachtouris
from kamaki.clients.connection.errors import KamakiConnectionError
41 b2c5b650 Stavros Sachtouris
from kamaki.clients.connection.errors import KamakiResponseError
42 f364f960 Stavros Sachtouris
43 5b263ba2 Stavros Sachtouris
from json import loads
44 f364f960 Stavros Sachtouris
45 f364f960 Stavros Sachtouris
from time import sleep
46 f364f960 Stavros Sachtouris
from httplib import ResponseNotReady
47 f364f960 Stavros Sachtouris
48 3dabe5d2 Stavros Sachtouris
49 b2c5b650 Stavros Sachtouris
class KamakiHTTPResponse(KamakiResponse):
50 f364f960 Stavros Sachtouris
51 f364f960 Stavros Sachtouris
    def _get_response(self):
52 f364f960 Stavros Sachtouris
        if self.prefetched:
53 f364f960 Stavros Sachtouris
            return
54 5b263ba2 Stavros Sachtouris
55 58ddc630 Stavros Sachtouris
        try:
56 58ddc630 Stavros Sachtouris
            ready = False
57 58ddc630 Stavros Sachtouris
            while not ready:
58 58ddc630 Stavros Sachtouris
                try:
59 58ddc630 Stavros Sachtouris
                    r = self.request.getresponse()
60 58ddc630 Stavros Sachtouris
                except ResponseNotReady:
61 58ddc630 Stavros Sachtouris
                    sleep(0.001)
62 58ddc630 Stavros Sachtouris
                    continue
63 58ddc630 Stavros Sachtouris
                break
64 58ddc630 Stavros Sachtouris
            self.prefetched = True
65 58ddc630 Stavros Sachtouris
            headers = {}
66 58ddc630 Stavros Sachtouris
            for k, v in r.getheaders():
67 58ddc630 Stavros Sachtouris
                headers.update({k: v})
68 58ddc630 Stavros Sachtouris
            self.headers = headers
69 58ddc630 Stavros Sachtouris
            self.content = r.read()
70 58ddc630 Stavros Sachtouris
            self.status_code = r.status
71 58ddc630 Stavros Sachtouris
            self.status = r.reason
72 58ddc630 Stavros Sachtouris
        finally:
73 5b263ba2 Stavros Sachtouris
            try:
74 58ddc630 Stavros Sachtouris
                self.request.close()
75 58ddc630 Stavros Sachtouris
            except Exception as err:
76 58ddc630 Stavros Sachtouris
                from kamaki.clients import recvlog
77 58ddc630 Stavros Sachtouris
                recvlog.debug('\n'.join(['%s' % type(err)] + format_stack()))
78 58ddc630 Stavros Sachtouris
                raise
79 f364f960 Stavros Sachtouris
80 3dabe5d2 Stavros Sachtouris
    @property
81 f364f960 Stavros Sachtouris
    def text(self):
82 b4c8b916 Stavros Sachtouris
        """
83 b4c8b916 Stavros Sachtouris
        :returns: (str) content
84 b4c8b916 Stavros Sachtouris
        """
85 5b263ba2 Stavros Sachtouris
        self._get_response()
86 a517ff50 Stavros Sachtouris
        return '%s' % self._content
87 3dabe5d2 Stavros Sachtouris
88 f364f960 Stavros Sachtouris
    @text.setter
89 8dd20bc7 Stavros Sachtouris
    def text(self, v):
90 f364f960 Stavros Sachtouris
        pass
91 f364f960 Stavros Sachtouris
92 3dabe5d2 Stavros Sachtouris
    @property
93 f364f960 Stavros Sachtouris
    def json(self):
94 b4c8b916 Stavros Sachtouris
        """
95 b4c8b916 Stavros Sachtouris
        :returns: (dict) the json-formated content
96 b4c8b916 Stavros Sachtouris

97 b2c5b650 Stavros Sachtouris
        :raises KamakiResponseError: if content is not json formated
98 b4c8b916 Stavros Sachtouris
        """
99 5b263ba2 Stavros Sachtouris
        self._get_response()
100 f364f960 Stavros Sachtouris
        try:
101 f364f960 Stavros Sachtouris
            return loads(self._content)
102 f364f960 Stavros Sachtouris
        except ValueError as err:
103 b2c5b650 Stavros Sachtouris
            KamakiResponseError('Response not formated in JSON - %s' % err)
104 3dabe5d2 Stavros Sachtouris
105 f364f960 Stavros Sachtouris
    @json.setter
106 f364f960 Stavros Sachtouris
    def json(self, v):
107 f364f960 Stavros Sachtouris
        pass
108 f364f960 Stavros Sachtouris
109 5b263ba2 Stavros Sachtouris
    def release(self):
110 b4c8b916 Stavros Sachtouris
        """ Release the connection. Should always be called if the response
111 b4c8b916 Stavros Sachtouris
        content hasn't been used.
112 b4c8b916 Stavros Sachtouris
        """
113 5b263ba2 Stavros Sachtouris
        if not self.prefetched:
114 58ddc630 Stavros Sachtouris
            try:
115 58ddc630 Stavros Sachtouris
                self.request.close()
116 58ddc630 Stavros Sachtouris
            except Exception as err:
117 58ddc630 Stavros Sachtouris
                from kamaki.clients import recvlog
118 58ddc630 Stavros Sachtouris
                recvlog.debug('\n'.join(['%s' % type(err)] + format_stack()))
119 58ddc630 Stavros Sachtouris
                raise
120 f364f960 Stavros Sachtouris
121 f364f960 Stavros Sachtouris
122 b2c5b650 Stavros Sachtouris
class KamakiHTTPConnection(KamakiConnection):
123 f364f960 Stavros Sachtouris
124 9a7efb0d Stavros Sachtouris
    def _retrieve_connection_info(self, extra_params={}):
125 05208859 Stavros Sachtouris
        """
126 05208859 Stavros Sachtouris
        :param extra_params: (dict) key:val for url parameters
127 05208859 Stavros Sachtouris

128 05208859 Stavros Sachtouris
        :returns: (scheme, netloc, url?with&params)
129 05208859 Stavros Sachtouris
        """
130 7d91734c Stavros Sachtouris
        if self.url:
131 7d91734c Stavros Sachtouris
            url = self.url if self.url[-1] == '/' else (self.url + '/')
132 7d91734c Stavros Sachtouris
        else:
133 7e5415a4 Stavros Sachtouris
            url = 'http://127.0.0.1'
134 7d91734c Stavros Sachtouris
        if self.path:
135 7d91734c Stavros Sachtouris
            url += self.path[1:] if self.path[0] == '/' else self.path
136 0004301f Stavros Sachtouris
        params = dict(self.params)
137 5d16ef46 Stavros Sachtouris
        params.update(extra_params)
138 3dabe5d2 Stavros Sachtouris
        for i, (key, val) in enumerate(params.items()):
139 622a12fe Stavros Sachtouris
            url += '%s%s' % ('&' if i else '?', key)
140 622a12fe Stavros Sachtouris
            if val:
141 622a12fe Stavros Sachtouris
                url += '=%s' % val
142 f364f960 Stavros Sachtouris
143 7d91734c Stavros Sachtouris
        parsed = urlparse(url)
144 5b263ba2 Stavros Sachtouris
        self.url = url
145 5d16ef46 Stavros Sachtouris
        self.path = parsed.path or '/'
146 5d16ef46 Stavros Sachtouris
        if parsed.query:
147 5d16ef46 Stavros Sachtouris
            self.path += '?%s' % parsed.query
148 5b263ba2 Stavros Sachtouris
        return (parsed.scheme, parsed.netloc)
149 f364f960 Stavros Sachtouris
150 24ff0a35 Stavros Sachtouris
    def perform_request(
151 2005b18e Stavros Sachtouris
            self,
152 2005b18e Stavros Sachtouris
            method=None, data=None, async_headers={}, async_params={}):
153 5f7882af Stavros Sachtouris
        """
154 5f7882af Stavros Sachtouris
        :param method: (str) http method ('get', 'post', etc.)
155 5f7882af Stavros Sachtouris

156 5f7882af Stavros Sachtouris
        :param data: (binary object)
157 5f7882af Stavros Sachtouris

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

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

166 e0214780 Stavros Sachtouris
        :returns: (KamakiHTTPResponse) a response object
167 5f7882af Stavros Sachtouris

168 b2c5b650 Stavros Sachtouris
        :raises KamakiConnectionError: Connection failures
169 5f7882af Stavros Sachtouris
        """
170 5d16ef46 Stavros Sachtouris
        (scheme, netloc) = self._retrieve_connection_info(async_params)
171 0004301f Stavros Sachtouris
        headers = dict(self.headers)
172 3dabe5d2 Stavros Sachtouris
        for k, v in async_headers.items():
173 9a8892d8 Stavros Sachtouris
            v = quote(v.encode('utf-8')) if isinstance(v, unicode) else str(v)
174 9a7efb0d Stavros Sachtouris
            headers[k] = v
175 3ad34f11 Stavros Sachtouris
176 3ad34f11 Stavros Sachtouris
        #de-unicode headers to prepare them for http
177 3ad34f11 Stavros Sachtouris
        http_headers = {}
178 3dabe5d2 Stavros Sachtouris
        for k, v in headers.items():
179 9a8892d8 Stavros Sachtouris
            v = quote(v.encode('utf-8')) if isinstance(v, unicode) else str(v)
180 9a8892d8 Stavros Sachtouris
            http_headers[k] = v
181 3ad34f11 Stavros Sachtouris
182 1785ad41 Stavros Sachtouris
        #get connection from pool
183 a03ade9e Stavros Sachtouris
        try:
184 a03ade9e Stavros Sachtouris
            conn = get_http_connection(netloc=netloc, scheme=scheme)
185 a03ade9e Stavros Sachtouris
        except ValueError as ve:
186 b2c5b650 Stavros Sachtouris
            raise KamakiConnectionError(
187 a03ade9e Stavros Sachtouris
                'Cannot establish connection to %s %s' % (self.url, ve),
188 a03ade9e Stavros Sachtouris
                errno=-1)
189 f364f960 Stavros Sachtouris
        try:
190 3ad34f11 Stavros Sachtouris
            #Be carefull, all non-body variables should not be unicode
191 24ff0a35 Stavros Sachtouris
            conn.request(
192 24ff0a35 Stavros Sachtouris
                method=str(method.upper()),
193 7d91734c Stavros Sachtouris
                url=str(self.path),
194 3ad34f11 Stavros Sachtouris
                headers=http_headers,
195 3ad34f11 Stavros Sachtouris
                body=data)
196 67469d65 Stavros Sachtouris
        except IOError as ioe:
197 b2c5b650 Stavros Sachtouris
            raise KamakiConnectionError(
198 67469d65 Stavros Sachtouris
                'Cannot connect to %s: %s' % (self.url, ioe.strerror),
199 67469d65 Stavros Sachtouris
                errno=ioe.errno)
200 b9d07587 Stavros Sachtouris
        except Exception as err:
201 1f417830 Stavros Sachtouris
            from kamaki.clients import recvlog
202 1f417830 Stavros Sachtouris
            recvlog.debug('\n'.join(['%s' % type(err)] + format_stack()))
203 f364f960 Stavros Sachtouris
            conn.close()
204 f364f960 Stavros Sachtouris
            raise
205 e0214780 Stavros Sachtouris
        return KamakiHTTPResponse(conn)