Statistics
| Branch: | Tag: | Revision:

root / lib / http / client.py @ 0fbae49a

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 dcd511c8 Guido Trotter
from ganeti import utils
41 02cab3e7 Michael Hanselmann
42 02cab3e7 Michael Hanselmann
43 02cab3e7 Michael Hanselmann
HTTP_CLIENT_THREADS = 10
44 02cab3e7 Michael Hanselmann
45 02cab3e7 Michael Hanselmann
46 02cab3e7 Michael Hanselmann
class HttpClientRequest(object):
47 02cab3e7 Michael Hanselmann
  def __init__(self, host, port, method, path, headers=None, post_data=None,
48 02cab3e7 Michael Hanselmann
               ssl_params=None, ssl_verify_peer=False):
49 02cab3e7 Michael Hanselmann
    """Describes an HTTP request.
50 02cab3e7 Michael Hanselmann

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

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

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

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

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

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

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

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

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

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

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

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

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

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