cli: Fix wrong argument kind for groups
[ganeti-local] / lib / http / server.py
index 95a3756..7a46af6 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
 #
 # 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
@@ -26,15 +26,14 @@ import BaseHTTPServer
 import cgi
 import logging
 import os
 import cgi
 import logging
 import os
-import select
 import socket
 import time
 import signal
 import socket
 import time
 import signal
+import asyncore
 
 
-from ganeti import constants
-from ganeti import serializer
-from ganeti import utils
 from ganeti import http
 from ganeti import http
+from ganeti import utils
+from ganeti import netutils
 
 
 WEEKDAYNAME = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
 
 
 WEEKDAYNAME = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
@@ -76,12 +75,12 @@ class _HttpServerRequest(object):
   """Data structure for HTTP request on server side.
 
   """
   """Data structure for HTTP request on server side.
 
   """
-  def __init__(self, request_msg):
+  def __init__(self, method, path, headers, body):
     # Request attributes
     # Request attributes
-    self.request_method = request_msg.start_line.method
-    self.request_path = request_msg.start_line.path
-    self.request_headers = request_msg.headers
-    self.request_body = request_msg.decoded_body
+    self.request_method = method
+    self.request_path = path
+    self.request_headers = headers
+    self.request_body = body
 
     # Response attributes
     self.resp_headers = {}
 
     # Response attributes
     self.resp_headers = {}
@@ -90,6 +89,14 @@ class _HttpServerRequest(object):
     # authentication)
     self.private = None
 
     # authentication)
     self.private = None
 
+  def __repr__(self):
+    status = ["%s.%s" % (self.__class__.__module__, self.__class__.__name__),
+              self.request_method, self.request_path,
+              "headers=%r" % str(self.request_headers),
+              "body=%r" % (self.request_body, )]
+
+    return "<%s at %#x>" % (" ".join(status), id(self))
+
 
 class _HttpServerToClientMessageWriter(http.HttpMessageWriter):
   """Writes an HTTP response to client.
 
 class _HttpServerToClientMessageWriter(http.HttpMessageWriter):
   """Writes an HTTP response to client.
@@ -271,6 +278,14 @@ class HttpServerRequestExecutor(object):
         try:
           try:
             request_msg_reader = self._ReadRequest()
         try:
           try:
             request_msg_reader = self._ReadRequest()
+
+            # RFC2616, 14.23: All Internet-based HTTP/1.1 servers MUST respond
+            # with a 400 (Bad Request) status code to any HTTP/1.1 request
+            # message which lacks a Host header field.
+            if (self.request_msg.start_line.version == http.HTTP_1_1 and
+                http.HTTP_HOST not in self.request_msg.headers):
+              raise http.HttpBadRequest(message="Missing Host header")
+
             self._HandleRequest()
 
             # Only wait for client to close if we didn't have any exception.
             self._HandleRequest()
 
             # Only wait for client to close if we didn't have any exception.
@@ -310,7 +325,12 @@ class HttpServerRequestExecutor(object):
     """Calls the handler function for the current request.
 
     """
     """Calls the handler function for the current request.
 
     """
-    handler_context = _HttpServerRequest(self.request_msg)
+    handler_context = _HttpServerRequest(self.request_msg.start_line.method,
+                                         self.request_msg.start_line.path,
+                                         self.request_msg.headers,
+                                         self.request_msg.body)
+
+    logging.debug("Handling request %r", handler_context)
 
     try:
       try:
 
     try:
       try:
@@ -328,12 +348,12 @@ class HttpServerRequestExecutor(object):
         logging.exception("Unknown exception")
         raise http.HttpInternalServerError(message="Unknown error")
 
         logging.exception("Unknown exception")
         raise http.HttpInternalServerError(message="Unknown error")
 
-      # TODO: Content-type
-      encoder = http.HttpJsonConverter()
+      if not isinstance(result, basestring):
+        raise http.HttpError("Handler function didn't return string type")
+
       self.response_msg.start_line.code = http.HTTP_OK
       self.response_msg.start_line.code = http.HTTP_OK
-      self.response_msg.body = encoder.Encode(result)
       self.response_msg.headers = handler_context.resp_headers
       self.response_msg.headers = handler_context.resp_headers
-      self.response_msg.headers[http.HTTP_CONTENT_TYPE] = encoder.CONTENT_TYPE
+      self.response_msg.body = result
     finally:
       # No reason to keep this any longer, even for exceptions
       handler_context.private = None
     finally:
       # No reason to keep this any longer, even for exceptions
       handler_context.private = None
