Statistics
| Branch: | Tag: | Revision:

root / lib / http / client.py @ abbf2cd9

History | View | Annotate | Download (10.6 kB)

1 02cab3e7 Michael Hanselmann
#
2 02cab3e7 Michael Hanselmann
#
3 02cab3e7 Michael Hanselmann
4 33231500 Michael Hanselmann
# Copyright (C) 2007, 2008, 2010 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 33231500 Michael Hanselmann
import logging
26 33231500 Michael Hanselmann
import pycurl
27 aea5caef Michael Hanselmann
import threading
28 33231500 Michael Hanselmann
from cStringIO import StringIO
29 6c881c52 Iustin Pop
30 02cab3e7 Michael Hanselmann
from ganeti import http
31 33231500 Michael Hanselmann
from ganeti import compat
32 1a8337f2 Manuel Franceschini
from ganeti import netutils
33 aea5caef Michael Hanselmann
from ganeti import locking
34 02cab3e7 Michael Hanselmann
35 02cab3e7 Michael Hanselmann
36 02cab3e7 Michael Hanselmann
class HttpClientRequest(object):
37 02cab3e7 Michael Hanselmann
  def __init__(self, host, port, method, path, headers=None, post_data=None,
38 7cb2d205 Michael Hanselmann
               read_timeout=None, curl_config_fn=None, nicename=None):
39 02cab3e7 Michael Hanselmann
    """Describes an HTTP request.
40 02cab3e7 Michael Hanselmann

41 02cab3e7 Michael Hanselmann
    @type host: string
42 02cab3e7 Michael Hanselmann
    @param host: Hostname
43 02cab3e7 Michael Hanselmann
    @type port: int
44 02cab3e7 Michael Hanselmann
    @param port: Port
45 02cab3e7 Michael Hanselmann
    @type method: string
46 02cab3e7 Michael Hanselmann
    @param method: Method name
47 02cab3e7 Michael Hanselmann
    @type path: string
48 02cab3e7 Michael Hanselmann
    @param path: Request path
49 33231500 Michael Hanselmann
    @type headers: list or None
50 33231500 Michael Hanselmann
    @param headers: Additional headers to send, list of strings
51 02cab3e7 Michael Hanselmann
    @type post_data: string or None
52 02cab3e7 Michael Hanselmann
    @param post_data: Additional data to send
53 e0036155 Iustin Pop
    @type read_timeout: int
54 e0036155 Iustin Pop
    @param read_timeout: if passed, it will be used as the read
55 e0036155 Iustin Pop
        timeout while reading the response from the server
56 33231500 Michael Hanselmann
    @type curl_config_fn: callable
57 33231500 Michael Hanselmann
    @param curl_config_fn: Function to configure cURL object before request
58 7cb2d205 Michael Hanselmann
    @type nicename: string
59 7cb2d205 Michael Hanselmann
    @param nicename: Name, presentable to a user, to describe this request (no
60 7cb2d205 Michael Hanselmann
                     whitespace)
61 02cab3e7 Michael Hanselmann

62 02cab3e7 Michael Hanselmann
    """
63 02cab3e7 Michael Hanselmann
    assert path.startswith("/"), "Path must start with slash (/)"
64 33231500 Michael Hanselmann
    assert curl_config_fn is None or callable(curl_config_fn)
65 02cab3e7 Michael Hanselmann
66 02cab3e7 Michael Hanselmann
    # Request attributes
67 02cab3e7 Michael Hanselmann
    self.host = host
68 02cab3e7 Michael Hanselmann
    self.port = port
69 02cab3e7 Michael Hanselmann
    self.method = method
70 02cab3e7 Michael Hanselmann
    self.path = path
71 e0036155 Iustin Pop
    self.read_timeout = read_timeout
72 33231500 Michael Hanselmann
    self.curl_config_fn = curl_config_fn
73 7cb2d205 Michael Hanselmann
    self.nicename = nicename
