diff --git a/app/logical/danbooru/http.rb b/app/logical/danbooru/http.rb index 6e0b21711..d80c869c9 100644 --- a/app/logical/danbooru/http.rb +++ b/app/logical/danbooru/http.rb @@ -1,17 +1,22 @@ module Danbooru class Http DEFAULT_TIMEOUT = 3 + MAX_REDIRECTS = 5 attr_writer :cache, :http class << self - delegate :get, :post, :delete, :cache, :auth, :basic_auth, :headers, to: :new + delegate :get, :put, :post, :delete, :cache, :timeout, :auth, :basic_auth, :headers, to: :new end def get(url, **options) request(:get, url, **options) end + def put(url, **options) + request(:get, url, **options) + end + def post(url, **options) request(:post, url, **options) end @@ -24,6 +29,10 @@ module Danbooru dup.tap { |o| o.cache = expiry.to_i } end + def timeout(*args) + dup.tap { |o| o.http = o.http.timeout(*args) } + end + def auth(*args) dup.tap { |o| o.http = o.http.auth(*args) } end @@ -44,9 +53,11 @@ module Danbooru else raw_request(method, url, **options) end + rescue HTTP::Redirector::TooManyRedirectsError + ::HTTP::Response.new(status: 598, body: "", version: "1.1") rescue HTTP::TimeoutError # return a synthetic http error on connection timeouts - ::HTTP::Response.new(status: 522, body: "", version: "1.1") + ::HTTP::Response.new(status: 599, body: "", version: "1.1") end def cached_request(method, url, **options) @@ -65,7 +76,12 @@ module Danbooru end def http - @http ||= ::HTTP.timeout(DEFAULT_TIMEOUT).use(:auto_inflate).headers(Danbooru.config.http_headers).headers("Accept-Encoding" => "gzip") + @http ||= ::HTTP. + follow(max_hops: MAX_REDIRECTS). + timeout(DEFAULT_TIMEOUT). + use(:auto_inflate). + headers(Danbooru.config.http_headers). + headers("Accept-Encoding" => "gzip") end end end diff --git a/test/unit/danbooru_http_test.rb b/test/unit/danbooru_http_test.rb new file mode 100644 index 000000000..42e585bef --- /dev/null +++ b/test/unit/danbooru_http_test.rb @@ -0,0 +1,49 @@ +require 'test_helper' + +class DanbooruHttpTest < ActiveSupport::TestCase + context "Danbooru::Http" do + context "#get method" do + should "work for all basic methods" do + %i[get put post delete].each do |method| + response = Danbooru::Http.send(method, "https://httpbin.org/status/200") + assert_equal(200, response.status) + end + end + + should "follow redirects" do + response = Danbooru::Http.get("https://httpbin.org/absolute-redirect/3") + assert_equal(200, response.status) + end + + should "fail if redirected too many times" do + response = Danbooru::Http.get("https://httpbin.org/absolute-redirect/10") + assert_equal(598, response.status) + end + + should "fail if the request takes too long to connect" do + response = Danbooru::Http.timeout(1).get("https://httpbin.org/delay/5") + assert_equal(599, response.status) + end + + should "fail if the request takes too long to download" do + response = Danbooru::Http.timeout(1).get("https://httpbin.org/drip?duration=5&numbytes=5") + assert_equal(599, response.status) + end + + should "automatically decompress gzipped responses" do + response = Danbooru::Http.get("https://httpbin.org/gzip") + assert_equal(200, response.status) + assert_equal(true, response.parse["gzipped"]) + end + + should "cache requests" do + response1 = Danbooru::Http.cache(1.minute).get("https://httpbin.org/uuid") + assert_equal(200, response1.status) + + response2 = Danbooru::Http.cache(1.minute).get("https://httpbin.org/uuid") + assert_equal(200, response2.status) + assert_equal(response2.body, response1.body) + end + end + end +end