mirror of https://github.com/mastodon/goldfinger
Compare commits
24 Commits
Author | SHA1 | Date |
---|---|---|
dependabot[bot] | d013213138 | |
Eugen Rochko | 4f585464ba | |
Eugen Rochko | 62d296bb98 | |
Eugen Rochko | 4c57f5978d | |
dependabot[bot] | d0f59cb65d | |
Tom Scott | 2deb9da0ab | |
Eugen Rochko | 1fca7e670e | |
MIYAGI Hikaru | d5f16ebeca | |
Eugen Rochko | 114c45f62d | |
Yamagishi Kazutoshi | 29dce59043 | |
Eugen Rochko | 13d8b032fd | |
Eugen Rochko | 36434d836e | |
Eugen Rochko | b6eeb547b3 | |
Eugen Rochko | cf17716d4c | |
Eugen Rochko | eddf330c22 | |
Eugen Rochko | 73a44507c6 | |
Eugen Rochko | 3fae9826cf | |
Eugen Rochko | 868cfbe0d8 | |
Eugen Rochko | 592248e4a7 | |
Eugen Rochko | f0a7a9baf3 | |
Eugen Rochko | b2b1b5c252 | |
Eugen Rochko | 8eb4e350b1 | |
Eugen Rochko | 237a9c2e81 | |
Eugen Rochko | c376932f31 |
|
@ -0,0 +1,41 @@
|
||||||
|
name: Ruby Gem
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- v*
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: Build + Publish
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@master
|
||||||
|
- name: Set up Ruby 2.6
|
||||||
|
uses: actions/setup-ruby@v1
|
||||||
|
with:
|
||||||
|
version: 2.6.x
|
||||||
|
|
||||||
|
- name: Publish to GPR
|
||||||
|
run: |
|
||||||
|
mkdir -p $HOME/.gem
|
||||||
|
touch $HOME/.gem/credentials
|
||||||
|
chmod 0600 $HOME/.gem/credentials
|
||||||
|
printf -- "---\n:github: Bearer ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials
|
||||||
|
gem build *.gemspec
|
||||||
|
gem push --KEY github --host https://rubygems.pkg.github.com/${OWNER} *.gem
|
||||||
|
env:
|
||||||
|
GEM_HOST_API_KEY: ${{secrets.GPR_AUTH_TOKEN}}
|
||||||
|
OWNER: tootsuite
|
||||||
|
|
||||||
|
- name: Publish to RubyGems
|
||||||
|
run: |
|
||||||
|
mkdir -p $HOME/.gem
|
||||||
|
touch $HOME/.gem/credentials
|
||||||
|
chmod 0600 $HOME/.gem/credentials
|
||||||
|
printf -- "---\n:rubygems_api_key: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials
|
||||||
|
gem build *.gemspec
|
||||||
|
gem push *.gem
|
||||||
|
env:
|
||||||
|
GEM_HOST_API_KEY: ${{secrets.RUBYGEMS_AUTH_TOKEN}}
|
|
@ -0,0 +1,11 @@
|
||||||
|
/.bundle/
|
||||||
|
/.yardoc
|
||||||
|
/Gemfile.lock
|
||||||
|
/_yardoc/
|
||||||
|
/coverage/
|
||||||
|
/doc/
|
||||||
|
/pkg/
|
||||||
|
/spec/reports/
|
||||||
|
/tmp/
|
||||||
|
*.gem
|
||||||
|
|
2
.rspec
2
.rspec
|
@ -1,3 +1,3 @@
|
||||||
--color
|
--color
|
||||||
--require spec_helper
|
--require spec_helper
|
||||||
--format NyanCatFormatter
|
--format Fuubar
|
||||||
|
|
|
@ -0,0 +1,100 @@
|
||||||
|
AllCops:
|
||||||
|
TargetRubyVersion: 2.3
|
||||||
|
Exclude:
|
||||||
|
- 'spec/**/*'
|
||||||
|
- 'bin/*'
|
||||||
|
- 'Rakefile'
|
||||||
|
- 'vendor/**/*'
|
||||||
|
|
||||||
|
Bundler/OrderedGems:
|
||||||
|
Enabled: false
|
||||||
|
|
||||||
|
Layout/AccessModifierIndentation:
|
||||||
|
EnforcedStyle: indent
|
||||||
|
|
||||||
|
Layout/EmptyLineAfterMagicComment:
|
||||||
|
Enabled: false
|
||||||
|
|
||||||
|
Layout/SpaceInsideHashLiteralBraces:
|
||||||
|
EnforcedStyle: space
|
||||||
|
|
||||||
|
Metrics/AbcSize:
|
||||||
|
Max: 100
|
||||||
|
|
||||||
|
Metrics/BlockNesting:
|
||||||
|
Max: 3
|
||||||
|
|
||||||
|
Metrics/ClassLength:
|
||||||
|
CountComments: false
|
||||||
|
Max: 200
|
||||||
|
|
||||||
|
Metrics/CyclomaticComplexity:
|
||||||
|
Max: 15
|
||||||
|
|
||||||
|
Metrics/LineLength:
|
||||||
|
AllowURI: true
|
||||||
|
Enabled: false
|
||||||
|
|
||||||
|
Metrics/MethodLength:
|
||||||
|
CountComments: false
|
||||||
|
Max: 55
|
||||||
|
|
||||||
|
Metrics/ModuleLength:
|
||||||
|
CountComments: false
|
||||||
|
Max: 200
|
||||||
|
|
||||||
|
Metrics/ParameterLists:
|
||||||
|
Max: 4
|
||||||
|
CountKeywordArgs: true
|
||||||
|
|
||||||
|
Metrics/PerceivedComplexity:
|
||||||
|
Max: 10
|
||||||
|
|
||||||
|
Rails:
|
||||||
|
Enabled: true
|
||||||
|
|
||||||
|
Rails/HasAndBelongsToMany:
|
||||||
|
Enabled: false
|
||||||
|
|
||||||
|
Rails/SkipsModelValidations:
|
||||||
|
Enabled: false
|
||||||
|
|
||||||
|
Style/ClassAndModuleChildren:
|
||||||
|
Enabled: false
|
||||||
|
|
||||||
|
Style/CollectionMethods:
|
||||||
|
Enabled: true
|
||||||
|
PreferredMethods:
|
||||||
|
find_all: 'select'
|
||||||
|
|
||||||
|
Style/Documentation:
|
||||||
|
Enabled: false
|
||||||
|
|
||||||
|
Style/DoubleNegation:
|
||||||
|
Enabled: true
|
||||||
|
|
||||||
|
Style/FrozenStringLiteralComment:
|
||||||
|
Enabled: true
|
||||||
|
|
||||||
|
Style/GuardClause:
|
||||||
|
Enabled: false
|
||||||
|
|
||||||
|
Style/Lambda:
|
||||||
|
Enabled: false
|
||||||
|
|
||||||
|
Style/PercentLiteralDelimiters:
|
||||||
|
PreferredDelimiters:
|
||||||
|
'%i': '()'
|
||||||
|
'%w': '()'
|
||||||
|
|
||||||
|
Style/PerlBackrefs:
|
||||||
|
AutoCorrect: false
|
||||||
|
|
||||||
|
Style/RegexpLiteral:
|
||||||
|
Enabled: false
|
||||||
|
|
||||||
|
Style/SymbolArray:
|
||||||
|
Enabled: false
|
||||||
|
|
||||||
|
Style/TrailingCommaInLiteral:
|
||||||
|
EnforcedStyleForMultiline: 'comma'
|
|
@ -1,2 +1 @@
|
||||||
2.2.0
|
2.5.0
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
language: ruby
|
||||||
|
cache: bundler
|
||||||
|
|
||||||
|
notifications:
|
||||||
|
email: false
|
||||||
|
|
||||||
|
rvm:
|
||||||
|
- 2.3.1
|
||||||
|
- 2.4.1
|
||||||
|
|
||||||
|
bundler_args: --without development --retry=3 --jobs=3
|
||||||
|
|
||||||
|
script: bundle exec rake
|
7
Gemfile
7
Gemfile
|
@ -1,11 +1,14 @@
|
||||||
source "https://rubygems.org"
|
source "https://rubygems.org"
|
||||||
|
|
||||||
gem 'pry'
|
group :development, :test do
|
||||||
|
gem 'pry', '>= 0.10.3'
|
||||||
|
end
|
||||||
|
|
||||||
group :test do
|
group :test do
|
||||||
gem 'rspec', '>= 3.0'
|
gem 'rspec', '>= 3.0'
|
||||||
gem 'nyan-cat-formatter'
|
gem 'fuubar'
|
||||||
gem 'webmock'
|
gem 'webmock'
|
||||||
|
gem 'rake'
|
||||||
end
|
end
|
||||||
|
|
||||||
gemspec
|
gemspec
|
||||||
|
|
90
Gemfile.lock
90
Gemfile.lock
|
@ -1,60 +1,72 @@
|
||||||
PATH
|
PATH
|
||||||
remote: .
|
remote: .
|
||||||
specs:
|
specs:
|
||||||
goldfinger (0.1.1)
|
goldfinger (2.1.1)
|
||||||
addressable (~> 2.4)
|
addressable (~> 2.5)
|
||||||
http (~> 1.0)
|
http (~> 4.0)
|
||||||
nokogiri (~> 1.6)
|
nokogiri (~> 1.8)
|
||||||
|
oj (~> 3.0)
|
||||||
|
|
||||||
GEM
|
GEM
|
||||||
remote: https://rubygems.org/
|
remote: https://rubygems.org/
|
||||||
specs:
|
specs:
|
||||||
addressable (2.4.0)
|
addressable (2.5.2)
|
||||||
coderay (1.1.0)
|
public_suffix (>= 2.0.2, < 4.0)
|
||||||
|
coderay (1.1.1)
|
||||||
crack (0.4.3)
|
crack (0.4.3)
|
||||||
safe_yaml (~> 1.0.0)
|
safe_yaml (~> 1.0.0)
|
||||||
diff-lcs (1.2.5)
|
diff-lcs (1.3)
|
||||||
domain_name (0.5.20160128)
|
domain_name (0.5.20190701)
|
||||||
unf (>= 0.0.5, < 1.0.0)
|
unf (>= 0.0.5, < 1.0.0)
|
||||||
hashdiff (0.3.0)
|
ffi (1.12.2)
|
||||||
http (1.0.2)
|
ffi-compiler (1.0.1)
|
||||||
|
ffi (>= 1.0.0)
|
||||||
|
rake
|
||||||
|
fuubar (2.2.0)
|
||||||
|
rspec-core (~> 3.0)
|
||||||
|
ruby-progressbar (~> 1.4)
|
||||||
|
hashdiff (0.3.4)
|
||||||
|
http (4.3.0)
|
||||||
addressable (~> 2.3)
|
addressable (~> 2.3)
|
||||||
http-cookie (~> 1.0)
|
http-cookie (~> 1.0)
|
||||||
http-form_data (~> 1.0.1)
|
http-form_data (~> 2.2)
|
||||||
http_parser.rb (~> 0.6.0)
|
http-parser (~> 1.2.0)
|
||||||
http-cookie (1.0.2)
|
http-cookie (1.0.3)
|
||||||
domain_name (~> 0.5)
|
domain_name (~> 0.5)
|
||||||
http-form_data (1.0.1)
|
http-form_data (2.2.0)
|
||||||
http_parser.rb (0.6.0)
|
http-parser (1.2.1)
|
||||||
|
ffi-compiler (>= 1.0, < 2.0)
|
||||||
method_source (0.8.2)
|
method_source (0.8.2)
|
||||||
mini_portile2 (2.0.0)
|
mini_portile2 (2.4.0)
|
||||||
nokogiri (1.6.7.2)
|
nokogiri (1.10.8)
|
||||||
mini_portile2 (~> 2.0.0.rc2)
|
mini_portile2 (~> 2.4.0)
|
||||||
nyan-cat-formatter (0.11)
|
oj (3.10.2)
|
||||||
rspec (>= 2.99, >= 2.14.2, < 4)
|
pry (0.10.4)
|
||||||
pry (0.10.1)
|
|
||||||
coderay (~> 1.1.0)
|
coderay (~> 1.1.0)
|
||||||
method_source (~> 0.8.1)
|
method_source (~> 0.8.1)
|
||||||
slop (~> 3.4)
|
slop (~> 3.4)
|
||||||
rspec (3.4.0)
|
public_suffix (3.0.3)
|
||||||
rspec-core (~> 3.4.0)
|
rake (12.3.3)
|
||||||
rspec-expectations (~> 3.4.0)
|
rspec (3.6.0)
|
||||||
rspec-mocks (~> 3.4.0)
|
rspec-core (~> 3.6.0)
|
||||||
rspec-core (3.4.2)
|
rspec-expectations (~> 3.6.0)
|
||||||
rspec-support (~> 3.4.0)
|
rspec-mocks (~> 3.6.0)
|
||||||
rspec-expectations (3.4.0)
|
rspec-core (3.6.0)
|
||||||
|
rspec-support (~> 3.6.0)
|
||||||
|
rspec-expectations (3.6.0)
|
||||||
diff-lcs (>= 1.2.0, < 2.0)
|
diff-lcs (>= 1.2.0, < 2.0)
|
||||||
rspec-support (~> 3.4.0)
|
rspec-support (~> 3.6.0)
|
||||||
rspec-mocks (3.4.1)
|
rspec-mocks (3.6.0)
|
||||||
diff-lcs (>= 1.2.0, < 2.0)
|
diff-lcs (>= 1.2.0, < 2.0)
|
||||||
rspec-support (~> 3.4.0)
|
rspec-support (~> 3.6.0)
|
||||||
rspec-support (3.4.1)
|
rspec-support (3.6.0)
|
||||||
|
ruby-progressbar (1.8.1)
|
||||||
safe_yaml (1.0.4)
|
safe_yaml (1.0.4)
|
||||||
slop (3.6.0)
|
slop (3.6.0)
|
||||||
unf (0.1.4)
|
unf (0.1.4)
|
||||||
unf_ext
|
unf_ext
|
||||||
unf_ext (0.0.7.2)
|
unf_ext (0.0.7.6)
|
||||||
webmock (1.22.6)
|
webmock (3.0.1)
|
||||||
addressable (>= 2.3.6)
|
addressable (>= 2.3.6)
|
||||||
crack (>= 0.3.2)
|
crack (>= 0.3.2)
|
||||||
hashdiff
|
hashdiff
|
||||||
|
@ -63,9 +75,13 @@ PLATFORMS
|
||||||
ruby
|
ruby
|
||||||
|
|
||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
bundler (~> 1.3)
|
bundler (~> 1.15)
|
||||||
|
fuubar
|
||||||
goldfinger!
|
goldfinger!
|
||||||
nyan-cat-formatter
|
pry (>= 0.10.3)
|
||||||
pry
|
rake
|
||||||
rspec (>= 3.0)
|
rspec (>= 3.0)
|
||||||
webmock
|
webmock
|
||||||
|
|
||||||
|
BUNDLED WITH
|
||||||
|
1.16.1
|
||||||
|
|
11
README.md
11
README.md
|
@ -1,11 +1,16 @@
|
||||||
Goldfinger, a Webfinger client for Ruby
|
Goldfinger, a WebFinger client for Ruby
|
||||||
=======================================
|
=======================================
|
||||||
|
|
||||||
[![Gem Version](http://img.shields.io/gem/v/goldfinger.svg)][gem]
|
[![Gem Version](http://img.shields.io/gem/v/goldfinger.svg)][gem]
|
||||||
|
[![Build Status](http://img.shields.io/travis/tootsuite/goldfinger.svg)][travis]
|
||||||
|
|
||||||
[gem]: https://rubygems.org/gems/goldfinger
|
[gem]: https://rubygems.org/gems/goldfinger
|
||||||
|
[travis]: https://travis-ci.org/tootsuite/goldfinger
|
||||||
|
|
||||||
A Webfinger client for Ruby. Supports `application/xrd+xml` and `application/jrd+json` responses. Raises `Goldfinger::NotFoundError` on failure to fetch the Webfinger or XRD data, or `Goldfinger::SSLError` if something is wrong with the HTTPS connection it uses.
|
A WebFinger client for Ruby. Supports `application/xrd+xml` and `application/jrd+json` responses. Raises `Goldfinger::NotFoundError` on failure to fetch the Webfinger or XRD data, can also raise `HTTP:Error` or `OpenSSL::SSL::SSLError` if something is wrong with the HTTPS connection it uses.
|
||||||
|
|
||||||
|
- **Does not** fall back to HTTP if HTTPS is not available
|
||||||
|
- **Does** check host-meta XRD, but *only* if the standard WebFinger path yielded no result
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
|
@ -26,4 +31,4 @@ A Webfinger client for Ruby. Supports `application/xrd+xml` and `application/jrd
|
||||||
|
|
||||||
## RFC support
|
## RFC support
|
||||||
|
|
||||||
The official Webfinger RFC is [7033](https://tools.ietf.org/html/rfc7033).
|
The official WebFinger RFC is [7033](https://tools.ietf.org/html/rfc7033).
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
require 'rspec/core/rake_task'
|
||||||
|
require 'bundler/gem_tasks'
|
||||||
|
|
||||||
|
RSpec::Core::RakeTask.new(:spec) do |task|
|
||||||
|
task.rspec_opts = ['--color', '--format', 'documentation']
|
||||||
|
end
|
||||||
|
|
||||||
|
task :default => :spec
|
|
@ -1,20 +1,23 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
Gem::Specification.new do |s|
|
Gem::Specification.new do |s|
|
||||||
s.name = 'goldfinger'
|
s.name = 'goldfinger'
|
||||||
s.version = '1.0.0'
|
s.version = '2.1.1'
|
||||||
s.platform = Gem::Platform::RUBY
|
s.platform = Gem::Platform::RUBY
|
||||||
s.required_ruby_version = '>= 2.0.0'
|
s.required_ruby_version = '>= 2.3.0'
|
||||||
s.date = '2016-02-17'
|
s.date = '2016-02-17'
|
||||||
s.summary = "A Webfinger utility for Ruby"
|
s.summary = 'A Webfinger utility for Ruby'
|
||||||
s.description = "A Webfinger utility for Ruby"
|
s.description = 'A Webfinger utility for Ruby'
|
||||||
s.authors = ["Eugen Rochko"]
|
s.authors = ['Eugen Rochko']
|
||||||
s.email = 'eugen@zeonfederated.com'
|
s.email = 'eugen@zeonfederated.com'
|
||||||
s.files = `git ls-files lib LICENSE README.md`.split($RS)
|
s.files = `git ls-files lib LICENSE README.md`.split($RS)
|
||||||
s.homepage = 'https://github.com/Gargron/goldfinger'
|
s.homepage = 'https://github.com/tootsuite/goldfinger'
|
||||||
s.licenses = ['MIT']
|
s.licenses = ['MIT']
|
||||||
|
|
||||||
s.add_dependency('http', '~> 1.0')
|
s.add_dependency('http', '~> 4.0')
|
||||||
s.add_dependency('addressable', '~> 2.4')
|
s.add_dependency('addressable', '~> 2.5')
|
||||||
s.add_dependency('nokogiri', '~> 1.6')
|
s.add_dependency('nokogiri', '~> 1.8')
|
||||||
|
s.add_dependency('oj', '~> 3.0')
|
||||||
|
|
||||||
s.add_development_dependency('bundler', '~> 1.3')
|
s.add_development_dependency('bundler', '~> 1.15')
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'goldfinger/request'
|
require 'goldfinger/request'
|
||||||
require 'goldfinger/link'
|
require 'goldfinger/link'
|
||||||
require 'goldfinger/result'
|
require 'goldfinger/result'
|
||||||
|
@ -11,16 +13,14 @@ module Goldfinger
|
||||||
class NotFoundError < Error
|
class NotFoundError < Error
|
||||||
end
|
end
|
||||||
|
|
||||||
class SSLError < Error
|
|
||||||
end
|
|
||||||
|
|
||||||
# Returns result for the Webfinger query
|
# Returns result for the Webfinger query
|
||||||
#
|
#
|
||||||
# @raise [Goldfinger::NotFoundError] Error raised when the Webfinger resource could not be retrieved
|
# @raise [Goldfinger::NotFoundError] Error raised when the Webfinger resource could not be retrieved
|
||||||
# @raise [Goldfinger::SSLError] Error raised when there was a SSL error when fetching the resource
|
# @raise [Goldfinger::SSLError] Error raised when there was a SSL error when fetching the resource
|
||||||
# @param uri [String] A full resource identifier in the format acct:user@example.com
|
# @param uri [String] A full resource identifier in the format acct:user@example.com
|
||||||
|
# @param opts [Hash] Options passed to HTTP.rb client
|
||||||
# @return [Goldfinger::Result]
|
# @return [Goldfinger::Result]
|
||||||
def self.finger(uri)
|
def self.finger(uri, opts = {})
|
||||||
Goldfinger::Client.new(uri).finger
|
Goldfinger::Client.new(uri, opts).finger
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'addressable'
|
require 'addressable'
|
||||||
require 'nokogiri'
|
require 'nokogiri'
|
||||||
|
|
||||||
|
@ -5,36 +7,43 @@ module Goldfinger
|
||||||
class Client
|
class Client
|
||||||
include Goldfinger::Utils
|
include Goldfinger::Utils
|
||||||
|
|
||||||
def initialize(uri)
|
def initialize(uri, opts = {})
|
||||||
@uri = uri
|
@uri = uri
|
||||||
|
@ssl = opts.delete(:ssl) { true }
|
||||||
|
@scheme = @ssl ? 'https' : 'http'
|
||||||
|
@opts = opts
|
||||||
end
|
end
|
||||||
|
|
||||||
def finger
|
def finger
|
||||||
ssl = true
|
response = perform_get(standard_url, @opts)
|
||||||
|
|
||||||
begin
|
return finger_from_template if response.code != 200
|
||||||
_, template = perform_get(url(ssl))
|
|
||||||
rescue HTTP::Error
|
|
||||||
if ssl
|
|
||||||
ssl = false
|
|
||||||
retry
|
|
||||||
else
|
|
||||||
raise Goldfinger::NotFoundError
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
headers, body = perform_get(url_from_template(template))
|
Goldfinger::Result.new(response)
|
||||||
Goldfinger::Result.new(headers, body)
|
rescue Addressable::URI::InvalidURIError
|
||||||
rescue HTTP::Error
|
raise Goldfinger::NotFoundError, 'Invalid URI'
|
||||||
raise Goldfinger::NotFoundError
|
|
||||||
rescue OpenSSL::SSL::SSLError
|
|
||||||
raise Goldfinger::SSLError
|
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def url(ssl = true)
|
def finger_from_template
|
||||||
"http#{'s' if ssl}://#{domain}/.well-known/host-meta"
|
template = perform_get(url, @opts)
|
||||||
|
|
||||||
|
raise Goldfinger::NotFoundError, 'No host-meta on the server' if template.code != 200
|
||||||
|
|
||||||
|
response = perform_get(url_from_template(template.body), @opts)
|
||||||
|
|
||||||
|
raise Goldfinger::NotFoundError, 'No such user on the server' if response.code != 200
|
||||||
|
|
||||||
|
Goldfinger::Result.new(response)
|
||||||
|
end
|
||||||
|
|
||||||
|
def url
|
||||||
|
"#{@scheme}://#{domain}/.well-known/host-meta"
|
||||||
|
end
|
||||||
|
|
||||||
|
def standard_url
|
||||||
|
"#{@scheme}://#{domain}/.well-known/webfinger?resource=#{@uri}"
|
||||||
end
|
end
|
||||||
|
|
||||||
def url_from_template(template)
|
def url_from_template(template)
|
||||||
|
@ -43,8 +52,9 @@ module Goldfinger
|
||||||
|
|
||||||
raise Goldfinger::NotFoundError if links.empty?
|
raise Goldfinger::NotFoundError if links.empty?
|
||||||
|
|
||||||
url = Addressable::Template.new(links.first.attribute('template').value)
|
links.first.attribute('template').value.gsub('{uri}', @uri)
|
||||||
url.expand({ uri: @uri }).to_s
|
rescue Nokogiri::XML::XPath::SyntaxError
|
||||||
|
raise Goldfinger::Error, "Bad XML: #{template}"
|
||||||
end
|
end
|
||||||
|
|
||||||
def domain
|
def domain
|
||||||
|
|
|
@ -1,15 +1,20 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
module Goldfinger
|
module Goldfinger
|
||||||
# @!attribute [r] href
|
# @!attribute [r] href
|
||||||
# @return [String] The href the link points to
|
# @return [String] The href the link points to
|
||||||
|
# @!attribute [r] template
|
||||||
|
# @return [String] The template the link contains
|
||||||
# @!attribute [r] type
|
# @!attribute [r] type
|
||||||
# @return [String] The mime type of the link
|
# @return [String] The mime type of the link
|
||||||
# @!attribute [r] rel
|
# @!attribute [r] rel
|
||||||
# @return [String] The relation descriptor of the link
|
# @return [String] The relation descriptor of the link
|
||||||
class Link
|
class Link
|
||||||
attr_reader :href, :type, :rel
|
attr_reader :href, :template, :type, :rel
|
||||||
|
|
||||||
def initialize(a)
|
def initialize(a)
|
||||||
@href = a[:href]
|
@href = a[:href]
|
||||||
|
@template = a[:template]
|
||||||
@type = a[:type]
|
@type = a[:type]
|
||||||
@rel = a[:rel]
|
@rel = a[:rel]
|
||||||
@titles = a[:titles]
|
@titles = a[:titles]
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'http'
|
require 'http'
|
||||||
require 'addressable'
|
require 'addressable'
|
||||||
|
|
||||||
|
@ -10,14 +12,13 @@ module Goldfinger
|
||||||
end
|
end
|
||||||
|
|
||||||
def perform
|
def perform
|
||||||
response = http_client.request(@request_method, @uri.to_s, @options)
|
http_client.request(@request_method, @uri.to_s, @options)
|
||||||
[response.headers, response.body]
|
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def http_client
|
def http_client
|
||||||
HTTP
|
HTTP.timeout(write: 60, connect: 20, read: 60).follow
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,8 +1,26 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'oj'
|
||||||
|
|
||||||
module Goldfinger
|
module Goldfinger
|
||||||
|
# @!attribute [r] subject
|
||||||
|
# @return [String] URI that identifies the entity that the JRD describes.
|
||||||
|
# @!attribute [r] aliases
|
||||||
|
# @return [Array] Zero or more URI strings that identify the same entity as the "subject" URI.
|
||||||
class Result
|
class Result
|
||||||
def initialize(headers, body)
|
MIME_TYPES = [
|
||||||
@mime_type = headers.get(HTTP::Headers::CONTENT_TYPE).first
|
'application/jrd+json',
|
||||||
@body = body
|
'application/json',
|
||||||
|
'application/xrd+xml',
|
||||||
|
'application/xml',
|
||||||
|
'text/xml',
|
||||||
|
].freeze
|
||||||
|
|
||||||
|
attr_reader :subject, :aliases
|
||||||
|
|
||||||
|
def initialize(response)
|
||||||
|
@mime_type = response.mime_type
|
||||||
|
@body = response.body
|
||||||
@subject = nil
|
@subject = nil
|
||||||
@aliases = []
|
@aliases = []
|
||||||
@links = {}
|
@links = {}
|
||||||
|
@ -11,20 +29,6 @@ module Goldfinger
|
||||||
parse
|
parse
|
||||||
end
|
end
|
||||||
|
|
||||||
# The value of the "subject" member is a URI that identifies the entity
|
|
||||||
# that the JRD describes.
|
|
||||||
# @return [String]
|
|
||||||
def subject
|
|
||||||
@subject
|
|
||||||
end
|
|
||||||
|
|
||||||
# The "aliases" array is an array of zero or more URI strings that
|
|
||||||
# identify the same entity as the "subject" URI.
|
|
||||||
# @return [Array]
|
|
||||||
def aliases
|
|
||||||
@aliases
|
|
||||||
end
|
|
||||||
|
|
||||||
# The "properties" object comprises zero or more name/value pairs whose
|
# The "properties" object comprises zero or more name/value pairs whose
|
||||||
# names are URIs (referred to as "property identifiers") and whose
|
# names are URIs (referred to as "property identifiers") and whose
|
||||||
# values are strings or nil.
|
# values are strings or nil.
|
||||||
|
@ -62,13 +66,15 @@ module Goldfinger
|
||||||
case @mime_type
|
case @mime_type
|
||||||
when 'application/jrd+json', 'application/json'
|
when 'application/jrd+json', 'application/json'
|
||||||
parse_json
|
parse_json
|
||||||
when 'application/xrd+xml'
|
when 'application/xrd+xml', 'application/xml', 'text/xml'
|
||||||
parse_xml
|
parse_xml
|
||||||
|
else
|
||||||
|
raise Goldfinger::Error, "Invalid response mime type: #{@mime_type}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def parse_json
|
def parse_json
|
||||||
json = JSON.parse(@body)
|
json = Oj.load(@body.to_s, mode: :null)
|
||||||
|
|
||||||
@subject = json['subject']
|
@subject = json['subject']
|
||||||
@aliases = json['aliases'] || []
|
@aliases = json['aliases'] || []
|
||||||
|
@ -84,7 +90,7 @@ module Goldfinger
|
||||||
xml = Nokogiri::XML(@body)
|
xml = Nokogiri::XML(@body)
|
||||||
|
|
||||||
@subject = xml.at_xpath('//xmlns:Subject').content
|
@subject = xml.at_xpath('//xmlns:Subject').content
|
||||||
@aliases = xml.xpath('//xmlns:Alias').map { |a| a.content }
|
@aliases = xml.xpath('//xmlns:Alias').map(&:content)
|
||||||
|
|
||||||
properties = xml.xpath('/xmlns:XRD/xmlns:Property')
|
properties = xml.xpath('/xmlns:XRD/xmlns:Property')
|
||||||
properties.each { |prop| @properties[prop.attribute('type').value] = prop.attribute('nil') ? nil : prop.content }
|
properties.each { |prop| @properties[prop.attribute('type').value] = prop.attribute('nil') ? nil : prop.content }
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
module Goldfinger
|
module Goldfinger
|
||||||
module Utils
|
module Utils
|
||||||
def perform_get(path, options = {})
|
def perform_get(path, options = {})
|
||||||
|
|
|
@ -2,8 +2,7 @@ describe Goldfinger::Client do
|
||||||
context 'with HTTPS available' do
|
context 'with HTTPS available' do
|
||||||
describe '#finger' do
|
describe '#finger' do
|
||||||
before do
|
before do
|
||||||
stub_request(:get, 'https://quitter.no/.well-known/host-meta').to_return(body: fixture('quitter.no_.well-known_host-meta'))
|
stub_request(:get, 'https://quitter.no/.well-known/webfinger?resource=acct:gargron@quitter.no').to_return(body: fixture('quitter.no_.well-known_webfinger.json'), headers: { content_type: 'application/jrd+json' })
|
||||||
stub_request(:get, 'https://quitter.no/.well-known/webfinger?resource=acct:gargron@quitter.no').to_return(body: fixture('quitter.no_.well-known_webfinger.json'))
|
|
||||||
end
|
end
|
||||||
|
|
||||||
subject { Goldfinger::Client.new('acct:gargron@quitter.no') }
|
subject { Goldfinger::Client.new('acct:gargron@quitter.no') }
|
||||||
|
@ -11,21 +10,25 @@ describe Goldfinger::Client do
|
||||||
it 'returns a result' do
|
it 'returns a result' do
|
||||||
expect(subject.finger).to be_instance_of Goldfinger::Result
|
expect(subject.finger).to be_instance_of Goldfinger::Result
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'performs a single HTTP request' do
|
||||||
|
subject.finger
|
||||||
|
expect(a_request(:get, 'https://quitter.no/.well-known/webfinger?resource=acct:gargron@quitter.no')).to have_been_made.once
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with only HTTP available' do
|
context 'with only HTTP available' do
|
||||||
describe '#finger' do
|
describe '#finger' do
|
||||||
before do
|
before do
|
||||||
stub_request(:get, 'https://quitter.no/.well-known/host-meta').to_raise(HTTP::Error)
|
stub_request(:get, 'https://quitter.no/.well-known/webfinger?resource=acct:gargron@quitter.no').to_raise(HTTP::Error)
|
||||||
stub_request(:get, 'http://quitter.no/.well-known/host-meta').to_return(body: fixture('quitter.no_.well-known_host-meta'))
|
stub_request(:get, 'http://quitter.no/.well-known/webfinger?resource=acct:gargron@quitter.no').to_return(body: fixture('quitter.no_.well-known_webfinger.json'), headers: { content_type: 'application/jrd+json' })
|
||||||
stub_request(:get, 'https://quitter.no/.well-known/webfinger?resource=acct:gargron@quitter.no').to_return(body: fixture('quitter.no_.well-known_webfinger.json'))
|
|
||||||
end
|
end
|
||||||
|
|
||||||
subject { Goldfinger::Client.new('acct:gargron@quitter.no') }
|
subject { Goldfinger::Client.new('acct:gargron@quitter.no') }
|
||||||
|
|
||||||
it 'returns a result' do
|
it 'raises an error' do
|
||||||
expect(subject.finger).to be_instance_of Goldfinger::Result
|
expect { subject.finger }.to raise_error HTTP::Error
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -33,6 +36,8 @@ describe Goldfinger::Client do
|
||||||
context 'with XRD missing' do
|
context 'with XRD missing' do
|
||||||
describe '#finger' do
|
describe '#finger' do
|
||||||
before do
|
before do
|
||||||
|
stub_request(:get, 'https://quitter.no/.well-known/webfinger?resource=acct:gargron@quitter.no').to_raise(HTTP::Error)
|
||||||
|
stub_request(:get, 'http://quitter.no/.well-known/webfinger?resource=acct:gargron@quitter.no').to_raise(HTTP::Error)
|
||||||
stub_request(:get, 'https://quitter.no/.well-known/host-meta').to_raise(HTTP::Error)
|
stub_request(:get, 'https://quitter.no/.well-known/host-meta').to_raise(HTTP::Error)
|
||||||
stub_request(:get, 'http://quitter.no/.well-known/host-meta').to_raise(HTTP::Error)
|
stub_request(:get, 'http://quitter.no/.well-known/host-meta').to_raise(HTTP::Error)
|
||||||
end
|
end
|
||||||
|
@ -40,7 +45,7 @@ describe Goldfinger::Client do
|
||||||
subject { Goldfinger::Client.new('acct:gargron@quitter.no') }
|
subject { Goldfinger::Client.new('acct:gargron@quitter.no') }
|
||||||
|
|
||||||
it 'raises an error' do
|
it 'raises an error' do
|
||||||
expect {subject.finger }.to raise_error(Goldfinger::NotFoundError)
|
expect { subject.finger }.to raise_error(HTTP::Error)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,8 +6,8 @@ describe Goldfinger::Request do
|
||||||
|
|
||||||
subject { Goldfinger::Request.new(:get, 'http://example.com').perform }
|
subject { Goldfinger::Request.new(:get, 'http://example.com').perform }
|
||||||
|
|
||||||
it 'returns the body' do
|
it 'returns a http response' do
|
||||||
expect(subject.last.to_s).to eql 'OK'
|
expect(subject).to be_a HTTP::Response
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
describe Goldfinger::Result do
|
describe Goldfinger::Result do
|
||||||
shared_examples 'a working finger result' do
|
shared_examples 'a working finger result' do
|
||||||
subject { Goldfinger::Result.new(headers, body) }
|
subject { Goldfinger::Result.new(response) }
|
||||||
|
|
||||||
describe '#links' do
|
describe '#links' do
|
||||||
it 'returns a non-empty array' do
|
it 'returns a non-empty array' do
|
||||||
|
@ -59,15 +59,21 @@ describe Goldfinger::Result do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when the input mime type is application/xrd+xml' do
|
context 'when the input mime type is application/xrd+xml' do
|
||||||
let(:headers) { h = HTTP::Headers.new; h.set(HTTP::Headers::CONTENT_TYPE, 'application/xrd+xml'); h }
|
before do
|
||||||
let(:body) { File.read(fixture_path('quitter.no_.well-known_webfinger.xml')) }
|
stub_request(:get, 'https://quitter.no/.well-known/webfinger?resource=acct:gargron@quitter.no').to_return(body: fixture('quitter.no_.well-known_webfinger.xml'), headers: { content_type: 'application/xrd+xml' })
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:response) { HTTP.get('https://quitter.no/.well-known/webfinger?resource=acct:gargron@quitter.no') }
|
||||||
|
|
||||||
it_behaves_like 'a working finger result'
|
it_behaves_like 'a working finger result'
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when the input mime type is application/jrd+json' do
|
context 'when the input mime type is application/jrd+json' do
|
||||||
let(:headers) { h = HTTP::Headers.new; h.set(HTTP::Headers::CONTENT_TYPE, 'application/jrd+json'); h }
|
before do
|
||||||
let(:body) { File.read(fixture_path('quitter.no_.well-known_webfinger.json')) }
|
stub_request(:get, 'https://quitter.no/.well-known/webfinger?resource=acct:gargron@quitter.no').to_return(body: fixture('quitter.no_.well-known_webfinger.json'), headers: { content_type: 'application/jrd+json' })
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:response) { HTTP.get('https://quitter.no/.well-known/webfinger?resource=acct:gargron@quitter.no') }
|
||||||
|
|
||||||
it_behaves_like 'a working finger result'
|
it_behaves_like 'a working finger result'
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,7 +5,6 @@ require 'pry'
|
||||||
WebMock.disable_net_connect!
|
WebMock.disable_net_connect!
|
||||||
|
|
||||||
RSpec.configure do |config|
|
RSpec.configure do |config|
|
||||||
|
|
||||||
config.expect_with :rspec do |expectations|
|
config.expect_with :rspec do |expectations|
|
||||||
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
||||||
end
|
end
|
||||||
|
@ -13,7 +12,6 @@ RSpec.configure do |config|
|
||||||
config.mock_with :rspec do |mocks|
|
config.mock_with :rspec do |mocks|
|
||||||
mocks.verify_partial_doubles = true
|
mocks.verify_partial_doubles = true
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def fixture_path(path)
|
def fixture_path(path)
|
||||||
|
|
Loading…
Reference in New Issue