74 02cab3e7 Michael Hanselmann
75 33231500 Michael Hanselmann
    if post_data is None:
76 33231500 Michael Hanselmann
      self.post_data = ""
77 33231500 Michael Hanselmann
    else:
78 33231500 Michael Hanselmann
      self.post_data = post_data
79 33231500 Michael Hanselmann
80 33231500 Michael Hanselmann
    if headers is None:
81 33231500 Michael Hanselmann
      self.headers = []
82 33231500 Michael Hanselmann
    elif isinstance(headers, dict):
83 33231500 Michael Hanselmann
      # Support for old interface
84 33231500 Michael Hanselmann
      self.headers = ["%s: %s" % (name, value)
85 33231500 Michael Hanselmann
                      for name, value in headers.items()]
86 33231500 Michael Hanselmann
    else:
87 33231500 Michael Hanselmann
      self.headers = headers
88 33231500 Michael Hanselmann
89 33231500 Michael Hanselmann
    # Response status
90 02cab3e7 Michael Hanselmann
    self.success = None
91 02cab3e7 Michael Hanselmann
    self.error = None
92 02cab3e7 Michael Hanselmann
93 02cab3e7 Michael Hanselmann
    # Response attributes
94 02cab3e7 Michael Hanselmann
    self.resp_status_code = None
95 02cab3e7 Michael Hanselmann
    self.resp_body = None
96 02cab3e7 Michael Hanselmann
97 9fa2e150 Michael Hanselmann
  def __repr__(self):
98 9fa2e150 Michael Hanselmann
    status = ["%s.%s" % (self.__class__.__module__, self.__class__.__name__),
99 9fa2e150 Michael Hanselmann
              "%s:%s" % (self.host, self.port),
100 9fa2e150 Michael Hanselmann
              self.method,
101 9fa2e150 Michael Hanselmann
              self.path]
102 9fa2e150 Michael Hanselmann
103 9fa2e150 Michael Hanselmann
    return "<%s at %#x>" % (" ".join(status), id(self))
104 9fa2e150 Michael Hanselmann
105 33231500 Michael Hanselmann
  @property
106 33231500 Michael Hanselmann
  def url(self):
107 33231500 Michael Hanselmann
    """Returns the full URL for this requests.
108 02cab3e7 Michael Hanselmann

109 33231500 Michael Hanselmann
    """
110 1a8337f2 Manuel Franceschini
    if netutils.IPAddress.IsValid(self.host):
111 981732fb Manuel Franceschini
      address = netutils.FormatAddress((self.host, self.port))
112 1a8337f2 Manuel Franceschini
    else:
113 1a8337f2 Manuel Franceschini
      address = "%s:%s" % (self.host, self.port)
114 33231500 Michael Hanselmann
    # TODO: Support for non-SSL requests
115 1a8337f2 Manuel Franceschini
    return "https://%s%s" % (address, self.path)
116 02cab3e7 Michael Hanselmann
117 02cab3e7 Michael Hanselmann
118 abbf2cd9 Michael Hanselmann
def _StartRequest(curl, req):
119 abbf2cd9 Michael Hanselmann
  """Starts a request on a cURL object.
120 02cab3e7 Michael Hanselmann

121 abbf2cd9 Michael Hanselmann
  @type curl: pycurl.Curl
122 abbf2cd9 Michael Hanselmann
  @param curl: cURL object
123 abbf2cd9 Michael Hanselmann
  @type req: L{HttpClientRequest}
124 abbf2cd9 Michael Hanselmann
  @param req: HTTP request
125 02cab3e7 Michael Hanselmann

126 abbf2cd9 Michael Hanselmann
  """
127 abbf2cd9 Michael Hanselmann
  logging.debug("Starting request %r", req)
128 02cab3e7 Michael Hanselmann
129 abbf2cd9 Michael Hanselmann
  url = req.url
130 abbf2cd9 Michael Hanselmann
  method = req.method
