Skip to content

Commit 791f3fb

Browse files
committed
Rewrite and simplify parsing code
1 parent 40049ca commit 791f3fb

5 files changed

Lines changed: 65 additions & 122 deletions

File tree

lib/indieweb/endpoints.rb

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@
66

77
require_relative "endpoints/client"
88
require_relative "endpoints/parser"
9-
require_relative "endpoints/response_body_parser"
10-
require_relative "endpoints/response_headers_parser"
119

1210
module IndieWeb
1311
module Endpoints
@@ -21,13 +19,14 @@ class SSLError < Error; end
2119

2220
# Discover a URL's IndieAuth, Micropub, Microsub, and Webmention endpoints.
2321
#
24-
# Convenience method for {IndieWeb::Endpoints::Client#endpoints}.
22+
# Convenience method for {Client#endpoints}.
2523
#
2624
# @example
2725
# IndieWeb::Endpoints.get("https://aaronparecki.com")
2826
#
29-
# @param (see IndieWeb::Endpoints::Client#endpoints)
30-
# @return (see IndieWeb::Endpoints::Client#endpoints)
27+
# @param (see Client#initialize)
28+
#
29+
# @return (see Client#endpoints)
3130
def self.get(url)
3231
Client.new(url).endpoints
3332
end

lib/indieweb/endpoints/client.rb

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ class Client
1616
# client = IndieWeb::Endpoints::Client.new("https://aaronparecki.com")
1717
#
1818
# @param url [String, HTTP::URI, #to_s] an absolute URL
19-
# @raise [IndieWeb::Endpoints::InvalidURIError]
19+
#
20+
# @raise [InvalidURIError]
2021
def initialize(url)
2122
@uri = HTTP::URI.parse(url.to_s)
2223
rescue Addressable::URI::InvalidURIError => e
@@ -32,13 +33,14 @@ def inspect
3233
#
3334
# @return [Hash{Symbol => String, Array, nil}]
3435
def endpoints
35-
@endpoints ||= Parser.new(response).results
36+
@endpoints ||= Parser.new(response).to_h
3637
end
3738

38-
# The +HTTP::Response+ object returned by the provided URL.
39+
# The {HTTP::Response} object returned by the provided URL.
3940
#
4041
# @return [HTTP::Response]
41-
# @raise [IndieWeb::Endpoints::HttpError, IndieWeb::Endpoints::SSLError]
42+
#
43+
# @raise [HttpError, SSLError]
4244
def response
4345
@response ||= HTTP
4446
.follow(max_hops: 20)

lib/indieweb/endpoints/parser.rb

Lines changed: 55 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,43 @@ def initialize(response)
99
@response = response
1010
end
1111

12-
# @return [Hash{Symbol => String, Array<String>, nil}]
13-
def results
12+
# @param identifier [String]
13+
# @param node_names [Array<String>]
14+
#
15+
# @return [Array<String>]
16+
#
17+
# @raise [InvalidURIError]
18+
def matches(identifier, node_names: ["link"])
19+
results =
20+
(matches_from_headers(identifier) + matches_from_body(identifier, node_names))
21+
.compact
22+
.map! { |endpoint| response.uri.join(endpoint).to_s }
23+
24+
results.uniq!
25+
results.sort!
26+
27+
results
28+
rescue Addressable::URI::InvalidURIError => e
29+
raise InvalidURIError, e
30+
end
31+
32+
# @param (see #matches)
33+
#
34+
# @return [String]
35+
def match(identifier, **kwargs)
36+
matches(identifier, **kwargs).first
37+
end
38+
39+
# @return [Hash{Symbol => String, Array, nil}]
40+
def to_h
1441
{
15-
authorization_endpoint: result_for(:authorization_endpoint),
16-
"indieauth-metadata": result_for(:"indieauth-metadata"),
17-
micropub: result_for(:micropub),
18-
microsub: result_for(:microsub),
19-
redirect_uri: results_for(:redirect_uri),
20-
token_endpoint: result_for(:token_endpoint),
21-
webmention: result_for(:webmention, ["link", "a"]),
42+
authorization_endpoint: match("authorization_endpoint"),
43+
"indieauth-metadata": match("indieauth-metadata"),
44+
micropub: match("micropub"),
45+
microsub: match("microsub"),
46+
redirect_uri: (redirect_uri = matches("redirect_uri")).any? ? redirect_uri : nil,
47+
token_endpoint: match("token_endpoint"),
48+
webmention: match("webmention", node_names: ["link", "a"]),
2249
}
2350
end
2451

@@ -27,38 +54,32 @@ def results
2754
# @return [HTTP::Response]
2855
attr_reader :response
2956

30-
# @return [IndieWeb::Endpoints::ResponseBodyParser]
31-
def response_body_parser
32-
@response_body_parser ||= ResponseBodyParser.new(response)
57+
# @return [Nokogiri::HTML5::Document]
58+
def body
59+
@body ||= Nokogiri::HTML5(response.body)
3360
end
3461

35-
# @return [IndieWeb::Endpoints::ResponseHeadersParser]
36-
def response_headers_parser
37-
@response_headers_parser ||= ResponseHeadersParser.new(response)
62+
# @return [Hash{Symbol => Array<LinkHeaderParser::LinkHeader>}]
63+
def headers
64+
@headers ||= LinkHeaderParser.parse(response.headers.get("link"), base: response.uri).group_by_relation_type
3865
end
3966

40-
# @param identifier [Symbol]
41-
# @param nodes [Array<String>]
42-
# @return [String, nil]
43-
def result_for(identifier, nodes = ["link"])
44-
results_for(identifier, nodes)&.first
45-
end
67+
# @return [Array<String>]
68+
def matches_from_body(identifier, node_names)
69+
return [] unless response.mime_type == "text/html"
4670

47-
# @param identifier [Symbol]
48-
# @param nodes [Array<String>]
49-
# @return [Array<String>, nil]
50-
# @raise [IndieWeb::Endpoints::InvalidURIError]
51-
def results_for(identifier, nodes = ["link"])
52-
results_from_request = [
53-
response_headers_parser.results_for(identifier),
54-
response_body_parser.results_for(identifier, nodes),
55-
].flatten.compact
71+
# Reject endpoints that contain a fragment identifier.
72+
selectors = node_names.map { |node| %(#{node}[rel~="#{identifier}"][href]:not([href*="#"])) }.join(",")
5673

57-
return if results_from_request.none?
74+
body.css(selectors).map { |element| element["href"] }
75+
end
5876

59-
results_from_request.map { |endpoint| response.uri.join(endpoint).to_s }.uniq.sort
60-
rescue Addressable::URI::InvalidURIError => e
61-
raise InvalidURIError, e
77+
# @return [Array<String>]
78+
def matches_from_headers(identifier)
79+
# Reject endpoints that contain a fragment identifier.
80+
Array(headers[identifier.to_sym])
81+
.filter { |header| !HTTP::URI.parse(header.target_uri).fragment }
82+
.map(&:target_uri)
6283
end
6384
end
6485
end

lib/indieweb/endpoints/response_body_parser.rb

Lines changed: 0 additions & 43 deletions
This file was deleted.

lib/indieweb/endpoints/response_headers_parser.rb

Lines changed: 0 additions & 36 deletions
This file was deleted.

0 commit comments

Comments
 (0)