really fixed the redundant logging issue this time.
[sfa.git] / sfa / util / server.py
index 0151e5b..fadd4ca 100644 (file)
@@ -6,26 +6,26 @@
 # TODO: investigate ways to combine this with existing PLC server?
 ##
 
-### $Id$
-### $URL$
-
 import sys
+import socket, os
 import traceback
 import threading
-import socket, os
+from Queue import Queue
 import SocketServer
 import BaseHTTPServer
 import SimpleHTTPServer
 import SimpleXMLRPCServer
 from OpenSSL import SSL
-from Queue import Queue
+
 from sfa.trust.certificate import Keypair, Certificate
+from sfa.trust.trustedroot import TrustedRootList
+from sfa.util.config import Config
 from sfa.trust.credential import *
 from sfa.util.faults import *
 from sfa.plc.api import SfaAPI
 from sfa.util.cache import Cache 
-from sfa.util.debug import log
 from sfa.util.sfalogging import logger
+
 ##
 # Verification callback for pyOpenSSL. We do our own checking of keys because
 # we have our own authentication spec. Thus we disable several of the normal
@@ -77,7 +77,7 @@ def verify_callback(conn, x509, err, depth, preverify):
     return 0
 
 ##
-# taken from the web (XXX find reference). Implents HTTPS xmlrpc request handler
+# taken from the web (XXX find reference). Implements HTTPS xmlrpc request handler
 class SecureXMLRpcRequestHandler(SimpleXMLRPCServer.SimpleXMLRPCRequestHandler):
     """Secure XML-RPC request handler class.
 
@@ -107,25 +107,24 @@ class SecureXMLRpcRequestHandler(SimpleXMLRPCServer.SimpleXMLRPCRequestHandler):
             remote_addr = (remote_ip, remote_port) = self.connection.getpeername()
             self.api.remote_addr = remote_addr            
             response = self.api.handle(remote_addr, request, self.server.method_map)
-
-        
         except Exception, fault:
             # This should only happen if the module is buggy
             # internal error, report as HTTP server error
-            self.send_response(500)
-            self.end_headers()
-            traceback.print_exc()
-        else:
-            # got a valid XML RPC response
-            self.send_response(200)
-            self.send_header("Content-type", "text/xml")
-            self.send_header("Content-length", str(len(response)))
-            self.end_headers()
-            self.wfile.write(response)
-
-            # shut down the connection
-            self.wfile.flush()
-            self.connection.shutdown() # Modified here!
+            logger.log_exc("server.do_POST")
+            response = self.api.prepare_response(fault)
+            #self.send_response(500)
+            #self.end_headers()
+       
+        # got a valid response
+        self.send_response(200)
+        self.send_header("Content-type", "text/xml")
+        self.send_header("Content-length", str(len(response)))
+        self.end_headers()
+        self.wfile.write(response)
+
+        # shut down the connection
+        self.wfile.flush()
+        self.connection.shutdown() # Modified here!
 
 ##
 # Taken from the web (XXX find reference). Implements an HTTPS xmlrpc server
@@ -135,6 +134,7 @@ class SecureXMLRPCServer(BaseHTTPServer.HTTPServer,SimpleXMLRPCServer.SimpleXMLR
 
         It it very similar to SimpleXMLRPCServer but it uses HTTPS for transporting XML data.
         """
+        logger.debug("SecureXMLRPCServer.__init__, server_address=%s, cert_file=%s"%(server_address,cert_file))
         self.logRequests = logRequests
         self.interface = None
         self.key_file = key_file
@@ -153,6 +153,10 @@ class SecureXMLRPCServer(BaseHTTPServer.HTTPServer,SimpleXMLRPCServer.SimpleXMLR
         ctx.use_certificate_file(cert_file)
         # If you wanted to verify certs against known CAs.. this is how you would do it
         #ctx.load_verify_locations('/etc/sfa/trusted_roots/plc.gpo.gid')
+        config = Config()
+        trusted_cert_files = TrustedRootList(config.get_trustedroots_dir()).get_file_list()
+        for cert_file in trusted_cert_files:
+            ctx.load_verify_locations(cert_file)
         ctx.set_verify(SSL.VERIFY_PEER | SSL.VERIFY_FAIL_IF_NO_PEER_CERT, verify_callback)
         ctx.set_verify_depth(5)
         ctx.set_app_data(self)
@@ -167,14 +171,41 @@ class SecureXMLRPCServer(BaseHTTPServer.HTTPServer,SimpleXMLRPCServer.SimpleXMLR
     # the client.
 
     def _dispatch(self, method, params):
+        logger.debug("SecureXMLRPCServer._dispatch, method=%s"%method)
         try:
             return SimpleXMLRPCServer.SimpleXMLRPCDispatcher._dispatch(self, method, params)
         except:
             # can't use format_exc() as it is not available in jython yet
-            # (evein in trunk).
+            # (even in trunk).
             type, value, tb = sys.exc_info()
             raise xmlrpclib.Fault(1,''.join(traceback.format_exception(type, value, tb)))
 
+    # override this one from the python 2.7 code
+    # originally defined in class TCPServer
+    def shutdown_request(self, request):
+        """Called to shutdown and close an individual request."""
+        # ---------- 
+        # the std python 2.7 code just attempts a request.shutdown(socket.SHUT_WR)
+        # this works fine with regular sockets
+        # However we are dealing with an instance of OpenSSL.SSL.Connection instead
+        # This one only supports shutdown(), and in addition this does not
+        # always perform as expected
+        # ---------- std python 2.7 code
+        try:
+            #explicitly shutdown.  socket.close() merely releases
+            #the socket and waits for GC to perform the actual close.
+            request.shutdown(socket.SHUT_WR)
+        except socket.error:
+            pass #some platforms may raise ENOTCONN here
+        # ----------
+        except TypeError:
+            # we are dealing with an OpenSSL.Connection object, 
+            # try to shut it down but never mind if that fails
+            try: request.shutdown()
+            except: pass
+        # ----------
+        self.close_request(request)
+
 ## From Active State code: http://code.activestate.com/recipes/574454/
 # This is intended as a drop-in replacement for the ThreadingMixIn class in 
 # module SocketServer of the standard lib. Instead of spawning a new thread 
@@ -247,15 +278,16 @@ class SfaServer(threading.Thread):
     # @param cert_file certificate filename containing public key 
     #   (could be a GID file)
 
-    def __init__(self, ip, port, key_file, cert_file):
+    def __init__(self, ip, port, key_file, cert_file,interface):
         threading.Thread.__init__(self)
         self.key = Keypair(filename = key_file)
         self.cert = Certificate(filename = cert_file)
         #self.server = SecureXMLRPCServer((ip, port), SecureXMLRpcRequestHandler, key_file, cert_file)
         self.server = ThreadedServer((ip, port), SecureXMLRpcRequestHandler, key_file, cert_file)
+        self.server.interface=interface
         self.trusted_cert_list = None
         self.register_functions()
-
+        logger.info("Starting SfaServer, interface=%s"%interface)
 
     ##
     # Register functions that will be served by the XMLRPC server. This
@@ -270,7 +302,6 @@ class SfaServer(threading.Thread):
 
     def noop(self, cred, anything):
         self.decode_authentication(cred)
-
         return anything
 
     ##