131 abbf2cd9 Michael Hanselmann
  post_data = req.post_data
132 abbf2cd9 Michael Hanselmann
  headers = req.headers
133 02cab3e7 Michael Hanselmann
134 abbf2cd9 Michael Hanselmann
  # PycURL requires strings to be non-unicode
135 abbf2cd9 Michael Hanselmann
  assert isinstance(method, str)
136 abbf2cd9 Michael Hanselmann
  assert isinstance(url, str)
137 abbf2cd9 Michael Hanselmann
  assert isinstance(post_data, str)
138 abbf2cd9 Michael Hanselmann
  assert compat.all(isinstance(i, str) for i in headers)
139 02cab3e7 Michael Hanselmann
140 abbf2cd9 Michael Hanselmann
  # Buffer for response
141 abbf2cd9 Michael Hanselmann
  resp_buffer = StringIO()
142 02cab3e7 Michael Hanselmann
143 abbf2cd9 Michael Hanselmann
  # Configure client for request
144 abbf2cd9 Michael Hanselmann
  curl.setopt(pycurl.VERBOSE, False)
145 abbf2cd9 Michael Hanselmann
  curl.setopt(pycurl.NOSIGNAL, True)
146 abbf2cd9 Michael Hanselmann
  curl.setopt(pycurl.USERAGENT, http.HTTP_GANETI_VERSION)
147 abbf2cd9 Michael Hanselmann
  curl.setopt(pycurl.PROXY, "")
148 abbf2cd9 Michael Hanselmann
  curl.setopt(pycurl.CUSTOMREQUEST, str(method))
149 abbf2cd9 Michael Hanselmann
  curl.setopt(pycurl.URL, url)
150 abbf2cd9 Michael Hanselmann
  curl.setopt(pycurl.POSTFIELDS, post_data)
151 abbf2cd9 Michael Hanselmann
  curl.setopt(pycurl.HTTPHEADER, headers)
152 02cab3e7 Michael Hanselmann
153 abbf2cd9 Michael Hanselmann
  if req.read_timeout is None:
154 abbf2cd9 Michael Hanselmann
    curl.setopt(pycurl.TIMEOUT, 0)
155 abbf2cd9 Michael Hanselmann
  else:
156 abbf2cd9 Michael Hanselmann
    curl.setopt(pycurl.TIMEOUT, int(req.read_timeout))
157 02cab3e7 Michael Hanselmann
158 abbf2cd9 Michael Hanselmann
  # Disable SSL session ID caching (pycurl >= 7.16.0)
159 abbf2cd9 Michael Hanselmann
  if hasattr(pycurl, "SSL_SESSIONID_CACHE"):
160 abbf2cd9 Michael Hanselmann
    curl.setopt(pycurl.SSL_SESSIONID_CACHE, False)
161 02cab3e7 Michael Hanselmann
162 abbf2cd9 Michael Hanselmann
  curl.setopt(pycurl.WRITEFUNCTION, resp_buffer.write)
163 7b70d7a8 Apollon Oikonomopoulos
164 abbf2cd9 Michael Hanselmann
  # Pass cURL object to external config function
165 abbf2cd9 Michael Hanselmann
  if req.curl_config_fn:
166 abbf2cd9 Michael Hanselmann
    req.curl_config_fn(curl)
167 02cab3e7 Michael Hanselmann
168 abbf2cd9 Michael Hanselmann
  return _PendingRequest(curl, req, resp_buffer.getvalue)
169 02cab3e7 Michael Hanselmann
170 abbf2cd9 Michael Hanselmann
171 abbf2cd9 Michael Hanselmann
class _PendingRequest:
172 abbf2cd9 Michael Hanselmann
  def __init__(self, curl, req, resp_buffer_read):