@@ -418,7 +438,8 @@ class HttpServerRequestExecutor(object):
     """
     return self.error_message_format % values
 
     """
     return self.error_message_format % values
 
-class HttpServer(http.HttpBase):
+
+class HttpServer(http.HttpBase, asyncore.dispatcher):
   """Generic HTTP server class
 
   Users of this class must subclass it and override the HandleRequest function.
   """Generic HTTP server class
 
   Users of this class must subclass it and override the HandleRequest function.
@@ -448,6 +469,7 @@ class HttpServer(http.HttpBase):
 
     """
     http.HttpBase.__init__(self)
 
     """
     http.HttpBase.__init__(self)
+    asyncore.dispatcher.__init__(self)
 
     if request_executor_class is None:
       self.request_executor = HttpServerRequestExecutor
 
     if request_executor_class is None:
       self.request_executor = HttpServerRequestExecutor
@@ -457,15 +479,15 @@ class HttpServer(http.HttpBase):
     self.mainloop = mainloop
     self.local_address = local_address
     self.port = port
     self.mainloop = mainloop
     self.local_address = local_address
     self.port = port
-
-    self.socket = self._CreateSocket(ssl_params, ssl_verify_peer)
+    family = netutils.IPAddress.GetAddressFamily(local_address)
+    self.socket = self._CreateSocket(ssl_params, ssl_verify_peer, family)
 
     # Allow port to be reused
     self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
 
     self._children = []
 
     # Allow port to be reused
     self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
 
     self._children = []
-
-    mainloop.RegisterIO(self, self.socket.fileno(), select.POLLIN)
+    self.set_socket(self.socket)
+    self.accepting = True
     mainloop.RegisterSignal(self)
 
   def Start(self):
     mainloop.RegisterSignal(self)
 
   def Start(self):
@@ -475,9 +497,8 @@ class HttpServer(http.HttpBase):
   def Stop(self):
     self.socket.close()
 
   def Stop(self):
     self.socket.close()
 
-  def OnIO(self, fd, condition):
-    if condition & select.POLLIN:
-      self._IncomingConnection()
+  def handle_accept(self):
+    self._IncomingConnection()
 
   def OnSignal(self, signum):
     if signum == signal.SIGCHLD:
 
   def OnSignal(self, signum):
     if signum == signal.SIGCHLD:
@@ -498,7 +519,7 @@ class HttpServer(http.HttpBase):
           # As soon as too many children run, we'll not respond to new
           # requests. The real solution would be to add a timeout for children
           # and killing them after some time.
           # As soon as too many children run, we'll not respond to new
           # requests. The real solution would be to add a timeout for children
           # and killing them after some time.
-          pid, status = os.waitpid(0, 0)
+          pid, _ = os.waitpid(0, 0)
         except os.error:
           pid = None
         if pid and pid in self._children:
         except os.error:
           pid = None
         if pid and pid in self._children:
@@ -506,7 +527,7 @@ class HttpServer(http.HttpBase):
 
     for child in self._children:
       try:
 
     for child in self._children:
       try:
-        pid, status = os.waitpid(child, os.WNOHANG)
+        pid, _ = os.waitpid(child, os.WNOHANG)
       except os.error:
         pid = None
       if pid and pid in self._children:
       except os.error:
         pid = None
       if pid and pid in self._children:
@@ -516,6 +537,7 @@ class HttpServer(http.HttpBase):
     """Called for each incoming connection
 
     """
     """Called for each incoming connection
 
     """
+    # pylint: disable-msg=W0212
     (connection, client_addr) = self.socket.accept()
 
     self._CollectChildren(False)
     (connection, client_addr) = self.socket.accept()
 
     self._CollectChildren(False)
@@ -524,8 +546,21 @@ class HttpServer(http.HttpBase):
     if pid == 0:
       # Child process
       try:
     if pid == 0:
       # Child process
       try:
+        # The client shouldn't keep the listening socket open. If the parent
+        # process is restarted, it would fail when there's already something
+        # listening (in this case its own child from a previous run) on the
+        # same port.
+        try:
+          self.socket.close()
+        except socket.error:
+          pass
+        self.socket = None
+
+        # In case the handler code uses temporary files
+        utils.ResetTempfileModule()
+
         self.request_executor(self, connection, client_addr)
         self.request_executor(self, connection, client_addr)
-      except Exception:
+      except Exception: # pylint: disable-msg=W0703
         logging.exception("Error while handling request from %s:%s",
                           client_addr[0], client_addr[1])
         os._exit(1)
         logging.exception("Error while handling request from %s:%s",
                           client_addr[0], client_addr[1])
         os._exit(1)