# ex:ts=4 # # Coolwater XMLRPC -- SourceLight-Medusa Powered XMLRPC Framework # # $LinuxKorea: xmlrpc.py,v 2.14 2001/10/29 07:34:46 perky Exp $ # # Copyright 2001 Hye-Shik Chang. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. Neither the name of author nor the names of its contributors # may be used to endorse or promote products derived from this software # without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. # # code by Hye-Shik Chang # import socket import string import asyncore import cw_medusa try: from cStringIO import StringIO except: from StringIO import StringIO # SourceLight's py-xmlrpc *FAST* Implementation import _xmlrpc STATE_HEADER = 0 STATE_CONTENT = 1 HEADER_CLEN = 'content-length: ' HTTP_TERMINATOR = '\r\n' class xmlrpc_root: pass class xmlrpc_fancyroot(xmlrpc_root): def _getmethods(self, clsinst=None): if not clsinst: clsinst = self r = [] try: oclass = clsinst.__class__.__dict__.keys() except AttributeError: oclass = [] for m in oclass + dir(clsinst): if m[0] != '_' and type(getattr(clsinst, m)) in (type(self._getmethods), type(string.join)): r.append(m) return r def _getclasses(self, clsinst=None): if not clsinst: clsinst = self r = [] try: oclass = clsinst.__class__.__dict__.keys() except AttributeError: oclass = [] for m in oclass + dir(clsinst): if m[0] != '_' and type(getattr(clsinst, m)) in (type(self), type(string)): r.append(m) return r def _getallmethods(self, clsinst=None): if not clsinst: clsinst = self r = self._getmethods(clsinst) for cl in self._getclasses(clsinst): try: for m in self._getallmethods(getattr(clsinst, cl)): r.append(cl+'.'+m) except AttributeError: pass return r def _getdoc(self, name='', clsinst=None): if not clsinst: clsinst = self m = string.split(name, '.', 1) if len(m) > 1: return self._getdoc(m[1], getattr(clsinst, m[0])) elif m[0]: return self._getdoc('', getattr(clsinst, m[0])) else: try: return getattr(clsinst, '__doc__') except: return None class xmlrpc_channel(cw_medusa.async_chat): def __init__(self, root, conn, addr, map=None): cw_medusa.async_chat.__init__(self, conn, map) self.root = root self.addr = addr self.buffer = StringIO() self.reqbuffer = StringIO() # XXX: Hmm. something leaking efficiency. self.setmode(STATE_HEADER) def log(*ignore): pass def collect_incoming_data(self, data): self.buffer.write(data) def setmode(self, mode): if mode == STATE_HEADER: self.state = STATE_HEADER self.set_terminator(HTTP_TERMINATOR) self.content_length = None self.reqbuffer.seek(0) self.reqbuffer.truncate() elif mode == STATE_CONTENT: self.state = STATE_CONTENT if self.content_length: self.set_terminator(self.content_length) else: print "no Content-length" def found_terminator(self): data = self.buffer.getvalue() self.buffer.seek(0) self.buffer.truncate() self.reqbuffer.write(data + HTTP_TERMINATOR) if self.state is STATE_HEADER: if string.lower(data[:len(HEADER_CLEN)]) == HEADER_CLEN: self.content_length = int(data[len(HEADER_CLEN):]) elif not data: # header ends self.setmode(STATE_CONTENT) elif self.state is STATE_CONTENT: #print self.reqbuffer.getvalue() reqdata = _xmlrpc.parseRequest(self.reqbuffer.getvalue()) o = self.root e = None try: for p in string.split(reqdata[0], '.'): o = getattr(o, p) result = apply(o, reqdata[1]) except: _TOREPLY_ = _xmlrpc.buildFault(-1, repr(asyncore.compact_traceback()), {}) else: _TOREPLY_ = _xmlrpc.buildResponse(result, {}) self.push(_TOREPLY_) #print _TOREPLY_ self.close_when_done() #self.setmode(STATE_HEADER) class xmlrpc_server(asyncore.dispatcher): def __init__(self, root, address = ('', 43434)): self.root = root if type(address) is type(()): family = socket.AF_INET else: family = socket.AF_UNIX self.create_socket(family, socket.SOCK_STREAM) self.address = address self.set_reuse_addr() self.bind(address) self.listen(128) self.listening = 1 def handle_accept(self): if self.listening: conn, addr = self.accept() xmlrpc_channel(self.root, conn, addr) def writable(self): return 0 try: import forkfarm except ImportError: pass else: class xmlrpc_carrot(forkfarm.forkfarm_carrot): def run(self): mymap = {} xmlrpc_channel(self.side['root'], self.sock, self.addr, mymap) asyncore.loop(map=mymap) def xmlrpc_forkfarm(root, address=('', 43434), maxprocess=4, runlimit=0, farmer=None, carrot=None): """ Simple wrapper for creating xmlrpc server in ForkFarm model """ side = {"root": root} if not carrot: carrot = xmlrpc_carrot if not farmer: farmer = forkfarm.forkfarm return farmer( address, carrot, maxprocess, runlimit=runlimit, side=side ) if __name__ == "__main__": class secdepth: def helloabroad(self): return 'merry christmas hohoho' class myroot(xmlrpc_fancyroot): import re secdepth = secdepth() def hello(self): return 'welcome' def echo(self, *args): return args # example for async server #k = xmlrpc_server(myroot()) #asyncore.loop() # example for forkfarm server k = xmlrpc_forkfarm(myroot()) k.loop()