173 abbf2cd9 Michael Hanselmann
    """Initializes this class.
174 abbf2cd9 Michael Hanselmann

175 abbf2cd9 Michael Hanselmann
    @type curl: pycurl.Curl
176 abbf2cd9 Michael Hanselmann
    @param curl: cURL object
177 abbf2cd9 Michael Hanselmann
    @type req: L{HttpClientRequest}
178 abbf2cd9 Michael Hanselmann
    @param req: HTTP request
179 abbf2cd9 Michael Hanselmann
    @type resp_buffer_read: callable
180 abbf2cd9 Michael Hanselmann
    @param resp_buffer_read: Function to read response body
181 02cab3e7 Michael Hanselmann

182 02cab3e7 Michael Hanselmann
    """
183 abbf2cd9 Michael Hanselmann
    assert req.success is None
184 abbf2cd9 Michael Hanselmann
185 abbf2cd9 Michael Hanselmann
    self._curl = curl
186 abbf2cd9 Michael Hanselmann
    self._req = req
187 abbf2cd9 Michael Hanselmann
    self._resp_buffer_read = resp_buffer_read
188 02cab3e7 Michael Hanselmann
189 33231500 Michael Hanselmann
  def GetCurlHandle(self):
190 33231500 Michael Hanselmann
    """Returns the cURL object.
191 02cab3e7 Michael Hanselmann

192 33231500 Michael Hanselmann
    """
193 33231500 Michael Hanselmann
    return self._curl
194 02cab3e7 Michael Hanselmann
195 33231500 Michael Hanselmann
  def GetCurrentRequest(self):
196 33231500 Michael Hanselmann
    """Returns the current request.
197 02cab3e7 Michael Hanselmann

198 33231500 Michael Hanselmann
    """
199 33231500 Michael Hanselmann
    return self._req
200 02cab3e7 Michael Hanselmann
201 33231500 Michael Hanselmann
  def Done(self, errmsg):
202 33231500 Michael Hanselmann
    """Finishes a request.
203 02cab3e7 Michael Hanselmann

204 33231500 Michael Hanselmann
    @type errmsg: string or None
205 33231500 Michael Hanselmann
    @param errmsg: Error message if request failed
206 02cab3e7 Michael Hanselmann

207 02cab3e7 Michael Hanselmann
    """
208 abbf2cd9 Michael Hanselmann
    curl = self._curl
209 33231500 Michael Hanselmann
    req = self._req
210 02cab3e7 Michael Hanselmann
211 abbf2cd9 Michael Hanselmann
    assert req.success is None, "Request has already been finalized"
212 02cab3e7 Michael Hanselmann
213 abbf2cd9 Michael Hanselmann
    logging.debug("Request %s finished, errmsg=%s", req, errmsg)
214 02cab3e7 Michael Hanselmann
215 33231500 Michael Hanselmann
    req.success = not bool(errmsg)
216 33231500 Michael Hanselmann
    req.error = errmsg
217 02cab3e7 Michael Hanselmann
218 33231500 Michael Hanselmann
    # Get HTTP response code
219 33231500 Michael Hanselmann
    req.resp_status_code = curl.getinfo(pycurl.RESPONSE_CODE)
220 abbf2cd9 Michael Hanselmann
    req.resp_body = self._resp_buffer_read()
221 02cab3e7 Michael Hanselmann
222 33231500 Michael Hanselmann
    # Ensure no potentially large variables are referenced
223 33231500 Michael Hanselmann
    try:
224 abbf2cd9 Michael Hanselmann
      # Only available in PycURL 7.19.0 and above
225 abbf2cd9 Michael Hanselmann
      reset_fn = curl.reset
226 abbf2cd9 Michael Hanselmann
    except AttributeError:
227 abbf2cd9 Michael Hanselmann
      curl.setopt(pycurl.POSTFIELDS, "")
228 abbf2cd9 Michael Hanselmann
      curl.setopt(pycurl.WRITEFUNCTION, lambda _: None)
229 33231500 Michael Hanselmann
    else:
230 abbf2cd9 Michael Hanselmann
      reset_fn()
