indieweb-endpoints 10.2.0 → 10.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 93961d5b2254ded47773579d8b1d357621571be0256c6ccfc5768177703a28c5
4
- data.tar.gz: 95904542302a29d07aefe58f49095a4db8cacb2a3fb0faca80ddd4a62b8e178d
3
+ metadata.gz: ea9b5e5a041478750b3af2f317436b40df1e360994107298737874aa914ad669
4
+ data.tar.gz: 3b2efbfeef50178c5a1583160483fa8a920d49f7a5d5bd82d81cfdcde8b995b4
5
5
  SHA512:
6
- metadata.gz: 6b33c1da625391e747d6b8481cff57980d3bb8099b6ff5d286f8d6765bdec91cba5560d9a49d0cc0df51593e94ea8eb0bb42c34a8c6ccd85f6d3d898eeba5234
7
- data.tar.gz: f4394054e1814421691e981d4b16d6c8568d0f9184f03b74ce88f228687b24ecc10e9de253dc9ad12aeb23342cee77ab8ef39c2844cd2f2f0ec854e20ed207e7
6
+ metadata.gz: b01d5fe05e209cebc5859d70b954a3d3833e6e3859b3276d8914c8bc4d8e79ce75950c7866725c8f4082a4fbd6f5d5265e8b361a87c8493ba68a116a33cfe655
7
+ data.tar.gz: 1efbe90ec8d70b802b474904e58042f8284c11eaac41f717ce07d083146cb2af86ea863f06fdd9329a99965ad83474f73dab703826be09faf83d693c720e42c5
data/README.md CHANGED
@@ -37,11 +37,11 @@ With indieweb-endpoints-ruby added to your project's `Gemfile` and installed, yo
37
37
  ```ruby
38
38
  require "indieweb/endpoints"
39
39
 
40
- IndieWeb::Endpoints.get("https://aaronparecki.com")
41
- #=> { authorization_endpoint: "https://aaronparecki.com/auth", "indieauth-metadata": "https://aaronparecki.com/.well-known/oauth-authorization-server", micropub: "https://aaronparecki.com/micropub", microsub: "https://aperture.p3k.io/microsub/1", redirect_uri: nil, token_endpoint: "https://aaronparecki.com/auth/token", webmention: "https://webmention.io/aaronpk/webmention" }
40
+ IndieWeb::Endpoints.get("https://adactio.com")
41
+ #=> { authorization_endpoint: "https://indieauth.com/auth", "indieauth-metadata": nil, micropub: "https://adactio.com/micropub", microsub: "https://aperture.p3k.io/microsub/152", redirect_uri: nil, token_endpoint: "https://tokens.indieauth.com/token", webmention: "https://adactio.com/webmention"}
42
42
  ```
43
43
 
