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 +4 -4
- data/README.md +7 -7
- data/indieweb-endpoints.gemspec +2 -2
- data/lib/indieweb/endpoints/client.rb +19 -46
- data/lib/indieweb/endpoints/exceptions.rb +7 -0
- data/lib/indieweb/endpoints/http.rb +75 -0
- data/lib/indieweb/endpoints/parser.rb +15 -10
- data/lib/indieweb/endpoints.rb +7 -4
- metadata +10 -9
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ea9b5e5a041478750b3af2f317436b40df1e360994107298737874aa914ad669
|
|
4
|
+
data.tar.gz: 3b2efbfeef50178c5a1583160483fa8a920d49f7a5d5bd82d81cfdcde8b995b4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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://
|
|
41
|
-
#=> { authorization_endpoint: "https://
|
|
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 [
|
|
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://
|
|
54
|
-
#=> #<IndieWeb::Endpoints::Client uri:
|
|
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://
|
|
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
|
data/indieweb-endpoints.gemspec
CHANGED
|
@@ -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.
|
|
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 "
|
|
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://
|
|
31
|
-
#
|
|
32
|
-
# @param url [String, HTTP::URI, #to_s] An absolute URL.
|
|
10
|
+
# IndieWeb::Endpoints::Client.new("https://adactio.com")
|
|
33
11
|
#
|
|
34
|
-
# @
|
|
12
|
+
# @param url [String, #to_s] An absolute URL.
|
|
35
13
|
def initialize(url)
|
|
36
|
-
@
|
|
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)} @
|
|
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.
|
|
24
|
+
# The IndieWeb endpoints advertised by the provided URL.
|
|
49
25
|
#
|
|
50
26
|
# @example
|
|
51
|
-
# IndieWeb::Endpoints::Client.new("https://
|
|
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 ||=
|
|
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 `
|
|
42
|
+
# The `Net::HTTPResponse` for the provided URL.
|
|
59
43
|
#
|
|
60
|
-
# @raise
|
|
44
|
+
# @raise (see HTTP#get)
|
|
61
45
|
#
|
|
62
|
-
# @return
|
|
46
|
+
# @return (see HTTP#get)
|
|
63
47
|
def response
|
|
64
|
-
@response ||=
|
|
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
|
|
6
|
-
#
|
|
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
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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
|
|
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
|
data/lib/indieweb/endpoints.rb
CHANGED
|
@@ -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://
|
|
25
|
+
# IndieWeb::Endpoints.get("https://adactio.com")
|
|
23
26
|
#
|
|
24
27
|
# @param (see Client#initialize)
|
|
25
28
|
#
|
|
26
|
-
# @raise (see Client#
|
|
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.
|
|
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:
|
|
13
|
+
name: addressable
|
|
14
14
|
requirement: !ruby/object:Gem::Requirement
|
|
15
15
|
requirements:
|
|
16
|
-
- - "
|
|
16
|
+
- - "~>"
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
|
-
version: '
|
|
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: '
|
|
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.
|
|
84
|
-
documentation_uri: https://rubydoc.info/gems/indieweb-endpoints/10.
|
|
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.
|
|
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
|