indieweb-endpoints 0.4.0 → 0.5.0
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.
- checksums.yaml +4 -4
- data/.rubocop.yml +1 -0
- data/CHANGELOG.md +9 -0
- data/README.md +2 -1
- data/indieweb-endpoints.gemspec +1 -3
- data/lib/indieweb/endpoints/client.rb +3 -3
- data/lib/indieweb/endpoints/http_request.rb +15 -4
- data/lib/indieweb/endpoints/parsers/microsub_parser.rb +13 -0
- data/lib/indieweb/endpoints/parsers/redirect_uri_parser.rb +18 -6
- data/lib/indieweb/endpoints/parsers/webmention_parser.rb +9 -1
- data/lib/indieweb/endpoints/parsers.rb +65 -33
- data/lib/indieweb/endpoints/version.rb +1 -1
- data/lib/indieweb/endpoints.rb +1 -0
- metadata +6 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9cb1b27367683210b63651d8195e6d12013f994be97d8c216b2e2165429c4adb
|
4
|
+
data.tar.gz: 587e29ea23c02d6de575cefbc6a5f9a808c5e6b47bf662ee138ceaadba03c57f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1ca06c752aa3ff624d1ab3dbcad733d69bd5ab701ccdac754e7f5bc4e4d7c70ee8b42b23dd911448fb85fab44264e2e7f3c9c136cb23f5b4aaad282a21555ec7
|
7
|
+
data.tar.gz: adbb98ffc16e4664d8ea47a94bdfb32b5fbd552155716a1928bf3b62fbdd4bc545768d00ce046101db454b8a71752acdb14330c99e57c1597eca73b8b0e71b93
|
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,14 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## 0.5.0 / 2019-05-09
|
4
|
+
|
5
|
+
- Add support for Microsub endpoint discovery ([5e81d9f](https://github.com/indieweb/indieweb-endpoints-ruby/commit/5e81d9f)).
|
6
|
+
- Refactor parsers to ignore URLs with fragments ([797b376](https://github.com/indieweb/indieweb-endpoints-ruby/commit/797b376)).
|
7
|
+
- Rescue `NoMethodError` (for `nil`) and `TypeError` (for non-`String`) ([e33522e](https://github.com/indieweb/indieweb-endpoints-ruby/commit/e33522e)).
|
8
|
+
- Raise `ArgumentError` if url scheme is not `http` or `https` ([8eb1b1a](https://github.com/indieweb/indieweb-endpoints-ruby/commit/8eb1b1a)).
|
9
|
+
- Shorten up User Agent string ([f9717b4](https://github.com/indieweb/indieweb-endpoints-ruby/commit/f9717b4)).
|
10
|
+
- Refactor `HTTPRequest` class using specification defaults ([feef2ba](https://github.com/indieweb/indieweb-endpoints-ruby/commit/feef2ba)).
|
11
|
+
|
3
12
|
## 0.4.0 / 2019-05-01
|
4
13
|
|
5
14
|
- Add `IndieWeb::Endpoints.client` method ([c4d42d0](https://github.com/indieweb/indieweb-endpoints-ruby/commit/c4d42d0)).
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# indieweb-endpoints-ruby
|
2
2
|
|
3
|
-
**A Ruby gem for discovering a URL's [IndieAuth](https://indieweb.org/IndieAuth), [Micropub](https://indieweb.org/Micropub), and [Webmention](https://indieweb.org/Webmention) endpoints.**
|
3
|
+
**A Ruby gem for discovering a URL's [IndieAuth](https://indieweb.org/IndieAuth), [Micropub](https://indieweb.org/Micropub), [Microsub](https://indieweb.org/Microsub), and [Webmention](https://indieweb.org/Webmention) endpoints.**
|
4
4
|
|
5
5
|
[](https://rubygems.org/gems/indieweb-endpoints)
|
6
6
|
[](https://rubygems.org/gems/indieweb-endpoints)
|
@@ -56,6 +56,7 @@ This example will search `https://aaronparecki.com` for valid IndieAuth, Micropu
|
|
56
56
|
{
|
57
57
|
authorization_endpoint: 'https://aaronparecki.com/auth',
|
58
58
|
micropub: 'https://aaronparecki.com/micropub',
|
59
|
+
microsub: 'https://aperture.p3k.io/microsub/1',
|
59
60
|
redirect_uri: nil,
|
60
61
|
token_endpoint: 'https://aaronparecki.com/auth/token',
|
61
62
|
webmention: 'https://webmention.io/aaronpk/webmention'
|
data/indieweb-endpoints.gemspec
CHANGED
@@ -3,7 +3,6 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
3
3
|
|
4
4
|
require 'indieweb/endpoints/version'
|
5
5
|
|
6
|
-
# rubocop:disable Metrics/BlockLength
|
7
6
|
Gem::Specification.new do |spec|
|
8
7
|
spec.required_ruby_version = ['>= 2.4', '< 2.7']
|
9
8
|
|
@@ -12,7 +11,7 @@ Gem::Specification.new do |spec|
|
|
12
11
|
spec.authors = ['Jason Garber']
|
13
12
|
spec.email = ['jason@sixtwothree.org']
|
14
13
|
|
15
|
-
spec.summary = 'Discover a URL’s IndieAuth, Micropub, and Webmention endpoints.'
|
14
|
+
spec.summary = 'Discover a URL’s IndieAuth, Micropub, Microsub, and Webmention endpoints.'
|
16
15
|
spec.description = spec.summary
|
17
16
|
spec.homepage = 'https://github.com/indieweb/indieweb-endpoints-ruby'
|
18
17
|
spec.license = 'MIT'
|
@@ -41,4 +40,3 @@ Gem::Specification.new do |spec|
|
|
41
40
|
spec.add_runtime_dependency 'http', '~> 5.0.0.pre'
|
42
41
|
spec.add_runtime_dependency 'nokogiri', '~> 1.10'
|
43
42
|
end
|
44
|
-
# rubocop:enable Metrics/BlockLength
|
@@ -2,13 +2,13 @@ module IndieWeb
|
|
2
2
|
module Endpoints
|
3
3
|
class Client
|
4
4
|
def initialize(url)
|
5
|
-
raise ArgumentError, "url must be a String (given #{url.class.name})" unless url.is_a?(String)
|
6
|
-
|
7
5
|
@uri = Addressable::URI.parse(url)
|
8
6
|
|
9
|
-
raise ArgumentError, 'url must be an absolute URL (e.g. https://example.com)' unless @uri.absolute?
|
7
|
+
raise ArgumentError, 'url must be an absolute URL (e.g. https://example.com)' unless @uri.absolute? && @uri.scheme.match?(/^https?$/)
|
10
8
|
rescue Addressable::URI::InvalidURIError => exception
|
11
9
|
raise InvalidURIError, exception
|
10
|
+
rescue NoMethodError, TypeError
|
11
|
+
raise ArgumentError, "url must be a String (given #{url.class})"
|
12
12
|
end
|
13
13
|
|
14
14
|
def endpoints
|
@@ -1,13 +1,24 @@
|
|
1
1
|
module IndieWeb
|
2
2
|
module Endpoints
|
3
3
|
class HttpRequest
|
4
|
-
|
5
|
-
|
6
|
-
|
4
|
+
# Defaults derived from Webmention specification examples
|
5
|
+
# https://www.w3.org/TR/webmention/#limits-on-get-requests
|
6
|
+
HTTP_CLIENT_OPTS = {
|
7
|
+
follow: {
|
8
|
+
max_hops: 20
|
9
|
+
},
|
10
|
+
headers: {
|
11
|
+
accept: '*/*',
|
12
|
+
user_agent: 'IndieWeb Endpoint Discovery (https://rubygems.org/gems/indieweb-endpoints)'
|
13
|
+
},
|
14
|
+
timeout_options: {
|
15
|
+
connect_timeout: 5,
|
16
|
+
read_timeout: 5
|
17
|
+
}
|
7
18
|
}.freeze
|
8
19
|
|
9
20
|
def self.get(uri)
|
10
|
-
HTTP.
|
21
|
+
HTTP::Client.new(HTTP_CLIENT_OPTS).request(:get, uri)
|
11
22
|
rescue HTTP::ConnectionError,
|
12
23
|
HTTP::TimeoutError,
|
13
24
|
HTTP::Redirector::TooManyRedirectsError => exception
|
@@ -11,7 +11,7 @@ module IndieWeb
|
|
11
11
|
def results
|
12
12
|
return unless results_from_http_request.any?
|
13
13
|
|
14
|
-
@results ||= results_from_http_request.map { |endpoint| Absolutely.to_abs(base:
|
14
|
+
@results ||= results_from_http_request.map { |endpoint| Absolutely.to_abs(base: response.uri.to_s, relative: endpoint) }.uniq.sort
|
15
15
|
rescue Absolutely::InvalidURIError => exception
|
16
16
|
raise InvalidURIError, exception
|
17
17
|
end
|
@@ -19,10 +19,26 @@ module IndieWeb
|
|
19
19
|
private
|
20
20
|
|
21
21
|
def results_from_body
|
22
|
-
|
22
|
+
RedirectUriLinkElementParser.new(response, self.class.identifier).results
|
23
23
|
end
|
24
24
|
|
25
25
|
def results_from_headers
|
26
|
+
RedirectUriLinkHeaderParser.new(response, self.class.identifier).results
|
27
|
+
end
|
28
|
+
|
29
|
+
def results_from_http_request
|
30
|
+
@results_from_http_request ||= [results_from_headers, results_from_body].flatten.compact
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class RedirectUriLinkElementParser < LinkElementParser
|
35
|
+
def results
|
36
|
+
link_elements.map { |element| element['href'] } if response_is_html && link_elements.any?
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
class RedirectUriLinkHeaderParser < LinkHeaderParser
|
41
|
+
def results
|
26
42
|
return unless link_headers.any?
|
27
43
|
|
28
44
|
link_headers.map do |header|
|
@@ -31,10 +47,6 @@ module IndieWeb
|
|
31
47
|
endpoint_match_data[1] if endpoint_match_data
|
32
48
|
end
|
33
49
|
end
|
34
|
-
|
35
|
-
def results_from_http_request
|
36
|
-
@results_from_http_request ||= [results_from_headers, results_from_body].flatten.compact
|
37
|
-
end
|
38
50
|
end
|
39
51
|
end
|
40
52
|
end
|
@@ -10,6 +10,14 @@ module IndieWeb
|
|
10
10
|
|
11
11
|
private
|
12
12
|
|
13
|
+
def results_from_body
|
14
|
+
WebmentionLinkElementParser.new(response, self.class.identifier).results
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class WebmentionLinkElementParser < LinkElementParser
|
19
|
+
private
|
20
|
+
|
13
21
|
def link_element
|
14
22
|
# Return first `a` or `link` element with valid `rel` attribute
|
15
23
|
# https://www.w3.org/TR/webmention/#sender-discovers-receiver-webmention-endpoint
|
@@ -17,7 +25,7 @@ module IndieWeb
|
|
17
25
|
end
|
18
26
|
|
19
27
|
def link_elements_css_selector
|
20
|
-
@link_elements_css_selector ||= %([rel~="#{
|
28
|
+
@link_elements_css_selector ||= %([rel~="#{identifier}"][href]:not([href*="#"]))
|
21
29
|
end
|
22
30
|
end
|
23
31
|
end
|
@@ -4,13 +4,7 @@ module IndieWeb
|
|
4
4
|
extend Registerable
|
5
5
|
|
6
6
|
class BaseParser
|
7
|
-
|
8
|
-
# https://tools.ietf.org/html/rfc8288#section-3.3
|
9
|
-
REGEXP_REG_REL_TYPE_PATTERN = '[a-z\d][a-z\d\-\.]*'.freeze
|
10
|
-
|
11
|
-
# Liberal pattern matching a string of text between angle brackets
|
12
|
-
# https://tools.ietf.org/html/rfc5988#section-5.1
|
13
|
-
REGEXP_TARGET_URI_PATTERN = /^<(.*)>;/.freeze
|
7
|
+
attr_reader :response
|
14
8
|
|
15
9
|
def initialize(response)
|
16
10
|
raise ArgumentError, "response must be an HTTP::Response (given #{response.class.name})" unless response.is_a?(HTTP::Response)
|
@@ -21,21 +15,42 @@ module IndieWeb
|
|
21
15
|
def results
|
22
16
|
return unless results_from_http_request
|
23
17
|
|
24
|
-
@results ||= Absolutely.to_abs(base:
|
18
|
+
@results ||= Absolutely.to_abs(base: response.uri.to_s, relative: results_from_http_request)
|
25
19
|
rescue Absolutely::InvalidURIError => exception
|
26
20
|
raise InvalidURIError, exception
|
27
21
|
end
|
28
22
|
|
29
23
|
private
|
30
24
|
|
31
|
-
def
|
32
|
-
|
33
|
-
|
34
|
-
|
25
|
+
def results_from_body
|
26
|
+
LinkElementParser.new(response, self.class.identifier).results
|
27
|
+
end
|
28
|
+
|
29
|
+
def results_from_headers
|
30
|
+
LinkHeaderParser.new(response, self.class.identifier).results
|
31
|
+
end
|
32
|
+
|
33
|
+
def results_from_http_request
|
34
|
+
@results_from_http_request ||= results_from_headers || results_from_body || nil
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class LinkElementParser
|
39
|
+
attr_reader :identifier, :response
|
40
|
+
|
41
|
+
def initialize(response, identifier)
|
42
|
+
@response = response
|
43
|
+
@identifier = identifier
|
35
44
|
end
|
36
45
|
|
46
|
+
def results
|
47
|
+
link_element['href'] if response_is_html && link_element
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
37
52
|
def doc
|
38
|
-
@doc ||= Nokogiri::HTML(
|
53
|
+
@doc ||= Nokogiri::HTML(response.body.to_s)
|
39
54
|
end
|
40
55
|
|
41
56
|
def link_element
|
@@ -50,42 +65,59 @@ module IndieWeb
|
|
50
65
|
end
|
51
66
|
|
52
67
|
def link_elements_css_selector
|
53
|
-
@link_elements_css_selector ||= %(link[rel~="#{
|
68
|
+
@link_elements_css_selector ||= %(link[rel~="#{identifier}"][href]:not([href*="#"]))
|
54
69
|
end
|
55
70
|
|
56
|
-
def
|
57
|
-
@
|
71
|
+
def response_is_html
|
72
|
+
@response_is_html ||= response.mime_type == 'text/html'
|
58
73
|
end
|
74
|
+
end
|
59
75
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
76
|
+
class LinkHeaderParser
|
77
|
+
# Ultra-orthodox pattern matching allowed values in Link header `rel` parameter
|
78
|
+
# https://tools.ietf.org/html/rfc8288#section-3.3
|
79
|
+
REGEXP_REG_REL_TYPE_PATTERN = '[a-z\d][a-z\d\-\.]*'.freeze
|
64
80
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
@regexp_rel_paramater_pattern ||= /(?:;|\s)rel="?(?:#{REGEXP_REG_REL_TYPE_PATTERN}+\s)?#{self.class.identifier}(?:\s#{REGEXP_REG_REL_TYPE_PATTERN})?"?/
|
69
|
-
end
|
81
|
+
# Liberal pattern capturing a string of text (excepting the octothorp) between angle brackets
|
82
|
+
# https://tools.ietf.org/html/rfc5988#section-5.1
|
83
|
+
REGEXP_TARGET_URI_PATTERN = '^<(.[^#]*)>;'.freeze
|
70
84
|
|
71
|
-
|
72
|
-
|
85
|
+
attr_reader :identifier, :response
|
86
|
+
|
87
|
+
def initialize(response, identifier)
|
88
|
+
@response = response
|
89
|
+
@identifier = identifier
|
73
90
|
end
|
74
91
|
|
75
|
-
def
|
92
|
+
def results
|
76
93
|
return unless link_header
|
77
94
|
|
78
|
-
endpoint_match_data = link_header.match(REGEXP_TARGET_URI_PATTERN)
|
95
|
+
endpoint_match_data = link_header.match(/#{REGEXP_TARGET_URI_PATTERN}/)
|
79
96
|
|
80
97
|
return endpoint_match_data[1] if endpoint_match_data
|
81
98
|
end
|
82
99
|
|
83
|
-
|
84
|
-
|
100
|
+
private
|
101
|
+
|
102
|
+
def discrete_link_headers
|
103
|
+
# Split Link headers with multiple values, flatten the resulting array, and strip whitespace
|
104
|
+
# https://webmention.rocks/test/19
|
105
|
+
@discrete_link_headers ||= response.headers.get('link').map { |header| header.split(',') }.flatten.map(&:strip)
|
85
106
|
end
|
86
107
|
|
87
|
-
def
|
88
|
-
@
|
108
|
+
def link_header
|
109
|
+
@link_header ||= link_headers.shift
|
110
|
+
end
|
111
|
+
|
112
|
+
def link_headers
|
113
|
+
# Reduce Link headers to those with valid `rel` attribute
|
114
|
+
@link_headers ||= discrete_link_headers.find_all { |header| header.match?(/#{REGEXP_TARGET_URI_PATTERN}\s*#{regexp_rel_paramater_pattern}/) }
|
115
|
+
end
|
116
|
+
|
117
|
+
def regexp_rel_paramater_pattern
|
118
|
+
# Ultra-orthodox pattern matching Link header `rel` parameter including a matching identifier value
|
119
|
+
# https://www.w3.org/TR/webmention/#sender-discovers-receiver-webmention-endpoint
|
120
|
+
@regexp_rel_paramater_pattern ||= %(rel="?(?:#{REGEXP_REG_REL_TYPE_PATTERN}+\s)?#{identifier}(?:\s#{REGEXP_REG_REL_TYPE_PATTERN})?"?)
|
89
121
|
end
|
90
122
|
end
|
91
123
|
end
|
data/lib/indieweb/endpoints.rb
CHANGED
@@ -13,6 +13,7 @@ require 'indieweb/endpoints/registerable'
|
|
13
13
|
require 'indieweb/endpoints/parsers'
|
14
14
|
require 'indieweb/endpoints/parsers/authorization_endpoint_parser'
|
15
15
|
require 'indieweb/endpoints/parsers/micropub_parser'
|
16
|
+
require 'indieweb/endpoints/parsers/microsub_parser'
|
16
17
|
require 'indieweb/endpoints/parsers/redirect_uri_parser'
|
17
18
|
require 'indieweb/endpoints/parsers/token_endpoint_parser'
|
18
19
|
require 'indieweb/endpoints/parsers/webmention_parser'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: indieweb-endpoints
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jason Garber
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-05-
|
11
|
+
date: 2019-05-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -192,7 +192,7 @@ dependencies:
|
|
192
192
|
- - "~>"
|
193
193
|
- !ruby/object:Gem::Version
|
194
194
|
version: '1.10'
|
195
|
-
description: Discover a URL’s IndieAuth, Micropub, and Webmention endpoints.
|
195
|
+
description: Discover a URL’s IndieAuth, Micropub, Microsub, and Webmention endpoints.
|
196
196
|
email:
|
197
197
|
- jason@sixtwothree.org
|
198
198
|
executables: []
|
@@ -224,6 +224,7 @@ files:
|
|
224
224
|
- lib/indieweb/endpoints/parsers.rb
|
225
225
|
- lib/indieweb/endpoints/parsers/authorization_endpoint_parser.rb
|
226
226
|
- lib/indieweb/endpoints/parsers/micropub_parser.rb
|
227
|
+
- lib/indieweb/endpoints/parsers/microsub_parser.rb
|
227
228
|
- lib/indieweb/endpoints/parsers/redirect_uri_parser.rb
|
228
229
|
- lib/indieweb/endpoints/parsers/token_endpoint_parser.rb
|
229
230
|
- lib/indieweb/endpoints/parsers/webmention_parser.rb
|
@@ -234,7 +235,7 @@ licenses:
|
|
234
235
|
- MIT
|
235
236
|
metadata:
|
236
237
|
bug_tracker_uri: https://github.com/indieweb/indieweb-endpoints-ruby/issues
|
237
|
-
changelog_uri: https://github.com/indieweb/indieweb-endpoints-ruby/blob/v0.
|
238
|
+
changelog_uri: https://github.com/indieweb/indieweb-endpoints-ruby/blob/v0.5.0/CHANGELOG.md
|
238
239
|
post_install_message:
|
239
240
|
rdoc_options: []
|
240
241
|
require_paths:
|
@@ -256,5 +257,5 @@ requirements: []
|
|
256
257
|
rubygems_version: 3.0.3
|
257
258
|
signing_key:
|
258
259
|
specification_version: 4
|
259
|
-
summary: Discover a URL’s IndieAuth, Micropub, and Webmention endpoints.
|
260
|
+
summary: Discover a URL’s IndieAuth, Micropub, Microsub, and Webmention endpoints.
|
260
261
|
test_files: []
|