Add timeout to GetMediaUrl's HTTParty (#18136)

This commit is contained in:
Mac Siri 2022-07-15 17:33:44 -04:00 committed by GitHub
parent 19e5b6da35
commit 54f3d704fe
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 57 additions and 69 deletions

View file

@ -9,6 +9,8 @@ module Podcasts
OpenSSL::SSL::SSLError,
].freeze
TIMEOUT = 20
def initialize(enclosure_url)
@enclosure_url = enclosure_url.to_s
end
@ -43,7 +45,7 @@ module Podcasts
def url_reachable?(url)
url = Addressable::URI.parse(url).normalize.to_s
HTTParty.head(url).code == 200
HTTParty.head(url, timeout: TIMEOUT).code == 200
rescue *HANDLED_ERRORS
false
end

View file

@ -64,18 +64,16 @@ RSpec.describe Podcasts::Feed, type: :service, vcr: vcr_option do
end
it "re-checks episodes urls when setting as unreachable" do
allow(HTTParty).to receive(:get).with("http://podcast.example.com/podcast",
httparty_options).and_raise(Errno::ECONNREFUSED)
episode = create(:podcast_episode, podcast: unpodcast, reachable: true,
media_url: "http://podcast.example.com/ep1.mp3")
allow(HTTParty).to receive(:head).with("http://podcast.example.com/ep1.mp3").and_raise(Errno::ECONNREFUSED)
allow(HTTParty).to receive(:head).with("https://podcast.example.com/ep1.mp3").and_raise(Errno::ECONNREFUSED)
sidekiq_perform_enqueued_jobs do
described_class.new(unpodcast).get_episodes
end
options = { timeout: Podcasts::GetMediaUrl::TIMEOUT }
error = Errno::ECONNREFUSED
allow(HTTParty).to receive(:get).with("http://podcast.example.com/podcast", httparty_options).and_raise(error)
allow(HTTParty).to receive(:head).with("http://podcast.example.com/ep1.mp3", options).and_raise(error)
allow(HTTParty).to receive(:head).with("https://podcast.example.com/ep1.mp3", options).and_raise(error)
episode = create(:podcast_episode, podcast: unpodcast, reachable: true, media_url: "http://podcast.example.com/ep1.mp3")
sidekiq_perform_enqueued_jobs { described_class.new(unpodcast).get_episodes }
episode.reload
expect(episode.reachable).to be false
end

View file

