ruby-yadis 0.2.2 → 0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/INSTALL CHANGED
@@ -17,6 +17,6 @@ Make sure everything installed ok:
17
17
 
18
18
  == Run the test suite
19
19
 
20
- Go into the test directory and execute the *runtests* script.
20
+ Go into the test directory and execute the *runtests.rb* script.
21
21
 
22
22
 
@@ -1 +1,6 @@
1
1
  require 'yadis/yadis'
2
+ require 'yadis/xrds'
3
+ require 'yadis/service'
4
+ require 'yadis/parsehtml'
5
+ require 'yadis/fetcher'
6
+ require 'yadis/manager'
@@ -0,0 +1 @@
1
+ require 'yadis/yadis'
@@ -1,5 +1,13 @@
1
1
  require "uri"
2
- require "net/https"
2
+
3
+ begin
4
+ require "net/https"
5
+ rescue LoadError
6
+ HAS_OPENSSL_ = false
7
+ require 'net/http'
8
+ else
9
+ HAS_OPENSSL_ = true
10
+ end
3
11
 
4
12
  class NetHTTPFetcher
5
13
 
@@ -22,29 +30,31 @@ class NetHTTPFetcher
22
30
 
23
31
  protected
24
32
 
25
- # return a Net::HTTP object ready for use
26
-
33
+ # return a Net::HTTP object ready for use
27
34
  def get_http_obj(uri)
28
35
  http = Net::HTTP.new(uri.host, uri.port)
29
36
  http.read_timeout = @read_timeout
30
37
  http.open_timeout = @open_timeout
31
38
 
32
- if uri.scheme == 'https'
33
- http.use_ssl = true
34
- if @ca_path
35
- http.ca_file = @ca_path
36
- http.verify_mode = OpenSSL::SSL::VERIFY_PEER
39
+ if uri.scheme == 'https'
40
+ if HAS_OPENSSL_
41
+ http.use_ssl = true
42
+ if @ca_path
43
+ http.ca_file = @ca_path
44
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
45
+ else
46
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
47
+ STDERR.puts("Warning: fetching over https without verifying server certificate")
48
+ end
37
49
  else
38
- http.verify_mode = OpenSSL::SSL::VERIFY_NONE
39
- STDERR.puts("Warning: fetching over https without verifying server certificate")
50
+ STDERR.puts('Warning: trying to fetch HTTPS URL without OpenSSL support')
40
51
  end
41
52
  end
42
53
 
43
- http
54
+ return http
44
55
  end
45
56
 
46
- # do a GET following redirects limit deep
47
-
57
+ # do a GET following redirects limit deep
48
58
  def do_get(url, params, limit=5)
49
59
  if limit == 0
50
60
  return nil
@@ -57,10 +67,12 @@ class NetHTTPFetcher
57
67
  nil
58
68
  else
59
69
  case resp
60
- when Net::HTTPSuccess then [resp, URI.parse(url).to_s]
61
- when Net::HTTPRedirection then do_get(resp["location"], params, limit-1)
62
- else
63
- nil
70
+ when Net::HTTPSuccess
71
+ return [resp, URI.parse(url).to_s]
72
+ when Net::HTTPRedirection
73
+ return do_get(resp["location"], params, limit-1)
74
+ else
75
+ return nil
64
76
  end
65
77
  end
66
78
  end
