11c19f6cc9
HTTP connections must be explicitly closed in many cases, and letting perform method close connections makes its callers less redundant and prevent them from forgetting to close connections.
88 lines
3.1 KiB
Ruby
88 lines
3.1 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
class FetchAtomService < BaseService
|
|
include JsonLdHelper
|
|
|
|
def call(url)
|
|
return if url.blank?
|
|
|
|
result = process(url)
|
|
|
|
# retry without ActivityPub
|
|
result ||= process(url) if @unsupported_activity
|
|
|
|
result
|
|
rescue OpenSSL::SSL::SSLError => e
|
|
Rails.logger.debug "SSL error: #{e}"
|
|
nil
|
|
rescue HTTP::ConnectionError => e
|
|
Rails.logger.debug "HTTP ConnectionError: #{e}"
|
|
nil
|
|
end
|
|
|
|
private
|
|
|
|
def process(url, terminal = false)
|
|
@url = url
|
|
perform_request { |response| process_response(response, terminal) }
|
|
end
|
|
|
|
def perform_request(&block)
|
|
accept = 'text/html'
|
|
accept = 'application/activity+json, application/ld+json, application/atom+xml, ' + accept unless @unsupported_activity
|
|
|
|
Request.new(:get, @url).add_headers('Accept' => accept).perform(&block)
|
|
end
|
|
|
|
def process_response(response, terminal = false)
|
|
return nil if response.code != 200
|
|
|
|
if response.mime_type == 'application/atom+xml'
|
|
[@url, { prefetched_body: response.to_s }, :ostatus]
|
|
elsif ['application/activity+json', 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"'].include?(response.mime_type)
|
|
json = body_to_json(response.to_s)
|
|
if supported_context?(json) && json['type'] == 'Person' && json['inbox'].present?
|
|
[json['id'], { prefetched_body: response.to_s, id: true }, :activitypub]
|
|
elsif supported_context?(json) && json['type'] == 'Note'
|
|
[json['id'], { prefetched_body: response.to_s, id: true }, :activitypub]
|
|
else
|
|
@unsupported_activity = true
|
|
nil
|
|
end
|
|
elsif !terminal
|
|
link_header = response['Link'] && parse_link_header(response)
|
|
|
|
if link_header&.find_link(%w(rel alternate))
|
|
process_link_headers(link_header)
|
|
elsif response.mime_type == 'text/html'
|
|
process_html(response)
|
|
end
|
|
end
|
|
end
|
|
|
|
def process_html(response)
|
|
page = Nokogiri::HTML(response.to_s)
|
|
|
|
json_link = page.xpath('//link[@rel="alternate"]').find { |link| ['application/activity+json', 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"'].include?(link['type']) }
|
|
atom_link = page.xpath('//link[@rel="alternate"]').find { |link| link['type'] == 'application/atom+xml' }
|
|
|
|
result ||= process(json_link['href'], terminal: true) unless json_link.nil? || @unsupported_activity
|
|
result ||= process(atom_link['href'], terminal: true) unless atom_link.nil?
|
|
|
|
result
|
|
end
|
|
|
|
def process_link_headers(link_header)
|
|
json_link = link_header.find_link(%w(rel alternate), %w(type application/activity+json)) || link_header.find_link(%w(rel alternate), ['type', 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"'])
|
|
atom_link = link_header.find_link(%w(rel alternate), %w(type application/atom+xml))
|
|
|
|
result ||= process(json_link.href, terminal: true) unless json_link.nil? || @unsupported_activity
|
|
result ||= process(atom_link.href, terminal: true) unless atom_link.nil?
|
|
|
|
result
|
|
end
|
|
|
|
def parse_link_header(response)
|
|
LinkHeader.parse(response['Link'].is_a?(Array) ? response['Link'].first : response['Link'])
|
|
end
|
|
end
|