231 ecd61b4e Michael Hanselmann
232 ecd61b4e Michael Hanselmann
233 aea5caef Michael Hanselmann
class _NoOpRequestMonitor: # pylint: disable=W0232
234 aea5caef Michael Hanselmann
  """No-op request monitor.
235 aea5caef Michael Hanselmann

236 aea5caef Michael Hanselmann
  """
237 aea5caef Michael Hanselmann
  @staticmethod
238 aea5caef Michael Hanselmann
  def acquire(*args, **kwargs):
239 aea5caef Michael Hanselmann
    pass
240 aea5caef Michael Hanselmann
241 aea5caef Michael Hanselmann
  release = acquire
242 aea5caef Michael Hanselmann
  Disable = acquire
243 aea5caef Michael Hanselmann
244 aea5caef Michael Hanselmann
245 aea5caef Michael Hanselmann
class _PendingRequestMonitor:
246 aea5caef Michael Hanselmann
  _LOCK = "_lock"
247 aea5caef Michael Hanselmann
248 aea5caef Michael Hanselmann
  def __init__(self, owner, pending_fn):
249 aea5caef Michael Hanselmann
    """Initializes this class.
250 aea5caef Michael Hanselmann

251 aea5caef Michael Hanselmann
    """
252 aea5caef Michael Hanselmann
    self._owner = owner
253 aea5caef Michael Hanselmann
    self._pending_fn = pending_fn
254 aea5caef Michael Hanselmann
255 aea5caef Michael Hanselmann
    # The lock monitor runs in another thread, hence locking is necessary
256 aea5caef Michael Hanselmann
    self._lock = locking.SharedLock("PendingHttpRequests")
257 aea5caef Michael Hanselmann
    self.acquire = self._lock.acquire
258 aea5caef Michael Hanselmann
    self.release = self._lock.release
259 aea5caef Michael Hanselmann
260 abbf2cd9 Michael Hanselmann
  @locking.ssynchronized(_LOCK)
261 aea5caef Michael Hanselmann
  def Disable(self):
262 aea5caef Michael Hanselmann
    """Disable monitor.
263 aea5caef Michael Hanselmann

264 aea5caef Michael Hanselmann
    """
265 aea5caef Michael Hanselmann
    self._pending_fn = None
266 aea5caef Michael Hanselmann
267 aea5caef Michael Hanselmann
  @locking.ssynchronized(_LOCK, shared=1)
268 aea5caef Michael Hanselmann
  def GetLockInfo(self, requested): # pylint: disable=W0613
269 aea5caef Michael Hanselmann
    """Retrieves information about pending requests.
270 aea5caef Michael Hanselmann

271 aea5caef Michael Hanselmann
    @type requested: set
272 aea5caef Michael Hanselmann
    @param requested: Requested information, see C{query.LQ_*}
273 aea5caef Michael Hanselmann

274 aea5caef Michael Hanselmann
    """
275 aea5caef Michael Hanselmann
    # No need to sort here, that's being done by the lock manager and query
276 aea5caef Michael Hanselmann
    # library. There are no priorities for requests, hence all show up as
277 aea5caef Michael Hanselmann
    # one item under "pending".
278 aea5caef Michael Hanselmann
    result = []
279 aea5caef Michael Hanselmann
280 aea5caef Michael Hanselmann
    if self._pending_fn:
281 aea5caef Michael Hanselmann
      owner_name = self._owner.getName()
282 aea5caef Michael Hanselmann
283 abbf2cd9 Michael Hanselmann
      for client in self._pending_fn():
284 abbf2cd9 Michael Hanselmann
        req = client.GetCurrentRequest()
285 aea5caef Michael Hanselmann
        if req:
286 7cb2d205 Michael Hanselmann
          if req.nicename is None:
287 7cb2d205 Michael Hanselmann
            name = "%s%s" % (req.host, req.path)
288 7cb2d205 Michael Hanselmann
          else:
289 7cb2d205 Michael Hanselmann
            name = req.nicename