@@ -0,0 +1,79 @@
1
+ require "uri"
2
+
3
+ begin
4
+ require "net/https"
5
+ rescue LoadError
6
+ HAS_OPENSSL_ = false
7
+ require 'net/http'
8
+ else
9
+ HAS_OPENSSL_ = true
10
+ end
11
+
12
+ class NetHTTPFetcher
13
+
14
+ attr_accessor :ca_path
15
+
16
+ def initialize(read_timeout=20, open_timeout=20)
17
+ @read_timeout = read_timeout
18
+ @open_timeout = open_timeout
19
+ @ca_path = nil
20
+ end
21
+
22
+ def get(url, params = nil)
23
+ resp, final_url = do_get(url, params)
24
+ if resp.nil?
25
+ nil
26
+ else
27
+ [final_url, resp]
28
+ end
29
+ end
30
+
31
+ protected
32
+
33
+ # return a Net::HTTP object ready for use
34
+ def get_http_obj(uri)
35
+ http = Net::HTTP.new(uri.host, uri.port)
36
+ http.read_timeout = @read_timeout
37
+ http.open_timeout = @open_timeout
38
+
39
+ if uri.scheme == 'https'
40
+ if HAS_OPENSSL_
41
+ http.use_ssl = true
42
+ if @ca_path
43
+ http.ca_file = @ca_path
44
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
45
+ else
46
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
47
+ STDERR.puts("Warning: fetching over https without verifying server certificate")
48
+ end
49
+ else
50
+ STDERR.puts('Warning: trying to fetch HTTPS URL without OpenSSL support')
51
+ end
52
+ end
53
+
54
+ return http
55
+ end
56
+
57
+ # do a GET following redirects limit deep
58
+ def do_get(url, params, limit=5)
59
+ if limit == 0
60
+ return nil
61
+ end
62
+ begin
63
+ uri = URI.parse(url)
64
+ http = get_http_obj(uri)
65
+ resp = http.request_get(uri.request_uri, params)
66
+ rescue
67
+ nil
68
+ else
69
+ case resp
70
+ when Net::HTTPSuccess then [resp, URI.parse(url).to_s]
71
+ when Net::HTTPRedirection then do_get(resp["location"], params, limit-1)
72
+ else
73
+ STDERR.puts("ERROR, what to do with #{resp}")
74
+ nil
75
+ end
76
+ end
77
+ end
78
+
79
+ end
@@ -0,0 +1,145 @@
1
+ require 'yadis/yadis'
2
+
3
+ class YadisServiceManager
4
+
5
+ attr_reader :starting_url, :yadis_url, :services, :session_key, :current
6
+
7
+ def initialize(starting_url, yadis_url, services)
8
+ @starting_url = starting_url
9
+ @yadis_url = yadis_url
10
+ @services = services
11
+ @current = nil
12
+ end
13
+
14
+ def next
15
+ @current = @services.shift
16
+ end
17
+
18
+ def for_url?(url)
19
+ url == @starting_url or url == @yadis_url
20
+ end
21
+
22
+ def started?
23
+ not @current.nil?
24
+ end
25
+
26
+ def length
27
+ @services.length
28
+ end
29
+
30
+ end
31
+
32
+ class Discovery
33
+
34
+ @@default_suffix = 'auth'
35
+ @@prefix = '_yadis_services_'
36
+
37
+ # expects a normalized url
38
+ def initialize(session, url, session_key_suffix=nil)
39
+ @session = session
40
+ @url = url
41
+ @session_key = @@prefix + (session_key_suffix or @@default_suffix)
42
+ end
43
+
44
+ def next_service
45
+ manager = self.get_manager
46
+ if manager and manager.length <= 0
47
+ self.destroy_manager
48
+ manager = nil
49
+ end
50
+
51
+ unless manager
52
+ begin
53
+ yadis_url, services = self.discover
54
+ rescue YADISParseError, YADISHTTPError
55
+ manager = nil
56
+ else
57
+ manager = self.create_manager(services, yadis_url)
58
+ end
59
+ end
60
+
61
+ if manager
62
+ service = manager.next
63
+ self.store_manager(manager)
64
+ else
65
+ service = nil
66
+ end
67
+
68
+ return service
69
+ end
70
+
71
+ def finish
72
+ manager = self.get_manager
73
+ return nil unless manager
74
+
75
+ service = manager.current
76
+ self.destroy_manager
77
+ return service
78
+ end
79
+
80
+ def current
81
+ manager = self.get_manager
82
+ return nil unless manager
83
+ return manager.current
84
+ end
85
+
86
+ def get_manager
87
+ manager = @session[@session_key]
88
+
89
+ # make sure we've got the right manager here
90
+ if manager and manager.for_url?(@url)
91
+ return manager
92
+ end
93
+
94
+ return nil
95
+ end
96
+
97
+ def create_manager(services, yadis_url=nil)
98
+ if self.get_manager
99
+ raise ArgumentError, "There is already a manager for #{@url}"
100
+ end
101
+
102
+ if services.length > 0
103
+ manager = YadisServiceManager.new(@url, yadis_url, services)
104
+ self.store_manager(manager)
105
+ else
106
+ manager = nil
107
+ end
108
+
109
+ return manager
110
+ end
111
+
112
+ def destroy_manager
113
+ if self.get_manager
114
+ begin
115
+ @session.delete(@session_key)
116
+ rescue
117
+ # sometimes Hash like session objects don't have a delete
118
+ # method. We handle that case by assigning nil to the session[key]
119
+ @session[@session_key] = nil
120
+ end
121
+ end
122
+ end
123
+
124
+ def store_manager(manager)
125
+ @session[@session_key] = manager
126
+ end
127
+
128
+ # The filter argument is a Proc that will be used to call
129
+ # YADIS.filter_services. See the documentation for YADIS.filter_services
130
+ # for more information about writing filters.
131
+ def discover(filter=nil)
132
+ y = YADIS.new(@url)
133
+
134
+ # a default filter which sends through everything. you should
135
+ # probably consider writing a custom filter and passing it in.
136
+ unless filter
137
+ filter = lambda {|s| s}
138
+ end
139
+
140
+ return [y.uri, y.filter_services(filter)]
141
+ end
142
+
143
+ end
144
+
145
+
@@ -0,0 +1,138 @@
1
+ require 'yadis/yadis'
2
+
3
+ class YadisServiceManager
4
+
5
+ attr_reader :starting_url, :yadis_url, :services, :session_key, :current
6
+
7
+ def initialize(starting_url, yadis_url, services)
8
+ @starting_url = starting_url
9
+ @yadis_url = yadis_url
10
+ @services = services
11
+ @current = nil
12
+ end
13
+
14
+ def next
15
+ @current = @services.shift
16
+ end
17
+
18
+ def for_url?(url)
19
+ url == @starting_url or url == @yadis_url
20
+ end
21
+
22
+ def started?
23
+ not @current.nil?
24
+ end
25
+
26
+ def length
27
+ @services.length
28
+ end
29
+
30
+ end
31
+
32
+ class Discovery
33
+
34
+ @@default_suffix = nil
35
+ @@prefix = '_yadis_services_'
36
+
37
+ def initialize(session, url, session_key_siffix=nil)
38
+ @session = session
39
+ @url = url
40
+ @session_key = @@prefix + (session_key_suffix or @@default_suffix)
41
+ end
42
+
43
+ def next_service(discover_block)
44
+ manager = self.get_manager
45
+ if manager and manager.length <= 0
46
+ self.destroy_manager
47
+ manager = nil
48
+ end
49
+
50
+ unless manager
51
+ begin
52
+ yadis_url, services = self.discover
53
+ rescue YADISParseError, YADISHTTPError
54
+ manager = nil
55
+ else
56
+ manager = self.create_manager(services, yadis_url)
57
+ end
58
+ end
59
+
60
+ if manager
61
+ service = manager.next
62
+ self.store_manager(manager)
63
+ else
64
+ service = nil
65
+ end
66
+
67
+ return service
68
+ end
69
+
70
+ def finish
71
+ manager = self.get_manager
72
+ return nil unless manager
73
+
74
+ service = manager.current
75
+ self.destroy_manager
76
+ return service
77
+ end
78
+
79
+ def get_manager
80
+ manager = @session[@session_key]
81
+
82
+ # make sure we've got the right manager here
83
+ if manager and manager.for_url?(@url)
84
+ return manager
85
+ end
86
+
87
+ return nil
88
+ end
89
+
90
+ def create_manager(services, yadis_url=nil)
91
+ if self.get_manager
92
+ raise ArgumentError, "There is already a manager for #{@url}"
93
+ end
94
+
95
+ if services.length > 0
96
+ manager = YadisServiceManager.new(@url, yadis_url, services)
97
+ self.store_manager(manager)
98
+ else
99
+ manager = nil
100
+ end
101
+
102
+ return manager
103
+ end
104
+
105
+ def destroy_manager
106
+ if self.get_manager
107
+ begin
108
+ @session.delete(@session_key)
109
+ rescue
110
+ # sometimes Hash like session objects don't have a delete
111
+ # method. We handle that case by assigning nil to the session[key]
112
+ @session[@session_key] = nil
113
+ end
114
+ end
115
+ end
116
+
117
+ def store_manager(manager)
118
+ @session[@session_key] = manager
119
+ end
120
+
121
+ # The filter argument is a Proc that will be used to call
122
+ # YADIS.filter_services. See the documentation for YADIS.filter_services
123
+ # for more information about writing filters.
124
+ def discover(filter=nil)
125
+ y = YADIS.new(@url)
126
+
127
+ # a default filter which sends through everything. you should
128
+ # probably consider writing a custom filter and passing it in.
129
+ unless filter
130
+ filter = lambda {|s| s}
131
+ end
132
+
133
+ return [y.url, y.filter_services(filter)]
134
+ end
135
+
136
+ end
137
+
138
+
@@ -0,0 +1,22 @@
1
+ # Class representing an XRD Service element.
2
+ class ServiceEndpoint
3
+
4
+ attr_accessor :service_types, :uri, :yadis_uri, :element, :yadis
5
+
6
+ def initialize
7
+ @service_types = []
8
+ @uri = nil
9
+ @yadis_uri = nil
10
+ @element = nil
11
+ @yadis_uri = nil
12
+ end
13
+
14
+ def match_type_uris(type_uris)
15
+ type_uris.find_all {|t| @service_types.member?(t)}
16
+ end
17
+
18
+ def ==(other)
19
+ return self.instance_variables == other.instance_variables
20
+ end
21
+
22
+ end
@@ -0,0 +1,24 @@
1
+ require 'rexml/document'
2
+
3
+ # Class representing an XRD Service element.
4
+ class ServiceEndpoint
5
+
6
+ attr_accessor :service_types, :uri, :yadis_uri, :element, :yadis
7
+
8
+ def initialize
9
+ @service_types = []
10
+ @uri = nil
11
+ @yadis_uri = nil
12
+ @element = nil
13
+ @yadis_uri = nil
14
+ end
15
+
16
+ def match_type_uris(type_uris)
17
+ type_uris.find_all {|t| @service_types.member?(t)}
18
+ end
19
+
20
+ def ==(other)
21
+ return self.instance_variables == other.instance_variables
22
+ end
23
+
24
+ end
@@ -1,8 +1,25 @@
1
1
  require 'rexml/document'
2
+ require 'yadis/service'
2
3
 
3
4
  # Class that handles XRDS parsing and XRD Service element extraction.
5
+
6
+ module XRDSUtil
7
+
8
+ @@default_namespace = 'xri://$xrd*($v*2.0)'
9
+ @@xrds_namespace = {'xrds' => 'xri://$xrds'}
10
+
11
+ def last_xrd(root_element)
12
+ REXML::XPath.match(root_element, '/xrds:XRDS/XRD',
13
+ @@xrds_namespace)[-1]
14
+ end
15
+
16
+ end
17
+
4
18
  class XRDS
5
19
 
20
+ include XRDSUtil
21
+ attr_reader :xml
22
+
6
23
  # Method for producing a valid XRDS object. Accepts an XML
7
24
  # String. Returns an XRDS object on success, or nil on failure.
8
25
  # Same as calling XRDS.new, but does not rails ArgumentErrors.
@@ -14,25 +31,24 @@ class XRDS
14
31
  end
15
32
  end
16
33
 
17
- def XRDS.parse_verbose(xml)
18
- new(xml)
19
- end
20
-
21
34
  # Create a new XRDS object. Raises ArgumentError if xml_text is
22
35
  # malformed or invalid XRDS.
23
36
  def initialize(xml_text)
37
+ @xml_text = xml_text
24
38
  parse_xml(xml_text)
