0
0
Fork 0

Validate HTTP response length while receiving (#6891)

to_s method of HTTP::Response keeps blocking while it receives the whole
content, no matter how it is big. This means it may waste time to receive
unacceptably large files. It may also consume memory and disk in the
process. This solves the inefficency by checking response length while
receiving.
This commit is contained in:
Akihiko Odaki 2018-03-26 21:02:10 +09:00 committed by Eugen Rochko
parent 18965cb0e6
commit 40e5d2303b
18 changed files with 115 additions and 26 deletions

View file

@ -5,6 +5,7 @@ module Mastodon
class NotPermittedError < Error; end
class ValidationError < Error; end
class HostValidationError < ValidationError; end
class LengthValidationError < ValidationError; end
class RaceConditionError < Error; end
class UnexpectedResponseError < Error

View file

@ -18,7 +18,7 @@ class ProviderDiscovery < OEmbed::ProviderDiscovery
else
Request.new(:get, url).perform do |res|
raise OEmbed::NotFound, url if res.code != 200 || res.mime_type != 'text/html'
Nokogiri::HTML(res.to_s)
Nokogiri::HTML(res.body_with_limit)
end
end

View file

@ -40,7 +40,7 @@ class Request
end
begin
yield response
yield response.extend(ClientLimit)
ensure
http_client.close
end
@ -99,6 +99,33 @@ class Request
@http_client ||= HTTP.timeout(:per_operation, timeout).follow(max_hops: 2)
end
module ClientLimit
def body_with_limit(limit = 1.megabyte)
raise Mastodon::LengthValidationError if content_length.present? && content_length > limit
if charset.nil?
encoding = Encoding::BINARY
else
begin
encoding = Encoding.find(charset)
rescue ArgumentError
encoding = Encoding::BINARY
end
end
contents = String.new(encoding: encoding)
while (chunk = readpartial)
contents << chunk
chunk.clear
raise Mastodon::LengthValidationError if contents.bytesize > limit
end
contents
end
end
class Socket < TCPSocket
class << self
def open(host, *args)
@ -118,5 +145,5 @@ class Request
end
end
private_constant :Socket
private_constant :ClientLimit, :Socket
end