1 # -*- test-case-name: openid.test.test_xrires -*-
5 from urllib
import urlencode
6 from openid
import fetchers
7 from openid
.yadis
import etxrd
8 from openid
.yadis
.xri
import toURINormal
9 from openid
.yadis
.services
import iterServices
11 DEFAULT_PROXY
= 'http://proxy.xri.net/'
13 class ProxyResolver(object):
14 """Python interface to a remote XRI proxy resolver.
16 def __init__(self
, proxy_url
=DEFAULT_PROXY
):
17 self
.proxy_url
= proxy_url
20 def queryURL(self
, xri
, service_type
=None):
21 """Build a URL to query the proxy resolver.
23 @param xri: An XRI to resolve.
26 @param service_type: The service type to resolve, if you desire
27 service endpoint selection. A service type is a URI.
28 @type service_type: str
33 # Trim off the xri:// prefix. The proxy resolver didn't accept it
34 # when this code was written, but that may (or may not) change for
35 # XRI Resolution 2.0 Working Draft 11.
36 qxri
= toURINormal(xri
)[6:]
37 hxri
= self
.proxy_url
+ qxri
39 # XXX: If the proxy resolver will ensure that it doesn't return
40 # bogus CanonicalIDs (as per Steve's message of 15 Aug 2006
41 # 11:13:42), then we could ask for application/xrd+xml instead,
42 # which would give us a bit less to process.
43 '_xrd_r': 'application/xrds+xml',
46 args
['_xrd_t'] = service_type
48 # Don't perform service endpoint selection.
49 args
['_xrd_r'] += ';sep=false'
50 query
= _appendArgs(hxri
, args
)
54 def query(self
, xri
, service_types
):
55 """Resolve some services for an XRI.
57 Note: I don't implement any service endpoint selection beyond what
58 the resolver I'm querying does, so the Services I return may well
59 include Services that were not of the types you asked for.
61 May raise fetchers.HTTPFetchingError or L{etxrd.XRDSError} if
62 the fetching or parsing don't go so well.
64 @param xri: An XRI to resolve.
67 @param service_types: A list of services types to query for. Service
69 @type service_types: list of str
71 @returns: tuple of (CanonicalID, Service elements)
72 @returntype: (unicode, list of C{ElementTree.Element}s)
74 # FIXME: No test coverage!
76 # Make a seperate request to the proxy resolver for each service
77 # type, as, if it is following Refs, it could return a different
82 for service_type
in service_types
:
83 url
= self
.queryURL(xri
, service_type
)
84 response
= fetchers
.fetch(url
)
85 if response
.status
not in (200, 206):
86 # XXX: sucks to fail silently.
87 # print "response not OK:", response
89 et
= etxrd
.parseXRDS(response
.body
)
90 canonicalID
= etxrd
.getCanonicalID(xri
, et
)
91 some_services
= list(iterServices(et
))
92 services
.extend(some_services
)
94 # * If we do get hits for multiple service_types, we're almost
95 # certainly going to have duplicated service entries and
96 # broken priority ordering.
97 return canonicalID
, services
100 def _appendArgs(url
, args
):
101 """Append some arguments to an HTTP query.
103 # to be merged with oidutil.appendArgs when we combine the projects.
104 if hasattr(args
, 'items'):
111 # According to XRI Resolution section "QXRI query parameters":
113 # """If the original QXRI had a null query component (only a leading
114 # question mark), or a query component consisting of only question
115 # marks, one additional leading question mark MUST be added when
116 # adding any XRI resolution parameters."""
118 if '?' in url
.rstrip('?'):
123 return '%s%s%s' % (url
, sep
, urlencode(args
))