25
39
  end
26
40
 
27
- # The schema was defined to support multiple XRD elements, but YADIS
28
- # will never use it, so we assume just one.
29
- #
30
- # multiple Type and URI funkiness
31
- #
32
- # Does not implement URI priority yet.
41
+ def _dump(depth)
42
+ return @xml_text
43
+ end
44
+
45
+ def XRDS._load(s)
46
+ XRDS.new(s)
47
+ end
48
+
33
49
  def parse_xml(xml_text)
34
50
  begin
35
- xml = REXML::Document.new(xml_text)
51
+ xml = @xml = REXML::Document.new(xml_text)
36
52
  rescue
37
53
  raise ArgumentError, "Can't parse XRDS"
38
54
  end
@@ -41,36 +57,25 @@ class XRDS
41
57
  raise ArgumentError, "No document root"
42
58
  end
43
59
 
44
- if xml.root.attributes['xmlns'] != 'xri://$xrd*($v*2.0)'
45
- raise ArgumentError, "Unknown XRID version #{xml.root.attributes['xmlns'].to_s}"
46
- end
47
-
48
- # get the last <XRD> element
49
- xrd_elemets = xml.root.get_elements('/xrds:XRDS/XRD')
50
- last_xrd = xrd_elemets[-1]
51
- if last_xrd.nil?
52
- raise ArgumentError, "No XRD Elements found"
60
+ xmlns = xml.root.attributes['xmlns']
61
+ if xmlns != @@default_namespace
62
+ raise ArgumentError, "Unknown XRID version #{xmlns.to_s}"
53
63
  end
54
64
 
55
- # get Service elements
56
- @services = {} # keyed by priority
57
- priority_index = -1 # for services w/ no priority specified
58
- last_xrd.elements.each('Service') do |s|
59
- s.elements.each('URI') do |u|
60
- priority_index = _create_services(s, u, priority_index)
61
- end
62
- if !s.elements['URI']
63
- priority_index = _create_services(s, nil, priority_index)
64
- end
65
- end
65
+ xrd = self.last_xrd(xml.root)
66
+ raise ArgumentError, "No XRD Elements found" if xrd.nil?
67
+
68
+ @services = {} # keyed by [service_priority, uri_priority]
69
+ xrd.elements.each('Service') {|s| _create_services(s)}
66
70
  end
67
71
 
68
- # Returns an Array of Service objects, sorted by priority. Highest
72
+
73
+ # Returns an Array of ServiceEndpoint objects, sorted by priority. Highest
69
74
  # priority is at element 0.
70
75
  def services
71
76
  s = []
72
77
 
73
- @services.keys.sort.each do |key| @services[key]
78
+ @services.keys.sort.each do |key|
74
79
  services_list = @services[key].dup
75
80
 
76
81
  # randomize services with the same priority
@@ -85,61 +90,48 @@ class XRDS
85
90
 
86
91
  private
87
92
 
88
- def _add_service(priority_index, service)
89
- unless @services.has_key?(priority_index)
90
- @services[priority_index] = []
91
- end
92
-
93
- # services with the same priority are appended to the list
94
- @services[priority_index] << service
95
- end
96
-
97
93
  # create services objects
98
- def _create_services(service_element, uri_element, priority_index)
99
- service_element.elements.each('Type') do |t|
100
- service = Service.new
101
- service.uri = uri_element.text if uri_element
102
- service.service_type = t.text
103
- service.element = service_element
104
-
105
- service_element.elements.each do |e|
106
- service.add_other(e.prefix + ':' + e.name, e.text) if e.prefix
107
- end
108
-
109
- if service_element.attributes['priority']
110
- priority = service_element.attributes['priority'].to_i
111
- _add_service(priority, service)
94
+ def _create_services(service_element)
95
+ service = ServiceEndpoint.new
96
+ service.element = service_element
97
+ service.uri = nil
98
+ service.service_types = []
112
99
 