290 90b2eeb0 Michael Hanselmann
          result.append(("rpc/%s" % name, None, [owner_name], None))
291 aea5caef Michael Hanselmann
292 aea5caef Michael Hanselmann
    return result
293 aea5caef Michael Hanselmann
294 aea5caef Michael Hanselmann
295 ecd61b4e Michael Hanselmann
def _ProcessCurlRequests(multi, requests):
296 ecd61b4e Michael Hanselmann
  """cURL request processor.
297 ecd61b4e Michael Hanselmann

298 ecd61b4e Michael Hanselmann
  This generator yields a tuple once for every completed request, successful or
299 ecd61b4e Michael Hanselmann
  not. The first value in the tuple is the handle, the second an error message
300 ecd61b4e Michael Hanselmann
  or C{None} for successful requests.
301 ecd61b4e Michael Hanselmann

302 ecd61b4e Michael Hanselmann
  @type multi: C{pycurl.CurlMulti}
303 ecd61b4e Michael Hanselmann
  @param multi: cURL multi object
304 ecd61b4e Michael Hanselmann
  @type requests: sequence
305 ecd61b4e Michael Hanselmann
  @param requests: cURL request handles
306 ecd61b4e Michael Hanselmann

307 ecd61b4e Michael Hanselmann
  """
308 ecd61b4e Michael Hanselmann
  for curl in requests:
309 ecd61b4e Michael Hanselmann
    multi.add_handle(curl)
310 ecd61b4e Michael Hanselmann
311 ecd61b4e Michael Hanselmann
  while True:
312 ecd61b4e Michael Hanselmann
    (ret, active) = multi.perform()
313 ecd61b4e Michael Hanselmann
    assert ret in (pycurl.E_MULTI_OK, pycurl.E_CALL_MULTI_PERFORM)
314 ecd61b4e Michael Hanselmann
315 ecd61b4e Michael Hanselmann
    if ret == pycurl.E_CALL_MULTI_PERFORM:
316 ecd61b4e Michael Hanselmann
      # cURL wants to be called again
317 ecd61b4e Michael Hanselmann
      continue
318 ecd61b4e Michael Hanselmann
319 ecd61b4e Michael Hanselmann
    while True:
320 ecd61b4e Michael Hanselmann
      (remaining_messages, successful, failed) = multi.info_read()
321 ecd61b4e Michael Hanselmann
322 ecd61b4e Michael Hanselmann
      for curl in successful:
323 ecd61b4e Michael Hanselmann
        multi.remove_handle(curl)
324 ecd61b4e Michael Hanselmann
        yield (curl, None)
325 ecd61b4e Michael Hanselmann
326 ecd61b4e Michael Hanselmann
      for curl, errnum, errmsg in failed:
327 ecd61b4e Michael Hanselmann
        multi.remove_handle(curl)
328 ecd61b4e Michael Hanselmann
        yield (curl, "Error %s: %s" % (errnum, errmsg))
329 ecd61b4e Michael Hanselmann
330 ecd61b4e Michael Hanselmann
      if remaining_messages == 0:
331 ecd61b4e Michael Hanselmann
        break
332 ecd61b4e Michael Hanselmann
333 ecd61b4e Michael Hanselmann
    if active == 0:
334 ecd61b4e Michael Hanselmann
      # No active handles anymore
335 ecd61b4e Michael Hanselmann
      break
336 ecd61b4e Michael Hanselmann
337 ecd61b4e Michael Hanselmann
    # Wait for I/O. The I/O timeout shouldn't be too long so that HTTP
338 ecd61b4e Michael Hanselmann
    # timeouts, which are only evaluated in multi.perform, aren't
339 ecd61b4e Michael Hanselmann
    # unnecessarily delayed.
340 ecd61b4e Michael Hanselmann
    multi.select(1.0)