@ -3,103 +3,93 @@ require "rails_helper"
RSpec.describe Podcasts::GetMediaUrl, type: :service do
let(:https_url) { "https://hello.example.com/" }
let(:http_url) { "http://hello.example.com/" }
let(:options) { { timeout: described_class::TIMEOUT } }
it "https, reachable" do
stub_request(:head, https_url).to_return(status: 200)
result = described_class.call(https_url)
expect(result.https).to be true
expect(result.reachable).to be true
expect(result.url).to eq(https_url)
expect(result).to have_attributes(https: true, reachable: true, url: https_url)
end
it "normalizes url" do
url = "https://hello.example.com/hi%20there.mp3"
stub_request(:head, url).to_return(status: 200)
result = described_class.call(url)
expect(result.https).to be true
expect(result.reachable).to be true
expect(result.url).to eq(url)
expect(result).to have_attributes(https: true, reachable: true, url: url)
end
it "https, unreachable" do
stub_request(:head, https_url).to_return(status: 404)
result = described_class.call(https_url)
expect(result.https).to be true
expect(result.reachable).to be false
expect(result.url).to eq(https_url)
expect(result).to have_attributes(https: true, reachable: false, url: https_url)
end
it "http, https reachable" do
stub_request(:head, https_url).to_return(status: 200)
result = described_class.call(http_url)
expect(result.https).to be true
expect(result.reachable).to be true
expect(result.url).to eq(https_url)
expect(result).to have_attributes(https: true, reachable: true, url: https_url)
end
it "http, https unreachable, http reachable" do
httparty_result = double
allow(httparty_result).to receive(:code).and_return(200)
allow(HTTParty).to receive(:head).with(http_url).and_return(httparty_result)
allow(HTTParty).to receive(:head).with(https_url).and_raise(Errno::ECONNREFUSED)
allow(HTTParty).to receive(:head).with(http_url, options).and_return(httparty_result)
allow(HTTParty).to receive(:head).with(https_url, options).and_raise(Errno::ECONNREFUSED)
result = described_class.call(http_url)
expect(result.https).to be false
expect(result.reachable).to be true
expect(result.url).to eq(http_url)
expect(result).to have_attributes(https: false, reachable: true, url: http_url)
end
it "http, https unreachable" do
allow(HTTParty).to receive(:head).with(https_url).and_raise(Errno::ECONNREFUSED)
allow(HTTParty).to receive(:head).with(http_url).and_raise(Errno::ECONNREFUSED)
allow(HTTParty).to receive(:head).with(https_url, options).and_raise(Errno::ECONNREFUSED)
allow(HTTParty).to receive(:head).with(http_url, options).and_raise(Errno::ECONNREFUSED)
result = described_class.call(http_url)
expect(result.https).to be false
expect(result.reachable).to be false
expect(result.url).to eq(http_url)
expect(result).to have_attributes(https: false, reachable: false, url: http_url)
end
it "http, https unreachable with other exception" do
allow(HTTParty).to receive(:head).with(https_url).and_raise(Errno::EINVAL)
allow(HTTParty).to receive(:head).with(http_url).and_raise(Errno::EINVAL)
allow(HTTParty).to receive(:head).with(https_url, options).and_raise(Errno::EINVAL)
allow(HTTParty).to receive(:head).with(http_url, options).and_raise(Errno::EINVAL)
result = described_class.call(http_url)
expect(result.https).to be false
expect(result.reachable).to be false
expect(result.url).to eq(http_url)
expect(result).to have_attributes(https: false, reachable: false, url: http_url)
end
it "http, https unreachable with invalid url exception" do
allow(HTTParty).to receive(:head).with(https_url).and_raise(URI::InvalidURIError)
allow(HTTParty).to receive(:head).with(http_url).and_raise(URI::InvalidURIError)
allow(HTTParty).to receive(:head).with(https_url, options).and_raise(URI::InvalidURIError)
allow(HTTParty).to receive(:head).with(http_url, options).and_raise(URI::InvalidURIError)
result = described_class.call(http_url)
expect(result.https).to be false
expect(result.reachable).to be false
expect(result.url).to eq(http_url)
expect(result).to have_attributes(https: false, reachable: false, url: http_url)
end
it "http, https unreachable with openssl error" do
httparty_result = instance_double(HTTParty::Response, code: 200)
allow(HTTParty).to receive(:head).with(https_url).and_raise(OpenSSL::SSL::SSLError)
allow(HTTParty).to receive(:head).with(http_url).and_return(httparty_result)
allow(HTTParty).to receive(:head).with(https_url, options).and_raise(OpenSSL::SSL::SSLError)
allow(HTTParty).to receive(:head).with(http_url, options).and_return(httparty_result)
result = described_class.call(http_url)
expect(result.https).to be false
expect(result.reachable).to be true
expect(result.url).to eq(http_url)
expect(result).to have_attributes(https: false, reachable: true, url: http_url)
end
it "marks unreachable with addressable invalid url exception" do
allow(HTTParty).to receive(:head).with(https_url).and_raise(Addressable::URI::InvalidURIError)
allow(HTTParty).to receive(:head).with(http_url).and_raise(Addressable::URI::InvalidURIError)
allow(HTTParty).to receive(:head).with(https_url, options).and_raise(Addressable::URI::InvalidURIError)
allow(HTTParty).to receive(:head).with(http_url, options).and_raise(Addressable::URI::InvalidURIError)
result = described_class.call(http_url)
expect(result.https).to be false
expect(result.reachable).to be false
expect(result.url).to eq(http_url)
expect(result).to have_attributes(https: false, reachable: false, url: http_url)
end
it "marks socket errors as invalid url exception" do
allow(HTTParty).to receive(:head).with(https_url).and_raise(SocketError)
allow(HTTParty).to receive(:head).with(http_url).and_raise(SocketError)
allow(HTTParty).to receive(:head).with(https_url, options).and_raise(SocketError)
allow(HTTParty).to receive(:head).with(http_url, options).and_raise(SocketError)
result = described_class.call(http_url)
expect(result.https).to be false
expect(result.reachable).to be false
expect(result.url).to eq(http_url)
expect(result).to have_attributes(https: false, reachable: false, url: http_url)
end
end

View file

@ -11,23 +11,22 @@ RSpec.describe Podcasts::UpdateEpisodeMediaUrl, type: :service do
episode = create(:podcast_episode, podcast: podcast, media_url: "http://example.com/1.mp3")
described_class.call(episode, http_url)
episode.reload
expect(episode.media_url).to eq(https_url)
expect(episode.reachable).to be true
expect(episode.https).to be true
expect(episode).to have_attributes(reachable: true, https: true, media_url: https_url)
end
it "keeps http when https and http are not reachable" do
http_url = "http://example.com/1.mp3"
https_url = "https://example.com/1.mp3"
allow(HTTParty).to receive(:head).with(http_url).and_raise(Errno::ECONNREFUSED)
allow(HTTParty).to receive(:head).with(https_url).and_raise(Errno::ECONNREFUSED)
options = { timeout: Podcasts::GetMediaUrl::TIMEOUT }
allow(HTTParty).to receive(:head).with(http_url, options).and_raise(Errno::ECONNREFUSED)
allow(HTTParty).to receive(:head).with(https_url, options).and_raise(Errno::ECONNREFUSED)
episode = create(:podcast_episode, podcast: podcast, media_url: http_url)
described_class.call(episode, http_url)
episode.reload
expect(episode.media_url).to eq(http_url)
expect(episode.reachable).to be false
expect(episode.https).to be false
expect(episode).to have_attributes(reachable: false, https: false, media_url: http_url)
end
it "does fine when there's nothing to update" do
@ -36,8 +35,7 @@ RSpec.describe Podcasts::UpdateEpisodeMediaUrl, type: :service do
episode = create(:podcast_episode, podcast: podcast, media_url: url)
described_class.call(episode, url)
episode.reload
expect(episode.media_url).to eq(url)
expect(episode.reachable).to be true
expect(episode.https).to be true
expect(episode).to have_attributes(reachable: true, https: true, media_url: url)
end
end