Statistics
| Branch: | Tag: | Revision:

root / lib / http / client.py @ 168c1de2

History | View | Annotate | Download (11.4 kB)

1 02cab3e7 Michael Hanselmann
#
2 02cab3e7 Michael Hanselmann
#
3 02cab3e7 Michael Hanselmann
4 02cab3e7 Michael Hanselmann
# Copyright (C) 2007, 2008 Google Inc.
5 02cab3e7 Michael Hanselmann
#
6 02cab3e7 Michael Hanselmann
# This program is free software; you can redistribute it and/or modify
7 02cab3e7 Michael Hanselmann
# it under the terms of the GNU General Public License as published by
8 02cab3e7 Michael Hanselmann
# the Free Software Foundation; either version 2 of the License, or
9 02cab3e7 Michael Hanselmann
# (at your option) any later version.
10 02cab3e7 Michael Hanselmann
#
11 02cab3e7 Michael Hanselmann
# This program is distributed in the hope that it will be useful, but
12 02cab3e7 Michael Hanselmann
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 02cab3e7 Michael Hanselmann
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 02cab3e7 Michael Hanselmann
# General Public License for more details.
15 02cab3e7 Michael Hanselmann
#
16 02cab3e7 Michael Hanselmann
# You should have received a copy of the GNU General Public License
17 02cab3e7 Michael Hanselmann
# along with this program; if not, write to the Free Software
18 02cab3e7 Michael Hanselmann
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02cab3e7 Michael Hanselmann
# 02110-1301, USA.
20 02cab3e7 Michael Hanselmann
21 02cab3e7 Michael Hanselmann
"""HTTP client module.
22 02cab3e7 Michael Hanselmann

23 02cab3e7 Michael Hanselmann
"""
24 02cab3e7 Michael Hanselmann
25 6c881c52 Iustin Pop
# pylint: disable-msg=E1103
26 6c881c52 Iustin Pop
27 6c881c52 Iustin Pop
# # E1103: %s %r has no %r member (but some types could not be
28 6c881c52 Iustin Pop
# inferred), since _socketobject could be ssl or not and pylint
29 6c881c52 Iustin Pop
# doesn't parse that
30 6c881c52 Iustin Pop
31 6c881c52 Iustin Pop
32 02cab3e7 Michael Hanselmann
import os
33 02cab3e7 Michael Hanselmann
import select
34 02cab3e7 Michael Hanselmann
import socket
35 02cab3e7 Michael Hanselmann
import errno
36 02cab3e7 Michael Hanselmann
import threading
37 02cab3e7 Michael Hanselmann
38 02cab3e7 Michael Hanselmann
from ganeti import workerpool
39 02cab3e7 Michael Hanselmann
from ganeti import http
40 02cab3e7 Michael Hanselmann
41 02cab3e7 Michael Hanselmann
42 02cab3e7 Michael Hanselmann
HTTP_CLIENT_THREADS = 10
43 02cab3e7 Michael Hanselmann
44 02cab3e7 Michael Hanselmann
45 02cab3e7 Michael Hanselmann
class HttpClientRequest(object):
46 02cab3e7 Michael Hanselmann
  def __init__(self, host, port, method, path, headers=None, post_data=None,
47 02cab3e7 Michael Hanselmann
               ssl_params=None, ssl_verify_peer=False):
48 02cab3e7 Michael Hanselmann
    """Describes an HTTP request.
49 02cab3e7 Michael Hanselmann

50 02cab3e7 Michael Hanselmann
    @type host: string
51 02cab3e7 Michael Hanselmann
    @param host: Hostname
52 02cab3e7 Michael Hanselmann
    @type port: int
53 02cab3e7 Michael Hanselmann
    @param port: Port
54 02cab3e7 Michael Hanselmann
    @type method: string
55 02cab3e7 Michael Hanselmann
    @param method: Method name
56 02cab3e7 Michael Hanselmann
    @type path: string
57 02cab3e7 Michael Hanselmann
    @param path: Request path
58 02cab3e7 Michael Hanselmann
    @type headers: dict or None
59 02cab3e7 Michael Hanselmann
    @param headers: Additional headers to send
60 02cab3e7 Michael Hanselmann
    @type post_data: string or None
61 02cab3e7 Michael Hanselmann
    @param post_data: Additional data to send
62 02cab3e7 Michael Hanselmann
    @type ssl_params: HttpSslParams
63 02cab3e7 Michael Hanselmann
    @param ssl_params: SSL key and certificate
64 02cab3e7 Michael Hanselmann
    @type ssl_verify_peer: bool
65 25e7b43f Iustin Pop
    @param ssl_verify_peer: Whether to compare our certificate with
66 25e7b43f Iustin Pop
        server's certificate
67 02cab3e7 Michael Hanselmann

68 02cab3e7 Michael Hanselmann
    """
