From: <bov...@us...> - 2007-06-08 21:59:36
|
Revision: 1392 http://svn.sourceforge.net/pywebsvcs/?rev=1392&view=rev Author: boverhof Date: 2007-06-08 14:59:37 -0700 (Fri, 08 Jun 2007) Log Message: ----------- M test/test_callhome.py A ZSI/twisted/wsgi.py M ZSI/twisted/__init__.py M ZSI/twisted/WSresource.py -- moving some stuff around, like to use soap handler stuff for all WSGI -- Added a "DeferHandlerChain" for twisted, did some minor refactoring to "render_POST" in order to support it, doesn't affect existing apps. This will add all soap "handler" callbacks into a Deferred, thus returning control to the reactor before processing the soap request/response messages. To use this processing model just set appropriate chain factory protocol from ZSI.twisted.WSresource import * DefaultHandlerChainFactory.protocol = DeferHandlerChain WSAddressHandlerChainFactory.protocol = DeferHandlerChain WSSecurityHandlerChainFactory WSResource.factory = DefaultHandlerChainFactory # or WSResource.factory = WSAddressHandlerChainFactory # or WSResource.factory = Modified Paths: -------------- trunk/zsi/ZSI/twisted/WSresource.py trunk/zsi/ZSI/twisted/__init__.py trunk/zsi/test/test_callhome.py Added Paths: ----------- trunk/zsi/ZSI/twisted/wsgi.py Modified: trunk/zsi/ZSI/twisted/WSresource.py =================================================================== --- trunk/zsi/ZSI/twisted/WSresource.py 2007-06-08 21:31:45 UTC (rev 1391) +++ trunk/zsi/ZSI/twisted/WSresource.py 2007-06-08 21:59:37 UTC (rev 1392) @@ -24,65 +24,13 @@ from ZSI.address import Address from ZSI.ServiceContainer import WSActionException +from interfaces import CheckInputArgs, HandlerChainInterface, CallbackChainInterface,\ + DataHandler, DefaultHandlerChain + # # Stability: Unstable -# - -class HandlerChainInterface(Interface): - - def processRequest(self, input, **kw): - """returns a representation of the request, the - last link in the chain must return a response - pyobj with a typecode attribute. - Parameters: - input -- - Keyword Parameters: - request -- HTTPRequest instance - resource -- Resource instance - """ - def processResponse(self, output, **kw): - """returns a string representing the soap response. - Parameters - output -- - Keyword Parameters: - request -- HTTPRequest instance - resource -- Resource instance - """ - -class CallbackChainInterface(Interface): - - def processRequest(self, input, **kw): - """returns a response pyobj with a typecode - attribute. - Parameters: - input -- - Keyword Parameters: - request -- HTTPRequest instance - resource -- Resource instance - """ - -class DataHandler: - """ - class variables: - readerClass -- factory class to create reader for ParsedSoap instances. - writerClass -- ElementProxy implementation to use for SoapWriter instances. - """ - classProvides(HandlerChainInterface) - readerClass = None - writerClass = None - - @classmethod - def processRequest(cls, input, **kw): - return ParsedSoap(input, readerclass=cls.readerClass) - - @classmethod - def processResponse(cls, output, **kw): - sw = SoapWriter(outputclass=cls.writerClass) - sw.serialize(output) - return sw - - +# class DefaultCallbackHandler: classProvides(CallbackChainInterface) @@ -218,26 +166,12 @@ raise return rsp_pyobj + - -def CheckInputArgs(*interfaces): - """Must provide at least one interface, the last one may be repeated. +class DeferHandlerChain: + """Each handler is """ - l = len(interfaces) - def wrapper(func): - def check_args(self, *args, **kw): - for i in range(len(args)): - if (l > i and interfaces[i].providedBy(args[i])) or interfaces[-1].providedBy(args[i]): - continue - if l > i: raise TypeError, 'arg %s does not implement %s' %(args[i], interfaces[i]) - raise TypeError, 'arg %s does not implement %s' %(args[i], interfaces[-1]) - func(self, *args, **kw) - return check_args - return wrapper - -class DefaultHandlerChain: - @CheckInputArgs(CallbackChainInterface, HandlerChainInterface) def __init__(self, cb, *handlers): self.handlercb = cb @@ -245,31 +179,51 @@ self.debug = len(log.theLogPublisher.observers) > 0 def processRequest(self, arg, **kw): - if self.debug: - log.msg('--->PROCESS REQUEST\n%s' %arg, debug=1) - + from twisted.internet import reactor + from twisted.internet.defer import Deferred + + debug = self.debug + if debug: log.msg('--->DEFER PROCESS REQUEST: %s' %arg, debug=1) + + d = Deferred() for h in self.handlers: - arg = h.processRequest(arg, **kw) + if debug: + log.msg('\t%s handler: %s' %(arg, h), debug=1) + log.msg('\thandler callback: %s' %h.processRequest) + #arg = h.processRequest(arg, **kw) + d.addCallback(h.processRequest, **kw) - return self.handlercb.processRequest(arg, **kw) + #return self.handlercb.processRequest(arg, **kw) + d.addCallback(self.handlercb.processRequest, **kw) + reactor.callLater(.0001, d.callback, arg) + #return d - def processResponse(self, arg, **kw): - if self.debug: - log.msg('===>PROCESS RESPONSE: %s' %str(arg), debug=1) + #def processResponse(self, arg, **kw): + #from twisted.internet.defer import Deferred + + #debug = self.debug + if debug: log.msg('===>DEFER PROCESS RESPONSE: %s' %str(arg), debug=1) - if arg is None: - return - + #if arg is None: + # return d + for h in self.handlers: - arg = h.processResponse(arg, **kw) + if debug: log.msg('\t%s handler: %s' %(arg, h), debug=1) + #arg = h.processResponse(arg, **kw) + d.addCallback(h.processResponse, **kw) - s = str(arg) - if self.debug: - log.msg(s, debug=1) + #s = str(arg) + #if debug: log.msg(s, debug=1) + #return s + + # convert SoapWriter into a string + d.addCallback(str) + return d + + def processResponse(self, arg, **kw): + return arg - return s - class DefaultHandlerChainFactory: protocol = DefaultHandlerChain @@ -285,8 +239,8 @@ def newInstance(cls): return cls.protocol(WSAddressCallbackHandler, DataHandler, WSAddressHandler()) - + class WSResource(twisted.web.resource.Resource, object): """ class variables: @@ -302,7 +256,7 @@ """ twisted.web.resource.Resource.__init__(self) - def _writeResponse(self, request, response, status=200): + def _writeResponse(self, response, request, status=200): """ request -- request message response --- response message @@ -318,38 +272,32 @@ request.setHeader("Content-Length", str(len(response))) request.write(response) request.finish() - return NOT_DONE_YET - def _writeFault(self, request, ex): + def _writeFault(self, fail, request): """ + fail -- failure request -- request message ex -- Exception """ - response = None - response = fault.FaultFromException(ex, False, sys.exc_info()[2]).AsSOAP() - log.err('SOAP FAULT: %s' % response) - return self._writeResponse(request, response, status=500) + response = fault.FaultFromException(fail.value, False, fail.tb).AsSOAP() + self._writeResponse(response, request, status=500) def render_POST(self, request): """Dispatch Method called by twisted render, creates a request/response handler chain. request -- twisted.web.server.Request """ + from twisted.internet.defer import maybeDeferred + chain = self.factory.newInstance() data = request.content.read() - try: - pyobj = chain.processRequest(data, request=request, resource=self) - except Exception, ex: - return self._writeFault(request, ex) + + d = maybeDeferred(chain.processRequest, data, request=request, resource=self) + d.addCallback(chain.processResponse, request=request, resource=self) + d.addCallback(self._writeResponse, request) + d.addErrback(self._writeFault, request) + + return NOT_DONE_YET - try: - soap = chain.processResponse(pyobj, request=request, resource=self) - except Exception, ex: - return self._writeFault(request, ex) - if soap is not None: - return self._writeResponse(request, soap) - request.finish() - return NOT_DONE_YET - Modified: trunk/zsi/ZSI/twisted/__init__.py =================================================================== --- trunk/zsi/ZSI/twisted/__init__.py 2007-06-08 21:31:45 UTC (rev 1391) +++ trunk/zsi/ZSI/twisted/__init__.py 2007-06-08 21:59:37 UTC (rev 1392) @@ -4,4 +4,5 @@ # $Id$ ########################################################################### -__all__=['WSresource', 'WSsecurity'] +__all__=['interfaces', 'client', 'WSresource', 'WSsecurity'] +import interfaces \ No newline at end of file Added: trunk/zsi/ZSI/twisted/wsgi.py =================================================================== --- trunk/zsi/ZSI/twisted/wsgi.py (rev 0) +++ trunk/zsi/ZSI/twisted/wsgi.py 2007-06-08 21:59:37 UTC (rev 1392) @@ -0,0 +1,186 @@ +############################################################################ +# Joshua R. Boverhof, LBNL +# See Copyright for copyright notice! +# $Id: __init__.py 1132 2006-02-17 01:55:41Z boverhof $ +########################################################################### +import os, sys, warnings +from StringIO import StringIO + +# twisted & related imports +from zope.interface import classProvides, implements, Interface +from twisted.python import log, failure +#from twisted.web.error import NoResource +#from twisted.web.server import NOT_DONE_YET +#import twisted.web.http +#import twisted.web.resource + +# ZSI imports +from ZSI import _get_element_nsuri_name, EvaluateException, ParseException +from ZSI import fault, ParsedSoap, SoapWriter + + +# WS-Address related imports +from ZSI.address import Address + + +class WSAddressException(Exception): + """ + """ + +from ZSI.twisted.interfaces import HandlerChainInterface, CallbackChainInterface,\ + DataHandler, CheckInputArgs, DefaultHandlerChain + + + +class DefaultCallbackHandler: + classProvides(CallbackChainInterface) + + @classmethod + def processRequest(cls, ps, **kw): + """invokes callback that should return a (request,response) tuple. + representing the SOAP request and response respectively. + ps -- ParsedSoap instance representing HTTP Body. + request -- twisted.web.server.Request + """ + #env = kw['env'] + #start_response = kw['start_response'] + resource = kw['resource'] + #request = kw['request'] + method = getattr(resource, 'soap_%s' % + _get_element_nsuri_name(ps.body_root)[-1]) + + try: + req_pyobj,rsp_pyobj = method(ps) + except TypeError, ex: + log.err( + 'ERROR: service %s is broken, method MUST return request, response'\ + % cls.__name__ + ) + raise + except Exception, ex: + log.err('failure when calling bound method') + raise + + return rsp_pyobj + + +class DefaultHandlerChainFactory: + protocol = DefaultHandlerChain + + @classmethod + def newInstance(cls): + return cls.protocol(DefaultCallbackHandler, DataHandler) + + + +class WSGIApplication(dict): + encoding = "UTF-8" + + def __call__(self, env, start_response): + """do dispatching, else process + """ + script = env['SCRIPT_NAME'] # consumed + ipath = os.path.split(env['PATH_INFO'])[1:] + for i in range(1, len(ipath)+1): + path = os.path.join(*ipath[:i]) + print "PATH: ", path + application = self.get(path) + if application is not None: + env['SCRIPT_NAME'] = script + path + env['PATH_INFO'] = '' + print "SCRIPT: ", env['SCRIPT_NAME'] + return application(env, start_response) + + return self._request_cb(env, start_response) + + def _request_cb(self, env, start_response): + """callback method, override + """ + start_response("404 ERROR", [('Content-Type','text/plain')]) + return ['Move along people, there is nothing to see to hear'] + + +class SOAPApplication(WSGIApplication): + """ + """ + factory = DefaultHandlerChainFactory + soapAction = {} + root = {} + + def __init__(self, **kw): + dict.__init__(self, **kw) + self.delegate = None + + def _request_cb(self, env, start_response): + """process request, + """ + if env['REQUEST_METHOD'] == 'GET': + return self._handle_GET(env, start_response) + + if env['REQUEST_METHOD'] == 'POST': + return self._handle_POST(env, start_response) + + start_response("500 ERROR", [('Content-Type','text/plain')]) + s = StringIO() + h = env.items(); h.sort() + for k,v in h: + print >>s, k,'=',`v` + return [s.getvalue()] + + def _handle_GET(self, env, start_response): + if env['QUERY_STRING'].lower() == 'wsdl': + start_response("200 OK", [('Content-Type','text/plain')]) + return ['NO WSDL YET'] + + start_response("404 ERROR", [('Content-Type','text/plain')]) + return ['NO RESOURCE FOR GET'] + + def _handle_POST(self, env, start_response): + """Dispatch Method called by twisted render, creates a + request/response handler chain. + request -- twisted.web.server.Request + """ + input = env['wsgi.input'] + data = input.read( int(env['CONTENT_LENGTH']) ) + mimeType = "text/xml" + if self.encoding is not None: + mimeType = 'text/xml; charset="%s"' % self.encoding + + request = None + resource = self.delegate or self + chain = self.factory.newInstance() + try: + pyobj = chain.processRequest(data, request=request, resource=resource) + except Exception, ex: + start_response("500 ERROR", [('Content-Type',mimeType)]) + return [fault.FaultFromException(ex, False, sys.exc_info()[2]).AsSOAP()] + + try: + soap = chain.processResponse(pyobj, request=request, resource=resource) + except Exception, ex: + start_response("500 ERROR", [('Content-Type',mimeType)]) + return [fault.FaultFromException(ex, False, sys.exc_info()[2]).AsSOAP()] + + start_response("200 OK", [('Content-Type',mimeType)]) + return [soap] + + +def test(app, port=8080, host="localhost"): + """ + """ + import sys + from twisted.internet import reactor + from twisted.python import log + from twisted.web2.channel import HTTPFactory + from twisted.web2.server import Site + from twisted.web2.wsgi import WSGIResource + + log.startLogging(sys.stdout) + reactor.listenTCP(port, + HTTPFactory( Site(WSGIResource(app)) ), + interface=host, + ) + reactor.run() + + + Modified: trunk/zsi/test/test_callhome.py =================================================================== --- trunk/zsi/test/test_callhome.py 2007-06-08 21:31:45 UTC (rev 1391) +++ trunk/zsi/test/test_callhome.py 2007-06-08 21:59:37 UTC (rev 1392) @@ -4,7 +4,7 @@ from ZSI.wstools.logging import gridLog os.environ['GRIDLOG_ON'] = '1' -os.environ['GRIDLOG_DEST'] = "gridlog-udp://portnoy.lbl.gov:15100" +os.environ['GRIDLOG_DEST'] = "gridlog-udp://netlogger.lbl.gov:15100" class TestCase(unittest.TestCase): This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |