source: fedd/fedd.py @ ea0a821

axis_examplecompt_changesinfo-opsversion-1.30version-2.00version-3.01version-3.02
Last change on this file since ea0a821 was 19cc408, checked in by Ted Faber <faber@…>, 16 years ago

refactoring

  • Property mode set to 100755
File size: 6.7 KB
Line 
1#!/usr/local/bin/python
2
3import sys
4
5from BaseHTTPServer import BaseHTTPRequestHandler
6
7from ZSI import Fault, ParseException, FaultFromNotUnderstood, \
8    FaultFromZSIException, FaultFromException, ParsedSoap, SoapWriter
9
10from M2Crypto import SSL
11from M2Crypto.SSL.SSLServer import SSLServer
12import xmlrpclib
13
14from optparse import OptionParser
15
16from fedd_util import fedd_ssl_context, fedid
17from fedd_deter_impl import new_feddservice
18from service_error import *
19
20# The SSL server here is based on the implementation described at
21# http://www.xml.com/pub/a/ws/2004/01/20/salz.html
22
23# Turn off the matching of hostname to certificate ID
24SSL.Connection.clientPostConnectionCheck = None
25
26class fedd_server(SSLServer):
27    def __init__(self, ME, handler, ssl_ctx, impl):
28        SSLServer.__init__(self, ME, handler, ssl_ctx)
29        self.impl = impl
30        self.soap_methods = impl.get_soap_services()
31        self.xmlrpc_methods = impl.get_xmlrpc_services()
32
33class fedd_soap_handler(BaseHTTPRequestHandler):
34    server_version = "ZSI/2.0 fedd/0.1 " + BaseHTTPRequestHandler.server_version
35
36    def send_xml(self, text, code=200):
37        """Send an XML document as reply"""
38        self.send_response(code)
39        self.send_header('Content-type', 'text/xml; charset="utf-8"')
40        self.send_header('Content-Length', str(len(text)))
41        self.end_headers()
42        self.wfile.write(text)
43        self.wfile.flush()
44
45    def send_fault(self, f, code=500):
46        """Send a SOAP encoded fault as reply"""
47        self.send_xml(f.AsSOAP(processContents="lax"), code)
48
49    def check_headers(self, ps):
50        """Send a fault for any required envelope headers"""
51        for (uri, localname) in ps.WhatMustIUnderstand():
52            self.send_fault(FaultFromNotUnderstood(uri, lname, 'fedd'))
53            return False
54        return  True
55
56    def check_method(self, ps):
57        """Confirm that this class implements the namespace and SOAP method"""
58        root = ps.body_root
59        if root.namespaceURI not in self.server.impl.soap_namespaces:
60            self.send_fault(Fault(Fault.Client, 
61                'Unknown namespace "%s"' % root.namespaceURI))
62            return False
63
64        if getattr(root, 'localName', None) == None:
65            self.send_fault(Fault(Fault.Client, 'No method"'))
66            return False
67        return True
68
69    def do_POST(self):
70        """Treat an HTTP POST request as a SOAP service call"""
71        try:
72            cl = int(self.headers['content-length'])
73            data = self.rfile.read(cl)
74            ps = ParsedSoap(data)
75        except ParseException, e:
76            self.send_fault(Fault(Fault.Client, str(e)))
77            return
78        except Exception, e:
79            self.send_fault(FaultFromException(e, 0, sys.exc_info()[2]))
80            return
81        if not self.check_headers(ps): return
82        if not self.check_method(ps): return
83        try:
84            resp = self.soap_dispatch(ps.body_root.localName, ps,
85                    fedid(cert=self.request.get_peer_cert()))
86        except Fault, f:
87            self.send_fault(f)
88            resp = None
89       
90        if resp != None:
91            sw = SoapWriter()
92            sw.serialize(resp)
93            self.send_xml(str(sw))
94
95    def soap_dispatch(self, method, req, fid):
96        if self.server.soap_methods.has_key(method):
97            try:
98                return self.server.soap_methods[method](req, fid)
99            except service_error, e:
100                de = ns0.faultType_Def(
101                        (ns0.faultType_Def.schema,
102                            "FeddFaultBody")).pyclass()
103                de._code=e.code
104                de._errstr=e.code_string()
105                de._desc=e.desc
106                if  e.is_server_error():
107                    raise Fault(Fault.Server, e.code_string(), detail=de)
108                else:
109                    raise Fault(Fault.Client, e.code_string(), detail=de)
110        else:
111            raise Fault(Fault.Client, "Unknown method: %s" % method)
112
113
114class fedd_xmlrpc_handler(BaseHTTPRequestHandler):
115    server_version = "ZSI/2.0 fedd/0.1 " + BaseHTTPRequestHandler.server_version
116
117    def send_xml(self, text, code=200):
118        """Send an XML document as reply"""
119        self.send_response(code)
120        self.send_header('Content-type', 'text/xml; charset="utf-8"')
121        self.send_header('Content-Length', str(len(text)))
122        self.end_headers()
123        self.wfile.write(text)
124        self.wfile.flush()
125
126    def do_POST(self):
127        """Treat an HTTP POST request as an XMLRPC service call"""
128
129        resp = None
130        data = None
131        method = None
132        cl = int(self.headers['content-length'])
133        data = self.rfile.read(cl)
134
135        try:
136            params, method = xmlrpclib.loads(data)
137        except xmlrpclib.ResponseError:
138            data = xmlrpclib.dumps(xmlrpclib.Fault("Client", 
139                "Malformed request"), methodresponse=True)
140       
141        if method != None:
142            try:
143                resp = self.xmlrpc_dispatch(method, params,
144                            fedid(cert=self.request.get_peer_cert()))
145                data = xmlrpclib.dumps((resp,), encoding='UTF-8', 
146                        methodresponse=True)
147            except xmlrpclib.Fault, f:
148                data = xmlrpclib.dumps(f, methodresponse=True)
149                resp = None
150        self.send_xml(data)
151
152
153    def xmlrpc_dispatch(self, method, req, fid):
154        if self.server.xmlrpc_methods.has_key(method):
155            try:
156                return self.server.xmlrpc_methods[method](req, fid)
157            except service_error, e:
158                raise xmlrpclib.Fault(e.code_string(), e.desc)
159        else:
160            raise xmlrpclib.Fault(100, "Unknown method: %s" % method)
161
162class fedd_opts(OptionParser):
163    """Encapsulate option processing in this class, rather than in main"""
164    def __init__(self):
165        OptionParser.__init__(self, usage="%prog [opts] (--help for details)",
166                version="0.1")
167
168        self.set_defaults(host="localhost", port=23235, 
169                transport="soap",debug=0)
170
171        self.add_option("-d", "--debug", action="count", dest="debug", 
172                help="Set debug.  Repeat for more information")
173        self.add_option("-f", "--configfile", action="store",
174                dest="configfile", help="Configuration file (required)")
175        self.add_option("-H", "--host", action="store", type="string",
176                dest="host", help="Hostname to listen on (default %default)")
177        self.add_option("-p", "--port", action="store", type="int",
178                dest="port", help="Port to listen on (default %default)")
179        self.add_option("-x","--transport", action="store", type="choice",
180                choices=("xmlrpc", "soap"),
181                help="Transport for request (xmlrpc|soap) (Default: %default)")
182        self.add_option("--trace", action="store_const", dest="tracefile", 
183                const=sys.stderr, help="Print SOAP exchange to stderr")
184
185opts, args = fedd_opts().parse_args()
186
187if opts.configfile != None: 
188    try:
189        impl = new_feddservice(opts.configfile)
190    except RuntimeError, e:
191        str = getattr(e, 'desc', None) or getattr(e,'message', None) or \
192                "No message"
193        sys.exit("Error configuring fedd: %s" % str)
194else: 
195    sys.exit("--configfile is required")
196
197SOAP_port = (opts.host, opts.port)
198
199if impl.cert_file == None:
200    sys.exit("Must supply certificate file (probably in config)")
201
202ctx = None
203while ctx == None:
204    try:
205        ctx = fedd_ssl_context(impl.cert_file, impl.trusted_certs, 
206                password=impl.cert_pwd)
207    except SSL.SSLError, e:
208        if str(e) != "bad decrypt" or impl.cert_pwd != None:
209            raise
210
211if opts.transport == "soap":
212    s = fedd_server(SOAP_port, fedd_soap_handler, ctx, impl)
213elif opts.transport == "xmlrpc":
214    s = fedd_server(SOAP_port, fedd_xmlrpc_handler, ctx, impl)
215else:
216    s = None
217
218s.serve_forever()
Note: See TracBrowser for help on using the repository browser.