113
- elsif uri_element.attributes['priority']
114
- priority = uri_element.attributes['priority'].to_i
115
- _add_service(priority, service)
100
+ service_element.elements.each('Type') do |t|
101
+ service.service_types << t.text.strip
102
+ end
116
103
 
117
- else
118
- _add_service(priority_index, service)
119
- priority_index -= 1
104
+ sp = service_element.attributes['priority']
105
+ service_priority = sp ? sp.to_i : -1
106
+
107
+ if service.element.elements['URI']
108
+ service.element.elements.each('URI') do |uri|
109
+ _service = service.dup
110
+ _service.uri = uri.text.strip
111
+
112
+ up = uri.attributes['priority']
113
+ uri_priority = up ? up.to_i : -1
114
+ priority = [service_priority, uri_priority]
115
+
116
+ _add_service(priority, _service)
120
117
  end
118
+
119
+ else
120
+ priority = [service_priority, -1]
121
+ _add_service(priority, service)
121
122
 
122
123
  end
123
124
 
124
- return priority_index
125
125
  end
126
126
 
127
- end
128
-
129
- # Class representing an XRD Service element.
130
- class Service
131
- attr_reader :service_type, :uri, :element
132
- attr_writer :service_type, :uri, :element
133
-
134
- def initialize
135
- @other = Hash.new
136
- end
127
+ def _add_service(priority, service)
128
+ unless @services.has_key?(priority)
129
+ @services[priority] = []
130
+ end
137
131
 
138
- def add_other(name, value)
139
- @other[name] = value
132
+ # services with the same priority are appended to the list
133
+ @services[priority] << service
140
134
  end
141
135
 
142
- def other
143
- return @other
144
- end
145
136
  end
137
+
@@ -0,0 +1,128 @@
1
+ require 'rexml/document'
2
+ require 'yadis/service'
3
+
4
+ # Class that handles XRDS parsing and XRD Service element extraction.
5
+
6
+ module XRDSUtil
7
+
8
+ @@default_namespace = 'xri://$xrd*($v*2.0)'
9
+ @@xrds_namespace = {'xrds' => 'xri://$xrds'}
10
+
11
+ def last_xrd(root_element)
12
+ REXML::XPath.match(root_element, '/xrds:XRDS/XRD',
13
+ @@xrds_namespace)[-1]
14
+ end
15
+
16
+ end
17
+
18
+ class XRDS
19
+
20
+ include XRDSUtil
21
+ attr_reader :xml
22
+
23
+ # Method for producing a valid XRDS object. Accepts an XML
24
+ # String. Returns an XRDS object on success, or nil on failure.
25
+ # Same as calling XRDS.new, but does not rails ArgumentErrors.
26
+ def XRDS.parse(xml)
27
+ begin
28
+ return new(xml)
29
+ rescue
30
+ return nil
31
+ end
32
+ end
33
+
34
+ # Create a new XRDS object. Raises ArgumentError if xml_text is
35
+ # malformed or invalid XRDS.
36
+ def initialize(xml_text)
37
+ parse_xml(xml_text)
38
+ end
39
+
40
+ def parse_xml(xml_text)
41
+ begin
42
+ xml = @xml = REXML::Document.new(xml_text)
43
+ rescue
44
+ raise ArgumentError, "Can't parse XRDS"
45
+ end
46
+
47
+ if xml.root.nil?
48
+ raise ArgumentError, "No document root"
49
+ end
50
+
51
+ xmlns = xml.root.attributes['xmlns']
52
+ if xmlns != @@default_namespace
53
+ raise ArgumentError, "Unknown XRID version #{xmlns.to_s}"
54
+ end
55
+
56
+ xrd = self.last_xrd(xml.root)
57
+ raise ArgumentError, "No XRD Elements found" if xrd.nil?
58
+
59
+ @services = {} # keyed by [service_priority, uri_priority]
60
+ xrd.elements.each('Service') {|s| _create_services(s)}
61
+ end
62
+
63
+
64
+ # Returns an Array of ServiceEndpoint objects, sorted by priority. Highest
65
+ # priority is at element 0.
66
+ def services
67
+ s = []
68
+
69
+ @services.keys.sort.each do |key|
70
+ services_list = @services[key].dup
71
+
72
+ # randomize services with the same priority
73
+ while services_list.length > 0
74
+ s << services_list.delete_at((rand * services_list.length).to_i)
75
+ end
76
+
77
+ end
78
+
79
+ return s
80
+ end
81
+
82
+ private
83
+
84
+ # create services objects
85
+ def _create_services(service_element)
86
+ service = ServiceEndpoint.new
87
+ service.element = service_element
88
+ service.uri = nil
89
+ service.service_types = []
90
+
91
+ service_element.elements.each('Type') do |t|
92
+ service.service_types << t.text.strip
93
+ end
94
+
95
+ sp = service_element.attributes['priority']
96
+ service_priority = sp ? sp.to_i : -1
97
+
98
+ if service.element.elements['URI']
99
+ service.element.elements.each('URI') do |uri|
100
+ _service = service.dup
101
+ _service.uri = uri.text.strip
102
+
103
+ up = uri.attributes['priority']
104
+ uri_priority = up ? up.to_i : -1
105
+ priority = [service_priority, uri_priority]
106
+
107
+ _add_service(priority, _service)
108
+ end
109
+
110
+ else
111
+ priority = [service_priority, -1]
112
+ _add_service(priority, service)
113
+
114
+ end
115
+
116
+ end
117
+
118
+ def _add_service(priority, service)
119
+ unless @services.has_key?(priority)
120
+ @services[priority] = []
121
+ end
122
+
123
+ # services with the same priority are appended to the list
124
+ @services[priority] << service
125
+ end
126
+
127
+ end
128
+
@@ -5,14 +5,18 @@ require 'yadis/parsehtml'
5
5
  class YADISParseError < StandardError; end
