indieweb-endpoints 7.0.0 → 7.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 151e40ea1ac993997ee70da6c2f997609fb1311f33ab4f10d9090d76a555c3ec
4
- data.tar.gz: 73547ed72b54f2c2b954bc89df1b697bd22fbc02ea59104b6dc841b7ec9ab6ab
3
+ metadata.gz: d00bf075ce905d49768b047c4963ccc8fb260e3ce7c06a1305a10099bdf6e4a0
4
+ data.tar.gz: 2c50a4fe3f85d401ccf981e5ef4c374e35f2788a2a00422f52df08c7a7c9821f
5
5
  SHA512:
6
- metadata.gz: 34e13013f71ae9d4ea89fbf1ba2a40619579023203a95a72b30c552b80ce3442e13d4eceeef4eaeceed074f3e9e39300a0b45c42e50a16c072a72f9f863b8809
7
- data.tar.gz: 2f3c6b0e7267e69096f348210bf087b171c2d4acb64577bcc1a9440b0367655c85c62c23d837f054984389b4b484e39daab085db4112bd2f5777b3ce0cd33534
6
+ metadata.gz: ed757712cf9c99812ebdf2eb514c37b4a78cf0fd9f86c416611ce64a854706eb0e7b08599be0681438a774b76dbc9e03b2bb8434107fd17dec37403686743182
7
+ data.tar.gz: cadd7fad871a5d33f88e9365f7b8b1300c331cd96f2716d17f24ae8eac9eec79c463f4e45c68cb18fa35d602393fd771d3face3723c0acc1e3ab46acba02f7c7
data/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 7.1.0 / 2022-03-08
4
+
5
+ - Refactor gem code (eba8115)
6
+ - Refactor specs (5b92119)
7
+ - Rescue from `OpenSSL::SSL::SSLError` (4ea38e5)
8
+
3
9
  ## 7.0.0 / 2022-01-06
4
10
 
5
11
  - Update runtime dependency versions (5c9430f)
data/README.md CHANGED
@@ -59,7 +59,7 @@ Should the need arise, you may work with the `IndieWeb::Endpoints::Client` class
59
59
  require 'indieweb/endpoints'
60
60
 
61
61
  client = IndieWeb::Endpoints::Client.new('https://aaronparecki.com')
62
- #=> #<IndieWeb::Endpoints::Client url: "https://aaronparecki.com">
62
+ #=> #<IndieWeb::Endpoints::Client uri: "https://aaronparecki.com">
63
63
 
64
64
  client.response
65
65
  #=> #<HTTP::Response/1.1 200 OK {…}>