69 02cab3e7 Michael Hanselmann
    if post_data is not None:
70 02cab3e7 Michael Hanselmann
      assert method.upper() in (http.HTTP_POST, http.HTTP_PUT), \
71 02cab3e7 Michael Hanselmann
        "Only POST and GET requests support sending data"
72 02cab3e7 Michael Hanselmann
73 02cab3e7 Michael Hanselmann
    assert path.startswith("/"), "Path must start with slash (/)"
74 02cab3e7 Michael Hanselmann
75 02cab3e7 Michael Hanselmann
    # Request attributes
76 02cab3e7 Michael Hanselmann
    self.host = host
77 02cab3e7 Michael Hanselmann
    self.port = port
78 02cab3e7 Michael Hanselmann
    self.ssl_params = ssl_params
79 02cab3e7 Michael Hanselmann
    self.ssl_verify_peer = ssl_verify_peer
80 02cab3e7 Michael Hanselmann
    self.method = method
81 02cab3e7 Michael Hanselmann
    self.path = path
82 02cab3e7 Michael Hanselmann
    self.headers = headers
83 02cab3e7 Michael Hanselmann
    self.post_data = post_data
84 02cab3e7 Michael Hanselmann
85 02cab3e7 Michael Hanselmann
    self.success = None
86 02cab3e7 Michael Hanselmann
    self.error = None
87 02cab3e7 Michael Hanselmann
88 02cab3e7 Michael Hanselmann
    # Raw response
89 02cab3e7 Michael Hanselmann
    self.response = None
90 02cab3e7 Michael Hanselmann
91 02cab3e7 Michael Hanselmann
    # Response attributes
92 02cab3e7 Michael Hanselmann
    self.resp_version = None
93 02cab3e7 Michael Hanselmann
    self.resp_status_code = None
94 02cab3e7 Michael Hanselmann
    self.resp_reason = None
95 02cab3e7 Michael Hanselmann
    self.resp_headers = None
96 02cab3e7 Michael Hanselmann
    self.resp_body = None
97 02cab3e7 Michael Hanselmann
98 9fa2e150 Michael Hanselmann
  def __repr__(self):
99 9fa2e150 Michael Hanselmann
    status = ["%s.%s" % (self.__class__.__module__, self.__class__.__name__),
100 9fa2e150 Michael Hanselmann
              "%s:%s" % (self.host, self.port),
101 9fa2e150 Michael Hanselmann
              self.method,
102 9fa2e150 Michael Hanselmann
              self.path]
103 9fa2e150 Michael Hanselmann
104 9fa2e150 Michael Hanselmann
    return "<%s at %#x>" % (" ".join(status), id(self))
105 9fa2e150 Michael Hanselmann
106 02cab3e7 Michael Hanselmann
107 02cab3e7 Michael Hanselmann
class _HttpClientToServerMessageWriter(http.HttpMessageWriter):
108 02cab3e7 Michael Hanselmann
  pass
109 02cab3e7 Michael Hanselmann
110 02cab3e7 Michael Hanselmann
111 02cab3e7 Michael Hanselmann
class _HttpServerToClientMessageReader(http.HttpMessageReader):
112 02cab3e7 Michael Hanselmann
  # Length limits
113 02cab3e7 Michael Hanselmann
  START_LINE_LENGTH_MAX = 512
114 02cab3e7 Michael Hanselmann
  HEADER_LENGTH_MAX = 4096
115 02cab3e7 Michael Hanselmann
116 02cab3e7 Michael Hanselmann
  def ParseStartLine(self, start_line):
117 02cab3e7 Michael Hanselmann
    """Parses the status line sent by the server.
118 02cab3e7 Michael Hanselmann

119 02cab3e7 Michael Hanselmann
    """
120 02cab3e7 Michael Hanselmann
    # Empty lines are skipped when reading