6
6
  class YADISHTTPError < StandardError; end
7
7
 
8
+
9
+ # High level class for performing the Yadis protocol on a given URL. The
10
+ # YADIS.discover class method is a good place to get started in determining
11
+ # which services a URL supports.
8
12
  class YADIS
9
13
 
10
14
  @@ca_path = nil
11
15
  attr_accessor :uri, :xrds_uri, :xrds
12
16
 
13
17
  # Discover services for a given URI. Please note that no normalization
14
- # will be done to the passed in URI, it should be valid before calling
15
- # discover.
18
+ # will be done to the passed in URI, it should be a textually
19
+ # valid URI string before calling discover.
16
20
  #
17
21
  # Returns nil if no XRDS was found, or a YADIS object on success. This
18
22
  # method is essentially the same as YADIS.new, but does not raise any
@@ -46,9 +50,9 @@ class YADIS
46
50
  # or YADISHTTPError is the URI cannot be fetched.
47
51
  def initialize(uri)
48
52
  http = NetHTTPFetcher.new
49
- http.ca_path = @@ca_path if @@ca_path
50
-
53
+ http.ca_path = @@ca_path if @@ca_path
51
54
  headers = {'Accept' => 'application/xrds+xml'}
55
+
52
56
  response = http.get(uri, headers)
53
57
  raise YADISHTTPError, "Could not fetch #{uri}" if response.nil?
54
58
 
@@ -82,34 +86,23 @@ class YADIS
82
86
 
83
87
  # Returns an Array Service objects sorted by priority.
84
88
  def services
89
+ @xrds.services.each {|s| s.yadis = self}
85
90
  @xrds.services
86
91
  end
87
92
 
88
- # Returns an Array of Service objects that represent OpenID servers,
89
- # sorted by priority.
90
- #
91
- # Optionally accpets an Array of OpenID protocol versions.Versions
92
- # should be given as strings eg: '1.0'
93
- def openid_servers(versions=nil)
94
- versions.collect! {|v| v.gsub('.', '\.')} if versions
95
-
96
- base_url = 'http://openid.net/signon/'
97
- base_url += '(' + versions.join('|') + '){1}' if versions
93
+ # Returns a list of services, ordered by priority,
94
+ # that match the filter. filter is a Proc object that produces
95
+ # ServiceEnpoint objects, subclasses of ServiceEnpoint or nil.
96
+ # This method is useful for extracting several types of services while
97
+ # maintaining priority, for example you may write a filter Proc to extract
98
+ # OpenID and LID ServiceEnpoint objects.
99
+ def filter_services(filter)
100
+ # product a list of filtered ServiceEndpoint objects. filtered
101
+ # will contain a list of nil or ServiceEnpoint (subclasses) objects.
102
+ filtered = self.services.collect {|s| filter.call(s)}
98
103
 
99
- return @xrds.services.find_all {|s| s.service_type.match(base_url)}
104
+ # return all object in filtered that are not nil
105
+ return filtered.find_all {|s| s}
100
106
  end
101
107
 
102
- # Returns an Array of Service objects that represent LID servers,
103
- # sorted by priority.
104
- #
105
- # Optionally accpets an Array of LID protocol versions.Versions
106
- # should be given as strings eg: '1.0'
107
- def lid_servers(versions)
108
- versions.collect! {|v| v.gsub('.', '\.')} if versions
109
-
110
- base_url = 'http://lid.netmesh.org/sso/'
111
- base_url += '(' + versions.join('|') + '){1}' if versions
112
-
113
- return @xrds.services.find_all {|s| s.service_type.match(base_url)}
114
- end
115
108
  end
