diff --git a/.rspec b/.rspec new file mode 100644 index 0000000..27d79f4 --- /dev/null +++ b/.rspec @@ -0,0 +1,2 @@ +--colour +--format nested \ No newline at end of file diff --git a/Rakefile b/Rakefile index 6de76e6..5076760 100644 --- a/Rakefile +++ b/Rakefile @@ -10,7 +10,7 @@ begin gem.email = "michael@intridea.com" gem.homepage = "http://github.com/mbleigh/redfinger" gem.authors = ["Michael Bleigh"] - gem.add_dependency "rest-client" + gem.add_dependency "rest-client", ">= 1.5.0" gem.add_dependency "nokogiri", ">= 1.4.0" gem.add_dependency "hashie" gem.add_development_dependency "rspec", ">= 1.2.9" @@ -22,15 +22,10 @@ rescue LoadError puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler" end -require 'spec/rake/spectask' -Spec::Rake::SpecTask.new(:spec) do |spec| - spec.libs << 'lib' << 'spec' - spec.spec_files = FileList['spec/**/*_spec.rb'] -end +require 'rspec/core/rake_task' +RSpec::Core::RakeTask.new(:spec) -Spec::Rake::SpecTask.new(:rcov) do |spec| - spec.libs << 'lib' << 'spec' - spec.pattern = 'spec/**/*_spec.rb' +RSpec::Core::RakeTask.new(:rcov) do |spec| spec.rcov = true end diff --git a/VERSION b/VERSION index bcab45a..6c6aa7c 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.0.3 +0.1.0 \ No newline at end of file diff --git a/lib/redfinger/client.rb b/lib/redfinger/client.rb index cf44fa6..61b0733 100644 --- a/lib/redfinger/client.rb +++ b/lib/redfinger/client.rb @@ -4,16 +4,23 @@ module Redfinger class Client - attr_accessor :account, :domain, :uri_template + attr_accessor :account, :domain, :uri_template, :xrd_timeout, :xrd_open_timeout def initialize(email, uri_template = nil) - self.account = urify(email) + self.account = normalize(email) self.domain = account.split('@').last + + self.xrd_timeout = 10 + self.xrd_open_timeout = 5 end def finger self.uri_template ||= retrieve_template_from_xrd - Finger.new RestClient.get(swizzle).body + begin + return Finger.new self.account, RestClient.get(swizzle).body + rescue RestClient::ResourceNotFound + return Finger.new self.account, RestClient.get(swizzle(account_with_scheme)).body + end end def xrd_url(ssl = true) @@ -22,24 +29,28 @@ def xrd_url(ssl = true) private - def swizzle + def swizzle(account = nil) + account ||= self.account uri_template.gsub '{uri}', URI.escape(self.account) end def retrieve_template_from_xrd(ssl = true) - doc = Nokogiri::XML::Document.parse(RestClient.get(xrd_url(ssl)).body) - if doc.namespaces["xmlns:hm"] != "http://host-meta.net/xrd/1.0" + xrd_client = RestClient::Resource.new(xrd_url(ssl), + :timeout => self.xrd_timeout, + :open_timeout => self.xrd_open_timeout + ) + + doc = Nokogiri::XML::Document.parse(xrd_client.get.body) + if doc.namespaces["xmlns"] != "http://docs.oasis-open.org/ns/xri/xrd-1.0" # it's probably not finger, let's try without ssl # http://code.google.com/p/webfinger/wiki/WebFingerProtocol # says first ssl should be tried then without ssl, should fix issue #2 doc = Nokogiri::XML::Document.parse(RestClient.get(xrd_url(false)).body) end - unless doc.at_xpath('.//hm:Host').content == self.domain - raise Redfinger::SecurityException, "The XRD document's host did not match the account's host." - end doc.at('Link[rel=lrdd]').attribute('template').value - rescue Errno::ECONNREFUSED, RestClient::ResourceNotFound + rescue Errno::ECONNREFUSED, Errno::ETIMEDOUT, + RestClient::RequestTimeout, RestClient::ResourceNotFound, RestClient::Forbidden if ssl retrieve_template_from_xrd(false) else @@ -47,9 +58,13 @@ def retrieve_template_from_xrd(ssl = true) end end - def urify(email) - email = "acct:#{email}" unless email.include?("acct:") - email + def normalize(email) + email.sub! /^acct:/, '' + email.downcase + end + + def account_with_scheme + "acct:" + account end end end diff --git a/lib/redfinger/finger.rb b/lib/redfinger/finger.rb index 1b4dda9..6ca4b42 100644 --- a/lib/redfinger/finger.rb +++ b/lib/redfinger/finger.rb @@ -5,14 +5,14 @@ module Redfinger # special helpers that are availale to pull specific types # of URLs, see Redfinger::LinkHelpers class Finger - def initialize(xml) # :nodoc: + def initialize(subject, xml) # :nodoc: @doc = Nokogiri::XML::Document.parse(xml) - @subject = @doc.at_css('Subject').content + @subject = subject end # All of the links provided by the Webfinger response. def links - @links ||= @doc.css('Link').map{|l| Link.new(l)} + @links ||= @doc.css('Link').map{|l| Link.from_xml(l)} end def inspect # :nodoc: diff --git a/lib/redfinger/link.rb b/lib/redfinger/link.rb index 788f268..790b29c 100644 --- a/lib/redfinger/link.rb +++ b/lib/redfinger/link.rb @@ -3,10 +3,12 @@ module Redfinger class Link < Hashie::Mash - def initialize(xml_link) - self[:rel] = xml_link['rel'] - self[:href] = xml_link['href'] - self[:type] = xml_link['type'] + def self.from_xml(xml_link) + new_link = Link.new + new_link[:rel] = xml_link['rel'] + new_link[:href] = xml_link['href'] + new_link[:type] = xml_link['type'] + new_link end # Outputs the URL of the link, useful for using diff --git a/lib/redfinger/link_helpers.rb b/lib/redfinger/link_helpers.rb index 54d27b3..8c0f7de 100644 --- a/lib/redfinger/link_helpers.rb +++ b/lib/redfinger/link_helpers.rb @@ -25,7 +25,7 @@ def xfn protected def relmap(uri, substring=false) - @doc.css("Link[rel#{'^' if substring}=\"#{uri}\"]").map{|e| Link.new(e)} + @doc.css("Link[rel#{'^' if substring}=\"#{uri}\"]").map{|e| Link.from_xml(e)} end end end diff --git a/redfinger.gemspec b/redfinger.gemspec index c70a6c0..decd057 100644 --- a/redfinger.gemspec +++ b/redfinger.gemspec @@ -1,69 +1,67 @@ # Generated by jeweler # DO NOT EDIT THIS FILE DIRECTLY -# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command +# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec' # -*- encoding: utf-8 -*- Gem::Specification.new do |s| s.name = %q{redfinger} - s.version = "0.0.3" + s.version = "0.1.0" s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= s.authors = ["Michael Bleigh"] - s.date = %q{2010-02-13} + s.date = %q{2011-02-07} s.description = %q{A Ruby Webfinger client} s.email = %q{michael@intridea.com} s.extra_rdoc_files = [ "LICENSE", - "README.rdoc" + "README.rdoc" ] s.files = [ ".document", - ".gitignore", - "LICENSE", - "README.rdoc", - "Rakefile", - "VERSION", - "lib/redfinger.rb", - "lib/redfinger/client.rb", - "lib/redfinger/finger.rb", - "lib/redfinger/link.rb", - "lib/redfinger/link_helpers.rb", - "redfinger.gemspec", - "spec/redfinger/client_spec.rb", - "spec/redfinger_spec.rb", - "spec/spec.opts", - "spec/spec_helper.rb" + ".rspec", + "LICENSE", + "README.rdoc", + "Rakefile", + "VERSION", + "lib/redfinger.rb", + "lib/redfinger/client.rb", + "lib/redfinger/finger.rb", + "lib/redfinger/link.rb", + "lib/redfinger/link_helpers.rb", + "redfinger.gemspec", + "spec/redfinger/client_spec.rb", + "spec/redfinger_spec.rb", + "spec/spec.opts", + "spec/spec_helper.rb" ] s.homepage = %q{http://github.com/mbleigh/redfinger} - s.rdoc_options = ["--charset=UTF-8"] s.require_paths = ["lib"] - s.rubygems_version = %q{1.3.5} + s.rubygems_version = %q{1.5.0} s.summary = %q{A Ruby WebFinger client.} s.test_files = [ "spec/redfinger/client_spec.rb", - "spec/redfinger_spec.rb", - "spec/spec_helper.rb" + "spec/redfinger_spec.rb", + "spec/spec_helper.rb" ] if s.respond_to? :specification_version then - current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION s.specification_version = 3 - if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then - s.add_runtime_dependency(%q, [">= 0"]) + if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then + s.add_runtime_dependency(%q, [">= 1.5.0"]) s.add_runtime_dependency(%q, [">= 1.4.0"]) s.add_runtime_dependency(%q, [">= 0"]) s.add_development_dependency(%q, [">= 1.2.9"]) s.add_development_dependency(%q, [">= 0"]) else - s.add_dependency(%q, [">= 0"]) + s.add_dependency(%q, [">= 1.5.0"]) s.add_dependency(%q, [">= 1.4.0"]) s.add_dependency(%q, [">= 0"]) s.add_dependency(%q, [">= 1.2.9"]) s.add_dependency(%q, [">= 0"]) end else - s.add_dependency(%q, [">= 0"]) + s.add_dependency(%q, [">= 1.5.0"]) s.add_dependency(%q, [">= 1.4.0"]) s.add_dependency(%q, [">= 0"]) s.add_dependency(%q, [">= 1.2.9"]) diff --git a/spec/redfinger/client_spec.rb b/spec/redfinger/client_spec.rb index d3a8261..875f78b 100644 --- a/spec/redfinger/client_spec.rb +++ b/spec/redfinger/client_spec.rb @@ -4,12 +4,8 @@ class HaltSuccessError < StandardError; end describe Redfinger::Client do describe '#new' do - it 'should add acct: if it is not in URI form' do - Redfinger::Client.new('abc@example.com').account.should == 'acct:abc@example.com' - end - - it 'should not add acct: if it is already in URI form' do - Redfinger::Client.new('acct:abc@example.com').account.should == 'acct:abc@example.com' + it 'should remove acct: if it is already in URI form' do + Redfinger::Client.new('acct:abc@example.com').account.should == 'abc@example.com' end it 'should set the domain to whatevers after the @ sign' do @@ -29,6 +25,24 @@ class HaltSuccessError < StandardError; end stub_request(:get, 'http://example.com/.well-known/host-meta').to_raise(HaltSuccessError) lambda{Redfinger::Client.new('acct:abc@example.com').send(:retrieve_template_from_xrd)}.should raise_error(HaltSuccessError) end + + it 'should make an HTTP request if the HTTPS request times out in Net::HTTP' do + stub_request(:get, 'https://example.com/.well-known/host-meta').to_raise(Errno::ETIMEDOUT) + stub_request(:get, 'http://example.com/.well-known/host-meta').to_raise(HaltSuccessError) + lambda{Redfinger::Client.new('acct:abc@example.com').send(:retrieve_template_from_xrd)}.should raise_error(HaltSuccessError) + end + + it 'should make an HTTP request if the HTTPS request times out in RestClient' do + stub_request(:get, 'https://example.com/.well-known/host-meta').to_raise(RestClient::RequestTimeout) + stub_request(:get, 'http://example.com/.well-known/host-meta').to_raise(HaltSuccessError) + lambda{Redfinger::Client.new('acct:abc@example.com').send(:retrieve_template_from_xrd)}.should raise_error(HaltSuccessError) + end + + it 'should make an HTTP request if the server throws a 403 forbidden on the HTTPS request' do + stub_request(:get, 'https://example.com/.well-known/host-meta').to_return(:status => [403, "Forbidden"]) + stub_request(:get, 'http://example.com/.well-known/host-meta').to_raise(HaltSuccessError) + lambda{Redfinger::Client.new('acct:abc@example.com').send(:retrieve_template_from_xrd)}.should raise_error(HaltSuccessError) + end it 'should raise Redfinger::ResourceNotFound if HTTP fails as well' do stub_request(:get, 'https://example.com/.well-known/host-meta').to_raise(Errno::ECONNREFUSED) @@ -45,11 +59,6 @@ class HaltSuccessError < StandardError; end stub_request(:get, 'https://example.com/.well-known/host-meta').to_return(:status => 200, :body => host_xrd) Redfinger::Client.new('acct:abc@example.com').send(:retrieve_template_from_xrd).should == 'http://example.com/webfinger/?q={uri}' end - - it 'should raise a SecurityException if there is a host mismatch' do - stub_request(:get, 'https://franklin.com/.well-known/host-meta').to_return(:status => 200, :body => host_xrd) - lambda{Redfinger::Client.new('acct:abc@franklin.com').send(:retrieve_template_from_xrd)}.should raise_error(Redfinger::SecurityException) - end end describe '#finger' do diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index f001bdb..b9e1147 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -3,20 +3,16 @@ require 'rubygems' require 'redfinger' -require 'spec' -require 'spec/autorun' +require 'rspec' +require 'rspec/autorun' require 'webmock/rspec' -include WebMock +include WebMock::API def host_xrd <<-XML - - - - example.com + Resource Descriptor @@ -29,7 +25,6 @@ def finger_xrd <<-XML - acct:abc@example.com http://www.google.com/profiles/abc @@ -43,11 +38,11 @@ def finger_xrd XML end -def stub_success +def stub_success(address = 'abc@example.com') stub_request(:get, 'https://example.com/.well-known/host-meta').to_return(:status => 200, :body => host_xrd) - stub_request(:get, /webfinger\/\?q=.*/).to_return(:status => 200, :body => finger_xrd) + stub_request(:get, /webfinger\/\?q=#{address}/).to_return(:status => 200, :body => finger_xrd) end -Spec::Runner.configure do |config| +RSpec.configure do |config| end