121 02cab3e7 Michael Hanselmann
    assert start_line
122 02cab3e7 Michael Hanselmann
123 02cab3e7 Michael Hanselmann
    try:
124 02cab3e7 Michael Hanselmann
      [version, status, reason] = start_line.split(None, 2)
125 02cab3e7 Michael Hanselmann
    except ValueError:
126 02cab3e7 Michael Hanselmann
      try:
127 02cab3e7 Michael Hanselmann
        [version, status] = start_line.split(None, 1)
128 02cab3e7 Michael Hanselmann
        reason = ""
129 02cab3e7 Michael Hanselmann
      except ValueError:
130 02cab3e7 Michael Hanselmann
        version = http.HTTP_0_9
131 02cab3e7 Michael Hanselmann
132 02cab3e7 Michael Hanselmann
    if version:
133 02cab3e7 Michael Hanselmann
      version = version.upper()
134 02cab3e7 Michael Hanselmann
135 02cab3e7 Michael Hanselmann
    # The status code is a three-digit number
136 02cab3e7 Michael Hanselmann
    try:
137 02cab3e7 Michael Hanselmann
      status = int(status)
138 02cab3e7 Michael Hanselmann
      if status < 100 or status > 999:
139 02cab3e7 Michael Hanselmann
        status = -1
140 691744c4 Iustin Pop
    except (TypeError, ValueError):
141 02cab3e7 Michael Hanselmann
      status = -1
142 02cab3e7 Michael Hanselmann
143 02cab3e7 Michael Hanselmann
    if status == -1:
144 02cab3e7 Michael Hanselmann
      raise http.HttpError("Invalid status code (%r)" % start_line)
145 02cab3e7 Michael Hanselmann
146 02cab3e7 Michael Hanselmann
    return http.HttpServerToClientStartLine(version, status, reason)
147 02cab3e7 Michael Hanselmann
148 02cab3e7 Michael Hanselmann
149 f4322a1e Michael Hanselmann
class HttpClientRequestExecutor(http.HttpBase):
150 02cab3e7 Michael Hanselmann
  # Default headers
151 02cab3e7 Michael Hanselmann
  DEFAULT_HEADERS = {
152 02cab3e7 Michael Hanselmann
    http.HTTP_USER_AGENT: http.HTTP_GANETI_VERSION,
153 02cab3e7 Michael Hanselmann
    # TODO: For keep-alive, don't send "Connection: close"
154 02cab3e7 Michael Hanselmann
    http.HTTP_CONNECTION: "close",
155 02cab3e7 Michael Hanselmann
    }
156 02cab3e7 Michael Hanselmann
157 02cab3e7 Michael Hanselmann
  # Timeouts in seconds for socket layer
158 02cab3e7 Michael Hanselmann
  # TODO: Soft timeout instead of only socket timeout?
159 02cab3e7 Michael Hanselmann
  # TODO: Make read timeout configurable per OpCode?
160 02cab3e7 Michael Hanselmann
  CONNECT_TIMEOUT = 5
161 02cab3e7 Michael Hanselmann
  WRITE_TIMEOUT = 10
162 02cab3e7 Michael Hanselmann
  READ_TIMEOUT = None
163 02cab3e7 Michael Hanselmann
  CLOSE_TIMEOUT = 1
164 02cab3e7 Michael Hanselmann
165 02cab3e7 Michael Hanselmann
  def __init__(self, req):
166 02cab3e7 Michael Hanselmann
    """Initializes the HttpClientRequestExecutor class.
167 02cab3e7 Michael Hanselmann

168 02cab3e7 Michael Hanselmann
    @type req: HttpClientRequest
169 02cab3e7 Michael Hanselmann
    @param req: Request object
170 02cab3e7 Michael Hanselmann

171 02cab3e7 Michael Hanselmann
    """
172 f4322a1e Michael Hanselmann
    http.HttpBase.__init__(self)
173 02cab3e7 Michael Hanselmann
    self.request = req
174 02cab3e7 Michael Hanselmann
175 02cab3e7 Michael Hanselmann
    try:
176 02cab3e7 Michael Hanselmann
      # TODO: Implement connection caching/keep-alive
177 02cab3e7 Michael Hanselmann
      self.sock = self._CreateSocket(req.ssl_params,
178 02cab3e7 Michael Hanselmann
                                     req.ssl_verify_peer)