@@ -0,0 +1,104 @@
1
+ require 'yadis/xrds'
2
+ require 'yadis/fetcher'
3
+ require 'yadis/parsehtml'
4
+
5
+ class YADISParseError < StandardError; end
6
+ class YADISHTTPError < StandardError; end
7
+
8
+ class YADIS
9
+
10
+ @@ca_path = nil
11
+ attr_accessor :uri, :xrds_uri, :xrds
12
+
13
+ # Discover services for a given URI. Please note that no normalization
14
+ # will be done to the passed in URI, it should be a textually
15
+ # valid URI string before calling discover.
16
+ #
17
+ # Returns nil if no XRDS was found, or a YADIS object on success. This
18
+ # method is essentially the same as YADIS.new, but does not raise any
19
+ # exceptions.
20
+ def YADIS.discover(uri)
21
+ return nil unless uri
22
+ begin
23
+ return YADIS.new(uri)
24
+ rescue
25
+ return nil
26
+ end
27
+ end
28
+
29
+ # Set the path to a certificate authority pem file, for verifying
30
+ # server certificates of HTTPS pages. If you are interested in verifying
31
+ # certs like the mozilla web browser, have a look at the files here:
32
+ #
33
+ # http://curl.haxx.se/docs/caextract.html
34
+ def YADIS.ca_path=(ca_path)
35
+ ca_path = ca_path.to_s
36
+ if File.exists?(ca_path)
37
+ @@ca_path = ca_path
38
+ else
39
+ raise ArgumentError, "#{ca_path} is not a valid file path"
40
+ end
41
+ end
42
+
43
+ # Discover services for a URI using the Yadis protocol. +uri+ should
44
+ # be a valid URI represented as a string. This method may raise
45
+ # YADISParseError in the case of an invalid or unparsable XRDS file,
46
+ # or YADISHTTPError is the URI cannot be fetched.
47
+ def initialize(uri)
48
+ http = NetHTTPFetcher.new
49
+ http.ca_path = @@ca_path if @@ca_path
50
+ headers = {'Accept' => 'application/xrds+xml'}
51
+
52
+ response = http.get(uri, headers)
53
+ raise YADISHTTPError, "Could not fetch #{uri}" if response.nil?
54
+
55
+ uri, resp_payload = response
56
+ xrds_uri = uri
57
+
58
+ header = resp_payload['x-xrds-location']
59
+ header = resp_payload['x-yadis-location'] if header.nil?
60
+
61
+ if header
62
+ xrds_uri = header
63
+ response = http.get(xrds_uri)
64
+ raise YADISHTTPError, "Could not fetch XRDS #{xrds_uri}" if response.nil?
65
+ resp_payload = response[1]
66
+ end
67
+
68
+ unless resp_payload['content-type'] == 'application/xrds+xml'
69
+ loc = html_yadis_location(resp_payload.body)
70
+ unless loc.nil?
71
+ xrds_uri, resp_payload = http.get(loc)
72
+ end
73
+ end
74
+
75
+ xrds = XRDS.parse(resp_payload.body)
76
+ raise YADISParseError, "Bad XRDS" if xrds.nil?
77
+
78
+ @uri = uri
79
+ @xrds_uri = xrds_uri
80
+ @xrds = xrds
81
+ end
82
+
83
+ # Returns an Array Service objects sorted by priority.
84
+ def services
85
+ @xrds.services.each {|s| s.yadis = self}
86
+ @xrds.services
87
+ end
88
+
89
+ # Returns a list of services, ordered by priority,
90
+ # that match the filter. filter is a Proc object that produces
91
+ # ServiceEnpoint objects, subclasses of ServiceEnpoint or nil.
92
+ # This method is useful for extracting several types of services while
93
+ # maintaining priority, for example you may write a filter Proc to extract
94
+ # OpenID and LID ServiceEnpoint objects.
95
+ def filter_services(filter)
96
+ # product a list of filtered ServiceEndpoint objects. filtered
97
+ # will contain a list of nil or ServiceEnpoint (subclasses) objects.
98
+ filtered = self.services.collect {|s| filter.call(s)}
99
+
100
+ # return all object in filtered that are not nil
101
+ return filtered.find_all {|s| s}
102
+ end
103
+
104
+ end
@@ -39,4 +39,9 @@ class DiscoveryTestCase < Test::Unit::TestCase
39
39
  assert_nil(YADIS.discover('http://google.com/?q=huh'))
40
40
  end
41
41
 
42
+ def test_marshal
43
+ y = YADIS.discover('http://brian.myopenid.com/')
44
+ Marshal.dump(y)
45
+ end
46
+
42
47
  end
@@ -5,26 +5,25 @@ class XRDSTestCase < Test::Unit::TestCase
5
5
 
6
6
  def test_xrds_good
7
7
  File.open('data/brian.xrds') do |f|
8
- xrds = XRDS.parse(f.read)
8
+ xrds = XRDS.new(f.read)
9
9
  assert_not_nil(xrds)
10
10
  assert_equal(xrds.services.length, 1)
11
11
  end
12
12
  end
13
13
 
14
-
15
14
  def test_xrds_good_multi
16
15
  File.open('data/brian.multi.xrds') do |f|
17
- xrds = XRDS.parse(f.read)
16
+ xrds = XRDS.new(f.read)
18
17
  assert_not_nil(xrds)
