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.lolsob-rspec
parent
e8a1d0b4d4
commit
11c19f6cc9
|
@ -60,9 +60,9 @@ module JsonLdHelper
|
||||||
end
|
end
|
||||||
|
|
||||||
def fetch_resource_without_id_validation(uri)
|
def fetch_resource_without_id_validation(uri)
|
||||||
response = build_request(uri).perform
|
build_request(uri).perform do |response|
|
||||||
return if response.code != 200
|
response.code == 200 ? body_to_json(response.to_s) : nil
|
||||||
body_to_json(response.to_s)
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def body_to_json(body)
|
def body_to_json(body)
|
||||||
|
|
|
@ -13,14 +13,13 @@ class ProviderDiscovery < OEmbed::ProviderDiscovery
|
||||||
def discover_provider(url, **options)
|
def discover_provider(url, **options)
|
||||||
format = options[:format]
|
format = options[:format]
|
||||||
|
|
||||||
if options[:html]
|
html = if options[:html]
|
||||||
html = Nokogiri::HTML(options[:html])
|
Nokogiri::HTML(options[:html])
|
||||||
else
|
else
|
||||||
res = Request.new(:get, url).perform
|
Request.new(:get, url).perform do |res|
|
||||||
|
|
||||||
raise OEmbed::NotFound, url if res.code != 200 || res.mime_type != 'text/html'
|
raise OEmbed::NotFound, url if res.code != 200 || res.mime_type != 'text/html'
|
||||||
|
Nokogiri::HTML(res.to_s)
|
||||||
html = Nokogiri::HTML(res.to_s)
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if format.nil? || format == :json
|
if format.nil? || format == :json
|
||||||
|
|
|
@ -33,11 +33,19 @@ class Request
|
||||||
end
|
end
|
||||||
|
|
||||||
def perform
|
def perform
|
||||||
http_client.headers(headers).public_send(@verb, @url.to_s, @options)
|
begin
|
||||||
|
response = http_client.headers(headers).public_send(@verb, @url.to_s, @options)
|
||||||
rescue => e
|
rescue => e
|
||||||
raise e.class, "#{e.message} on #{@url}", e.backtrace[0]
|
raise e.class, "#{e.message} on #{@url}", e.backtrace[0]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
begin
|
||||||
|
yield response
|
||||||
|
ensure
|
||||||
|
http_client.close
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def headers
|
def headers
|
||||||
(@account ? @headers.merge('Signature' => signature) : @headers).without(REQUEST_TARGET)
|
(@account ? @headers.merge('Signature' => signature) : @headers).without(REQUEST_TARGET)
|
||||||
end
|
end
|
||||||
|
@ -88,7 +96,7 @@ class Request
|
||||||
end
|
end
|
||||||
|
|
||||||
def http_client
|
def http_client
|
||||||
HTTP.timeout(:per_operation, timeout).follow(max_hops: 2)
|
@http_client ||= HTTP.timeout(:per_operation, timeout).follow(max_hops: 2)
|
||||||
end
|
end
|
||||||
|
|
||||||
class Socket < TCPSocket
|
class Socket < TCPSocket
|
||||||
|
|
|
@ -21,9 +21,8 @@ module Remotable
|
||||||
return if !%w(http https).include?(parsed_url.scheme) || parsed_url.host.empty? || self[attribute_name] == url
|
return if !%w(http https).include?(parsed_url.scheme) || parsed_url.host.empty? || self[attribute_name] == url
|
||||||
|
|
||||||
begin
|
begin
|
||||||
response = Request.new(:get, url).perform
|
Request.new(:get, url).perform do |response|
|
||||||
|
next if response.code != 200
|
||||||
return if response.code != 200
|
|
||||||
|
|
||||||
matches = response.headers['content-disposition']&.match(/filename="([^"]*)"/)
|
matches = response.headers['content-disposition']&.match(/filename="([^"]*)"/)
|
||||||
filename = matches.nil? ? parsed_url.path.split('/').last : matches[1]
|
filename = matches.nil? ? parsed_url.path.split('/').last : matches[1]
|
||||||
|
@ -38,6 +37,7 @@ module Remotable
|
||||||
send("#{attachment_name}_file_name=", basename + extname)
|
send("#{attachment_name}_file_name=", basename + extname)
|
||||||
|
|
||||||
self[attribute_name] = url if has_attribute?(attribute_name)
|
self[attribute_name] = url if has_attribute?(attribute_name)
|
||||||
|
end
|
||||||
rescue HTTP::TimeoutError, HTTP::ConnectionError, OpenSSL::SSL::SSLError, Paperclip::Errors::NotIdentifiedByImageMagickError, Addressable::URI::InvalidURIError, Mastodon::HostValidationError => e
|
rescue HTTP::TimeoutError, HTTP::ConnectionError, OpenSSL::SSL::SSLError, Paperclip::Errors::NotIdentifiedByImageMagickError, Addressable::URI::InvalidURIError, Mastodon::HostValidationError => e
|
||||||
Rails.logger.debug "Error fetching remote #{attachment_name}: #{e}"
|
Rails.logger.debug "Error fetching remote #{attachment_name}: #{e}"
|
||||||
nil
|
nil
|
||||||
|
|
|
@ -24,43 +24,44 @@ class FetchAtomService < BaseService
|
||||||
|
|
||||||
def process(url, terminal = false)
|
def process(url, terminal = false)
|
||||||
@url = url
|
@url = url
|
||||||
perform_request
|
perform_request { |response| process_response(response, terminal) }
|
||||||
process_response(terminal)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def perform_request
|
def perform_request(&block)
|
||||||
accept = 'text/html'
|
accept = 'text/html'
|
||||||
accept = 'application/activity+json, application/ld+json, application/atom+xml, ' + accept unless @unsupported_activity
|
accept = 'application/activity+json, application/ld+json, application/atom+xml, ' + accept unless @unsupported_activity
|
||||||
|
|
||||||
@response = Request.new(:get, @url)
|
Request.new(:get, @url).add_headers('Accept' => accept).perform(&block)
|
||||||
.add_headers('Accept' => accept)
|
|
||||||
.perform
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def process_response(terminal = false)
|
def process_response(response, terminal = false)
|
||||||
return nil if @response.code != 200
|
return nil if response.code != 200
|
||||||
|
|
||||||
if @response.mime_type == 'application/atom+xml'
|
if response.mime_type == 'application/atom+xml'
|
||||||
[@url, { prefetched_body: @response.to_s }, :ostatus]
|
[@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)
|
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)
|
json = body_to_json(response.to_s)
|
||||||
if supported_context?(json) && json['type'] == 'Person' && json['inbox'].present?
|
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'
|
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
|
else
|
||||||
@unsupported_activity = true
|
@unsupported_activity = true
|
||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
elsif @response['Link'] && !terminal && link_header.find_link(%w(rel alternate))
|
elsif !terminal
|
||||||
process_headers
|
link_header = response['Link'] && parse_link_header(response)
|
||||||
elsif @response.mime_type == 'text/html' && !terminal
|
|
||||||
process_html
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
def process_html
|
def process_html(response)
|
||||||
page = Nokogiri::HTML(@response.to_s)
|
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']) }
|
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' }
|
atom_link = page.xpath('//link[@rel="alternate"]').find { |link| link['type'] == 'application/atom+xml' }
|
||||||
|
@ -71,7 +72,7 @@ class FetchAtomService < BaseService
|
||||||
result
|
result
|
||||||
end
|
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"'])
|
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))
|
atom_link = link_header.find_link(%w(rel alternate), %w(type application/atom+xml))
|
||||||
|
|
||||||
|
@ -81,7 +82,7 @@ class FetchAtomService < BaseService
|
||||||
result
|
result
|
||||||
end
|
end
|
||||||
|
|
||||||
def link_header
|
def parse_link_header(response)
|
||||||
@link_header ||= LinkHeader.parse(@response['Link'].is_a?(Array) ? @response['Link'].first : @response['Link'])
|
LinkHeader.parse(response['Link'].is_a?(Array) ? response['Link'].first : response['Link'])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -36,15 +36,24 @@ class FetchLinkCardService < BaseService
|
||||||
|
|
||||||
def process_url
|
def process_url
|
||||||
@card ||= PreviewCard.new(url: @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
|
attempt_oembed || attempt_opengraph
|
||||||
end
|
end
|
||||||
|
@ -118,7 +127,7 @@ class FetchLinkCardService < BaseService
|
||||||
detector = CharlockHolmes::EncodingDetector.new
|
detector = CharlockHolmes::EncodingDetector.new
|
||||||
detector.strip_tags = true
|
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))
|
page = Nokogiri::HTML(@html, nil, guess&.fetch(:encoding, nil))
|
||||||
|
|
||||||
if meta_property(page, 'twitter:player')
|
if meta_property(page, 'twitter:player')
|
||||||
|
|
|
@ -179,11 +179,10 @@ class ResolveAccountService < BaseService
|
||||||
def atom_body
|
def atom_body
|
||||||
return @atom_body if defined?(@atom_body)
|
return @atom_body if defined?(@atom_body)
|
||||||
|
|
||||||
response = Request.new(:get, atom_url).perform
|
@atom_body = Request.new(:get, atom_url).perform do |response|
|
||||||
|
|
||||||
raise Mastodon::UnexpectedResponseError, response unless response.code == 200
|
raise Mastodon::UnexpectedResponseError, response unless response.code == 200
|
||||||
|
response.to_s
|
||||||
@atom_body = response.to_s
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def actor_json
|
def actor_json
|
||||||
|
|
|
@ -12,11 +12,9 @@ class SendInteractionService < BaseService
|
||||||
|
|
||||||
return if !target_account.ostatus? || block_notification?
|
return if !target_account.ostatus? || block_notification?
|
||||||
|
|
||||||
delivery = build_request.perform
|
build_request.perform do |delivery|
|
||||||
|
|
||||||
raise Mastodon::UnexpectedResponseError, delivery unless delivery.code > 199 && delivery.code < 300
|
raise Mastodon::UnexpectedResponseError, delivery unless delivery.code > 199 && delivery.code < 300
|
||||||
|
end
|
||||||
delivery.connection&.close
|
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
|
@ -6,21 +6,21 @@ class SubscribeService < BaseService
|
||||||
|
|
||||||
@account = account
|
@account = account
|
||||||
@account.secret = SecureRandom.hex
|
@account.secret = SecureRandom.hex
|
||||||
@response = build_request.perform
|
|
||||||
|
|
||||||
if response_failed_permanently?
|
build_request.perform do |response|
|
||||||
|
if response_failed_permanently? response
|
||||||
# We're not allowed to subscribe. Fail and move on.
|
# We're not allowed to subscribe. Fail and move on.
|
||||||
@account.secret = ''
|
@account.secret = ''
|
||||||
@account.save!
|
@account.save!
|
||||||
elsif response_successful?
|
elsif response_successful? response
|
||||||
# The subscription will be confirmed asynchronously.
|
# The subscription will be confirmed asynchronously.
|
||||||
@account.save!
|
@account.save!
|
||||||
else
|
else
|
||||||
# The response was either a 429 rate limit, or a 5xx error.
|
# The response was either a 429 rate limit, or a 5xx error.
|
||||||
# We need to retry at a later time. Fail loudly!
|
# We need to retry at a later time. Fail loudly!
|
||||||
raise Mastodon::UnexpectedResponseError, @response
|
raise Mastodon::UnexpectedResponseError, response
|
||||||
|
end
|
||||||
end
|
end
|
||||||
@response.connection&.close
|
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
@ -47,12 +47,12 @@ class SubscribeService < BaseService
|
||||||
end
|
end
|
||||||
|
|
||||||
# Any response in the 3xx or 4xx range, except for 429 (rate limit)
|
# Any response in the 3xx or 4xx range, except for 429 (rate limit)
|
||||||
def response_failed_permanently?
|
def response_failed_permanently?(response)
|
||||||
(@response.status.redirect? || @response.status.client_error?) && !@response.status.too_many_requests?
|
(response.status.redirect? || response.status.client_error?) && !response.status.too_many_requests?
|
||||||
end
|
end
|
||||||
|
|
||||||
# Any response in the 2xx range
|
# Any response in the 2xx range
|
||||||
def response_successful?
|
def response_successful?(response)
|
||||||
@response.status.success?
|
response.status.success?
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -7,10 +7,9 @@ class UnsubscribeService < BaseService
|
||||||
@account = account
|
@account = account
|
||||||
|
|
||||||
begin
|
begin
|
||||||
@response = build_request.perform
|
build_request.perform do |response|
|
||||||
|
Rails.logger.debug "PuSH unsubscribe for #{@account.acct} failed: #{response.status}" unless response.status.success?
|
||||||
Rails.logger.debug "PuSH unsubscribe for #{@account.acct} failed: #{@response.status}" unless @response.status.success?
|
end
|
||||||
@response.connection&.close
|
|
||||||
rescue HTTP::Error, OpenSSL::SSL::SSLError => e
|
rescue HTTP::Error, OpenSSL::SSL::SSLError => e
|
||||||
Rails.logger.debug "PuSH unsubscribe for #{@account.acct} failed: #{e}"
|
Rails.logger.debug "PuSH unsubscribe for #{@account.acct} failed: #{e}"
|
||||||
end
|
end
|
||||||
|
|
|
@ -12,11 +12,10 @@ class ActivityPub::DeliveryWorker
|
||||||
@source_account = Account.find(source_account_id)
|
@source_account = Account.find(source_account_id)
|
||||||
@inbox_url = inbox_url
|
@inbox_url = inbox_url
|
||||||
|
|
||||||
perform_request
|
perform_request do |response|
|
||||||
|
raise Mastodon::UnexpectedResponseError, response unless response_successful? response
|
||||||
|
end
|
||||||
|
|
||||||
raise Mastodon::UnexpectedResponseError, @response unless response_successful?
|
|
||||||
|
|
||||||
@response.connection&.close
|
|
||||||
failure_tracker.track_success!
|
failure_tracker.track_success!
|
||||||
rescue => e
|
rescue => e
|
||||||
failure_tracker.track_failure!
|
failure_tracker.track_failure!
|
||||||
|
@ -31,12 +30,12 @@ class ActivityPub::DeliveryWorker
|
||||||
request.add_headers(HEADERS)
|
request.add_headers(HEADERS)
|
||||||
end
|
end
|
||||||
|
|
||||||
def perform_request
|
def perform_request(&block)
|
||||||
@response = build_request.perform
|
build_request.perform(&block)
|
||||||
end
|
end
|
||||||
|
|
||||||
def response_successful?
|
def response_successful?(response)
|
||||||
@response.code > 199 && @response.code < 300
|
response.code > 199 && response.code < 300
|
||||||
end
|
end
|
||||||
|
|
||||||
def failure_tracker
|
def failure_tracker
|
||||||
|
|
|
@ -21,8 +21,8 @@ class Pubsubhubbub::ConfirmationWorker
|
||||||
def process_confirmation
|
def process_confirmation
|
||||||
prepare_subscription
|
prepare_subscription
|
||||||
|
|
||||||
confirm_callback
|
callback_get_with_params
|
||||||
logger.debug "Confirming PuSH subscription for #{subscription.callback_url} with challenge #{challenge}: #{callback_response_body}"
|
logger.debug "Confirming PuSH subscription for #{subscription.callback_url} with challenge #{challenge}: #{@callback_response_body}"
|
||||||
|
|
||||||
update_subscription
|
update_subscription
|
||||||
end
|
end
|
||||||
|
@ -44,7 +44,7 @@ class Pubsubhubbub::ConfirmationWorker
|
||||||
end
|
end
|
||||||
|
|
||||||
def response_matches_challenge?
|
def response_matches_challenge?
|
||||||
callback_response_body == challenge
|
@callback_response_body == challenge
|
||||||
end
|
end
|
||||||
|
|
||||||
def subscribing?
|
def subscribing?
|
||||||
|
@ -55,16 +55,10 @@ class Pubsubhubbub::ConfirmationWorker
|
||||||
mode == 'unsubscribe'
|
mode == 'unsubscribe'
|
||||||
end
|
end
|
||||||
|
|
||||||
def confirm_callback
|
|
||||||
@_confirm_callback ||= callback_get_with_params
|
|
||||||
end
|
|
||||||
|
|
||||||
def callback_get_with_params
|
def callback_get_with_params
|
||||||
Request.new(:get, subscription.callback_url, params: callback_params).perform
|
Request.new(:get, subscription.callback_url, params: callback_params).perform do |response|
|
||||||
|
@callback_response_body = response.body.to_s
|
||||||
end
|
end
|
||||||
|
|
||||||
def callback_response_body
|
|
||||||
confirm_callback.body.to_s
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def callback_params
|
def callback_params
|
||||||
|
|
|
@ -23,22 +23,17 @@ class Pubsubhubbub::DeliveryWorker
|
||||||
private
|
private
|
||||||
|
|
||||||
def process_delivery
|
def process_delivery
|
||||||
payload_delivery
|
callback_post_payload do |payload_delivery|
|
||||||
|
raise Mastodon::UnexpectedResponseError, payload_delivery unless response_successful? payload_delivery
|
||||||
|
end
|
||||||
|
|
||||||
raise Mastodon::UnexpectedResponseError, payload_delivery unless response_successful?
|
|
||||||
|
|
||||||
payload_delivery.connection&.close
|
|
||||||
subscription.touch(:last_successful_delivery_at)
|
subscription.touch(:last_successful_delivery_at)
|
||||||
end
|
end
|
||||||
|
|
||||||
def payload_delivery
|
def callback_post_payload(&block)
|
||||||
@_payload_delivery ||= callback_post_payload
|
|
||||||
end
|
|
||||||
|
|
||||||
def callback_post_payload
|
|
||||||
request = Request.new(:post, subscription.callback_url, body: payload)
|
request = Request.new(:post, subscription.callback_url, body: payload)
|
||||||
request.add_headers(headers)
|
request.add_headers(headers)
|
||||||
request.perform
|
request.perform(&block)
|
||||||
end
|
end
|
||||||
|
|
||||||
def blocked_domain?
|
def blocked_domain?
|
||||||
|
@ -80,7 +75,7 @@ class Pubsubhubbub::DeliveryWorker
|
||||||
OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha1'), subscription.secret, payload)
|
OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha1'), subscription.secret, payload)
|
||||||
end
|
end
|
||||||
|
|
||||||
def response_successful?
|
def response_successful?(payload_delivery)
|
||||||
payload_delivery.code > 199 && payload_delivery.code < 300
|
payload_delivery.code > 199 && payload_delivery.code < 300
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -777,7 +777,7 @@ namespace :mastodon do
|
||||||
progress_bar.increment
|
progress_bar.increment
|
||||||
|
|
||||||
begin
|
begin
|
||||||
res = Request.new(:head, account.uri).perform
|
code = Request.new(:head, account.uri).perform(&:code)
|
||||||
rescue StandardError
|
rescue StandardError
|
||||||
# This could happen due to network timeout, DNS timeout, wrong SSL cert, etc,
|
# This could happen due to network timeout, DNS timeout, wrong SSL cert, etc,
|
||||||
# which should probably not lead to perceiving the account as deleted, so
|
# which should probably not lead to perceiving the account as deleted, so
|
||||||
|
@ -785,7 +785,7 @@ namespace :mastodon do
|
||||||
next
|
next
|
||||||
end
|
end
|
||||||
|
|
||||||
if [404, 410].include?(res.code)
|
if [404, 410].include?(code)
|
||||||
if options[:force]
|
if options[:force]
|
||||||
SuspendAccountService.new.call(account)
|
SuspendAccountService.new.call(account)
|
||||||
account.destroy
|
account.destroy
|
||||||
|
|
|
@ -39,12 +39,10 @@ describe Request do
|
||||||
|
|
||||||
describe '#perform' do
|
describe '#perform' do
|
||||||
context 'with valid host' do
|
context 'with valid host' do
|
||||||
before do
|
before { stub_request(:get, 'http://example.com') }
|
||||||
stub_request(:get, 'http://example.com')
|
|
||||||
subject.perform
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'executes a HTTP request' do
|
it 'executes a HTTP request' do
|
||||||
|
expect { |block| subject.perform &block }.to yield_control
|
||||||
expect(a_request(:get, 'http://example.com')).to have_been_made.once
|
expect(a_request(:get, 'http://example.com')).to have_been_made.once
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -52,12 +50,20 @@ describe Request do
|
||||||
allow(Addrinfo).to receive(:foreach).with('example.com', nil, nil, :SOCK_STREAM)
|
allow(Addrinfo).to receive(:foreach).with('example.com', nil, nil, :SOCK_STREAM)
|
||||||
.and_yield(Addrinfo.new(["AF_INET", 0, "example.com", "0.0.0.0"], :PF_INET, :SOCK_STREAM))
|
.and_yield(Addrinfo.new(["AF_INET", 0, "example.com", "0.0.0.0"], :PF_INET, :SOCK_STREAM))
|
||||||
.and_yield(Addrinfo.new(["AF_INET6", 0, "example.com", "2001:4860:4860::8844"], :PF_INET6, :SOCK_STREAM))
|
.and_yield(Addrinfo.new(["AF_INET6", 0, "example.com", "2001:4860:4860::8844"], :PF_INET6, :SOCK_STREAM))
|
||||||
|
|
||||||
|
expect { |block| subject.perform &block }.to yield_control
|
||||||
expect(a_request(:get, 'http://example.com')).to have_been_made.once
|
expect(a_request(:get, 'http://example.com')).to have_been_made.once
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'sets headers' do
|
it 'sets headers' do
|
||||||
|
expect { |block| subject.perform &block }.to yield_control
|
||||||
expect(a_request(:get, 'http://example.com').with(headers: subject.headers)).to have_been_made
|
expect(a_request(:get, 'http://example.com').with(headers: subject.headers)).to have_been_made
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'closes underlaying connection' do
|
||||||
|
expect_any_instance_of(HTTP::Client).to receive(:close)
|
||||||
|
expect { |block| subject.perform &block }.to yield_control
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with private host' do
|
context 'with private host' do
|
||||||
|
|
Loading…
Reference in New Issue