このコミットが含まれているのは:
@@ -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
|
||||
|
||||
新しい課題から参照
ユーザをブロックする