rubysl-open-uri 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 05f995f524d3e9632f582e2bbd8dcdbf5659e488
4
+ data.tar.gz: 458739e0c6c86c472d691d1b01443c76928ed392
5
+ SHA512:
6
+ metadata.gz: d429adfd778c09cf4a20d386a6371d0f3b423041ad1239b4abc6b774f446587ff6081f395e99f50d0dae022ab60df795242336a767a4a636996409da1717db22
7
+ data.tar.gz: 357631988ef83b25738f170ce565aa88cf7c69954f6730869a5e5ecf752326528245decff5333c23c98a01e1bb5712f90d1424837f3f05757228c474f3296812
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
@@ -0,0 +1,8 @@
1
+ language: ruby
2
+ before_install:
3
+ - gem update --system
4
+ - gem --version
5
+ - gem install rubysl-bundler
6
+ script: bundle exec mspec spec
7
+ rvm:
8
+ - rbx-nightly-18mode
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in rubysl-open-uri.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,25 @@
1
+ Copyright (c) 2013, Brian Shirai
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without
5
+ modification, are permitted provided that the following conditions are met:
6
+
7
+ 1. Redistributions of source code must retain the above copyright notice, this
8
+ list of conditions and the following disclaimer.
9
+ 2. Redistributions in binary form must reproduce the above copyright notice,
10
+ this list of conditions and the following disclaimer in the documentation
11
+ and/or other materials provided with the distribution.
12
+ 3. Neither the name of the library nor the names of its contributors may be
13
+ used to endorse or promote products derived from this software without
14
+ specific prior written permission.
15
+
16
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19
+ DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY DIRECT,
20
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21
+ BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
23
+ OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
25
+ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,29 @@
1
+ # Rubysl::Open::Uri
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'rubysl-open-uri'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install rubysl-open-uri
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1 @@
1
+ require "rubysl/open-uri"
@@ -0,0 +1,2 @@
1
+ require "rubysl/open-uri/open-uri"
2
+ require "rubysl/open-uri/version"
@@ -0,0 +1,678 @@
1
+ require 'uri'
2
+ require 'stringio'
3
+ require 'time'
4
+
5
+ module Kernel
6
+ private
7
+ alias open_uri_original_open open # :nodoc:
8
+
9
+ # makes possible to open various resources including URIs.
10
+ # If the first argument respond to `open' method,
11
+ # the method is called with the rest arguments.
12
+ #
13
+ # If the first argument is a string which begins with xxx://,
14
+ # it is parsed by URI.parse. If the parsed object respond to `open' method,
15
+ # the method is called with the rest arguments.
16
+ #
17
+ # Otherwise original open is called.
18
+ #
19
+ # Since open-uri.rb provides URI::HTTP#open, URI::HTTPS#open and
20
+ # URI::FTP#open,
21
+ # Kernel[#.]open can accepts such URIs and strings which begins with
22
+ # http://, https:// and ftp://.
23
+ # In these case, the opened file object is extended by OpenURI::Meta.
24
+ def open(name, *rest, &block) # :doc:
25
+ if name.respond_to?(:open)
26
+ name.open(*rest, &block)
27
+ elsif name.respond_to?(:to_str) &&
28
+ %r{\A[A-Za-z][A-Za-z0-9+\-\.]*://} =~ name &&
29
+ (uri = URI.parse(name)).respond_to?(:open)
30
+ uri.open(*rest, &block)
31
+ else
32
+ open_uri_original_open(name, *rest, &block)
33
+ end
34
+ end
35
+ module_function :open
36
+ end
37
+
38
+ # OpenURI is an easy-to-use wrapper for net/http, net/https and net/ftp.
39
+ #
40
+ #== Example
41
+ #
42
+ # It is possible to open http/https/ftp URL as usual like opening a file:
43
+ #
44
+ # open("http://www.ruby-lang.org/") {|f|
45
+ # f.each_line {|line| p line}
46
+ # }
47
+ #
48
+ # The opened file has several methods for meta information as follows since
49
+ # it is extended by OpenURI::Meta.
50
+ #
51
+ # open("http://www.ruby-lang.org/en") {|f|
52
+ # f.each_line {|line| p line}
53
+ # p f.base_uri # <URI::HTTP:0x40e6ef2 URL:http://www.ruby-lang.org/en/>
54
+ # p f.content_type # "text/html"
55
+ # p f.charset # "iso-8859-1"
56
+ # p f.content_encoding # []
57
+ # p f.last_modified # Thu Dec 05 02:45:02 UTC 2002
58
+ # }
59
+ #
60
+ # Additional header fields can be specified by an optional hash argument.
61
+ #
62
+ # open("http://www.ruby-lang.org/en/",
63
+ # "User-Agent" => "Ruby/#{RUBY_VERSION}",
64
+ # "From" => "foo@bar.invalid",
65
+ # "Referer" => "http://www.ruby-lang.org/") {|f|
66
+ # # ...
67
+ # }
68
+ #
69
+ # The environment variables such as http_proxy, https_proxy and ftp_proxy
70
+ # are in effect by default. :proxy => nil disables proxy.
71
+ #
72
+ # open("http://www.ruby-lang.org/en/raa.html", :proxy => nil) {|f|
73
+ # # ...
74
+ # }
75
+ #
76
+ # URI objects can be opened in a similar way.
77
+ #
78
+ # uri = URI.parse("http://www.ruby-lang.org/en/")
79
+ # uri.open {|f|
80
+ # # ...
81
+ # }
82
+ #
83
+ # URI objects can be read directly. The returned string is also extended by
84
+ # OpenURI::Meta.
85
+ #
86
+ # str = uri.read
87
+ # p str.base_uri
88
+ #
89
+ # Author:: Tanaka Akira <akr@m17n.org>
90
+
91
+ module OpenURI
92
+ Options = {
93
+ :proxy => true,
94
+ :progress_proc => true,
95
+ :content_length_proc => true,
96
+ :http_basic_authentication => true,
97
+ }
98
+
99
+ def OpenURI.check_options(options) # :nodoc:
100
+ options.each {|k, v|
101
+ next unless Symbol === k
102
+ unless Options.include? k
103
+ raise ArgumentError, "unrecognized option: #{k}"
104
+ end
105
+ }
106
+ end
107
+
108
+ def OpenURI.scan_open_optional_arguments(*rest) # :nodoc:
109
+ if !rest.empty? && (String === rest.first || Integer === rest.first)
110
+ mode = rest.shift
111
+ if !rest.empty? && Integer === rest.first
112
+ perm = rest.shift
113
+ end
114
+ end
115
+ return mode, perm, rest
116
+ end
117
+
118
+ def OpenURI.open_uri(name, *rest) # :nodoc:
119
+ uri = URI::Generic === name ? name : URI.parse(name)
120
+ mode, perm, rest = OpenURI.scan_open_optional_arguments(*rest)
121
+ options = rest.shift if !rest.empty? && Hash === rest.first
122
+ raise ArgumentError.new("extra arguments") if !rest.empty?
123
+ options ||= {}
124
+ OpenURI.check_options(options)
125
+
126
+ unless mode == nil ||
127
+ mode == 'r' || mode == 'rb' ||
128
+ mode == File::RDONLY
129
+ raise ArgumentError.new("invalid access mode #{mode} (#{uri.class} resource is read only.)")
130
+ end
131
+
132
+ io = open_loop(uri, options)
133
+ if block_given?
134
+ begin
135
+ yield io
136
+ ensure
137
+ io.close
138
+ end
139
+ else
140
+ io
141
+ end
142
+ end
143
+
144
+ def OpenURI.open_loop(uri, options) # :nodoc:
145
+ case opt_proxy = options.fetch(:proxy, true)
146
+ when true
147
+ find_proxy = lambda {|u| u.find_proxy}
148
+ when nil, false
149
+ find_proxy = lambda {|u| nil}
150
+ when String
151
+ opt_proxy = URI.parse(opt_proxy)
152
+ find_proxy = lambda {|u| opt_proxy}
153
+ when URI::Generic
154
+ find_proxy = lambda {|u| opt_proxy}
155
+ else
156
+ raise ArgumentError.new("Invalid proxy option: #{opt_proxy}")
157
+ end
158
+
159
+ uri_set = {}
160
+ buf = nil
161
+ while true
162
+ redirect = catch(:open_uri_redirect) {
163
+ buf = Buffer.new
164
+ uri.buffer_open(buf, find_proxy.call(uri), options)
165
+ nil
166
+ }
167
+ if redirect
168
+ if redirect.relative?
169
+ # Although it violates RFC2616, Location: field may have relative
170
+ # URI. It is converted to absolute URI using uri as a base URI.
171
+ redirect = uri + redirect
172
+ end
173
+ unless OpenURI.redirectable?(uri, redirect)
174
+ raise "redirection forbidden: #{uri} -> #{redirect}"
175
+ end
176
+ if options.include? :http_basic_authentication
177
+ # send authentication only for the URI directly specified.
178
+ options = options.dup
179
+ options.delete :http_basic_authentication
180
+ end
181
+ uri = redirect
182
+ raise "HTTP redirection loop: #{uri}" if uri_set.include? uri.to_s
183
+ uri_set[uri.to_s] = true
184
+ else
185
+ break
186
+ end
187
+ end
188
+ io = buf.io
189
+ io.base_uri = uri
190
+ io
191
+ end
192
+
193
+ def OpenURI.redirectable?(uri1, uri2) # :nodoc:
194
+ # This test is intended to forbid a redirection from http://... to
195
+ # file:///etc/passwd.
196
+ # However this is ad hoc. It should be extensible/configurable.
197
+ uri1.scheme.downcase == uri2.scheme.downcase ||
198
+ (/\A(?:http|ftp)\z/i =~ uri1.scheme && /\A(?:http|ftp)\z/i =~ uri2.scheme)
199
+ end
200
+
201
+ def OpenURI.open_http(buf, target, proxy, options) # :nodoc:
202
+ if proxy
203
+ raise "Non-HTTP proxy URI: #{proxy}" if proxy.class != URI::HTTP
204
+ end
205
+
206
+ if target.userinfo && "1.9.0" <= RUBY_VERSION
207
+ # don't raise for 1.8 because compatibility.
208
+ raise ArgumentError, "userinfo not supported. [RFC3986]"
209
+ end
210
+
211
+ require 'net/http'
212
+ klass = Net::HTTP
213
+ if URI::HTTP === target
214
+ # HTTP or HTTPS
215
+ if proxy
216
+ klass = Net::HTTP::Proxy(proxy.host, proxy.port)
217
+ end
218
+ target_host = target.host
219
+ target_port = target.port
220
+ request_uri = target.request_uri
221
+ else
222
+ # FTP over HTTP proxy
223
+ target_host = proxy.host
224
+ target_port = proxy.port
225
+ request_uri = target.to_s
226
+ end
227
+
228
+ http = klass.new(target_host, target_port)
229
+ if target.class == URI::HTTPS
230
+ require 'net/https'
231
+ http.use_ssl = true
232
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
233
+ store = OpenSSL::X509::Store.new
234
+ store.set_default_paths
235
+ http.cert_store = store
236
+ end
237
+
238
+ header = {}
239
+ options.each {|k, v| header[k] = v if String === k }
240
+
241
+ resp = nil
242
+ http.start {
243
+ req = Net::HTTP::Get.new(request_uri, header)
244
+ if options.include? :http_basic_authentication
245
+ user, pass = options[:http_basic_authentication]
246
+ req.basic_auth user, pass
247
+ end
248
+ http.request(req) {|response|
249
+ resp = response
250
+ if options[:content_length_proc] && Net::HTTPSuccess === resp
251
+ if resp.key?('Content-Length')
252
+ options[:content_length_proc].call(resp['Content-Length'].to_i)
253
+ else
254
+ options[:content_length_proc].call(nil)
255
+ end
256
+ end
257
+ resp.read_body {|str|
258
+ buf << str
259
+ if options[:progress_proc] && Net::HTTPSuccess === resp
260
+ options[:progress_proc].call(buf.size)
261
+ end
262
+ }
263
+ }
264
+ }
265
+ io = buf.io
266
+ io.rewind
267
+ io.status = [resp.code, resp.message]
268
+ resp.each {|name,value| buf.io.meta_add_field name, value }
269
+ case resp
270
+ when Net::HTTPSuccess
271
+ when Net::HTTPMovedPermanently, # 301
272
+ Net::HTTPFound, # 302
273
+ Net::HTTPSeeOther, # 303
274
+ Net::HTTPTemporaryRedirect # 307
275
+ throw :open_uri_redirect, URI.parse(resp['location'])
276
+ else
277
+ raise OpenURI::HTTPError.new(io.status.join(' '), io)
278
+ end
279
+ end
280
+
281
+ class HTTPError < StandardError
282
+ def initialize(message, io)
283
+ super(message)
284
+ @io = io
285
+ end
286
+ attr_reader :io
287
+ end
288
+
289
+ class Buffer # :nodoc:
290
+ def initialize
291
+ @io = StringIO.new
292
+ @size = 0
293
+ end
294
+ attr_reader :size
295
+
296
+ StringMax = 10240
297
+ def <<(str)
298
+ @io << str
299
+ @size += str.length
300
+ if StringIO === @io && StringMax < @size
301
+ require 'tempfile'
302
+ io = Tempfile.new('open-uri')
303
+ io.binmode
304
+ Meta.init io, @io if @io.respond_to? :meta
305
+ io << @io.string
306
+ @io = io
307
+ end
308
+ end
309
+
310
+ def io
311
+ Meta.init @io unless @io.respond_to? :meta
312
+ @io
313
+ end
314
+ end
315
+
316
+ # Mixin for holding meta-information.
317
+ module Meta
318
+ def Meta.init(obj, src=nil) # :nodoc:
319
+ obj.extend Meta
320
+ obj.instance_eval {
321
+ @base_uri = nil
322
+ @meta = {}
323
+ }
324
+ if src
325
+ obj.status = src.status
326
+ obj.base_uri = src.base_uri
327
+ src.meta.each {|name, value|
328
+ obj.meta_add_field(name, value)
329
+ }
330
+ end
331
+ end
332
+
333
+ # returns an Array which consists status code and message.
334
+ attr_accessor :status
335
+
336
+ # returns a URI which is base of relative URIs in the data.
337
+ # It may differ from the URI supplied by a user because redirection.
338
+ attr_accessor :base_uri
339
+
340
+ # returns a Hash which represents header fields.
341
+ # The Hash keys are downcased for canonicalization.
342
+ attr_reader :meta
343
+
344
+ def meta_add_field(name, value) # :nodoc:
345
+ @meta[name.downcase] = value
346
+ end
347
+
348
+ # returns a Time which represents Last-Modified field.
349
+ def last_modified
350
+ if v = @meta['last-modified']
351
+ Time.httpdate(v)
352
+ else
353
+ nil
354
+ end
355
+ end
356
+
357
+ RE_LWS = /[\r\n\t ]+/n
358
+ RE_TOKEN = %r{[^\x00- ()<>@,;:\\"/\[\]?={}\x7f]+}n
359
+ RE_QUOTED_STRING = %r{"(?:[\r\n\t !#-\[\]-~\x80-\xff]|\\[\x00-\x7f])*"}n
360
+ RE_PARAMETERS = %r{(?:;#{RE_LWS}?#{RE_TOKEN}#{RE_LWS}?=#{RE_LWS}?(?:#{RE_TOKEN}|#{RE_QUOTED_STRING})#{RE_LWS}?)*}n
361
+
362
+ def content_type_parse # :nodoc:
363
+ v = @meta['content-type']
364
+ # The last (?:;#{RE_LWS}?)? matches extra ";" which violates RFC2045.
365
+ if v && %r{\A#{RE_LWS}?(#{RE_TOKEN})#{RE_LWS}?/(#{RE_TOKEN})#{RE_LWS}?(#{RE_PARAMETERS})(?:;#{RE_LWS}?)?\z}no =~ v
366
+ type = $1.downcase
367
+ subtype = $2.downcase
368
+ parameters = []
369
+ $3.scan(/;#{RE_LWS}?(#{RE_TOKEN})#{RE_LWS}?=#{RE_LWS}?(?:(#{RE_TOKEN})|(#{RE_QUOTED_STRING}))/no) {|att, val, qval|
370
+ val = qval.gsub(/[\r\n\t !#-\[\]-~\x80-\xff]+|(\\[\x00-\x7f])/) { $1 ? $1[1,1] : $& } if qval
371
+ parameters << [att.downcase, val]
372
+ }
373
+ ["#{type}/#{subtype}", *parameters]
374
+ else
375
+ nil
376
+ end
377
+ end
378
+
379
+ # returns "type/subtype" which is MIME Content-Type.
380
+ # It is downcased for canonicalization.
381
+ # Content-Type parameters are stripped.
382
+ def content_type
383
+ type, *parameters = content_type_parse
384
+ type || 'application/octet-stream'
385
+ end
386
+
387
+ # returns a charset parameter in Content-Type field.
388
+ # It is downcased for canonicalization.
389
+ #
390
+ # If charset parameter is not given but a block is given,
391
+ # the block is called and its result is returned.
392
+ # It can be used to guess charset.
393
+ #
394
+ # If charset parameter and block is not given,
395
+ # nil is returned except text type in HTTP.
396
+ # In that case, "iso-8859-1" is returned as defined by RFC2616 3.7.1.
397
+ def charset
398
+ type, *parameters = content_type_parse
399
+ if pair = parameters.assoc('charset')
400
+ pair.last.downcase
401
+ elsif block_given?
402
+ yield
403
+ elsif type && %r{\Atext/} =~ type &&
404
+ @base_uri && /\Ahttp\z/i =~ @base_uri.scheme
405
+ "iso-8859-1" # RFC2616 3.7.1
406
+ else
407
+ nil
408
+ end
409
+ end
410
+
411
+ # returns a list of encodings in Content-Encoding field
412
+ # as an Array of String.
413
+ # The encodings are downcased for canonicalization.
414
+ def content_encoding
415
+ v = @meta['content-encoding']
416
+ if v && %r{\A#{RE_LWS}?#{RE_TOKEN}#{RE_LWS}?(?:,#{RE_LWS}?#{RE_TOKEN}#{RE_LWS}?)*}o =~ v
417
+ v.scan(RE_TOKEN).map {|content_coding| content_coding.downcase}
418
+ else
419
+ []
420
+ end
421
+ end
422
+ end
423
+
424
+ # Mixin for HTTP and FTP URIs.
425
+ module OpenRead
426
+ # OpenURI::OpenRead#open provides `open' for URI::HTTP and URI::FTP.
427
+ #
428
+ # OpenURI::OpenRead#open takes optional 3 arguments as:
429
+ # OpenURI::OpenRead#open([mode [, perm]] [, options]) [{|io| ... }]
430
+ #
431
+ # `mode', `perm' is same as Kernel#open.
432
+ #
433
+ # However, `mode' must be read mode because OpenURI::OpenRead#open doesn't
434
+ # support write mode (yet).
435
+ # Also `perm' is just ignored because it is meaningful only for file
436
+ # creation.
437
+ #
438
+ # `options' must be a hash.
439
+ #
440
+ # Each pairs which key is a string in the hash specify a extra header
441
+ # field for HTTP.
442
+ # I.e. it is ignored for FTP without HTTP proxy.
443
+ #
444
+ # The hash may include other options which key is a symbol:
445
+ #
446
+ # [:proxy]
447
+ # Synopsis:
448
+ # :proxy => "http://proxy.foo.com:8000/"
449
+ # :proxy => URI.parse("http://proxy.foo.com:8000/")
450
+ # :proxy => true
451
+ # :proxy => false
452
+ # :proxy => nil
453
+ #
454
+ # If :proxy option is specified, the value should be String, URI,
455
+ # boolean or nil.
456
+ # When String or URI is given, it is treated as proxy URI.
457
+ # When true is given or the option itself is not specified,
458
+ # environment variable `scheme_proxy' is examined.
459
+ # `scheme' is replaced by `http', `https' or `ftp'.
460
+ # When false or nil is given, the environment variables are ignored and
461
+ # connection will be made to a server directly.
462
+ #
463
+ # [:http_basic_authentication]
464
+ # Synopsis:
465
+ # :http_basic_authentication=>[user, password]
466
+ #
467
+ # If :http_basic_authentication is specified,
468
+ # the value should be an array which contains 2 strings:
469
+ # username and password.
470
+ # It is used for HTTP Basic authentication defined by RFC 2617.
471
+ #
472
+ # [:content_length_proc]
473
+ # Synopsis:
474
+ # :content_length_proc => lambda {|content_length| ... }
475
+ #
476
+ # If :content_length_proc option is specified, the option value procedure
477
+ # is called before actual transfer is started.
478
+ # It takes one argument which is expected content length in bytes.
479
+ #
480
+ # If two or more transfer is done by HTTP redirection, the procedure
481
+ # is called only one for a last transfer.
482
+ #
483
+ # When expected content length is unknown, the procedure is called with
484
+ # nil.
485
+ # It is happen when HTTP response has no Content-Length header.
486
+ #
487
+ # [:progress_proc]
488
+ # Synopsis:
489
+ # :progress_proc => lambda {|size| ...}
490
+ #
491
+ # If :progress_proc option is specified, the proc is called with one
492
+ # argument each time when `open' gets content fragment from network.
493
+ # The argument `size' `size' is a accumulated transfered size in bytes.
494
+ #
495
+ # If two or more transfer is done by HTTP redirection, the procedure
496
+ # is called only one for a last transfer.
497
+ #
498
+ # :progress_proc and :content_length_proc are intended to be used for
499
+ # progress bar.
500
+ # For example, it can be implemented as follows using Ruby/ProgressBar.
501
+ #
502
+ # pbar = nil
503
+ # open("http://...",
504
+ # :content_length_proc => lambda {|t|
505
+ # if t && 0 < t
506
+ # pbar = ProgressBar.new("...", t)
507
+ # pbar.file_transfer_mode
508
+ # end
509
+ # },
510
+ # :progress_proc => lambda {|s|
511
+ # pbar.set s if pbar
512
+ # }) {|f| ... }
513
+ #
514
+ # OpenURI::OpenRead#open returns an IO like object if block is not given.
515
+ # Otherwise it yields the IO object and return the value of the block.
516
+ # The IO object is extended with OpenURI::Meta.
517
+ def open(*rest, &block)
518
+ OpenURI.open_uri(self, *rest, &block)
519
+ end
520
+
521
+ # OpenURI::OpenRead#read([options]) reads a content referenced by self and
522
+ # returns the content as string.
523
+ # The string is extended with OpenURI::Meta.
524
+ # The argument `options' is same as OpenURI::OpenRead#open.
525
+ def read(options={})
526
+ self.open(options) {|f|
527
+ str = f.read
528
+ Meta.init str, f
529
+ str
530
+ }
531
+ end
532
+ end
533
+ end
534
+
535
+ module URI
536
+ class Generic
537
+ # returns a proxy URI.
538
+ # The proxy URI is obtained from environment variables such as http_proxy,
539
+ # ftp_proxy, no_proxy, etc.
540
+ # If there is no proper proxy, nil is returned.
541
+ #
542
+ # Note that capitalized variables (HTTP_PROXY, FTP_PROXY, NO_PROXY, etc.)
543
+ # are examined too.
544
+ #
545
+ # But http_proxy and HTTP_PROXY is treated specially under CGI environment.
546
+ # It's because HTTP_PROXY may be set by Proxy: header.
547
+ # So HTTP_PROXY is not used.
548
+ # http_proxy is not used too if the variable is case insensitive.
549
+ # CGI_HTTP_PROXY can be used instead.
550
+ def find_proxy
551
+ name = self.scheme.downcase + '_proxy'
552
+ proxy_uri = nil
553
+ if name == 'http_proxy' && ENV.include?('REQUEST_METHOD') # CGI?
554
+ # HTTP_PROXY conflicts with *_proxy for proxy settings and
555
+ # HTTP_* for header information in CGI.
556
+ # So it should be careful to use it.
557
+ pairs = ENV.reject {|k, v| /\Ahttp_proxy\z/i !~ k }
558
+ case pairs.length
559
+ when 0 # no proxy setting anyway.
560
+ proxy_uri = nil
561
+ when 1
562
+ k, v = pairs.shift
563
+ if k == 'http_proxy' && ENV[k.upcase] == nil
564
+ # http_proxy is safe to use because ENV is case sensitive.
565
+ proxy_uri = ENV[name]
566
+ else
567
+ proxy_uri = nil
568
+ end
569
+ else # http_proxy is safe to use because ENV is case sensitive.
570
+ proxy_uri = ENV.to_hash[name]
571
+ end
572
+ if !proxy_uri
573
+ # Use CGI_HTTP_PROXY. cf. libwww-perl.
574
+ proxy_uri = ENV["CGI_#{name.upcase}"]
575
+ end
576
+ elsif name == 'http_proxy'
577
+ unless proxy_uri = ENV[name]
578
+ if proxy_uri = ENV[name.upcase]
579
+ warn 'The environment variable HTTP_PROXY is discouraged. Use http_proxy.'
580
+ end
581
+ end
582
+ else
583
+ proxy_uri = ENV[name] || ENV[name.upcase]
584
+ end
585
+
586
+ if proxy_uri && self.host
587
+ require 'socket'
588
+ begin
589
+ addr = IPSocket.getaddress(self.host)
590
+ proxy_uri = nil if /\A127\.|\A::1\z/ =~ addr
591
+ rescue SocketError
592
+ end
593
+ end
594
+
595
+ if proxy_uri
596
+ proxy_uri = URI.parse(proxy_uri)
597
+ name = 'no_proxy'
598
+ if no_proxy = ENV[name] || ENV[name.upcase]
599
+ no_proxy.scan(/([^:,]*)(?::(\d+))?/) {|host, port|
600
+ if /(\A|\.)#{Regexp.quote host}\z/i =~ self.host &&
601
+ (!port || self.port == port.to_i)
602
+ proxy_uri = nil
603
+ break
604
+ end
605
+ }
606
+ end
607
+ proxy_uri
608
+ else
609
+ nil
610
+ end
611
+ end
612
+ end
613
+
614
+ class HTTP
615
+ def buffer_open(buf, proxy, options) # :nodoc:
616
+ OpenURI.open_http(buf, self, proxy, options)
617
+ end
618
+
619
+ include OpenURI::OpenRead
620
+ end
621
+
622
+ class FTP
623
+ def buffer_open(buf, proxy, options) # :nodoc:
624
+ if proxy
625
+ OpenURI.open_http(buf, self, proxy, options)
626
+ return
627
+ end
628
+ require 'net/ftp'
629
+
630
+ directories = self.path.split(%r{/}, -1)
631
+ directories.shift if directories[0] == '' # strip a field before leading slash
632
+ directories.each {|d|
633
+ d.gsub!(/%([0-9A-Fa-f][0-9A-Fa-f])/) { [$1].pack("H2") }
634
+ }
635
+ unless filename = directories.pop
636
+ raise ArgumentError, "no filename: #{self.inspect}"
637
+ end
638
+ directories.each {|d|
639
+ if /[\r\n]/ =~ d
640
+ raise ArgumentError, "invalid directory: #{d.inspect}"
641
+ end
642
+ }
643
+ if /[\r\n]/ =~ filename
644
+ raise ArgumentError, "invalid filename: #{filename.inspect}"
645
+ end
646
+ typecode = self.typecode
647
+ if typecode && /\A[aid]\z/ !~ typecode
648
+ raise ArgumentError, "invalid typecode: #{typecode.inspect}"
649
+ end
650
+
651
+ # The access sequence is defined by RFC 1738
652
+ ftp = Net::FTP.open(self.host)
653
+ # todo: extract user/passwd from .netrc.
654
+ user = 'anonymous'
655
+ passwd = nil
656
+ user, passwd = self.userinfo.split(/:/) if self.userinfo
657
+ ftp.login(user, passwd)
658
+ directories.each {|cwd|
659
+ ftp.voidcmd("CWD #{cwd}")
660
+ }
661
+ if typecode
662
+ # xxx: typecode D is not handled.
663
+ ftp.voidcmd("TYPE #{typecode.upcase}")
664
+ end
665
+ if options[:content_length_proc]
666
+ options[:content_length_proc].call(ftp.size(filename))
667
+ end
668
+ ftp.retrbinary("RETR #{filename}", 4096) { |str|
669
+ buf << str
670
+ options[:progress_proc].call(buf.size) if options[:progress_proc]
671
+ }
672
+ ftp.close
673
+ buf.io.rewind
674
+ end
675
+
676
+ include OpenURI::OpenRead
677
+ end
678
+ end
@@ -0,0 +1,5 @@
1
+ module RubySL
2
+ module OpenURI
3
+ VERSION = "1.0.0"
4
+ end
5
+ end
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+ require './lib/rubysl/open-uri/version'
3
+
4
+ Gem::Specification.new do |spec|
5
+ spec.name = "rubysl-open-uri"
6
+ spec.version = RubySL::OpenURI::VERSION
7
+ spec.authors = ["Brian Shirai"]
8
+ spec.email = ["brixen@gmail.com"]
9
+ spec.description = %q{Ruby standard library uri.}
10
+ spec.summary = %q{Ruby standard library uri.}
11
+ spec.homepage = "https://github.com/rubysl/rubysl-open-uri"
12
+ spec.license = "BSD"
13
+
14
+ spec.files = `git ls-files`.split($/)
15
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
16
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
17
+ spec.require_paths = ["lib"]
18
+
19
+ spec.add_development_dependency "bundler", "~> 1.3"
20
+ spec.add_development_dependency "rake", "~> 10.0"
21
+ spec.add_development_dependency "mspec", "~> 1.5"
22
+ spec.add_development_dependency "rubysl-prettyprint", "~> 1.0"
23
+ end
metadata ADDED
@@ -0,0 +1,111 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rubysl-open-uri
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Brian Shirai
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-12-25 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: mspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: '1.5'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '1.5'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rubysl-prettyprint
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '1.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: '1.0'
69
+ description: Ruby standard library uri.
70
+ email:
71
+ - brixen@gmail.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - .gitignore
77
+ - .travis.yml
78
+ - Gemfile
79
+ - LICENSE
80
+ - README.md
81
+ - Rakefile
82
+ - lib/open-uri.rb
83
+ - lib/rubysl/open-uri.rb
84
+ - lib/rubysl/open-uri/open-uri.rb
85
+ - lib/rubysl/open-uri/version.rb
86
+ - rubysl-open-uri.gemspec
87
+ homepage: https://github.com/rubysl/rubysl-open-uri
88
+ licenses:
89
+ - BSD
90
+ metadata: {}
91
+ post_install_message:
92
+ rdoc_options: []
93
+ require_paths:
94
+ - lib
95
+ required_ruby_version: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - '>='
98
+ - !ruby/object:Gem::Version
99
+ version: '0'
100
+ required_rubygems_version: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - '>='
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
105
+ requirements: []
106
+ rubyforge_project:
107
+ rubygems_version: 2.0.7
108
+ signing_key:
109
+ specification_version: 4
110
+ summary: Ruby standard library uri.
111
+ test_files: []