00001
00002
00003 '''Simple CGI dispatching.
00004 '''
00005
00006 import types, os, sys
00007 from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
00008 from ZSI import *
00009 from ZSI import _child_elements, _copyright, _seqtypes, _find_arraytype, _find_type, resolvers
00010 from ZSI.auth import _auth_tc, AUTH, ClientBinding
00011
00012
00013
00014
00015 _client_binding = None
00016
00017 def GetClientBinding():
00018 '''Return the client binding object.
00019 '''
00020 return _client_binding
00021
00022 gettypecode = lambda mod,e: getattr(mod, str(e.localName)).typecode
00023 def _Dispatch(ps, modules, SendResponse, SendFault, nsdict={}, typesmodule=None,
00024 gettypecode=gettypecode, rpc=False, docstyle=False, **kw):
00025 '''Find a handler for the SOAP request in ps; search modules.
00026 Call SendResponse or SendFault to send the reply back, appropriately.
00027
00028 Behaviors:
00029 default -- Call "handler" method with pyobj representation of body root, and return
00030 a self-describing request (w/typecode). Parsing done via a typecode from
00031 typesmodule, or Any.
00032
00033 docstyle -- Call "handler" method with ParsedSoap instance and parse result with an
00034 XML typecode (DOM). Behavior, wrap result in a body_root "Response" appended message.
00035
00036 rpc -- Specify RPC wrapper of result. Behavior, ignore body root (RPC Wrapper)
00037 of request, parse all "parts" of message via individual typecodes. Expect
00038 the handler to return the parts of the message, whether it is a dict, single instance,
00039 or a list try to serialize it as a Struct but if this is not possible put it in an Array.
00040 Parsing done via a typecode from typesmodule, or Any.
00041
00042 '''
00043 global _client_binding
00044 try:
00045 what = str(ps.body_root.localName)
00046
00047
00048 if modules is None:
00049 modules = ( sys.modules['__main__'], )
00050
00051 handlers = [ getattr(m, what) for m in modules if hasattr(m, what) ]
00052 if len(handlers) == 0:
00053 raise TypeError("Unknown method " + what)
00054
00055
00056 handlers = [ h for h in handlers if callable(h) ]
00057 if len(handlers) == 0:
00058 raise TypeError("Unimplemented method " + what)
00059 if len(handlers) > 1:
00060 raise TypeError("Multiple implementations found: " + `handlers`)
00061 handler = handlers[0]
00062
00063 _client_binding = ClientBinding(ps)
00064 if docstyle:
00065 result = handler(ps.body_root)
00066 tc = TC.XML(aslist=1, pname=what+'Response')
00067 elif not rpc:
00068 try:
00069 tc = gettypecode(typesmodule, ps.body_root)
00070 except Exception:
00071 tc = TC.Any()
00072
00073 try:
00074 arg = tc.parse(ps.body_root, ps)
00075 except EvaluateException, ex:
00076 SendFault(FaultFromZSIException(ex), **kw)
00077 return
00078
00079 try:
00080 result = handler(arg)
00081 except Exception,ex:
00082 SendFault(FaultFromZSIException(ex), **kw)
00083 return
00084
00085 try:
00086 tc = result.typecode
00087 except AttributeError,ex:
00088 SendFault(FaultFromZSIException(ex), **kw)
00089 return
00090
00091 elif typesmodule is not None:
00092 kwargs = {}
00093 for e in _child_elements(ps.body_root):
00094 try:
00095 tc = gettypecode(typesmodule, e)
00096 except Exception:
00097 tc = TC.Any()
00098
00099 try:
00100 kwargs[str(e.localName)] = tc.parse(e, ps)
00101 except EvaluateException, ex:
00102 SendFault(FaultFromZSIException(ex), **kw)
00103 return
00104
00105 result = handler(**kwargs)
00106 aslist = False
00107
00108 if type(result) in _seqtypes:
00109 for o in result:
00110 aslist = hasattr(result, 'typecode')
00111 if aslist: break
00112 elif type(result) is not dict:
00113 aslist = not hasattr(result, 'typecode')
00114 result = (result,)
00115
00116 tc = TC.Any(pname=what+'Response', aslist=aslist)
00117 else:
00118
00119
00120 tp = _find_type(ps.body_root)
00121 isarray = ((type(tp) in (tuple,list) and tp[1] == 'Array') or _find_arraytype(ps.body_root))
00122 data = _child_elements(ps.body_root)
00123 tc = TC.Any()
00124 if isarray and len(data) == 0:
00125 result = handler()
00126 elif isarray:
00127 try: arg = [ tc.parse(e, ps) for e in data ]
00128 except EvaluateException, e:
00129
00130 SendFault(RuntimeError("THIS IS AN ARRAY: %s" %isarray))
00131 return
00132
00133 result = handler(*arg)
00134 else:
00135 try: kwarg = dict([ (str(e.localName),tc.parse(e, ps)) for e in data ])
00136 except EvaluateException, e:
00137 SendFault(FaultFromZSIException(e), **kw)
00138 return
00139
00140 result = handler(**kwarg)
00141
00142
00143
00144 tc = TC.Any(pname=what+'Response')
00145
00146 sw = SoapWriter(nsdict=nsdict)
00147 sw.serialize(result, tc)
00148 return SendResponse(str(sw), **kw)
00149 except Fault, e:
00150 return SendFault(e, **kw)
00151 except Exception, e:
00152
00153 return SendFault(FaultFromException(e, 0, sys.exc_info()[2]), **kw)
00154
00155
00156 def _ModPythonSendXML(text, code=200, **kw):
00157 req = kw['request']
00158 req.content_type = 'text/xml'
00159 req.content_length = len(text)
00160 req.send_http_header()
00161 req.write(text)
00162
00163
00164 def _ModPythonSendFault(f, **kw):
00165 _ModPythonSendXML(f.AsSOAP(), 500, **kw)
00166
00167 def _JonPySendFault(f, **kw):
00168 _JonPySendXML(f.AsSOAP(), 500, **kw)
00169
00170 def _JonPySendXML(text, code=200, **kw):
00171 req = kw['request']
00172 req.set_header("Content-Type", 'text/xml; charset="%s"' %UNICODE_ENCODING)
00173 req.set_header("Content-Length", str(len(text)))
00174 req.write(text)
00175
00176 def _CGISendXML(text, code=200, **kw):
00177 print 'Status: %d' % code
00178 print 'Content-Type: text/xml; charset="%s"' %UNICODE_ENCODING
00179 print 'Content-Length: %d' % len(text)
00180 print ''
00181 print text
00182
00183 def _CGISendFault(f, **kw):
00184 _CGISendXML(f.AsSOAP(), 500, **kw)
00185
00186
00187 class SOAPRequestHandler(BaseHTTPRequestHandler):
00188 '''SOAP handler.
00189 '''
00190 server_version = 'ZSI/1.1 ' + BaseHTTPRequestHandler.server_version
00191
00192 def send_xml(self, text, code=200):
00193 '''Send some XML.
00194 '''
00195 self.send_response(code)
00196
00197 if text:
00198 self.send_header('Content-type', 'text/xml; charset="%s"' %UNICODE_ENCODING)
00199 self.send_header('Content-Length', str(len(text)))
00200
00201 self.end_headers()
00202
00203 if text:
00204 self.wfile.write(text)
00205
00206 self.wfile.flush()
00207
00208 def send_fault(self, f, code=500):
00209 '''Send a fault.
00210 '''
00211 self.send_xml(f.AsSOAP(), code)
00212
00213 def do_POST(self):
00214 '''The POST command.
00215 '''
00216 try:
00217 ct = self.headers['content-type']
00218 if ct.startswith('multipart/'):
00219 cid = resolvers.MIMEResolver(ct, self.rfile)
00220 xml = cid.GetSOAPPart()
00221 ps = ParsedSoap(xml, resolver=cid.Resolve)
00222 else:
00223 length = int(self.headers['content-length'])
00224 ps = ParsedSoap(self.rfile.read(length))
00225 except ParseException, e:
00226 self.send_fault(FaultFromZSIException(e))
00227 return
00228 except Exception, e:
00229
00230 self.send_fault(FaultFromException(e, 1, sys.exc_info()[2]))
00231 return
00232
00233 _Dispatch(ps, self.server.modules, self.send_xml, self.send_fault,
00234 docstyle=self.server.docstyle, nsdict=self.server.nsdict,
00235 typesmodule=self.server.typesmodule, rpc=self.server.rpc)
00236
00237 def AsServer(port=80, modules=None, docstyle=False, nsdict={}, typesmodule=None,
00238 rpc=False, addr=''):
00239 address = (addr, port)
00240 httpd = HTTPServer(address, SOAPRequestHandler)
00241 httpd.modules = modules
00242 httpd.docstyle = docstyle
00243 httpd.nsdict = nsdict
00244 httpd.typesmodule = typesmodule
00245 httpd.rpc = rpc
00246 httpd.serve_forever()
00247
00248 def AsCGI(nsdict={}, typesmodule=None, rpc=False, modules=None):
00249 '''Dispatch within a CGI script.
00250 '''
00251 if os.environ.get('REQUEST_METHOD') != 'POST':
00252 _CGISendFault(Fault(Fault.Client, 'Must use POST'))
00253 return
00254 ct = os.environ['CONTENT_TYPE']
00255 try:
00256 if ct.startswith('multipart/'):
00257 cid = resolvers.MIMEResolver(ct, sys.stdin)
00258 xml = cid.GetSOAPPart()
00259 ps = ParsedSoap(xml, resolver=cid.Resolve)
00260 else:
00261 length = int(os.environ['CONTENT_LENGTH'])
00262 ps = ParsedSoap(sys.stdin.read(length))
00263 except ParseException, e:
00264 _CGISendFault(FaultFromZSIException(e))
00265 return
00266 _Dispatch(ps, modules, _CGISendXML, _CGISendFault, nsdict=nsdict,
00267 typesmodule=typesmodule, rpc=rpc)
00268
00269 def AsHandler(request=None, modules=None, **kw):
00270 '''Dispatch from within ModPython.'''
00271 ps = ParsedSoap(request)
00272 kw['request'] = request
00273 _Dispatch(ps, modules, _ModPythonSendXML, _ModPythonSendFault, **kw)
00274
00275 def AsJonPy(request=None, modules=None, **kw):
00276 '''Dispatch within a jonpy CGI/FastCGI script.
00277 '''
00278
00279 kw['request'] = request
00280 if request.environ.get('REQUEST_METHOD') != 'POST':
00281 _JonPySendFault(Fault(Fault.Client, 'Must use POST'), **kw)
00282 return
00283 ct = request.environ['CONTENT_TYPE']
00284 try:
00285 if ct.startswith('multipart/'):
00286 cid = resolvers.MIMEResolver(ct, request.stdin)
00287 xml = cid.GetSOAPPart()
00288 ps = ParsedSoap(xml, resolver=cid.Resolve)
00289 else:
00290 length = int(request.environ['CONTENT_LENGTH'])
00291 ps = ParsedSoap(request.stdin.read(length))
00292 except ParseException, e:
00293 _JonPySendFault(FaultFromZSIException(e), **kw)
00294 return
00295 _Dispatch(ps, modules, _JonPySendXML, _JonPySendFault, **kw)
00296
00297
00298 if __name__ == '__main__': print _copyright