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.
Files changed (3) hide show
  1. data/CHANGELOG +1 -0
  2. data/lib/rest-open-uri.rb +721 -0
  3. metadata +46 -0
@@ -0,0 +1 @@
1
+ 1.0.0 - 20061221 - Initial release.
@@ -0,0 +1,721 @@
1
+ # This is a hack of (and drop-in replacement for) open-uri that
2
+ # supports entity-bodies, and HTTP methods other than GET. This makes
3
+ # it easy to build clients for REST web services.
4
+
5
+ require 'uri'
6
+ require 'stringio'
7
+ require 'time'
8
+ require 'net/http'
9
+
10
+ module Kernel
11
+ private
12
+ alias open_uri_original_open open # :nodoc:
13
+
14
+ # makes possible to open various resources including URIs.
15
+ # If the first argument respond to `open' method,
16
+ # the method is called with the rest arguments.
17
+ #
18
+ # If the first argument is a string which begins with xxx://,
19
+ # it is parsed by URI.parse. If the parsed object respond to `open' method,
20
+ # the method is called with the rest arguments.
21
+ #
22
+ # Otherwise original open is called.
23
+ #
24
+ # Since open-uri.rb provides URI::HTTP#open, URI::HTTPS#open and
25
+ # URI::FTP#open,
26
+ # Kernel[#.]open can accepts such URIs and strings which begins with
27
+ # http://, https:// and ftp://.
28
+ # In these case, the opened file object is extended by OpenURI::Meta.
29
+ def open(name, *rest, &block) # :doc:
30
+ if name.respond_to?(:open)
31
+ name.open(*rest, &block)
32
+ elsif name.respond_to?(:to_str) &&
33
+ %r{\A[A-Za-z][A-Za-z0-9+\-\.]*://} =~ name &&
34
+ (uri = URI.parse(name)).respond_to?(:open)
35
+ uri.open(*rest, &block)
36
+ else
37
+ open_uri_original_open(name, *rest, &block)
38
+ end
39
+ end
40
+ module_function :open
41
+ end
42
+
43
+ # OpenURI is an easy-to-use wrapper for net/http, net/https and net/ftp.
44
+ #
45
+ #== Example
46
+ #
47
+ # It is possible to open http/https/ftp URL as usual like opening a file:
48
+ #
49
+ # open("http://www.ruby-lang.org/") {|f|
50
+ # f.each_line {|line| p line}
51
+ # }
52
+ #
53
+ # The opened file has several methods for meta information as follows since
54
+ # it is extended by OpenURI::Meta.
55
+ #
56
+ # open("http://www.ruby-lang.org/en") {|f|
57
+ # f.each_line {|line| p line}
58
+ # p f.base_uri # <URI::HTTP:0x40e6ef2 URL:http://www.ruby-lang.org/en/>
59
+ # p f.content_type # "text/html"
60
+ # p f.charset # "iso-8859-1"
61
+ # p f.content_encoding # []
62
+ # p f.last_modified # Thu Dec 05 02:45:02 UTC 2002
63
+ # }
64
+ #
65
+ # Additional header fields can be specified by an optional hash argument.
66
+ #
67
+ # open("http://www.ruby-lang.org/en/",
68
+ # "User-Agent" => "Ruby/#{RUBY_VERSION}",
69
+ # "From" => "foo@bar.invalid",
70
+ # "Referer" => "http://www.ruby-lang.org/") {|f|
71
+ # # ...
72
+ # }
73
+ #
74
+ # The environment variables such as http_proxy, https_proxy and ftp_proxy
75
+ # are in effect by default. :proxy => nil disables proxy.
76
+ #
77
+ # open("http://www.ruby-lang.org/en/raa.html", :proxy => nil) {|f|
78
+ # # ...
79
+ # }
80
+ #
81
+ # URI objects can be opened in a similar way.
82
+ #
83
+ # uri = URI.parse("http://www.ruby-lang.org/en/")
84
+ # uri.open {|f|
85
+ # # ...
86
+ # }
87
+ #
88
+ # URI objects can be read directly. The returned string is also extended by
89
+ # OpenURI::Meta.
90
+ #
91
+ # str = uri.read
92
+ # p str.base_uri
93
+ #
94
+ # Author:: Tanaka Akira <akr@m17n.org>
95
+
96
+ module OpenURI
97
+ Options = {
98
+ :proxy => true,
99
+ :method => true,
100
+ :progress_proc => true,
101
+ :content_length_proc => true,
102
+ :http_basic_authentication => true,
103
+ :body => true
104
+ }
105
+
106
+ # xxx: I'd like a better way of representing this trivial mapping.
107
+ Methods = {
108
+ :copy => Net::HTTP::Copy,
109
+ :delete => Net::HTTP::Delete,
110
+ :get => Net::HTTP::Get,
111
+ :head => Net::HTTP::Head,
112
+ :lock => Net::HTTP::Lock,
113
+ :mkcol => Net::HTTP::Mkcol,
114
+ :options => Net::HTTP::Options,
115
+ :post => Net::HTTP::Post,
116
+ :propfind => Net::HTTP::Propfind,
117
+ :proppatch => Net::HTTP::Proppatch,
118
+ :put => Net::HTTP::Put,
119
+ :trace => Net::HTTP::Trace,
120
+ :unlock => Net::HTTP::Unlock,
121
+ }
122
+
123
+
124
+ def OpenURI.check_options(options) # :nodoc:
125
+ options.each {|k, v|
126
+ next unless Symbol === k
127
+ unless Options.include? k
128
+ raise ArgumentError, "unrecognized option: #{k}"
129
+ end
130
+ }
131
+
132
+ m = options[:method]
133
+ if m && !Methods[m]
134
+ raise ArgumentError, "unrecognized HTTP method symbol: #{m}"
135
+ end
136
+ end
137
+
138
+ def OpenURI.scan_open_optional_arguments(*rest) # :nodoc:
139
+ if !rest.empty? && (String === rest.first || Integer === rest.first)
140
+ mode = rest.shift
141
+ if !rest.empty? && Integer === rest.first
142
+ perm = rest.shift
143
+ end
144
+ end
145
+ return mode, perm, rest
146
+ end
147
+
148
+ def OpenURI.open_uri(name, *rest) # :nodoc:
149
+ uri = URI::Generic === name ? name : URI.parse(name)
150
+ mode, perm, rest = OpenURI.scan_open_optional_arguments(*rest)
151
+ options = rest.shift if !rest.empty? && Hash === rest.first
152
+ raise ArgumentError.new("extra arguments") if !rest.empty?
153
+ options ||= {}
154
+ OpenURI.check_options(options)
155
+
156
+ unless mode == nil ||
157
+ mode == 'r' || mode == 'rb' ||
158
+ mode == File::RDONLY
159
+ raise ArgumentError.new("invalid access mode #{mode} (#{uri.class} resource is read only.)")
160
+ end
161
+
162
+ io = open_loop(uri, options)
163
+ if block_given?
164
+ begin
165
+ yield io
166
+ ensure
167
+ io.close
168
+ end
169
+ else
170
+ io
171
+ end
172
+ end
173
+
174
+ def OpenURI.open_loop(uri, options) # :nodoc:
175
+ case opt_proxy = options.fetch(:proxy, true)
176
+ when true
177
+ find_proxy = lambda {|u| u.find_proxy}
178
+ when nil, false
179
+ find_proxy = lambda {|u| nil}
180
+ when String
181
+ opt_proxy = URI.parse(opt_proxy)
182
+ find_proxy = lambda {|u| opt_proxy}
183
+ when URI::Generic
184
+ find_proxy = lambda {|u| opt_proxy}
185
+ else
186
+ raise ArgumentError.new("Invalid proxy option: #{opt_proxy}")
187
+ end
188
+
189
+ uri_set = {}
190
+ buf = nil
191
+ while true
192
+ redirect = catch(:open_uri_redirect) {
193
+ buf = Buffer.new
194
+ uri.buffer_open(buf, find_proxy.call(uri), options)
195
+ nil
196
+ }
197
+ if redirect
198
+ if redirect.relative?
199
+ # Although it violates RFC2616, Location: field may have relative
200
+ # URI. It is converted to absolute URI using uri as a base URI.
201
+ redirect = uri + redirect
202
+ end
203
+ unless OpenURI.redirectable?(uri, redirect)
204
+ raise "redirection forbidden: #{uri} -> #{redirect}"
205
+ end
206
+ if options.include? :http_basic_authentication
207
+ # send authentication only for the URI directly specified.
208
+ options = options.dup
209
+ options.delete :http_basic_authentication
210
+ end
211
+ uri = redirect
212
+ raise "HTTP redirection loop: #{uri}" if uri_set.include? uri.to_s
213
+ uri_set[uri.to_s] = true
214
+ else
215
+ break
216
+ end
217
+ end
218
+ io = buf.io
219
+ io.base_uri = uri
220
+ io
221
+ end
222
+
223
+ def OpenURI.redirectable?(uri1, uri2) # :nodoc:
224
+ # This test is intended to forbid a redirection from http://... to
225
+ # file:///etc/passwd.
226
+ # However this is ad hoc. It should be extensible/configurable.
227
+ uri1.scheme.downcase == uri2.scheme.downcase ||
228
+ (/\A(?:http|ftp)\z/i =~ uri1.scheme && /\A(?:http|ftp)\z/i =~ uri2.scheme)
229
+ end
230
+
231
+ def OpenURI.open_http(buf, target, proxy, options) # :nodoc:
232
+ if proxy
233
+ raise "Non-HTTP proxy URI: #{proxy}" if proxy.class != URI::HTTP
234
+ end
235
+
236
+ if target.userinfo && "1.9.0" <= RUBY_VERSION
237
+ # don't raise for 1.8 because compatibility.
238
+ raise ArgumentError, "userinfo not supported. [RFC3986]"
239
+ end
240
+
241
+ klass = Net::HTTP
242
+ if URI::HTTP === target
243
+ # HTTP or HTTPS
244
+ if proxy
245
+ klass = Net::HTTP::Proxy(proxy.host, proxy.port)
246
+ end
247
+ target_host = target.host
248
+ target_port = target.port
249
+ request_uri = target.request_uri
250
+ else
251
+ # FTP over HTTP proxy
252
+ target_host = proxy.host
253
+ target_port = proxy.port
254
+ request_uri = target.to_s
255
+ end
256
+
257
+ http = klass.new(target_host, target_port)
258
+ if target.class == URI::HTTPS
259
+ require 'net/https'
260
+ http.use_ssl = true
261
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
262
+ store = OpenSSL::X509::Store.new
263
+ store.set_default_paths
264
+ http.cert_store = store
265
+ end
266
+
267
+ header = {}
268
+ options.each {|k, v| header[k] = v if String === k }
269
+
270
+ resp = nil
271
+ http.start {
272
+ if target.class == URI::HTTPS
273
+ # xxx: information hiding violation
274
+ sock = http.instance_variable_get(:@socket)
275
+ if sock.respond_to?(:io)
276
+ sock = sock.io # 1.9
277
+ else
278
+ sock = sock.instance_variable_get(:@socket) # 1.8
279
+ end
280
+ sock.post_connection_check(target_host)
281
+ end
282
+
283
+ methodclass = Methods[options[:method] || :get]
284
+ req = methodclass.new(request_uri, header)
285
+ req.body = options[:body] if methodclass::REQUEST_HAS_BODY
286
+
287
+ if options.include? :http_basic_authentication
288
+ user, pass = options[:http_basic_authentication]
289
+ req.basic_auth user, pass
290
+ end
291
+ http.request(req) {|response|
292
+ resp = response
293
+ if options[:content_length_proc] && Net::HTTPSuccess === resp
294
+ if resp.key?('Content-Length')
295
+ options[:content_length_proc].call(resp['Content-Length'].to_i)
296
+ else
297
+ options[:content_length_proc].call(nil)
298
+ end
299
+ end
300
+ resp.read_body {|str|
301
+ buf << str
302
+ if options[:progress_proc] && Net::HTTPSuccess === resp
303
+ options[:progress_proc].call(buf.size)
304
+ end
305
+ }
306
+ }
307
+ }
308
+ io = buf.io
309
+ io.rewind
310
+ io.status = [resp.code, resp.message]
311
+ resp.each {|name,value| buf.io.meta_add_field name, value }
312
+ case resp
313
+ when Net::HTTPSuccess
314
+ when Net::HTTPMovedPermanently, # 301
315
+ Net::HTTPFound, # 302
316
+ Net::HTTPSeeOther, # 303
317
+ Net::HTTPTemporaryRedirect # 307
318
+ throw :open_uri_redirect, URI.parse(resp['location'])
319
+ else
320
+ raise OpenURI::HTTPError.new(io.status.join(' '), io)
321
+ end
322
+ end
323
+
324
+ class HTTPError < StandardError
325
+ def initialize(message, io)
326
+ super(message)
327
+ @io = io
328
+ end
329
+ attr_reader :io
330
+ end
331
+
332
+ class Buffer # :nodoc:
333
+ def initialize
334
+ @io = StringIO.new
335
+ @size = 0
336
+ end
337
+ attr_reader :size
338
+
339
+ StringMax = 10240
340
+ def <<(str)
341
+ @io << str
342
+ @size += str.length
343
+ if StringIO === @io && StringMax < @size
344
+ require 'tempfile'
345
+ io = Tempfile.new('open-uri')
346
+ io.binmode
347
+ Meta.init io, @io if @io.respond_to? :meta
348
+ io << @io.string
349
+ @io = io
350
+ end
351
+ end
352
+
353
+ def io
354
+ Meta.init @io unless @io.respond_to? :meta
355
+ @io
356
+ end
357
+ end
358
+
359
+ # Mixin for holding meta-information.
360
+ module Meta
361
+ def Meta.init(obj, src=nil) # :nodoc:
362
+ obj.extend Meta
363
+ obj.instance_eval {
364
+ @base_uri = nil
365
+ @meta = {}
366
+ }
367
+ if src
368
+ obj.status = src.status
369
+ obj.base_uri = src.base_uri
370
+ src.meta.each {|name, value|
371
+ obj.meta_add_field(name, value)
372
+ }
373
+ end
374
+ end
375
+
376
+ # returns an Array which consists status code and message.
377
+ attr_accessor :status
378
+
379
+ # returns a URI which is base of relative URIs in the data.
380
+ # It may differ from the URI supplied by a user because redirection.
381
+ attr_accessor :base_uri
382
+
383
+ # returns a Hash which represents header fields.
384
+ # The Hash keys are downcased for canonicalization.
385
+ attr_reader :meta
386
+
387
+ def meta_add_field(name, value) # :nodoc:
388
+ @meta[name.downcase] = value
389
+ end
390
+
391
+ # returns a Time which represents Last-Modified field.
392
+ def last_modified
393
+ if v = @meta['last-modified']
394
+ Time.httpdate(v)
395
+ else
396
+ nil
397
+ end
398
+ end
399
+
400
+ RE_LWS = /[\r\n\t ]+/n
401
+ RE_TOKEN = %r{[^\x00- ()<>@,;:\\"/\[\]?={}\x7f]+}n
402
+ RE_QUOTED_STRING = %r{"(?:[\r\n\t !#-\[\]-~\x80-\xff]|\\[\x00-\x7f])*"}n
403
+ RE_PARAMETERS = %r{(?:;#{RE_LWS}?#{RE_TOKEN}#{RE_LWS}?=#{RE_LWS}?(?:#{RE_TOKEN}|#{RE_QUOTED_STRING})#{RE_LWS}?)*}n
404
+
405
+ def content_type_parse # :nodoc:
406
+ v = @meta['content-type']
407
+ # The last (?:;#{RE_LWS}?)? matches extra ";" which violates RFC2045.
408
+ if v && %r{\A#{RE_LWS}?(#{RE_TOKEN})#{RE_LWS}?/(#{RE_TOKEN})#{RE_LWS}?(#{RE_PARAMETERS})(?:;#{RE_LWS}?)?\z}no =~ v
409
+ type = $1.downcase
410
+ subtype = $2.downcase
411
+ parameters = []
412
+ $3.scan(/;#{RE_LWS}?(#{RE_TOKEN})#{RE_LWS}?=#{RE_LWS}?(?:(#{RE_TOKEN})|(#{RE_QUOTED_STRING}))/no) {|att, val, qval|
413
+ val = qval.gsub(/[\r\n\t !#-\[\]-~\x80-\xff]+|(\\[\x00-\x7f])/) { $1 ? $1[1,1] : $& } if qval
414
+ parameters << [att.downcase, val]
415
+ }
416
+ ["#{type}/#{subtype}", *parameters]
417
+ else
418
+ nil
419
+ end
420
+ end
421
+
422
+ # returns "type/subtype" which is MIME Content-Type.
423
+ # It is downcased for canonicalization.
424
+ # Content-Type parameters are stripped.
425
+ def content_type
426
+ type, *parameters = content_type_parse
427
+ type || 'application/octet-stream'
428
+ end
429
+
430
+ # returns a charset parameter in Content-Type field.
431
+ # It is downcased for canonicalization.
432
+ #
433
+ # If charset parameter is not given but a block is given,
434
+ # the block is called and its result is returned.
435
+ # It can be used to guess charset.
436
+ #
437
+ # If charset parameter and block is not given,
438
+ # nil is returned except text type in HTTP.
439
+ # In that case, "iso-8859-1" is returned as defined by RFC2616 3.7.1.
440
+ def charset
441
+ type, *parameters = content_type_parse
442
+ if pair = parameters.assoc('charset')
443
+ pair.last.downcase
444
+ elsif block_given?
445
+ yield
446
+ elsif type && %r{\Atext/} =~ type &&
447
+ @base_uri && /\Ahttp\z/i =~ @base_uri.scheme
448
+ "iso-8859-1" # RFC2616 3.7.1
449
+ else
450
+ nil
451
+ end
452
+ end
453
+
454
+ # returns a list of encodings in Content-Encoding field
455
+ # as an Array of String.
456
+ # The encodings are downcased for canonicalization.
457
+ def content_encoding
458
+ v = @meta['content-encoding']
459
+ if v && %r{\A#{RE_LWS}?#{RE_TOKEN}#{RE_LWS}?(?:,#{RE_LWS}?#{RE_TOKEN}#{RE_LWS}?)*}o =~ v
460
+ v.scan(RE_TOKEN).map {|content_coding| content_coding.downcase}
461
+ else
462
+ []
463
+ end
464
+ end
465
+ end
466
+
467
+ # Mixin for HTTP and FTP URIs.
468
+ module OpenRead
469
+ # OpenURI::OpenRead#open provides `open' for URI::HTTP and URI::FTP.
470
+ #
471
+ # OpenURI::OpenRead#open takes optional 3 arguments as:
472
+ # OpenURI::OpenRead#open([mode [, perm]] [, options]) [{|io| ... }]
473
+ #
474
+ # `mode', `perm' is same as Kernel#open.
475
+ #
476
+ # However, `mode' must be read mode because OpenURI::OpenRead#open doesn't
477
+ # support write mode (yet).
478
+ # Also `perm' is just ignored because it is meaningful only for file
479
+ # creation.
480
+ #
481
+ # `options' must be a hash.
482
+ #
483
+ # Each pairs which key is a string in the hash specify a extra header
484
+ # field for HTTP.
485
+ # I.e. it is ignored for FTP without HTTP proxy.
486
+ #
487
+ # The hash may include other options which key is a symbol:
488
+ #
489
+ # [:proxy]
490
+ # Synopsis:
491
+ # :proxy => "http://proxy.foo.com:8000/"
492
+ # :proxy => URI.parse("http://proxy.foo.com:8000/")
493
+ # :proxy => true
494
+ # :proxy => false
495
+ # :proxy => nil
496
+ #
497
+ # If :proxy option is specified, the value should be String, URI,
498
+ # boolean or nil.
499
+ # When String or URI is given, it is treated as proxy URI.
500
+ # When true is given or the option itself is not specified,
501
+ # environment variable `scheme_proxy' is examined.
502
+ # `scheme' is replaced by `http', `https' or `ftp'.
503
+ # When false or nil is given, the environment variables are ignored and
504
+ # connection will be made to a server directly.
505
+ #
506
+ # [:http_basic_authentication]
507
+ # Synopsis:
508
+ # :http_basic_authentication=>[user, password]
509
+ #
510
+ # If :http_basic_authentication is specified,
511
+ # the value should be an array which contains 2 strings:
512
+ # username and password.
513
+ # It is used for HTTP Basic authentication defined by RFC 2617.
514
+ #
515
+ # [:content_length_proc]
516
+ # Synopsis:
517
+ # :content_length_proc => lambda {|content_length| ... }
518
+ #
519
+ # If :content_length_proc option is specified, the option value procedure
520
+ # is called before actual transfer is started.
521
+ # It takes one argument which is expected content length in bytes.
522
+ #
523
+ # If two or more transfer is done by HTTP redirection, the procedure
524
+ # is called only one for a last transfer.
525
+ #
526
+ # When expected content length is unknown, the procedure is called with
527
+ # nil.
528
+ # It is happen when HTTP response has no Content-Length header.
529
+ #
530
+ # [:progress_proc]
531
+ # Synopsis:
532
+ # :progress_proc => lambda {|size| ...}
533
+ #
534
+ # If :progress_proc option is specified, the proc is called with one
535
+ # argument each time when `open' gets content fragment from network.
536
+ # The argument `size' `size' is a accumulated transfered size in bytes.
537
+ #
538
+ # If two or more transfer is done by HTTP redirection, the procedure
539
+ # is called only one for a last transfer.
540
+ #
541
+ # :progress_proc and :content_length_proc are intended to be used for
542
+ # progress bar.
543
+ # For example, it can be implemented as follows using Ruby/ProgressBar.
544
+ #
545
+ # pbar = nil
546
+ # open("http://...",
547
+ # :content_length_proc => lambda {|t|
548
+ # if t && 0 < t
549
+ # pbar = ProgressBar.new("...", t)
550
+ # pbar.file_transfer_mode
551
+ # end
552
+ # },
553
+ # :progress_proc => lambda {|s|
554
+ # pbar.set s if pbar
555
+ # }) {|f| ... }
556
+ #
557
+ # OpenURI::OpenRead#open returns an IO like object if block is not given.
558
+ # Otherwise it yields the IO object and return the value of the block.
559
+ # The IO object is extended with OpenURI::Meta.
560
+ def open(*rest, &block)
561
+ OpenURI.open_uri(self, *rest, &block)
562
+ end
563
+
564
+ # OpenURI::OpenRead#read([options]) reads a content referenced by self and
565
+ # returns the content as string.
566
+ # The string is extended with OpenURI::Meta.
567
+ # The argument `options' is same as OpenURI::OpenRead#open.
568
+ def read(options={})
569
+ self.open(options) {|f|
570
+ str = f.read
571
+ Meta.init str, f
572
+ str
573
+ }
574
+ end
575
+ end
576
+ end
577
+
578
+ module URI
579
+ class Generic
580
+ # returns a proxy URI.
581
+ # The proxy URI is obtained from environment variables such as http_proxy,
582
+ # ftp_proxy, no_proxy, etc.
583
+ # If there is no proper proxy, nil is returned.
584
+ #
585
+ # Note that capitalized variables (HTTP_PROXY, FTP_PROXY, NO_PROXY, etc.)
586
+ # are examined too.
587
+ #
588
+ # But http_proxy and HTTP_PROXY is treated specially under CGI environment.
589
+ # It's because HTTP_PROXY may be set by Proxy: header.
590
+ # So HTTP_PROXY is not used.
591
+ # http_proxy is not used too if the variable is case insensitive.
592
+ # CGI_HTTP_PROXY can be used instead.
593
+ def find_proxy
594
+ name = self.scheme.downcase + '_proxy'
595
+ proxy_uri = nil
596
+ if name == 'http_proxy' && ENV.include?('REQUEST_METHOD') # CGI?
597
+ # HTTP_PROXY conflicts with *_proxy for proxy settings and
598
+ # HTTP_* for header information in CGI.
599
+ # So it should be careful to use it.
600
+ pairs = ENV.reject {|k, v| /\Ahttp_proxy\z/i !~ k }
601
+ case pairs.length
602
+ when 0 # no proxy setting anyway.
603
+ proxy_uri = nil
604
+ when 1
605
+ k, v = pairs.shift
606
+ if k == 'http_proxy' && ENV[k.upcase] == nil
607
+ # http_proxy is safe to use because ENV is case sensitive.
608
+ proxy_uri = ENV[name]
609
+ else
610
+ proxy_uri = nil
611
+ end
612
+ else # http_proxy is safe to use because ENV is case sensitive.
613
+ proxy_uri = ENV[name]
614
+ end
615
+ if !proxy_uri
616
+ # Use CGI_HTTP_PROXY. cf. libwww-perl.
617
+ proxy_uri = ENV["CGI_#{name.upcase}"]
618
+ end
619
+ elsif name == 'http_proxy'
620
+ unless proxy_uri = ENV[name]
621
+ if proxy_uri = ENV[name.upcase]
622
+ warn 'The environment variable HTTP_PROXY is discouraged. Use http_proxy.'
623
+ end
624
+ end
625
+ else
626
+ proxy_uri = ENV[name] || ENV[name.upcase]
627
+ end
628
+
629
+ if proxy_uri && self.host
630
+ require 'socket'
631
+ begin
632
+ addr = IPSocket.getaddress(self.host)
633
+ proxy_uri = nil if /\A127\.|\A::1\z/ =~ addr
634
+ rescue SocketError
635
+ end
636
+ end
637
+
638
+ if proxy_uri
639
+ proxy_uri = URI.parse(proxy_uri)
640
+ name = 'no_proxy'
641
+ if no_proxy = ENV[name] || ENV[name.upcase]
642
+ no_proxy.scan(/([^:,]*)(?::(\d+))?/) {|host, port|
643
+ if /(\A|\.)#{Regexp.quote host}\z/i =~ self.host &&
644
+ (!port || self.port == port.to_i)
645
+ proxy_uri = nil
646
+ break
647
+ end
648
+ }
649
+ end
650
+ proxy_uri
651
+ else
652
+ nil
653
+ end
654
+ end
655
+ end
656
+
657
+ class HTTP
658
+ def buffer_open(buf, proxy, options) # :nodoc:
659
+ OpenURI.open_http(buf, self, proxy, options)
660
+ end
661
+
662
+ include OpenURI::OpenRead
663
+ end
664
+
665
+ class FTP
666
+ def buffer_open(buf, proxy, options) # :nodoc:
667
+ if proxy
668
+ OpenURI.open_http(buf, self, proxy, options)
669
+ return
670
+ end
671
+ require 'net/ftp'
672
+
673
+ directories = self.path.split(%r{/}, -1)
674
+ directories.shift if directories[0] == '' # strip a field before leading slash
675
+ directories.each {|d|
676
+ d.gsub!(/%([0-9A-Fa-f][0-9A-Fa-f])/) { [$1].pack("H2") }
677
+ }
678
+ unless filename = directories.pop
679
+ raise ArgumentError, "no filename: #{self.inspect}"
680
+ end
681
+ directories.each {|d|
682
+ if /[\r\n]/ =~ d
683
+ raise ArgumentError, "invalid directory: #{d.inspect}"
684
+ end
685
+ }
686
+ if /[\r\n]/ =~ filename
687
+ raise ArgumentError, "invalid filename: #{filename.inspect}"
688
+ end
689
+ typecode = self.typecode
690
+ if typecode && /\A[aid]\z/ !~ typecode
691
+ raise ArgumentError, "invalid typecode: #{typecode.inspect}"
692
+ end
693
+
694
+ # The access sequence is defined by RFC 1738
695
+ ftp = Net::FTP.open(self.host)
696
+ # todo: extract user/passwd from .netrc.
697
+ user = 'anonymous'
698
+ passwd = nil
699
+ user, passwd = self.userinfo.split(/:/) if self.userinfo
700
+ ftp.login(user, passwd)
701
+ directories.each {|cwd|
702
+ ftp.voidcmd("CWD #{cwd}")
703
+ }
704
+ if typecode
705
+ # xxx: typecode D is not handled.
706
+ ftp.voidcmd("TYPE #{typecode.upcase}")
707
+ end
708
+ if options[:content_length_proc]
709
+ options[:content_length_proc].call(ftp.size(filename))
710
+ end
711
+ ftp.retrbinary("RETR #{filename}", 4096) { |str|
712
+ buf << str
713
+ options[:progress_proc].call(buf.size) if options[:progress_proc]
714
+ }
715
+ ftp.close
716
+ buf.io.rewind
717
+ end
718
+
719
+ include OpenURI::OpenRead
720
+ end
721
+ end
metadata ADDED
@@ -0,0 +1,46 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.8.11
3
+ specification_version: 1
4
+ name: rest-open-uri
5
+ version: !ruby/object:Gem::Version
6
+ version: 1.0.0
7
+ date: 2006-12-21 00:00:00 -05:00
8
+ summary: A drop-in replacement for open-uri for use in REST clients.
9
+ require_paths:
10
+ - lib
11
+ email: leonardr@segfault.org
12
+ homepage: http://rubyforge.org/tracker/?func=detail&aid=6321&group_id=426&atid=1700
13
+ rubyforge_project:
14
+ description: rest-open-uri is a hack of, and drop-in replacement for, open-uri. It supports all standard HTTP methods (not just GET) and allows you to send an entity-body. This gem will be deprecated if and when equivalent functionality makes it into core Ruby.
15
+ autorequire:
16
+ default_executable:
17
+ bindir: bin
18
+ has_rdoc: true
19
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
20
+ requirements:
21
+ - - ">"
22
+ - !ruby/object:Gem::Version
23
+ version: 0.0.0
24
+ version:
25
+ platform: ruby
26
+ signing_key:
27
+ cert_chain:
28
+ authors:
29
+ - Leonard Richardson
30
+ files:
31
+ - lib/rest-open-uri.rb
32
+ - CHANGELOG
33
+ test_files: []
34
+
35
+ rdoc_options: []
36
+
37
+ extra_rdoc_files:
38
+ - CHANGELOG
39
+ executables: []
40
+
41
+ extensions: []
42
+
43
+ requirements: []
44
+
45
+ dependencies: []
46
+