Python XRI Stub Resolver
This code is functional against the proxy resolution part of XRI resolution. It should be pretty self explanatory. This code features a bit of smarts about trying multiple proxies for the purpose of failover. If a proxy stops responding, the code will try the next proxy in the list of proxies provided. It will also remember the fact that the original proxy is down and will not try the downed proxies for 600 seconds (configurable).
This code is licensed GPL. It probably should live somewhere else than in this wiki
1 #!/usr/bin/python
2 '''
3 XRI resolution and processing code
4 (C) 2006 Amsoft System US LLC
5 See source for license
6 '''
7
8
9 ##(C) 2006 Amsoft System US LLC
10 ##
11 ##This program is free software; you can redistribute it and/or
12 ##modify it under the terms of the GNU General Public License
13 ##as published by the Free Software Foundation; either version 2
14 ##of the License, or (at your option) any later version.
15 ##
16 ##This program is distributed in the hope that it will be useful,
17 ##but WITHOUT ANY WARRANTY; without even the implied warranty of
18 ##MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 ##GNU General Public License for more details.
20 ##
21 ##You should have received a copy of the GNU General Public License
22 ##along with this program; if not, write to the Free Software
23 ##Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
24
25 import xml.dom.minidom
26 import urllib
27 import string
28
29
30 __version__='0.1.2'
31
32 RETRY_DELAY=600 # 10 minutes to retry a proxy not working
33 XRD_MTYPE="application/xrd+xml"
34 XRDS_MTYPE="application/xrds+xml"
35 URILIST_MTYPE="text/uri-list"
36
37
38 def convertXRItoIRI(xri):
39 iri=xri.replace("%", "%25")
40 # find xref extents
41 xreflevel=0
42 extents=[]
43 for i in range(len(iri)):
44 if iri[i] == "(":
45 if xreflevel==0:
46 curstart=i
47 xreflevel+=1
48 elif iri[i] == ")":
49 xreflevel-=1
50 if (xreflevel==0):
51 extents.append((curstart, i))
52 offset=0
53 for extent in extents:
54 xref=iri[extent[0]+offset:extent[1]+1+offset]
55 escaped=xref.replace("#", "%23")
56 escaped=escaped.replace("?", "%3F")
57 escaped=escaped.replace("/", "%2F")
58 increased = len(escaped) - len(xref)
59 iri=iri[0:extent[0]+offset] + escaped+ iri[extent[1]+offset+1:]
60 offset+=increased
61 return iri
62
63 def convertIRItoURI(iri, idna=False):
64 untouched=""
65 if idna and not (iri[6] in "(@=+$!"):
66 pos=iri.find("/", 6)
67 domainname=iri[6:pos]
68 idna=domainname.encode("idna")
69 iri=iri[pos:]
70 untouched="xri://"+idna
71 if (type(iri) == type(u'')):
72 uri=iri.encode('utf-8')
73 else:
74 uri=iri
75 uri=urllib.quote(uri, safe=":/?#@=+$()%*!")
76 return untouched+uri
77
78 def idna(iri):
79 # Check to see if first segment is dns
80 if not (iri[6] in "(@=+$!"):
81 # assume there's no xref, so just find the first / after position 6
82 pos=iri.find("/", 6)
83 domainname=iri[6:pos]
84 idna=domainname.encode("idna")
85
86 iri.replace(domainname, idna)
87 return iri
88
89
90 class StubResolver:
91
92
93 def __init__(self, proxylist=('http://xri.net/',)):
94 self._proxylist=proxylist
95 self._failedproxies={}
96 self._clearProxyStatus()
97
98 def _clearProxyStatus(self):
99 self._failedproxies={}
100
101 def _reportGood(self, proxy):
102 if self._failedproxies.has_key(proxy):
103 del(self._failedproxies[proxy])
104
105 def _reportBad(self, proxy):
106 self._failedproxies[proxy]=time.time()
107
108 def _getProxyURI(self):
109 ''' Implements strategy for getting right proxy to access
110 '''
111 timedate=0
112 for proxy in self._proxylist:
113 if not (self._failedproxies.has_key(proxy)):
114 return proxy
115 timedate=self._failedproxies[proxy]
116 if timedate+RETRY_DELAY < time.time():
117 del(self._failedproxies[proxy])
118 return proxy
119 return None # this is a problem - no proxies available
120
121 def _resolveXRI(self, xri, trusted=False, rtype=None, stype=None, mtype=None):
122 pparams='_xrd_r='+rtype
123 if ((rtype==XRD_MTYPE) or (rtype==URILIST_MTYPE)) and (stype != None):
124 pparams=pparams+"&_xrd_t="+stype
125 if (mtype!=None):
126 pparams=pparams+"&_xrd_m="+mtype
127 complete=False
128 while (not complete):
129 proxyuri=self._getProxyURI()
130 esxri=urllib.quote((convertIRItoURI(convertXRItoIRI(xri))), safe="=@*!+()")
131 if (proxyuri[-1] != '/'):
132 sep='/'
133 else:
134 sep=''
135 requri=proxyuri+sep+esxri+'?'+pparams
136 try:
137 #print "Using request URI",requri
138 resp=urllib.urlopen(requri)
139 except IOError, e:
140 self._reportBad(proxyuri)
141 continue
142 self._reportGood(proxyuri)
143 complete=True
144 if complete:
145 return resp.read()
146 else:
147 # Probably should raise a meaningful exception - No Proxies Working
148 return None
149
150 def getXRD(self, xri, trusted=False, service=None, mediatype=None):
151 '''
152 Returns the final XRD describing the XRI.
153 May or may not do filtering of the XRD returned depending on whether service type is provided
154 If it is, then the service type and mediatype params are used to filter out all non-matching
155 XRI's
156 Returns None if no proxies are available.
157 '''
158 return self._resolveXRI(xri, trusted=trusted, rtype=XRD_MTYPE, stype=service, mtype=mediatype)
159
160 def getXRDS(self, xri, trusted=False):
161 '''
162 Returns the XRDS chain that the proxy returns for the XRI. No filtering/selection done by
163 the proxy.
164 Returns None if no proxies are available.
165 '''
166
167 return self._resolveXRI(xri, trusted=trusted, rtype=XRDS_MTYPE, stype=None, mtype=mtype)
168
169
170 def getURIList(self, xri, trusted=False, service=None, mtype=None):
171 '''
172 Returns a list of URIs from the final resolved XRD which match the service type and
173 (if present) the media type given.
174 Returns None if no proxies are available.
175 '''
176 results=self._resolveXRI(xri, trusted=trusted, rtype=URILIST_MTYPE, stype=service, mtype=mtype)
177 if (results==None):
178 # No proxies available
179 return None
180 #return a list of strings using RFC2483
181 return filter(lambda x: not((len(x) == 0) or (x[0] == '#')), results.split())
182
183
184 if (__name__=="__main__"):
185 sr=StubResolver()
186 print sr.getXRD('=GabeW', service='http://openid.net/signon/1.0')
187 print sr.getURIList('=GabeW', service='http://openid.net/signon/1.0')
XRI Wiki