341 abbf2cd9 Michael Hanselmann
342 abbf2cd9 Michael Hanselmann
343 abbf2cd9 Michael Hanselmann
def ProcessRequests(requests, lock_monitor_cb=None, _curl=pycurl.Curl,
344 abbf2cd9 Michael Hanselmann
                    _curl_multi=pycurl.CurlMulti,
345 abbf2cd9 Michael Hanselmann
                    _curl_process=_ProcessCurlRequests):
346 abbf2cd9 Michael Hanselmann
  """Processes any number of HTTP client requests.
347 abbf2cd9 Michael Hanselmann

348 abbf2cd9 Michael Hanselmann
  @type requests: list of L{HttpClientRequest}
349 abbf2cd9 Michael Hanselmann
  @param requests: List of all requests
350 abbf2cd9 Michael Hanselmann
  @param lock_monitor_cb: Callable for registering with lock monitor
351 abbf2cd9 Michael Hanselmann

352 abbf2cd9 Michael Hanselmann
  """
353 abbf2cd9 Michael Hanselmann
  assert compat.all((req.error is None and
354 abbf2cd9 Michael Hanselmann
                     req.success is None and
355 abbf2cd9 Michael Hanselmann
                     req.resp_status_code is None and
356 abbf2cd9 Michael Hanselmann
                     req.resp_body is None)
357 abbf2cd9 Michael Hanselmann
                    for req in requests)
358 abbf2cd9 Michael Hanselmann
359 abbf2cd9 Michael Hanselmann
  # Prepare all requests
360 abbf2cd9 Michael Hanselmann
  curl_to_client = \
361 abbf2cd9 Michael Hanselmann
    dict((client.GetCurlHandle(), client)
362 abbf2cd9 Michael Hanselmann
         for client in map(lambda req: _StartRequest(_curl(), req), requests))
363 abbf2cd9 Michael Hanselmann
364 abbf2cd9 Michael Hanselmann
  assert len(curl_to_client) == len(requests)
365 abbf2cd9 Michael Hanselmann
366 abbf2cd9 Michael Hanselmann
  if lock_monitor_cb:
367 abbf2cd9 Michael Hanselmann
    monitor = _PendingRequestMonitor(threading.currentThread(),
368 abbf2cd9 Michael Hanselmann
                                     curl_to_client.values)
369 abbf2cd9 Michael Hanselmann
    lock_monitor_cb(monitor)
370 abbf2cd9 Michael Hanselmann
  else:
371 abbf2cd9 Michael Hanselmann
    monitor = _NoOpRequestMonitor
372 abbf2cd9 Michael Hanselmann
373 abbf2cd9 Michael Hanselmann
  # Process all requests and act based on the returned values
374 abbf2cd9 Michael Hanselmann
  for (curl, msg) in _curl_process(_curl_multi(), curl_to_client.keys()):
375 abbf2cd9 Michael Hanselmann
    monitor.acquire(shared=0)
376 abbf2cd9 Michael Hanselmann
    try:
377 abbf2cd9 Michael Hanselmann
      curl_to_client.pop(curl).Done(msg)
378 abbf2cd9 Michael Hanselmann
    finally:
379 abbf2cd9 Michael Hanselmann
      monitor.release()
380 abbf2cd9 Michael Hanselmann
381 abbf2cd9 Michael Hanselmann
  assert not curl_to_client, "Not all requests were processed"
382 abbf2cd9 Michael Hanselmann
383 abbf2cd9 Michael Hanselmann
  # Don't try to read information anymore as all requests have been processed
384 abbf2cd9 Michael Hanselmann
  monitor.Disable()
385 abbf2cd9 Michael Hanselmann
386 abbf2cd9 Michael Hanselmann
  assert compat.all(req.error is not None or
387 abbf2cd9 Michael Hanselmann
                    (req.success and
388 abbf2cd9 Michael Hanselmann
                     req.resp_status_code is not None and
389 abbf2cd9 Michael Hanselmann
                     req.resp_body is not None)
390 abbf2cd9 Michael Hanselmann
                    for req in requests)