Statistics
| Branch: | Tag: | Revision:

root / lib / http / client.py @ 691744c4

History | View | Annotate | Download (10.7 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 os
26 02cab3e7 Michael Hanselmann
import select
27 02cab3e7 Michael Hanselmann
import socket
28 02cab3e7 Michael Hanselmann
import errno
29 02cab3e7 Michael Hanselmann
import threading
30 02cab3e7 Michael Hanselmann
31 02cab3e7 Michael Hanselmann
from ganeti import workerpool
32 02cab3e7 Michael Hanselmann
from ganeti import http
33 02cab3e7 Michael Hanselmann
34 02cab3e7 Michael Hanselmann
35 02cab3e7 Michael Hanselmann
HTTP_CLIENT_THREADS = 10
36 02cab3e7 Michael Hanselmann
37 02cab3e7 Michael Hanselmann
38 02cab3e7 Michael Hanselmann
class HttpClientRequest(object):
39 02cab3e7 Michael Hanselmann
  def __init__(self, host, port, method, path, headers=None, post_data=None,
40 02cab3e7 Michael Hanselmann
               ssl_params=None, ssl_verify_peer=False):
41 02cab3e7 Michael Hanselmann
    """Describes an HTTP request.
42 02cab3e7 Michael Hanselmann

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

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

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

153 02cab3e7 Michael Hanselmann
    @type req: HttpClientRequest
154 02cab3e7 Michael Hanselmann
    @param req: Request object
155 02cab3e7 Michael Hanselmann

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

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

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

299 02cab3e7 Michael Hanselmann
    """
300 02cab3e7 Michael Hanselmann
    response_msg = http.HttpMessage()
301 02cab3e7 Michael Hanselmann
302 02cab3e7 Michael Hanselmann
    try:
303 02cab3e7 Michael Hanselmann
      response_msg_reader = \
304 02cab3e7 Michael Hanselmann
        _HttpServerToClientMessageReader(self.sock, response_msg,
305 02cab3e7 Michael Hanselmann
                                         self.READ_TIMEOUT)
306 02cab3e7 Michael Hanselmann
    except http.HttpSocketTimeout:
307 02cab3e7 Michael Hanselmann
      raise http.HttpError("Timeout while reading response")
308 02cab3e7 Michael Hanselmann
    except socket.error, err:
309 02cab3e7 Michael Hanselmann
      raise http.HttpError("Error reading response: %s" % err)
310 02cab3e7 Michael Hanselmann
311 02cab3e7 Michael Hanselmann
    return (response_msg_reader, response_msg)
312 02cab3e7 Michael Hanselmann
313 02cab3e7 Michael Hanselmann
314 02cab3e7 Michael Hanselmann
class _HttpClientPendingRequest(object):
315 02cab3e7 Michael Hanselmann
  """Data class for pending requests.
316 02cab3e7 Michael Hanselmann

317 02cab3e7 Michael Hanselmann
  """
318 02cab3e7 Michael Hanselmann
  def __init__(self, request):
319 02cab3e7 Michael Hanselmann
    self.request = request
320 02cab3e7 Michael Hanselmann
321 02cab3e7 Michael Hanselmann
    # Thread synchronization
322 02cab3e7 Michael Hanselmann
    self.done = threading.Event()
323 02cab3e7 Michael Hanselmann
324 02cab3e7 Michael Hanselmann
325 02cab3e7 Michael Hanselmann
class HttpClientWorker(workerpool.BaseWorker):
326 02cab3e7 Michael Hanselmann
  """HTTP client worker class.
327 02cab3e7 Michael Hanselmann

328 02cab3e7 Michael Hanselmann
  """
329 02cab3e7 Michael Hanselmann
  def RunTask(self, pend_req):
330 02cab3e7 Michael Hanselmann
    try:
331 02cab3e7 Michael Hanselmann
      HttpClientRequestExecutor(pend_req.request)
332 02cab3e7 Michael Hanselmann
    finally:
333 02cab3e7 Michael Hanselmann
      pend_req.done.set()
334 02cab3e7 Michael Hanselmann
335 02cab3e7 Michael Hanselmann
336 02cab3e7 Michael Hanselmann
class HttpClientWorkerPool(workerpool.WorkerPool):
337 02cab3e7 Michael Hanselmann
  def __init__(self, manager):
338 02cab3e7 Michael Hanselmann
    workerpool.WorkerPool.__init__(self, HTTP_CLIENT_THREADS,
339 02cab3e7 Michael Hanselmann
                                   HttpClientWorker)
340 02cab3e7 Michael Hanselmann
    self.manager = manager
341 02cab3e7 Michael Hanselmann
342 02cab3e7 Michael Hanselmann
343 02cab3e7 Michael Hanselmann
class HttpClientManager(object):
344 02cab3e7 Michael Hanselmann
  """Manages HTTP requests.
345 02cab3e7 Michael Hanselmann

346 02cab3e7 Michael Hanselmann
  """
347 02cab3e7 Michael Hanselmann
  def __init__(self):
348 02cab3e7 Michael Hanselmann
    self._wpool = HttpClientWorkerPool(self)
349 02cab3e7 Michael Hanselmann
350 02cab3e7 Michael Hanselmann
  def __del__(self):
351 02cab3e7 Michael Hanselmann
    self.Shutdown()
352 02cab3e7 Michael Hanselmann
353 02cab3e7 Michael Hanselmann
  def ExecRequests(self, requests):
354 02cab3e7 Michael Hanselmann
    """Execute HTTP requests.
355 02cab3e7 Michael Hanselmann

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

358 02cab3e7 Michael Hanselmann
    @type requests: List of HttpClientRequest instances
359 02cab3e7 Michael Hanselmann
    @param requests: The requests to execute
360 02cab3e7 Michael Hanselmann
    @rtype: List of HttpClientRequest instances
361 5fcc718f Iustin Pop
    @return: The list of requests passed in
362 02cab3e7 Michael Hanselmann

363 02cab3e7 Michael Hanselmann
    """
364 02cab3e7 Michael Hanselmann
    # _HttpClientPendingRequest is used for internal thread synchronization
365 02cab3e7 Michael Hanselmann
    pending = [_HttpClientPendingRequest(req) for req in requests]
366 02cab3e7 Michael Hanselmann
367 02cab3e7 Michael Hanselmann
    try:
368 02cab3e7 Michael Hanselmann
      # Add requests to queue
369 02cab3e7 Michael Hanselmann
      for pend_req in pending:
370 02cab3e7 Michael Hanselmann
        self._wpool.AddTask(pend_req)
371 02cab3e7 Michael Hanselmann
372 02cab3e7 Michael Hanselmann
    finally:
373 02cab3e7 Michael Hanselmann
      # In case of an exception we should still wait for the rest, otherwise
374 02cab3e7 Michael Hanselmann
      # another thread from the worker pool could modify the request object
375 02cab3e7 Michael Hanselmann
      # after we returned.
376 02cab3e7 Michael Hanselmann
377 02cab3e7 Michael Hanselmann
      # And wait for them to finish
378 02cab3e7 Michael Hanselmann
      for pend_req in pending:
379 02cab3e7 Michael Hanselmann
        pend_req.done.wait()
380 02cab3e7 Michael Hanselmann
381 02cab3e7 Michael Hanselmann
    # Return original list
382 02cab3e7 Michael Hanselmann
    return requests
383 02cab3e7 Michael Hanselmann
384 02cab3e7 Michael Hanselmann
  def Shutdown(self):
385 02cab3e7 Michael Hanselmann
    self._wpool.Quiesce()
386 02cab3e7 Michael Hanselmann
    self._wpool.TerminateWorkers()