-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathclient.rb
148 lines (127 loc) · 4.4 KB
/
client.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# frozen_string_literal: true
module Webmention
class Client
@registered_parsers = {}
class << self
# @api private
attr_reader :registered_parsers
end
# @return [Webmention::Url]
attr_reader :source_url
# @return [Webmention::Url]
attr_reader :vouch_url
# @api private
def self.register_parser(klass)
klass.mime_types.each { |mime_type| @registered_parsers[mime_type] = klass }
end
# Create a new Webmention::Client.
#
# @example
# Webmention::Client.new('https://jgarber.example/posts/100')
#
# @example
# Webmention::Client.new('https://jgarber.example/posts/100', vouch: 'https://tantek.example/notes/1')
#
# @param source [String, HTTP::URI, #to_s]
# An absolute URL representing a source document.
# @param vouch [String, HTTP::URI, #to_s]
# An absolute URL representing a document vouching for the source document.
# See https://indieweb.org/Vouch for additional details.
#
# @return [Webmention::Client]
def initialize(source, vouch: nil)
@source_url = Url.new(source)
@vouch_url = Url.new(vouch)
end
# :nocov:
# @return [String]
def inspect
"#<#{self.class}:#{format("%#0x", object_id)} " \
"source_url: #{source_url} " \
"vouch_url: #{vouch_url}>"
end
# :nocov:
# Retrieve unique URLs mentioned by this client's source URL.
#
# @example
# client = Webmention::Client.new('https://jgarber.example/posts/100')
# client.mentioned_urls
#
# @raise [NoMethodError]
# Raised when response is a Webmention::ErrorResponse or response is of an
# unsupported MIME type.
#
# @return [Array<String>]
def mentioned_urls
response = source_url.response
urls = Set.new(
self.class
.registered_parsers[response.mime_type]
.new(response.body, response.uri)
.results
)
urls.reject! { |url| url.match(/^#{response.uri}(?:#.*)?$/) }
urls.to_a.sort
end
# Send a webmention from this client's source URL to a target URL.
#
# @example
# client = Webmention::Client.new('https://jgarber.example/posts/100')
# client.send_webmention('https://aaronpk.example/notes/1')
#
# @param target [String, HTTP::URI, #to_s]
# An absolute URL representing a target document.
#
# @return [Webmention::Response, Webmention::ErrorResponse]
def send_webmention(target)
target_url = Url.new(target)
# A Webmention endpoint exists. Send the request and return the response.
if target_url.webmention_endpoint?
return Request.post(target_url.webmention_endpoint, **request_options_for(target))
end
# An error was encountered fetching the target URL. Return the response.
return target_url.response unless target_url.response.ok?
# No Webmention endpoint exists. Return a new ErrorResponse.
ErrorResponse.new("No webmention endpoint found for target URL #{target}", target_url.response.request)
end
# Send webmentions from this client's source URL to multiple target URLs.
#
# @example
# client = Webmention::Client.new('https://jgarber.example/posts/100')
# targets = ['https://aaronpk.example/notes/1', 'https://adactio.example/notes/1']
# client.send_webmentions(targets)
#
# @param targets [Array<String, HTTP::URI, #to_s>]
# An array of absolute URLs representing multiple target documents.
#
# @return [Array<Webmention::Response, Webmention::ErrorResponse>]
def send_webmentions(*targets)
targets.map { |target| send_webmention(target) }
end
# Verify that this client's source URL links to a target URL.
#
# @param target [String, HTTP::URI, #to_s]
# An absolute URL representing a target document.
#
# @raise (see Webmention::Client#mentioned_urls)
#
# @return [Webmention::Verification]
def verify_webmention(target)
Verification.new(source_url, Url.new(target), vouch_url: vouch_url)
end
private
# @param target [String, HTTP::URI, #to_s]
#
# @return [Hash{Symbol => String}]
def request_options_for(target)
opts = {
source: source_url,
target: target,
vouch: vouch_url
}
opts.transform_values! { |value| value.to_s.strip }
opts.reject! { |_, value| value.empty? }
opts
end
end
end