Statistics
| Branch: | Tag: | Revision:

root / lib / http / client.py @ 3636400f

History | View | Annotate | Download (11.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 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 e0036155 Iustin Pop
               ssl_params=None, ssl_verify_peer=False, read_timeout=None):
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 e0036155 Iustin Pop
    @type read_timeout: int
69 e0036155 Iustin Pop
    @param read_timeout: if passed, it will be used as the read
70 e0036155 Iustin Pop
        timeout while reading the response from the server
71 02cab3e7 Michael Hanselmann

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

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

173 02cab3e7 Michael Hanselmann
    @type req: HttpClientRequest
174 02cab3e7 Michael Hanselmann
    @param req: Request object
175 02cab3e7 Michael Hanselmann

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

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

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

319 02cab3e7 Michael Hanselmann
    """
320 02cab3e7 Michael Hanselmann
    response_msg = http.HttpMessage()
321 02cab3e7 Michael Hanselmann
322 e0036155 Iustin Pop
    if self.request.read_timeout is None:
323 e0036155 Iustin Pop
      timeout = self.READ_TIMEOUT
324 e0036155 Iustin Pop
    else:
325 e0036155 Iustin Pop
      timeout = self.request.read_timeout
326 e0036155 Iustin Pop
327 02cab3e7 Michael Hanselmann
    try:
328 02cab3e7 Michael Hanselmann
      response_msg_reader = \
329 e0036155 Iustin Pop
        _HttpServerToClientMessageReader(self.sock, response_msg, timeout)
330 02cab3e7 Michael Hanselmann
    except http.HttpSocketTimeout:
331 02cab3e7 Michael Hanselmann
      raise http.HttpError("Timeout while reading response")
332 02cab3e7 Michael Hanselmann
    except socket.error, err:
333 02cab3e7 Michael Hanselmann
      raise http.HttpError("Error reading response: %s" % err)
334 02cab3e7 Michael Hanselmann
335 02cab3e7 Michael Hanselmann
    return (response_msg_reader, response_msg)
336 02cab3e7 Michael Hanselmann
337 02cab3e7 Michael Hanselmann
338 02cab3e7 Michael Hanselmann
class _HttpClientPendingRequest(object):
339 02cab3e7 Michael Hanselmann
  """Data class for pending requests.
340 02cab3e7 Michael Hanselmann

341 02cab3e7 Michael Hanselmann
  """
342 02cab3e7 Michael Hanselmann
  def __init__(self, request):
343 02cab3e7 Michael Hanselmann
    self.request = request
344 02cab3e7 Michael Hanselmann
345 02cab3e7 Michael Hanselmann
    # Thread synchronization
346 02cab3e7 Michael Hanselmann
    self.done = threading.Event()
347 02cab3e7 Michael Hanselmann
348 9fa2e150 Michael Hanselmann
  def __repr__(self):
349 9fa2e150 Michael Hanselmann
    status = ["%s.%s" % (self.__class__.__module__, self.__class__.__name__),
350 9fa2e150 Michael Hanselmann
              "req=%r" % self.request]
351 9fa2e150 Michael Hanselmann
352 9fa2e150 Michael Hanselmann
    return "<%s at %#x>" % (" ".join(status), id(self))
353 9fa2e150 Michael Hanselmann
354 02cab3e7 Michael Hanselmann
355 02cab3e7 Michael Hanselmann
class HttpClientWorker(workerpool.BaseWorker):
356 02cab3e7 Michael Hanselmann
  """HTTP client worker class.
357 02cab3e7 Michael Hanselmann

358 02cab3e7 Michael Hanselmann
  """
359 7260cfbe Iustin Pop
  def RunTask(self, pend_req): # pylint: disable-msg=W0221
360 02cab3e7 Michael Hanselmann
    try:
361 02cab3e7 Michael Hanselmann
      HttpClientRequestExecutor(pend_req.request)
362 02cab3e7 Michael Hanselmann
    finally:
363 02cab3e7 Michael Hanselmann
      pend_req.done.set()
364 02cab3e7 Michael Hanselmann
365 02cab3e7 Michael Hanselmann
366 02cab3e7 Michael Hanselmann
class HttpClientWorkerPool(workerpool.WorkerPool):
367 02cab3e7 Michael Hanselmann
  def __init__(self, manager):
368 89e2b4d2 Michael Hanselmann
    workerpool.WorkerPool.__init__(self, "HttpClient",
369 89e2b4d2 Michael Hanselmann
                                   HTTP_CLIENT_THREADS,
370 02cab3e7 Michael Hanselmann
                                   HttpClientWorker)
371 02cab3e7 Michael Hanselmann
    self.manager = manager
372 02cab3e7 Michael Hanselmann
373 02cab3e7 Michael Hanselmann
374 02cab3e7 Michael Hanselmann
class HttpClientManager(object):
375 02cab3e7 Michael Hanselmann
  """Manages HTTP requests.
376 02cab3e7 Michael Hanselmann

377 02cab3e7 Michael Hanselmann
  """
378 02cab3e7 Michael Hanselmann
  def __init__(self):
379 02cab3e7 Michael Hanselmann
    self._wpool = HttpClientWorkerPool(self)
380 02cab3e7 Michael Hanselmann
381 02cab3e7 Michael Hanselmann
  def __del__(self):
382 02cab3e7 Michael Hanselmann
    self.Shutdown()
383 02cab3e7 Michael Hanselmann
384 02cab3e7 Michael Hanselmann
  def ExecRequests(self, requests):
385 02cab3e7 Michael Hanselmann
    """Execute HTTP requests.
386 02cab3e7 Michael Hanselmann

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

389 02cab3e7 Michael Hanselmann
    @type requests: List of HttpClientRequest instances
390 02cab3e7 Michael Hanselmann
    @param requests: The requests to execute
391 02cab3e7 Michael Hanselmann
    @rtype: List of HttpClientRequest instances
392 5fcc718f Iustin Pop
    @return: The list of requests passed in
393 02cab3e7 Michael Hanselmann

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