rf-rest-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,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
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in rest-open-uri.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Rick Fletcher
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,21 @@
1
+ # rf-rest-open-uri
2
+
3
+ This is a fork of the [rest-open-uri gem][1]. The rest-open-uri gem is based on an old copy of open-uri, so it's missing newer open-uri features, like the [:ssl_verify_mode option][2]. This fork is based on open-uri from ruby 1.9.3-p327, which includes those features.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'rf-rest-open-uri'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install rf-rest-open-uri
18
+
19
+
20
+ [1]: http://rubygems.org/gems/rest-open-uri
21
+ [2]: http://ruby-doc.org/stdlib-1.9.3/libdoc/open-uri/rdoc/OpenURI/OpenRead.html
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,867 @@
1
+ require 'uri'
2
+ require 'stringio'
3
+ require 'time'
4
+ require 'net/http'
5
+
6
+ module Kernel
7
+ private
8
+ alias open_uri_original_open open # :nodoc:
9
+ class << self
10
+ alias open_uri_original_open open # :nodoc:
11
+ end
12
+
13
+ # Allows the opening of various resources including URIs.
14
+ #
15
+ # If the first argument responds to the 'open' method, 'open' is called on
16
+ # it with the rest of the arguments.
17
+ #
18
+ # If the first argument is a string that begins with xxx://, it is parsed by
19
+ # URI.parse. If the parsed object responds to the 'open' method,
20
+ # 'open' is called on it with the rest of the arguments.
21
+ #
22
+ # Otherwise, the original Kernel#open is called.
23
+ #
24
+ # Since open-uri.rb provides URI::HTTP#open, URI::HTTPS#open and
25
+ # URI::FTP#open, Kernel[#.]open can accept URIs and strings that begin with
26
+ # http://, https:// and ftp://. In these cases, the opened file object is
27
+ # extended by OpenURI::Meta.
28
+ def open(name, *rest, &block) # :doc:
29
+ if name.respond_to?(:open)
30
+ name.open(*rest, &block)
31
+ elsif name.respond_to?(:to_str) &&
32
+ %r{\A[A-Za-z][A-Za-z0-9+\-\.]*://} =~ name &&
33
+ (uri = URI.parse(name)).respond_to?(:open)
34
+ uri.open(*rest, &block)
35
+ else
36
+ open_uri_original_open(name, *rest, &block)
37
+ end
38
+ end
39
+ module_function :open
40
+ end
41
+
42
+ # OpenURI is an easy-to-use wrapper for net/http, net/https and net/ftp.
43
+ #
44
+ #== Example
45
+ #
46
+ # It is possible to open an http, https or ftp URL as though it were a file:
47
+ #
48
+ # open("http://www.ruby-lang.org/") {|f|
49
+ # f.each_line {|line| p line}
50
+ # }
51
+ #
52
+ # The opened file has several getter methods for its meta-information, as
53
+ # follows, since it is extended by OpenURI::Meta.
54
+ #
55
+ # open("http://www.ruby-lang.org/en") {|f|
56
+ # f.each_line {|line| p line}
57
+ # p f.base_uri # <URI::HTTP:0x40e6ef2 URL:http://www.ruby-lang.org/en/>
58
+ # p f.content_type # "text/html"
59
+ # p f.charset # "iso-8859-1"
60
+ # p f.content_encoding # []
61
+ # p f.last_modified # Thu Dec 05 02:45:02 UTC 2002
62
+ # }
63
+ #
64
+ # Additional header fields can be specified by an optional hash argument.
65
+ #
66
+ # open("http://www.ruby-lang.org/en/",
67
+ # "User-Agent" => "Ruby/#{RUBY_VERSION}",
68
+ # "From" => "foo@bar.invalid",
69
+ # "Referer" => "http://www.ruby-lang.org/") {|f|
70
+ # # ...
71
+ # }
72
+ #
73
+ # The environment variables such as http_proxy, https_proxy and ftp_proxy
74
+ # are in effect by default. :proxy => nil disables proxy.
75
+ #
76
+ # open("http://www.ruby-lang.org/en/raa.html", :proxy => nil) {|f|
77
+ # # ...
78
+ # }
79
+ #
80
+ # URI objects can be opened in a similar way.
81
+ #
82
+ # uri = URI.parse("http://www.ruby-lang.org/en/")
83
+ # uri.open {|f|
84
+ # # ...
85
+ # }
86
+ #
87
+ # URI objects can be read directly. The returned string is also extended by
88
+ # OpenURI::Meta.
89
+ #
90
+ # str = uri.read
91
+ # p str.base_uri
92
+ #
93
+ # Author:: Tanaka Akira <akr@m17n.org>
94
+
95
+ module OpenURI
96
+ Options = {
97
+ :proxy => true,
98
+ :proxy_http_basic_authentication => true,
99
+ :method => true,
100
+ :progress_proc => true,
101
+ :content_length_proc => true,
102
+ :http_basic_authentication => true,
103
+ :body => true,
104
+ :read_timeout => true,
105
+ :ssl_ca_cert => nil,
106
+ :ssl_verify_mode => nil,
107
+ :ftp_active_mode => false,
108
+ :redirect => true,
109
+ }
110
+
111
+ # xxx: I'd like a better way of representing this trivial mapping.
112
+ Methods = {
113
+ :copy => Net::HTTP::Copy,
114
+ :delete => Net::HTTP::Delete,
115
+ :get => Net::HTTP::Get,
116
+ :head => Net::HTTP::Head,
117
+ :lock => Net::HTTP::Lock,
118
+ :mkcol => Net::HTTP::Mkcol,
119
+ :options => Net::HTTP::Options,
120
+ :post => Net::HTTP::Post,
121
+ :propfind => Net::HTTP::Propfind,
122
+ :proppatch => Net::HTTP::Proppatch,
123
+ :put => Net::HTTP::Put,
124
+ :trace => Net::HTTP::Trace,
125
+ :unlock => Net::HTTP::Unlock,
126
+ }
127
+
128
+ def OpenURI.check_options(options) # :nodoc:
129
+ options.each {|k, v|
130
+ next unless Symbol === k
131
+ unless Options.include? k
132
+ raise ArgumentError, "unrecognized option: #{k}"
133
+ end
134
+ }
135
+
136
+ m = options[:method]
137
+ if m && !Methods[m]
138
+ raise ArgumentError, "unrecognized HTTP method symbol: #{m}"
139
+ end
140
+ end
141
+
142
+ def OpenURI.scan_open_optional_arguments(*rest) # :nodoc:
143
+ if !rest.empty? && (String === rest.first || Integer === rest.first)
144
+ mode = rest.shift
145
+ if !rest.empty? && Integer === rest.first
146
+ perm = rest.shift
147
+ end
148
+ end
149
+ return mode, perm, rest
150
+ end
151
+
152
+ def OpenURI.open_uri(name, *rest) # :nodoc:
153
+ uri = URI::Generic === name ? name : URI.parse(name)
154
+ mode, _, rest = OpenURI.scan_open_optional_arguments(*rest)
155
+ options = rest.shift if !rest.empty? && Hash === rest.first
156
+ raise ArgumentError.new("extra arguments") if !rest.empty?
157
+ options ||= {}
158
+ OpenURI.check_options(options)
159
+
160
+ if /\Arb?(?:\Z|:([^:]+))/ =~ mode
161
+ encoding, = $1,Encoding.find($1) if $1
162
+ mode = nil
163
+ end
164
+
165
+ unless mode == nil ||
166
+ mode == 'r' || mode == 'rb' ||
167
+ mode == File::RDONLY
168
+ raise ArgumentError.new("invalid access mode #{mode} (#{uri.class} resource is read only.)")
169
+ end
170
+
171
+ io = open_loop(uri, options)
172
+ io.set_encoding(encoding) if encoding
173
+ if block_given?
174
+ begin
175
+ yield io
176
+ ensure
177
+ io.close
178
+ end
179
+ else
180
+ io
181
+ end
182
+ end
183
+
184
+ def OpenURI.open_loop(uri, options) # :nodoc:
185
+ proxy_opts = []
186
+ proxy_opts << :proxy_http_basic_authentication if options.include? :proxy_http_basic_authentication
187
+ proxy_opts << :proxy if options.include? :proxy
188
+ proxy_opts.compact!
189
+ if 1 < proxy_opts.length
190
+ raise ArgumentError, "multiple proxy options specified"
191
+ end
192
+ case proxy_opts.first
193
+ when :proxy_http_basic_authentication
194
+ opt_proxy, proxy_user, proxy_pass = options.fetch(:proxy_http_basic_authentication)
195
+ proxy_user = proxy_user.to_str
196
+ proxy_pass = proxy_pass.to_str
197
+ if opt_proxy == true
198
+ raise ArgumentError.new("Invalid authenticated proxy option: #{options[:proxy_http_basic_authentication].inspect}")
199
+ end
200
+ when :proxy
201
+ opt_proxy = options.fetch(:proxy)
202
+ proxy_user = nil
203
+ proxy_pass = nil
204
+ when nil
205
+ opt_proxy = true
206
+ proxy_user = nil
207
+ proxy_pass = nil
208
+ end
209
+ case opt_proxy
210
+ when true
211
+ find_proxy = lambda {|u| pxy = u.find_proxy; pxy ? [pxy, nil, nil] : nil}
212
+ when nil, false
213
+ find_proxy = lambda {|u| nil}
214
+ when String
215
+ opt_proxy = URI.parse(opt_proxy)
216
+ find_proxy = lambda {|u| [opt_proxy, proxy_user, proxy_pass]}
217
+ when URI::Generic
218
+ find_proxy = lambda {|u| [opt_proxy, proxy_user, proxy_pass]}
219
+ else
220
+ raise ArgumentError.new("Invalid proxy option: #{opt_proxy}")
221
+ end
222
+
223
+ uri_set = {}
224
+ buf = nil
225
+ while true
226
+ redirect = catch(:open_uri_redirect) {
227
+ buf = Buffer.new
228
+ uri.buffer_open(buf, find_proxy.call(uri), options)
229
+ nil
230
+ }
231
+ if redirect
232
+ if redirect.relative?
233
+ # Although it violates RFC2616, Location: field may have relative
234
+ # URI. It is converted to absolute URI using uri as a base URI.
235
+ redirect = uri + redirect
236
+ end
237
+ if !options.fetch(:redirect, true)
238
+ raise HTTPRedirect.new(buf.io.status.join(' '), buf.io, redirect)
239
+ end
240
+ unless OpenURI.redirectable?(uri, redirect)
241
+ raise "redirection forbidden: #{uri} -> #{redirect}"
242
+ end
243
+ if options.include? :http_basic_authentication
244
+ # send authentication only for the URI directly specified.
245
+ options = options.dup
246
+ options.delete :http_basic_authentication
247
+ end
248
+ uri = redirect
249
+ raise "HTTP redirection loop: #{uri}" if uri_set.include? uri.to_s
250
+ uri_set[uri.to_s] = true
251
+ else
252
+ break
253
+ end
254
+ end
255
+ io = buf.io
256
+ io.base_uri = uri
257
+ io
258
+ end
259
+
260
+ def OpenURI.redirectable?(uri1, uri2) # :nodoc:
261
+ # This test is intended to forbid a redirection from http://... to
262
+ # file:///etc/passwd, file:///dev/zero, etc. CVE-2011-1521
263
+ # https to http redirect is also forbidden intentionally.
264
+ # It avoids sending secure cookie or referer by non-secure HTTP protocol.
265
+ # (RFC 2109 4.3.1, RFC 2965 3.3, RFC 2616 15.1.3)
266
+ # However this is ad hoc. It should be extensible/configurable.
267
+ uri1.scheme.downcase == uri2.scheme.downcase ||
268
+ (/\A(?:http|ftp)\z/i =~ uri1.scheme && /\A(?:http|ftp)\z/i =~ uri2.scheme)
269
+ end
270
+
271
+ def OpenURI.open_http(buf, target, proxy, options) # :nodoc:
272
+ if proxy
273
+ proxy_uri, proxy_user, proxy_pass = proxy
274
+ raise "Non-HTTP proxy URI: #{proxy_uri}" if proxy_uri.class != URI::HTTP
275
+ end
276
+
277
+ if target.userinfo && "1.9.0" <= RUBY_VERSION
278
+ # don't raise for 1.8 because compatibility.
279
+ raise ArgumentError, "userinfo not supported. [RFC3986]"
280
+ end
281
+
282
+ header = {}
283
+ options.each {|k, v| header[k] = v if String === k }
284
+
285
+ klass = Net::HTTP
286
+ if URI::HTTP === target
287
+ # HTTP or HTTPS
288
+ if proxy
289
+ if proxy_user && proxy_pass
290
+ klass = Net::HTTP::Proxy(proxy_uri.hostname, proxy_uri.port, proxy_user, proxy_pass)
291
+ else
292
+ klass = Net::HTTP::Proxy(proxy_uri.hostname, proxy_uri.port)
293
+ end
294
+ end
295
+ target_host = target.hostname
296
+ target_port = target.port
297
+ request_uri = target.request_uri
298
+ else
299
+ # FTP over HTTP proxy
300
+ target_host = proxy_uri.hostname
301
+ target_port = proxy_uri.port
302
+ request_uri = target.to_s
303
+ if proxy_user && proxy_pass
304
+ header["Proxy-Authorization"] = 'Basic ' + ["#{proxy_user}:#{proxy_pass}"].pack('m').delete("\r\n")
305
+ end
306
+ end
307
+
308
+ http = klass.new(target_host, target_port)
309
+ if target.class == URI::HTTPS
310
+ require 'net/https'
311
+ http.use_ssl = true
312
+ http.verify_mode = options[:ssl_verify_mode] || OpenSSL::SSL::VERIFY_PEER
313
+ store = OpenSSL::X509::Store.new
314
+ if options[:ssl_ca_cert]
315
+ if File.directory? options[:ssl_ca_cert]
316
+ store.add_path options[:ssl_ca_cert]
317
+ else
318
+ store.add_file options[:ssl_ca_cert]
319
+ end
320
+ else
321
+ store.set_default_paths
322
+ end
323
+ http.cert_store = store
324
+ end
325
+ if options.include? :read_timeout
326
+ http.read_timeout = options[:read_timeout]
327
+ end
328
+
329
+ resp = nil
330
+ http.start {
331
+ methodclass = Methods[options[:method] || :get]
332
+ req = methodclass.new(request_uri, header)
333
+ req.body = options[:body] if methodclass::REQUEST_HAS_BODY
334
+
335
+ if options.include? :http_basic_authentication
336
+ user, pass = options[:http_basic_authentication]
337
+ req.basic_auth user, pass
338
+ end
339
+ http.request(req) {|response|
340
+ resp = response
341
+ if options[:content_length_proc] && Net::HTTPSuccess === resp
342
+ if resp.key?('Content-Length')
343
+ options[:content_length_proc].call(resp['Content-Length'].to_i)
344
+ else
345
+ options[:content_length_proc].call(nil)
346
+ end
347
+ end
348
+ resp.read_body {|str|
349
+ buf << str
350
+ if options[:progress_proc] && Net::HTTPSuccess === resp
351
+ options[:progress_proc].call(buf.size)
352
+ end
353
+ }
354
+ }
355
+ }
356
+ io = buf.io
357
+ io.rewind
358
+ io.status = [resp.code, resp.message]
359
+ resp.each {|name,value| buf.io.meta_add_field name, value }
360
+ case resp
361
+ when Net::HTTPSuccess
362
+ when Net::HTTPMovedPermanently, # 301
363
+ Net::HTTPFound, # 302
364
+ Net::HTTPSeeOther, # 303
365
+ Net::HTTPTemporaryRedirect # 307
366
+ begin
367
+ loc_uri = URI.parse(resp['location'])
368
+ rescue URI::InvalidURIError
369
+ raise OpenURI::HTTPError.new(io.status.join(' ') + ' (Invalid Location URI)', io)
370
+ end
371
+ throw :open_uri_redirect, loc_uri
372
+ else
373
+ raise OpenURI::HTTPError.new(io.status.join(' '), io)
374
+ end
375
+ end
376
+
377
+ class HTTPError < StandardError
378
+ def initialize(message, io)
379
+ super(message)
380
+ @io = io
381
+ end
382
+ attr_reader :io
383
+ end
384
+
385
+ class HTTPRedirect < HTTPError
386
+ def initialize(message, io, uri)
387
+ super(message, io)
388
+ @uri = uri
389
+ end
390
+ attr_reader :uri
391
+ end
392
+
393
+ class Buffer # :nodoc:
394
+ def initialize
395
+ @io = StringIO.new
396
+ @size = 0
397
+ end
398
+ attr_reader :size
399
+
400
+ StringMax = 10240
401
+ def <<(str)
402
+ @io << str
403
+ @size += str.length
404
+ if StringIO === @io && StringMax < @size
405
+ require 'tempfile'
406
+ io = Tempfile.new('open-uri')
407
+ io.binmode
408
+ Meta.init io, @io if Meta === @io
409
+ io << @io.string
410
+ @io = io
411
+ end
412
+ end
413
+
414
+ def io
415
+ Meta.init @io unless Meta === @io
416
+ @io
417
+ end
418
+ end
419
+
420
+ # Mixin for holding meta-information.
421
+ module Meta
422
+ def Meta.init(obj, src=nil) # :nodoc:
423
+ obj.extend Meta
424
+ obj.instance_eval {
425
+ @base_uri = nil
426
+ @meta = {}
427
+ }
428
+ if src
429
+ obj.status = src.status
430
+ obj.base_uri = src.base_uri
431
+ src.meta.each {|name, value|
432
+ obj.meta_add_field(name, value)
433
+ }
434
+ end
435
+ end
436
+
437
+ # returns an Array that consists of status code and message.
438
+ attr_accessor :status
439
+
440
+ # returns a URI that is the base of relative URIs in the data.
441
+ # It may differ from the URI supplied by a user due to redirection.
442
+ attr_accessor :base_uri
443
+
444
+ # returns a Hash that represents header fields.
445
+ # The Hash keys are downcased for canonicalization.
446
+ attr_reader :meta
447
+
448
+ def meta_setup_encoding # :nodoc:
449
+ charset = self.charset
450
+ enc = nil
451
+ if charset
452
+ begin
453
+ enc = Encoding.find(charset)
454
+ rescue ArgumentError
455
+ end
456
+ end
457
+ enc = Encoding::ASCII_8BIT unless enc
458
+ if self.respond_to? :force_encoding
459
+ self.force_encoding(enc)
460
+ elsif self.respond_to? :string
461
+ self.string.force_encoding(enc)
462
+ else # Tempfile
463
+ self.set_encoding enc
464
+ end
465
+ end
466
+
467
+ def meta_add_field(name, value) # :nodoc:
468
+ name = name.downcase
469
+ @meta[name] = value
470
+ meta_setup_encoding if name == 'content-type'
471
+ end
472
+
473
+ # returns a Time that represents the Last-Modified field.
474
+ def last_modified
475
+ if v = @meta['last-modified']
476
+ Time.httpdate(v)
477
+ else
478
+ nil
479
+ end
480
+ end
481
+
482
+ RE_LWS = /[\r\n\t ]+/n
483
+ RE_TOKEN = %r{[^\x00- ()<>@,;:\\"/\[\]?={}\x7f]+}n
484
+ RE_QUOTED_STRING = %r{"(?:[\r\n\t !#-\[\]-~\x80-\xff]|\\[\x00-\x7f])*"}n
485
+ RE_PARAMETERS = %r{(?:;#{RE_LWS}?#{RE_TOKEN}#{RE_LWS}?=#{RE_LWS}?(?:#{RE_TOKEN}|#{RE_QUOTED_STRING})#{RE_LWS}?)*}n
486
+
487
+ def content_type_parse # :nodoc:
488
+ v = @meta['content-type']
489
+ # The last (?:;#{RE_LWS}?)? matches extra ";" which violates RFC2045.
490
+ if v && %r{\A#{RE_LWS}?(#{RE_TOKEN})#{RE_LWS}?/(#{RE_TOKEN})#{RE_LWS}?(#{RE_PARAMETERS})(?:;#{RE_LWS}?)?\z}no =~ v
491
+ type = $1.downcase
492
+ subtype = $2.downcase
493
+ parameters = []
494
+ $3.scan(/;#{RE_LWS}?(#{RE_TOKEN})#{RE_LWS}?=#{RE_LWS}?(?:(#{RE_TOKEN})|(#{RE_QUOTED_STRING}))/no) {|att, val, qval|
495
+ if qval
496
+ val = qval[1...-1].gsub(/[\r\n\t !#-\[\]-~\x80-\xff]+|(\\[\x00-\x7f])/n) { $1 ? $1[1,1] : $& }
497
+ end
498
+ parameters << [att.downcase, val]
499
+ }
500
+ ["#{type}/#{subtype}", *parameters]
501
+ else
502
+ nil
503
+ end
504
+ end
505
+
506
+ # returns "type/subtype" which is MIME Content-Type.
507
+ # It is downcased for canonicalization.
508
+ # Content-Type parameters are stripped.
509
+ def content_type
510
+ type, *_ = content_type_parse
511
+ type || 'application/octet-stream'
512
+ end
513
+
514
+ # returns a charset parameter in Content-Type field.
515
+ # It is downcased for canonicalization.
516
+ #
517
+ # If charset parameter is not given but a block is given,
518
+ # the block is called and its result is returned.
519
+ # It can be used to guess charset.
520
+ #
521
+ # If charset parameter and block is not given,
522
+ # nil is returned except text type in HTTP.
523
+ # In that case, "iso-8859-1" is returned as defined by RFC2616 3.7.1.
524
+ def charset
525
+ type, *parameters = content_type_parse
526
+ if pair = parameters.assoc('charset')
527
+ pair.last.downcase
528
+ elsif block_given?
529
+ yield
530
+ elsif type && %r{\Atext/} =~ type &&
531
+ @base_uri && /\Ahttp\z/i =~ @base_uri.scheme
532
+ "iso-8859-1" # RFC2616 3.7.1
533
+ else
534
+ nil
535
+ end
536
+ end
537
+
538
+ # returns a list of encodings in Content-Encoding field
539
+ # as an Array of String.
540
+ # The encodings are downcased for canonicalization.
541
+ def content_encoding
542
+ v = @meta['content-encoding']
543
+ if v && %r{\A#{RE_LWS}?#{RE_TOKEN}#{RE_LWS}?(?:,#{RE_LWS}?#{RE_TOKEN}#{RE_LWS}?)*}o =~ v
544
+ v.scan(RE_TOKEN).map {|content_coding| content_coding.downcase}
545
+ else
546
+ []
547
+ end
548
+ end
549
+ end
550
+
551
+ # Mixin for HTTP and FTP URIs.
552
+ module OpenRead
553
+ # OpenURI::OpenRead#open provides `open' for URI::HTTP and URI::FTP.
554
+ #
555
+ # OpenURI::OpenRead#open takes optional 3 arguments as:
556
+ #
557
+ # OpenURI::OpenRead#open([mode [, perm]] [, options]) [{|io| ... }]
558
+ #
559
+ # OpenURI::OpenRead#open returns an IO-like object if block is not given.
560
+ # Otherwise it yields the IO object and return the value of the block.
561
+ # The IO object is extended with OpenURI::Meta.
562
+ #
563
+ # +mode+ and +perm+ are the same as Kernel#open.
564
+ #
565
+ # However, +mode+ must be read mode because OpenURI::OpenRead#open doesn't
566
+ # support write mode (yet).
567
+ # Also +perm+ is ignored because it is meaningful only for file creation.
568
+ #
569
+ # +options+ must be a hash.
570
+ #
571
+ # Each option with a string key specifies an extra header field for HTTP.
572
+ # I.e., it is ignored for FTP without HTTP proxy.
573
+ #
574
+ # The hash may include other options, where keys are symbols:
575
+ #
576
+ # [:proxy]
577
+ # Synopsis:
578
+ # :proxy => "http://proxy.foo.com:8000/"
579
+ # :proxy => URI.parse("http://proxy.foo.com:8000/")
580
+ # :proxy => true
581
+ # :proxy => false
582
+ # :proxy => nil
583
+ #
584
+ # If :proxy option is specified, the value should be String, URI,
585
+ # boolean or nil.
586
+ #
587
+ # When String or URI is given, it is treated as proxy URI.
588
+ #
589
+ # When true is given or the option itself is not specified,
590
+ # environment variable `scheme_proxy' is examined.
591
+ # `scheme' is replaced by `http', `https' or `ftp'.
592
+ #
593
+ # When false or nil is given, the environment variables are ignored and
594
+ # connection will be made to a server directly.
595
+ #
596
+ # [:proxy_http_basic_authentication]
597
+ # Synopsis:
598
+ # :proxy_http_basic_authentication =>
599
+ # ["http://proxy.foo.com:8000/", "proxy-user", "proxy-password"]
600
+ # :proxy_http_basic_authentication =>
601
+ # [URI.parse("http://proxy.foo.com:8000/"),
602
+ # "proxy-user", "proxy-password"]
603
+ #
604
+ # If :proxy option is specified, the value should be an Array with 3
605
+ # elements. It should contain a proxy URI, a proxy user name and a proxy
606
+ # password. The proxy URI should be a String, an URI or nil. The proxy
607
+ # user name and password should be a String.
608
+ #
609
+ # If nil is given for the proxy URI, this option is just ignored.
610
+ #
611
+ # If :proxy and :proxy_http_basic_authentication is specified,
612
+ # ArgumentError is raised.
613
+ #
614
+ # [:http_basic_authentication]
615
+ # Synopsis:
616
+ # :http_basic_authentication=>[user, password]
617
+ #
618
+ # If :http_basic_authentication is specified,
619
+ # the value should be an array which contains 2 strings:
620
+ # username and password.
621
+ # It is used for HTTP Basic authentication defined by RFC 2617.
622
+ #
623
+ # [:content_length_proc]
624
+ # Synopsis:
625
+ # :content_length_proc => lambda {|content_length| ... }
626
+ #
627
+ # If :content_length_proc option is specified, the option value procedure
628
+ # is called before actual transfer is started.
629
+ # It takes one argument, which is expected content length in bytes.
630
+ #
631
+ # If two or more transfer is done by HTTP redirection, the procedure
632
+ # is called only one for a last transfer.
633
+ #
634
+ # When expected content length is unknown, the procedure is called with
635
+ # nil. This happens when the HTTP response has no Content-Length header.
636
+ #
637
+ # [:progress_proc]
638
+ # Synopsis:
639
+ # :progress_proc => lambda {|size| ...}
640
+ #
641
+ # If :progress_proc option is specified, the proc is called with one
642
+ # argument each time when `open' gets content fragment from network.
643
+ # The argument +size+ is the accumulated transferred size in bytes.
644
+ #
645
+ # If two or more transfer is done by HTTP redirection, the procedure
646
+ # is called only one for a last transfer.
647
+ #
648
+ # :progress_proc and :content_length_proc are intended to be used for
649
+ # progress bar.
650
+ # For example, it can be implemented as follows using Ruby/ProgressBar.
651
+ #
652
+ # pbar = nil
653
+ # open("http://...",
654
+ # :content_length_proc => lambda {|t|
655
+ # if t && 0 < t
656
+ # pbar = ProgressBar.new("...", t)
657
+ # pbar.file_transfer_mode
658
+ # end
659
+ # },
660
+ # :progress_proc => lambda {|s|
661
+ # pbar.set s if pbar
662
+ # }) {|f| ... }
663
+ #
664
+ # [:read_timeout]
665
+ # Synopsis:
666
+ # :read_timeout=>nil (no timeout)
667
+ # :read_timeout=>10 (10 second)
668
+ #
669
+ # :read_timeout option specifies a timeout of read for http connections.
670
+ #
671
+ # [:ssl_ca_cert]
672
+ # Synopsis:
673
+ # :ssl_ca_cert=>filename
674
+ #
675
+ # :ssl_ca_cert is used to specify CA certificate for SSL.
676
+ # If it is given, default certificates are not used.
677
+ #
678
+ # [:ssl_verify_mode]
679
+ # Synopsis:
680
+ # :ssl_verify_mode=>mode
681
+ #
682
+ # :ssl_verify_mode is used to specify openssl verify mode.
683
+ #
684
+ # [:ftp_active_mode]
685
+ # Synopsis:
686
+ # :ftp_active_mode=>bool
687
+ #
688
+ # <tt>:ftp_active_mode => true</tt> is used to make ftp active mode.
689
+ # Ruby 1.9 uses passive mode by default.
690
+ # Note that the active mode is default in Ruby 1.8 or prior.
691
+ #
692
+ # [:redirect]
693
+ # Synopsis:
694
+ # :redirect=>bool
695
+ #
696
+ # +:redirect+ is true by default. <tt>:redirect => false</tt> is used to
697
+ # disable all HTTP redirects.
698
+ #
699
+ # OpenURI::HTTPRedirect exception raised on redirection.
700
+ # Using +true+ also means that redirections between http and ftp are
701
+ # permitted.
702
+ #
703
+ def open(*rest, &block)
704
+ OpenURI.open_uri(self, *rest, &block)
705
+ end
706
+
707
+ # OpenURI::OpenRead#read([options]) reads a content referenced by self and
708
+ # returns the content as string.
709
+ # The string is extended with OpenURI::Meta.
710
+ # The argument +options+ is same as OpenURI::OpenRead#open.
711
+ def read(options={})
712
+ self.open(options) {|f|
713
+ str = f.read
714
+ Meta.init str, f
715
+ str
716
+ }
717
+ end
718
+ end
719
+ end
720
+
721
+ module URI
722
+ class Generic
723
+ # returns a proxy URI.
724
+ # The proxy URI is obtained from environment variables such as http_proxy,
725
+ # ftp_proxy, no_proxy, etc.
726
+ # If there is no proper proxy, nil is returned.
727
+ #
728
+ # Note that capitalized variables (HTTP_PROXY, FTP_PROXY, NO_PROXY, etc.)
729
+ # are examined too.
730
+ #
731
+ # But http_proxy and HTTP_PROXY is treated specially under CGI environment.
732
+ # It's because HTTP_PROXY may be set by Proxy: header.
733
+ # So HTTP_PROXY is not used.
734
+ # http_proxy is not used too if the variable is case insensitive.
735
+ # CGI_HTTP_PROXY can be used instead.
736
+ def find_proxy
737
+ name = self.scheme.downcase + '_proxy'
738
+ proxy_uri = nil
739
+ if name == 'http_proxy' && ENV.include?('REQUEST_METHOD') # CGI?
740
+ # HTTP_PROXY conflicts with *_proxy for proxy settings and
741
+ # HTTP_* for header information in CGI.
742
+ # So it should be careful to use it.
743
+ pairs = ENV.reject {|k, v| /\Ahttp_proxy\z/i !~ k }
744
+ case pairs.length
745
+ when 0 # no proxy setting anyway.
746
+ proxy_uri = nil
747
+ when 1
748
+ k, _ = pairs.shift
749
+ if k == 'http_proxy' && ENV[k.upcase] == nil
750
+ # http_proxy is safe to use because ENV is case sensitive.
751
+ proxy_uri = ENV[name]
752
+ else
753
+ proxy_uri = nil
754
+ end
755
+ else # http_proxy is safe to use because ENV is case sensitive.
756
+ proxy_uri = ENV.to_hash[name]
757
+ end
758
+ if !proxy_uri
759
+ # Use CGI_HTTP_PROXY. cf. libwww-perl.
760
+ proxy_uri = ENV["CGI_#{name.upcase}"]
761
+ end
762
+ elsif name == 'http_proxy'
763
+ unless proxy_uri = ENV[name]
764
+ if proxy_uri = ENV[name.upcase]
765
+ warn 'The environment variable HTTP_PROXY is discouraged. Use http_proxy.'
766
+ end
767
+ end
768
+ else
769
+ proxy_uri = ENV[name] || ENV[name.upcase]
770
+ end
771
+
772
+ if proxy_uri && self.hostname
773
+ require 'socket'
774
+ begin
775
+ addr = IPSocket.getaddress(self.hostname)
776
+ proxy_uri = nil if /\A127\.|\A::1\z/ =~ addr
777
+ rescue SocketError
778
+ end
779
+ end
780
+
781
+ if proxy_uri
782
+ proxy_uri = URI.parse(proxy_uri)
783
+ name = 'no_proxy'
784
+ if no_proxy = ENV[name] || ENV[name.upcase]
785
+ no_proxy.scan(/([^:,]*)(?::(\d+))?/) {|host, port|
786
+ if /(\A|\.)#{Regexp.quote host}\z/i =~ self.host &&
787
+ (!port || self.port == port.to_i)
788
+ proxy_uri = nil
789
+ break
790
+ end
791
+ }
792
+ end
793
+ proxy_uri
794
+ else
795
+ nil
796
+ end
797
+ end
798
+ end
799
+
800
+ class HTTP
801
+ def buffer_open(buf, proxy, options) # :nodoc:
802
+ OpenURI.open_http(buf, self, proxy, options)
803
+ end
804
+
805
+ include OpenURI::OpenRead
806
+ end
807
+
808
+ class FTP
809
+ def buffer_open(buf, proxy, options) # :nodoc:
810
+ if proxy
811
+ OpenURI.open_http(buf, self, proxy, options)
812
+ return
813
+ end
814
+ require 'net/ftp'
815
+
816
+ path = self.path
817
+ path = path.sub(%r{\A/}, '%2F') # re-encode the beginning slash because uri library decodes it.
818
+ directories = path.split(%r{/}, -1)
819
+ directories.each {|d|
820
+ d.gsub!(/%([0-9A-Fa-f][0-9A-Fa-f])/) { [$1].pack("H2") }
821
+ }
822
+ unless filename = directories.pop
823
+ raise ArgumentError, "no filename: #{self.inspect}"
824
+ end
825
+ directories.each {|d|
826
+ if /[\r\n]/ =~ d
827
+ raise ArgumentError, "invalid directory: #{d.inspect}"
828
+ end
829
+ }
830
+ if /[\r\n]/ =~ filename
831
+ raise ArgumentError, "invalid filename: #{filename.inspect}"
832
+ end
833
+ typecode = self.typecode
834
+ if typecode && /\A[aid]\z/ !~ typecode
835
+ raise ArgumentError, "invalid typecode: #{typecode.inspect}"
836
+ end
837
+
838
+ # The access sequence is defined by RFC 1738
839
+ ftp = Net::FTP.new
840
+ ftp.connect(self.hostname, self.port)
841
+ ftp.passive = true if !options[:ftp_active_mode]
842
+ # todo: extract user/passwd from .netrc.
843
+ user = 'anonymous'
844
+ passwd = nil
845
+ user, passwd = self.userinfo.split(/:/) if self.userinfo
846
+ ftp.login(user, passwd)
847
+ directories.each {|cwd|
848
+ ftp.voidcmd("CWD #{cwd}")
849
+ }
850
+ if typecode
851
+ # xxx: typecode D is not handled.
852
+ ftp.voidcmd("TYPE #{typecode.upcase}")
853
+ end
854
+ if options[:content_length_proc]
855
+ options[:content_length_proc].call(ftp.size(filename))
856
+ end
857
+ ftp.retrbinary("RETR #{filename}", 4096) { |str|
858
+ buf << str
859
+ options[:progress_proc].call(buf.size) if options[:progress_proc]
860
+ }
861
+ ftp.close
862
+ buf.io.rewind
863
+ end
864
+
865
+ include OpenURI::OpenRead
866
+ end
867
+ end
@@ -0,0 +1,22 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "rf-rest-open-uri"
7
+ spec.version = "1.0.0"
8
+ spec.authors = ["Leonard Richardson", "Rick Fletcher"]
9
+ spec.description = %q{A fork of rest-open-uri, updated for ruby 1.9.3+}
10
+ spec.summary = %q{rf-rest-open-uri applies the original rest-open-uri patch to ruby 1.9.3's open-uri. The version provided with rest-open-uri 1.0.0 is old, and missing newer open-uri features like :ssl_verify_mode.}
11
+ spec.homepage = "https://github.com/rfletcher/rf-rest-open-uri"
12
+ spec.license = "MIT"
13
+
14
+ spec.required_ruby_version = ">= 1.9.3"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "rake"
22
+ end
metadata ADDED
@@ -0,0 +1,71 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rf-rest-open-uri
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Leonard Richardson
9
+ - Rick Fletcher
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2014-03-21 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rake
17
+ requirement: !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ! '>='
21
+ - !ruby/object:Gem::Version
22
+ version: '0'
23
+ type: :development
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ none: false
27
+ requirements:
28
+ - - ! '>='
29
+ - !ruby/object:Gem::Version
30
+ version: '0'
31
+ description: A fork of rest-open-uri, updated for ruby 1.9.3+
32
+ email:
33
+ executables: []
34
+ extensions: []
35
+ extra_rdoc_files: []
36
+ files:
37
+ - .gitignore
38
+ - Gemfile
39
+ - LICENSE.txt
40
+ - README.md
41
+ - Rakefile
42
+ - lib/rf-rest-open-uri.rb
43
+ - rf-rest-open-uri.gemspec
44
+ homepage: https://github.com/rfletcher/rf-rest-open-uri
45
+ licenses:
46
+ - MIT
47
+ post_install_message:
48
+ rdoc_options: []
49
+ require_paths:
50
+ - lib
51
+ required_ruby_version: !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ! '>='
55
+ - !ruby/object:Gem::Version
56
+ version: 1.9.3
57
+ required_rubygems_version: !ruby/object:Gem::Requirement
58
+ none: false
59
+ requirements:
60
+ - - ! '>='
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ requirements: []
64
+ rubyforge_project:
65
+ rubygems_version: 1.8.23
66
+ signing_key:
67
+ specification_version: 3
68
+ summary: rf-rest-open-uri applies the original rest-open-uri patch to ruby 1.9.3's
69
+ open-uri. The version provided with rest-open-uri 1.0.0 is old, and missing newer
70
+ open-uri features like :ssl_verify_mode.
71
+ test_files: []