Statistics
| Branch: | Tag: | Revision:

root / kamaki / clients / connection / kamakicon.py @ a517ff50

History | View | Annotate | Download (6.4 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 d5eeabc0 Stavros Sachtouris
from kamaki.clients.connection import HTTPConnection, HTTPResponse
37 d5eeabc0 Stavros Sachtouris
from kamaki.clients.connection.errors import HTTPConnectionError
38 0238c167 Stavros Sachtouris
from kamaki.clients.connection.errors import HTTPResponseError
39 67469d65 Stavros Sachtouris
from socket import gaierror, error
40 f364f960 Stavros Sachtouris
41 5b263ba2 Stavros Sachtouris
from json import loads
42 f364f960 Stavros Sachtouris
43 f364f960 Stavros Sachtouris
from time import sleep
44 f364f960 Stavros Sachtouris
from httplib import ResponseNotReady
45 f364f960 Stavros Sachtouris
46 3dabe5d2 Stavros Sachtouris
47 f364f960 Stavros Sachtouris
class KamakiHTTPResponse(HTTPResponse):
48 f364f960 Stavros Sachtouris
49 f364f960 Stavros Sachtouris
    def _get_response(self):
50 f364f960 Stavros Sachtouris
        if self.prefetched:
51 f364f960 Stavros Sachtouris
            return
52 5b263ba2 Stavros Sachtouris
53 5b263ba2 Stavros Sachtouris
        ready = False
54 5b263ba2 Stavros Sachtouris
        while not ready:
55 5b263ba2 Stavros Sachtouris
            try:
56 5b263ba2 Stavros Sachtouris
                r = self.request.getresponse()
57 5b263ba2 Stavros Sachtouris
            except ResponseNotReady:
58 0004301f Stavros Sachtouris
                sleep(0.001)
59 5b263ba2 Stavros Sachtouris
                continue
60 5b263ba2 Stavros Sachtouris
            break
61 f364f960 Stavros Sachtouris
        self.prefetched = True
62 f364f960 Stavros Sachtouris
        headers = {}
63 3dabe5d2 Stavros Sachtouris
        for k, v in r.getheaders():
64 3dabe5d2 Stavros Sachtouris
            headers.update({k: v})
65 f364f960 Stavros Sachtouris
        self.headers = headers
66 0004301f Stavros Sachtouris
        self.content = r.read()
67 f364f960 Stavros Sachtouris
        self.status_code = r.status
68 f364f960 Stavros Sachtouris
        self.status = r.reason
69 5b263ba2 Stavros Sachtouris
        self.request.close()
70 f364f960 Stavros Sachtouris
71 3dabe5d2 Stavros Sachtouris
    @property
72 f364f960 Stavros Sachtouris
    def text(self):
73 b4c8b916 Stavros Sachtouris
        """
74 b4c8b916 Stavros Sachtouris
        :returns: (str) content
75 b4c8b916 Stavros Sachtouris
        """
76 5b263ba2 Stavros Sachtouris
        self._get_response()
77 a517ff50 Stavros Sachtouris
        return '%s' % self._content
78 3dabe5d2 Stavros Sachtouris
79 f364f960 Stavros Sachtouris
    @text.setter
80 f364f960 Stavros Sachtouris
    def test(self, v):
81 f364f960 Stavros Sachtouris
        pass
82 f364f960 Stavros Sachtouris
83 3dabe5d2 Stavros Sachtouris
    @property
84 f364f960 Stavros Sachtouris
    def json(self):
85 b4c8b916 Stavros Sachtouris
        """
86 b4c8b916 Stavros Sachtouris
        :returns: (dict) the json-formated content
87 b4c8b916 Stavros Sachtouris

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

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

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

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

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

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

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