44
- This example will search [Aaron's website](https://aaronparecki.com) for valid IndieAuth, Micropub, and Webmention endpoints and return a `Hash` of results. Each key in the returned `Hash` will have a value of either a `String` representing a URL or `nil`. The `redirect_uri` key's value will be either an `Array` or `nil` since a given URL may register multiple callback URLs.
44
+ This example will search [Jeremy's website](https://adactio.com) for valid IndieAuth, Micropub, and Webmention endpoints and return a `Hash` of results. Each key in the returned `Hash` will have a value of either a `String` representing a URL or `nil`. The `redirect_uri` key's value will be either an `Array` or `nil` since a given URL may register multiple callback URLs.
45
45
 
46
46
  ### Advanced Usage
47
47
 
@@ -50,22 +50,22 @@ Should the need arise, you may work with the `IndieWeb::Endpoints::Client` class
50
50
  ```ruby
51
51
  require "indieweb/endpoints"
52
52
 
53
- client = IndieWeb::Endpoints::Client.new("https://aaronparecki.com")
54
- #=> #<IndieWeb::Endpoints::Client uri: "https://aaronparecki.com">
53
+ client = IndieWeb::Endpoints::Client.new("https://adactio.com")
54
+ #=> #<IndieWeb::Endpoints::Client @uri: Addressable::URI URI:https://adactio.com>>
55
55
 
56
56
  client.response
57
57
  #=> #<HTTP::Response/1.1 200 OK {…}>
58
58
 
59
59
  client.endpoints
60
- #=> { authorization_endpoint: "https://aaronparecki.com/auth", micropub: "https://aaronparecki.com/micropub", microsub: "https://aperture.p3k.io/microsub/1", redirect_uri: nil, token_endpoint: "https://aaronparecki.com/auth/token", webmention: "https://webmention.io/aaronpk/webmention" }
60
+ #=> { authorization_endpoint: "https://indieauth.com/auth", "indieauth-metadata": nil, micropub: "https://adactio.com/micropub", microsub: "https://aperture.p3k.io/microsub/152", redirect_uri: nil, token_endpoint: "https://tokens.indieauth.com/token", webmention: "https://adactio.com/webmention"}
61
61
  ```
62
62
 
63
63
  ### Exception Handling
64
64
 
65
65
  indieweb-endpoints-ruby may raise the following exceptions which are subclasses of `IndieWeb::Endpoints::Error` (which itself is a subclass of `StandardError`).
66
66
 
67
- - `IndieWeb::Endpoints::InvalidURIError`
68
67
  - `IndieWeb::Endpoints::HttpError`
68
+ - `IndieWeb::Endpoints::InvalidURIError`
69
69
  - `IndieWeb::Endpoints::SSLError`
70
70
 
71
71
  ## Contributing
@@ -4,7 +4,7 @@ Gem::Specification.new do |spec|
4
4
  spec.required_ruby_version = ">= 2.7"
5
5
 
6
6
  spec.name = "indieweb-endpoints"
7
- spec.version = "10.2.0"
7
+ spec.version = "10.3.0"
8
8
  spec.authors = ["Jason Garber"]
9
9
  spec.email = ["jason@sixtwothree.org"]
10
10
 
@@ -28,7 +28,7 @@ Gem::Specification.new do |spec|
28
28
  "source_code_uri" => "#{spec.homepage}/tree/v#{spec.version}",
29
29
  }
30
30
 
31
- spec.add_dependency "http", ">= 5.3"
31
+ spec.add_dependency "addressable", "~> 2.9"
32
32
  spec.add_dependency "link-header-parser", "~> 7.0", ">= 7.0.1"
33
33
  spec.add_dependency "nokogiri-html-ext", "~> 1.7"
34
34
  end
@@ -4,75 +4,48 @@ module IndieWeb
4
4
  module Endpoints
5
5
  # A client interface to parse IndieWeb endpoints from a URL.
6
6
  class Client
7
- # Set defaults derived from Webmention specification examples.
8
- #
9
- # @see https://www.w3.org/TR/webmention/#limits-on-get-requests
10
- # W3C Webmention Recommendation § 4.2 Limits on GET requests
11
- HTTP_CLIENT_OPTS = {
12
- follow: {
13
- max_hops: 20,
14
- },
15
- headers: {
16
- accept: "*/*",
17
- user_agent: "Mozilla/5.0 (compatible; IndieWebEndpointsDiscovery/1.0; +https://rubygems.org/gems/indieweb-endpoints)",
18
- },
19
- timeout_options: {
20
- connect_timeout: 5,
21
- read_timeout: 5,
22
- },
23
- }.freeze
24
-
25
- private_constant :HTTP_CLIENT_OPTS
26
-
27
7
  # Create a new {Client} to parse a URL for IndieWeb endpoints.
28
8
  #
29
9
  # @example
30
- # IndieWeb::Endpoints::Client.new("https://aaronparecki.com")
31
- #
32
- # @param url [String, HTTP::URI, #to_s] An absolute URL.
10
+ # IndieWeb::Endpoints::Client.new("https://adactio.com")
33
11
  #
34
- # @raise [InvalidURIError]
12
+ # @param url [String, #to_s] An absolute URL.
35
13
  def initialize(url)
36
- @uri = HTTP::URI.parse(url)
37
- rescue Addressable::URI::InvalidURIError, HTTP::URI::InvalidError => e
38
- raise InvalidURIError, e
14
+ @url = url
39
15
  end
40
16
 
41
17
  # @!visibility private
42
18
  # simplecov:disable
43
19
  def inspect
44
- "#<#{self.class}:#{format("%#0x", object_id)} @uri: #{@uri.inspect}>"
20
+ "#<#{self.class}:#{format("%#0x", object_id)} @url: #{@url}>"
45
21
  end
46
22
  # simplecov:enable
47
23
 
48
- # The IndieWeb endpoints advertised by the provided URL.i
24
+ # The IndieWeb endpoints advertised by the provided URL.
49
25
  #
50
26
  # @example
51
- # IndieWeb::Endpoints::Client.new("https://aaronparecki.com").endpoints
27
+ # IndieWeb::Endpoints::Client.new("https://adactio.com").endpoints
28
+ #
29
+ # @raise (see #response)
52
30
  #
53
31
  # @return [Hash{Symbol => String, Array, nil}]
54
32
  def endpoints
55
- @endpoints ||= Parser.new(response).to_h
33
+ @endpoints ||=
34
+ Parser.new(
35
+ body: response.body,
36
+ headers: response.fetch("link", []),
37
+ mime_type: response.content_type,
38
+ uri: response.uri
39
+ ).to_h
56
40
  end
57
41
 
58
- # The `HTTP::Response` object for the provided URL.
42
+ # The `Net::HTTPResponse` for the provided URL.
59
43
  #
60
- # @raise [HttpError, SSLError]
44
+ # @raise (see HTTP#get)
61
45
  #
62
- # @return [HTTP::Response]
46
+ # @return (see HTTP#get)
63
47
  def response
64
- @response ||= client.request(:get, @uri)
65
- rescue HTTP::Error => e
66
- raise HttpError, e
67
- rescue OpenSSL::SSL::SSLError => e
68
- raise SSLError, e
69
- end
70
-
71
- private
72
-
73
- # @return [HTTP::Client]
74
- def client
75
- @client ||= HTTP::Client.new(HTTP_CLIENT_OPTS)
48
+ @response ||= HTTP.new(@url).fetch
76
49
  end
77
50
  end
78
51
  end
@@ -2,9 +2,16 @@
2
2
 
3
3
  module IndieWeb
4
4
  module Endpoints
5
+ # Base error class.
5
6
  class Error < StandardError; end
7
+
8
+ # Re-raises rescued exceptions originating from `Net::HTTP` responses.
6
9
  class HttpError < Error; end
10
+
11
+ # Re-raises rescued `Addressable::URI::InvalidURIError`.
7
12
  class InvalidURIError < Error; end
13
+
14
+ # Re-raises rescued `OpenSSL::OpenSSLError`.
8
15
  class SSLError < Error; end
9
16
  end
10
17
  end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IndieWeb
4
+ module Endpoints
5
+ # A simple HTTP client to fetch a URL.
6
+ #
7
+ # @api private
8
+ #
9
+ # HTTP client defaults are derived from Webmention specification.
10
+ #
11
+ # @see https://www.w3.org/TR/webmention/#limits-on-get-requests
12
+ # W3C Webmention Recommendation § 4.2 Limits on GET requests
13
+ class HTTP
14
+ HTTP_CLIENT_OPTS = {
15
+ open_timeout: 5,
16
+ read_timeout: 5,
17
+ ssl_timeout: 5,
18
+ }.freeze
19
+
20
+ HTTP_MAX_REDIRECTS = 20
21
+
22
+ HTTP_REQUEST_HEADERS = {
23
+ "accept" => "*/*",
24
+ "user-agent" => "Mozilla/5.0 (compatible; IndieWebEndpointsDiscovery/1.0; +https://rubygems.org/gems/indieweb-endpoints)",
25
+ }.freeze
26
+
27
+ private_constant :HTTP_CLIENT_OPTS
28
+ private_constant :HTTP_MAX_REDIRECTS
29
+ private_constant :HTTP_REQUEST_HEADERS
30
+
31
+ # @param uri [Addressable::URI]
32
+ def initialize(uri)
33
+ @redirects = 0
34
+ @uri = uri
35
+ end
36
+
37
+ # @param uri [Addressable::URI, URI, String] An absolute URL.
38
+ #
39
+ # @raise [HttpError, InvalidURIError, SSLError]
40
+ #
41
+ # @return [Net::HTTPResponse]
42
+ def fetch(uri = @uri)
43
+ raise if @redirects > HTTP_MAX_REDIRECTS
44
+
45
+ make_request(Addressable::URI.parse(uri).normalize!)
46
+ rescue Addressable::URI::InvalidURIError => e
47
+ raise InvalidURIError, e
48
+ rescue OpenSSL::OpenSSLError => e
49
+ raise SSLError, e
50
+ rescue StandardError => e
51
+ raise HttpError, e
52
+ end
53
+
54
+ private
55
+
56
+ def handle_response(response)
57
+ case response
58
+ when Net::HTTPRedirection
59
+ @redirects += 1
60
+ fetch(response.uri.merge(response.fetch("location")))
61
+ when Net::HTTPSuccess
62
+ response
63
+ else
64
+ response.value
65
+ end
66
+ end
67
+
68
+ def make_request(uri)
69
+ Net::HTTP.start(uri.host, use_ssl: uri.scheme == "https", **HTTP_CLIENT_OPTS) do |http|
70
+ handle_response(http.get(URI.parse(uri), HTTP_REQUEST_HEADERS))
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -2,16 +2,17 @@
2
2
 
3
3
  module IndieWeb
4
4
  module Endpoints
5
- # A parser for extracting IndieWeb endpoints from an `HTTP::Response`
6
- # instance's headers and body.
7
- #
8
- # @api private
5
+ # A parser for extracting IndieWeb endpoints from an HTTP response's headers
6
+ # and body.
9
7
  class Parser
10
- # @param response [HTTP::Response]
11
- def initialize(response)
12
- @body = Nokogiri::HTML5(response.body, response.uri.to_s).resolve_urls!
13
- @headers = LinkHeaderParser.parse(response.headers.get("link"), base_uri: response.uri).group_by_relation_type
14
- @mime_type = response.mime_type
8
+ # @param body [String, #to_s]
9
+ # @param headers [Array<String>]
10
+ # @param mime_type [String, #to_s]
11
+ # @param uri [Addressable::URI, URI, #to_s]
12
+ def initialize(body: "", headers: [], mime_type: "text/html", uri: "")
13
+ @body = Nokogiri::HTML5(body.to_s, uri.to_s).resolve_urls!
14
+ @headers = LinkHeaderParser.parse(Array(headers), base_uri: uri).group_by_relation_type
15
+ @mime_type = mime_type.to_s
15
16
  end
16
17
 
17
18
  # @param identifier [String]
@@ -69,11 +70,15 @@ module IndieWeb
69
70
  #
70
71
  # @param identifier [String, #to_sym]
71
72
  #
73
+ # @raise [InvalidURIError]
74
+ #
72
75
  # @return [Array<String>]
73
76
  def matches_from_headers(identifier)
74
77
  Array(@headers[identifier]).filter_map do |header|
75
- header.target_uri unless HTTP::URI.parse(header.target_uri).fragment
78
+ header.target_uri unless Addressable::URI.parse(header.target_uri).fragment
76
79
  end
80
+ rescue Addressable::URI::InvalidURIError => e
81
+ raise InvalidURIError, e
77
82
  end
78
83
  end
79
84
  end
@@ -1,11 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "http"
3
+ require "net/http"
4
+
5
+ require "addressable"
4
6
  require "link-header-parser"
5
7
  require "nokogiri/html-ext"
6
8
 
7
- require_relative "endpoints/exceptions"
8
9
  require_relative "endpoints/client"
10
+ require_relative "endpoints/exceptions"
11
+ require_relative "endpoints/http"
9
12
  require_relative "endpoints/parser"
10
13
 
11
14
  # Welcome to the IndieWeb, a people-focused alternative to the "corporate web."
@@ -19,11 +22,11 @@ module IndieWeb
19
22
  # Convenience method for {Client#endpoints}.
20
23
  #
21
24
  # @example
22
- # IndieWeb::Endpoints.get("https://aaronparecki.com")
25
+ # IndieWeb::Endpoints.get("https://adactio.com")
23
26
  #
24
27
  # @param (see Client#initialize)
25
28
  #
26
- # @raise (see Client#initialize)
29
+ # @raise (see Client#endpoints)
27
30
  #
28
31
  # @return (see Client#endpoints)
29
32
  def self.get(url)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: indieweb-endpoints
3
3
  version: !ruby/object:Gem::Version
4
- version: 10.2.0
4
+ version: 10.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jason Garber
@@ -10,19 +10,19 @@ cert_chain: []
10
10
  date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
- name: http
13
+ name: addressable
14
14
  requirement: !ruby/object:Gem::Requirement
15
15
  requirements:
16
- - - ">="
16
+ - - "~>"
17
17
  - !ruby/object:Gem::Version
18
- version: '5.3'
18
+ version: '2.9'
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
- - - ">="
23
+ - - "~>"
24
24
  - !ruby/object:Gem::Version
25
- version: '5.3'
25
+ version: '2.9'
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: link-header-parser
28
28
  requirement: !ruby/object:Gem::Requirement
@@ -74,17 +74,18 @@ files:
74
74
  - lib/indieweb/endpoints.rb
75
75
  - lib/indieweb/endpoints/client.rb
76
76
  - lib/indieweb/endpoints/exceptions.rb
77
+ - lib/indieweb/endpoints/http.rb
77
78
  - lib/indieweb/endpoints/parser.rb
78
79
  homepage: https://github.com/indieweb/indieweb-endpoints-ruby
79
80
  licenses:
80
81
  - MIT
81
82
  metadata:
82
83
  bug_tracker_uri: https://github.com/indieweb/indieweb-endpoints-ruby/issues
83
- changelog_uri: https://github.com/indieweb/indieweb-endpoints-ruby/releases/tag/v10.2.0
84
- documentation_uri: https://rubydoc.info/gems/indieweb-endpoints/10.2.0
84
+ changelog_uri: https://github.com/indieweb/indieweb-endpoints-ruby/releases/tag/v10.3.0
85
+ documentation_uri: https://rubydoc.info/gems/indieweb-endpoints/10.3.0
85
86
  homepage_uri: https://github.com/indieweb/indieweb-endpoints-ruby
86
87
  rubygems_mfa_required: 'true'
87
- source_code_uri: https://github.com/indieweb/indieweb-endpoints-ruby/tree/v10.2.0
88
+ source_code_uri: https://github.com/indieweb/indieweb-endpoints-ruby/tree/v10.3.0
88
89
  rdoc_options: []
89
90
  require_paths:
90
91
  - lib