diff --git a/app/logical/danbooru/http.rb b/app/logical/danbooru/http.rb index 8fe06a0d1..dfe6558f8 100644 --- a/app/logical/danbooru/http.rb +++ b/app/logical/danbooru/http.rb @@ -1,5 +1,6 @@ require "danbooru/http/html_adapter" require "danbooru/http/xml_adapter" +require "danbooru/http/redirector" require "danbooru/http/retriable" require "danbooru/http/session" @@ -11,12 +12,24 @@ module Danbooru DEFAULT_TIMEOUT = 10 MAX_REDIRECTS = 5 - attr_writer :cache, :max_size, :http + attr_accessor :cache, :max_size, :http class << self delegate :get, :head, :put, :post, :delete, :cache, :follow, :max_size, :timeout, :auth, :basic_auth, :headers, :cookies, :use, :public_only, :download_media, to: :new end + def initialize + @http ||= + ::Danbooru::Http::ApplicationClient.new + .timeout(DEFAULT_TIMEOUT) + .headers(Danbooru.config.http_headers) + .headers("Accept-Encoding" => "gzip") + .use(:auto_inflate) + .use(:retriable) + .use(redirector: { max_redirects: MAX_REDIRECTS }) + .use(:session) + end + def get(url, **options) request(:get, url, **options) end @@ -147,16 +160,5 @@ module Danbooru def fake_response(status, body) ::HTTP::Response.new(status: status, version: "1.1", body: ::HTTP::Response::Body.new(body)) end - - def http - @http ||= - ::Danbooru::Http::ApplicationClient.new - .follow(strict: false, max_hops: MAX_REDIRECTS) - .use(:session) - .timeout(DEFAULT_TIMEOUT) - .use(:auto_inflate) - .headers(Danbooru.config.http_headers) - .headers("Accept-Encoding" => "gzip") - end end end diff --git a/app/logical/danbooru/http/redirector.rb b/app/logical/danbooru/http/redirector.rb new file mode 100644 index 000000000..26331226e --- /dev/null +++ b/app/logical/danbooru/http/redirector.rb @@ -0,0 +1,40 @@ +# A HTTP::Feature that automatically follows HTTP redirects. +# https://developer.mozilla.org/en-US/docs/Web/HTTP/Redirections + +module Danbooru + class Http + class Redirector < HTTP::Feature + HTTP::Options.register_feature :redirector, self + + attr_reader :max_redirects + + def initialize(max_redirects: 5) + @max_redirects = max_redirects + end + + def perform(request, &block) + response = yield request + + redirects = max_redirects + while response.status.redirect? + raise HTTP::Redirector::TooManyRedirectsError if redirects <= 0 + + response = yield build_redirect(request, response) + redirects -= 1 + end + + response + end + + def build_redirect(request, response) + location = response.headers["Location"].to_s + uri = HTTP::URI.parse(location) + + verb = request.verb + verb = :get if response.status == 303 && !request.verb.in?([:get, :head]) + + request.redirect(uri, verb) + end + end + end +end diff --git a/app/logical/nico_seiga_api_client.rb b/app/logical/nico_seiga_api_client.rb index f5418ac76..2b67bbde0 100644 --- a/app/logical/nico_seiga_api_client.rb +++ b/app/logical/nico_seiga_api_client.rb @@ -5,10 +5,11 @@ class NicoSeigaApiClient attr_reader :http # XXX temp disable following redirects. - def initialize(work_id:, type:, http: Danbooru::Http.follow(nil)) + def initialize(work_id:, type:) @work_id = work_id @work_type = type - @http = http + @http = Danbooru::Http.new + @http.http.default_options.features.reject! { |name, _| name == :redirector } end def image_ids