Feature #21773
openSupport for setting encoding when a block is passed to `Net::HTTPResponse.read_body`
Description
Hi everyone,
This feature request could be considered a follow-up to https://bugs.ruby-lang.org/issues/2567. Opting in to automatic detection of the right encoding for the body when passing a block to read_body results in an error as follows:
require "net/http"
uri = URI.parse("https://rosa.codes")
http = Net::HTTP.new(uri.host, uri.port).tap { it.use_ssl = true }
req = Net::HTTP::Get.new(uri)
http.request(req) do |response|
response.body_encoding = true
body = +""
response.read_body do |str|
body << str
end
end
# => lib/net/http/response.rb:380:in 'Net::HTTPResponse#read_body': undefined method 'force_encoding' for an instance of Net::ReadAdapter (NoMethodError)
It's expected this doesn't work because when passing a block, the body is not kept anywhere in the Net::HTTPResponse object, each segment read is just passed to the block via the wrapping Net::ReadAdapter.
In the case I was handling, I required both to read the body using a block and also set the encoding in the final body. I didn't want to duplicate all the code to do the encoding detection, as it's not trivial. I preferred to rely on the Net::HTTP implementation of it. I ended up doing something different, along these lines:
require "net/http"
require "active_support"
class Body
attr_reader :content
delegate_missing_to :content
def initialize(&block)
@block = block
@content = +""
end
def <<(str)
@content << str
@block.call(str) if @block
end
end
uri = URI.parse("https://rosa.codes")
http = Net::HTTP.new(uri.host, uri.port).tap { it.use_ssl = true }
req = Net::HTTP::Get.new(uri)
http.request(req) do |response|
response.body_encoding = true
dest = Body.new
response.read_body(dest)
end
But this got me wondering whether:
- The error that happens when calling
force_encodingon an instance ofNet::ReadAdaptershould be handled more gracefully or in a more informative way for the user. - This could be better supported by
Net::HTTP. All the pieces are there; there just isn't an easy way to put them together. There's a way to detect the encoding, there's a way to set a body directly in the response, viaNet::HTTPResponse#body=, but there's no way to trigger the encoding detection and set it in the body besides the first time the body is read.
Thanks for the consideration!