rf-rest-open-uri 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []