|
From: <wa...@us...> - 2003-03-26 16:29:48
|
Update of /cvsroot/pywebsvcs/SOAPpy/SOAPpy In directory sc8-pr-cvs1:/tmp/cvs-serv8838 Modified Files: SOAP.py Log Message: - Split class SOAPServer into SOAPServerBase and two sublcasses, SOAPServer and SOAPUnixSocketServer. SOAPServer has the same functionality as before, while SOAPUnixSocketServer connects over a Unix domain socket instead of to a (public) TCP/IP port. Index: SOAP.py =================================================================== RCS file: /cvsroot/pywebsvcs/SOAPpy/SOAPpy/SOAP.py,v retrieving revision 1.22 retrieving revision 1.23 diff -u -d -r1.22 -r1.23 --- SOAP.py 26 Mar 2003 04:51:00 -0000 1.22 +++ SOAP.py 26 Mar 2003 16:29:42 -0000 1.23 @@ -95,6 +95,7 @@ from XMLname import toXMLname, fromXMLname from types import * import ieee754 +import BaseHTTPServer try: from M2Crypto import SSL except: pass @@ -3746,284 +3747,322 @@ ################################################################################ # SOAP Server ################################################################################ -class SOAPServer(SocketServer.TCPServer): - import BaseHTTPServer +class SOAPServerBase: - class SOAPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): - def version_string(self): - return '<a href="http://pywebsvcs.sf.net">' + \ - 'SOAP.py ' + __version__ + '</a> (Python ' + \ - sys.version.split()[0] + ')' + def get_request(self): + sock, addr = get_request(self) - def date_time_string(self): - self.__last_date_time_string = \ - SOAPServer.BaseHTTPServer.BaseHTTPRequestHandler.\ - date_time_string(self) + if self.ssl_context: + sock = SSL.Connection(self.ssl_context, sock) + sock._setup_ssl(addr) + if sock.accept_ssl() != 1: + raise socket.error, "Couldn't accept SSL connection" - return self.__last_date_time_string + return sock, addr - def do_POST(self): - try: - if self.server.config.dumpHeadersIn: - s = 'Incoming HTTP headers' - debugHeader(s) - print self.raw_requestline.strip() - print "\n".join(map (lambda x: x.strip(), - self.headers.headers)) - debugFooter(s) + def registerObject(self, object, namespace = ''): + if namespace == '': namespace = self.namespace + self.objmap[namespace] = object - data = self.rfile.read(int(self.headers["content-length"])) + def registerFunction(self, function, namespace = '', funcName = None): + if not funcName : funcName = function.__name__ + if namespace == '': namespace = self.namespace + if self.funcmap.has_key(namespace): + self.funcmap[namespace][funcName] = function + else: + self.funcmap[namespace] = {funcName : function} - if self.server.config.dumpSOAPIn: - s = 'Incoming SOAP' - debugHeader(s) - print data, - if data[-1] != '\n': - print - debugFooter(s) + def registerKWObject(self, object, namespace = ''): + if namespace == '': namespace = self.namespace + for i in dir(object.__class__): + if i[0] != "_" and callable(getattr(object, i)): + self.registerKWFunction(getattr(object,i), namespace) - (r, header, body, attrs) = \ - parseSOAPRPC(data, header = 1, body = 1, attrs = 1) + # convenience - wraps your func for you. + def registerKWFunction(self, function, namespace = '', funcName = None): + self.registerFunction(MethodSig(function,keywords=1), namespace, + funcName) - method = r._name - args = r._aslist - kw = r._asdict - - # Handle mixed named and unnamed arguments by assuming - # that all arguments with names of the form "_[0-9]+" - # are unnamed and should be passed in numeric order, - # other arguments are named and should be passed using - # this name. This is a custom exension to the SOAP - # protocol, and is thus disabled by default. To - # enable, set Config.specialArgs to a true value. - if Config.specialArgs: - - ordered_args = {} - named_args = {} - - for (k,v) in kw.items(): - m = re.match("_([0-9]+)", k) - if m is None: - named_args[str(k)] = v - else: - ordered_args[int(m.group(1))] = v - - keylist = ordered_args.keys() - keylist.sort() - tmp = map( lambda x: ordered_args[x], keylist) +class SOAPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): + def version_string(self): + return '<a href="http://pywebsvcs.sf.net">' + \ + 'SOAP.py ' + __version__ + '</a> (Python ' + \ + sys.version.split()[0] + ')' - ordered_args = tmp + def date_time_string(self): + self.__last_date_time_string = \ + BaseHTTPServer.BaseHTTPRequestHandler.\ + date_time_string(self) - #print '<-> Argument Matching Yielded:' - #print '<-> Ordered Arguments:' + str(ordered_args) - #print '<-> Named Arguments :' + str(named_args) - + return self.__last_date_time_string - ns = r._ns - resp = "" - # For fault messages - if ns: - nsmethod = "%s:%s" % (ns, method) - else: - nsmethod = method + def do_POST(self): + try: + if self.server.config.dumpHeadersIn: + s = 'Incoming HTTP headers' + debugHeader(s) + print self.raw_requestline.strip() + print "\n".join(map (lambda x: x.strip(), + self.headers.headers)) + debugFooter(s) - try: - # First look for registered functions - if self.server.funcmap.has_key(ns) and \ - self.server.funcmap[ns].has_key(method): - f = self.server.funcmap[ns][method] - else: # Now look at registered objects - # Check for nested attributes. This works even if - # there are none, because the split will return - # [method] - f = self.server.objmap[ns] - l = method.split(".") - for i in l: - f = getattr(f, i) - except: - resp = buildSOAP(faultType("%s:Client" % NS.ENV_T, - "No method %s found" % nsmethod, - "%s %s" % tuple(sys.exc_info()[0:2])), - encoding = self.server.encoding, - config = self.server.config) - status = 500 - else: - try: - if header: - x = HeaderHandler(header, attrs) + data = self.rfile.read(int(self.headers["content-length"])) - # If it's wrapped, some special action may be needed - - if isinstance(f, MethodSig): - c = None - - if f.context: # Build context object - c = SOAPContext(header, body, attrs, data, - self.connection, self.headers, - self.headers["soapaction"]) + if self.server.config.dumpSOAPIn: + s = 'Incoming SOAP' + debugHeader(s) + print data, + if data[-1] != '\n': + print + debugFooter(s) - if Config.specialArgs: - if c: - named_args["_SOAPContext"] = c - fr = apply(f, ordered_args, named_args) - elif f.keywords: - # This is lame, but have to de-unicode - # keywords - - strkw = {} - - for (k, v) in kw.items(): - strkw[str(k)] = v - if c: - strkw["_SOAPContext"] = c - fr = apply(f, (), strkw) - elif c: - fr = apply(f, args, {'_SOAPContext':c}) - else: - fr = apply(f, args, {}) + (r, header, body, attrs) = \ + parseSOAPRPC(data, header = 1, body = 1, attrs = 1) - else: - if Config.specialArgs: - fr = apply(f, ordered_args, named_args) - else: - fr = apply(f, args, {}) + method = r._name + args = r._aslist + kw = r._asdict + + # Handle mixed named and unnamed arguments by assuming + # that all arguments with names of the form "_[0-9]+" + # are unnamed and should be passed in numeric order, + # other arguments are named and should be passed using + # this name. This is a custom exension to the SOAP + # protocol, and is thus disabled by default. To + # enable, set Config.specialArgs to a true value. + if Config.specialArgs: + + ordered_args = {} + named_args = {} + + for (k,v) in kw.items(): + m = re.match("_([0-9]+)", k) + if m is None: + named_args[str(k)] = v + else: + ordered_args[int(m.group(1))] = v - if type(fr) == type(self) and \ - isinstance(fr, voidType): - resp = buildSOAP(kw = {'%sResponse' % method: fr}, - encoding = self.server.encoding, - config = self.server.config) - else: - resp = buildSOAP(kw = - {'%sResponse' % method: {'Result': fr}}, - encoding = self.server.encoding, - config = self.server.config) - except Exception, e: - import traceback - info = sys.exc_info() + keylist = ordered_args.keys() + keylist.sort() + tmp = map( lambda x: ordered_args[x], keylist) - if self.server.config.dumpFaultInfo: - s = 'Method %s exception' % nsmethod - debugHeader(s) - traceback.print_exception(info[0], info[1], - info[2]) - debugFooter(s) + ordered_args = tmp - if isinstance(e, faultType): - f = e - else: - f = faultType("%s:Server" % NS.ENV_T, - "Method %s failed." % nsmethod) + #print '<-> Argument Matching Yielded:' + #print '<-> Ordered Arguments:' + str(ordered_args) + #print '<-> Named Arguments :' + str(named_args) + - if self.server.config.returnFaultInfo: - f._setDetail("".join(traceback.format_exception( - info[0], info[1], info[2]))) - elif not hasattr(f, 'detail'): - f._setDetail("%s %s" % (info[0], info[1])) + ns = r._ns + resp = "" + # For fault messages + if ns: + nsmethod = "%s:%s" % (ns, method) + else: + nsmethod = method - resp = buildSOAP(f, encoding = self.server.encoding, - config = self.server.config) - status = 500 - else: - status = 200 - except faultType, e: - import traceback - info = sys.exc_info() + try: + # First look for registered functions + if self.server.funcmap.has_key(ns) and \ + self.server.funcmap[ns].has_key(method): + f = self.server.funcmap[ns][method] + else: # Now look at registered objects + # Check for nested attributes. This works even if + # there are none, because the split will return + # [method] + f = self.server.objmap[ns] + l = method.split(".") + for i in l: + f = getattr(f, i) + except: + resp = buildSOAP(faultType("%s:Client" % NS.ENV_T, + "No method %s found" % nsmethod, + "%s %s" % tuple(sys.exc_info()[0:2])), + encoding = self.server.encoding, + config = self.server.config) + status = 500 + else: + try: + if header: + x = HeaderHandler(header, attrs) - if self.server.config.dumpFaultInfo: - s = 'Received fault exception' - debugHeader(s) - traceback.print_exception(info[0], info[1], - info[2]) - debugFooter(s) + # If it's wrapped, some special action may be needed + + if isinstance(f, MethodSig): + c = None + + if f.context: # Build context object + c = SOAPContext(header, body, attrs, data, + self.connection, self.headers, + self.headers["soapaction"]) - if self.server.config.returnFaultInfo: - e._setDetail("".join(traceback.format_exception( - info[0], info[1], info[2]))) - elif not hasattr(e, 'detail'): - e._setDetail("%s %s" % (info[0], info[1])) + if Config.specialArgs: + if c: + named_args["_SOAPContext"] = c + fr = apply(f, ordered_args, named_args) + elif f.keywords: + # This is lame, but have to de-unicode + # keywords + + strkw = {} + + for (k, v) in kw.items(): + strkw[str(k)] = v + if c: + strkw["_SOAPContext"] = c + fr = apply(f, (), strkw) + elif c: + fr = apply(f, args, {'_SOAPContext':c}) + else: + fr = apply(f, args, {}) - resp = buildSOAP(e, encoding = self.server.encoding, - config = self.server.config) - status = 500 - except: - # internal error, report as HTTP server error - if self.server.config.dumpFaultInfo: - import traceback - s = 'Internal exception' - debugHeader(s) - traceback.print_exc () - debugFooter(s) - self.send_response(500) - self.end_headers() + else: + if Config.specialArgs: + fr = apply(f, ordered_args, named_args) + else: + fr = apply(f, args, {}) - if self.server.config.dumpHeadersOut and \ - self.request_version != 'HTTP/0.9': - s = 'Outgoing HTTP headers' - debugHeader(s) - if self.responses.has_key(status): - s = ' ' + self.responses[status][0] + + if type(fr) == type(self) and \ + isinstance(fr, voidType): + resp = buildSOAP(kw = {'%sResponse' % method: fr}, + encoding = self.server.encoding, + config = self.server.config) else: - s = '' - print "%s %d%s" % (self.protocol_version, 500, s) - print "Server:", self.version_string() - print "Date:", self.__last_date_time_string - debugFooter(s) - else: - # got a valid SOAP response - self.send_response(status) + resp = buildSOAP(kw = + {'%sResponse' % method: {'Result': fr}}, + encoding = self.server.encoding, + config = self.server.config) + except Exception, e: + import traceback + info = sys.exc_info() - t = 'text/xml'; - if self.server.encoding != None: - t += '; charset="%s"' % self.server.encoding - self.send_header("Content-type", t) - self.send_header("Content-length", str(len(resp))) - self.end_headers() + if self.server.config.dumpFaultInfo: + s = 'Method %s exception' % nsmethod + debugHeader(s) + traceback.print_exception(info[0], info[1], + info[2]) + debugFooter(s) - if self.server.config.dumpHeadersOut and \ - self.request_version != 'HTTP/0.9': - s = 'Outgoing HTTP headers' - debugHeader(s) - if self.responses.has_key(status): - s = ' ' + self.responses[status][0] + if isinstance(e, faultType): + f = e else: - s = '' - print "%s %d%s" % (self.protocol_version, status, s) - print "Server:", self.version_string() - print "Date:", self.__last_date_time_string - print "Content-type:", t - print "Content-length:", len(resp) - debugFooter(s) + f = faultType("%s:Server" % NS.ENV_T, + "Method %s failed." % nsmethod) - if self.server.config.dumpSOAPOut: - s = 'Outgoing SOAP' - debugHeader(s) - print resp, - if resp[-1] != '\n': - print - debugFooter(s) + if self.server.config.returnFaultInfo: + f._setDetail("".join(traceback.format_exception( + info[0], info[1], info[2]))) + elif not hasattr(f, 'detail'): + f._setDetail("%s %s" % (info[0], info[1])) - self.wfile.write(resp) - self.wfile.flush() + resp = buildSOAP(f, encoding = self.server.encoding, + config = self.server.config) + status = 500 + else: + status = 200 + except faultType, e: + import traceback + info = sys.exc_info() - # We should be able to shut down both a regular and an SSL - # connection, but under Python 2.1, calling shutdown on an - # SSL connections drops the output, so this work-around. - # This should be investigated more someday. + if self.server.config.dumpFaultInfo: + s = 'Received fault exception' + debugHeader(s) + traceback.print_exception(info[0], info[1], + info[2]) + debugFooter(s) - if self.server.config.SSLserver and \ - isinstance(self.connection, SSL.Connection): - self.connection.set_shutdown(SSL.SSL_SENT_SHUTDOWN | - SSL.SSL_RECEIVED_SHUTDOWN) + if self.server.config.returnFaultInfo: + e._setDetail("".join(traceback.format_exception( + info[0], info[1], info[2]))) + elif not hasattr(e, 'detail'): + e._setDetail("%s %s" % (info[0], info[1])) + + resp = buildSOAP(e, encoding = self.server.encoding, + config = self.server.config) + status = 500 + except: + # internal error, report as HTTP server error + if self.server.config.dumpFaultInfo: + import traceback + s = 'Internal exception' + debugHeader(s) + traceback.print_exc () + debugFooter(s) + self.send_response(500) + self.end_headers() + + if self.server.config.dumpHeadersOut and \ + self.request_version != 'HTTP/0.9': + s = 'Outgoing HTTP headers' + debugHeader(s) + if self.responses.has_key(status): + s = ' ' + self.responses[status][0] else: - self.connection.shutdown(1) + s = '' + print "%s %d%s" % (self.protocol_version, 500, s) + print "Server:", self.version_string() + print "Date:", self.__last_date_time_string + debugFooter(s) + else: + # got a valid SOAP response + self.send_response(status) + + t = 'text/xml'; + if self.server.encoding != None: + t += '; charset="%s"' % self.server.encoding + self.send_header("Content-type", t) + self.send_header("Content-length", str(len(resp))) + self.end_headers() + + if self.server.config.dumpHeadersOut and \ + self.request_version != 'HTTP/0.9': + s = 'Outgoing HTTP headers' + debugHeader(s) + if self.responses.has_key(status): + s = ' ' + self.responses[status][0] + else: + s = '' + print "%s %d%s" % (self.protocol_version, status, s) + print "Server:", self.version_string() + print "Date:", self.__last_date_time_string + print "Content-type:", t + print "Content-length:", len(resp) + debugFooter(s) + + if self.server.config.dumpSOAPOut: + s = 'Outgoing SOAP' + debugHeader(s) + print resp, + if resp[-1] != '\n': + print + debugFooter(s) + + self.wfile.write(resp) + self.wfile.flush() + + # We should be able to shut down both a regular and an SSL + # connection, but under Python 2.1, calling shutdown on an + # SSL connections drops the output, so this work-around. + # This should be investigated more someday. + + if self.server.config.SSLserver and \ + isinstance(self.connection, SSL.Connection): + self.connection.set_shutdown(SSL.SSL_SENT_SHUTDOWN | + SSL.SSL_RECEIVED_SHUTDOWN) + else: + self.connection.shutdown(1) + + def log_message(self, format, *args): + if self.server.log: + BaseHTTPServer.BaseHTTPRequestHandler.\ + log_message (self, format, *args) + - def log_message(self, format, *args): - if self.server.log: - SOAPServer.BaseHTTPServer.BaseHTTPRequestHandler.\ - log_message (self, format, *args) + +class SOAPServer(SocketServer.TCPServer, SOAPServerBase): def __init__(self, addr = ('localhost', 8000), RequestHandler = SOAPRequestHandler, log = 1, encoding = 'UTF-8', @@ -4049,36 +4088,28 @@ SocketServer.TCPServer.__init__(self, addr, RequestHandler) - def get_request(self): - sock, addr = SocketServer.TCPServer.get_request(self) +class SOAPUnixSocketServer(SocketServer.UnixStreamServer, SOAPServerBase): - if self.ssl_context: - sock = SSL.Connection(self.ssl_context, sock) - sock._setup_ssl(addr) - if sock.accept_ssl() != 1: - raise socket.error, "Couldn't accept SSL connection" + def __init__(self, addr = 8000, + RequestHandler = SOAPRequestHandler, log = 1, encoding = 'UTF-8', + config = Config, namespace = None, ssl_context = None): - return sock, addr + # Test the encoding, raising an exception if it's not known + if encoding != None: + ''.encode(encoding) - def registerObject(self, object, namespace = ''): - if namespace == '': namespace = self.namespace - self.objmap[namespace] = object + if ssl_context != None and not config.SSLserver: + raise AttributeError, \ + "SSL server not supported by this Python installation" - def registerFunction(self, function, namespace = '', funcName = None): - if not funcName : funcName = function.__name__ - if namespace == '': namespace = self.namespace - if self.funcmap.has_key(namespace): - self.funcmap[namespace][funcName] = function - else: - self.funcmap[namespace] = {funcName : function} + self.namespace = namespace + self.objmap = {} + self.funcmap = {} + self.ssl_context = ssl_context + self.encoding = encoding + self.config = config + self.log = log - def registerKWObject(self, object, namespace = ''): - if namespace == '': namespace = self.namespace - for i in dir(object.__class__): - if i[0] != "_" and callable(getattr(object, i)): - self.registerKWFunction(getattr(object,i), namespace) + self.allow_reuse_address= 1 - # convenience - wraps your func for you. - def registerKWFunction(self, function, namespace = '', funcName = None): - self.registerFunction(MethodSig(function,keywords=1), namespace, - funcName) + SocketServer.UnixStreamServer.__init__(self, str(addr), RequestHandler) |