179 02cab3e7 Michael Hanselmann
180 02cab3e7 Michael Hanselmann
      # Disable Python's timeout
181 02cab3e7 Michael Hanselmann
      self.sock.settimeout(None)
182 02cab3e7 Michael Hanselmann
183 02cab3e7 Michael Hanselmann
      # Operate in non-blocking mode
184 02cab3e7 Michael Hanselmann
      self.sock.setblocking(0)
185 02cab3e7 Michael Hanselmann
186 02cab3e7 Michael Hanselmann
      response_msg_reader = None
187 02cab3e7 Michael Hanselmann
      response_msg = None
188 02cab3e7 Michael Hanselmann
      force_close = True
189 02cab3e7 Michael Hanselmann
190 02cab3e7 Michael Hanselmann
      self._Connect()
191 02cab3e7 Michael Hanselmann
      try:
192 02cab3e7 Michael Hanselmann
        self._SendRequest()
193 02cab3e7 Michael Hanselmann
        (response_msg_reader, response_msg) = self._ReadResponse()
194 02cab3e7 Michael Hanselmann
195 02cab3e7 Michael Hanselmann
        # Only wait for server to close if we didn't have any exception.
196 02cab3e7 Michael Hanselmann
        force_close = False
197 02cab3e7 Michael Hanselmann
      finally:
198 02cab3e7 Michael Hanselmann
        # TODO: Keep-alive is not supported, always close connection
199 02cab3e7 Michael Hanselmann
        force_close = True
200 aea0ed67 Michael Hanselmann
        http.ShutdownConnection(self.sock, self.CLOSE_TIMEOUT,
201 aea0ed67 Michael Hanselmann
                                self.WRITE_TIMEOUT, response_msg_reader,
202 aea0ed67 Michael Hanselmann
                                force_close)
203 02cab3e7 Michael Hanselmann
204 02cab3e7 Michael Hanselmann
      self.sock.close()
205 02cab3e7 Michael Hanselmann
      self.sock = None
206 02cab3e7 Michael Hanselmann
207 02cab3e7 Michael Hanselmann
      req.response = response_msg
208 02cab3e7 Michael Hanselmann
209 02cab3e7 Michael Hanselmann
      req.resp_version = req.response.start_line.version
210 02cab3e7 Michael Hanselmann
      req.resp_status_code = req.response.start_line.code
211 02cab3e7 Michael Hanselmann
      req.resp_reason = req.response.start_line.reason
212 02cab3e7 Michael Hanselmann
      req.resp_headers = req.response.headers
213 02cab3e7 Michael Hanselmann
      req.resp_body = req.response.body
214 02cab3e7 Michael Hanselmann
215 02cab3e7 Michael Hanselmann
      req.success = True
216 02cab3e7 Michael Hanselmann
      req.error = None
217 02cab3e7 Michael Hanselmann
218 02cab3e7 Michael Hanselmann
    except http.HttpError, err:
219 02cab3e7 Michael Hanselmann
      req.success = False
220 02cab3e7 Michael Hanselmann
      req.error = str(err)
221 02cab3e7 Michael Hanselmann
222 02cab3e7 Michael Hanselmann
  def _Connect(self):
223 02cab3e7 Michael Hanselmann
    """Non-blocking connect to host with timeout.
224 02cab3e7 Michael Hanselmann

225 02cab3e7 Michael Hanselmann
    """
226 02cab3e7 Michael Hanselmann
    connected = False
227 02cab3e7 Michael Hanselmann
    while True:
228 02cab3e7 Michael Hanselmann
      try:
229 02cab3e7 Michael Hanselmann
        connect_error = self.sock.connect_ex((self.request.host,
230 02cab3e7 Michael Hanselmann
                                              self.request.port))
231 02cab3e7 Michael Hanselmann
      except socket.gaierror, err:
232 02cab3e7 Michael Hanselmann
        raise http.HttpError("Connection failed: %s" % str(err))
233 02cab3e7 Michael Hanselmann
234 02cab3e7 Michael Hanselmann
      if connect_error == errno.EINTR:
235 02cab3e7 Michael Hanselmann
        # Mask signals
236 02cab3e7 Michael Hanselmann
        pass
237 02cab3e7 Michael Hanselmann
238 02cab3e7 Michael Hanselmann
      elif connect_error == 0:
239 02cab3e7 Michael Hanselmann
        # Connection established
240 02cab3e7 Michael Hanselmann
        connected = True
241 02cab3e7 Michael Hanselmann
        break
242 02cab3e7 Michael Hanselmann
243 02cab3e7 Michael Hanselmann
      elif connect_error == errno.EINPROGRESS:
244 02cab3e7 Michael Hanselmann
        # Connection started
245 02cab3e7 Michael Hanselmann
        break
246 02cab3e7 Michael Hanselmann
247 02cab3e7 Michael Hanselmann
      raise http.HttpError("Connection failed (%s: %s)" %
248 02cab3e7 Michael Hanselmann
                             (connect_error, os.strerror(connect_error)))
249 02cab3e7 Michael Hanselmann
250 02cab3e7 Michael Hanselmann
    if not connected:
251 02cab3e7 Michael Hanselmann
      # Wait for connection
252 aea0ed67 Michael Hanselmann
      event = http.WaitForSocketCondition(self.sock, select.POLLOUT,
253 aea0ed67 Michael Hanselmann
                                          self.CONNECT_TIMEOUT)
254 02cab3e7 Michael Hanselmann
      if event is None:
255 02cab3e7 Michael Hanselmann
        raise http.HttpError("Timeout while connecting to server")
256 02cab3e7 Michael Hanselmann
257 02cab3e7 Michael Hanselmann
      # Get error code
258 02cab3e7 Michael Hanselmann
      connect_error = self.sock.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR)
259 02cab3e7 Michael Hanselmann
      if connect_error != 0:
260 02cab3e7 Michael Hanselmann
        raise http.HttpError("Connection failed (%s: %s)" %
261 02cab3e7 Michael Hanselmann
                               (connect_error, os.strerror(connect_error)))
262 02cab3e7 Michael Hanselmann
263 02cab3e7 Michael Hanselmann
    # Enable TCP keep-alive
264 02cab3e7 Michael Hanselmann
    self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
265 02cab3e7 Michael Hanselmann
266 02cab3e7 Michael Hanselmann
    # If needed, Linux specific options are available to change the TCP
267 02cab3e7 Michael Hanselmann
    # keep-alive settings, see "man 7 tcp" for TCP_KEEPCNT, TCP_KEEPIDLE and
268 02cab3e7 Michael Hanselmann
    # TCP_KEEPINTVL.
269 02cab3e7 Michael Hanselmann
270 f2e13d55 Michael Hanselmann
    # Do the secret SSL handshake
271 f2e13d55 Michael Hanselmann
    if self.using_ssl:
272 fe267188 Iustin Pop
      self.sock.set_connect_state() # pylint: disable-msg=E1103
273 f2e13d55 Michael Hanselmann
      try:
274 aea0ed67 Michael Hanselmann
        http.Handshake(self.sock, self.WRITE_TIMEOUT)
275 f2e13d55 Michael Hanselmann
      except http.HttpSessionHandshakeUnexpectedEOF:
276 f2e13d55 Michael Hanselmann
        raise http.HttpError("Server closed connection during SSL handshake")
277 f2e13d55 Michael Hanselmann
278 02cab3e7 Michael Hanselmann
  def _SendRequest(self):
279 02cab3e7 Michael Hanselmann
    """Sends request to server.
280 02cab3e7 Michael Hanselmann

281 02cab3e7 Michael Hanselmann
    """
282 02cab3e7 Michael Hanselmann
    # Headers
283 02cab3e7 Michael Hanselmann
    send_headers = self.DEFAULT_HEADERS.copy()
284 02cab3e7 Michael Hanselmann
285 02cab3e7 Michael Hanselmann
    if self.request.headers:
286 02cab3e7 Michael Hanselmann
      send_headers.update(self.request.headers)
287 02cab3e7 Michael Hanselmann
288 5a9c3f46 Iustin Pop
    send_headers[http.HTTP_HOST] = "%s:%s" % (self.request.host,
289 5a9c3f46 Iustin Pop
                                              self.request.port)
290 02cab3e7 Michael Hanselmann
291 02cab3e7 Michael Hanselmann
    # Response message
292 02cab3e7 Michael Hanselmann
    msg = http.HttpMessage()