19
18
  assert_equal(1, xrds.services.length)
20
19
  s = xrds.services[0]
21
- assert_equal('http://openid.net/signon/1.0', s.service_type)
20
+ assert s.service_types.member?('http://openid.net/signon/1.0')
22
21
  end
23
22
  end
24
23
 
25
24
  def test_xrds_good_uri_multi
26
25
  File.open('data/brian.multi_uri.xrds') do |f|
27
- xrds = XRDS.parse(f.read)
26
+ xrds = XRDS.new(f.read)
28
27
  assert_not_nil(xrds)
29
28
  assert_equal(2, xrds.services.length)
30
29
  end
@@ -38,4 +37,13 @@ class XRDSTestCase < Test::Unit::TestCase
38
37
  assert_nil(XRDS.parse('\000'))
39
38
  end
40
39
 
40
+ def test_marshal
41
+ File.open('data/brian.xrds') do |f|
42
+ xrds = XRDS.new(f.read)
43
+ s = Marshal.dump(xrds)
44
+ xrds2 = Marshal.load(s)
45
+ assert_equal(xrds.services, xrds2.services)
46
+ end
47
+ end
48
+
41
49
  end
@@ -3,44 +3,6 @@ require 'yadis'
3
3
 
4
4
  class YADISTestCase < Test::Unit::TestCase
5
5
 
6
- def test_yadis_openid
7
- File.open('data/brian.xrds') do |f|
8
- xrds = XRDS.parse(f.read)
9
-
10
- assert_not_nil(xrds)
11
- assert_equal(xrds.services.length, 1)
12
-
13
- uri = 'http://brian.myopenid.com/'
14
- xrds_uri = 'http://brian.myopenid.com/xrds'
15
- y = YADIS.new(uri)
16
- y.uri = uri
17
- y.xrds_uri = xrds_uri
18
- y.xrds_uri = xrds
19
-
20
- servers = y.openid_servers
21
- assert_equal(servers.length, 1)
22
-
23
- myopenid = servers[0]
24
- assert_equal(myopenid.service_type, 'http://openid.net/signon/1.0')
25
- assert_equal(myopenid.uri, 'http://www.myopenid.com/server')
26
- assert_equal(myopenid.other['openid:Delegate'], 'http://brian.myopenid.com/')
27
-
28
- servers = y.openid_servers(['1.0'])
29
- assert_equal(servers.length, 1)
30
-
31
- myopenid = servers[0]
32
- assert_equal(myopenid.service_type, 'http://openid.net/signon/1.0')
33
- assert_equal(myopenid.uri, 'http://www.myopenid.com/server')
34
- assert_equal(myopenid.other['openid:Delegate'], 'http://brian.myopenid.com/')
35
-
36
- servers = y.openid_servers(['0.9'])
37
- assert_equal(servers.length, 0)
38
-
39
- servers = y.openid_servers(['0.9', '1.1'])
40
- assert_equal(servers.length, 0)
41
- end
42
- end
43
-
44
6
  def test_yadis_lid
45
7
  # need an xrds with lid stuff in it for this part
46
8
  end
@@ -49,8 +11,8 @@ class YADISTestCase < Test::Unit::TestCase
49
11
  xrds = XRDS.parse(File.open('data/brian_priority.xrds').read)
50
12
  assert_equal(2, xrds.services.length)
51
13
  assert_equal('http://www.myopenid.com/server', xrds.services[0].uri)
52
- assert_equal('http://www.schtuff.com/?action=openid_server', xrds.services[1].uri)
53
-
14
+ assert_equal('http://www.schtuff.com/?action=openid_server',
15
+ xrds.services[1].uri)
54
16
  end
55
17
 
56
18
 
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.8.11
3
3
  specification_version: 1
4
4
  name: ruby-yadis
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.2.2
7
- date: 2006-03-22 00:00:00 -08:00
6
+ version: "0.3"
7
+ date: 2006-05-10 00:00:00 -07:00
8
8
  summary: A library for performing Yadis service discovery
9
9
  require_paths:
10
10
  - lib
@@ -31,11 +31,19 @@ files:
31
31
  - examples/openid.rb
32
32
  - lib/yadis.rb
33
33
  - lib/yadis
34
+ - lib/yadis.rb~
34
35
  - lib/yadis/parsehtml.rb
35
36
  - lib/yadis/xrds.rb
36
37
  - lib/yadis/yadis.rb
37
38
  - lib/yadis/fetcher.rb
38
39
  - lib/yadis/htmltokenizer.rb
40
+ - lib/yadis/manager.rb
41
+ - lib/yadis/yadis.rb~
42
+ - lib/yadis/service.rb
43
+ - lib/yadis/manager.rb~
44
+ - lib/yadis/service.rb~
45
+ - lib/yadis/xrds.rb~
46
+ - lib/yadis/fetcher.rb~
39
47
  - test/test_parse.rb
40
48
  - test/data
41
49
  - test/test_discovery.rb