I have XML-RPC server:
import time
import xmlrpclib
from SimpleXMLRPCServer import SimpleXMLRPCServer
class Worker(object):
def start_work(self):
# is it possible do return value to client here?
self.do_work()
return 'we started!'
def do_work(self):
while(True):
print 'I\'m doing work...'
time.sleep(3)
if __name__ == '__main__':
port = 8080
server = SimpleXMLRPCServer(("localhost", port))
print "Listening on port %s..." % port
w = Worker()
server.register_function(w.start_work)
while(1):
server.handle_request()
# vim: filetype=python syntax=python expandtab shiftwidth=4 softtabstop=4 encoding=utf8
And easy client:
import xmlrpclib
c = xmlrpclib.ServerProxy('http://localhost:8080/')
print c.start_work()
Of course value returned by start_work function will be never printed.
My question is how to rewrite server code to make possible returning value before finishing work. I know that I can use threads for that but I want to be sure that there is no easier way.
If you want XML-RPC with long-running, early returning tasks, you probably need to rewrite your server into an asynchronous framework, like twisted
I'm not sure this is a good idea to do such a thing without threads or without reimplement some parts of the SimpleXMLRPCServer or without breaking XMLRPC protocol, however you can get an early answer by using a yield
statement in your called method :
class Worker(object):
def start_work(self):
# is it possible do return value to client here?
# yes, with a yield
yield "the_return_value"
self.do_work()
# but yielding a first value and returning it to client will disable any next response
# return 'we started!'
def do_work(self):
while(True):
print 'I\'m doing work...'
time.sleep(3)
And overriding the do_POST method of the SimpleXMLRPCRequestHandler
to call twice the called method (everything around the ### parts are the standard part of python code)
class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
def do_POST(self):
"""Handles the HTTP POST request.
Attempts to interpret all HTTP POST requests as XML-RPC calls,
which are forwarded to the server's _dispatch method for handling.
"""
# Check that the path is legal
if not self.is_rpc_path_valid():
self.report_404()
return
try:
# Get arguments by reading body of request.
# We read this in chunks to avoid straining
# socket.read(); around the 10 or 15Mb mark, some platforms
# begin to have problems (bug #792570).
max_chunk_size = 10*1024*1024
size_remaining = int(self.headers["content-length"])
L = []
while size_remaining:
chunk_size = min(size_remaining, max_chunk_size)
L.append(self.rfile.read(chunk_size))
size_remaining -= len(L[-1])
data = ''.join(L)
# In previous versions of SimpleXMLRPCServer, _dispatch
# could be overridden in this class, instead of in
# SimpleXMLRPCDispatcher. To maintain backwards compatibility,
# check to see if a subclass implements _dispatch and dispatch
# using that method if present.
response = self.server._marshaled_dispatch(
data, getattr(self, '_dispatch', None)
)
##############################################
##############################################
## Here you'll have the first part of your response
response = response.next() # to get the first item of the generator (yes, a little bit uggly)
##############################################
##############################################
except Exception, e: # This should only happen if the module is buggy
# internal error, report as HTTP server error
self.send_response(500)
# Send information about the exception if requested
if hasattr(self.server, '_send_traceback_header') and \
self.server._send_traceback_header:
self.send_header("X-exception", str(e))
self.send_header("X-traceback", traceback.format_exc())
self.end_headers()
else:
# got a valid XML RPC response
self.send_response(200)
self.send_header("Content-type", "text/xml")
self.send_header("Content-length", str(len(response)))
self.end_headers()
self.wfile.write(response)
# shut down the connection
self.wfile.flush()
self.connection.shutdown(1)
##############################################
##############################################
## Here you've send the the first part of your response to the client, relaunch the method
self.server._marshaled_dispatch(
data, getattr(self, '_dispatch', None)
)
##############################################
##############################################
Then, the main will become :
if __name__ == '__main__':
port = 8080
server = SimpleXMLRPCServer(("localhost", port), requestHandler=MySimpleXMLRPCRequestHandler)
print "Listening on port %s..." % port
w = Worker()
server.register_function(w.start_work)
while(1):
server.handle_request()