Close http connection in perform method of Request class (#6889)
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.
This commit is contained in:
committed by
Eugen Rochko
parent
4e71b104e6
commit
54b273bf99
@ -24,43 +24,44 @@ class FetchAtomService < BaseService
|
||||
|
||||
def process(url, terminal = false)
|
||||
@url = url
|
||||
perform_request
|
||||
process_response(terminal)
|
||||
perform_request { |response| process_response(response, terminal) }
|
||||
end
|
||||
|
||||
def perform_request
|
||||
def perform_request(&block)
|
||||
accept = 'text/html'
|
||||
accept = 'application/activity+json, application/ld+json, application/atom+xml, ' + accept unless @unsupported_activity
|
||||
|
||||
@response = Request.new(:get, @url)
|
||||
.add_headers('Accept' => accept)
|
||||
.perform
|
||||
Request.new(:get, @url).add_headers('Accept' => accept).perform(&block)
|
||||
end
|
||||
|
||||
def process_response(terminal = false)
|
||||
return nil if @response.code != 200
|
||||
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 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]
|
||||
[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]
|
||||
[json['id'], { prefetched_body: response.to_s, id: true }, :activitypub]
|
||||
else
|
||||
@unsupported_activity = true
|
||||
nil
|
||||
end
|
||||
elsif @response['Link'] && !terminal && link_header.find_link(%w(rel alternate))
|
||||
process_headers
|
||||
elsif @response.mime_type == 'text/html' && !terminal
|
||||
process_html
|
||||
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
|
||||
page = Nokogiri::HTML(@response.to_s)
|
||||
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' }
|
||||
@ -71,7 +72,7 @@ class FetchAtomService < BaseService
|
||||
result
|
||||
end
|
||||
|
||||
def process_headers
|
||||
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))
|
||||
|
||||
@ -81,7 +82,7 @@ class FetchAtomService < BaseService
|
||||
result
|
||||
end
|
||||
|
||||
def link_header
|
||||
@link_header ||= LinkHeader.parse(@response['Link'].is_a?(Array) ? @response['Link'].first : @response['Link'])
|
||||
def parse_link_header(response)
|
||||
LinkHeader.parse(response['Link'].is_a?(Array) ? response['Link'].first : response['Link'])
|
||||
end
|
||||
end
|
||||
|
@ -36,15 +36,24 @@ class FetchLinkCardService < BaseService
|
||||
|
||||
def process_url
|
||||
@card ||= PreviewCard.new(url: @url)
|
||||
res = Request.new(:head, @url).perform
|
||||
|
||||
return if res.code != 405 && (res.code != 200 || res.mime_type != 'text/html')
|
||||
failed = Request.new(:head, @url).perform do |res|
|
||||
res.code != 405 && (res.code != 200 || res.mime_type != 'text/html')
|
||||
end
|
||||
|
||||
@response = Request.new(:get, @url).perform
|
||||
return if failed
|
||||
|
||||
return if @response.code != 200 || @response.mime_type != 'text/html'
|
||||
Request.new(:get, @url).perform do |res|
|
||||
if res.code == 200 && res.mime_type == 'text/html'
|
||||
@html = res.to_s
|
||||
@html_charset = res.charset
|
||||
else
|
||||
@html = nil
|
||||
@html_charset = nil
|
||||
end
|
||||
end
|
||||
|
||||
@html = @response.to_s
|
||||
return if @html.nil?
|
||||
|
||||
attempt_oembed || attempt_opengraph
|
||||
end
|
||||
@ -118,7 +127,7 @@ class FetchLinkCardService < BaseService
|
||||
detector = CharlockHolmes::EncodingDetector.new
|
||||
detector.strip_tags = true
|
||||
|
||||
guess = detector.detect(@html, @response.charset)
|
||||
guess = detector.detect(@html, @html_charset)
|
||||
page = Nokogiri::HTML(@html, nil, guess&.fetch(:encoding, nil))
|
||||
|
||||
if meta_property(page, 'twitter:player')
|
||||
|
@ -179,11 +179,10 @@ class ResolveAccountService < BaseService
|
||||
def atom_body
|
||||
return @atom_body if defined?(@atom_body)
|
||||
|
||||
response = Request.new(:get, atom_url).perform
|
||||
|
||||
raise Mastodon::UnexpectedResponseError, response unless response.code == 200
|
||||
|
||||
@atom_body = response.to_s
|
||||
@atom_body = Request.new(:get, atom_url).perform do |response|
|
||||
raise Mastodon::UnexpectedResponseError, response unless response.code == 200
|
||||
response.to_s
|
||||
end
|
||||
end
|
||||
|
||||
def actor_json
|
||||
|
@ -12,11 +12,9 @@ class SendInteractionService < BaseService
|
||||
|
||||
return if !target_account.ostatus? || block_notification?
|
||||
|
||||
delivery = build_request.perform
|
||||
|
||||
raise Mastodon::UnexpectedResponseError, delivery unless delivery.code > 199 && delivery.code < 300
|
||||
|
||||
delivery.connection&.close
|
||||
build_request.perform do |delivery|
|
||||
raise Mastodon::UnexpectedResponseError, delivery unless delivery.code > 199 && delivery.code < 300
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -6,21 +6,21 @@ class SubscribeService < BaseService
|
||||
|
||||
@account = account
|
||||
@account.secret = SecureRandom.hex
|
||||
@response = build_request.perform
|
||||
|
||||
if response_failed_permanently?
|
||||
# We're not allowed to subscribe. Fail and move on.
|
||||
@account.secret = ''
|
||||
@account.save!
|
||||
elsif response_successful?
|
||||
# The subscription will be confirmed asynchronously.
|
||||
@account.save!
|
||||
else
|
||||
# The response was either a 429 rate limit, or a 5xx error.
|
||||
# We need to retry at a later time. Fail loudly!
|
||||
raise Mastodon::UnexpectedResponseError, @response
|
||||
build_request.perform do |response|
|
||||
if response_failed_permanently? response
|
||||
# We're not allowed to subscribe. Fail and move on.
|
||||
@account.secret = ''
|
||||
@account.save!
|
||||
elsif response_successful? response
|
||||
# The subscription will be confirmed asynchronously.
|
||||
@account.save!
|
||||
else
|
||||
# The response was either a 429 rate limit, or a 5xx error.
|
||||
# We need to retry at a later time. Fail loudly!
|
||||
raise Mastodon::UnexpectedResponseError, response
|
||||
end
|
||||
end
|
||||
@response.connection&.close
|
||||
end
|
||||
|
||||
private
|
||||
@ -47,12 +47,12 @@ class SubscribeService < BaseService
|
||||
end
|
||||
|
||||
# Any response in the 3xx or 4xx range, except for 429 (rate limit)
|
||||
def response_failed_permanently?
|
||||
(@response.status.redirect? || @response.status.client_error?) && !@response.status.too_many_requests?
|
||||
def response_failed_permanently?(response)
|
||||
(response.status.redirect? || response.status.client_error?) && !response.status.too_many_requests?
|
||||
end
|
||||
|
||||
# Any response in the 2xx range
|
||||
def response_successful?
|
||||
@response.status.success?
|
||||
def response_successful?(response)
|
||||
response.status.success?
|
||||
end
|
||||
end
|
||||
|
@ -7,10 +7,9 @@ class UnsubscribeService < BaseService
|
||||
@account = account
|
||||
|
||||
begin
|
||||
@response = build_request.perform
|
||||
|
||||
Rails.logger.debug "PuSH unsubscribe for #{@account.acct} failed: #{@response.status}" unless @response.status.success?
|
||||
@response.connection&.close
|
||||
build_request.perform do |response|
|
||||
Rails.logger.debug "PuSH unsubscribe for #{@account.acct} failed: #{response.status}" unless response.status.success?
|
||||
end
|
||||
rescue HTTP::Error, OpenSSL::SSL::SSLError => e
|
||||
Rails.logger.debug "PuSH unsubscribe for #{@account.acct} failed: #{e}"
|
||||
end
|
||||
|
Reference in New Issue
Block a user