From 54b273bf993888cd079113dd588cb7a90228b93b Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Sat, 24 Mar 2018 20:49:54 +0900 Subject: 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. --- app/services/fetch_atom_service.rb | 47 ++++++++++++++++---------------- app/services/fetch_link_card_service.rb | 21 ++++++++++---- app/services/resolve_account_service.rb | 9 +++--- app/services/send_interaction_service.rb | 8 ++---- app/services/subscribe_service.rb | 36 ++++++++++++------------ app/services/unsubscribe_service.rb | 7 ++--- 6 files changed, 67 insertions(+), 61 deletions(-) (limited to 'app/services') diff --git a/app/services/fetch_atom_service.rb b/app/services/fetch_atom_service.rb index c07859845..48ad5dcd3 100644 --- a/app/services/fetch_atom_service.rb +++ b/app/services/fetch_atom_service.rb @@ -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 diff --git a/app/services/fetch_link_card_service.rb b/app/services/fetch_link_card_service.rb index 8f252e64c..26deb5ecc 100644 --- a/app/services/fetch_link_card_service.rb +++ b/app/services/fetch_link_card_service.rb @@ -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') diff --git a/app/services/resolve_account_service.rb b/app/services/resolve_account_service.rb index fd6d30605..034821dc0 100644 --- a/app/services/resolve_account_service.rb +++ b/app/services/resolve_account_service.rb @@ -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 diff --git a/app/services/send_interaction_service.rb b/app/services/send_interaction_service.rb index fabba8a3e..3419043e5 100644 --- a/app/services/send_interaction_service.rb +++ b/app/services/send_interaction_service.rb @@ -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 diff --git a/app/services/subscribe_service.rb b/app/services/subscribe_service.rb index 2f725e2ec..2893b5410 100644 --- a/app/services/subscribe_service.rb +++ b/app/services/subscribe_service.rb @@ -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 diff --git a/app/services/unsubscribe_service.rb b/app/services/unsubscribe_service.rb index 01f5c6b7a..95c1fb4fc 100644 --- a/app/services/unsubscribe_service.rb +++ b/app/services/unsubscribe_service.rb @@ -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 -- cgit