1 # -*- test-case-name: openid.test.test_yadis_discover -*-
2 __all__
= ['discover', 'DiscoveryResult', 'DiscoveryFailure']
4 from cStringIO
import StringIO
6 from openid
import fetchers
8 from openid
.yadis
.constants
import \
9 YADIS_HEADER_NAME
, YADIS_CONTENT_TYPE
, YADIS_ACCEPT_HEADER
10 from openid
.yadis
.parsehtml
import MetaNotFound
, findHTMLMeta
12 class DiscoveryFailure(Exception):
13 """Raised when a YADIS protocol error occurs in the discovery process"""
16 def __init__(self
, message
, http_response
):
17 Exception.__init
__(self
, message
)
18 self
.http_response
= http_response
20 class DiscoveryResult(object):
21 """Contains the result of performing Yadis discovery on a URI"""
23 # The URI that was passed to the fetcher
26 # The result of following redirects from the request_uri
29 # The URI from which the response text was returned (set to
30 # None if there was no XRDS document found)
33 # The content-type returned with the response_text
36 # The document returned from the xrds_uri
39 def __init__(self
, request_uri
):
40 """Initialize the state of the object
42 sets all attributes to None except the request_uri
44 self
.request_uri
= request_uri
46 def usedYadisLocation(self
):
47 """Was the Yadis protocol's indirection used?"""
48 return self
.normalized_uri
!= self
.xrds_uri
51 """Is the response text supposed to be an XRDS document?"""
52 return (self
.usedYadisLocation() or
53 self
.content_type
== YADIS_CONTENT_TYPE
)
56 """Discover services for a given URI.
58 @param uri: The identity URI as a well-formed http or https
59 URI. The well-formedness and the protocol are not checked, but
60 the results of this function are undefined if those properties
63 @return: DiscoveryResult object
65 @raises Exception: Any exception that can be raised by fetching a URL with
67 @raises DiscoveryFailure: When the HTTP response does not have a 200 code.
69 result
= DiscoveryResult(uri
)
70 resp
= fetchers
.fetch(uri
, headers
={'Accept': YADIS_ACCEPT_HEADER
})
71 if resp
.status
not in (200, 206):
72 raise DiscoveryFailure(
73 'HTTP Response status from identity URL host is not 200. '
74 'Got status %r' % (resp
.status
,), resp
)
76 # Note the URL after following redirects
77 result
.normalized_uri
= resp
.final_url
79 # Attempt to find out where to go to discover the document
80 # or if we already have it
81 result
.content_type
= resp
.headers
.get('content-type')
83 result
.xrds_uri
= whereIsYadis(resp
)
85 if result
.xrds_uri
and result
.usedYadisLocation():
86 resp
= fetchers
.fetch(result
.xrds_uri
)
87 if resp
.status
not in (200, 206):
88 exc
= DiscoveryFailure(
89 'HTTP Response status from Yadis host is not 200. '
90 'Got status %r' % (resp
.status
,), resp
)
91 exc
.identity_url
= result
.normalized_uri
93 result
.content_type
= resp
.headers
.get('content-type')
95 result
.response_text
= resp
.body
100 def whereIsYadis(resp
):
101 """Given a HTTPResponse, return the location of the Yadis document.
103 May be the URL just retrieved, another URL, or None, if I can't
108 @returns: str or None
110 # Attempt to find out where to go to discover the document
111 # or if we already have it
112 content_type
= resp
.headers
.get('content-type')
114 # According to the spec, the content-type header must be an exact
115 # match, or else we have to look for an indirection.
117 content_type
.split(';', 1)[0].lower() == YADIS_CONTENT_TYPE
):
118 return resp
.final_url
121 yadis_loc
= resp
.headers
.get(YADIS_HEADER_NAME
.lower())
124 # Parse as HTML if the header is missing.
126 # XXX: do we want to do something with content-type, like
127 # have a whitelist or a blacklist (for detecting that it's
130 yadis_loc
= findHTMLMeta(StringIO(resp
.body
))