Welcome, guest | Sign In | My Account | Store | Cart

This recipe illustrates an XML RPC server using twisted with support for HTTP basic client authentication.

Python, 92 lines
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
import xmlrpclib
from twisted.web import xmlrpc, server, http
from twisted.internet import defer, protocol, reactor

Fault = xmlrpclib.Fault

class TwistedRPCServer(xmlrpc.XMLRPC):
    """ A class which works as an XML-RPC server with
    HTTP basic authentication """

    def __init__(self, user='', password=''):
        self._user = user
        self._password = password
        self._auth = (self._user !='')
        xmlrpc.XMLRPC.__init__(self)
        
    def xmlrpc_echo(self, x):
        return x

    def xmlrpc_ping(self):
        return 'OK'

    def render(self, request):
        """ Overridden 'render' method which takes care of
        HTTP basic authorization """
        
        if self._auth:
            cleartext_token = self._user + ':' + self._password
            user = request.getUser()
            passwd = request.getPassword()
        
            if user=='' and passwd=='':
                request.setResponseCode(http.UNAUTHORIZED)
                return 'Authorization required!'
            else:
                token = user + ':' + passwd
                if token != cleartext_token:
                    request.setResponseCode(http.UNAUTHORIZED)
                    return 'Authorization Failed!'

        request.content.seek(0, 0)
        args, functionPath = xmlrpclib.loads(request.content.read())
        try:
            function = self._getFunction(functionPath)
        except Fault, f:
            self._cbRender(f, request)
        else:
            request.setHeader("content-type", "text/xml")
            defer.maybeDeferred(function, *args).addErrback(
                self._ebRender
                ).addCallback(
                self._cbRender, request
                )

        return server.NOT_DONE_YET


# The server
# How to use the server... (Sample code)

# $ python
# >>> from twistedserver import TwistedRPCServer
# >>> s = TwistedRPCServer('user','pass')
# >>> from twisted.web import server
# >>> from twisted.internet import reactor
# >>> reactor.listenTCP(8080, server.Site(s))
# >>> reactor.run()

# Sample client
# Without auth
# 
# $ python
# >>> from xmlrpclib import ServerProxy
# >>> p = ServerProxy('http://localhost:8080')
# >>> p
# <ServerProxy for localhost:8080/RPC2>
# >>> p.echo('hi')
# Traceback (most recent call last):
#  File "<stdin>", line 1, in <module>
#  File "/usr/lib/python2.5/xmlrpclib.py", line 1147, in __call__
#    return self.__send(self.__name, args)
#  File "/usr/lib/python2.5/xmlrpclib.py", line 1437, in __request
#    verbose=self.__verbose
#  File "/usr/lib/python2.5/xmlrpclib.py", line 1191, in request
#    headers
# xmlrpclib.ProtocolError: <ProtocolError for localhost:8080/RPC2: 401  
# Unauthorized>

# With auth
# >>> p = ServerProxy('http://user:pass@localhost:8080')
# >>> p.echo('hi')
# 'hi'

This uses twisted version 2.5.0