293 02cab3e7 Michael Hanselmann
294 02cab3e7 Michael Hanselmann
    # Combine request line. We only support HTTP/1.0 (no chunked transfers and
295 02cab3e7 Michael Hanselmann
    # no keep-alive).
296 02cab3e7 Michael Hanselmann
    # TODO: For keep-alive, change to HTTP/1.1
297 02cab3e7 Michael Hanselmann
    msg.start_line = \
298 02cab3e7 Michael Hanselmann
      http.HttpClientToServerStartLine(method=self.request.method.upper(),
299 5a9c3f46 Iustin Pop
                                       path=self.request.path,
300 5a9c3f46 Iustin Pop
                                       version=http.HTTP_1_0)
301 02cab3e7 Michael Hanselmann
    msg.headers = send_headers
302 02cab3e7 Michael Hanselmann
    msg.body = self.request.post_data
303 02cab3e7 Michael Hanselmann
304 02cab3e7 Michael Hanselmann
    try:
305 02cab3e7 Michael Hanselmann
      _HttpClientToServerMessageWriter(self.sock, msg, self.WRITE_TIMEOUT)
306 02cab3e7 Michael Hanselmann
    except http.HttpSocketTimeout:
307 02cab3e7 Michael Hanselmann
      raise http.HttpError("Timeout while sending request")
308 02cab3e7 Michael Hanselmann
    except socket.error, err:
309 02cab3e7 Michael Hanselmann
      raise http.HttpError("Error sending request: %s" % err)
310 02cab3e7 Michael Hanselmann
311 02cab3e7 Michael Hanselmann
  def _ReadResponse(self):
312 02cab3e7 Michael Hanselmann
    """Read response from server.
313 02cab3e7 Michael Hanselmann

314 02cab3e7 Michael Hanselmann
    """
315 02cab3e7 Michael Hanselmann
    response_msg = http.HttpMessage()
316 02cab3e7 Michael Hanselmann
317 02cab3e7 Michael Hanselmann
    try:
318 02cab3e7 Michael Hanselmann
      response_msg_reader = \
319 02cab3e7 Michael Hanselmann
        _HttpServerToClientMessageReader(self.sock, response_msg,
320 02cab3e7 Michael Hanselmann
                                         self.READ_TIMEOUT)
321 02cab3e7 Michael Hanselmann
    except http.HttpSocketTimeout:
322 02cab3e7 Michael Hanselmann
      raise http.HttpError("Timeout while reading response")
323 02cab3e7 Michael Hanselmann
    except socket.error, err:
324 02cab3e7 Michael Hanselmann
      raise http.HttpError("Error reading response: %s" % err)
325 02cab3e7 Michael Hanselmann
326 02cab3e7 Michael Hanselmann
    return (response_msg_reader, response_msg)
327 02cab3e7 Michael Hanselmann
328 02cab3e7 Michael Hanselmann
329 02cab3e7 Michael Hanselmann
class _HttpClientPendingRequest(object):
330 02cab3e7 Michael Hanselmann
  """Data class for pending requests.
331 02cab3e7 Michael Hanselmann

332 02cab3e7 Michael Hanselmann
  """
333 02cab3e7 Michael Hanselmann
  def __init__(self, request):
334 02cab3e7 Michael Hanselmann
    self.request = request
335 02cab3e7 Michael Hanselmann
336 02cab3e7 Michael Hanselmann
    # Thread synchronization
337 02cab3e7 Michael Hanselmann
    self.done = threading.Event()
338 02cab3e7 Michael Hanselmann
339 9fa2e150 Michael Hanselmann
  def __repr__(self):
340 9fa2e150 Michael Hanselmann
    status = ["%s.%s" % (self.__class__.__module__, self.__class__.__name__),
341 9fa2e150 Michael Hanselmann
              "req=%r" % self.request]
342 9fa2e150 Michael Hanselmann
343 9fa2e150 Michael Hanselmann
    return "<%s at %#x>" % (" ".join(status), id(self))
344 9fa2e150 Michael Hanselmann
345 02cab3e7 Michael Hanselmann
346 02cab3e7 Michael Hanselmann
class HttpClientWorker(workerpool.BaseWorker):
347 02cab3e7 Michael Hanselmann
  """HTTP client worker class.
348 02cab3e7 Michael Hanselmann

349 02cab3e7 Michael Hanselmann
  """
