このコミットが含まれているのは:
2026-07-10 00:34:45 +09:00
コミット 3a8f7d8d89
4個のファイルの変更109行の追加23行の削除
+40 -5
ファイルの表示
@@ -1,4 +1,5 @@
require 'net/http'
require 'json'
module Preview
class HttpFetcher
@@ -16,9 +17,17 @@ module Preview
response = request(uri, addresses.first, max_bytes)
if response.is_a?(Net::HTTPRedirection)
raise FetchFailed, 'redirect が多すぎます.' if redirects.zero?
location = response['location']
if redirects.zero?
log_failure(:redirect_limit,
url: uri.to_s,
redirects:,
location:,
content_type: response['content-type'],
content_length: response['content-length'])
raise FetchFailed, 'redirect が多すぎます.'
end
raise FetchFailed, 'redirect 先が不正です.' if location.blank?
return fetch(URI.join(uri, location).to_s,
@@ -27,13 +36,20 @@ module Preview
end
unless response.is_a?(Net::HTTPSuccess)
log_failure(:http_status,
url: uri.to_s,
code: response.code,
content_type: response['content-type'],
content_length: response['content-length'])
raise FetchFailed, "外部サーバーが HTTP #{ response.code } を返しました."
end
Response.new(response.body, response['content-type'].to_s, uri.to_s)
rescue Net::OpenTimeout, Net::ReadTimeout, Timeout::Error => e
log_failure(:timeout, url: uri&.to_s || raw_url, error: e.class.name, message: e.message)
raise FetchTimeout, e.message
rescue SocketError, SystemCallError, OpenSSL::SSL::SSLError, EOFError => e
log_failure(:network_error, url: uri&.to_s || raw_url, error: e.class.name, message: e.message)
raise FetchFailed, e.message
end
@@ -51,12 +67,27 @@ module Preview
http.request(request) do |response|
length = response['content-length'].to_i
raise ResponseTooLarge, '外部データが大きすぎます.' if length > max_bytes
if length > max_bytes
log_failure(:response_too_large,
url: uri.to_s,
content_type: response['content-type'],
content_length: response['content-length'],
max_bytes:)
raise ResponseTooLarge, '外部データが大きすぎます.'
end
body = +''
response.read_body do |chunk|
body << chunk
raise ResponseTooLarge, '外部データが大きすぎます.' if body.bytesize > max_bytes
next unless body.bytesize > max_bytes
log_failure(:response_too_large,
url: uri.to_s,
content_type: response['content-type'],
content_length: response['content-length'],
bytes_read: body.bytesize,
max_bytes:)
raise ResponseTooLarge, '外部データが大きすぎます.'
end
response.instance_variable_set(:@body, body)
response.instance_variable_set(:@read, true)
@@ -64,6 +95,10 @@ module Preview
end
end
private_class_method :request
def self.log_failure(reason, **payload)
Rails.logger.warn("preview_http_fetcher_failure #{ { reason:, **payload }.to_json }")
end
private_class_method :request, :log_failure
end
end