@@ -80,6 +80,10 @@ From [httprb/http](https://github.com/httprb/http):
80
80
 
81
81
  - `IndieWeb::Endpoints::HttpError`
82
82
 
83
+ From the Ruby Standard Library's [`OpenSSL::SSL::SSLError`](https://ruby-doc.org/stdlib-2.6.9/libdoc/openssl/rdoc/OpenSSL/SSL/SSLError.html):
84
+
85
+ - `IndieWeb::Endpoints::SSLError`
86
+
83
87
  ## Contributing
84
88
 
85
89
  Interested in helping improve indieweb-endpoints-ruby? Awesome! Your help is greatly appreciated. See [CONTRIBUTING.md](https://github.com/indieweb/indieweb-endpoints-ruby/blob/main/CONTRIBUTING.md) for details.
@@ -27,8 +27,7 @@ Gem::Specification.new do |spec|
27
27
  'rubygems_mfa_required' => 'true'
28
28
  }
29
29
 
30
- spec.add_runtime_dependency 'addressable', '~> 2.8'
31
30
  spec.add_runtime_dependency 'http', '~> 5.0'
32
31
  spec.add_runtime_dependency 'link-header-parser', '~> 4.0'
33
- spec.add_runtime_dependency 'nokogiri', '~> 1.12'
32
+ spec.add_runtime_dependency 'nokogiri', '~> 1.13'
34
33
  end
@@ -12,39 +12,45 @@ module IndieWeb
12
12
  #
13
13
  # client = IndieWeb::Endpoints::Client.new('https://aaronparecki.com')
14
14
  #
15
- # @param url [String] an absolute URL
15
+ # @param url [String, HTTP::URI, #to_s] an absolute URL
16
+ # @raise [IndieWeb::Endpoints::InvalidURIError]
16
17
  def initialize(url)
17
- @url = url.to_str
18
+ @uri = HTTP::URI.parse(url.to_s)
19
+ rescue Addressable::URI::InvalidURIError => e
20
+ raise InvalidURIError, e
18
21
  end
19
22
 
20
23
  # @return [String]
21
24
  def inspect
22
- "#<#{self.class.name}:#{format('%#0x', object_id)} url: #{url.inspect}>"
25
+ %(#<#{self.class.name}:#{format('%#0x', object_id)} uri: "#{uri}">)
23
26
  end
24
27
 
28
+ # A Hash of the discovered IndieWeb endpoints from the provided URL
29
+ #
25
30
  # @return [Hash{Symbol => String, Array, nil}]
26
31
  def endpoints
27
- @endpoints ||= Parsers.registered.transform_values { |parser| parser.new(response).results }
32
+ @endpoints ||= Parser.new(response).results
28
33
  end
29
34
 
30
- # @see https://www.w3.org/TR/webmention/#limits-on-get-requests
35
+ # The HTTP::Response object returned by the provided URL
31
36
  #
32
37
  # @return [HTTP::Response]
38
+ # @raise [IndieWeb::Endpoints::HttpError]
33
39
  def response
34
- @response ||= HTTP.follow(max_hops: 20).headers(HTTP_HEADERS_OPTS).timeout(connect: 5, read: 5).get(uri)
40
+ @response ||= HTTP.follow(max_hops: 20)
41
+ .headers(HTTP_HEADERS_OPTS)
42
+ .timeout(connect: 5, read: 5)
43
+ .get(uri)
35
44
  rescue HTTP::Error => e
36
45
  raise HttpError, e
46
+ rescue OpenSSL::SSL::SSLError => e
47
+ raise SSLError, e
37
48
  end
38
49
 
39
50
  private
40
51
 
41
- attr_accessor :url
42
-
43
- def uri
44
- @uri ||= Addressable::URI.parse(url)
45
- rescue Addressable::URI::InvalidURIError => e
46
- raise InvalidURIError, e
47
- end
52
+ # @return [HTTP::URI]
53
+ attr_reader :uri
48
54
  end
49
55
  end
50
56
  end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IndieWeb
4
+ module Endpoints
5
+ # @api private
6
+ class Parser
7
+ # @param response [HTTP::Response]
8
+ def initialize(response)
9
+ @response = response
10
+ end
11
+
12
+ # @return [Hash{Symbol => String, Array<String>, nil}]
13
+ def results
14
+ {
15
+ authorization_endpoint: result_for(:authorization_endpoint),
16
+ micropub: result_for(:micropub),
17
+ microsub: result_for(:microsub),
18
+ redirect_uri: results_for(:redirect_uri),
19
+ token_endpoint: result_for(:token_endpoint),
20
+ webmention: result_for(:webmention, %w[link a])
21
+ }
22
+ end
23
+
24
+ private
25
+
26
+ # @return [HTTP::Response]
27
+ attr_reader :response
28
+
29
+ # @return [IndieWeb::Endpoints::ResponseBodyParser]
30
+ def response_body_parser
31
+ @response_body_parser ||= ResponseBodyParser.new(response)
32
+ end
33
+
34
+ # @return [IndieWeb::Endpoints::ResponseHeadersParser]
35
+ def response_headers_parser
36
+ @response_headers_parser ||= ResponseHeadersParser.new(response)
37
+ end
38
+
39
+ # @param identifier [Symbol]
40
+ # @param nodes [Array<String>]
41
+ # @return [String, nil]
42
+ def result_for(identifier, nodes = ['link'])
43
+ results_for(identifier, nodes)&.first
44
+ end
45
+
46
+ # @param identifier [Symbol]
47
+ # @param nodes [Array<String>]
48
+ # @return [Array<String>, nil]
49
+ # @raise [IndieWeb::Endpoints::InvalidURIError]
50
+ def results_for(identifier, nodes = ['link'])
51
+ results_from_request = [
52
+ response_headers_parser.results_for(identifier),
53
+ response_body_parser.results_for(identifier, nodes)
54
+ ].flatten.compact
55
+
56
+ return if results_from_request.none?
57
+
58
+ results_from_request.map { |endpoint| response.uri.join(endpoint).to_s }.uniq.sort
59
+ rescue Addressable::URI::InvalidURIError => e
60
+ raise InvalidURIError, e
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IndieWeb
4
+ module Endpoints
5
+ # @api private
6
+ class ResponseBodyParser
7
+ # @param response [HTTP::Response]
8
+ def initialize(response)
9
+ @body = response.body.to_s
10
+ @mime_type = response.mime_type
11
+ @uri = response.uri
12
+ end
13
+
14
+ # @param identifier [Symbol]
15
+ # @param nodes [Array<String>]
16
+ # @return [Array<string>, nil]
17
+ def results_for(identifier, nodes = ['link'])
18
+ return unless mime_type == 'text/html'
19
+
20
+ # Reject endpoints that contain a fragment identifier
21
+ selectors = nodes.map { |node| %(#{node}[rel~="#{identifier}"][href]:not([href*="#"])) }.join(',')
22
+
23
+ parsed_body.css(selectors).map { |element| element['href'] }
24
+ end
25
+
26
+ private
27
+
28
+ # @return [String]
29
+ attr_reader :body
30
+
31
+ # @return [String]
32
+ attr_reader :mime_type
33
+
34
+ # @return [HTTP::URI]
35
+ attr_reader :uri
36
+
37
+ # @return [Nokogiri::HTML5::Document]
38
+ def parsed_body
39
+ @parsed_body ||= Nokogiri::HTML5(body)
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IndieWeb
4
+ module Endpoints
5
+ # @api private
6
+ class ResponseHeadersParser
7
+ # @param response [HTTP::Response]
8
+ def initialize(response)
9
+ @headers = response.headers.get('link')
10
+ @uri = response.uri
11
+ end
12
+
13
+ # @param headers [Symbol]
14
+ # @return [Array<String>, nil]
15
+ def results_for(identifier)
16
+ return unless parsed_headers.key?(identifier)
17
+
18
+ # Reject endpoints that contain a fragment identifier
19
+ parsed_headers[identifier].reject { |header| HTTP::URI.parse(header.target_uri).fragment }.map(&:target_uri)
20
+ end
21
+
22
+ private
23
+
24
+ # @return [Array<String>]
25
+ attr_reader :headers
26
+
27
+ # @return [HTTP::URI]
28
+ attr_reader :uri
29
+
30
+ # @return [Hash{Symbol => Array<LinkHeaderParser::LinkHeader>}]
31
+ def parsed_headers
32
+ @parsed_headers ||= LinkHeaderParser.parse(headers, base: uri).group_by_relation_type
33
+ end
34
+ end
35
+ end
36
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module IndieWeb
4
4
  module Endpoints
5
- VERSION = '7.0.0'
5
+ VERSION = '7.1.0'
6
6
  end
7
7
  end
@@ -1,28 +1,23 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'addressable/uri'
4
3
  require 'http'
5
4
  require 'link-header-parser'
6
5
  require 'nokogiri'
7
6
 
8
7
  require_relative 'endpoints/version'
9
- require_relative 'endpoints/exceptions'
10
-
11
- require_relative 'endpoints/services/response_parser_service'
12
8
 
13
9
  require_relative 'endpoints/client'
14
- require_relative 'endpoints/parsers'
15
-
16
- require_relative 'endpoints/parsers/base_parser'
17
- require_relative 'endpoints/parsers/authorization_endpoint_parser'
18
- require_relative 'endpoints/parsers/micropub_parser'
19
- require_relative 'endpoints/parsers/microsub_parser'
20
- require_relative 'endpoints/parsers/redirect_uri_parser'
21
- require_relative 'endpoints/parsers/token_endpoint_parser'
22
- require_relative 'endpoints/parsers/webmention_parser'
10
+ require_relative 'endpoints/parser'
11
+ require_relative 'endpoints/response_body_parser'
12
+ require_relative 'endpoints/response_headers_parser'
23
13
 
24
14
  module IndieWeb
25
15
  module Endpoints
16
+ class Error < StandardError; end
17
+ class HttpError < Error; end
18
+ class InvalidURIError < Error; end
19
+ class SSLError < Error; end
20
+
26
21
  # Discover a URL's IndieAuth, Micropub, Microsub, and Webmention endpoints
27
22
  #
28
23
  # IndieWeb::Endpoints.get('https://aaronparecki.com')
metadata CHANGED
@@ -1,29 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: indieweb-endpoints
3
3
  version: !ruby/object:Gem::Version
4
- version: 7.0.0
4
+ version: 7.1.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: 2022-01-06 00:00:00.000000000 Z
11
+ date: 2022-03-09 00:00:00.000000000 Z
12
12
  dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: addressable
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - "~>"
18
- - !ruby/object:Gem::Version
19
- version: '2.8'
20
- type: :runtime
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - "~>"
25
- - !ruby/object:Gem::Version
26
- version: '2.8'
27
13
  - !ruby/object:Gem::Dependency
28
14
  name: http
29
15
  requirement: !ruby/object:Gem::Requirement
@@ -58,14 +44,14 @@ dependencies:
58
44
  requirements:
59
45
  - - "~>"
60
46
  - !ruby/object:Gem::Version
61
- version: '1.12'
47
+ version: '1.13'
62
48
  type: :runtime
63
49
  prerelease: false
64
50
  version_requirements: !ruby/object:Gem::Requirement
65
51
  requirements:
66
52
  - - "~>"
67
53
  - !ruby/object:Gem::Version
68
- version: '1.12'
54
+ version: '1.13'
69
55
  description: Discover a URL’s IndieAuth, Micropub, Microsub, and Webmention endpoints.
70
56
  email:
71
57
  - jason@sixtwothree.org
@@ -81,23 +67,16 @@ files:
81
67
  - indieweb-endpoints.gemspec
82
68
  - lib/indieweb/endpoints.rb
83
69
  - lib/indieweb/endpoints/client.rb
84
- - lib/indieweb/endpoints/exceptions.rb
85
- - lib/indieweb/endpoints/parsers.rb
86
- - lib/indieweb/endpoints/parsers/authorization_endpoint_parser.rb
87
- - lib/indieweb/endpoints/parsers/base_parser.rb
88
- - lib/indieweb/endpoints/parsers/micropub_parser.rb
89
- - lib/indieweb/endpoints/parsers/microsub_parser.rb
90
- - lib/indieweb/endpoints/parsers/redirect_uri_parser.rb
91
- - lib/indieweb/endpoints/parsers/token_endpoint_parser.rb
92
- - lib/indieweb/endpoints/parsers/webmention_parser.rb
93
- - lib/indieweb/endpoints/services/response_parser_service.rb
70
+ - lib/indieweb/endpoints/parser.rb
71
+ - lib/indieweb/endpoints/response_body_parser.rb
72
+ - lib/indieweb/endpoints/response_headers_parser.rb
94
73
  - lib/indieweb/endpoints/version.rb
95
74
  homepage: https://github.com/indieweb/indieweb-endpoints-ruby
96
75
  licenses:
97
76
  - MIT
98
77
  metadata:
99
78
  bug_tracker_uri: https://github.com/indieweb/indieweb-endpoints-ruby/issues
100
- changelog_uri: https://github.com/indieweb/indieweb-endpoints-ruby/blob/v7.0.0/CHANGELOG.md
79
+ changelog_uri: https://github.com/indieweb/indieweb-endpoints-ruby/blob/v7.1.0/CHANGELOG.md
101
80
  rubygems_mfa_required: 'true'
102
81
  post_install_message:
103
82
  rdoc_options: []
@@ -1,13 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module IndieWeb
4
- module Endpoints
5
- class Error < StandardError; end
6
-
7
- class ArgumentError < Error; end
8
-
9
- class HttpError < Error; end
10
-
11
- class InvalidURIError < Error; end
12
- end
13
- end
@@ -1,13 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module IndieWeb
4
- module Endpoints
5
- module Parsers
6
- class AuthorizationEndpointParser < BaseParser
7
- @identifier = :authorization_endpoint
8
-
9
- Parsers.register(self)
10
- end
11
- end
12
- end
13
- end
@@ -1,59 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module IndieWeb
4
- module Endpoints
5
- module Parsers
6
- class BaseParser
7
- class << self
8
- attr_reader :identifier
9
- end
10
-
11
- # @param response [HTTP::Response]
12
- def initialize(response)
13
- raise ArgumentError, "response must be an HTTP::Response (given #{response.class.name})" unless response.is_a?(HTTP::Response)
14
-
15
- @response = response
16
- end
17
-
18
- # @return [String]
19
- def results
20
- mapped_results.shift
21
- end
22
-
23
- private
24
-
25
- attr_reader :response
26
-
27
- def mapped_results
28
- @mapped_results ||= results_from_http_request.map { |endpoint| Addressable::URI.join(response.uri, endpoint).to_s }.uniq.sort
29
- rescue Addressable::URI::InvalidURIError => e
30
- raise InvalidURIError, e
31
- end
32
-
33
- def parsed_response_body
34
- @parsed_response_body ||= Nokogiri::HTML(response.body.to_s)
35
- end
36
-
37
- def parsed_response_headers
38
- @parsed_response_headers ||= LinkHeaderParser.parse(response.headers.get('link'), base: response.uri)
39
- end
40
-
41
- def results_from_body
42
- return if response.mime_type != 'text/html'
43
-
44
- Services::ResponseParserService.parse_body(parsed_response_body, self.class.identifier)
45
- end
46
-
47
- def results_from_headers
48
- return if parsed_response_headers.none?
49
-
50
- Services::ResponseParserService.parse_headers(parsed_response_headers.group_by_relation_type, self.class.identifier)
51
- end
52
-
53
- def results_from_http_request
54
- @results_from_http_request ||= [results_from_headers, results_from_body].flatten.compact
55
- end
56
- end
57
- end
58
- end
59
- end
@@ -1,13 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module IndieWeb
4
- module Endpoints
5
- module Parsers
6
- class MicropubParser < BaseParser
7
- @identifier = :micropub
8
-
9
- Parsers.register(self)
10
- end
11
- end
12
- end
13
- end
@@ -1,13 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module IndieWeb
4
- module Endpoints
5
- module Parsers
6
- class MicrosubParser < BaseParser
7
- @identifier = :microsub
8
-
9
- Parsers.register(self)
10
- end
11
- end
12
- end
13
- end
@@ -1,20 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module IndieWeb
4
- module Endpoints
5
- module Parsers
6
- class RedirectUriParser < BaseParser
7
- @identifier = :redirect_uri
8
-
9
- Parsers.register(self)
10
-
11
- # @return [Array<String>, nil]
12
- def results
13
- return if mapped_results.none?
14
-
15
- mapped_results
16
- end
17
- end
18
- end
19
- end
20
- end
@@ -1,13 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module IndieWeb
4
- module Endpoints
5
- module Parsers
6
- class TokenEndpointParser < BaseParser
7
- @identifier = :token_endpoint
8
-
9
- Parsers.register(self)
10
- end
11
- end
12
- end
13
- end
@@ -1,24 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module IndieWeb
4
- module Endpoints
5
- module Parsers
6
- class WebmentionParser < BaseParser
7
- @identifier = :webmention
8
-
9
- Parsers.register(self)
10
-
11
- private
12
-
13
- def results_for_node(node)
14
- Services::ResponseParserService.parse_body(parsed_response_body, self.class.identifier, node)
15
- end
16
-
17
- # https://www.w3.org/TR/webmention/#sender-discovers-receiver-webmention-endpoint
18
- def results_from_body
19
- @results_from_body ||= [results_for_node('link'), results_for_node('a')].flatten.compact
20
- end
21
- end
22
- end
23
- end
24
- end
@@ -1,15 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module IndieWeb
4
- module Endpoints
5
- module Parsers
6
- def self.register(klass)
7
- registered[klass.identifier] = klass
8
- end
9
-
10
- def self.registered
11
- @registered ||= {}
12
- end
13
- end
14
- end
15
- end
@@ -1,27 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module IndieWeb
4
- module Endpoints
5
- module Services
6
- class ResponseParserService
7
- # @param body [Nokogiri::HTML::Document]
8
- # @param identifier [Symbol]
9
- # @return [Array<String>]
10
- def self.parse_body(body, identifier, node = 'link')
11
- # Reject endpoints that contain a fragment identifier
12
- body.css(%(#{node}[rel~="#{identifier}"][href]:not([href*="#"]))).map { |element| element['href'] }
13
- end
14
-
15
- # @param headers [Hash{Symbol => Array<LinkHeaderParser::LinkHeader}]
16
- # @param identifier [Symbol]
17
- # @return [Array<String>, nil]
18
- def self.parse_headers(headers, identifier)
19
- return unless headers.key?(identifier)
20
-
21
- # Reject endpoints that contain a fragment identifier
22
- headers[identifier].reject { |header| Addressable::URI.parse(header.target_uri).fragment }.map(&:target_uri)
23
- end
24
- end
25
- end
26
- end
27
- end