Statistics
| Branch: | Tag: | Revision:

root / lib / http / client.py @ f2e13d55

History | View | Annotate | Download (10.9 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 02cab3e7 Michael Hanselmann
import BaseHTTPServer
26 02cab3e7 Michael Hanselmann
import cgi
27 02cab3e7 Michael Hanselmann
import logging
28 02cab3e7 Michael Hanselmann
import OpenSSL
29 02cab3e7 Michael Hanselmann
import os
30 02cab3e7 Michael Hanselmann
import select
31 02cab3e7 Michael Hanselmann
import socket
32 02cab3e7 Michael Hanselmann
import sys
33 02cab3e7 Michael Hanselmann
import time
34 02cab3e7 Michael Hanselmann
import signal
35 02cab3e7 Michael Hanselmann
import errno
36 02cab3e7 Michael Hanselmann
import threading
37 02cab3e7 Michael Hanselmann
38 02cab3e7 Michael Hanselmann
from ganeti import constants
39 02cab3e7 Michael Hanselmann
from ganeti import serializer
40 02cab3e7 Michael Hanselmann
from ganeti import workerpool
41 02cab3e7 Michael Hanselmann
from ganeti import utils
42 02cab3e7 Michael Hanselmann
from ganeti import http
43 02cab3e7 Michael Hanselmann
44 02cab3e7 Michael Hanselmann
45 02cab3e7 Michael Hanselmann
HTTP_CLIENT_THREADS = 10
46 02cab3e7 Michael Hanselmann
47 02cab3e7 Michael Hanselmann
48 02cab3e7 Michael Hanselmann
class HttpClientRequest(object):
49 02cab3e7 Michael Hanselmann
  def __init__(self, host, port, method, path, headers=None, post_data=None,
50 02cab3e7 Michael Hanselmann
               ssl_params=None, ssl_verify_peer=False):
51 02cab3e7 Michael Hanselmann
    """Describes an HTTP request.
52 02cab3e7 Michael Hanselmann

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

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

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

163 02cab3e7 Michael Hanselmann
    @type req: HttpClientRequest
164 02cab3e7 Michael Hanselmann
    @param req: Request object
165 02cab3e7 Michael Hanselmann

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

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

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

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

329 02cab3e7 Michael Hanselmann
  """
330 02cab3e7 Michael Hanselmann
  def __init__(self, request):
331 02cab3e7 Michael Hanselmann
    self.request = request
332 02cab3e7 Michael Hanselmann
333 02cab3e7 Michael Hanselmann
    # Thread synchronization
334 02cab3e7 Michael Hanselmann
    self.done = threading.Event()
335 02cab3e7 Michael Hanselmann
336 02cab3e7 Michael Hanselmann
337 02cab3e7 Michael Hanselmann
class HttpClientWorker(workerpool.BaseWorker):
338 02cab3e7 Michael Hanselmann
  """HTTP client worker class.
339 02cab3e7 Michael Hanselmann

340 02cab3e7 Michael Hanselmann
  """
341 02cab3e7 Michael Hanselmann
  def RunTask(self, pend_req):
342 02cab3e7 Michael Hanselmann
    try:
343 02cab3e7 Michael Hanselmann
      HttpClientRequestExecutor(pend_req.request)
344 02cab3e7 Michael Hanselmann
    finally:
345 02cab3e7 Michael Hanselmann
      pend_req.done.set()
346 02cab3e7 Michael Hanselmann
347 02cab3e7 Michael Hanselmann
348 02cab3e7 Michael Hanselmann
class HttpClientWorkerPool(workerpool.WorkerPool):
349 02cab3e7 Michael Hanselmann
  def __init__(self, manager):
350 02cab3e7 Michael Hanselmann
    workerpool.WorkerPool.__init__(self, HTTP_CLIENT_THREADS,
351 02cab3e7 Michael Hanselmann
                                   HttpClientWorker)
352 02cab3e7 Michael Hanselmann
    self.manager = manager
353 02cab3e7 Michael Hanselmann
354 02cab3e7 Michael Hanselmann
355 02cab3e7 Michael Hanselmann
class HttpClientManager(object):
356 02cab3e7 Michael Hanselmann
  """Manages HTTP requests.
357 02cab3e7 Michael Hanselmann

358 02cab3e7 Michael Hanselmann
  """
359 02cab3e7 Michael Hanselmann
  def __init__(self):
360 02cab3e7 Michael Hanselmann
    self._wpool = HttpClientWorkerPool(self)
361 02cab3e7 Michael Hanselmann
362 02cab3e7 Michael Hanselmann
  def __del__(self):
363 02cab3e7 Michael Hanselmann
    self.Shutdown()
364 02cab3e7 Michael Hanselmann
365 02cab3e7 Michael Hanselmann
  def ExecRequests(self, requests):
366 02cab3e7 Michael Hanselmann
    """Execute HTTP requests.
367 02cab3e7 Michael Hanselmann

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

370 02cab3e7 Michael Hanselmann
    @type requests: List of HttpClientRequest instances
371 02cab3e7 Michael Hanselmann
    @param requests: The requests to execute
372 02cab3e7 Michael Hanselmann
    @rtype: List of HttpClientRequest instances
373 02cab3e7 Michael Hanselmann
    @returns: The list of requests passed in
374 02cab3e7 Michael Hanselmann

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