summaryrefslogtreecommitdiff
path: root/lib/open-uri.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/open-uri.rb')
-rw-r--r--lib/open-uri.rb94
1 files changed, 84 insertions, 10 deletions
diff --git a/lib/open-uri.rb b/lib/open-uri.rb
index 93e8cfcdb7..844865b13a 100644
--- a/lib/open-uri.rb
+++ b/lib/open-uri.rb
@@ -4,22 +4,25 @@ require 'stringio'
require 'time'
module URI
- # Allows the opening of various resources including URIs.
+ # Allows the opening of various resources including URIs. Example:
#
- # If the first argument responds to the 'open' method, 'open' is called on
+ # require "open-uri"
+ # URI.open("http://example.com") { |f| f.read }
+ #
+ # If the first argument responds to the +open+ method, +open+ is called on
# it with the rest of the arguments.
#
# If the first argument is a string that begins with <code>(protocol)://</code>, it is parsed by
- # URI.parse. If the parsed object responds to the 'open' method,
- # 'open' is called on it with the rest of the arguments.
+ # URI.parse. If the parsed object responds to the +open+ method,
+ # +open+ is called on it with the rest of the arguments.
#
# Otherwise, Kernel#open is called.
#
# OpenURI::OpenRead#open provides URI::HTTP#open, URI::HTTPS#open and
# URI::FTP#open, Kernel#open.
#
- # We can accept URIs and strings that begin with http://, https:// and
- # ftp://. In these cases, the opened file object is extended by OpenURI::Meta.
+ # We can accept URIs and strings that begin with <code>http://</code>, <code>https://</code> and
+ # <code>ftp://</code>. In these cases, the opened file object is extended by OpenURI::Meta.
def self.open(name, *rest, &block)
if name.respond_to?(:open)
name.open(*rest, &block)
@@ -31,6 +34,7 @@ module URI
super
end
end
+ singleton_class.send(:ruby2_keywords, :open) if respond_to?(:ruby2_keywords, true)
end
# OpenURI is an easy-to-use wrapper for Net::HTTP, Net::HTTPS and Net::FTP.
@@ -89,6 +93,11 @@ end
# Author:: Tanaka Akira <akr@m17n.org>
module OpenURI
+
+ # The version string
+ VERSION = "0.5.0"
+
+ # The default options
Options = {
:proxy => true,
:proxy_http_basic_authentication => true,
@@ -104,6 +113,8 @@ module OpenURI
:ftp_active_mode => false,
:redirect => true,
:encoding => nil,
+ :max_redirects => 64,
+ :request_specific_fields => nil,
}
def OpenURI.check_options(options) # :nodoc:
@@ -143,7 +154,11 @@ module OpenURI
end
encoding = Encoding.find(options[:encoding])
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 option: #{options[:request_specific_fields].inspect}"
+ end
+ end
unless mode == nil ||
mode == 'r' || mode == 'rb' ||
mode == File::RDONLY
@@ -207,11 +222,20 @@ module OpenURI
end
uri_set = {}
+ max_redirects = options[:max_redirects] || Options.fetch(:max_redirects)
buf = nil
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) {
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
}
if redirect
@@ -231,9 +255,14 @@ module OpenURI
options = options.dup
options.delete :http_basic_authentication
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
raise "HTTP redirection loop: #{uri}" if uri_set.include? uri.to_s
uri_set[uri.to_s] = true
+ raise TooManyRedirects.new("Too many redirects", buf.io) if max_redirects && uri_set.size > max_redirects
else
break
end
@@ -370,24 +399,31 @@ module OpenURI
end
end
+ # Raised on HTTP session failure
class HTTPError < StandardError
- def initialize(message, io)
+ def initialize(message, io) # :nodoc:
super(message)
@io = io
end
+ # StringIO having the received data
attr_reader :io
end
# Raised on redirection,
# only occurs when +redirect+ option for HTTP is +false+.
class HTTPRedirect < HTTPError
- def initialize(message, io, uri)
+ def initialize(message, io, uri) # :nodoc:
super(message, io)
@uri = uri
end
+ # URI to redirect
attr_reader :uri
end
+ # Raised on too many redirection,
+ class TooManyRedirects < HTTPError
+ end
+
class Buffer # :nodoc: all
def initialize
@io = StringIO.new
@@ -736,6 +772,44 @@ module OpenURI
# Using +true+ also means that redirections between http and ftp are
# permitted.
#
+ # [:max_redirects]
+ # Synopsis:
+ # :max_redirects=>int
+ #
+ # Number of HTTP redirects allowed before OpenURI::TooManyRedirects is raised.
+ # The default is 64.
+ #
+ # [:request_specific_fields]
+ # Synopsis:
+ # :request_specific_fields => {}
+ # :request_specific_fields => lambda {|url| ...}
+ #
+ # :request_specific_fields option allows specifying custom header fields that
+ # are sent with the HTTP request. It can be passed as a Hash or a Proc that
+ # gets evaluated on each request and returns a Hash of header fields.
+ #
+ # If a Hash is provided, it specifies the headers only for the initial
+ # request and these headers will not be sent on redirects.
+ #
+ # If a Proc is provided, it will be executed for each request including
+ # redirects, allowing dynamic header customization based on the request URL.
+ # It is important that the Proc returns a Hash. And this Hash specifies the
+ # headers to be sent with the request.
+ #
+ # For Example with Hash
+ # URI.open("http://...",
+ # request_specific_fields: {"Authorization" => "token dummy"}) {|f| ... }
+ #
+ # For Example with Proc:
+ # URI.open("http://...",
+ # request_specific_fields: lambda { |uri|
+ # if uri.host == "example.com"
+ # {"Authorization" => "token dummy"}
+ # else
+ # {}
+ # end
+ # }) {|f| ... }
+ #
def open(*rest, &block)
OpenURI.open_uri(self, *rest, &block)
end