350 7260cfbe Iustin Pop
  def RunTask(self, pend_req): # pylint: disable-msg=W0221
351 02cab3e7 Michael Hanselmann
    try:
352 02cab3e7 Michael Hanselmann
      HttpClientRequestExecutor(pend_req.request)
353 02cab3e7 Michael Hanselmann
    finally:
354 02cab3e7 Michael Hanselmann
      pend_req.done.set()
355 02cab3e7 Michael Hanselmann
356 02cab3e7 Michael Hanselmann
357 02cab3e7 Michael Hanselmann
class HttpClientWorkerPool(workerpool.WorkerPool):
358 02cab3e7 Michael Hanselmann
  def __init__(self, manager):
359 89e2b4d2 Michael Hanselmann
    workerpool.WorkerPool.__init__(self, "HttpClient",
360 89e2b4d2 Michael Hanselmann
                                   HTTP_CLIENT_THREADS,
361 02cab3e7 Michael Hanselmann
                                   HttpClientWorker)
362 02cab3e7 Michael Hanselmann
    self.manager = manager
363 02cab3e7 Michael Hanselmann
364 02cab3e7 Michael Hanselmann
365 02cab3e7 Michael Hanselmann
class HttpClientManager(object):
366 02cab3e7 Michael Hanselmann
  """Manages HTTP requests.
367 02cab3e7 Michael Hanselmann

368 02cab3e7 Michael Hanselmann
  """
369 02cab3e7 Michael Hanselmann
  def __init__(self):
370 02cab3e7 Michael Hanselmann
    self._wpool = HttpClientWorkerPool(self)
371 02cab3e7 Michael Hanselmann
372 02cab3e7 Michael Hanselmann
  def __del__(self):
373 02cab3e7 Michael Hanselmann
    self.Shutdown()
374 02cab3e7 Michael Hanselmann
375 02cab3e7 Michael Hanselmann
  def ExecRequests(self, requests):
376 02cab3e7 Michael Hanselmann
    """Execute HTTP requests.
377 02cab3e7 Michael Hanselmann

378 02cab3e7 Michael Hanselmann
    This function can be called from multiple threads at the same time.
379 02cab3e7 Michael Hanselmann

380 02cab3e7 Michael Hanselmann
    @type requests: List of HttpClientRequest instances
381 02cab3e7 Michael Hanselmann
    @param requests: The requests to execute
382 02cab3e7 Michael Hanselmann
    @rtype: List of HttpClientRequest instances
383 5fcc718f Iustin Pop
    @return: The list of requests passed in
384 02cab3e7 Michael Hanselmann

385 02cab3e7 Michael Hanselmann
    """
386 02cab3e7 Michael Hanselmann
    # _HttpClientPendingRequest is used for internal thread synchronization
387 02cab3e7 Michael Hanselmann
    pending = [_HttpClientPendingRequest(req) for req in requests]
388 02cab3e7 Michael Hanselmann
389 02cab3e7 Michael Hanselmann
    try:
390 02cab3e7 Michael Hanselmann
      # Add requests to queue
391 02cab3e7 Michael Hanselmann
      for pend_req in pending:
392 02cab3e7 Michael Hanselmann
        self._wpool.AddTask(pend_req)
393 02cab3e7 Michael Hanselmann
394 02cab3e7 Michael Hanselmann
    finally:
395 02cab3e7 Michael Hanselmann
      # In case of an exception we should still wait for the rest, otherwise
396 02cab3e7 Michael Hanselmann
      # another thread from the worker pool could modify the request object
397 02cab3e7 Michael Hanselmann
      # after we returned.
398 02cab3e7 Michael Hanselmann
399 02cab3e7 Michael Hanselmann
      # And wait for them to finish
400 02cab3e7 Michael Hanselmann
      for pend_req in pending:
401 02cab3e7 Michael Hanselmann
        pend_req.done.wait()
402 02cab3e7 Michael Hanselmann
403 02cab3e7 Michael Hanselmann
    # Return original list
404 02cab3e7 Michael Hanselmann
    return requests
405 02cab3e7 Michael Hanselmann
406 02cab3e7 Michael Hanselmann
  def Shutdown(self):
407 02cab3e7 Michael Hanselmann
    self._wpool.Quiesce()
408 02cab3e7 Michael Hanselmann
    self._wpool.TerminateWorkers()