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')

PythonCode (last edited 2009-08-12 18:07:16 by localhost)