initial checkin of component server
authorTony Mack <tmack@cs.princeton.edu>
Thu, 3 Dec 2009 22:01:59 +0000 (22:01 +0000)
committerTony Mack <tmack@cs.princeton.edu>
Thu, 3 Dec 2009 22:01:59 +0000 (22:01 +0000)
sfa/util/componentserver.py [new file with mode: 0644]

diff --git a/sfa/util/componentserver.py b/sfa/util/componentserver.py
new file mode 100644 (file)
index 0000000..86a00c2
--- /dev/null
@@ -0,0 +1,139 @@
+##
+# This module implements a general-purpose server layer for sfa.
+# The same basic server should be usable on the registry, component, or
+# other interfaces.
+#
+# TODO: investigate ways to combine this with existing PLC server?
+##
+
+### $Id: geniserver.py 15849 2009-11-23 01:36:21Z tmack $
+### $URL: https://svn.planet-lab.org/svn/sfa/trunk/sfa/util/geniserver.py $
+
+import sys
+import traceback
+import threading
+import socket, os
+
+import SocketServer
+import BaseHTTPServer
+import SimpleHTTPServer
+import SimpleXMLRPCServer
+
+from OpenSSL import SSL
+
+from sfa.trust.certificate import Keypair, Certificate
+from sfa.trust.credential import *
+
+from sfa.util.faults import *
+from sfa.plc.api import ComponentAPI 
+from sfa.util.geniserver import verify_callback, SecureXMLRPCServer
+from sfa.util.debug import log
+
+
+##
+# taken from the web (XXX find reference). Implents HTTPS xmlrpc request handler
+
+class SecureXMLRpcRequestHandler(SimpleXMLRPCServer.SimpleXMLRPCRequestHandler):
+    """Secure XML-RPC request handler class.
+
+    It it very similar to SimpleXMLRPCRequestHandler but it uses HTTPS for transporting XML data.
+    """
+    def setup(self):
+        self.connection = self.request
+        self.rfile = socket._fileobject(self.request, "rb", self.rbufsize)
+        self.wfile = socket._fileobject(self.request, "wb", self.wbufsize)
+
+    def do_POST(self):
+        """Handles the HTTPS POST request.
+
+        It was copied out from SimpleXMLRPCServer.py and modified to shutdown the socket cleanly.
+        """
+        try:
+            self.api = ComponentAPI(peer_cert = self.server.peer_cert, 
+                           interface = self.server.interface, 
+                           key_file = self.server.key_file, 
+                           cert_file = self.server.cert_file)
+            # get arguments
+            request = self.rfile.read(int(self.headers["content-length"]))
+            # In previous versions of SimpleXMLRPCServer, _dispatch
+            # could be overridden in this class, instead of in
+            # SimpleXMLRPCDispatcher. To maintain backwards compatibility,
+            # check to see if a subclass implements _dispatch and dispatch
+            # using that method if present.
+            #response = self.server._marshaled_dispatch(request, getattr(self, '_dispatch', None))
+            # XX TODO: Need to get the real remote address
+            remote_addr = (remote_ip, remote_port) = self.connection.getpeername()
+            self.api.remote_addr = remote_addr
+            #remote_addr = (self.rfile.connection.remote_ip, remote_port)
+            #self.api.remote_addr = remote_addr
+            response = self.api.handle(remote_addr, request)
+
+        
+        except Exception, fault:
+            raise
+            # This should only happen if the module is buggy
+            # internal error, report as HTTP server error
+            self.send_response(500)
+            self.end_headers()
+        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!
+
+##
+# Implements an HTTPS XML-RPC server. Generally it is expected that GENI
+# functions will take a credential string, which is passed to
+# decode_authentication. Decode_authentication() will verify the validity of
+# the credential, and verify that the user is using the key that matches the
+# GID supplied in the credential.
+
+class ComponentServer(threading.Thread):
+
+    ##
+    # Create a new GeniServer object.
+    #
+    # @param ip the ip address to listen on
+    # @param port the port to listen on
+    # @param key_file private key filename of registry
+    # @param cert_file certificate filename containing public key 
+    #   (could be a GID file)
+
+    def __init__(self, ip, port, key_file, cert_file, api=None):
+        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.trusted_cert_list = None
+        self.register_functions()
+
+
+    ##
+    # Register functions that will be served by the XMLRPC server. This
+    # function should be overrided by each descendant class.
+
+    def register_functions(self):
+        self.server.register_function(self.noop)
+
+    ##
+    # Sample no-op server function. The no-op function decodes the credential
+    # that was passed to it.
+
+    def noop(self, cred, anything):
+        self.decode_authentication(cred)
+
+        return anything
+
+    ##
+    # Execute the server, serving requests forever. 
+
+    def run(self):
+        self.server.serve_forever()
+
+