[ruby/open-uri] Add request_specific_fields option for customizing headers

This commit introduces the `request_specific_fields` option in OpenURI.
It provides two methods for customizing headers as follows.

1. Specify headers only for the initial request

Use a Hash to apply headers only to the first request.
These headers are automatically removed during redirects.

2. Specify headers dynamically for each request

Use a Proc to dynamically generate headers for each request,
including during redirects, based on the request URL.

This feature allows users to control headers flexibly,
ensuring that sensitive headers like "Authorization" are not unintentionally
transferred during redirects unless explicitly specified.

https://github.com/ruby/open-uri/commit/460f858e3c
This commit is contained in:
otegami 2024-08-27 13:45:57 +09:00 committed by git
parent ec5d5422ca
commit 6ea2b6f65f

View File

@ -109,6 +109,7 @@ module OpenURI
:redirect => true, :redirect => true,
:encoding => nil, :encoding => nil,
:max_redirects => 64, :max_redirects => 64,
:request_specific_fields => nil,
} }
def OpenURI.check_options(options) # :nodoc: def OpenURI.check_options(options) # :nodoc:
@ -148,7 +149,11 @@ module OpenURI
end end
encoding = Encoding.find(options[:encoding]) encoding = Encoding.find(options[:encoding])
end end
if options.has_key? :request_specific_fields
if !(options[:request_specific_fields].is_a?(Hash) || options[:request_specific_fields].is_a?(Proc))
raise ArgumentError, "Invalid request_specific_fields' format: #{options[:request_specific_fields]}"
end
end
unless mode == nil || unless mode == nil ||
mode == 'r' || mode == 'rb' || mode == 'r' || mode == 'rb' ||
mode == File::RDONLY mode == File::RDONLY
@ -215,9 +220,17 @@ module OpenURI
max_redirects = options[:max_redirects] || Options.fetch(:max_redirects) max_redirects = options[:max_redirects] || Options.fetch(:max_redirects)
buf = nil buf = nil
while true while true
request_specific_fields = {}
if options.has_key? :request_specific_fields
request_specific_fields = if options[:request_specific_fields].is_a?(Hash)
options[:request_specific_fields]
else options[:request_specific_fields].is_a?(Proc)
options[:request_specific_fields].call(uri)
end
end
redirect = catch(:open_uri_redirect) { redirect = catch(:open_uri_redirect) {
buf = Buffer.new buf = Buffer.new
uri.buffer_open(buf, find_proxy.call(uri), options) uri.buffer_open(buf, find_proxy.call(uri), options.merge(request_specific_fields))
nil nil
} }
if redirect if redirect
@ -237,6 +250,10 @@ module OpenURI
options = options.dup options = options.dup
options.delete :http_basic_authentication options.delete :http_basic_authentication
end end
if options.include?(:request_specific_fields) && options[:request_specific_fields].is_a?(Hash)
# Send request specific headers only for the initial request.
options.delete :request_specific_fields
end
uri = redirect uri = redirect
raise "HTTP redirection loop: #{uri}" if uri_set.include? uri.to_s raise "HTTP redirection loop: #{uri}" if uri_set.include? uri.to_s
uri_set[uri.to_s] = true uri_set[uri.to_s] = true