Add ParseCpuMask() utility function
[ganeti-local] / lib / http / client.py
index 108e954..697199d 100644 (file)
 
 """
 
-import BaseHTTPServer
-import cgi
-import logging
-import OpenSSL
+# pylint: disable-msg=E1103
+
+# # E1103: %s %r has no %r member (but some types could not be
+# inferred), since _socketobject could be ssl or not and pylint
+# doesn't parse that
+
+
 import os
 import select
 import socket
-import sys
-import time
-import signal
 import errno
 import threading
 
-from ganeti import constants
-from ganeti import serializer
 from ganeti import workerpool
-from ganeti import utils
 from ganeti import http
+from ganeti import utils
 
 
 HTTP_CLIENT_THREADS = 10
@@ -47,7 +45,7 @@ HTTP_CLIENT_THREADS = 10
 
 class HttpClientRequest(object):
   def __init__(self, host, port, method, path, headers=None, post_data=None,
-               ssl_params=None, ssl_verify_peer=False):
+               ssl_params=None, ssl_verify_peer=False, read_timeout=None):
     """Describes an HTTP request.
 
     @type host: string
@@ -65,8 +63,11 @@ class HttpClientRequest(object):
     @type ssl_params: HttpSslParams
     @param ssl_params: SSL key and certificate
     @type ssl_verify_peer: bool
-    @param ssl_verify_peer: Whether to compare our certificate with server's
-                            certificate
+    @param ssl_verify_peer: Whether to compare our certificate with
+        server's certificate
+    @type read_timeout: int
+    @param read_timeout: if passed, it will be used as the read
+        timeout while reading the response from the server
 
     """
     if post_data is not None:
@@ -84,6 +85,7 @@ class HttpClientRequest(object):
     self.path = path
     self.headers = headers
     self.post_data = post_data
+    self.read_timeout = read_timeout
 
     self.success = None
     self.error = None
@@ -98,6 +100,14 @@ class HttpClientRequest(object):
     self.resp_headers = None
     self.resp_body = None
 
+  def __repr__(self):
+    status = ["%s.%s" % (self.__class__.__module__, self.__class__.__name__),
+              "%s:%s" % (self.host, self.port),
+              self.method,
+              self.path]
+
+    return "<%s at %#x>" % (" ".join(status), id(self))
+
 
 class _HttpClientToServerMessageWriter(http.HttpMessageWriter):
   pass
@@ -132,7 +142,7 @@ class _HttpServerToClientMessageReader(http.HttpMessageReader):
       status = int(status)
       if status < 100 or status > 999:
         status = -1
-    except ValueError:
+    except (TypeError, ValueError):
       status = -1
 
     if status == -1:
@@ -167,8 +177,6 @@ class HttpClientRequestExecutor(http.HttpBase):
     http.HttpBase.__init__(self)
     self.request = req
 
-    self.poller = select.poll()
-
     try:
       # TODO: Implement connection caching/keep-alive
       self.sock = self._CreateSocket(req.ssl_params,
@@ -194,9 +202,9 @@ class HttpClientRequestExecutor(http.HttpBase):
       finally:
         # TODO: Keep-alive is not supported, always close connection
         force_close = True
-        http.ShutdownConnection(self.poller, self.sock,
-                                self.CLOSE_TIMEOUT, self.WRITE_TIMEOUT,
-                                response_msg_reader, force_close)
+        http.ShutdownConnection(self.sock, self.CLOSE_TIMEOUT,
+                                self.WRITE_TIMEOUT, response_msg_reader,
+                                force_close)
 
       self.sock.close()
       self.sock = None
@@ -246,8 +254,8 @@ class HttpClientRequestExecutor(http.HttpBase):
 
     if not connected:
       # Wait for connection
-      event = http.WaitForSocketCondition(self.poller, self.sock,
-                                          select.POLLOUT, self.CONNECT_TIMEOUT)
+      event = utils.WaitForFdCondition(self.sock, select.POLLOUT,
+                                       self.CONNECT_TIMEOUT)
       if event is None:
         raise http.HttpError("Timeout while connecting to server")
 
@@ -264,6 +272,14 @@ class HttpClientRequestExecutor(http.HttpBase):
     # keep-alive settings, see "man 7 tcp" for TCP_KEEPCNT, TCP_KEEPIDLE and
     # TCP_KEEPINTVL.
 
+    # Do the secret SSL handshake
+    if self.using_ssl:
+      self.sock.set_connect_state() # pylint: disable-msg=E1103
+      try:
+        http.Handshake(self.sock, self.WRITE_TIMEOUT)
+      except http.HttpSessionHandshakeUnexpectedEOF:
+        raise http.HttpError("Server closed connection during SSL handshake")
+
   def _SendRequest(self):
     """Sends request to server.
 
@@ -303,10 +319,14 @@ class HttpClientRequestExecutor(http.HttpBase):
     """
     response_msg = http.HttpMessage()
 
+    if self.request.read_timeout is None:
+      timeout = self.READ_TIMEOUT
+    else:
+      timeout = self.request.read_timeout
+
     try:
       response_msg_reader = \
-        _HttpServerToClientMessageReader(self.sock, response_msg,
-                                         self.READ_TIMEOUT)
+        _HttpServerToClientMessageReader(self.sock, response_msg, timeout)
     except http.HttpSocketTimeout:
       raise http.HttpError("Timeout while reading response")
     except socket.error, err:
@@ -325,12 +345,18 @@ class _HttpClientPendingRequest(object):
     # Thread synchronization
     self.done = threading.Event()
 
+  def __repr__(self):
+    status = ["%s.%s" % (self.__class__.__module__, self.__class__.__name__),
+              "req=%r" % self.request]
+
+    return "<%s at %#x>" % (" ".join(status), id(self))
+
 
 class HttpClientWorker(workerpool.BaseWorker):
   """HTTP client worker class.
 
   """
-  def RunTask(self, pend_req):
+  def RunTask(self, pend_req): # pylint: disable-msg=W0221
     try:
       HttpClientRequestExecutor(pend_req.request)
     finally:
@@ -339,7 +365,8 @@ class HttpClientWorker(workerpool.BaseWorker):
 
 class HttpClientWorkerPool(workerpool.WorkerPool):
   def __init__(self, manager):
-    workerpool.WorkerPool.__init__(self, HTTP_CLIENT_THREADS,
+    workerpool.WorkerPool.__init__(self, "HttpClient",
+                                   HTTP_CLIENT_THREADS,
                                    HttpClientWorker)
     self.manager = manager
 
@@ -362,7 +389,7 @@ class HttpClientManager(object):
     @type requests: List of HttpClientRequest instances
     @param requests: The requests to execute
     @rtype: List of HttpClientRequest instances
-    @returns: The list of requests passed in
+    @return: The list of requests passed in
 
     """
     # _HttpClientPendingRequest is used for internal thread synchronization