trimmed useless imports, unstarred all imports
[sfa.git] / sfa / server / componentserver.py
1 ##
2 # This module implements a general-purpose server layer for sfa.
3 # The same basic server should be usable on the registry, component, or
4 # other interfaces.
5 #
6 # TODO: investigate ways to combine this with existing PLC server?
7 ##
8
9 import threading
10 import socket
11 import SimpleXMLRPCServer
12
13 from sfa.util.sfalogging import logger
14 from sfa.trust.certificate import Keypair, Certificate
15 from sfa.plc.api import ComponentAPI 
16 from sfa.server.threadedserver import ThreadedServer 
17
18
19 ##
20 # taken from the web (XXX find reference). Implents HTTPS xmlrpc request handler
21
22 class SecureXMLRpcRequestHandler(SimpleXMLRPCServer.SimpleXMLRPCRequestHandler):
23     """Secure XML-RPC request handler class.
24
25     It it very similar to SimpleXMLRPCRequestHandler but it uses HTTPS for transporting XML data.
26     """
27     def setup(self):
28         self.connection = self.request
29         self.rfile = socket._fileobject(self.request, "rb", self.rbufsize)
30         self.wfile = socket._fileobject(self.request, "wb", self.wbufsize)
31
32     def do_POST(self):
33         """Handles the HTTPS POST request.
34
35         It was copied out from SimpleXMLRPCServer.py and modified to shutdown the socket cleanly.
36         """
37         try:
38             peer_cert = Certificate()
39             peer_cert.load_from_pyopenssl_x509(self.connection.get_peer_certificate())
40             self.api = ComponentAPI(peer_cert = peer_cert, 
41                                     interface = self.server.interface, 
42                                     key_file = self.server.key_file, 
43                                     cert_file = self.server.cert_file)
44             # get arguments
45             request = self.rfile.read(int(self.headers["content-length"]))
46             # In previous versions of SimpleXMLRPCServer, _dispatch
47             # could be overridden in this class, instead of in
48             # SimpleXMLRPCDispatcher. To maintain backwards compatibility,
49             # check to see if a subclass implements _dispatch and dispatch
50             # using that method if present.
51             #response = self.server._marshaled_dispatch(request, getattr(self, '_dispatch', None))
52             # XX TODO: Need to get the real remote address
53             remote_addr = (remote_ip, remote_port) = self.connection.getpeername()
54             self.api.remote_addr = remote_addr
55             #remote_addr = (self.rfile.connection.remote_ip, remote_port)
56             #self.api.remote_addr = remote_addr
57             response = self.api.handle(remote_addr, request)
58
59         
60         except Exception, fault:
61             raise
62             # This should only happen if the module is buggy
63             # internal error, report as HTTP server error
64             self.send_response(500)
65             self.end_headers()
66             logger.log_exc("componentserver.SecureXMLRpcRequestHandler.do_POST")
67         else:
68             # got a valid XML RPC response
69             self.send_response(200)
70             self.send_header("Content-type", "text/xml")
71             self.send_header("Content-length", str(len(response)))
72             self.end_headers()
73             self.wfile.write(response)
74
75             # shut down the connection
76             self.wfile.flush()
77             self.connection.shutdown() # Modified here!
78
79 ##
80 # Implements an HTTPS XML-RPC server. Generally it is expected that SFA
81 # functions will take a credential string, which is passed to
82 # decode_authentication. Decode_authentication() will verify the validity of
83 # the credential, and verify that the user is using the key that matches the
84 # GID supplied in the credential.
85
86 class ComponentServer(threading.Thread):
87
88     ##
89     # Create a new SfaServer object.
90     #
91     # @param ip the ip address to listen on
92     # @param port the port to listen on
93     # @param key_file private key filename of registry
94     # @param cert_file certificate filename containing public key 
95     #   (could be a GID file)
96
97     def __init__(self, ip, port, key_file, cert_file, api=None):
98         threading.Thread.__init__(self)
99         self.key = Keypair(filename = key_file)
100         self.cert = Certificate(filename = cert_file)
101         self.server = ThreadedServer((ip, port), SecureXMLRpcRequestHandler, key_file, cert_file)
102         self.trusted_cert_list = None
103         self.register_functions()
104
105
106     ##
107     # Register functions that will be served by the XMLRPC server. This
108     # function should be overrided by each descendant class.
109
110     def register_functions(self):
111         self.server.register_function(self.noop)
112
113     ##
114     # Sample no-op server function. The no-op function decodes the credential
115     # that was passed to it.
116
117     def noop(self, cred, anything):
118         self.decode_authentication(cred)
119
120         return anything
121
122     ##
123     # Execute the server, serving requests forever. 
124
125     def run(self):
126         self.server.serve_forever()
127
128