utils.log: Split formatter building into separate function
[ganeti-local] / lib / http / __init__.py
index 272cff0..9049829 100644 (file)
@@ -1,7 +1,7 @@
 #
 #
 
-# Copyright (C) 2007, 2008 Google Inc.
+# Copyright (C) 2007, 2008, 2010 Google Inc.
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -32,7 +32,6 @@ import errno
 from cStringIO import StringIO
 
 from ganeti import constants
-from ganeti import serializer
 from ganeti import utils
 
 
@@ -67,6 +66,7 @@ HTTP_AUTHENTICATION_INFO = "Authentication-Info"
 HTTP_ALLOW = "Allow"
 
 HTTP_APP_OCTET_STREAM = "application/octet-stream"
+HTTP_APP_JSON = "application/json"
 
 _SSL_UNEXPECTED_EOF = "Unexpected EOF"
 
@@ -323,56 +323,6 @@ class HttpVersionNotSupported(HttpException):
   code = 505
 
 
-class HttpJsonConverter: # pylint: disable-msg=W0232
-  CONTENT_TYPE = "application/json"
-
-  @staticmethod
-  def Encode(data):
-    return serializer.DumpJson(data)
-
-  @staticmethod
-  def Decode(data):
-    return serializer.LoadJson(data)
-
-
-def WaitForSocketCondition(sock, event, timeout):
-  """Waits for a condition to occur on the socket.
-
-  @type sock: socket
-  @param sock: Wait for events on this socket
-  @type event: int
-  @param event: ORed condition (see select module)
-  @type timeout: float or None
-  @param timeout: Timeout in seconds
-  @rtype: int or None
-  @return: None for timeout, otherwise occured conditions
-
-  """
-  check = (event | select.POLLPRI |
-           select.POLLNVAL | select.POLLHUP | select.POLLERR)
-
-  if timeout is not None:
-    # Poller object expects milliseconds
-    timeout *= 1000
-
-  poller = select.poll()
-  poller.register(sock, event)
-  try:
-    while True:
-      # TODO: If the main thread receives a signal and we have no timeout, we
-      # could wait forever. This should check a global "quit" flag or
-      # something every so often.
-      io_events = poller.poll(timeout)
-      if not io_events:
-        # Timeout
-        return None
-      for (_, evcond) in io_events:
-        if evcond & check:
-          return evcond
-  finally:
-    poller.unregister(sock)
-
-
 def SocketOperation(sock, op, arg1, timeout):
   """Wrapper around socket functions.
 
@@ -423,13 +373,13 @@ def SocketOperation(sock, op, arg1, timeout):
       else:
         wait_for_event = event_poll
 
-      event = WaitForSocketCondition(sock, wait_for_event, timeout)
+      event = utils.WaitForFdCondition(sock, wait_for_event, timeout)
       if event is None:
         raise HttpSocketTimeout()
 
-      if (op == SOCKOP_RECV and
-          event & (select.POLLNVAL | select.POLLHUP | select.POLLERR)):
-        return ""
+      if event & (select.POLLNVAL | select.POLLHUP | select.POLLERR):
+        # Let the socket functions handle these
+        break
 
       if not event & wait_for_event:
         continue
@@ -600,6 +550,7 @@ class HttpSslParams(object):
     """
     self.ssl_key_pem = utils.ReadFile(ssl_key_path)
     self.ssl_cert_pem = utils.ReadFile(ssl_cert_path)
+    self.ssl_cert_path = ssl_cert_path
 
   def GetKey(self):
     return OpenSSL.crypto.load_privatekey(OpenSSL.crypto.FILETYPE_PEM,
@@ -620,7 +571,7 @@ class HttpBase(object):
     self._ssl_key = None
     self._ssl_cert = None
 
-  def _CreateSocket(self, ssl_params, ssl_verify_peer):
+  def _CreateSocket(self, ssl_params, ssl_verify_peer, family):
     """Creates a TCP socket and initializes SSL if needed.
 
     @type ssl_params: HttpSslParams
@@ -628,11 +579,14 @@ class HttpBase(object):
     @type ssl_verify_peer: bool
     @param ssl_verify_peer: Whether to require client certificate
         and compare it with our certificate
+    @type family: int
+    @param family: socket.AF_INET | socket.AF_INET6
 
     """
-    self._ssl_params = ssl_params
+    assert family in (socket.AF_INET, socket.AF_INET6)
 
-    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+    self._ssl_params = ssl_params
+    sock = socket.socket(family, socket.SOCK_STREAM)
 
     # Should we enable SSL?
     self.using_ssl = ssl_params is not None
@@ -646,6 +600,10 @@ class HttpBase(object):
     ctx = OpenSSL.SSL.Context(OpenSSL.SSL.SSLv23_METHOD)
     ctx.set_options(OpenSSL.SSL.OP_NO_SSLv2)
 
+    ciphers = self.GetSslCiphers()
+    logging.debug("Setting SSL cipher string %s", ciphers)
+    ctx.set_cipher_list(ciphers)
+
     ctx.use_privatekey(self._ssl_key)
     ctx.use_certificate(self._ssl_cert)
     ctx.check_privatekey()
@@ -655,8 +613,23 @@ class HttpBase(object):
                      OpenSSL.SSL.VERIFY_FAIL_IF_NO_PEER_CERT,
                      self._SSLVerifyCallback)
 
+      # Also add our certificate as a trusted CA to be sent to the client.
+      # This is required at least for GnuTLS clients to work.
+      try:
+        # This will fail for PyOpenssl versions before 0.10
+        ctx.add_client_ca(self._ssl_cert)
+      except AttributeError:
+        # Fall back to letting OpenSSL read the certificate file directly.
+        ctx.load_client_ca(ssl_params.ssl_cert_path)
+
     return OpenSSL.SSL.Connection(ctx, sock)
 
+  def GetSslCiphers(self): # pylint: disable-msg=R0201
+    """Returns the ciphers string for SSL.
+
+    """
+    return constants.OPENSSL_CIPHERS
+
   def _SSLVerifyCallback(self, conn, cert, errnum, errdepth, ok):
     """Verify the certificate provided by the peer
 
@@ -680,7 +653,6 @@ class HttpMessage(object):
     self.start_line = None
     self.headers = None
     self.body = None
-    self.decoded_body = None
 
 
 class HttpClientToServerStartLine(object):
@@ -851,17 +823,9 @@ class HttpMessageReader(object):
     assert self.parser_status == self.PS_COMPLETE
     assert not buf, "Parser didn't read full response"
 
+    # Body is complete
     msg.body = self.body_buffer.getvalue()
 
-    # TODO: Content-type, error handling
-    if msg.body:
-      msg.decoded_body = HttpJsonConverter().Decode(msg.body)
-    else:
-      msg.decoded_body = None
-
-    if msg.decoded_body:
-      logging.debug("Message body: %s", msg.decoded_body)
-
   def _ContinueParsing(self, buf, eof):
     """Main function for HTTP message state machine.
 
@@ -886,7 +850,7 @@ class HttpMessageReader(object):
         # the CRLF."
         if idx == 0:
           # TODO: Limit number of CRLFs/empty lines for safety?
-          buf = buf[:2]
+          buf = buf[2:]
           continue
 
         if idx > 0:
@@ -1043,7 +1007,7 @@ class HttpMessageReader(object):
     if hdr_content_length:
       try:
         self.content_length = int(hdr_content_length)
-      except ValueError:
+      except (TypeError, ValueError):
         self.content_length = None
       if self.content_length is not None and self.content_length < 0:
         self.content_length = None