From 483ac5644036ec1f1cf6e74b773c9315c611c8b3 Mon Sep 17 00:00:00 2001 From: akr Date: Mon, 24 Nov 2003 08:02:36 +0000 Subject: [PATCH] * 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 --- ChangeLog | 5 ++ lib/open-uri.rb | 130 +++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 127 insertions(+), 8 deletions(-) diff --git a/ChangeLog b/ChangeLog index f8ea649786..93ecafedd4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +Mon Nov 24 17:00:00 2003 Tanaka Akira + + * lib/open-uri.rb: validate option names. + :content_length_proc and :progress_proc option implemented. + Mon Nov 24 14:53:10 2003 NAKAMURA Usaku * bcc32/Makefile.sub, win32/Makefile.sub, wince/Makefile.sub diff --git a/lib/open-uri.rb b/lib/open-uri.rb index f13f1d5edb..42503f15cf 100644 --- a/lib/open-uri.rb +++ b/lib/open-uri.rb @@ -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