* lib/open-uri.rb: validate option names.

:content_length_proc and :progress_proc option implemented.


git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@5012 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
akr 2003-11-24 08:02:36 +00:00
parent f14589947e
commit 483ac56440
2 changed files with 127 additions and 8 deletions

View File

@ -1,3 +1,8 @@
Mon Nov 24 17:00:00 2003 Tanaka Akira <akr@m17n.org>
* lib/open-uri.rb: validate option names.
:content_length_proc and :progress_proc option implemented.
Mon Nov 24 14:53:10 2003 NAKAMURA Usaku <usa@ruby-lang.org>
* bcc32/Makefile.sub, win32/Makefile.sub, wince/Makefile.sub

View File

@ -91,6 +91,22 @@ module Kernel
end
module OpenURI
Options = {
:proxy => true,
:progress_proc => true,
:content_length_proc => true,
}
def OpenURI.check_options(options) # :nodoc:
options.each {|k, v|
next unless Symbol === k
unless Options.include? k
raise ArgumentError, "unrecognized option: #{k}"
end
}
end
def OpenURI.scan_open_optional_arguments(*rest) # :nodoc:
if !rest.empty? && (String === rest.first || Integer === rest.first)
mode = rest.shift
@ -106,6 +122,8 @@ module OpenURI
mode, perm, rest = OpenURI.scan_open_optional_arguments(*rest)
options = rest.shift if !rest.empty? && Hash === rest.first
raise ArgumentError.new("extra arguments") if !rest.empty?
options ||= {}
OpenURI.check_options(options)
unless mode == nil ||
mode == 'r' || mode == 'rb' ||
@ -113,7 +131,7 @@ module OpenURI
raise ArgumentError.new("invalid access mode #{mode} (#{uri.class} resource is read only.)")
end
io = open_loop(uri, options || {})
io = open_loop(uri, options)
if block_given?
begin
yield io
@ -316,12 +334,90 @@ module OpenURI
# Mixin for HTTP and FTP URIs.
module OpenRead
# opens the URI.
# OpenURI::OpenRead#open provides `open' for URI::HTTP and URI::FTP.
#
# OpenURI::OpenRead#open takes optional 3 arguments as:
# OpenURI::OpenRead#open([mode [, perm]] [, options]) [{|io| ... }]
#
# `mode', `perm' is same as Kernel#open.
#
# However, `mode' must be read mode because OpenURI::OpenRead#open doesn't
# support write mode (yet).
# Also `perm' is just ignored because it is meaningful only for file
# creation.
#
# `options' must be a hash.
#
# Each pairs which key is a string in the hash specify a extra header
# field for HTTP.
# I.e. it is ignored for FTP without HTTP proxy.
#
# The hash may include other option which key is a symbol:
#
# :proxy => "http://proxy.foo.com:8000/"
# :proxy => URI.parse("http://proxy.foo.com:8000/")
# :proxy => true
# :proxy => false
# :proxy => nil
#
# If :proxy option is specified, the value should be String, URI,
# boolean or nil.
# When String or URI is given, it is treated as proxy URI.
# When true is given or the option itself is not specified,
# environment variable `scheme_proxy'(or `SCHEME_PROXY') is examined.
# `scheme' is replaced by `http' or `ftp'.
# When false or nil is given, the environment variables are ignored and
# connection will be made to a server directly.
#
# :content_length_proc => lambda {|content_length| ... }
#
# If :content_length_proc option is specified, the option value procedure
# is called before actual transfer is started.
# It takes one argument which is expected content length in bytes.
#
# If two or more transfer is done by HTTP redirection, the procedure
# is called only one for a last transfer.
#
# When expected content length is unknown, the procedure is called with
# nil.
# It is happen when HTTP response has no Content-Length header.
#
# :progress_proc => lambda {|size| ...}
#
# If :progress_proc option is specified, the proc is called with one
# argument each time when `open' gets content fragment from network.
# The argument `size' `size' is a accumulated transfered size in bytes.
#
# If two or more transfer is done by HTTP redirection, the procedure
# is called only one for a last transfer.
#
# :progress_proc and :content_length_proc are intended to be used for
# progress bar.
# For example, it can be implemented as follows using Ruby/ProgressBar.
#
# pbar = nil
# open("http://...",
# :content_length_proc => lambda {|t|
# if t && 0 < t
# pbar = ProgressBar.new("...", t)
# pbar.file_transfer_mode
# end
# },
# :progress_proc => lambda {|s|
# pbar.set s if pbar
# }) {|f| ... }
#
# OpenURI::OpenRead#open returns an IO like object if block is not given.
# Otherwise it yields the IO object and return the value of the block.
# The IO object is extended with OpenURI::Meta.
def open(*rest, &block)
OpenURI.open_uri(self, *rest, &block)
end
# reads a content of the URI.
# OpenURI::OpenRead#read([options]) reads a content referenced by self and
# returns the content as string.
# The string is extended with OpenURI::Meta.
# The argument `options' is same as OpenURI::OpenRead#open.
def read(options={})
self.open(options) {|f|
str = f.read
@ -369,11 +465,25 @@ module URI
options.each {|k, v| header[k] = v if String === k }
require 'net/http'
resp = Net::HTTP.start(self.host, self.port) {|http|
http.get(uri.to_s, header) {|str|
buf << str
}
}
resp = nil
Net::HTTP.start(self.host, self.port) {|http|
http.request_get(uri.to_s, header) {|response|
resp = response
if options[:content_length_proc] && Net::HTTPSuccess === resp
if resp.key?('Content-Length')
options[:content_length_proc].call(resp['Content-Length'].to_i)
else
options[:content_length_proc].call(nil)
end
end
resp.read_body {|str|
buf << str
if options[:progress_proc] && Net::HTTPSuccess === resp
options[:progress_proc].call(buf.size)
end
}
}
}
io = buf.io
io.rewind
io.status = [resp.code, resp.message]
@ -403,8 +513,12 @@ module URI
ftp = Net::FTP.open(self.host)
ftp.login(user, passwd)
if options[:content_length_proc]
options[:content_length_proc].call(ftp.size(self.path))
end
ftp.getbinaryfile(self.path, '/dev/null', Net::FTP::DEFAULT_BLOCKSIZE) {|str|
buf << str
options[:progress_proc].call(buf.size) if options[:progress_proc]
}
ftp.close
buf.io.rewind