curb-1.0.5/0000755000004100000410000000000014362470347012512 5ustar www-datawww-datacurb-1.0.5/tests/0000755000004100000410000000000014362470347013654 5ustar www-datawww-datacurb-1.0.5/tests/bug_postfields_crash2.rb0000644000004100000410000000360514362470347020460 0ustar www-datawww-data# Not sure if this is an IRB bug, but thought you guys should know. # # ** My Ruby version: ruby 1.8.7 (2009-06-12 patchlevel 174) [x86_64-linux] # ** Version of Rubygems: 1.3.5 # ** Version of the Curb gem: 0.6.6.0 # # # Transcript of IRB session: # ------------------------------------------------------------------------------------------------------------------ # irb(main):001:0> a = { # irb(main):002:1* :type => :pie, # irb(main):003:1* :series => { # irb(main):004:2* :names => [:a,:b], # irb(main):005:2* :values => [70,30], # irb(main):006:2* :colors => [:red,:green] # irb(main):007:2> }, # irb(main):008:1* :output_format => :png # irb(main):009:1> } # => {:type=>:pie, :output_format=>:png, :series=>{:names=>[:a, :b], :values=>[70, 30], :colors=>[:red, :green]}} # irb(main):010:0> post = [] # => [] # irb(main):011:0> require 'rubygems' # => true # irb(main):012:0> require 'curb' # => true # irb(main):013:0> include Curl # => Object # irb(main):014:0> a.each_pair do |k,v| # irb(main):015:1* post << PostField.content(k,v) # irb(main):016:1> end # => {:type=>:pie, :output_format=>:png, :series=>{:names=>[:a, :b], :values=>[70, 30], :colors=>[:red, :green]}} # irb(main):017:0> post # /usr/lib/ruby/1.8/irb.rb:302: [BUG] Segmentation fault # ruby 1.8.7 (2009-06-12 patchlevel 174) [x86_64-linux] # # Aborted # ------------------------------------------------------------------------------------------------------------------ require File.expand_path(File.join(File.dirname(__FILE__), 'helper')) class BugPostFieldsCrash2 < Test::Unit::TestCase def test_crash a = { :type => :pie, :series => { :names => [:a,:b], :values => [70,30], :colors => [:red,:green] }, :output_format => :png } post = [] a.each_pair do |k,v| post << Curl::PostField.content(k,v) end post.inspect end end curb-1.0.5/tests/require_last_or_segfault_script.rb0000644000004100000410000000243314362470347022660 0ustar www-datawww-data# From Vlad Jebelev: # # - if I have a require statement after "require 'curb'" and there is a # POST with at least 1 field, the script will fail with a segmentation # fault, e.g. the following sequence fails every time for me (Ruby 1.8.5): # ----------------------------------------------------------------- # require 'curb' # require 'uri' # # url = 'https://www.google.com/accounts/ServiceLoginAuth' # # c = Curl::Easy.http_post( # 'https://www.google.com/accounts/ServiceLoginAuth', # [Curl:: PostField.content('ltmpl','m_blanco')] ) do |curl| # end # ------------------------------------------------------------------ # :..dev/util$ ruby seg.rb # seg.rb:6: [BUG] Segmentation fault # ruby 1.8.5 (2006-08-25) [i686-linux] # # Aborted # ------------------------------------------------------------------ # $:.unshift(File.expand_path(File.join(File.dirname(__FILE__), '..', 'ext'))) $:.unshift(File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))) require 'curb' require 'uri' url = 'https://www.google.com/accounts/ServiceLoginAuth' c = Curl::Easy.http_post('https://www.google.com/accounts/ServiceLoginAuth', Curl:: PostField.content('ltmpl','m_blanco')) #do # end puts "success" curb-1.0.5/tests/mem_check.rb0000644000004100000410000000331014362470347016111 0ustar www-datawww-datarequire File.expand_path(File.join(File.dirname(__FILE__), 'helper')) #require 'rubygems' #require 'rmem' # # Run some tests to measure the memory usage of curb, these tests require fork and ps # class TestCurbMemory < Test::Unit::TestCase def test_easy_memory easy_avg, easy_std = measure_object_memory(Curl::Easy) printf "Easy average: %.2f kilobytes +/- %.2f kilobytes\n", easy_avg.to_f, easy_std.to_f multi_avg, multi_std = measure_object_memory(Curl::Multi) printf "Multi average: %.2f kilobytes +/- %.2f kilobytes\n", multi_avg.to_f, multi_std.to_f # now that we have the average size of an easy handle lets see how much a multi request consumes with 10 requests end def c_avg(report) sum = 0 report.each {|r| sum += r.last } (sum.to_f / report.size) end def c_std(report,avg) var = 0 report.each {|r| var += (r.last-avg)*(r.last-avg) } Math.sqrt(var / (report.size-1)) end def measure_object_memory(klass) report = [] 200.times do res = mem_check do obj = klass.new end report << res end avg = c_avg(report) std = c_std(report,avg) [avg,std] end def mem_check # see: http://gist.github.com/264060 for inspiration of ps command line rd, wr = IO.pipe memory_usage = `ps -o rss= -p #{Process.pid}`.to_i # in kilobytes fork do before = `ps -o rss= -p #{Process.pid}`.to_i # in kilobytes rd.close yield after = `ps -o rss= -p #{Process.pid}`.to_i # in kilobytes wr.write((after - before)) wr.flush wr.close end wr.close total = rd.read.to_i rd.close Process.wait # return the delta and the total [memory_usage, total] end end curb-1.0.5/tests/tc_curl_postfield.rb0000644000004100000410000000742114362470347017711 0ustar www-datawww-datarequire File.expand_path(File.join(File.dirname(__FILE__), 'helper')) class TestCurbCurlPostfield < Test::Unit::TestCase def test_private_new assert_raise(NoMethodError) { Curl::PostField.new } end def test_new_content_01 pf = Curl::PostField.content('foo', 'bar') assert_equal 'foo', pf.name assert_equal 'bar', pf.content assert_nil pf.content_type assert_nil pf.local_file assert_nil pf.remote_file assert_nil pf.set_content_proc end def test_new_content_02 pf = Curl::PostField.content('foo', 'bar', 'text/html') assert_equal 'foo', pf.name assert_equal 'bar', pf.content assert_equal 'text/html', pf.content_type assert_nil pf.local_file assert_nil pf.remote_file assert_nil pf.set_content_proc end def test_new_content_03 l = lambda { |field| "never gets run" } pf = Curl::PostField.content('foo', &l) assert_equal 'foo', pf.name assert_nil pf.content assert_nil pf.content_type assert_nil pf.local_file assert_nil pf.remote_file # N.B. This doesn't just get the proc, but also removes it. assert_equal l, pf.set_content_proc end def test_new_content_04 l = lambda { |field| "never gets run" } pf = Curl::PostField.content('foo', 'text/html', &l) assert_equal 'foo', pf.name assert_nil pf.content assert_equal 'text/html', pf.content_type assert_nil pf.local_file assert_nil pf.remote_file # N.B. This doesn't just get the proc, but also removes it. assert_equal l, pf.set_content_proc end def test_new_file_01 pf = Curl::PostField.file('foo', 'localname') assert_equal 'foo', pf.name assert_equal 'localname', pf.local_file assert_equal 'localname', pf.remote_file assert_nothing_raised { pf.to_s } assert_nil pf.content_type assert_nil pf.content assert_nil pf.set_content_proc end def test_new_file_02 pf = Curl::PostField.file('foo', 'localname', 'remotename') assert_equal 'foo', pf.name assert_equal 'localname', pf.local_file assert_equal 'remotename', pf.remote_file assert_nil pf.content_type assert_nil pf.content assert_nil pf.set_content_proc end def test_new_file_03 l = lambda { |field| "never gets run" } pf = Curl::PostField.file('foo', 'remotename', &l) assert_equal 'foo', pf.name assert_equal 'remotename', pf.remote_file assert_nil pf.local_file assert_nil pf.content_type assert_nil pf.content # N.B. This doesn't just get the proc, but also removes it. assert_equal l, pf.set_content_proc end def test_new_file_04 assert_raise(ArgumentError) do # no local name, no block Curl::PostField.file('foo') end assert_raise(ArgumentError) do # no remote name with block Curl::PostField.file('foo') { |field| "never runs" } end end def test_new_file_05 # local gets ignored when supplying a block, but remote # is still set up properly. l = lambda { |field| "never runs" } pf = Curl::PostField.file('foo', 'local', 'remote', &l) assert_equal 'foo', pf.name assert_equal 'remote', pf.remote_file assert_nil pf.local_file assert_nil pf.content_type assert_nil pf.content assert_equal l, pf.set_content_proc end def test_to_s_01 pf = Curl::PostField.content('foo', 'bar') assert_equal "foo=bar", pf.to_s end def test_to_s_02 pf = Curl::PostField.content('foo', 'bar ton') assert_equal "foo=bar%20ton", pf.to_s end def test_to_s_03 pf = Curl::PostField.content('foo') { |field| field.name.upcase + "BAR" } assert_equal "foo=FOOBAR", pf.to_s end def test_to_s_04 pf = Curl::PostField.file('foo.file', 'bar.file') assert_nothing_raised { pf.to_s } #assert_raise(Curl::Err::InvalidPostFieldError) { pf.to_s } end end curb-1.0.5/tests/helper.rb0000644000004100000410000002003114362470347015454 0ustar www-datawww-data# DO NOT REMOVE THIS COMMENT - PART OF TESTMODEL. # Copyright (c)2006 Ross Bamford. See LICENSE. $CURB_TESTING = true require 'uri' require 'stringio' $TOPDIR = File.expand_path(File.join(File.dirname(__FILE__), '..')) $EXTDIR = File.join($TOPDIR, 'ext') $LIBDIR = File.join($TOPDIR, 'lib') $:.unshift($LIBDIR) $:.unshift($EXTDIR) require 'curb' begin require 'test/unit' rescue LoadError gem 'test/unit' require 'test/unit' end require 'fileutils' $TEST_URL = "file://#{'/' if RUBY_DESCRIPTION =~ /mswin|msys|mingw|cygwin|bccwin|wince|emc/}#{File.expand_path(__FILE__).tr('\\','/')}" require 'thread' require 'webrick' # set this to true to avoid testing with multiple threads # or to test with multiple threads set it to false # this is important since, some code paths will change depending # on the presence of multiple threads TEST_SINGLE_THREADED=false # keep webrick quiet ::WEBrick::HTTPServer.send(:remove_method,:access_log) if ::WEBrick::HTTPServer.instance_methods.include?(:access_log) ::WEBrick::BasicLog.send(:remove_method,:log) if ::WEBrick::BasicLog.instance_methods.include?(:log) ::WEBrick::HTTPServer.class_eval do def access_log(config, req, res) # nop end end ::WEBrick::BasicLog.class_eval do def log(level, data) # nop end end # # Simple test server to record number of times a request is sent/recieved of a specific # request type, e.g. GET,POST,PUT,DELETE # class TestServlet < WEBrick::HTTPServlet::AbstractServlet def self.port=(p) @port = p end def self.port @port ||= 9129 end def self.path '/methods' end def self.url "http://127.0.0.1:#{port}#{path}" end def respond_with(method,req,res) res.body = method.to_s $auth_header = req['Authorization'] res['Content-Type'] = "text/plain" end def do_GET(req,res) if req.path.match(/redirect$/) res.status = 302 res['Location'] = '/foo' elsif req.path.match(/not_here$/) res.status = 404 elsif req.path.match(/error$/) res.status = 500 end respond_with("GET#{req.query_string}",req,res) end def do_HEAD(req,res) res['Location'] = "/nonexistent" respond_with("HEAD#{req.query_string}",req,res) end def do_POST(req,res) if req.query['filename'].nil? if req.body params = {} req.body.split('&').map{|s| k,v=s.split('='); params[k] = v } end if params and params['s'] == '500' res.status = 500 elsif params and params['c'] cookie = URI.decode_www_form_component(params['c']).split('=') res.cookies << WEBrick::Cookie.new(*cookie) else respond_with("POST\n#{req.body}",req,res) end else respond_with(req.query['filename'],req,res) end end def do_PUT(req,res) res['X-Requested-Content-Type'] = req.content_type respond_with("PUT\n#{req.body}",req,res) end def do_DELETE(req,res) respond_with("DELETE#{req.query_string}",req,res) end def do_PURGE(req,res) respond_with("PURGE#{req.query_string}",req,res) end def do_COPY(req,res) respond_with("COPY#{req.query_string}",req,res) end def do_PATCH(req,res) respond_with("PATCH\n#{req.body}",req,res) end def do_OPTIONS(req,res) respond_with("OPTIONS#{req.query_string}",req,res) end end module BugTestServerSetupTeardown def setup @port ||= 9992 @server = WEBrick::HTTPServer.new( :Port => @port ) @server.mount_proc("/test") do|req,res| if @response_proc @response_proc.call(res) else res.body = "hi" res['Content-Type'] = "text/html" end end @thread = Thread.new(@server) do|srv| srv.start end end def teardown while @server.status != :Shutdown @server.shutdown end @thread.join end end module TestServerMethods def locked_file File.join(File.dirname(__FILE__),"server_lock-#{@__port}") end def server_setup(port=9129,servlet=TestServlet) @__port = port if (@server ||= nil).nil? and !File.exist?(locked_file) File.open(locked_file,'w') {|f| f << 'locked' } if TEST_SINGLE_THREADED rd, wr = IO.pipe @__pid = fork do rd.close rd = nil # start up a webrick server for testing delete server = WEBrick::HTTPServer.new :Port => port, :DocumentRoot => File.expand_path(File.dirname(__FILE__)) server.mount(servlet.path, servlet) server.mount("/ext", WEBrick::HTTPServlet::FileHandler, File.join(File.dirname(__FILE__),'..','ext')) trap("INT") { server.shutdown } GC.start wr.flush wr.close server.start end wr.close rd.read rd.close else # start up a webrick server for testing delete @server = WEBrick::HTTPServer.new :Port => port, :DocumentRoot => File.expand_path(File.dirname(__FILE__)) @server.mount(servlet.path, servlet) @server.mount("/ext", WEBrick::HTTPServlet::FileHandler, File.join(File.dirname(__FILE__),'..','ext')) queue = Queue.new # synchronize the thread startup to the main thread @test_thread = Thread.new { queue << 1; @server.start } # wait for the queue value = queue.pop if !value STDERR.puts "Failed to startup test server!" exit(1) end end exit_code = lambda do begin if File.exist?(locked_file) File.unlink locked_file if TEST_SINGLE_THREADED Process.kill 'INT', @__pid else @server.shutdown unless @server.nil? end end #@server.shutdown unless @server.nil? rescue Object => e puts "Error #{__FILE__}:#{__LINE__}\n#{e.message}" end end trap("INT"){exit_code.call} at_exit{exit_code.call} end rescue Errno::EADDRINUSE end end # Backport for Ruby 1.8 module Backports module Ruby18 module URIFormEncoding TBLENCWWWCOMP_ = {} TBLDECWWWCOMP_ = {} def encode_www_form_component(str) if TBLENCWWWCOMP_.empty? 256.times do |i| TBLENCWWWCOMP_[i.chr] = '%%%02X' % i end TBLENCWWWCOMP_[' '] = '+' TBLENCWWWCOMP_.freeze end str.to_s.gsub( /([^*\-.0-9A-Z_a-z])/ ) {|*| TBLENCWWWCOMP_[$1] } end def decode_www_form_component(str) if TBLDECWWWCOMP_.empty? 256.times do |i| h, l = i>>4, i&15 TBLDECWWWCOMP_['%%%X%X' % [h, l]] = i.chr TBLDECWWWCOMP_['%%%x%X' % [h, l]] = i.chr TBLDECWWWCOMP_['%%%X%x' % [h, l]] = i.chr TBLDECWWWCOMP_['%%%x%x' % [h, l]] = i.chr end TBLDECWWWCOMP_['+'] = ' ' TBLDECWWWCOMP_.freeze end raise ArgumentError, "invalid %-encoding (#{str.dump})" unless /\A(?:%[[:xdigit:]]{2}|[^%]+)*\z/ =~ str str.gsub( /(\+|%[[:xdigit:]]{2})/ ) {|*| TBLDECWWWCOMP_[$1] } end def encode_www_form( enum ) enum.map do |k,v| if v.nil? encode_www_form_component(k) elsif v.respond_to?(:to_ary) v.to_ary.map do |w| str = encode_www_form_component(k) unless w.nil? str << '=' str << encode_www_form_component(w) end end.join('&') else str = encode_www_form_component(k) str << '=' str << encode_www_form_component(v) end end.join('&') end WFKV_ = '(?:%\h\h|[^%#=;&])' def decode_www_form(str, _) return [] if str.to_s == '' unless /\A#{WFKV_}=#{WFKV_}(?:[;&]#{WFKV_}=#{WFKV_})*\z/ =~ str raise ArgumentError, "invalid data of application/x-www-form-urlencoded (#{str})" end ary = [] $&.scan(/([^=;&]+)=([^;&]*)/) do ary << [decode_www_form_component($1, enc), decode_www_form_component($2, enc)] end ary end end end end unless URI.methods.include?(:encode_www_form) URI.extend(Backports::Ruby18::URIFormEncoding) end curb-1.0.5/tests/bug_issue102.rb0000644000004100000410000000056314362470347016415 0ustar www-datawww-datarequire File.expand_path(File.join(File.dirname(__FILE__), 'helper')) class BugIssue102 < Test::Unit::TestCase def test_interface test = "https://api.twitter.com/1/users/show.json?screen_name=TwitterAPI&include_entities=true" ip = "0.0.0.0" c = Curl::Easy.new do |curl| curl.url = test curl.interface = ip end c.perform end end curb-1.0.5/tests/timeout_server.rb0000644000004100000410000000145214362470347017257 0ustar www-datawww-data# This Sinatra application must be run with mongrel # or possibly with unicorn for the serve action to work properly. # See http://efreedom.com/Question/1-3669674/Streaming-Data-Sinatra-Rack-Application require 'sinatra' get '/wait/:time' do |time| time = time.to_i sleep(time) "Slept #{time} at #{Time.now}" end # http://efreedom.com/Question/1-3027435/Way-Flush-Html-Wire-Sinatra class Streamer def initialize(time, chunks) @time = time @chunks = chunks end def each @chunks.each do |chunk| sleep(@time) yield chunk end end end get '/serve/:chunk_size/every/:time/for/:count' do |chunk_size, time, count| chunk_size, time, count = chunk_size.to_i, time.to_i, count.to_i chunk = 'x' * chunk_size chunks = [chunk] * count Streamer.new(time, chunks) end curb-1.0.5/tests/bug_raise_on_callback.rb0000644000004100000410000000117114362470347020451 0ustar www-datawww-datarequire File.expand_path(File.join(File.dirname(__FILE__), 'helper')) class BugRaiseOnCallback < Test::Unit::TestCase include BugTestServerSetupTeardown def setup @port = 9999 super end def test_on_complte c = Curl::Easy.new('http://127.0.0.1:9999/test') did_raise = false begin c.on_complete do|x| assert_equal 'http://127.0.0.1:9999/test', x.url raise "error complete" # this will get swallowed end c.perform rescue => e did_raise = true end assert did_raise, "we want to raise an exception if the ruby callbacks raise" end end #test_on_debug curb-1.0.5/tests/bugtests.rb0000644000004100000410000000031014362470347016033 0ustar www-datawww-data$: << $TESTDIR = File.expand_path(File.dirname(__FILE__)) puts "start" begin Dir[File.join($TESTDIR, 'bug_*.rb')].each { |lib| require lib } rescue Object => e puts e.message ensure puts "done" end curb-1.0.5/tests/signals.rb0000644000004100000410000000141014362470347015635 0ustar www-datawww-datarequire File.expand_path(File.join(File.dirname(__FILE__), 'helper')) # This test suite requires the timeout server to be running # See tests/timeout.rb for more info about the timeout server class TestCurbSignals < Test::Unit::TestCase # Testcase for https://github.com/taf2/curb/issues/117 def test_continue_after_signal trap("SIGUSR1") { } curl = Curl::Easy.new(wait_url(2)) pid = $$ Thread.new do sleep 1 Process.kill("SIGUSR1", pid) end assert_equal true, curl.http_get end private def wait_url(time) "#{server_base}/wait/#{time}" end def serve_url(chunk_size, time, count) "#{server_base}/serve/#{chunk_size}/every/#{time}/for/#{count}" end def server_base 'http://127.0.0.1:9128' end end curb-1.0.5/tests/tc_curl_maxfilesize.rb0000644000004100000410000000037614362470347020242 0ustar www-datawww-datarequire File.expand_path(File.join(File.dirname(__FILE__), 'helper')) class TestCurbCurlMaxFileSize < Test::Unit::TestCase def setup @easy = Curl::Easy.new end def test_maxfilesize @easy.set(Curl::CURLOPT_MAXFILESIZE, 5000000) end end curb-1.0.5/tests/alltests.rb0000644000004100000410000000021614362470347016033 0ustar www-datawww-data$: << $TESTDIR = File.expand_path(File.dirname(__FILE__)) require 'unittests' Dir[File.join($TESTDIR, 'bug_*.rb')].each { |lib| require lib } curb-1.0.5/tests/tc_curl_protocols.rb0000644000004100000410000000171414362470347017743 0ustar www-datawww-dataclass TestCurbCurlProtocols < Test::Unit::TestCase include TestServerMethods def setup @easy = Curl::Easy.new @easy.set :protocols, Curl::CURLPROTO_HTTP | Curl::CURLPROTO_HTTPS @easy.follow_location = true server_setup end def test_protocol_allowed @easy.set :url, "http://127.0.0.1:9129/this_file_does_not_exist.html" @easy.perform assert_equal 404, @easy.response_code end def test_protocol_denied @easy.set :url, "gopher://google.com/" assert_raises Curl::Err::UnsupportedProtocolError do @easy.perform end end def test_redir_protocol_allowed @easy.set :url, TestServlet.url + "/redirect" @easy.set :redir_protocols, Curl::CURLPROTO_HTTP @easy.perform end def test_redir_protocol_denied @easy.set :url, TestServlet.url + "/redirect" @easy.set :redir_protocols, Curl::CURLPROTO_HTTPS assert_raises Curl::Err::UnsupportedProtocolError do @easy.perform end end end curb-1.0.5/tests/tc_curl_easy_setopt.rb0000644000004100000410000000105214362470347020251 0ustar www-datawww-datarequire File.expand_path(File.join(File.dirname(__FILE__), 'helper')) class TestCurbCurlEasySetOpt < Test::Unit::TestCase def setup @easy = Curl::Easy.new end def test_opt_verbose @easy.set :verbose, true assert @easy.verbose? end def test_opt_header @easy.set :header, true end def test_opt_noprogress @easy.set :noprogress, true end def test_opt_nosignal @easy.set :nosignal, true end def test_opt_url url = "http://google.com/" @easy.set :url, url assert_equal url, @easy.url end end curb-1.0.5/tests/bug_require_last_or_segfault.rb0000644000004100000410000000236714362470347022137 0ustar www-datawww-data# From Vlad Jebelev: # # - if I have a require statement after "require 'curb'" and there is a # POST with at least 1 field, the script will fail with a segmentation # fault, e.g. the following sequence fails every time for me (Ruby 1.8.5): # ----------------------------------------------------------------- # require 'curb' # require 'uri' # # url = 'https://www.google.com/accounts/ServiceLoginAuth' # # c = Curl::Easy.http_post( # 'https://www.google.com/accounts/ServiceLoginAuth', # [Curl:: PostField.content('ltmpl','m_blanco')] ) do |curl| # end # ------------------------------------------------------------------ # :..dev/util$ ruby seg.rb # seg.rb:6: [BUG] Segmentation fault # ruby 1.8.5 (2006-08-25) [i686-linux] # # Aborted # ------------------------------------------------------------------ # require 'test/unit' require 'rbconfig' $rubycmd = RbConfig::CONFIG['RUBY_INSTALL_NAME'] || 'ruby' class BugTestRequireLastOrSegfault < Test::Unit::TestCase def test_bug 5.times do |i| puts "Test ##{i}" # will be empty string if it segfaults... assert_equal 'success', `#$rubycmd #{File.dirname(__FILE__)}/require_last_or_segfault_script.rb`.chomp sleep 5 end end end curb-1.0.5/tests/unittests.rb0000644000004100000410000000017114362470347016242 0ustar www-datawww-data$: << $TESTDIR = File.expand_path(File.dirname(__FILE__)) Dir[File.join($TESTDIR, 'tc_*.rb')].each { |lib| require lib } curb-1.0.5/tests/bug_curb_easy_post_with_string_no_content_length_header.rb0000644000004100000410000000377214362470347027610 0ustar www-datawww-data=begin From jwhitmire Todd, I'm trying to use curb to post data to a REST url. We're using it to post support questions from our iphone app directly to tender. The post looks good to me, but curl is not adding the content-length header so I get a 411 length required response from the server. Here's my post block, do you see anything obvious? Do I need to manually add the Content-Length header? c = Curl::Easy.http_post(url) do |curl| curl.headers["User-Agent"] = "Curl/Ruby" if user curl.headers["X-Multipass"] = user.multipass else curl.headers["X-Tender-Auth"] = TOKEN end curl.headers["Accept"] = "application/vnd.tender-v1+json" curl.post_body = params.map{|f,k| "#{curl.escape(f)}=#{curl.escape(k)}"}.join('&') curl.verbose = true curl.follow_location = true curl.enable_cookies = true end Any insight you care to share would be helpful. Thanks. =end require File.expand_path(File.join(File.dirname(__FILE__), 'helper')) class BugCurbEasyPostWithStringNoContentLengthHeader < Test::Unit::TestCase include BugTestServerSetupTeardown def test_bug_workaround params = {:cat => "hat", :foo => "bar"} post_body = params.map{|f,k| "#{Curl::Easy.new.escape(f)}=#{Curl::Easy.new.escape(k)}"}.join('&') c = Curl::Easy.http_post("http://127.0.0.1:#{@port}/test",post_body) do |curl| curl.headers["User-Agent"] = "Curl/Ruby" curl.headers["X-Tender-Auth"] = "A Token" curl.headers["Accept"] = "application/vnd.tender-v1+json" curl.follow_location = true curl.enable_cookies = true end end def test_bug params = {:cat => "hat", :foo => "bar"} c = Curl::Easy.http_post("http://127.0.0.1:#{@port}/test") do |curl| curl.headers["User-Agent"] = "Curl/Ruby" curl.headers["X-Tender-Auth"] = "A Token" curl.headers["Accept"] = "application/vnd.tender-v1+json" curl.post_body = params.map{|f,k| "#{curl.escape(f)}=#{curl.escape(k)}"}.join('&') curl.follow_location = true curl.enable_cookies = true end end end curb-1.0.5/tests/tc_curl_multi.rb0000644000004100000410000004331614362470347017055 0ustar www-datawww-datarequire File.expand_path(File.join(File.dirname(__FILE__), 'helper')) require 'set' class TestCurbCurlMulti < Test::Unit::TestCase def teardown # get a better read on memory loss when running in valgrind ObjectSpace.garbage_collect end # for https://github.com/taf2/curb/issues/277 # must connect to an external def test_connection_keepalive # this test fails with libcurl 7.22.0. I didn't investigate, but it may be related # to CURLOPT_MAXCONNECTS bug fixed in 7.30.0: # https://github.com/curl/curl/commit/e87e76e2dc108efb1cae87df496416f49c55fca0 omit("Skip, libcurl too old (< 7.22.0)") if Curl::CURL_VERSION.split('.')[1].to_i <= 22 @server.shutdown if @server @test_thread.kill if @test_thread @server = nil File.unlink(locked_file) Curl::Multi.autoclose = true assert Curl::Multi.autoclose # XXX: thought maybe we can clean house here to have the full suite pass in osx... but for now running this test in isolate does pass # additionally, if ss allows this to pass on linux without requesting google i think this is a good trade off... leaving some of the thoughts below # in hopes that coming back to this later will find it and remember how to fix it # types = Set.new # close_types = Set.new([TCPServer,TCPSocket,Socket,Curl::Multi, Curl::Easy,WEBrick::Log]) # ObjectSpace.each_object {|o| # if o.respond_to?(:close) # types << o.class # end # if close_types.include?(o.class) # o.close # end # } #puts "unique types: #{types.to_a.join("\n")}" GC.start # cleanup FDs left over from other tests server_setup GC.start # cleanup FDs left over from other tests if `which ss`.strip.size == 0 # osx need lsof still :( open_fds = lambda do out = `/usr/sbin/lsof -p #{Process.pid} | egrep "TCP|UDP"`# | egrep ':#{TestServlet.port} ' | egrep ESTABLISHED`# | wc -l`.strip.to_i #puts out.lines.join("\n") out.lines.size end else ss = `which ss`.strip open_fds = lambda do `#{ss} -n4 state established dport = :#{TestServlet.port} | wc -l`.strip.to_i end end Curl::Multi.autoclose = false before_open = open_fds.call #puts "before_open: #{before_open.inspect}" assert !Curl::Multi.autoclose multi = Curl::Multi.new multi.max_connects = 1 # limit to 1 connection within the multi handle did_complete = false 5.times do |n| easy = Curl::Easy.new(TestServlet.url) do |curl| curl.timeout = 5 # ensure we don't hang for ever connecting to an external host curl.on_complete { did_complete = true } end multi.add(easy) end multi.perform assert did_complete after_open = open_fds.call #puts "after_open: #{after_open} before_open: #{before_open.inspect}" # ruby process may keep a connection alive assert (after_open - before_open) < 3, "with max connections set to 1 at this point the connection to google should still be open" assert (after_open - before_open) > 0, "with max connections set to 1 at this point the connection to google should still be open" multi.close after_open = open_fds.call #puts "after_open: #{after_open} before_open: #{before_open.inspect}" assert_equal 0, (after_open - before_open), "after closing the multi handle all connections should be closed" Curl::Multi.autoclose = true multi = Curl::Multi.new did_complete = false 5.times do |n| easy = Curl::Easy.new(TestServlet.url) do |curl| curl.timeout = 5 # ensure we don't hang for ever connecting to an external host curl.on_complete { did_complete = true } end multi.add(easy) end multi.perform assert did_complete after_open = open_fds.call #puts "after_open: #{after_open} before_open: #{before_open.inspect}" assert_equal 0, (after_open - before_open), "auto close the connections" ensure Curl::Multi.autoclose = false # restore default end def test_connection_autoclose assert !Curl::Multi.autoclose Curl::Multi.autoclose = true assert Curl::Multi.autoclose ensure Curl::Multi.autoclose = false # restore default end def test_new_multi_01 d1 = "" c1 = Curl::Easy.new($TEST_URL) do |curl| curl.headers["User-Agent"] = "myapp-0.0" curl.on_body {|d| d1 << d; d.length } end d2 = "" c2 = Curl::Easy.new($TEST_URL) do |curl| curl.headers["User-Agent"] = "myapp-0.0" curl.on_body {|d| d2 << d; d.length } end m = Curl::Multi.new m.add( c1 ) m.add( c2 ) m.perform assert_match(/^# DO NOT REMOVE THIS COMMENT/, d1) assert_match(/^# DO NOT REMOVE THIS COMMENT/, d2) m = nil end def test_perform_block c1 = Curl::Easy.new($TEST_URL) c2 = Curl::Easy.new($TEST_URL) m = Curl::Multi.new m.add( c1 ) m.add( c2 ) m.perform do # idle #puts "idling..." end assert_match(/^# DO NOT REMOVE THIS COMMENT/, c1.body_str) assert_match(/^# DO NOT REMOVE THIS COMMENT/, c2.body_str) m = nil end def test_multi_easy_get n = 1 urls = [] n.times { urls << $TEST_URL } Curl::Multi.get(urls, {timeout: 5}) {|easy| assert_match(/file:/, easy.last_effective_url) } end def test_multi_easy_get_with_error begin did_raise = false n = 3 urls = [] n.times { urls << $TEST_URL } error_line_number_should_be = nil Curl::Multi.get(urls, {timeout: 5}) {|easy| # if we got this right the error will be reported to be on the line below our error_line_number_should_be error_line_number_should_be = __LINE__ raise } rescue Curl::Err::AbortedByCallbackError => e did_raise = true in_file = e.backtrace.detect {|err| err.match?(File.basename(__FILE__)) } in_file_stack = e.backtrace.select {|err| err.match?(File.basename(__FILE__)) } assert_match(__FILE__, in_file) in_file.gsub!(__FILE__) parts = in_file.split(':') parts.shift line_no = parts.shift.to_i assert_equal error_line_number_should_be+1, line_no.to_i end assert did_raise, "we should have raised an exception" end # NOTE: if this test runs slowly on Mac OSX, it is probably due to the use of a port install curl+ssl+ares install # on my MacBook, this causes curl_easy_init to take nearly 0.01 seconds / * 100 below is 1 second too many! def test_n_requests n = 100 m = Curl::Multi.new responses = [] n.times do|i| responses[i] = "" c = Curl::Easy.new($TEST_URL) do|curl| curl.on_body{|data| responses[i] << data; data.size } end m.add c end m.perform assert_equal n, responses.size n.times do|i| assert_match(/^# DO NOT REMOVE THIS COMMENT/, responses[i], "response #{i}") end m = nil end def test_n_requests_with_break # process n requests then load the handle again and run it again n = 2 m = Curl::Multi.new 5.times do|it| responses = [] n.times do|i| responses[i] = "" c = Curl::Easy.new($TEST_URL) do|curl| curl.on_body{|data| responses[i] << data; data.size } end m.add c end m.perform assert_equal n, responses.size n.times do|i| assert_match(/^# DO NOT REMOVE THIS COMMENT/, responses[i], "response #{i}") end end m = nil end def test_idle_check m = Curl::Multi.new e = Curl::Easy.new($TEST_URL) assert(m.idle?, 'A new Curl::Multi handle should be idle') assert_nil e.multi m.add(e) assert_not_nil e.multi assert((not m.idle?), 'A Curl::Multi handle with a request should not be idle') m.perform assert(m.idle?, 'A Curl::Multi handle should be idle after performing its requests') end def test_requests m = Curl::Multi.new assert_equal(0, m.requests.length, 'A new Curl::Multi handle should have no requests') 10.times do m.add(Curl::Easy.new($TEST_URL)) end assert_equal(10, m.requests.length, 'multi.requests should contain all the active requests') m.perform assert_equal(0, m.requests.length, 'A new Curl::Multi handle should have no requests after a perform') end def test_cancel m = Curl::Multi.new m.cancel! # shouldn't raise anything 10.times do m.add(Curl::Easy.new($TEST_URL)) end m.cancel! assert_equal(0, m.requests.size, 'A new Curl::Multi handle should have no requests after being canceled') end def test_with_success c1 = Curl::Easy.new($TEST_URL) c2 = Curl::Easy.new($TEST_URL) success_called1 = false success_called2 = false c1.on_success do|c| success_called1 = true assert_match(/^# DO NOT REMOVE THIS COMMENT/, c.body_str) end c2.on_success do|c| success_called2 = true assert_match(/^# DO NOT REMOVE THIS COMMENT/, c.body_str) end m = Curl::Multi.new m.add( c1 ) m.add( c2 ) m.perform do # idle #puts "idling..." end assert success_called2 assert success_called1 m = nil end def test_with_success_cb_with_404 c1 = Curl::Easy.new("#{$TEST_URL.gsub(/file:\/\//,'')}/not_here") c2 = Curl::Easy.new($TEST_URL) success_called1 = false success_called2 = false c1.on_success do|c| success_called1 = true #puts "success 1 called: #{c.body_str.inspect}" assert_match(/^# DO NOT REMOVE THIS COMMENT/, c.body_str) end c1.on_failure do|c,rc| # rc => [Curl::Err::MalformedURLError, "URL using bad/illegal format or missing URL"] assert_equal Curl::Easy, c.class assert_equal Curl::Err::MalformedURLError, rc.first assert_equal "URL using bad/illegal format or missing URL", rc.last end c2.on_success do|c| # puts "success 2 called: #{c.body_str.inspect}" success_called2 = true assert_match(/^# DO NOT REMOVE THIS COMMENT/, c.body_str) end m = Curl::Multi.new #puts "c1: #{c1.url}" m.add( c1 ) #puts "c2: #{c2.url}" m.add( c2 ) #puts "calling" m.perform do # idle end assert success_called2 assert !success_called1 m = nil end # This tests whether, ruby's GC will trash an out of scope easy handle class TestForScope attr_reader :buf def t_method @buf = "" @m = Curl::Multi.new 10.times do|i| c = Curl::Easy.new($TEST_URL) c.on_success{|b| @buf << b.body_str } ObjectSpace.garbage_collect @m.add(c) ObjectSpace.garbage_collect end ObjectSpace.garbage_collect end def t_call @m.perform do ObjectSpace.garbage_collect end end def self.test ObjectSpace.garbage_collect tfs = TestForScope.new ObjectSpace.garbage_collect tfs.t_method ObjectSpace.garbage_collect tfs.t_call ObjectSpace.garbage_collect tfs.buf end end def test_with_garbage_collect ObjectSpace.garbage_collect buf = TestForScope.test ObjectSpace.garbage_collect assert_match(/^# DO NOT REMOVE THIS COMMENT/, buf) end =begin def test_remote_requests responses = {} requests = ["http://google.co.uk/", "http://ruby-lang.org/"] m = Curl::Multi.new # add a few easy handles requests.each do |url| responses[url] = "" responses["#{url}-header"] = "" c = Curl::Easy.new(url) do|curl| curl.follow_location = true curl.on_header{|data| responses["#{url}-header"] << data; data.size } curl.on_body{|data| responses[url] << data; data.size } curl.on_success { puts curl.last_effective_url } end m.add(c) end m.perform requests.each do|url| puts responses["#{url}-header"].split("\r\n").inspect #puts responses[url].size end end =end def test_multi_easy_get_01 urls = [] root_uri = 'http://127.0.0.1:9129/ext/' # send a request to fetch all c files in the ext dir Dir[File.dirname(__FILE__) + "/../ext/*.c"].each do|path| urls << root_uri + File.basename(path) end urls = urls[0..(urls.size/2)] # keep it fast, webrick... Curl::Multi.get(urls, {:follow_location => true}, {:pipeline => true}) do|curl| assert_equal 200, curl.response_code end end def test_multi_easy_download_01 # test collecting response buffers to file e.g. on_body root_uri = 'http://127.0.0.1:9129/ext/' urls = [] downloads = [] file_info = {} FileUtils.mkdir("tmp/") # for each file store the size by file name Dir[File.dirname(__FILE__) + "/../ext/*.c"].each do|path| urls << (root_uri + File.basename(path)) downloads << "tmp/" + File.basename(path) file_info[File.basename(path)] = {:size => File.size(path), :path => path} end # start downloads Curl::Multi.download(urls,{},{},downloads) do|curl,download_path| assert_equal 200, curl.response_code assert File.exist?(download_path) assert_equal file_info[File.basename(download_path)][:size], File.size(download_path), "incomplete download: #{download_path}" end ensure FileUtils.rm_rf("tmp/") end def test_multi_easy_post_01 urls = [ { :url => TestServlet.url + '?q=1', :post_fields => {'field1' => 'value1', 'k' => 'j'}}, { :url => TestServlet.url + '?q=2', :post_fields => {'field2' => 'value2', 'foo' => 'bar', 'i' => 'j' }}, { :url => TestServlet.url + '?q=3', :post_fields => {'field3' => 'value3', 'field4' => 'value4'}} ] Curl::Multi.post(urls, {:follow_location => true, :multipart_form_post => true}, {:pipeline => true}) do|easy| str = easy.body_str assert_match(/POST/, str) fields = {} str.gsub(/POST\n/,'').split('&').map{|sv| k, v = sv.split('='); fields[k] = v } expected = urls.find{|s| s[:url] == easy.last_effective_url } assert_equal expected[:post_fields], fields #puts "#{easy.last_effective_url} #{fields.inspect}" end end def test_multi_easy_put_01 urls = [{ :url => TestServlet.url, :method => :put, :put_data => "message", :headers => {'Content-Type' => 'application/json' } }, { :url => TestServlet.url, :method => :put, :put_data => "message", :headers => {'Content-Type' => 'application/json' } }] Curl::Multi.put(urls, {}, {:pipeline => true}) do|easy| assert_match(/PUT/, easy.body_str) assert_match(/message/, easy.body_str) end end def test_multi_easy_http_01 urls = [ { :url => TestServlet.url + '?q=1', :method => :post, :post_fields => {'field1' => 'value1', 'k' => 'j'}}, { :url => TestServlet.url + '?q=2', :method => :post, :post_fields => {'field2' => 'value2', 'foo' => 'bar', 'i' => 'j' }}, { :url => TestServlet.url + '?q=3', :method => :post, :post_fields => {'field3' => 'value3', 'field4' => 'value4'}}, { :url => TestServlet.url, :method => :put, :put_data => "message", :headers => {'Content-Type' => 'application/json' } }, { :url => TestServlet.url, :method => :get } ] Curl::Multi.http(urls, {:pipeline => true}) do|easy, code, method| assert_equal 200, code case method when :post assert_match(/POST/, easy.body_str) when :get assert_match(/GET/, easy.body_str) when :put assert_match(/PUT/, easy.body_str) end #puts "#{easy.body_str.inspect}, #{method.inspect}, #{code.inspect}" end end def test_multi_easy_http_with_max_connects urls = [ { :url => TestServlet.url + '?q=1', :method => :get }, { :url => TestServlet.url + '?q=2', :method => :get }, { :url => TestServlet.url + '?q=3', :method => :get } ] Curl::Multi.http(urls, {:pipeline => true, :max_connects => 1}) do|easy, code, method| assert_equal 200, code case method when :post assert_match(/POST/, easy.body) when :get assert_match(/GET/, easy.body) when :put assert_match(/PUT/, easy.body) end end end def test_multi_recieves_500 m = Curl::Multi.new e = Curl::Easy.new("http://127.0.0.1:9129/methods") failure = false e.post_body = "hello=world&s=500" e.on_failure{|c,r| failure = true } e.on_success{|c| failure = false } m.add(e) m.perform assert failure e2 = Curl::Easy.new(TestServlet.url) e2.post_body = "hello=world" e2.on_failure{|c,r| failure = true } m.add(e2) m.perform failure = false assert !failure assert_equal "POST\nhello=world", e2.body_str end def test_remove_exception_is_descriptive m = Curl::Multi.new c = Curl::Easy.new("http://127.9.9.9:999110") m.remove(c) rescue => e assert_equal 'CURLError: Invalid easy handle', e.message assert_equal 0, m.requests.size end def test_retry_easy_handle m = Curl::Multi.new tries = 10 c1 = Curl::Easy.new('http://127.1.1.1:99911') do |curl| curl.on_failure {|c,e| assert_equal [Curl::Err::MalformedURLError, "URL using bad/illegal format or missing URL"], e if tries > 0 tries -= 1 m.add(c) end } end tries -= 1 m.add(c1) m.perform assert_equal 0, tries assert_equal 0, m.requests.size end def test_reusing_handle m = Curl::Multi.new c = Curl::Easy.new('http://127.0.0.1') do|easy| easy.on_complete{|e,r| puts e.inspect } end m.add(c) m.add(c) rescue => e assert Curl::Err::MultiBadEasyHandle == e.class || Curl::Err::MultiAddedAlready == e.class end def test_multi_default_timeout assert_equal 100, Curl::Multi.default_timeout Curl::Multi.default_timeout = 12 assert_equal 12, Curl::Multi.default_timeout assert_equal 100, (Curl::Multi.default_timeout = 100) end include TestServerMethods def setup server_setup end end curb-1.0.5/tests/bug_follow_redirect_288.rb0000644000004100000410000000450114362470347020622 0ustar www-datawww-datarequire File.expand_path(File.join(File.dirname(__FILE__), 'helper')) class BugFollowRedirect288 < Test::Unit::TestCase include BugTestServerSetupTeardown def setup @port = 9999 super @server.mount_proc("/redirect_to_test") do|req,res| res.set_redirect(WEBrick::HTTPStatus::TemporaryRedirect, "/test") end end def test_follow_redirect_with_no_redirect c = Curl::Easy.new('http://127.0.0.1:9999/test') did_call_redirect = false c.on_redirect do|x| did_call_redirect = true end c.perform assert !did_call_redirect, "should reach this point redirect should not have been called" c = Curl::Easy.new('http://127.0.0.1:9999/test') did_call_redirect = false c.on_redirect do|x| did_call_redirect = true end c.follow_location = true c.perform assert_equal 0, c.redirect_count assert !did_call_redirect, "should reach this point redirect should not have been called" c = Curl::Easy.new('http://127.0.0.1:9999/redirect_to_test') did_call_redirect = false c.on_redirect do|x| did_call_redirect = true end c.perform assert_equal 307, c.response_code assert did_call_redirect, "we should have called on_redirect" c = Curl::Easy.new('http://127.0.0.1:9999/redirect_to_test') did_call_redirect = false c.follow_location = true # NOTE: while this API is not supported by libcurl e.g. there is no redirect function callback in libcurl we could # add support in ruby for this by executing this callback if redirect_count is greater than 0 at the end of a request in curb_multi.c c.on_redirect do|x| did_call_redirect = true end c.perform assert_equal 1, c.redirect_count assert_equal 200, c.response_code assert did_call_redirect, "we should have called on_redirect" c.url = 'http://127.0.0.1:9999/test' c.perform assert_equal 0, c.redirect_count assert_equal 200, c.response_code puts "checking for raise support" did_raise = false begin c = Curl::Easy.new('http://127.0.0.1:9999/redirect_to_test') did_call_redirect = false c.on_redirect do|x| raise "raise" did_call_redirect = true end c.perform rescue => e did_raise = true end assert_equal 307, c.response_code assert did_raise end end curb-1.0.5/tests/bug_issue277.rb0000644000004100000410000000127214362470347016430 0ustar www-datawww-datarequire File.expand_path(File.join(File.dirname(__FILE__), 'helper')) require 'curb' class BugIssue102 < Test::Unit::TestCase def test_gc_closewait 100.times do responses = {} requests = ["http://www.google.co.uk/", "http://www.ruby-lang.org/"] m = Curl::Multi.new # add a few easy handles requests.each do |url| responses[url] = "" c = Curl::Easy.new(url) do|curl| curl.follow_location = true curl.on_body{|data| responses[url] << data; data.size } curl.on_success {|easy| #puts "success, add more easy handles" } end m.add(c) end m.perform do #puts "idling... can do some work here" end GC.start end end end curb-1.0.5/tests/bug_instance_post_differs_from_class_post.rb0000644000004100000410000000266014362470347024672 0ustar www-datawww-data# From Vlad Jebelev: # # - Second thing - I think you just probably didn't have the time to update # instance methods yet but when I POST with a reusal of a previous curl # instance, it doesnt' work for me, e.g. when I create a curl previously and # then issue: # # c.http_post(login_url, *fields) # # instead of: # # c = Curl::Easy.http_post(login_url, *fields) do |curl| # ... # end # # then the result I am getting is quite different. # # ================ # # Update: # # It seems that class httpost is incorrectly passing arguments down to # instance httppost. This bug is intermittent, but results in an # exception from the first post when it occurs. # require File.expand_path(File.join(File.dirname(__FILE__), 'helper')) class BugTestInstancePostDiffersFromClassPost < Test::Unit::TestCase def test_bug 5.times do |i| puts "Test ##{i}" do_test sleep 2 end end def do_test c = Curl::Easy.http_post('https://www.google.com/accounts/ServiceLoginAuth', Curl::PostField.content('ltmpl','m_blanco')) body_c, header_c = c.body_str, c.header_str sleep 2 c.http_post('https://www.google.com/accounts/ServiceLoginAuth', Curl::PostField.content('ltmpl','m_blanco')) body_i, header_i = c.body, c.head # timestamps will differ, just check first bit. We wont get here if # the bug bites anyway... assert_equal header_c[0..50], header_i[0..50] end end curb-1.0.5/tests/tc_curl_easy.rb0000644000004100000410000007334014362470347016664 0ustar www-datawww-datarequire File.expand_path(File.join(File.dirname(__FILE__), 'helper')) class FooNoToS undef to_s end class TestCurbCurlEasy < Test::Unit::TestCase def test_global_reset Curl.get($TEST_URL) # in a Timeout block you should reset the thread current handle Curl.reset end def test_nested_easy_methods easy = Curl.get(TestServlet.url) {|http| res = Curl.get(TestServlet.url + '/not_here') assert_equal 404, res.code } assert_equal 200, easy.code end def test_curlopt_stderr_with_file # does not work with Tempfile directly path = Tempfile.new('curb_test_curlopt_stderr').path File.open(path, 'w') do |file| easy = Curl::Easy.new(TestServlet.url) easy.verbose = true easy.setopt(Curl::CURLOPT_STDERR, file) easy.perform end output = File.read(path) assert_match(/HTTP\/1\.1\ 200\ OK(?:\ )?/, output) assert_match('Host: 127.0.0.1:9129', output) end def test_curlopt_stderr_with_io path = Tempfile.new('curb_test_curlopt_stderr').path fd = IO.sysopen(path, 'w') io = IO.for_fd(fd) easy = Curl::Easy.new(TestServlet.url) easy.verbose = true easy.setopt(Curl::CURLOPT_STDERR, io) easy.perform output = File.read(path) assert_match(output, 'HTTP/1.1 200 OK') assert_match(output, 'Host: 127.0.0.1:9129') end def test_curlopt_stderr_fails_with_tempdir Tempfile.open('curb_test_curlopt_stderr') do |tempfile| easy = Curl::Easy.new(TestServlet.url) assert_raise(TypeError) do easy.setopt(Curl::CURLOPT_STDERR, tempfile) end end end def test_curlopt_stderr_fails_with_stringio stringio = StringIO.new easy = Curl::Easy.new(TestServlet.url) assert_raise(TypeError) do easy.setopt(Curl::CURLOPT_STDERR, stringio) end end def test_curlopt_stderr_fails_with_string string = String.new easy = Curl::Easy.new(TestServlet.url) assert_raise(TypeError) do easy.setopt(Curl::CURLOPT_STDERR, string) end end def test_exception begin Curl.get('NOT_FOUND_URL') rescue assert true rescue Exception assert false, "We should raise StandardError" end end def test_threads t = [] 5.times do t << Thread.new do 5.times do c = Curl.get($TEST_URL) assert_match(/^# DO NOT REMOVE THIS COMMENT/, c.body_str) assert_match(/^# DO NOT REMOVE THIS COMMENT/, c.body) end end end t.each {|x| x.join } end def test_class_perform_01 assert_instance_of Curl::Easy, c = Curl::Easy.perform($TEST_URL) assert_match(/^# DO NOT REMOVE THIS COMMENT/, c.body_str) assert_match(/^# DO NOT REMOVE THIS COMMENT/, c.body) end def test_class_perform_02 data = "" assert_instance_of Curl::Easy, c = Curl::Easy.perform($TEST_URL) { |curl| curl.on_body { |d| data << d; d.length } } assert_nil c.body_str assert_match(/^# DO NOT REMOVE THIS COMMENT/, data) end def test_class_perform_03 assert_raise(Curl::Err::CouldntReadError) { Curl::Easy.perform($TEST_URL + "nonexistent") } end def test_new_01 c = Curl::Easy.new assert_equal Curl::Easy, c.class assert_nil c.url assert_nil c.body_str assert_nil c.header_str end def test_new_02 c = Curl::Easy.new($TEST_URL) assert_equal $TEST_URL, c.url end def test_new_03 blk = lambda { |i| i.length } c = Curl::Easy.new do |curl| curl.on_body(&blk) end assert_nil c.url assert_equal blk, c.on_body # sets handler nil, returns old handler assert_equal nil, c.on_body end def test_new_04 blk = lambda { |i| i.length } c = Curl::Easy.new($TEST_URL) do |curl| curl.on_body(&blk) end assert_equal $TEST_URL, c.url assert_equal blk, c.on_body # sets handler nil, returns old handler assert_equal nil, c.on_body end class Foo < Curl::Easy ; end def test_new_05 # can use Curl::Easy as a base class c = Foo.new assert_equal Foo, c.class c.url = $TEST_URL c.perform assert_match(/^# DO NOT REMOVE THIS COMMENT/, c.body_str) end # test invalid use of new def test_new_06 Curl::Easy.new(TestServlet.url) do|curl| curl.http_post assert_equal "POST\n", curl.body_str end end def test_escape c = Curl::Easy.new assert_equal "one%20two", c.escape('one two') assert_equal "one%00two%20three", c.escape("one\000two three") end def test_unescape c = Curl::Easy.new assert_equal "one two", c.unescape('one%20two') # prior to 7.15.4 embedded nulls cannot be unescaped if Curl::VERNUM >= 0x070f04 assert_equal "one\000two three", c.unescape("one%00two%20three") end end def test_headers c = Curl::Easy.new($TEST_URL) assert_equal({}, c.headers) c.headers = "Expect:" assert_equal "Expect:", c.headers c.headers = ["Expect:", "User-Agent: myapp-0.0.0"] assert_equal ["Expect:", "User-Agent: myapp-0.0.0"], c.headers end def test_get_01 c = Curl::Easy.new($TEST_URL) assert_equal true, c.http_get assert_match(/^# DO NOT REMOVE THIS COMMENT/, c.body_str) end def test_get_02 data = "" c = Curl::Easy.new($TEST_URL) do |curl| curl.on_body { |d| data << d; d.length } end assert_equal true, c.http_get assert_nil c.body_str assert_match(/^# DO NOT REMOVE THIS COMMENT/, data) end def test_get_03 c = Curl::Easy.new($TEST_URL + "nonexistent") assert_raise(Curl::Err::CouldntReadError) { c.http_get } assert_equal "", c.body_str assert_equal "", c.header_str end def test_last_effective_url_01 c = Curl::Easy.new($TEST_URL) assert_equal $TEST_URL, c.url assert_nil c.last_effective_url assert c.http_get assert_equal c.url, c.last_effective_url c.url = "file://some/new.url" assert_not_equal c.last_effective_url, c.url end def test_http_get_block curl = Curl::Easy.http_get(TestServlet.url) do|c| c.follow_location = true c.max_redirects = 3 end assert_equal curl.url, curl.last_effective_url assert_equal 'GET', curl.body_str end def test_local_port_01 c = Curl::Easy.new($TEST_URL) assert_nil c.local_port assert_nil c.local_port_range assert_nil c.proxy_port c.local_port = 88 assert_equal 88, c.local_port assert_nil c.local_port_range assert_nil c.proxy_port c.local_port = nil assert_nil c.local_port assert_nil c.local_port_range assert_nil c.proxy_port end def test_local_port_02 c = Curl::Easy.new($TEST_URL) assert_nil c.local_port assert_raise(ArgumentError) { c.local_port = 0 } assert_raise(ArgumentError) { c.local_port = 65536 } assert_raise(ArgumentError) { c.local_port = -1 } end def test_local_port_range_01 c = Curl::Easy.new($TEST_URL) assert_nil c.local_port_range assert_nil c.local_port assert_nil c.proxy_port c.local_port_range = 88 assert_equal 88, c.local_port_range assert_nil c.local_port assert_nil c.proxy_port c.local_port_range = nil assert_nil c.local_port_range assert_nil c.local_port assert_nil c.proxy_port end def test_local_port_range_02 c = Curl::Easy.new($TEST_URL) assert_nil c.local_port_range assert_raise(ArgumentError) { c.local_port_range = 0 } assert_raise(ArgumentError) { c.local_port_range = 65536 } assert_raise(ArgumentError) { c.local_port_range = -1 } end def test_proxy_url_01 c = Curl::Easy.new($TEST_URL) assert_equal $TEST_URL, c.url assert_nil c.proxy_url c.proxy_url = "http://some.proxy" assert_equal $TEST_URL, c.url assert_equal "http://some.proxy", c.proxy_url c.proxy_url = nil assert_equal $TEST_URL, c.url assert_nil c.proxy_url end def test_proxy_port_01 c = Curl::Easy.new($TEST_URL) assert_nil c.local_port assert_nil c.local_port_range assert_nil c.proxy_port c.proxy_port = 88 assert_equal 88, c.proxy_port assert_nil c.local_port assert_nil c.local_port_range c.proxy_port = nil assert_nil c.proxy_port assert_nil c.local_port assert_nil c.local_port_range end def test_proxy_port_02 c = Curl::Easy.new($TEST_URL) assert_nil c.proxy_port assert_raise(ArgumentError) { c.proxy_port = 0 } assert_raise(ArgumentError) { c.proxy_port = 65536 } assert_raise(ArgumentError) { c.proxy_port = -1 } end def test_proxy_type_01 c = Curl::Easy.new($TEST_URL) assert_nil c.proxy_type c.proxy_type = 3 assert_equal 3, c.proxy_type c.proxy_type = nil assert_nil c.proxy_type end def test_http_auth_types_01 c = Curl::Easy.new($TEST_URL) assert_nil c.http_auth_types c.http_auth_types = 3 assert_equal 3, c.http_auth_types c.http_auth_types = nil assert_nil c.http_auth_types end def test_proxy_auth_types_01 c = Curl::Easy.new($TEST_URL) assert_nil c.proxy_auth_types c.proxy_auth_types = 3 assert_equal 3, c.proxy_auth_types c.proxy_auth_types = nil assert_nil c.proxy_auth_types end def test_max_redirects_01 c = Curl::Easy.new($TEST_URL) assert_nil c.max_redirects c.max_redirects = 3 assert_equal 3, c.max_redirects c.max_redirects = nil assert_nil c.max_redirects end def test_timeout_with_floats c = Curl::Easy.new($TEST_URL) c.timeout = 1.5 assert_equal 1500, c.timeout_ms assert_equal 1.5, c.timeout end def test_timeout_with_negative c = Curl::Easy.new($TEST_URL) c.timeout = -1.5 assert_equal 0, c.timeout assert_equal 0, c.timeout_ms c.timeout = -4.8 assert_equal 0, c.timeout assert_equal 0, c.timeout_ms end def test_timeout_01 c = Curl::Easy.new($TEST_URL) assert_equal 0, c.timeout c.timeout = 3 assert_equal 3, c.timeout c.timeout = 0 assert_equal 0, c.timeout end def test_timeout_ms_01 c = Curl::Easy.new($TEST_URL) assert_equal 0, c.timeout_ms c.timeout_ms = 100 assert_equal 100, c.timeout_ms c.timeout_ms = nil assert_equal 0, c.timeout_ms end def test_timeout_ms_with_floats c = Curl::Easy.new($TEST_URL) c.timeout_ms = 55.5 assert_equal 55, c.timeout_ms assert_equal 0.055, c.timeout end def test_timeout_ms_with_negative c = Curl::Easy.new($TEST_URL) c.timeout_ms = -1.5 assert_equal 0, c.timeout assert_equal 0, c.timeout_ms c.timeout_ms = -4.8 assert_equal 0, c.timeout assert_equal 0, c.timeout_ms end def test_connect_timeout_01 c = Curl::Easy.new($TEST_URL) assert_nil c.connect_timeout c.connect_timeout = 3 assert_equal 3, c.connect_timeout c.connect_timeout = nil assert_nil c.connect_timeout end def test_connect_timeout_ms_01 c = Curl::Easy.new($TEST_URL) assert_nil c.connect_timeout_ms c.connect_timeout_ms = 100 assert_equal 100, c.connect_timeout_ms c.connect_timeout_ms = nil assert_nil c.connect_timeout_ms end def test_ftp_response_timeout_01 c = Curl::Easy.new($TEST_URL) assert_nil c.ftp_response_timeout c.ftp_response_timeout = 3 assert_equal 3, c.ftp_response_timeout c.ftp_response_timeout = nil assert_nil c.ftp_response_timeout end def test_dns_cache_timeout_01 c = Curl::Easy.new($TEST_URL) assert_equal 60, c.dns_cache_timeout c.dns_cache_timeout = nil assert_nil c.dns_cache_timeout c.dns_cache_timeout = 30 assert_equal 30, c.dns_cache_timeout end def test_low_speed_limit_01 c = Curl::Easy.new($TEST_URL) assert_nil c.low_speed_limit c.low_speed_limit = 3 assert_equal 3, c.low_speed_limit c.low_speed_limit = nil assert_nil c.low_speed_limit end def test_low_speed_time_01 c = Curl::Easy.new($TEST_URL) assert_nil c.low_speed_time c.low_speed_time = 3 assert_equal 3, c.low_speed_time c.low_speed_time = nil assert_nil c.low_speed_time end def test_on_body blk = lambda { |i| i.length } c = Curl::Easy.new c.on_body(&blk) assert_equal blk, c.on_body # sets handler nil, returns old handler assert_equal nil, c.on_body end def test_inspect_with_no_url c = Curl::Easy.new assert_equal '#', c.inspect end def test_inspect_with_short_url c = Curl::Easy.new('http://www.google.com/') assert_equal "#", c.inspect end def test_inspect_truncates_to_64_chars base_url = 'http://www.google.com/' truncated_url = base_url + 'x' * (64 - '#'.size - base_url.size) long_url = truncated_url + 'yyyy' c = Curl::Easy.new(long_url) assert_equal 64, c.inspect.size assert_equal "#", c.inspect end def test_on_header blk = lambda { |i| i.length } c = Curl::Easy.new c.on_header(&blk) assert_equal blk, c.on_header # sets handler nil, returns old handler assert_equal nil, c.on_header end def test_on_progress blk = lambda { |*args| true } c = Curl::Easy.new c.on_progress(&blk) assert_equal blk, c.on_progress # sets handler nil, returns old handler assert_equal nil, c.on_progress end def test_on_debug blk = lambda { |*args| true } c = Curl::Easy.new c.on_debug(&blk) assert_equal blk, c.on_debug # sets handler nil, returns old handler assert_equal nil, c.on_debug end def test_proxy_tunnel c = Curl::Easy.new assert !c.proxy_tunnel? assert c.proxy_tunnel = true assert c.proxy_tunnel? end def test_fetch_file_time c = Curl::Easy.new assert !c.fetch_file_time? assert c.fetch_file_time = true assert c.fetch_file_time? end def test_ssl_verify_peer c = Curl::Easy.new assert c.ssl_verify_peer? assert !c.ssl_verify_peer = false assert !c.ssl_verify_peer? end def test_ssl_verify_host c = Curl::Easy.new assert c.ssl_verify_host? c.ssl_verify_host = 0 c.ssl_verify_host = false assert !c.ssl_verify_host? end def test_header_in_body c = Curl::Easy.new assert !c.header_in_body? assert c.header_in_body = true assert c.header_in_body? end def test_use_netrc c = Curl::Easy.new assert !c.use_netrc? assert c.use_netrc = true assert c.use_netrc? end def test_follow_location c = Curl::Easy.new assert !c.follow_location? assert c.follow_location = true assert c.follow_location? end def test_unrestricted_auth c = Curl::Easy.new assert !c.unrestricted_auth? assert c.unrestricted_auth = true assert c.unrestricted_auth? end def test_multipart_form_post c = Curl::Easy.new assert !c.multipart_form_post? assert c.multipart_form_post = true assert c.multipart_form_post? end def test_ignore_content_length c = Curl::Easy.new assert !c.ignore_content_length? assert c.ignore_content_length = true assert c.ignore_content_length? end def test_resolve_mode c = Curl::Easy.new assert_equal :auto, c.resolve_mode c.resolve_mode = :ipv4 assert_equal :ipv4, c.resolve_mode c.resolve_mode = :ipv6 assert_equal :ipv6, c.resolve_mode assert_raises(ArgumentError) { c.resolve_mode = :bad } end def test_enable_cookies c = Curl::Easy.new assert !c.enable_cookies? assert c.enable_cookies = true assert c.enable_cookies? end def test_cookies_option c = Curl::Easy.new assert_nil c.cookies assert_equal "name1=content1; name2=content2;", c.cookies = "name1=content1; name2=content2;" assert_equal "name1=content1; name2=content2;", c.cookies end def test_cookiefile c = Curl::Easy.new assert_nil c.cookiefile assert_equal "some.file", c.cookiefile = "some.file" assert_equal "some.file", c.cookiefile end def test_cookiejar c = Curl::Easy.new assert_nil c.cookiejar assert_equal "some.file", c.cookiejar = "some.file" assert_equal "some.file", c.cookiejar end def test_cookielist c = Curl::Easy.new TestServlet.url c.enable_cookies = true c.post_body = URI.encode_www_form('c' => 'somename=somevalue') assert_nil c.cookielist c.perform assert_match(/somevalue/, c.cookielist.join('')) end def test_on_success curl = Curl::Easy.new($TEST_URL) on_success_called = false curl.on_success {|c| on_success_called = true assert_not_nil c.body assert_match(/Content-Length: /, c.head) assert_match(/^# DO NOT REMOVE THIS COMMENT/, c.body) } curl.perform assert on_success_called, "Success handler not called" end def test_on_success_with_on_failure curl = Curl::Easy.new(TestServlet.url + '/error') on_failure_called = false curl.on_success {|c| } # make sure we get the failure call even though this handler is defined curl.on_failure {|c,code| on_failure_called = true } curl.perform assert_equal 500, curl.response_code assert on_failure_called, "Failure handler not called" end def test_on_success_with_on_missing curl = Curl::Easy.new(TestServlet.url + '/not_here') on_missing_called = false curl.on_success {|c| } # make sure we get the missing call even though this handler is defined curl.on_missing {|c,code| on_missing_called = true } curl.perform assert_equal 404, curl.response_code assert on_missing_called, "Missing handler not called" end def test_on_success_with_on_redirect curl = Curl::Easy.new(TestServlet.url + '/redirect') on_redirect_called = false curl.on_success {|c| } # make sure we get the redirect call even though this handler is defined curl.on_redirect {|c,code| on_redirect_called = true } curl.perform assert_equal 302, curl.response_code assert on_redirect_called, "Redirect handler not called" end def test_get_remote curl = Curl::Easy.new(TestServlet.url) curl.http_get assert_equal 'GET', curl.body_str end def test_post_remote curl = Curl::Easy.new(TestServlet.url) curl.http_post([Curl::PostField.content('document_id', 5)]) assert_equal "POST\ndocument_id=5", curl.unescape(curl.body_str) end def test_post_remote_is_easy_handle # see: http://pastie.org/560852 and # http://groups.google.com/group/curb---ruby-libcurl-bindings/browse_thread/thread/216bb2d9b037f347?hl=en [:post, :get, :head, :delete].each do |method| retries = 0 begin count = 0 Curl::Easy.send("http_#{method}", TestServlet.url) do|c| count += 1 assert_equal Curl::Easy, c.class end assert_equal 1, count, "For request method: #{method.to_s.upcase}" rescue Curl::Err::HostResolutionError => e # travis-ci.org fails to resolve... try again? retries+=1 retry if retries < 3 raise e end end end # see: https://github.com/rvanlieshout/curb/commit/8bcdefddc0162484681ebd1a92d52a642666a445 def test_post_multipart_array_remote curl = Curl::Easy.new(TestServlet.url) curl.multipart_form_post = true fields = [ Curl::PostField.file('foo', File.expand_path(File.join(File.dirname(__FILE__),'..','README.markdown'))), Curl::PostField.file('bar', File.expand_path(File.join(File.dirname(__FILE__),'..','README.markdown'))) ] curl.http_post(fields) assert_match(/HTTP POST file upload/, curl.body_str) assert_match(/Content-Disposition: form-data/, curl.body_str) end def test_post_with_body_remote curl = Curl::Easy.new(TestServlet.url) curl.post_body = 'foo=bar&encoded%20string=val' curl.perform assert_equal "POST\nfoo=bar&encoded%20string=val", curl.body_str assert_equal 'foo=bar&encoded%20string=val', curl.post_body end def test_form_post_body_remote curl = Curl::Easy.new(TestServlet.url) curl.http_post('foo=bar', 'encoded%20string=val') assert_equal "POST\nfoo=bar&encoded%20string=val", curl.body_str assert_equal 'foo=bar&encoded%20string=val', curl.post_body end def test_post_multipart_file_remote curl = Curl::Easy.new(TestServlet.url) curl.multipart_form_post = true pf = Curl::PostField.file('readme', File.expand_path(File.join(File.dirname(__FILE__),'..','README.markdown'))) curl.http_post(pf) assert_match(/HTTP POST file upload/, curl.body_str) assert_match(/Content-Disposition: form-data/, curl.body_str) end def test_delete_remote curl = Curl::Easy.new(TestServlet.url) curl.http_delete assert_equal 'DELETE', curl.body_str end def test_arbitrary_http_verb curl = Curl::Easy.new(TestServlet.url) curl.http('PURGE') assert_equal 'PURGE', curl.body_str end def test_head_remote curl = Curl::Easy.new(TestServlet.url) curl.http_head redirect = curl.header_str.match(/Location: (.*)/) assert_equal '', curl.body_str assert_match('/nonexistent', redirect[1]) end def test_head_accessor curl = Curl::Easy.new(TestServlet.url) curl.head = true curl.perform redirect = curl.header_str.match(/Location: (.*)/) assert_equal '', curl.body_str assert_match('/nonexistent', redirect[1]) curl.head = false curl.perform assert_equal 'GET', curl.body_str end def test_put_remote curl = Curl::Easy.new(TestServlet.url) curl.headers['Content-Type'] = 'application/json' assert curl.http_put("message") assert_match(/^PUT/, curl.body_str) assert_match(/message$/, curl.body_str) assert_match(/message$/, curl.body) assert_match(/application\/json/, curl.header_str) assert_match(/application\/json/, curl.head) end def test_put_data curl = Curl::Easy.new(TestServlet.url) curl.put_data = 'message' curl.perform assert_match(/^PUT/, curl.body_str) assert_match(/message$/, curl.body_str) end # https://github.com/taf2/curb/issues/101 def test_put_data_null_bytes curl = Curl::Easy.new(TestServlet.url) curl.put_data = "a\0b" curl.perform assert_match(/^PUT/, curl.body_str) assert_match("a\0b", curl.body_str) end def test_put_nil_data_no_crash curl = Curl::Easy.new(TestServlet.url) curl.put_data = nil curl.perform end def test_put_remote_file curl = Curl::Easy.new(TestServlet.url) File.open(__FILE__,'rb') do|f| assert curl.http_put(f) end assert_equal "PUT\n#{File.read(__FILE__)}", curl.body_str.tr("\r", '') end def test_put_class_method count = 0 curl = Curl::Easy.http_put(TestServlet.url,File.open(__FILE__,'rb')) do|c| count += 1 assert_equal Curl::Easy, c.class end assert_equal 1, count assert_equal "PUT\n#{File.read(__FILE__)}", curl.body_str.tr("\r", '') end # Generate a self-signed cert with # openssl req -new -newkey rsa:1024 -days 365 -nodes -x509 \ # -keyout tests/cert.pem -out tests/cert.pem def test_cert curl = Curl::Easy.new(TestServlet.url) curl.cert= File.join(File.dirname(__FILE__),"cert.pem") assert_match(/cert.pem$/,curl.cert) end def test_cert_with_password curl = Curl::Easy.new(TestServlet.url) path = File.join(File.dirname(__FILE__),"cert.pem") curl.certpassword = 'password' curl.cert = path assert_match(/cert.pem$/,curl.cert) end def test_cert_type curl = Curl::Easy.new(TestServlet.url) curl.certtype= "DER" assert_equal "DER", curl.certtype end def test_default_certtype curl = Curl::Easy.new(TestServlet.url) assert_nil curl.certtype curl.certtype = "PEM" assert_equal "PEM", curl.certtype end # Generate a CA cert with instructions at # http://technocage.com/~caskey/openssl/ def test_ca_cert curl = Curl::Easy.new(TestServlet.url) curl.cacert= File.join(File.dirname(__FILE__),"cacert.pem") assert_match(/cacert.pem$/, curl.cacert) end def test_user_agent curl = Curl::Easy.new(TestServlet.url) curl.useragent= "Curb-Easy/Ruby" assert_equal "Curb-Easy/Ruby",curl.useragent end def test_username_password curl = Curl::Easy.new(TestServlet.url) curl.username = "foo" curl.password = "bar" if !curl.username.nil? assert_equal "foo", curl.username assert_equal "bar", curl.password else curl.userpwd = "foo:bar" end curl.http_auth_types = :basic #curl.verbose = true curl.perform assert_equal 'Basic Zm9vOmJhcg==', $auth_header $auth_header = nil # curl checks the auth type supported by the server, so we have to create a # new easy handle if we're going to change the auth type... curl = Curl::Easy.new(TestServlet.url) curl.username = "foo" curl.password = "bar" if curl.username.nil? curl.userpwd = "foo:bar" end curl.http_auth_types = :ntlm curl.perform assert_equal 'NTLM TlRMTVNTUAABAAAABoIIAAAAAAAAAAAAAAAAAAAAAAA=', $auth_header end def test_primary_ip curl = Curl::Easy.new(TestServlet.url) if curl.respond_to?(:primary_ip) curl.perform assert_equal '127.0.0.1', curl.primary_ip end end def test_post_streaming readme = File.expand_path(File.join(File.dirname(__FILE__),'..','README.markdown')) pf = Curl::PostField.file("filename", readme) easy = Curl::Easy.new easy.url = TestServlet.url easy.multipart_form_post = true easy.http_post(pf) assert_not_equal(0,easy.body_str.size) assert_equal(easy.body_str.tr("\r", ''), File.read(readme)) end def test_easy_close easy = Curl::Easy.new easy.close easy.url = TestServlet.url easy.http_get end def test_easy_reset easy = Curl::Easy.new easy.url = TestServlet.url + "?query=foo" easy.http_get settings = easy.reset assert settings.key?(:url) assert settings.key?(:body_data) assert settings.key?(:header_data) easy.url = TestServlet.url easy.http_get end def test_easy_use_http_versions easy = Curl::Easy.new easy.url = TestServlet.url + "?query=foo" #puts "http none: #{Curl::HTTP_NONE.inspect}" #puts "http1.0: #{Curl::HTTP_1_0.inspect}" #puts "http1.1: #{Curl::HTTP_1_1.inspect}" easy.version = Curl::HTTP_1_1 #easy.verbose = true easy.http_get end def test_easy_http_verbs curl = Curl::Easy.new(TestServlet.url) curl.http_delete assert_equal 'DELETE', curl.body_str curl.http_get assert_equal 'GET', curl.body_str curl.http_post assert_equal "POST\n", curl.body_str curl.http('PURGE') assert_equal 'PURGE', curl.body_str curl.http_put('hello') assert_equal "PUT\nhello", curl.body_str curl.http('COPY') assert_equal 'COPY', curl.body_str end def test_easy_http_verbs_must_respond_to_str # issue http://github.com/taf2/curb/issues#issue/45 assert_nothing_raised do c = Curl::Easy.new ; c.url = 'http://example.com' ; c.http(:get) end assert_raise RuntimeError do c = Curl::Easy.new ; c.url = 'http://example.com' ; c.http(FooNoToS.new) end end # http://github.com/taf2/curb/issues/#issue/33 def test_easy_http_verbs_with_errors curl = Curl::Easy.new("http://127.0.0.1:9012/") # test will fail if http server on port 9012 assert_raise Curl::Err::ConnectionFailedError do curl.http_delete end curl.url = TestServlet.url curl.http_get assert_equal 'GET', curl.body_str end def test_easy_can_put_with_content_length curl = Curl::Easy.new(TestServlet.url) rd, wr = IO.pipe buf = (("hello")* (1000 / 5)) producer = Thread.new do 5.times do wr << buf sleep 0.1 # act as a slow producer end end consumer = Thread.new do #curl.verbose = true curl.headers['Content-Length'] = buf.size * 5 curl.headers['User-Agent'] = "Something else" curl.headers['Content-Type'] = "text/javascript" curl.headers['Date'] = Time.now.httpdate curl.headers['Host'] = 's3.amazonaws.com' curl.headers['Accept'] = '*/*' curl.headers['Authorization'] = 'Foo Bar Biz Baz' curl.http_put(rd) assert_match(/^PUT/, curl.body_str) assert_match(/hello$/, curl.body_str) curl.header_str curl.body_str end producer.join wr.close consumer.join end def test_get_set_multi_on_easy easy = Curl::Easy.new assert_nil easy.multi multi = Curl::Multi.new easy.multi = multi assert_not_nil easy.multi assert_equal multi, easy.multi end def test_raise_on_progress c = Curl::Easy.new($TEST_URL) c.on_progress {|w,x,y,z| raise "error" } c.perform rescue => e assert_equal 'Curl::Err::AbortedByCallbackError', e.class.to_s c.close end def test_raise_on_success c = Curl::Easy.new($TEST_URL) c.on_success {|x| raise "error" } c.perform rescue Curl::Err::AbortedByCallbackError => e assert_equal 'Curl::Err::AbortedByCallbackError', e.class.to_s c.close end def test_raise_on_debug c = Curl::Easy.new($TEST_URL) c.on_debug { raise "error" } c.perform assert true, "raise in on debug has no effect" end def test_status_codes curl = Curl::Easy.new(TestServlet.url) curl.perform assert_equal '200 OK', curl.status end def test_close_in_on_callbacks curl = Curl::Easy.new(TestServlet.url) curl.on_body {|d| curl.close; d.size } assert_raises RuntimeError do curl.perform end end def test_set_unsupported_options curl = Curl::Easy.new assert_raises TypeError do curl.set(99999, 1) end end include TestServerMethods def setup server_setup end end curb-1.0.5/tests/tc_curl_easy_resolve.rb0000644000004100000410000000057514362470347020423 0ustar www-datawww-datarequire File.expand_path(File.join(File.dirname(__FILE__), 'helper')) class TestCurbCurlEasyResolve < Test::Unit::TestCase def setup @easy = Curl::Easy.new end def test_resolve @easy.resolve = [ "example.com:80:127.0.0.1" ] assert_equal @easy.resolve, [ "example.com:80:127.0.0.1" ] end def test_empty_resolve assert_equal @easy.resolve, nil end end curb-1.0.5/tests/tc_curl.rb0000644000004100000410000000373614362470347015645 0ustar www-datawww-datarequire File.expand_path(File.join(File.dirname(__FILE__), 'helper')) class TestCurl < Test::Unit::TestCase def test_get curl = Curl.get(TestServlet.url, {:foo => "bar"}) assert_equal "GETfoo=bar", curl.body_str curl = Curl.options(TestServlet.url, {:foo => "bar"}) do|http| http.headers['Cookie'] = 'foo=1;bar=2' end assert_equal "OPTIONSfoo=bar", curl.body_str end def test_post curl = Curl.post(TestServlet.url, {:foo => "bar"}) assert_equal "POST\nfoo=bar", curl.body_str end def test_put curl = Curl.put(TestServlet.url, {:foo => "bar"}) assert_equal "PUT\nfoo=bar", curl.body_str end def test_patch curl = Curl.patch(TestServlet.url, {:foo => "bar"}) assert_equal "PATCH\nfoo=bar", curl.body_str end def test_options curl = Curl.options(TestServlet.url, {:foo => "bar"}) assert_equal "OPTIONSfoo=bar", curl.body_str end def test_urlalize_without_extra_params url_no_params = 'http://localhost/test' url_with_params = 'http://localhost/test?a=1' assert_equal(url_no_params, Curl.urlalize(url_no_params)) assert_equal(url_with_params, Curl.urlalize(url_with_params)) end def test_urlalize_with_nil_as_params url = 'http://localhost/test' assert_equal(url, Curl.urlalize(url, nil)) end def test_urlalize_with_extra_params url_no_params = 'http://localhost/test' url_with_params = 'http://localhost/test?a=1' extra_params = { :b => 2 } expected_url_no_params = 'http://localhost/test?b=2' expected_url_with_params = 'http://localhost/test?a=1&b=2' assert_equal(expected_url_no_params, Curl.urlalize(url_no_params, extra_params)) assert_equal(expected_url_with_params, Curl.urlalize(url_with_params, extra_params)) end def test_urlalize_does_not_strip_trailing_? url_empty_params = 'http://localhost/test?' assert_equal(url_empty_params, Curl.urlalize(url_empty_params)) end include TestServerMethods def setup server_setup end end curb-1.0.5/tests/bug_crash_on_debug.rb0000644000004100000410000000101214362470347017772 0ustar www-datawww-datarequire File.expand_path(File.join(File.dirname(__FILE__), 'helper')) class BugCrashOnDebug < Test::Unit::TestCase include BugTestServerSetupTeardown def test_on_debug c = Curl::Easy.new("http://127.0.0.1:#{@port}/test") did_raise = false did_call = false begin c.on_success do|x| did_call = true raise "error" # this will get swallowed end c.perform rescue => e did_raise = true end assert did_raise assert did_call end end #test_on_debug curb-1.0.5/tests/bug_multi_segfault.rb0000644000004100000410000000074714362470347020072 0ustar www-datawww-data# From safis http://github.com/taf2/curb/issues#issue/5 # irb: require 'curb' # irb: multi = Curl::Multi.new # irb: exit #
:47140: [BUG] Bus Error require File.expand_path(File.join(File.dirname(__FILE__), 'helper')) $:.unshift File.expand_path(File.join(File.dirname(__FILE__),'..','ext')) $:.unshift File.expand_path(File.join(File.dirname(__FILE__),'..','lib')) require 'curb' class BugMultiSegfault < Test::Unit::TestCase def test_bug multi = Curl::Multi.new end end curb-1.0.5/tests/tc_curl_download.rb0000644000004100000410000000434014362470347017524 0ustar www-datawww-datarequire File.expand_path(File.join(File.dirname(__FILE__), 'helper')) class TestCurbCurlDownload < Test::Unit::TestCase include TestServerMethods def setup server_setup end def test_download_url_to_file_via_string dl_url = "http://127.0.0.1:9129/ext/curb_easy.c" dl_path = File.join(Dir::tmpdir, "dl_url_test.file") Curl::Easy.download(dl_url, dl_path) assert File.exist?(dl_path) assert_equal File.read(File.join(File.dirname(__FILE__), '..','ext','curb_easy.c')), File.read(dl_path) ensure File.unlink(dl_path) if File.exist?(dl_path) end def test_download_url_to_file_via_file_io dl_url = "http://127.0.0.1:9129/ext/curb_easy.c" dl_path = File.join(Dir::tmpdir, "dl_url_test.file") io = File.open(dl_path, 'wb') Curl::Easy.download(dl_url, io) assert io.closed? assert File.exist?(dl_path) assert_equal File.read(File.join(File.dirname(__FILE__), '..','ext','curb_easy.c')), File.read(dl_path) ensure File.unlink(dl_path) if File.exist?(dl_path) end def test_download_url_to_file_via_io dl_url = "http://127.0.0.1:9129/ext/curb_easy.c" dl_path = File.join(Dir::tmpdir, "dl_url_test.file") reader, writer = IO.pipe # Write to local file fork do begin writer.close File.open(dl_path, 'wb') { |file| file << reader.read } ensure reader.close rescue IOError # if the stream has already been closed end end # Download remote source begin reader.close Curl::Easy.download(dl_url, writer) Process.wait ensure writer.close rescue IOError # if the stream has already been closed, which occurs in Easy::download end assert File.exist?(dl_path) assert_equal File.read(File.join(File.dirname(__FILE__), '..','ext','curb_easy.c')), File.read(dl_path) ensure File.unlink(dl_path) if File.exist?(dl_path) end def test_download_bad_url_gives_404 dl_url = "http://127.0.0.1:9129/this_file_does_not_exist.html" dl_path = File.join(Dir::tmpdir, "dl_url_test.file") curb = Curl::Easy.download(dl_url, dl_path) assert_equal Curl::Easy, curb.class assert_equal 404, curb.response_code ensure File.unlink(dl_path) if File.exist?(dl_path) end end curb-1.0.5/tests/bug_postfields_crash.rb0000644000004100000410000000134614362470347020376 0ustar www-datawww-data# From GICodeWarrior: # # $ ruby crash_curb.rb # crash_curb.rb:7: [BUG] Segmentation fault # ruby 1.8.7 (2009-06-12 patchlevel 174) [x86_64-linux] # # Aborted # crash_curb.rb: # #!/usr/bin/ruby # require 'rubygems' # require 'curb' # # curl = Curl::Easy.new('http://example.com/') # curl.multipart_form_post = true # curl.http_post(Curl::PostField.file('test', 'test.xml'){'example data'}) # Ubuntu 9.10 # curb gem version 0.6.2.1 require File.expand_path(File.join(File.dirname(__FILE__), 'helper')) class BugPostFieldsCrash < Test::Unit::TestCase def test_crash curl = Curl::Easy.new('http://example.com/') curl.multipart_form_post = true curl.http_post(Curl::PostField.file('test', 'test.xml'){'example data'}) end end curb-1.0.5/tests/bug_crash_on_progress.rb0000644000004100000410000000214214362470347020555 0ustar www-datawww-datarequire File.expand_path(File.join(File.dirname(__FILE__), 'helper')) class BugCrashOnDebug < Test::Unit::TestCase include BugTestServerSetupTeardown def test_on_progress_raise c = Curl::Easy.new("http://127.0.0.1:#{@port}/test") c.on_progress do|x| raise "error" end c.perform assert false, "should not reach this point" rescue => e assert_equal 'Curl::Err::AbortedByCallbackError', e.class.to_s c.close end def test_on_progress_abort # see: https://github.com/taf2/curb/issues/192, # to pass: # # c = Curl::Easy.new('http://127.0.0.1:9999/test') # c.on_progress do|x| # puts "we're in the progress callback" # false # end # c.perform # # notice no return keyword # c = Curl::Easy.new("http://127.0.0.1:#{@port}/test") did_progress = false c.on_progress do|x| did_progress = true return false end c.perform assert did_progress assert false, "should not reach this point" rescue => e assert_equal 'Curl::Err::AbortedByCallbackError', e.class.to_s c.close end end curb-1.0.5/tests/bug_curb_easy_blocks_ruby_threads.rb0000644000004100000410000000175514362470347023132 0ustar www-datawww-datarequire File.expand_path(File.join(File.dirname(__FILE__), 'helper')) class BugTestInstancePostDiffersFromClassPost < Test::Unit::TestCase include BugTestServerSetupTeardown def setup @port = 9999 @response_proc = lambda do|res| sleep 0.5 res.body = "hi" res['Content-Type'] = "text/html" end super end def test_bug threads = [] timer = Time.now 5.times do |i| t = Thread.new do c = Curl::Easy.perform('http://127.0.0.1:9999/test') c.header_str end threads << t end multi_responses = threads.collect do|t| t.value end multi_time = (Time.now - timer) puts "requested in #{multi_time}" timer = Time.now single_responses = [] 5.times do |i| c = Curl::Easy.perform('http://127.0.0.1:9999/test') single_responses << c.header_str end single_time = (Time.now - timer) puts "requested in #{single_time}" assert single_time > multi_time end end curb-1.0.5/tests/timeout.rb0000644000004100000410000000633514362470347015676 0ustar www-datawww-datarequire File.expand_path(File.join(File.dirname(__FILE__), 'helper')) # Run server with: ruby -rubygems timeout_server.rb -p 9128 # Note that curl requires all timeouts to be integers - # curl_easy_setopt does not have a provision for floating-point values class TestCurbTimeouts < Test::Unit::TestCase def test_no_timeout_by_default curl = Curl::Easy.new(wait_url(2)) start = Time.now assert_equal true, curl.http_get elapsed = Time.now - start assert elapsed > 2 end def test_overall_timeout_on_dead_transfer curl = Curl::Easy.new(wait_url(2)) curl.timeout = 1 exception = assert_raise(Curl::Err::TimeoutError) do curl.http_get end assert_match( /^Timeout was reached: Operation timed out after/, exception.message ) end def test_overall_timeout_ms_on_dead_transfer curl = Curl::Easy.new(wait_url(2)) curl.timeout_ms = 1000 assert_raise(Curl::Err::TimeoutError) do curl.http_get end end def test_clearing_timeout curl = Curl::Easy.new(wait_url(2)) curl.timeout = 1 curl.timeout = nil start = Time.now assert_equal true, curl.http_get elapsed = Time.now - start assert elapsed > 2 end def test_overall_timeout_on_slow_transfer curl = Curl::Easy.new(serve_url(100, 2, 3)) curl.timeout = 1 # transfer is aborted despite data being exchanged exception = assert_raise(Curl::Err::TimeoutError) do curl.http_get end assert_match( /^Timeout was reached: Operation timed out after/, exception.message ) end def test_low_speed_time_on_slow_transfer curl = Curl::Easy.new(serve_url(100, 1, 3)) curl.low_speed_time = 2 # use default low_speed_limit of 1 assert_equal true, curl.http_get end def test_low_speed_time_on_very_slow_transfer # send data slower than required curl = Curl::Easy.new(serve_url(10, 2, 3)) curl.low_speed_time = 1 # XXX for some reason this test fails if low speed limit is not specified curl.low_speed_limit = 1 # use default low_speed_limit of 1 exception = assert_raise(Curl::Err::TimeoutError) do curl.http_get end assert_match( /^Timeout was reached: Operation too slow/, exception.message ) end def test_low_speed_limit_on_slow_transfer curl = Curl::Easy.new(serve_url(10, 1, 3)) curl.low_speed_time = 2 curl.low_speed_limit = 1000 exception = assert_raise(Curl::Err::TimeoutError) do curl.http_get end assert_match( /^Timeout was reached: Operation too slow/, exception.message ) end def test_clearing_low_speed_time curl = Curl::Easy.new(serve_url(100, 2, 3)) curl.low_speed_time = 1 curl.low_speed_time = nil assert_equal true, curl.http_get end def test_clearing_low_speed_limit curl = Curl::Easy.new(serve_url(10, 1, 3)) curl.low_speed_time = 2 curl.low_speed_limit = 1000 curl.low_speed_limit = nil assert_equal true, curl.http_get end private def wait_url(time) "#{server_base}/wait/#{time}" end def serve_url(chunk_size, time, count) "#{server_base}/serve/#{chunk_size}/every/#{time}/for/#{count}" end def server_base 'http://127.0.0.1:9128' end end curb-1.0.5/LICENSE0000644000004100000410000000426014362470347013521 0ustar www-datawww-dataCopyright (c) 2006 Ross Bamford (rosco AT roscopeco DOT co DOT uk). Curb is free software licensed under the following terms: 1. You may make and give away verbatim copies of the source form of the software without restriction, provided that you duplicate all of the original copyright notices and associated disclaimers. 2. You may modify your copy of the software in any way, provided that you do at least ONE of the following: a) place your modifications in the Public Domain or otherwise make them Freely Available, such as by posting said modifications to Usenet or an equivalent medium, or by allowing the author to include your modifications in the software. b) use the modified software only within your corporation or organization. c) give non-standard binaries non-standard names, with instructions on where to get the original software distribution. d) make other distribution arrangements with the author. 3. You may distribute the software in object code or binary form, provided that you do at least ONE of the following: a) distribute the binaries and library files of the software, together with instructions (in the manual page or equivalent) on where to get the original distribution. b) accompany the distribution with the machine-readable source of the software. c) give non-standard binaries non-standard names, with instructions on where to get the original software distribution. d) make other distribution arrangements with the author. 4. You may modify and include the part of the software into any other software (possibly commercial). 5. The scripts and library files supplied as input to or produced as output from the software do not automatically fall under the copyright of the software, but belong to whomever generated them, and may be sold commercially, and may be aggregated with this software. 6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. curb-1.0.5/curb.gemspec0000644000004100000410000001007514362470347015015 0ustar www-datawww-data######################################################### # This file has been automatically generated by gem2tgz # ######################################################### # -*- encoding: utf-8 -*- # stub: curb 1.0.5 ruby libext # stub: ext/extconf.rb Gem::Specification.new do |s| s.name = "curb".freeze s.version = "1.0.5" s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version= s.require_paths = ["lib".freeze, "ext".freeze] s.authors = ["Ross Bamford".freeze, "Todd A. Fisher".freeze] s.date = "2023-01-04" s.description = "Curb (probably CUrl-RuBy or something) provides Ruby-language bindings for the libcurl(3), a fully-featured client-side URL transfer library. cURL and libcurl live at http://curl.haxx.se/".freeze s.email = "todd.fisher@gmail.com".freeze s.extensions = ["ext/extconf.rb".freeze] s.extra_rdoc_files = ["LICENSE".freeze, "README.markdown".freeze] s.files = ["LICENSE".freeze, "README.markdown".freeze, "Rakefile".freeze, "doc.rb".freeze, "ext/banned.h".freeze, "ext/curb.c".freeze, "ext/curb.h".freeze, "ext/curb_easy.c".freeze, "ext/curb_easy.h".freeze, "ext/curb_errors.c".freeze, "ext/curb_errors.h".freeze, "ext/curb_macros.h".freeze, "ext/curb_multi.c".freeze, "ext/curb_multi.h".freeze, "ext/curb_postfield.c".freeze, "ext/curb_postfield.h".freeze, "ext/curb_upload.c".freeze, "ext/curb_upload.h".freeze, "ext/extconf.rb".freeze, "lib/curb.rb".freeze, "lib/curl.rb".freeze, "lib/curl/easy.rb".freeze, "lib/curl/multi.rb".freeze, "tests/alltests.rb".freeze, "tests/bug_crash_on_debug.rb".freeze, "tests/bug_crash_on_progress.rb".freeze, "tests/bug_curb_easy_blocks_ruby_threads.rb".freeze, "tests/bug_curb_easy_post_with_string_no_content_length_header.rb".freeze, "tests/bug_follow_redirect_288.rb".freeze, "tests/bug_instance_post_differs_from_class_post.rb".freeze, "tests/bug_issue102.rb".freeze, "tests/bug_issue277.rb".freeze, "tests/bug_multi_segfault.rb".freeze, "tests/bug_postfields_crash.rb".freeze, "tests/bug_postfields_crash2.rb".freeze, "tests/bug_raise_on_callback.rb".freeze, "tests/bug_require_last_or_segfault.rb".freeze, "tests/bugtests.rb".freeze, "tests/helper.rb".freeze, "tests/mem_check.rb".freeze, "tests/require_last_or_segfault_script.rb".freeze, "tests/signals.rb".freeze, "tests/tc_curl.rb".freeze, "tests/tc_curl_download.rb".freeze, "tests/tc_curl_easy.rb".freeze, "tests/tc_curl_easy_resolve.rb".freeze, "tests/tc_curl_easy_setopt.rb".freeze, "tests/tc_curl_maxfilesize.rb".freeze, "tests/tc_curl_multi.rb".freeze, "tests/tc_curl_postfield.rb".freeze, "tests/tc_curl_protocols.rb".freeze, "tests/timeout.rb".freeze, "tests/timeout_server.rb".freeze, "tests/unittests.rb".freeze] s.homepage = "https://github.com/taf2/curb".freeze s.licenses = ["MIT".freeze] s.rdoc_options = ["--main".freeze, "README.markdown".freeze] s.rubygems_version = "3.2.5".freeze s.summary = "Ruby libcurl bindings".freeze s.test_files = ["tests/alltests.rb".freeze, "tests/bug_crash_on_debug.rb".freeze, "tests/bug_crash_on_progress.rb".freeze, "tests/bug_curb_easy_blocks_ruby_threads.rb".freeze, "tests/bug_curb_easy_post_with_string_no_content_length_header.rb".freeze, "tests/bug_follow_redirect_288.rb".freeze, "tests/bug_instance_post_differs_from_class_post.rb".freeze, "tests/bug_issue102.rb".freeze, "tests/bug_issue277.rb".freeze, "tests/bug_multi_segfault.rb".freeze, "tests/bug_postfields_crash.rb".freeze, "tests/bug_postfields_crash2.rb".freeze, "tests/bug_raise_on_callback.rb".freeze, "tests/bug_require_last_or_segfault.rb".freeze, "tests/bugtests.rb".freeze, "tests/helper.rb".freeze, "tests/mem_check.rb".freeze, "tests/require_last_or_segfault_script.rb".freeze, "tests/signals.rb".freeze, "tests/tc_curl.rb".freeze, "tests/tc_curl_download.rb".freeze, "tests/tc_curl_easy.rb".freeze, "tests/tc_curl_easy_resolve.rb".freeze, "tests/tc_curl_easy_setopt.rb".freeze, "tests/tc_curl_maxfilesize.rb".freeze, "tests/tc_curl_multi.rb".freeze, "tests/tc_curl_postfield.rb".freeze, "tests/tc_curl_protocols.rb".freeze, "tests/timeout.rb".freeze, "tests/timeout_server.rb".freeze, "tests/unittests.rb".freeze] end curb-1.0.5/Rakefile0000644000004100000410000002240214362470347014157 0ustar www-datawww-data# $Id$ # require 'rake/clean' require 'rake/testtask' require "ruby_memcheck" CLEAN.include '**/*.o' CLEAN.include "**/*.#{(defined?(RbConfig) ? RbConfig : Config)::MAKEFILE_CONFIG['DLEXT']}" CLOBBER.include 'doc' CLOBBER.include '**/*.log' CLOBBER.include '**/Makefile' CLOBBER.include '**/extconf.h' # Load support ruby and rake files (in this order) Dir.glob('tasks/*.rb').each { |r| load r} Dir.glob('tasks/*.rake').each { |r| load r} desc 'Print Ruby major version (ie "2_5")' task :ruby_version do print current_ruby_major end def announce(msg='') $stderr.puts msg end desc "Default Task (Test project)" task :default => :test # Determine the current version of the software if File.read('ext/curb.h') =~ /\s*CURB_VERSION\s*['"](\d.+)['"]/ CURRENT_VERSION = $1 else CURRENT_VERSION = "0.0.0" end if ENV['REL'] PKG_VERSION = ENV['REL'] else PKG_VERSION = CURRENT_VERSION end task :test_ver do puts PKG_VERSION end # Make tasks ----------------------------------------------------- make_program = (/mswin/ =~ RUBY_PLATFORM) ? 'nmake' : 'make' MAKECMD = ENV['MAKE_CMD'] || make_program MAKEOPTS = ENV['MAKE_OPTS'] || '' CURB_SO = "ext/curb_core.#{(defined?(RbConfig) ? RbConfig : Config)::MAKEFILE_CONFIG['DLEXT']}" file 'ext/Makefile' => 'ext/extconf.rb' do shell(['ruby', 'extconf.rb', ENV['EXTCONF_OPTS'].to_s], { :live_stdout => STDOUT , :cwd => "#{Dir.pwd}/ext" } ).error! end def make(target = '') shell(["#{MAKECMD}", "#{MAKEOPTS}", "#{target}"].reject(&:empty?), { :live_stdout => STDOUT, :cwd => "#{Dir.pwd}/ext" } ).error! end # Let make handle dependencies between c/o/so - we'll just run it. file CURB_SO => (['ext/Makefile'] + Dir['ext/*.c'] + Dir['ext/*.h']) do make end desc "Compile the shared object" task :compile => [CURB_SO] desc "Install to your site_ruby directory" task :install do make 'install' end # Test Tasks --------------------------------------------------------- task :ta => :alltests task :tu => :unittests task :test => [:rmpid,:unittests] task :rmpid do FileUtils.rm_rf Dir.glob("tests/server_lock-*") end if ENV['RELTEST'] announce "Release task testing - not running regression tests on alltests" task :alltests => [:unittests] else task :alltests => [:unittests, :bugtests] end RubyMemcheck.config(binary_name: 'curb_core') namespace :test do RubyMemcheck::TestTask.new(valgrind: :compile) do|t| t.test_files = FileList['tests/tc_*.rb'] t.verbose = false end end Rake::TestTask.new(:unittests) do |t| t.test_files = FileList['tests/tc_*.rb'] t.verbose = false end Rake::TestTask.new(:bugtests) do |t| t.test_files = FileList['tests/bug_*.rb'] t.verbose = false end #Rake::TestTask.new(:funtests) do |t| # t.test_files = FileList['test/func_*.rb'] #t.warning = true #t.warning = true #end task :unittests => :compile task :bugtests => :compile def has_gem?(file,name) begin require file has_http_persistent = true rescue LoadError => e puts "Skipping #{name}" end end desc "Benchmark curl against http://127.0.0.1/zeros-2k - will fail if /zeros-2k or 127.0.0.1 are missing" task :bench do sh "ruby bench/curb_easy.rb" sh "ruby bench/curb_multi.rb" sh "ruby bench/nethttp_test.rb" if has_gem?("net/http/persistent","net-http-persistent") sh "ruby bench/patron_test.rb" if has_gem?("patron","patron") sh "ruby bench/typhoeus_test.rb" if has_gem?("typhoeus","typhoeus") sh "ruby bench/typhoeus_hydra_test.rb" if has_gem?("typhoeus","typhoeus") end # RDoc Tasks --------------------------------------------------------- desc "Create the RDOC documentation" task :doc do ruby "doc.rb #{ENV['DOC_OPTS']}" end desc "Publish the RDoc documentation to project web site" task :doc_upload => [ :doc ] do begin require 'rdoc/task' rescue LoadError => e require 'rake/rdoctask' end if ENV['RELTEST'] announce "Release Task Testing, skipping doc upload" else unless ENV['RUBYFORGE_ACCT'] raise "Need to set RUBYFORGE_ACCT to your rubyforge.org user name (e.g. 'fred')" end require 'rake/contrib/sshpublisher' Rake::SshDirPublisher.new( "#{ENV['RUBYFORGE_ACCT']}@rubyforge.org", "/var/www/gforge-projects/curb", "doc" ).upload end end if ! defined?(Gem) warn "Package Target requires RubyGEMs" else desc 'Generate gem specification' task :gemspec do require 'erb' tspec = ERB.new(File.read(File.join(File.dirname(__FILE__),'lib','curb.gemspec.erb'))) File.open(File.join(File.dirname(__FILE__),'curb.gemspec'),'wb') do|f| f << tspec.result end end desc 'Build gem' task :package => :gemspec do require 'rubygems/package' spec_source = File.read File.join(File.dirname(__FILE__),'curb.gemspec') spec = nil # see: http://gist.github.com/16215 Thread.new { spec = eval("#{spec_source}") }.join spec.validate Gem::Package.build(spec) end task :static do ENV['STATIC_BUILD'] = '1' end task :binary_gemspec => [:static, :compile] do require 'erb' ENV['BINARY_PACKAGE'] = '1' tspec = ERB.new(File.read(File.join(File.dirname(__FILE__),'lib','curb.gemspec.erb'))) File.open(File.join(File.dirname(__FILE__),'curb-binary.gemspec'),'wb') do|f| f << tspec.result end end desc 'Strip extra strings from Binary' task :binary_strip do strip = '/usr/bin/strip' if File.exist?(strip) and `#{strip} -h 2>&1`.match(/GNU/) sh "#{strip} #{CURB_SO}" end end desc 'Build gem' task :binary_package => [:binary_gemspec, :binary_strip] do require 'rubygems/specification' spec_source = File.read File.join(File.dirname(__FILE__),'curb-binary.gemspec') spec = nil # see: http://gist.github.com/16215 Thread.new { spec = eval("$SAFE = 3\n#{spec_source}") }.join spec.validate Gem::Builder.new(spec).build end end # -------------------------------------------------------------------- # Creating a release desc "Make a new release (Requires SVN commit / webspace access)" task :release => [ :prerelease, :clobber, :alltests, :update_version, :package, :tag, :doc_upload] do announce announce "**************************************************************" announce "* Release #{PKG_VERSION} Complete." announce "* Packages ready to upload." announce "**************************************************************" announce end # Validate that everything is ready to go for a release. task :prerelease do announce announce "**************************************************************" announce "* Making RubyGem Release #{PKG_VERSION}" announce "* (current version #{CURRENT_VERSION})" announce "**************************************************************" announce # Is a release number supplied? unless ENV['REL'] fail "Usage: rake release REL=x.y.z [REUSE=tag_suffix]" end # Is the release different than the current release. # (or is REUSE set?) if PKG_VERSION == CURRENT_VERSION && ! ENV['REUSE'] fail "Current version is #{PKG_VERSION}, must specify REUSE=tag_suffix to reuse version" end # Are all source files checked in? if ENV['RELTEST'] announce "Release Task Testing, skipping checked-in file test" else announce "Checking for unchecked-in files..." data = `svn status` unless data =~ /^$/ fail "SVN status is not clean ... do you have unchecked-in files?" end announce "No outstanding checkins found ... OK" end announce "Doc will try to use GNU cpp if available" ENV['DOC_OPTS'] = "--cpp" end # Used during release packaging if a REL is supplied task :update_version do unless PKG_VERSION == CURRENT_VERSION pkg_vernum = PKG_VERSION.tr('.','').sub(/^0*/,'') pkg_vernum << '0' until pkg_vernum.length > 2 File.open('ext/curb.h.new','w+') do |f| maj, min, mic, patch = /(\d+)\.(\d+)(?:\.(\d+))?(?:\.(\d+))?/.match(PKG_VERSION).captures f << File.read('ext/curb.h'). gsub(/CURB_VERSION\s+"(\d.+)"/) { "CURB_VERSION \"#{PKG_VERSION}\"" }. gsub(/CURB_VER_NUM\s+\d+/) { "CURB_VER_NUM #{pkg_vernum}" }. gsub(/CURB_VER_MAJ\s+\d+/) { "CURB_VER_MAJ #{maj}" }. gsub(/CURB_VER_MIN\s+\d+/) { "CURB_VER_MIN #{min}" }. gsub(/CURB_VER_MIC\s+\d+/) { "CURB_VER_MIC #{mic || 0}" }. gsub(/CURB_VER_PATCH\s+\d+/) { "CURB_VER_PATCH #{patch || 0}" } end mv('ext/curb.h.new', 'ext/curb.h') if ENV['RELTEST'] announce "Release Task Testing, skipping commiting of new version" else sh %{svn commit -m "Updated to version #{PKG_VERSION}" ext/curb.h} end end end # "Create a new SVN tag with the latest release number (REL=x.y.z)" task :tag => [:prerelease] do reltag = "curb-#{PKG_VERSION}" reltag << ENV['REUSE'] if ENV['REUSE'] announce "Tagging SVN with [#{reltag}]" if ENV['RELTEST'] announce "Release Task Testing, skipping SVN tagging" else # need to get current base URL s = `svn info` if s =~ /URL:\s*([^\n]*)\n/ svnroot = $1 if svnroot =~ /^(.*)\/trunk/i svnbase = $1 sh %{svn cp #{svnroot} #{svnbase}/TAGS/#{reltag} -m "Release #{PKG_VERSION}"} else fail "Please merge to trunk before making a release" end else fail "Unable to determine repository URL from 'svn info' - is this a working copy?" end end end curb-1.0.5/lib/0000755000004100000410000000000014362470347013260 5ustar www-datawww-datacurb-1.0.5/lib/curl/0000755000004100000410000000000014362470347014225 5ustar www-datawww-datacurb-1.0.5/lib/curl/easy.rb0000644000004100000410000003506114362470347015520 0ustar www-datawww-data# frozen_string_literal: true module Curl class Easy alias post http_post alias put http_put alias body body_str alias head header_str class Error < StandardError attr_accessor :message, :code def initialize(code, msg) self.message = msg self.code = code end end # # call-seq: # easy.status => String # def status # Matches the last HTTP Status - following the HTTP protocol specification 'Status-Line = HTTP-Version SP Status-Code SP (Opt:)Reason-Phrase CRLF' statuses = self.header_str.to_s.scan(/HTTP\/\d(\.\d)?\s(\d+\s.*)\r\n/).map {|match| match[1] } statuses.last.strip if statuses.length > 0 end # # call-seq: # easy.set :sym|Fixnum, value # # set options on the curl easy handle see http://curl.haxx.se/libcurl/c/curl_easy_setopt.html # def set(opt,val) if opt.is_a?(Symbol) option = sym2curl(opt) else option = opt.to_i end begin setopt(option, val) rescue TypeError raise TypeError, "Curb doesn't support setting #{opt} [##{option}] option" end end # # call-seq: # easy.sym2curl :symbol => Fixnum # # translates ruby symbols to libcurl options # def sym2curl(opt) Curl.const_get("CURLOPT_#{opt.to_s.upcase}") end # # call-seq: # easy.perform => true # # Transfer the currently configured URL using the options set for this # Curl::Easy instance. If this is an HTTP URL, it will be transferred via # the configured HTTP Verb. # def perform self.multi = Curl::Multi.new if self.multi.nil? self.multi.add self ret = self.multi.perform self.multi.remove self if Curl::Multi.autoclose self.multi.close self.multi = nil end if self.last_result != 0 && self.on_failure.nil? (err_class, err_summary) = Curl::Easy.error(self.last_result) err_detail = self.last_error raise err_class.new([err_summary, err_detail].compact.join(": ")) end ret end # # call-seq: # # easy = Curl::Easy.new # easy.nosignal = true # def nosignal=(onoff) set :nosignal, !!onoff end # # call-seq: # easy = Curl::Easy.new("url") do|c| # c.delete = true # end # easy.perform # def delete=(onoff) set :customrequest, onoff ? 'DELETE' : nil onoff end # # call-seq: # # easy = Curl::Easy.new("url") # easy.version = Curl::HTTP_2_0 # easy.version = Curl::HTTP_1_1 # easy.version = Curl::HTTP_1_0 # easy.version = Curl::HTTP_NONE # def version=(http_version) set :http_version, http_version end # # call-seq: # easy.url = "http://some.url/" => "http://some.url/" # # Set the URL for subsequent calls to +perform+. It is acceptable # (and even recommended) to reuse Curl::Easy instances by reassigning # the URL between calls to +perform+. # def url=(u) set :url, u end # # call-seq: # easy.proxy_url = string => string # # Set the URL of the HTTP proxy to use for subsequent calls to +perform+. # The URL should specify the the host name or dotted IP address. To specify # port number in this string, append :[port] to the end of the host name. # The proxy string may be prefixed with [protocol]:// since any such prefix # will be ignored. The proxy's port number may optionally be specified with # the separate option proxy_port . # # When you tell the library to use an HTTP proxy, libcurl will transparently # convert operations to HTTP even if you specify an FTP URL etc. This may have # an impact on what other features of the library you can use, such as # FTP specifics that don't work unless you tunnel through the HTTP proxy. Such # tunneling is activated with proxy_tunnel = true. # # libcurl respects the environment variables *http_proxy*, *ftp_proxy*, # *all_proxy* etc, if any of those is set. The proxy_url option does however # override any possibly set environment variables. # # Starting with libcurl 7.14.1, the proxy host string given in environment # variables can be specified the exact same way as the proxy can be set with # proxy_url, including protocol prefix (http://) and embedded user + password. # def proxy_url=(url) set :proxy, url end def ssl_verify_host=(value) value = 1 if value.class == TrueClass value = 0 if value.class == FalseClass self.ssl_verify_host_integer=value end # # call-seq: # easy.ssl_verify_host? => boolean # # Deprecated: call easy.ssl_verify_host instead # can be one of [0,1,2] # # Determine whether this Curl instance will verify that the server cert # is for the server it is known as. # def ssl_verify_host? ssl_verify_host.nil? ? false : (ssl_verify_host > 0) end # # call-seq: # easy.interface = string => string # # Set the interface name to use as the outgoing network interface. # The name can be an interface name, an IP address or a host name. # def interface=(value) set :interface, value end # # call-seq: # easy.userpwd = string => string # # Set the username/password string to use for subsequent calls to +perform+. # The supplied string should have the form "username:password" # def userpwd=(value) set :userpwd, value end # # call-seq: # easy.proxypwd = string => string # # Set the username/password string to use for proxy connection during # subsequent calls to +perform+. The supplied string should have the # form "username:password" # def proxypwd=(value) set :proxyuserpwd, value end # # call-seq: # easy.cookies = "name1=content1; name2=content2;" => string # # Set cookies to be sent by this Curl::Easy instance. The format of the string should # be NAME=CONTENTS, where NAME is the cookie name and CONTENTS is what the cookie should contain. # Set multiple cookies in one string like this: "name1=content1; name2=content2;" etc. # def cookies=(value) set :cookie, value end # # call-seq: # easy.cookiefile = string => string # # Set a file that contains cookies to be sent in subsequent requests by this Curl::Easy instance. # # *Note* that you must set enable_cookies true to enable the cookie # engine, or this option will be ignored. # def cookiefile=(value) set :cookiefile, value end # # call-seq: # easy.cookiejar = string => string # # Set a cookiejar file to use for this Curl::Easy instance. # Cookies from the response will be written into this file. # # *Note* that you must set enable_cookies true to enable the cookie # engine, or this option will be ignored. # def cookiejar=(value) set :cookiejar, value end # # call-seq: # easy = Curl::Easy.new("url") do|c| # c.head = true # end # easy.perform # def head=(onoff) set :nobody, onoff end # # call-seq: # easy.follow_location = boolean => boolean # # Configure whether this Curl instance will follow Location: headers # in HTTP responses. Redirects will only be followed to the extent # specified by +max_redirects+. # def follow_location=(onoff) set :followlocation, onoff end # # call-seq: # easy.http_head => true # # Request headers from the currently configured URL using the HEAD # method and current options set for this Curl::Easy instance. This # method always returns true, or raises an exception (defined under # Curl::Err) on error. # def http_head set :nobody, true ret = self.perform set :nobody, false ret end # # call-seq: # easy.http_get => true # # GET the currently configured URL using the current options set for # this Curl::Easy instance. This method always returns true, or raises # an exception (defined under Curl::Err) on error. # def http_get set :httpget, true http :GET end alias get http_get # # call-seq: # easy.http_delete # # DELETE the currently configured URL using the current options set for # this Curl::Easy instance. This method always returns true, or raises # an exception (defined under Curl::Err) on error. # def http_delete self.http :DELETE end alias delete http_delete class << self # # call-seq: # Curl::Easy.perform(url) { |easy| ... } => # # # Convenience method that creates a new Curl::Easy instance with # the specified URL and calls the general +perform+ method, before returning # the new instance. For HTTP URLs, this is equivalent to calling +http_get+. # # If a block is supplied, the new instance will be yielded just prior to # the +http_get+ call. # def perform(*args) c = Curl::Easy.new(*args) yield c if block_given? c.perform c end # # call-seq: # Curl::Easy.http_get(url) { |easy| ... } => # # # Convenience method that creates a new Curl::Easy instance with # the specified URL and calls +http_get+, before returning the new instance. # # If a block is supplied, the new instance will be yielded just prior to # the +http_get+ call. # def http_get(*args) c = Curl::Easy.new(*args) yield c if block_given? c.http_get c end # # call-seq: # Curl::Easy.http_head(url) { |easy| ... } => # # # Convenience method that creates a new Curl::Easy instance with # the specified URL and calls +http_head+, before returning the new instance. # # If a block is supplied, the new instance will be yielded just prior to # the +http_head+ call. # def http_head(*args) c = Curl::Easy.new(*args) yield c if block_given? c.http_head c end # # call-seq: # Curl::Easy.http_put(url, data) {|c| ... } # # see easy.http_put # def http_put(url, data) c = Curl::Easy.new url yield c if block_given? c.http_put data c end # # call-seq: # Curl::Easy.http_post(url, "some=urlencoded%20form%20data&and=so%20on") => true # Curl::Easy.http_post(url, "some=urlencoded%20form%20data", "and=so%20on", ...) => true # Curl::Easy.http_post(url, "some=urlencoded%20form%20data", Curl::PostField, "and=so%20on", ...) => true # Curl::Easy.http_post(url, Curl::PostField, Curl::PostField ..., Curl::PostField) => true # # POST the specified formdata to the currently configured URL using # the current options set for this Curl::Easy instance. This method # always returns true, or raises an exception (defined under # Curl::Err) on error. # # If you wish to use multipart form encoding, you'll need to supply a block # in order to set multipart_form_post true. See #http_post for more # information. # def http_post(*args) url = args.shift c = Curl::Easy.new url yield c if block_given? c.http_post(*args) c end # # call-seq: # Curl::Easy.http_delete(url) { |easy| ... } => # # # Convenience method that creates a new Curl::Easy instance with # the specified URL and calls +http_delete+, before returning the new instance. # # If a block is supplied, the new instance will be yielded just prior to # the +http_delete+ call. # def http_delete(*args) c = Curl::Easy.new(*args) yield c if block_given? c.http_delete c end # call-seq: # Curl::Easy.download(url, filename = url.split(/\?/).first.split(/\//).last) { |curl| ... } # # Stream the specified url (via perform) and save the data directly to the # supplied filename (defaults to the last component of the URL path, which will # usually be the filename most simple urls). # # If a block is supplied, it will be passed the curl instance prior to the # perform call. # # *Note* that the semantics of the on_body handler are subtly changed when using # download, to account for the automatic routing of data to the specified file: The # data string is passed to the handler *before* it is written # to the file, allowing the handler to perform mutative operations where # necessary. As usual, the transfer will be aborted if the on_body handler # returns a size that differs from the data chunk size - in this case, the # offending chunk will *not* be written to the file, the file will be closed, # and a Curl::Err::AbortedByCallbackError will be raised. def download(url, filename = url.split(/\?/).first.split(/\//).last, &blk) curl = Curl::Easy.new(url, &blk) output = if filename.is_a? IO filename.binmode if filename.respond_to?(:binmode) filename else File.open(filename, 'wb') end begin old_on_body = curl.on_body do |data| result = old_on_body ? old_on_body.call(data) : data.length output << data if result == data.length result end curl.perform ensure output.close rescue IOError end return curl end end # Allow the incoming cert string to be file:password # but be careful to not use a colon from a windows file path # as the split point. Mimic what curl's main does if respond_to?(:cert=) alias_method :native_cert=, :cert= def cert=(cert_file) pos = cert_file.rindex(':') if pos && pos > 1 self.native_cert= cert_file[0..pos-1] self.certpassword= cert_file[pos+1..-1] else self.native_cert= cert_file end self.cert end end end end curb-1.0.5/lib/curl/multi.rb0000644000004100000410000002247714362470347015720 0ustar www-datawww-data# frozen_string_literal: true module Curl class Multi class DownloadError < RuntimeError attr_accessor :errors end class << self # call-seq: # Curl::Multi.get(['url1','url2','url3','url4','url5'], :follow_location => true) do|easy| # easy # end # # Blocking call to fetch multiple url's in parallel. def get(urls, easy_options={}, multi_options={}, &blk) url_confs = [] urls.each do|url| url_confs << {:url => url, :method => :get}.merge(easy_options) end self.http(url_confs, multi_options) {|c,code,method| blk.call(c) if blk } end # call-seq: # # Curl::Multi.post([{:url => 'url1', :post_fields => {'field1' => 'value1', 'field2' => 'value2'}}, # {:url => 'url2', :post_fields => {'field1' => 'value1', 'field2' => 'value2'}}, # {:url => 'url3', :post_fields => {'field1' => 'value1', 'field2' => 'value2'}}], # { :follow_location => true, :multipart_form_post => true }, # {:pipeline => Curl::CURLPIPE_HTTP1}) do|easy| # easy_handle_on_request_complete # end # # Blocking call to POST multiple form's in parallel. # # urls_with_config: is a hash of url's pointing to the postfields to send # easy_options: are a set of common options to set on all easy handles # multi_options: options to set on the Curl::Multi handle # def post(urls_with_config, easy_options={}, multi_options={}, &blk) url_confs = [] urls_with_config.each do|uconf| url_confs << uconf.merge(:method => :post).merge(easy_options) end self.http(url_confs, multi_options) {|c,code,method| blk.call(c) } end # call-seq: # # Curl::Multi.put([{:url => 'url1', :put_data => "some message"}, # {:url => 'url2', :put_data => IO.read('filepath')}, # {:url => 'url3', :put_data => "maybe another string or socket?"], # {:follow_location => true}, # {:pipeline => Curl::CURLPIPE_HTTP1}) do|easy| # easy_handle_on_request_complete # end # # Blocking call to POST multiple form's in parallel. # # urls_with_config: is a hash of url's pointing to the postfields to send # easy_options: are a set of common options to set on all easy handles # multi_options: options to set on the Curl::Multi handle # def put(urls_with_config, easy_options={}, multi_options={}, &blk) url_confs = [] urls_with_config.each do|uconf| url_confs << uconf.merge(:method => :put).merge(easy_options) end self.http(url_confs, multi_options) {|c,code,method| blk.call(c) } end # call-seq: # # Curl::Multi.http( [ # { :url => 'url1', :method => :post, # :post_fields => {'field1' => 'value1', 'field2' => 'value2'} }, # { :url => 'url2', :method => :get, # :follow_location => true, :max_redirects => 3 }, # { :url => 'url3', :method => :put, :put_data => File.open('file.txt','rb') }, # { :url => 'url4', :method => :head } # ], {:pipeline => Curl::CURLPIPE_HTTP1}) # # Blocking call to issue multiple HTTP requests with varying verb's. # # urls_with_config: is a hash of url's pointing to the easy handle options as well as the special option :method, that can by one of [:get, :post, :put, :delete, :head], when no verb is provided e.g. :method => nil -> GET is used # multi_options: options for the multi handle # blk: a callback, that yeilds when a handle is completed # def http(urls_with_config, multi_options={}, &blk) m = Curl::Multi.new # maintain a sane number of easy handles multi_options[:max_connects] = max_connects = multi_options.key?(:max_connects) ? multi_options[:max_connects] : 10 free_handles = [] # keep a list of free easy handles # configure the multi handle multi_options.each { |k,v| m.send("#{k}=", v) } callbacks = [:on_progress,:on_debug,:on_failure,:on_success,:on_redirect,:on_body,:on_header] add_free_handle = proc do|conf, easy| c = conf.dup # avoid being destructive to input url = c.delete(:url) method = c.delete(:method) headers = c.delete(:headers) easy = Curl::Easy.new if easy.nil? easy.url = url # assign callbacks callbacks.each do |cb| cbproc = c.delete(cb) easy.send(cb,&cbproc) if cbproc end case method when :post fields = c.delete(:post_fields) # set the post post using the url fields easy.post_body = fields.map{|f,k| "#{easy.escape(f)}=#{easy.escape(k)}"}.join('&') when :put easy.put_data = c.delete(:put_data) when :head easy.head = true when :delete easy.delete = true when :get else # XXX: nil is treated like a GET end # headers is a special key headers.each {|k,v| easy.headers[k] = v } if headers # # use the remaining options as specific configuration to the easy handle # bad options should raise an undefined method error # c.each { |k,v| easy.send("#{k}=",v) } easy.on_complete {|curl| free_handles << curl blk.call(curl,curl.response_code,method) if blk } m.add(easy) end max_connects.times do conf = urls_with_config.pop add_free_handle.call(conf, nil) if conf break if urls_with_config.empty? end consume_free_handles = proc do # as we idle consume free handles if urls_with_config.size > 0 && free_handles.size > 0 easy = free_handles.pop conf = urls_with_config.pop add_free_handle.call(conf, easy) if conf end end if urls_with_config.empty? m.perform else until urls_with_config.empty? m.perform do consume_free_handles.call end consume_free_handles.call end free_handles = nil end end # call-seq: # # Curl::Multi.download(['http://example.com/p/a/t/h/file1.txt','http://example.com/p/a/t/h/file2.txt']){|c|} # # will create 2 new files file1.txt and file2.txt # # 2 files will be opened, and remain open until the call completes # # when using the :post or :put method, urls should be a hash, including the individual post fields per post # def download(urls,easy_options={},multi_options={},download_paths=nil,&blk) errors = [] procs = [] files = [] urls_with_config = [] url_to_download_paths = {} urls.each_with_index do|urlcfg,i| if urlcfg.is_a?(Hash) url = url[:url] else url = urlcfg end if download_paths and download_paths[i] download_path = download_paths[i] else download_path = File.basename(url) end file = lambda do|dp| file = File.open(dp,"wb") procs << (lambda {|data| file.write data; data.size }) files << file file end.call(download_path) if urlcfg.is_a?(Hash) urls_with_config << urlcfg.merge({:on_body => procs.last}.merge(easy_options)) else urls_with_config << {:url => url, :on_body => procs.last, :method => :get}.merge(easy_options) end url_to_download_paths[url] = {:path => download_path, :file => file} # store for later end if blk # when injecting the block, ensure file is closed before yielding Curl::Multi.http(urls_with_config, multi_options) do |c,code,method| info = url_to_download_paths[c.url] begin file = info[:file] files.reject!{|f| f == file } file.close rescue => e errors << e end blk.call(c,info[:path]) end else Curl::Multi.http(urls_with_config, multi_options) end ensure files.each {|f| begin f.close rescue => e errors << e end } if errors.any? de = Curl::Multi::DownloadError.new de.errors = errors raise de end end end def cancel! requests.each do |_,easy| remove(easy) end end def idle? requests.empty? end def requests @requests ||= {} end def add(easy) return self if requests[easy.object_id] requests[easy.object_id] = easy _add(easy) self end def remove(easy) return self if !requests[easy.object_id] requests.delete(easy.object_id) _remove(easy) self end def close requests.values.each {|easy| _remove(easy) } @requests = {} _close self end end end curb-1.0.5/lib/curb.rb0000644000004100000410000000005514362470347014540 0ustar www-datawww-data# frozen_string_literal: true require 'curl' curb-1.0.5/lib/curl.rb0000644000004100000410000000364414362470347014561 0ustar www-datawww-data# frozen_string_literal: true require 'curb_core' require 'curl/easy' require 'curl/multi' require 'uri' require 'cgi' # expose shortcut methods module Curl def self.http(verb, url, post_body=nil, put_data=nil, &block) if Thread.current[:curb_curl_yielding] handle = Curl::Easy.new # we can't reuse this else handle = Thread.current[:curb_curl] ||= Curl::Easy.new handle.reset end handle.url = url handle.post_body = post_body if post_body handle.put_data = put_data if put_data if block_given? Thread.current[:curb_curl_yielding] = true yield handle Thread.current[:curb_curl_yielding] = false end handle.http(verb) handle end def self.get(url, params={}, &block) http :GET, urlalize(url, params), nil, nil, &block end def self.post(url, params={}, &block) http :POST, url, postalize(params), nil, &block end def self.put(url, params={}, &block) http :PUT, url, nil, postalize(params), &block end def self.delete(url, params={}, &block) http :DELETE, url, postalize(params), nil, &block end def self.patch(url, params={}, &block) http :PATCH, url, postalize(params), nil, &block end def self.head(url, params={}, &block) http :HEAD, urlalize(url, params), nil, nil, &block end def self.options(url, params={}, &block) http :OPTIONS, urlalize(url, params), nil, nil, &block end def self.urlalize(url, params={}) uri = URI(url) # early return if we didn't specify any extra params return uri.to_s if (params || {}).empty? params_query = URI.encode_www_form(params || {}) uri.query = [uri.query.to_s, params_query].reject(&:empty?).join('&') uri.to_s end def self.postalize(params={}) params.respond_to?(:map) ? URI.encode_www_form(params) : (params.respond_to?(:to_s) ? params.to_s : params) end def self.reset Thread.current[:curb_curl] = Curl::Easy.new end end curb-1.0.5/README.markdown0000644000004100000410000002045514362470347015221 0ustar www-datawww-data# Curb - Libcurl bindings for Ruby * [CI Build Status](https://github.com/taf2/curb/actions/workflows/CI.yml) * [rubydoc rdoc](http://www.rubydoc.info/github/taf2/curb/) * [github project](http://github.com/taf2/curb/tree/master) Curb (probably CUrl-RuBy or something) provides Ruby-language bindings for the libcurl(3), a fully-featured client-side URL transfer library. cURL and libcurl live at [https://curl.se/libcurl/](https://curl.se/libcurl/) . Curb is a work-in-progress, and currently only supports libcurl's `easy` and `multi` modes. ## License Curb is copyright (c) 2006 Ross Bamford, and released under the terms of the Ruby license. See the LICENSE file for the gory details. ## Easy mode GET request ``` res = Curl.get("https://www.google.com/") {|http| http.timeout = 10 # raise exception if request/response not handled within 10 seconds } puts res.code puts res.head puts res.body ``` POST request ``` res = Curl.post("https://your-server.com/endpoint", {post: "this"}.to_json) {|http| http.headers["Content-Type"] = "application/json" } puts res.code puts res.head puts res.body ``` PATCH request ``` res = Curl.patch("https://your-server.com/endpoint", {post: "this"}.to_json) {|http| http.headers["Content-Type"] = "application/json" } puts res.code puts res.head puts res.body ``` ## You will need * A working Ruby installation (`2.0.0+` will work but `2.1+` preferred) (it's possible it still works with 1.8.7 but you'd have to tell me if not...) * A working libcurl development installation (Ideally one of the versions listed in the compatibility chart below that maps to your `curb` version) * A sane build environment (e.g. gcc, make) ## Version Compatibility chart A **non-exhaustive** set of compatibility versions of the libcurl library with this gem are as follows. (Note that these are only the ones that have been tested and reported to work across a variety of platforms / rubies) | Gem Version | Release Date | libcurl versions | | ----------- | ----------- | ---------------- | | 1.0.0 | Jan 2022 | 7.58 - 7.81 | | 0.9.8 | Jan 2019 | 7.58 - 7.81 | | 0.9.7 | Nov 2018 | 7.56 - 7.60 | | 0.9.6 | May 2018 | 7.51 - 7.59 | | 0.9.5 | May 2018 | 7.51 - 7.59 | | 0.9.4 | Aug 2017 | 7.41 - 7.58 | | 0.9.3 | Apr 2016 | 7.26 - 7.58 | ## Installation... ... will usually be as simple as: $ gem install curb On Windows, make sure you're using the [DevKit](http://rubyinstaller.org/downloads/) and the [development version of libcurl](http://curl.se/gknw.net/7.39.0/dist-w32/curl-7.39.0-devel-mingw32.zip). Unzip, then run this in your command line (alter paths to your curl location, but remember to use forward slashes): gem install curb --platform=ruby -- --with-curl-lib=C:/curl-7.39.0-devel-mingw32/lib --with-curl-include=C:/curl-7.39.0-devel-mingw32/include Note that with Windows moving from one method of compiling to another as of Ruby `2.4` (DevKit -> MYSYS2), the usage of Ruby `2.4+` with this gem on windows is unlikely to work. It is advised to use the latest version of Ruby 2.3 available [HERE](https://dl.bintray.com/oneclick/rubyinstaller/rubyinstaller-2.3.3.exe) Or, if you downloaded the archive: $ rake compile && rake install If you have a weird setup, you might need extconf options. In this case, pass them like so: $ rake compile EXTCONF_OPTS='--with-curl-dir=/path/to/libcurl --prefix=/what/ever' && rake install Curb is tested only on GNU/Linux x86 and Mac OSX - YMMV on other platforms. If you do use another platform and experience problems, or if you can expand on the above instructions, please report the issue at http://github.com/taf2/curb/issues On Ubuntu, the dependencies can be satisfied by installing the following packages: 18.04 and onwards $ sudo apt-get install libcurl4 libcurl3-gnutls libcurl4-openssl-dev < 18.04 $ sudo apt-get install libcurl3 libcurl3-gnutls libcurl4-openssl-dev On RedHat: $ sudo yum install ruby-devel libcurl-devel openssl-devel Curb has fairly extensive RDoc comments in the source. You can build the documentation with: $ rake doc ## Usage & examples Curb provides two classes: * `Curl::Easy` - simple API, for day-to-day tasks. * `Curl::Multi` - more advanced API, for operating on multiple URLs simultaneously. To use either, you will need to require the curb gem: ```ruby require 'curb' ``` ### Super simple API (less typing) ```ruby http = Curl.get("http://www.google.com/") puts http.body_str http = Curl.post("http://www.google.com/", {:foo => "bar"}) puts http.body_str http = Curl.get("http://www.google.com/") do |http| http.headers['Cookie'] = 'foo=1;bar=2' end puts http.body_str ``` ### Simple fetch via HTTP: ```ruby c = Curl::Easy.perform("http://www.google.co.uk") puts c.body_str ``` Same thing, more manual: ```ruby c = Curl::Easy.new("http://www.google.co.uk") c.perform puts c.body_str ``` ### Additional config: ```ruby Curl::Easy.perform("http://www.google.co.uk") do |curl| curl.headers["User-Agent"] = "myapp-0.0" curl.verbose = true end ``` Same thing, more manual: ```ruby c = Curl::Easy.new("http://www.google.co.uk") do |curl| curl.headers["User-Agent"] = "myapp-0.0" curl.verbose = true end c.perform ``` ### HTTP basic authentication: ```ruby c = Curl::Easy.new("http://github.com/") c.http_auth_types = :basic c.username = 'foo' c.password = 'bar' c.perform ``` ### HTTP "insecure" SSL connections (like curl -k, --insecure) to avoid Curl::Err::SSLCACertificateError: ```ruby c = Curl::Easy.new("https://github.com/") c.ssl_verify_peer = false c.perform ``` ### Supplying custom handlers: ```ruby c = Curl::Easy.new("http://www.google.co.uk") c.on_body { |data| print(data) } c.on_header { |data| print(data) } c.perform ``` ### Reusing Curls: ```ruby c = Curl::Easy.new ["http://www.google.co.uk", "http://www.ruby-lang.org/"].map do |url| c.url = url c.perform c.body_str end ``` ### HTTP POST form: ```ruby c = Curl::Easy.http_post("http://my.rails.box/thing/create", Curl::PostField.content('thing[name]', 'box'), Curl::PostField.content('thing[type]', 'storage')) ``` ### HTTP POST file upload: ```ruby c = Curl::Easy.new("http://my.rails.box/files/upload") c.multipart_form_post = true c.http_post(Curl::PostField.file('thing[file]', 'myfile.rb')) ``` ### Using HTTP/2 ```ruby c = Curl::Easy.new("https://http2.akamai.com") c.set(:HTTP_VERSION, Curl::HTTP_2_0) c.perform puts (c.body_str.include? "You are using HTTP/2 right now!") ? "HTTP/2" : "HTTP/1.x" ``` ### Multi Interface (Basic HTTP GET): ```ruby # make multiple GET requests easy_options = {:follow_location => true} # Use Curl::CURLPIPE_MULTIPLEX for HTTP/2 multiplexing multi_options = {:pipeline => Curl::CURLPIPE_HTTP1} Curl::Multi.get(['url1','url2','url3','url4','url5'], easy_options, multi_options) do|easy| # do something interesting with the easy response puts easy.last_effective_url end ``` ### Multi Interface (Basic HTTP POST): ```ruby # make multiple POST requests easy_options = {:follow_location => true, :multipart_form_post => true} multi_options = {:pipeline => Curl::CURLPIPE_HTTP1} url_fields = [ { :url => 'url1', :post_fields => {'f1' => 'v1'} }, { :url => 'url2', :post_fields => {'f1' => 'v1'} }, { :url => 'url3', :post_fields => {'f1' => 'v1'} } ] Curl::Multi.post(url_fields, easy_options, multi_options) do|easy| # do something interesting with the easy response puts easy.last_effective_url end ``` ### Multi Interface (Advanced): ```ruby responses = {} requests = ["http://www.google.co.uk/", "http://www.ruby-lang.org/"] m = Curl::Multi.new # add a few easy handles requests.each do |url| responses[url] = "" c = Curl::Easy.new(url) do|curl| curl.follow_location = true curl.on_body{|data| responses[url] << data; data.size } curl.on_success {|easy| puts "success, add more easy handles" } end m.add(c) end m.perform do puts "idling... can do some work here" end requests.each do|url| puts responses[url] end ``` ### Easy Callbacks * `on_success` is called when the response code is 2xx * `on_redirect` is called when the response code is 3xx * `on_missing` is called when the response code is 4xx * `on_failure` is called when the response code is 5xx * `on_complete` is called in all cases. curb-1.0.5/doc.rb0000644000004100000410000000166114362470347013610 0ustar www-datawww-datarequire 'fileutils' include FileUtils begin incflags = File.read('ext/Makefile')[/INCFLAGS\s*=\s*(.*)$/,1] rescue Errno::ENOENT $stderr.puts("No makefile found; run `rake ext/Makefile' first.") end pp_srcdir = 'ext' rm_rf(tmpdir = '.doc-tmp') mkdir(tmpdir) begin if ARGV.include?('--cpp') begin if `cpp --version` =~ /\(GCC\)/ # gnu cpp $stderr.puts "Running GNU cpp over source" Dir['ext/*.c'].each do |fn| system("cpp -DRDOC_NEVER_DEFINED -C #{incflags} -o " + "#{File.join(tmpdir, File.basename(fn))} #{fn}") end pp_srcdir = tmpdir else $stderr.puts "Not running cpp (non-GNU)" end rescue # no cpp $stderr.puts "No cpp found" end end system("rdoc --title='Curb - libcurl bindings for ruby' --main=README #{pp_srcdir}/*.c README LICENSE lib/curb.rb") ensure rm_rf(tmpdir) end curb-1.0.5/ext/0000755000004100000410000000000014362470347013312 5ustar www-datawww-datacurb-1.0.5/ext/curb_errors.c0000644000004100000410000007276714362470347016030 0ustar www-datawww-data/* curb_errors.c - Ruby exception types for curl errors * Copyright (c)2006 Ross Bamford. * Licensed under the Ruby License. See LICENSE for details. * * $Id: curb_errors.c 10 2006-11-20 00:17:30Z roscopeco $ */ #include "curb_errors.h" extern VALUE mCurl; #ifdef RDOC_NEVER_DEFINED mCurl = rb_define_module("Curl"); #endif /* base errors */ VALUE mCurlErr; VALUE eCurlErrError; VALUE eCurlErrFTPError; VALUE eCurlErrHTTPError; VALUE eCurlErrFileError; VALUE eCurlErrLDAPError; VALUE eCurlErrTelnetError; VALUE eCurlErrTFTPError; VALUE eCurlErrRTSPError; /* Specific libcurl errors */ VALUE eCurlErrOK; /* not really an error but a return code */ VALUE eCurlErrUnsupportedProtocol; VALUE eCurlErrFailedInit; VALUE eCurlErrMalformedURL; VALUE eCurlErrMalformedURLUser; VALUE eCurlErrNotBuiltIn; VALUE eCurlErrProxyResolution; VALUE eCurlErrHostResolution; VALUE eCurlErrConnectFailed; VALUE eCurlErrFTPWeirdReply; VALUE eCurlErrFTPAccessDenied; VALUE eCurlErrFTPBadPassword; VALUE eCurlErrFTPWeirdPassReply; VALUE eCurlErrFTPWeirdUserReply; VALUE eCurlErrFTPWeirdPasvReply; VALUE eCurlErrFTPWeird227Format; VALUE eCurlErrFTPCantGetHost; VALUE eCurlErrFTPCantReconnect; VALUE eCurlErrFTPCouldntSetBinary; VALUE eCurlErrPartialFile; VALUE eCurlErrFTPCouldntRetrFile; VALUE eCurlErrFTPWrite; VALUE eCurlErrFTPQuote; VALUE eCurlErrHTTPFailed; VALUE eCurlErrWriteError; VALUE eCurlErrMalformedUser; VALUE eCurlErrFTPCouldntStorFile; VALUE eCurlErrReadError; VALUE eCurlErrOutOfMemory; VALUE eCurlErrTimeout; VALUE eCurlErrFTPCouldntSetASCII; VALUE eCurlErrFTPPortFailed; VALUE eCurlErrFTPCouldntUseRest; VALUE eCurlErrFTPCouldntGetSize; VALUE eCurlErrHTTPRange; VALUE eCurlErrHTTPPost; VALUE eCurlErrSSLConnectError; VALUE eCurlErrBadResume; VALUE eCurlErrFileCouldntRead; VALUE eCurlErrLDAPCouldntBind; VALUE eCurlErrLDAPSearchFailed; VALUE eCurlErrLibraryNotFound; VALUE eCurlErrFunctionNotFound; VALUE eCurlErrAbortedByCallback; VALUE eCurlErrBadFunctionArgument; VALUE eCurlErrBadCallingOrder; VALUE eCurlErrInterfaceFailed; VALUE eCurlErrBadPasswordEntered; VALUE eCurlErrTooManyRedirects; VALUE eCurlErrTelnetUnknownOption; VALUE eCurlErrTelnetBadOptionSyntax; VALUE eCurlErrObsolete; VALUE eCurlErrSSLPeerCertificate; VALUE eCurlErrGotNothing; VALUE eCurlErrSSLEngineNotFound; VALUE eCurlErrSSLEngineSetFailed; VALUE eCurlErrSendError; VALUE eCurlErrRecvError; VALUE eCurlErrShareInUse; VALUE eCurlErrSSLCertificate; VALUE eCurlErrSSLCipher; VALUE eCurlErrSSLCACertificate; VALUE eCurlErrBadContentEncoding; VALUE eCurlErrLDAPInvalidURL; VALUE eCurlErrFileSizeExceeded; VALUE eCurlErrFTPSSLFailed; VALUE eCurlErrSendFailedRewind; VALUE eCurlErrSSLEngineInitFailed; VALUE eCurlErrLoginDenied; VALUE eCurlErrTFTPNotFound; VALUE eCurlErrTFTPPermission; VALUE eCurlErrTFTPDiskFull; VALUE eCurlErrTFTPIllegalOperation; VALUE eCurlErrTFTPUnknownID; VALUE eCurlErrTFTPFileExists; VALUE eCurlErrTFTPNoSuchUser; VALUE eCurlErrConvFailed; VALUE eCurlErrConvReqd; VALUE eCurlErrSSLCacertBadfile; VALUE eCurlErrRemoteFileNotFound; VALUE eCurlErrSSH; VALUE eCurlErrSSLShutdownFailed; VALUE eCurlErrAgain; VALUE eCurlErrSSLCRLBadfile; VALUE eCurlErrSSLIssuerError; /* multi errors */ VALUE mCurlErrFailedInit; VALUE mCurlErrCallMultiPerform; VALUE mCurlErrBadHandle; VALUE mCurlErrBadEasyHandle; VALUE mCurlErrOutOfMemory; VALUE mCurlErrInternalError; VALUE mCurlErrBadSocket; #if HAVE_CURLM_ADDED_ALREADY VALUE mCurlErrAddedAlready; #endif VALUE mCurlErrUnknownOption; /* binding errors */ VALUE eCurlErrInvalidPostField; /* new errors */ VALUE eCurlErrFTPPRETFailed; VALUE eCurlErrRTSPCseqError; VALUE eCurlErrRTSPSessionError; VALUE eCurlErrFTPBadFileList; VALUE eCurlErrChunkFailed; VALUE eCurlErrNoConnectionAvailable; VALUE eCurlErrSSLPinnedPubKeyNotMatch; VALUE eCurlErrSSLInvalidCertStatus; VALUE eCurlErrHTTP2Stream; VALUE rb_curl_easy_error(CURLcode code) { VALUE exclz; const char *exmsg = NULL; VALUE results; switch (code) { case CURLE_OK: /* 0 */ exclz = eCurlErrOK; break; case CURLE_UNSUPPORTED_PROTOCOL: /* 1 */ exclz = eCurlErrUnsupportedProtocol; break; case CURLE_FAILED_INIT: /* 2 */ exclz = eCurlErrFailedInit; break; case CURLE_URL_MALFORMAT: /* 3 */ exclz = eCurlErrMalformedURL; break; #ifdef HAVE_CURLE_NOT_BUILT_IN case CURLE_NOT_BUILT_IN: /* 4 - [was obsoleted in August 2007 for 7.17.0, reused in April 2011 for 7.21.5] */ exclz = eCurlErrNotBuiltIn; break; #else case CURLE_URL_MALFORMAT_USER: /* 4 (NOT USED) */ exclz = eCurlErrMalformedURLUser; break; #endif case CURLE_COULDNT_RESOLVE_PROXY: /* 5 */ exclz = eCurlErrProxyResolution; break; case CURLE_COULDNT_RESOLVE_HOST: /* 6 */ exclz = eCurlErrHostResolution; break; case CURLE_COULDNT_CONNECT: /* 7 */ exclz = eCurlErrConnectFailed; break; case CURLE_FTP_WEIRD_SERVER_REPLY: /* 8 */ exclz = eCurlErrFTPWeirdReply; break; case CURLE_FTP_ACCESS_DENIED: /* 9 denied due to lack of access. */ exclz = eCurlErrFTPAccessDenied; break; case CURLE_FTP_USER_PASSWORD_INCORRECT: /* 10 */ exclz = eCurlErrFTPBadPassword; break; case CURLE_FTP_WEIRD_PASS_REPLY: /* 11 */ exclz = eCurlErrFTPWeirdPassReply; break; case CURLE_FTP_WEIRD_USER_REPLY: /* 12 */ exclz = eCurlErrFTPWeirdUserReply; break; case CURLE_FTP_WEIRD_PASV_REPLY: /* 13 */ exclz = eCurlErrFTPWeirdPasvReply; break; case CURLE_FTP_WEIRD_227_FORMAT: /* 14 */ exclz = eCurlErrFTPWeird227Format; break; case CURLE_FTP_CANT_GET_HOST: /* 15 */ exclz = eCurlErrFTPCantGetHost; break; case CURLE_FTP_CANT_RECONNECT: /* 16 */ exclz = eCurlErrFTPCantReconnect; break; case CURLE_FTP_COULDNT_SET_BINARY: /* 17 */ exclz = eCurlErrFTPCouldntSetBinary; break; case CURLE_PARTIAL_FILE: /* 18 */ exclz = eCurlErrPartialFile; break; case CURLE_FTP_COULDNT_RETR_FILE: /* 19 */ exclz = eCurlErrFTPCouldntRetrFile; break; case CURLE_FTP_WRITE_ERROR: /* 20 */ exclz = eCurlErrFTPWrite; break; case CURLE_FTP_QUOTE_ERROR: /* 21 */ exclz = eCurlErrFTPQuote; break; case CURLE_HTTP_RETURNED_ERROR: /* 22 */ exclz = eCurlErrHTTPFailed; break; case CURLE_WRITE_ERROR: /* 23 */ exclz = eCurlErrWriteError; break; case CURLE_MALFORMAT_USER: /* 24 - NOT USED */ exclz = eCurlErrMalformedUser; break; case CURLE_FTP_COULDNT_STOR_FILE: /* 25 - failed FTP upload */ exclz = eCurlErrFTPCouldntStorFile; break; case CURLE_READ_ERROR: /* 26 - could open/read from file */ exclz = eCurlErrReadError; break; case CURLE_OUT_OF_MEMORY: /* 27 */ exclz = eCurlErrOutOfMemory; break; case CURLE_OPERATION_TIMEOUTED: /* 28 - the timeout time was reached */ exclz = eCurlErrTimeout; break; case CURLE_FTP_COULDNT_SET_ASCII: /* 29 - TYPE A failed */ exclz = eCurlErrFTPCouldntSetASCII; break; case CURLE_FTP_PORT_FAILED: /* 30 - FTP PORT operation failed */ exclz = eCurlErrFTPPortFailed; break; case CURLE_FTP_COULDNT_USE_REST: /* 31 - the REST command failed */ exclz = eCurlErrFTPCouldntUseRest; break; case CURLE_FTP_COULDNT_GET_SIZE: /* 32 - the SIZE command failed */ exclz = eCurlErrFTPCouldntGetSize; break; case CURLE_HTTP_RANGE_ERROR: /* 33 - RANGE "command" didn't work */ exclz = eCurlErrHTTPRange; break; case CURLE_HTTP_POST_ERROR: /* 34 */ exclz = eCurlErrHTTPPost; break; case CURLE_SSL_CONNECT_ERROR: /* 35 - wrong when connecting with SSL */ exclz = eCurlErrSSLConnectError; break; case CURLE_BAD_DOWNLOAD_RESUME: /* 36 - couldn't resume download */ exclz = eCurlErrBadResume; break; case CURLE_FILE_COULDNT_READ_FILE: /* 37 */ exclz = eCurlErrFileCouldntRead; break; case CURLE_LDAP_CANNOT_BIND: /* 38 */ exclz = eCurlErrLDAPCouldntBind; break; case CURLE_LDAP_SEARCH_FAILED: /* 39 */ exclz = eCurlErrLDAPSearchFailed; break; case CURLE_LIBRARY_NOT_FOUND: /* 40 */ exclz = eCurlErrLibraryNotFound; break; case CURLE_FUNCTION_NOT_FOUND: /* 41 */ exclz = eCurlErrFunctionNotFound; break; case CURLE_ABORTED_BY_CALLBACK: /* 42 */ exclz = eCurlErrAbortedByCallback; break; case CURLE_BAD_FUNCTION_ARGUMENT: /* 43 */ exclz = eCurlErrBadFunctionArgument; break; case CURLE_BAD_CALLING_ORDER: /* 44 - NOT USED */ exclz = eCurlErrBadCallingOrder; break; case CURLE_INTERFACE_FAILED: /* 45 - CURLOPT_INTERFACE failed */ exclz = eCurlErrInterfaceFailed; break; case CURLE_BAD_PASSWORD_ENTERED: /* 46 - NOT USED */ exclz = eCurlErrBadPasswordEntered; break; case CURLE_TOO_MANY_REDIRECTS: /* 47 - catch endless re-direct loops */ exclz = eCurlErrTooManyRedirects; break; case CURLE_UNKNOWN_TELNET_OPTION: /* 48 - User specified an unknown option */ exclz = eCurlErrTelnetUnknownOption; break; case CURLE_TELNET_OPTION_SYNTAX: /* 49 - Malformed telnet option */ exclz = eCurlErrTelnetBadOptionSyntax; break; #ifdef HAVE_CURLE_OBSOLETE case CURLE_OBSOLETE: /* 50 - NOT USED */ exclz = eCurlErrObsolete; break; #endif #if LIBCURL_VERSION_NUM < 0x073e00 case CURLE_SSL_PEER_CERTIFICATE: /* 51 - peer's certificate wasn't ok */ exclz = eCurlErrSSLPeerCertificate; break; #endif case CURLE_GOT_NOTHING: /* 52 - when this is a specific error */ exclz = eCurlErrGotNothing; break; case CURLE_SSL_ENGINE_NOTFOUND: /* 53 - SSL crypto engine not found */ exclz = eCurlErrSSLEngineNotFound; break; case CURLE_SSL_ENGINE_SETFAILED: /* 54 - can not set SSL crypto engine as default */ exclz = eCurlErrSSLEngineSetFailed; break; case CURLE_SEND_ERROR: /* 55 - failed sending network data */ exclz = eCurlErrSendError; break; case CURLE_RECV_ERROR: /* 56 - failure in receiving network data */ exclz = eCurlErrRecvError; break; case CURLE_SHARE_IN_USE: /* 57 - share is in use */ exclz = eCurlErrShareInUse; break; case CURLE_SSL_CERTPROBLEM: /* 58 - problem with the local certificate */ exclz = eCurlErrSSLCertificate; break; case CURLE_SSL_CIPHER: /* 59 - couldn't use specified cipher */ exclz = eCurlErrSSLCipher; break; #if LIBCURL_VERSION_NUM >= 0x073e00 case CURLE_PEER_FAILED_VERIFICATION: /* 60 - problem with the CA cert (path?) */ exclz = eCurlErrSSLPeerCertificate; #else case CURLE_SSL_CACERT: /* 60 - problem with the CA cert (path?) */ exclz = eCurlErrSSLCACertificate; #endif break; case CURLE_BAD_CONTENT_ENCODING: /* 61 - Unrecognized transfer encoding */ exclz = eCurlErrBadContentEncoding; break; case CURLE_LDAP_INVALID_URL: /* 62 - Invalid LDAP URL */ exclz = eCurlErrLDAPInvalidURL; break; case CURLE_FILESIZE_EXCEEDED: /* 63 - Maximum file size exceeded */ exclz = eCurlErrFileSizeExceeded; break; case CURLE_FTP_SSL_FAILED: /* 64 - Requested FTP SSL level failed */ exclz = eCurlErrFTPSSLFailed; break; #ifdef HAVE_CURLE_SEND_FAIL_REWIND case CURLE_SEND_FAIL_REWIND: /* 65 - Sending the data requires a rewind that failed */ exclz = eCurlErrSendFailedRewind; break; #endif #ifdef HAVE_CURLE_SSL_ENGINE_INITFAILED case CURLE_SSL_ENGINE_INITFAILED: /* 66 - failed to initialise ENGINE */ exclz = eCurlErrSSLEngineInitFailed; break; #endif #ifdef HAVE_CURLE_LOGIN_DENIED case CURLE_LOGIN_DENIED: /* 67 - user, password or similar was not accepted and we failed to login */ exclz = eCurlErrLoginDenied; break; #endif // recent additions, may not be present in all supported versions #ifdef HAVE_CURLE_TFTP_NOTFOUND case CURLE_TFTP_NOTFOUND: /* 68 - file not found on server */ exclz = eCurlErrTFTPNotFound; break; #endif #ifdef HAVE_CURLE_TFTP_PERM case CURLE_TFTP_PERM: /* 69 - permission problem on server */ exclz = eCurlErrTFTPPermission; break; #endif #ifdef HAVE_CURLE_TFTP_DISKFULL case CURLE_TFTP_DISKFULL: /* 70 - out of disk space on server */ exclz = eCurlErrTFTPDiskFull; break; #endif #ifdef HAVE_CURLE_TFTP_ILLEGAL case CURLE_TFTP_ILLEGAL: /* 71 - Illegal TFTP operation */ exclz = eCurlErrTFTPIllegalOperation; break; #endif #ifdef HAVE_CURLE_TFTP_UNKNOWNID case CURLE_TFTP_UNKNOWNID: /* 72 - Unknown transfer ID */ exclz = eCurlErrTFTPUnknownID; break; #endif #ifdef HAVE_CURLE_TFTP_EXISTS case CURLE_TFTP_EXISTS: /* 73 - File already exists */ exclz = eCurlErrTFTPFileExists; break; #endif #ifdef HAVE_CURLE_TFTP_NOSUCHUSER case CURLE_TFTP_NOSUCHUSER: /* 74 - No such user */ exclz = eCurlErrTFTPNotFound; break; #endif #ifdef HAVE_CURLE_CONV_FAILED case CURLE_CONV_FAILED: /* 75 - conversion failed */ exclz = eCurlErrConvFailed; break; #endif #ifdef HAVE_CURLE_CONV_REQD case CURLE_CONV_REQD: /* 76 - caller must register conversion callbacks using curl_easy_setopt options CURLOPT_CONV_FROM_NETWORK_FUNCTION, CURLOPT_CONV_TO_NETWORK_FUNCTION, and CURLOPT_CONV_FROM_UTF8_FUNCTION */ exclz = eCurlErrConvReqd; break; #endif #ifdef HAVE_CURLE_SSL_CACERT_BADFILE case CURLE_SSL_CACERT_BADFILE: /* 77 - could not load CACERT file, missing or wrong format */ exclz = eCurlErrSSLCacertBadfile; break; #endif #ifdef HAVE_CURLE_REMOTE_FILE_NOT_FOUND case CURLE_REMOTE_FILE_NOT_FOUND: /* 78 - remote file not found */ exclz = eCurlErrRemoteFileNotFound; break; #endif #ifdef HAVE_CURLE_SSH case CURLE_SSH: /* 79 - error from the SSH layer, somewhat generic so the error message will be of interest when this has happened */ exclz = eCurlErrSSH; break; #endif #ifdef HAVE_CURLE_SSL_SHUTDOWN_FAILED case CURLE_SSL_SHUTDOWN_FAILED: /* 80 - Failed to shut down the SSL connection */ exclz = eCurlErrSSLShutdownFailed; break; #endif #ifdef HAVE_CURLE_AGAIN case CURLE_AGAIN: /* 81 - socket is not ready for send/recv, wait till it's ready and try again (Added in 7.18.2) */ exclz = eCurlErrAgain; break; #endif #ifdef HAVE_CURLE_SSL_CRL_BADFILE case CURLE_SSL_CRL_BADFILE: /* 82 - could not load CRL file, missing or wrong format (Added in 7.19.0) */ exclz = eCurlErrSSLCRLBadfile; break; #endif #ifdef HAVE_CURLE_SSL_ISSUER_ERROR case CURLE_SSL_ISSUER_ERROR: /* 83 - Issuer check failed. (Added in 7.19.0) */ exclz = eCurlErrSSLIssuerError; break; #endif #ifdef HAVE_CURLE_FTP_PRET_FAILED case CURLE_FTP_PRET_FAILED: /* 84 */ exclz = eCurlErrFTPPRETFailed; break; #endif #ifdef HAVE_CURLE_RTSP_CSEQ_ERROR case CURLE_RTSP_CSEQ_ERROR: /* 85 */ exclz = eCurlErrRTSPCseqError; break; #endif #ifdef HAVE_CURLE_RTSP_SESSION_ERROR case CURLE_RTSP_SESSION_ERROR: /* 86 */ exclz = eCurlErrRTSPSessionError; break; #endif #ifdef HAVE_CURLE_FTP_BAD_FILE_LIST case CURLE_FTP_BAD_FILE_LIST: /* 87 */ exclz = eCurlErrFTPBadFileList; break; #endif #ifdef HAVE_CURLE_CHUNK_FAILED case CURLE_CHUNK_FAILED: /* 88 */ exclz = eCurlErrChunkFailed; break; #endif #ifdef HAVE_CURLE_NO_CONNECTION_AVAILABLE case CURLE_NO_CONNECTION_AVAILABLE: /* 89 */ exclz = eCurlErrNoConnectionAvailable; break; #endif #ifdef HAVE_CURLE_SSL_PINNEDPUBKEYNOTMATCH case CURLE_SSL_PINNEDPUBKEYNOTMATCH: /* 90 */ exclz = eCurlErrSSLPinnedPubKeyNotMatch; break; #endif #ifdef HAVE_CURLE_SSL_INVALIDCERTSTATUS case CURLE_SSL_INVALIDCERTSTATUS: /* 91 */ exclz = eCurlErrSSLInvalidCertStatus; break; #endif #ifdef HAVE_CURLE_HTTP2_STREAM case CURLE_HTTP2_STREAM: /* 92 */ exclz = eCurlErrHTTP2Stream; break; #endif default: exclz = eCurlErrError; exmsg = "Unknown error result from libcurl"; } if (!exmsg) { exmsg = curl_easy_strerror(code); } results = rb_ary_new2(2); rb_ary_push(results, exclz); rb_ary_push(results, rb_str_new2(exmsg)); return results; } /* rb_raise an approriate exception for the supplied CURLcode */ void raise_curl_easy_error_exception(CURLcode code) { VALUE obj = rb_curl_easy_error(code); VALUE exmsg = rb_ary_entry(obj,1); rb_raise(rb_ary_entry(obj,0), "CURLError: %s", StringValueCStr(exmsg)); } VALUE rb_curl_multi_error(CURLMcode code) { VALUE exclz; const char *exmsg = NULL; VALUE results; switch(code) { case CURLM_CALL_MULTI_PERFORM: /* -1 */ exclz = mCurlErrCallMultiPerform; break; case CURLM_BAD_HANDLE: /* 1 */ exclz = mCurlErrBadHandle; break; case CURLM_BAD_EASY_HANDLE: /* 2 */ exclz = mCurlErrBadEasyHandle; break; case CURLM_OUT_OF_MEMORY: /* 3 */ exclz = mCurlErrOutOfMemory; break; case CURLM_INTERNAL_ERROR: /* 4 */ exclz = mCurlErrInternalError; break; #if HAVE_CURLM_BAD_SOCKET case CURLM_BAD_SOCKET: /* 5 */ exclz = mCurlErrBadSocket; break; #endif #if HAVE_CURLM_UNKNOWN_OPTION case CURLM_UNKNOWN_OPTION: /* 6 */ exclz = mCurlErrUnknownOption; break; #endif #if HAVE_CURLM_ADDED_ALREADY case CURLM_ADDED_ALREADY: /* 7 */ exclz = mCurlErrAddedAlready; break; #endif default: exclz = eCurlErrError; exmsg = "Unknown error result from libcurl"; } if (!exmsg) { exmsg = curl_multi_strerror(code); } results = rb_ary_new2(2); rb_ary_push(results, exclz); rb_ary_push(results, rb_str_new2(exmsg)); return results; } void raise_curl_multi_error_exception(CURLMcode code) { VALUE obj = rb_curl_multi_error(code); VALUE exmsg = rb_ary_entry(obj,1); rb_raise(rb_ary_entry(obj,0), "CURLError: %s", StringValueCStr(exmsg)); } void init_curb_errors() { mCurlErr = rb_define_module_under(mCurl, "Err"); eCurlErrError = rb_define_class_under(mCurlErr, "CurlError", rb_eRuntimeError); eCurlErrFTPError = rb_define_class_under(mCurlErr, "FTPError", eCurlErrError); eCurlErrHTTPError = rb_define_class_under(mCurlErr, "HTTPError", eCurlErrError); eCurlErrFileError = rb_define_class_under(mCurlErr, "FileError", eCurlErrError); eCurlErrLDAPError = rb_define_class_under(mCurlErr, "LDAPError", eCurlErrError); eCurlErrTelnetError = rb_define_class_under(mCurlErr, "TelnetError", eCurlErrError); eCurlErrTFTPError = rb_define_class_under(mCurlErr, "TFTPError", eCurlErrError); eCurlErrRTSPError = rb_define_class_under(mCurlErr, "RTSPError", eCurlErrError); eCurlErrOK = rb_define_class_under(mCurlErr, "CurlOK", eCurlErrError); eCurlErrUnsupportedProtocol = rb_define_class_under(mCurlErr, "UnsupportedProtocolError", eCurlErrError); eCurlErrFailedInit = rb_define_class_under(mCurlErr, "FailedInitError", eCurlErrError); eCurlErrMalformedURL = rb_define_class_under(mCurlErr, "MalformedURLError", eCurlErrError); eCurlErrNotBuiltIn = rb_define_class_under(mCurlErr, "NotBuiltInError", eCurlErrError); eCurlErrMalformedURLUser = rb_define_class_under(mCurlErr, "MalformedURLUserError", eCurlErrError); eCurlErrProxyResolution = rb_define_class_under(mCurlErr, "ProxyResolutionError", eCurlErrError); eCurlErrHostResolution = rb_define_class_under(mCurlErr, "HostResolutionError", eCurlErrError); eCurlErrConnectFailed = rb_define_class_under(mCurlErr, "ConnectionFailedError", eCurlErrError); eCurlErrFTPWeirdReply = rb_define_class_under(mCurlErr, "WeirdReplyError", eCurlErrFTPError); eCurlErrFTPAccessDenied = rb_define_class_under(mCurlErr, "AccessDeniedError", eCurlErrFTPError); eCurlErrFTPBadPassword = rb_define_class_under(mCurlErr, "BadPasswordError", eCurlErrFTPError); eCurlErrFTPWeirdPassReply = rb_define_class_under(mCurlErr, "WeirdPassReplyError", eCurlErrFTPError); eCurlErrFTPWeirdUserReply = rb_define_class_under(mCurlErr, "WeirdUserReplyError", eCurlErrFTPError); eCurlErrFTPWeirdPasvReply = rb_define_class_under(mCurlErr, "WeirdPasvReplyError", eCurlErrFTPError); eCurlErrFTPWeird227Format = rb_define_class_under(mCurlErr, "Weird227FormatError", eCurlErrFTPError); eCurlErrFTPCantGetHost = rb_define_class_under(mCurlErr, "CantGetHostError", eCurlErrFTPError); eCurlErrFTPCantReconnect = rb_define_class_under(mCurlErr, "CantReconnectError", eCurlErrFTPError); eCurlErrFTPCouldntSetBinary = rb_define_class_under(mCurlErr, "CouldntSetBinaryError", eCurlErrFTPError); eCurlErrPartialFile = rb_define_class_under(mCurlErr, "PartialFileError", eCurlErrError); eCurlErrFTPCouldntRetrFile = rb_define_class_under(mCurlErr, "CouldntRetrFileError", eCurlErrFTPError); eCurlErrFTPWrite = rb_define_class_under(mCurlErr, "FTPWriteError", eCurlErrFTPError); eCurlErrFTPQuote = rb_define_class_under(mCurlErr, "FTPQuoteError", eCurlErrFTPError); eCurlErrHTTPFailed = rb_define_class_under(mCurlErr, "HTTPFailedError", eCurlErrHTTPError); eCurlErrWriteError = rb_define_class_under(mCurlErr, "WriteError", eCurlErrError); eCurlErrMalformedUser = rb_define_class_under(mCurlErr, "MalformedUserError", eCurlErrError); eCurlErrFTPCouldntStorFile = rb_define_class_under(mCurlErr, "CouldntStorFileError", eCurlErrFTPError); eCurlErrReadError = rb_define_class_under(mCurlErr, "ReadError", eCurlErrError); eCurlErrOutOfMemory = rb_define_class_under(mCurlErr, "OutOfMemoryError", eCurlErrError); eCurlErrTimeout = rb_define_class_under(mCurlErr, "TimeoutError", eCurlErrError); eCurlErrFTPCouldntSetASCII = rb_define_class_under(mCurlErr, "CouldntSetASCIIError", eCurlErrFTPError); eCurlErrFTPPortFailed = rb_define_class_under(mCurlErr, "PortFailedError", eCurlErrFTPError); eCurlErrFTPCouldntUseRest = rb_define_class_under(mCurlErr, "CouldntUseRestError", eCurlErrFTPError); eCurlErrFTPCouldntGetSize = rb_define_class_under(mCurlErr, "CouldntGetSizeError", eCurlErrFTPError); eCurlErrHTTPRange = rb_define_class_under(mCurlErr, "HTTPRangeError", eCurlErrHTTPError); eCurlErrHTTPPost = rb_define_class_under(mCurlErr, "HTTPPostError", eCurlErrHTTPError); eCurlErrSSLConnectError = rb_define_class_under(mCurlErr, "SSLConnectError", eCurlErrError); eCurlErrBadResume = rb_define_class_under(mCurlErr, "BadResumeError", eCurlErrError); eCurlErrFileCouldntRead = rb_define_class_under(mCurlErr, "CouldntReadError", eCurlErrFileError); eCurlErrLDAPCouldntBind = rb_define_class_under(mCurlErr, "CouldntBindError", eCurlErrLDAPError); eCurlErrLDAPSearchFailed = rb_define_class_under(mCurlErr, "SearchFailedError", eCurlErrLDAPError); eCurlErrLibraryNotFound = rb_define_class_under(mCurlErr, "LibraryNotFoundError", eCurlErrError); eCurlErrFunctionNotFound = rb_define_class_under(mCurlErr, "FunctionNotFoundError", eCurlErrError); eCurlErrAbortedByCallback = rb_define_class_under(mCurlErr, "AbortedByCallbackError", eCurlErrError); eCurlErrBadFunctionArgument = rb_define_class_under(mCurlErr, "BadFunctionArgumentError", eCurlErrError); eCurlErrBadCallingOrder = rb_define_class_under(mCurlErr, "BadCallingOrderError", eCurlErrError); eCurlErrInterfaceFailed = rb_define_class_under(mCurlErr, "InterfaceFailedError", eCurlErrError); eCurlErrBadPasswordEntered = rb_define_class_under(mCurlErr, "BadPasswordEnteredError", eCurlErrError); eCurlErrTooManyRedirects = rb_define_class_under(mCurlErr, "TooManyRedirectsError", eCurlErrError); eCurlErrTelnetUnknownOption = rb_define_class_under(mCurlErr, "UnknownOptionError", eCurlErrTelnetError); eCurlErrTelnetBadOptionSyntax = rb_define_class_under(mCurlErr, "BadOptionSyntaxError", eCurlErrTelnetError); eCurlErrObsolete = rb_define_class_under(mCurlErr, "ObsoleteError", eCurlErrError); eCurlErrSSLPeerCertificate = rb_define_class_under(mCurlErr, "SSLPeerCertificateError", eCurlErrError); eCurlErrGotNothing = rb_define_class_under(mCurlErr, "GotNothingError", eCurlErrError); eCurlErrSSLEngineNotFound = rb_define_class_under(mCurlErr, "SSLEngineNotFoundError", eCurlErrError); eCurlErrSSLEngineSetFailed = rb_define_class_under(mCurlErr, "SSLEngineSetFailedError", eCurlErrError); eCurlErrSendError = rb_define_class_under(mCurlErr, "SendError", eCurlErrError); eCurlErrRecvError = rb_define_class_under(mCurlErr, "RecvError", eCurlErrError); eCurlErrShareInUse = rb_define_class_under(mCurlErr, "ShareInUseError", eCurlErrError); eCurlErrConvFailed = rb_define_class_under(mCurlErr, "ConvFailed", eCurlErrError); eCurlErrConvReqd = rb_define_class_under(mCurlErr, "ConvReqd", eCurlErrError); eCurlErrRemoteFileNotFound = rb_define_class_under(mCurlErr, "RemoteFileNotFound", eCurlErrError); eCurlErrAgain = rb_define_class_under(mCurlErr, "Again", eCurlErrError); eCurlErrSSLCertificate = rb_define_class_under(mCurlErr, "SSLCertificateError", eCurlErrError); eCurlErrSSLCipher = rb_define_class_under(mCurlErr, "SSLCypherError", eCurlErrError); eCurlErrSSLCACertificate = rb_define_class_under(mCurlErr, "SSLCACertificateError", eCurlErrError); eCurlErrBadContentEncoding = rb_define_class_under(mCurlErr, "BadContentEncodingError", eCurlErrError); eCurlErrSSLCacertBadfile = rb_define_class_under(mCurlErr, "SSLCaertBadFile", eCurlErrError); eCurlErrSSLCRLBadfile = rb_define_class_under(mCurlErr, "SSLCRLBadfile", eCurlErrError); eCurlErrSSLIssuerError = rb_define_class_under(mCurlErr, "SSLIssuerError", eCurlErrError); eCurlErrSSLShutdownFailed = rb_define_class_under(mCurlErr, "SSLShutdownFailed", eCurlErrError); eCurlErrSSH = rb_define_class_under(mCurlErr, "SSH", eCurlErrError); mCurlErrFailedInit = rb_define_class_under(mCurlErr, "MultiInitError", eCurlErrError); mCurlErrCallMultiPerform = rb_define_class_under(mCurlErr, "MultiPerform", eCurlErrError); mCurlErrBadHandle = rb_define_class_under(mCurlErr, "MultiBadHandle", eCurlErrError); mCurlErrBadEasyHandle = rb_define_class_under(mCurlErr, "MultiBadEasyHandle", eCurlErrError); mCurlErrOutOfMemory = rb_define_class_under(mCurlErr, "MultiOutOfMemory", eCurlErrError); mCurlErrInternalError = rb_define_class_under(mCurlErr, "MultiInternalError", eCurlErrError); mCurlErrBadSocket = rb_define_class_under(mCurlErr, "MultiBadSocket", eCurlErrError); #if HAVE_CURLM_ADDED_ALREADY mCurlErrAddedAlready = rb_define_class_under(mCurlErr, "MultiAddedAlready", eCurlErrError); #endif mCurlErrUnknownOption = rb_define_class_under(mCurlErr, "MultiUnknownOption", eCurlErrError); eCurlErrLDAPInvalidURL = rb_define_class_under(mCurlErr, "InvalidLDAPURLError", eCurlErrLDAPError); eCurlErrFileSizeExceeded = rb_define_class_under(mCurlErr, "FileSizeExceededError", eCurlErrError); eCurlErrFTPSSLFailed = rb_define_class_under(mCurlErr, "FTPSSLFailed", eCurlErrFTPError); eCurlErrSendFailedRewind = rb_define_class_under(mCurlErr, "SendFailedRewind", eCurlErrError); eCurlErrSSLEngineInitFailed = rb_define_class_under(mCurlErr, "SSLEngineInitFailedError", eCurlErrError); eCurlErrLoginDenied = rb_define_class_under(mCurlErr, "LoginDeniedError", eCurlErrError); eCurlErrTFTPNotFound = rb_define_class_under(mCurlErr, "NotFoundError", eCurlErrTFTPError); eCurlErrTFTPPermission = rb_define_class_under(mCurlErr, "PermissionError", eCurlErrTFTPError); eCurlErrTFTPDiskFull = rb_define_class_under(mCurlErr, "DiskFullError", eCurlErrTFTPError); eCurlErrTFTPIllegalOperation = rb_define_class_under(mCurlErr, "IllegalOperationError", eCurlErrTFTPError); eCurlErrTFTPUnknownID = rb_define_class_under(mCurlErr, "UnknownIDError", eCurlErrTFTPError); eCurlErrTFTPFileExists = rb_define_class_under(mCurlErr, "FileExistsError", eCurlErrTFTPError); eCurlErrTFTPNoSuchUser = rb_define_class_under(mCurlErr, "NoSuchUserError", eCurlErrTFTPError); eCurlErrInvalidPostField = rb_define_class_under(mCurlErr, "InvalidPostFieldError", eCurlErrError); eCurlErrFTPPRETFailed = rb_define_class_under(mCurlErr, "PPRETFailedError", eCurlErrFTPError); eCurlErrRTSPCseqError = rb_define_class_under(mCurlErr, "CseqError", eCurlErrRTSPError); eCurlErrRTSPSessionError = rb_define_class_under(mCurlErr, "SessionError", eCurlErrRTSPError); eCurlErrFTPBadFileList = rb_define_class_under(mCurlErr, "BadFileListError", eCurlErrFTPError); eCurlErrChunkFailed = rb_define_class_under(mCurlErr, "ChunkFailedError", eCurlErrError); eCurlErrNoConnectionAvailable = rb_define_class_under(mCurlErr, "NoConnectionAvailableError", eCurlErrError); eCurlErrSSLPinnedPubKeyNotMatch = rb_define_class_under(mCurlErr, "SSLPinnedPubKeyNotMatchError", eCurlErrError); eCurlErrSSLInvalidCertStatus = rb_define_class_under(mCurlErr, "SSLInvalidCertStatusError", eCurlErrError); eCurlErrHTTP2Stream = rb_define_class_under(mCurlErr, "HTTP2StreamError", eCurlErrHTTPError); } curb-1.0.5/ext/curb_multi.h0000644000004100000410000000067014362470347015633 0ustar www-datawww-data/* curb_multi.h - Curl easy mode * Copyright (c)2008 Todd A. Fisher. * Licensed under the Ruby License. See LICENSE for details. * * $Id$ */ #ifndef __CURB_MULTI_H #define __CURB_MULTI_H #include "curb.h" #include "curb_easy.h" #include typedef struct { int active; int running; CURLM *handle; } ruby_curl_multi; extern VALUE cCurlMulti; void init_curb_multi(); VALUE ruby_curl_multi_new(VALUE klass); #endif curb-1.0.5/ext/curb_errors.h0000644000004100000410000001034614362470347016016 0ustar www-datawww-data/* curb_errors.h - Ruby exception types for curl errors * Copyright (c)2006 Ross Bamford. * Licensed under the Ruby License. See LICENSE for details. * * $Id: curb_errors.h 4 2006-11-17 18:35:31Z roscopeco $ */ #ifndef __CURB_ERRORS_H #define __CURB_ERRORS_H #include "curb.h" /* base errors */ extern VALUE cCurlErr; /* easy errors */ extern VALUE mCurlErr; extern VALUE eCurlErrError; extern VALUE eCurlErrFTPError; extern VALUE eCurlErrHTTPError; extern VALUE eCurlErrFileError; extern VALUE eCurlErrLDAPError; extern VALUE eCurlErrTelnetError; extern VALUE eCurlErrTFTPError; /* libcurl errors */ extern VALUE eCurlErrUnsupportedProtocol; extern VALUE eCurlErrFailedInit; extern VALUE eCurlErrMalformedURL; extern VALUE eCurlErrMalformedURLUser; extern VALUE eCurlErrProxyResolution; extern VALUE eCurlErrHostResolution; extern VALUE eCurlErrConnectFailed; extern VALUE eCurlErrFTPWeirdReply; extern VALUE eCurlErrFTPAccessDenied; extern VALUE eCurlErrFTPBadPassword; extern VALUE eCurlErrFTPWeirdPassReply; extern VALUE eCurlErrFTPWeirdUserReply; extern VALUE eCurlErrFTPWeirdPasvReply; extern VALUE eCurlErrFTPWeird227Format; extern VALUE eCurlErrFTPCantGetHost; extern VALUE eCurlErrFTPCantReconnect; extern VALUE eCurlErrFTPCouldntSetBinary; extern VALUE eCurlErrPartialFile; extern VALUE eCurlErrFTPCouldntRetrFile; extern VALUE eCurlErrFTPWrite; extern VALUE eCurlErrFTPQuote; extern VALUE eCurlErrHTTPFailed; extern VALUE eCurlErrWriteError; extern VALUE eCurlErrMalformedUser; extern VALUE eCurlErrFTPCouldntStorFile; extern VALUE eCurlErrReadError; extern VALUE eCurlErrOutOfMemory; extern VALUE eCurlErrTimeout; extern VALUE eCurlErrFTPCouldntSetASCII; extern VALUE eCurlErrFTPPortFailed; extern VALUE eCurlErrFTPCouldntUseRest; extern VALUE eCurlErrFTPCouldntGetSize; extern VALUE eCurlErrHTTPRange; extern VALUE eCurlErrHTTPPost; extern VALUE eCurlErrSSLConnectError; extern VALUE eCurlErrBadResume; extern VALUE eCurlErrFileCouldntRead; extern VALUE eCurlErrLDAPCouldntBind; extern VALUE eCurlErrLDAPSearchFailed; extern VALUE eCurlErrLibraryNotFound; extern VALUE eCurlErrFunctionNotFound; extern VALUE eCurlErrAbortedByCallback; extern VALUE eCurlErrBadFunctionArgument; extern VALUE eCurlErrBadCallingOrder; extern VALUE eCurlErrInterfaceFailed; extern VALUE eCurlErrBadPasswordEntered; extern VALUE eCurlErrTooManyRedirects; extern VALUE eCurlErrTelnetUnknownOption; extern VALUE eCurlErrTelnetBadOptionSyntax; extern VALUE eCurlErrObsolete; extern VALUE eCurlErrSSLPeerCertificate; extern VALUE eCurlErrGotNothing; extern VALUE eCurlErrSSLEngineNotFound; extern VALUE eCurlErrSSLEngineSetFailed; extern VALUE eCurlErrSendError; extern VALUE eCurlErrRecvError; extern VALUE eCurlErrShareInUse; extern VALUE eCurlErrSSLCertificate; extern VALUE eCurlErrSSLCipher; extern VALUE eCurlErrSSLCACertificate; extern VALUE eCurlErrBadContentEncoding; extern VALUE eCurlErrLDAPInvalidURL; extern VALUE eCurlErrFileSizeExceeded; extern VALUE eCurlErrFTPSSLFailed; extern VALUE eCurlErrSendFailedRewind; extern VALUE eCurlErrSSLEngineInitFailed; extern VALUE eCurlErrLoginDenied; extern VALUE eCurlErrTFTPNotFound; extern VALUE eCurlErrTFTPPermission; extern VALUE eCurlErrTFTPDiskFull; extern VALUE eCurlErrTFTPIllegalOperation; extern VALUE eCurlErrTFTPUnknownID; extern VALUE eCurlErrTFTPFileExists; extern VALUE eCurlErrTFTPNoSuchUser; extern VALUE eCurlErrConvFailed; extern VALUE eCurlErrConvReqd; extern VALUE eCurlErrSSLCacertBadfile; extern VALUE eCurlErrRemoteFileNotFound; extern VALUE eCurlErrSSH; extern VALUE eCurlErrSSLShutdownFailed; extern VALUE eCurlErrAgain; extern VALUE eCurlErrSSLCRLBadfile; extern VALUE eCurlErrSSLIssuerError; /* multi errors */ extern VALUE mCurlErrFailedInit; extern VALUE mCurlErrCallMultiPerform; extern VALUE mCurlErrBadHandle; extern VALUE mCurlErrBadEasyHandle; extern VALUE mCurlErrOutOfMemory; extern VALUE mCurlErrInternalError; extern VALUE mCurlErrBadSocket; extern VALUE mCurlErrUnknownOption; #if HAVE_CURLM_ADDED_ALREADY extern VALUE mCurlErrAddedAlready; #endif /* binding errors */ extern VALUE eCurlErrInvalidPostField; void init_curb_errors(); void raise_curl_easy_error_exception(CURLcode code); void raise_curl_multi_error_exception(CURLMcode code); VALUE rb_curl_easy_error(CURLcode code); VALUE rb_curl_multi_error(CURLMcode code); #endif curb-1.0.5/ext/curb_easy.h0000644000004100000410000000537214362470347015446 0ustar www-datawww-data/* curb_easy.h - Curl easy mode * Copyright (c)2006 Ross Bamford. * Licensed under the Ruby License. See LICENSE for details. * * $Id: curb_easy.h 25 2006-12-07 23:38:25Z roscopeco $ */ #ifndef __CURB_EASY_H #define __CURB_EASY_H #include "curb.h" #include #ifdef CURL_VERSION_SSL #if LIBCURL_VERSION_NUM >= 0x070b00 # if LIBCURL_VERSION_NUM <= 0x071004 # define CURB_FTPSSL CURLOPT_FTP_SSL # define CURB_FTPSSL_ALL CURLFTPSSL_ALL # define CURB_FTPSSL_TRY CURLFTPSSL_TRY # define CURB_FTPSSL_CONTROL CURLFTPSSL_CONTROL # define CURB_FTPSSL_NONE CURLFTPSSL_NONE # else # define CURB_FTPSSL CURLOPT_USE_SSL # define CURB_FTPSSL_ALL CURLUSESSL_ALL # define CURB_FTPSSL_TRY CURLUSESSL_TRY # define CURB_FTPSSL_CONTROL CURLUSESSL_CONTROL # define CURB_FTPSSL_NONE CURLUSESSL_NONE # endif #endif #endif /* a lot of this *could* be kept in the handler itself, * but then we lose the ability to query it's status. */ typedef struct { /* The handler */ CURL *curl; /* Buffer for error details from CURLOPT_ERRORBUFFER */ char err_buf[CURL_ERROR_SIZE]; VALUE opts; /* rather then allocate everything we might need to store, allocate a Hash and only store objects we actually use... */ VALUE multi; /* keep a multi handle alive for each easy handle not being used by a multi handle. This improves easy performance when not within a multi context */ /* Other opts */ unsigned short local_port; // 0 is no port unsigned short local_port_range; // " " " " unsigned short proxy_port; // " " " " int proxy_type; long http_auth_types; long proxy_auth_types; long max_redirs; unsigned long timeout; unsigned long timeout_ms; unsigned long connect_timeout; unsigned long connect_timeout_ms; long dns_cache_timeout; unsigned long ftp_response_timeout; long low_speed_limit; long low_speed_time; long max_send_speed_large; long max_recv_speed_large; long ssl_version; long use_ssl; long ftp_filemethod; unsigned short resolve_mode; /* bool flags */ char proxy_tunnel; char fetch_file_time; char ssl_verify_peer; char ssl_verify_host; char header_in_body; char use_netrc; char follow_location; char unrestricted_auth; char verbose; char multipart_form_post; char enable_cookies; char ignore_content_length; char callback_active; struct curl_slist *curl_headers; struct curl_slist *curl_proxy_headers; struct curl_slist *curl_ftp_commands; struct curl_slist *curl_resolve; int last_result; /* last result code from multi loop */ } ruby_curl_easy; extern VALUE cCurlEasy; VALUE ruby_curl_easy_setup(ruby_curl_easy *rbce); VALUE ruby_curl_easy_cleanup(VALUE self, ruby_curl_easy *rbce); void init_curb_easy(); #endif curb-1.0.5/ext/curb_upload.c0000644000004100000410000000442414362470347015761 0ustar www-datawww-data/* curb_upload.c - Curl upload handle * Copyright (c)2009 Todd A Fisher. * Licensed under the Ruby License. See LICENSE for details. */ #include "curb_upload.h" extern VALUE mCurl; VALUE cCurlUpload; #ifdef RDOC_NEVER_DEFINED mCurl = rb_define_module("Curl"); #endif static void curl_upload_mark(ruby_curl_upload *rbcu) { if (rbcu->stream && !NIL_P(rbcu->stream)) rb_gc_mark(rbcu->stream); } static void curl_upload_free(ruby_curl_upload *rbcu) { free(rbcu); } /* * call-seq: * internal class for sending large file uploads */ VALUE ruby_curl_upload_new(VALUE klass) { VALUE upload; ruby_curl_upload *rbcu = ALLOC(ruby_curl_upload); rbcu->stream = Qnil; rbcu->offset = 0; upload = Data_Wrap_Struct(klass, curl_upload_mark, curl_upload_free, rbcu); return upload; } /* * call-seq: * internal class for sending large file uploads */ VALUE ruby_curl_upload_stream_set(VALUE self, VALUE stream) { ruby_curl_upload *rbcu; Data_Get_Struct(self, ruby_curl_upload, rbcu); rbcu->stream = stream; return stream; } /* * call-seq: * internal class for sending large file uploads */ VALUE ruby_curl_upload_stream_get(VALUE self) { ruby_curl_upload *rbcu; Data_Get_Struct(self, ruby_curl_upload, rbcu); return rbcu->stream; } /* * call-seq: * internal class for sending large file uploads */ VALUE ruby_curl_upload_offset_set(VALUE self, VALUE offset) { ruby_curl_upload *rbcu; Data_Get_Struct(self, ruby_curl_upload, rbcu); rbcu->offset = NUM2LONG(offset); return offset; } /* * call-seq: * internal class for sending large file uploads */ VALUE ruby_curl_upload_offset_get(VALUE self) { ruby_curl_upload *rbcu; Data_Get_Struct(self, ruby_curl_upload, rbcu); return LONG2NUM(rbcu->offset); } /* =================== INIT LIB =====================*/ void init_curb_upload() { cCurlUpload = rb_define_class_under(mCurl, "Upload", rb_cObject); rb_undef_alloc_func(cCurlUpload); rb_define_singleton_method(cCurlUpload, "new", ruby_curl_upload_new, 0); rb_define_method(cCurlUpload, "stream=", ruby_curl_upload_stream_set, 1); rb_define_method(cCurlUpload, "stream", ruby_curl_upload_stream_get, 0); rb_define_method(cCurlUpload, "offset=", ruby_curl_upload_offset_set, 1); rb_define_method(cCurlUpload, "offset", ruby_curl_upload_offset_get, 0); } curb-1.0.5/ext/curb.c0000644000004100000410000010613614362470347014420 0ustar www-datawww-data/* Curb - Libcurl(3) bindings for Ruby. * Copyright (c)2006 Ross Bamford. * Licensed under the Ruby License. See LICENSE for details. * * $Id: curb.c 35 2006-12-23 15:22:19Z roscopeco $ */ #include "curb.h" #include "curb_upload.h" VALUE mCurl; /* ================== VER QUERY FUNCS ==============*/ /* * call-seq: * Curl.ipv6? => true or false * * Returns true if the installed libcurl supports IPv6. */ static VALUE ruby_curl_ipv6_q(VALUE mod) { curl_version_info_data *ver = curl_version_info(CURLVERSION_NOW); return((ver->features & CURL_VERSION_IPV6) ? Qtrue : Qfalse); } /* * call-seq: * Curl.kerberos4? => true or false * * Returns true if the installed libcurl supports Kerberos4 authentication * with FTP connections. */ static VALUE ruby_curl_kerberos4_q(VALUE mod) { curl_version_info_data *ver = curl_version_info(CURLVERSION_NOW); return((ver->features & CURL_VERSION_KERBEROS4) ? Qtrue : Qfalse); } /* * call-seq: * Curl.ssl? => true or false * * Returns true if the installed libcurl supports SSL connections. * For libcurl versions < 7.10, always returns false. */ static VALUE ruby_curl_ssl_q(VALUE mod) { #ifdef HAVE_CURL_VERSION_SSL curl_version_info_data *ver = curl_version_info(CURLVERSION_NOW); return((ver->features & CURL_VERSION_SSL) ? Qtrue : Qfalse); #else return Qfalse; #endif } /* * call-seq: * Curl.libz? => true or false * * Returns true if the installed libcurl supports HTTP deflate * using libz. For libcurl versions < 7.10, always returns false. */ static VALUE ruby_curl_libz_q(VALUE mod) { #ifdef HAVE_CURL_VERSION_LIBZ curl_version_info_data *ver = curl_version_info(CURLVERSION_NOW); return((ver->features & CURL_VERSION_LIBZ) ? Qtrue : Qfalse); #else return Qfalse; #endif } /* * call-seq: * Curl.ntlm? => true or false * * Returns true if the installed libcurl supports HTTP NTLM. * For libcurl versions < 7.10.6, always returns false. */ static VALUE ruby_curl_ntlm_q(VALUE mod) { #ifdef HAVE_CURL_VERSION_NTLM curl_version_info_data *ver = curl_version_info(CURLVERSION_NOW); return((ver->features & CURL_VERSION_NTLM) ? Qtrue : Qfalse); #else return Qfalse; #endif } /* * call-seq: * Curl.gssnegotiate? => true or false * * Returns true if the installed libcurl supports HTTP GSS-Negotiate. * For libcurl versions < 7.10.6, always returns false. */ static VALUE ruby_curl_gssnegotiate_q(VALUE mod) { #ifdef HAVE_CURL_VERSION_GSSNEGOTIATE curl_version_info_data *ver = curl_version_info(CURLVERSION_NOW); return((ver->features & CURL_VERSION_GSSNEGOTIATE) ? Qtrue : Qfalse); #else return Qfalse; #endif } /* * call-seq: * Curl.debug? => true or false * * Returns true if the installed libcurl was built with extra debug * capabilities built-in. For libcurl versions < 7.10.6, always returns * false. */ static VALUE ruby_curl_debug_q(VALUE mod) { #ifdef HAVE_CURL_VERSION_DEBUG curl_version_info_data *ver = curl_version_info(CURLVERSION_NOW); return((ver->features & CURL_VERSION_DEBUG) ? Qtrue : Qfalse); #else return Qfalse; #endif } /* * call-seq: * Curl.asyncdns? => true or false * * Returns true if the installed libcurl was built with support for * asynchronous name lookups, which allows more exact timeouts (even * on Windows) and less blocking when using the multi interface. * For libcurl versions < 7.10.7, always returns false. */ static VALUE ruby_curl_asyncdns_q(VALUE mod) { #ifdef HAVE_CURL_VERSION_ASYNCHDNS curl_version_info_data *ver = curl_version_info(CURLVERSION_NOW); return((ver->features & CURL_VERSION_ASYNCHDNS) ? Qtrue : Qfalse); #else return Qfalse; #endif } /* * call-seq: * Curl.spnego? => true or false * * Returns true if the installed libcurl was built with support for SPNEGO * authentication (Simple and Protected GSS-API Negotiation Mechanism, defined * in RFC 2478). For libcurl versions < 7.10.8, always returns false. */ static VALUE ruby_curl_spnego_q(VALUE mod) { #ifdef HAVE_CURL_VERSION_SPNEGO curl_version_info_data *ver = curl_version_info(CURLVERSION_NOW); return((ver->features & CURL_VERSION_SPNEGO) ? Qtrue : Qfalse); #else return Qfalse; #endif } /* * call-seq: * Curl.largefile? => true or false * * Returns true if the installed libcurl was built with support for large * files. For libcurl versions < 7.11.1, always returns false. */ static VALUE ruby_curl_largefile_q(VALUE mod) { #ifdef HAVE_CURL_VERSION_LARGEFILE curl_version_info_data *ver = curl_version_info(CURLVERSION_NOW); return((ver->features & CURL_VERSION_LARGEFILE) ? Qtrue : Qfalse); #else return Qfalse; #endif } /* * call-seq: * Curl.idn? => true or false * * Returns true if the installed libcurl was built with support for IDNA, * domain names with international letters. For libcurl versions < 7.12.0, * always returns false. */ static VALUE ruby_curl_idn_q(VALUE mod) { #ifdef HAVE_CURL_VERSION_IDN curl_version_info_data *ver = curl_version_info(CURLVERSION_NOW); return((ver->features & CURL_VERSION_IDN) ? Qtrue : Qfalse); #else return Qfalse; #endif } /* * call-seq: * Curl.sspi? => true or false * * Returns true if the installed libcurl was built with support for SSPI. * This is only available on Windows and makes libcurl use Windows-provided * functions for NTLM authentication. It also allows libcurl to use the current * user and the current user's password without the app having to pass them on. * For libcurl versions < 7.13.2, always returns false. */ static VALUE ruby_curl_sspi_q(VALUE mod) { #ifdef HAVE_CURL_VERSION_SSPI curl_version_info_data *ver = curl_version_info(CURLVERSION_NOW); return((ver->features & CURL_VERSION_SSPI) ? Qtrue : Qfalse); #else return Qfalse; #endif } /* * call-seq: * Curl.conv? => true or false * * Returns true if the installed libcurl was built with support for character * conversions. For libcurl versions < 7.15.4, always returns false. */ static VALUE ruby_curl_conv_q(VALUE mod) { #ifdef HAVE_CURL_VERSION_CONV curl_version_info_data *ver = curl_version_info(CURLVERSION_NOW); return((ver->features & CURL_VERSION_CONV) ? Qtrue : Qfalse); #else return Qfalse; #endif } /* * call-seq: * Curl.http2? => true or false * * Returns true if the installed libcurl was built with support for HTTP2. * For libcurl versions < 7.33.0, always returns false. */ static VALUE ruby_curl_http2_q(VALUE mod) { #ifdef HAVE_CURL_VERSION_HTTP2 curl_version_info_data *ver = curl_version_info(CURLVERSION_NOW); return((ver->features & CURL_VERSION_HTTP2) ? Qtrue : Qfalse); #else return Qfalse; #endif } static void finalize_curb_core(VALUE data) { curl_global_cleanup(); } void Init_curb_core() { curl_version_info_data *ver; VALUE curlver, curllongver, curlvernum; curl_global_init(CURL_GLOBAL_ALL); rb_set_end_proc(finalize_curb_core, Qnil); ver = curl_version_info(CURLVERSION_NOW); mCurl = rb_define_module("Curl"); curlver = rb_str_new2(ver->version); curllongver = rb_str_new2(curl_version()); curlvernum = LONG2NUM(LIBCURL_VERSION_NUM); rb_define_const(mCurl, "CURB_VERSION", rb_str_new2(CURB_VERSION)); rb_define_const(mCurl, "VERSION", curlver); rb_define_const(mCurl, "CURL_VERSION", curlver); rb_define_const(mCurl, "VERNUM", curlvernum); rb_define_const(mCurl, "CURL_VERNUM", curlvernum); rb_define_const(mCurl, "LONG_VERSION", curllongver); rb_define_const(mCurl, "CURL_LONG_VERSION", curllongver); /* Passed to on_debug handler to indicate that the data is informational text. */ rb_define_const(mCurl, "CURLINFO_TEXT", LONG2NUM(CURLINFO_TEXT)); /* Passed to on_debug handler to indicate that the data is header (or header-like) data received from the peer. */ rb_define_const(mCurl, "CURLINFO_HEADER_IN", LONG2NUM(CURLINFO_HEADER_IN)); /* Passed to on_debug handler to indicate that the data is header (or header-like) data sent to the peer. */ rb_define_const(mCurl, "CURLINFO_HEADER_OUT", LONG2NUM(CURLINFO_HEADER_OUT)); /* Passed to on_debug handler to indicate that the data is protocol data received from the peer. */ rb_define_const(mCurl, "CURLINFO_DATA_IN", LONG2NUM(CURLINFO_DATA_IN)); /* Passed to on_debug handler to indicate that the data is protocol data sent to the peer. */ rb_define_const(mCurl, "CURLINFO_DATA_OUT", LONG2NUM(CURLINFO_DATA_OUT)); #ifdef HAVE_CURLFTPMETHOD_MULTICWD rb_define_const(mCurl, "CURL_MULTICWD", LONG2NUM(CURLFTPMETHOD_MULTICWD)); #endif #ifdef HAVE_CURLFTPMETHOD_NOCWD rb_define_const(mCurl, "CURL_NOCWD", LONG2NUM(CURLFTPMETHOD_NOCWD)); #endif #ifdef HAVE_CURLFTPMETHOD_SINGLECWD rb_define_const(mCurl, "CURL_SINGLECWD", LONG2NUM(CURLFTPMETHOD_SINGLECWD)); #endif /* When passed to Curl::Easy#proxy_type , indicates that the proxy is an HTTP proxy. (libcurl >= 7.10) */ #ifdef HAVE_CURLPROXY_HTTP rb_define_const(mCurl, "CURLPROXY_HTTP", LONG2NUM(CURLPROXY_HTTP)); #else rb_define_const(mCurl, "CURLPROXY_HTTP", LONG2NUM(-1)); #endif #ifdef CURL_VERSION_SSL rb_define_const(mCurl, "CURL_SSLVERSION_DEFAULT", LONG2NUM(CURL_SSLVERSION_DEFAULT)); rb_define_const(mCurl, "CURL_SSLVERSION_MAX_DEFAULT", LONG2NUM(CURL_SSLVERSION_MAX_DEFAULT)); rb_define_const(mCurl, "CURL_SSLVERSION_TLSv1", LONG2NUM(CURL_SSLVERSION_TLSv1)); rb_define_const(mCurl, "CURL_SSLVERSION_SSLv2", LONG2NUM(CURL_SSLVERSION_SSLv2)); rb_define_const(mCurl, "CURL_SSLVERSION_SSLv3", LONG2NUM(CURL_SSLVERSION_SSLv3)); #if HAVE_CURL_SSLVERSION_TLSV1_0 rb_define_const(mCurl, "CURL_SSLVERSION_TLSv1_0", LONG2NUM(CURL_SSLVERSION_TLSv1_0)); rb_define_const(mCurl, "CURL_SSLVERSION_MAX_TLSv1_0", LONG2NUM(CURL_SSLVERSION_MAX_TLSv1_0)); #endif #if HAVE_CURL_SSLVERSION_TLSV1_1 rb_define_const(mCurl, "CURL_SSLVERSION_TLSv1_1", LONG2NUM(CURL_SSLVERSION_TLSv1_1)); rb_define_const(mCurl, "CURL_SSLVERSION_MAX_TLSv1_1", LONG2NUM(CURL_SSLVERSION_MAX_TLSv1_1)); #endif #if HAVE_CURL_SSLVERSION_TLSV1_2 rb_define_const(mCurl, "CURL_SSLVERSION_TLSv1_2", LONG2NUM(CURL_SSLVERSION_TLSv1_2)); rb_define_const(mCurl, "CURL_SSLVERSION_MAX_TLSv1_2", LONG2NUM(CURL_SSLVERSION_MAX_TLSv1_2)); #endif #if HAVE_CURL_SSLVERSION_TLSV1_3 rb_define_const(mCurl, "CURL_SSLVERSION_TLSv1_3", LONG2NUM(CURL_SSLVERSION_TLSv1_3)); rb_define_const(mCurl, "CURL_SSLVERSION_MAX_TLSv1_3", LONG2NUM(CURL_SSLVERSION_MAX_TLSv1_3)); #endif rb_define_const(mCurl, "CURL_USESSL_CONTROL", LONG2NUM(CURB_FTPSSL_CONTROL)); rb_define_const(mCurl, "CURL_USESSL_NONE", LONG2NUM(CURB_FTPSSL_NONE)); rb_define_const(mCurl, "CURL_USESSL_TRY", LONG2NUM(CURB_FTPSSL_TRY)); rb_define_const(mCurl, "CURL_USESSL_ALL", LONG2NUM(CURB_FTPSSL_ALL)); #else rb_define_const(mCurl, "CURL_SSLVERSION_DEFAULT", LONG2NUM(-1)); rb_define_const(mCurl, "CURL_SSLVERSION_TLSv1", LONG2NUM(-1)); rb_define_const(mCurl, "CURL_SSLVERSION_SSLv2", LONG2NUM(-1)); rb_define_const(mCurl, "CURL_SSLVERSION_SSLv3", LONG2NUM(-1)); #if HAVE_CURL_SSLVERSION_TLSv1_0 rb_define_const(mCurl, "CURL_SSLVERSION_TLSv1_0", LONG2NUM(-1)); rb_define_const(mCurl, "CURL_SSLVERSION_MAX_TLSv1_0", LONG2NUM(-1)); #endif #if HAVE_CURL_SSLVERSION_TLSv1_1 rb_define_const(mCurl, "CURL_SSLVERSION_TLSv1_1", LONG2NUM(-1)); rb_define_const(mCurl, "CURL_SSLVERSION_MAX_TLSv1_1", LONG2NUM(-1)); #endif #if HAVE_CURL_SSLVERSION_TLSv1_2 rb_define_const(mCurl, "CURL_SSLVERSION_TLSv1_2", LONG2NUM(-1)); rb_define_const(mCurl, "CURL_SSLVERSION_MAX_TLSv1_2", LONG2NUM(-1)); #endif #if HAVE_CURL_SSLVERSION_TLSv1_3 rb_define_const(mCurl, "CURL_SSLVERSION_TLSv1_3", LONG2NUM(-1)); rb_define_const(mCurl, "CURL_SSLVERSION_MAX_TLSv1_3", LONG2NUM(-1)); #endif rb_define_const(mCurl, "CURL_USESSL_CONTROL", LONG2NUM(-1)); rb_define_const(mCurl, "CURL_USESSL_NONE", LONG2NUM(-1)); rb_define_const(mCurl, "CURL_USESSL_TRY", LONG2NUM(-1)); rb_define_const(mCurl, "CURL_USESSL_ALL", LONG2NUM(-1)); #endif /* When passed to Curl::Easy#proxy_type , indicates that the proxy is a SOCKS4 proxy. (libcurl >= 7.15.2) */ #ifdef HAVE_CURLPROXY_SOCKS4 rb_define_const(mCurl, "CURLPROXY_SOCKS4", LONG2NUM(CURLPROXY_SOCKS4)); #else rb_define_const(mCurl, "CURLPROXY_SOCKS4", LONG2NUM(-2)); #endif /* When passed to Curl::Easy#proxy_type , indicates that the proxy is a SOCKS4A proxy. (libcurl >= 7.18.0) */ #ifdef HAVE_CURLPROXY_SOCKS4A rb_define_const(mCurl, "CURLPROXY_SOCKS4A", LONG2NUM(CURLPROXY_SOCKS4A)); #else rb_define_const(mCurl, "CURLPROXY_SOCKS4A", LONG2NUM(-2)); #endif /* When passed to Curl::Easy#proxy_type , indicates that the proxy is a SOCKS5 proxy. (libcurl >= 7.10) */ #ifdef HAVE_CURLPROXY_SOCKS5 rb_define_const(mCurl, "CURLPROXY_SOCKS5", LONG2NUM(CURLPROXY_SOCKS5)); #else rb_define_const(mCurl, "CURLPROXY_SOCKS5", LONG2NUM(-2)); #endif /* When passed to Curl::Easy#proxy_type , indicates that the proxy is a SOCKS5 proxy (and that the proxy should resolve the hostname). (libcurl >= 7.17.2) */ #ifdef HAVE_CURLPROXY_SOCKS5_HOSTNAME rb_define_const(mCurl, "CURLPROXY_SOCKS5_HOSTNAME", LONG2NUM(CURLPROXY_SOCKS5_HOSTNAME)); #else rb_define_const(mCurl, "CURLPROXY_SOCKS5_HOSTNAME", LONG2NUM(-2)); #endif /* When passed to Curl::Easy#http_auth_types or Curl::Easy#proxy_auth_types, directs libcurl to use Basic authentication. */ #ifdef HAVE_CURLAUTH_BASIC rb_define_const(mCurl, "CURLAUTH_BASIC", LONG2NUM(CURLAUTH_BASIC)); #else rb_define_const(mCurl, "CURLAUTH_BASIC", LONG2NUM(0)); #endif /* When passed to Curl::Easy#http_auth_types or Curl::Easy#proxy_auth_types, directs libcurl to use Digest authentication. */ #ifdef HAVE_CURLAUTH_DIGEST rb_define_const(mCurl, "CURLAUTH_DIGEST", LONG2NUM(CURLAUTH_DIGEST)); #else rb_define_const(mCurl, "CURLAUTH_DIGEST", LONG2NUM(0)); #endif /* When passed to Curl::Easy#http_auth_types or Curl::Easy#proxy_auth_types, directs libcurl to use GSS Negotiate authentication. Requires a suitable GSS-API library. */ #ifdef HAVE_CURLAUTH_GSSNEGOTIATE rb_define_const(mCurl, "CURLAUTH_GSSNEGOTIATE", LONG2NUM(CURLAUTH_GSSNEGOTIATE)); #else rb_define_const(mCurl, "CURLAUTH_GSSNEGOTIATE", LONG2NUM(0)); #endif /* When passed to Curl::Easy#http_auth_types or Curl::Easy#proxy_auth_types, directs libcurl to use HTTP NTLM authentication. Requires MS Windows or OpenSSL support. */ #ifdef HAVE_CURLAUTH_NTLM rb_define_const(mCurl, "CURLAUTH_NTLM", LONG2NUM(CURLAUTH_NTLM)); #else rb_define_const(mCurl, "CURLAUTH_NTLM", LONG2NUM(0)); #endif /* When passed to Curl::Easy#http_auth_types or Curl::Easy#proxy_auth_types, allows libcurl to select any suitable authentication method except basic. */ #ifdef HAVE_CURLAUTH_ANYSAFE rb_define_const(mCurl, "CURLAUTH_ANYSAFE", LONG2NUM(CURLAUTH_ANYSAFE)); #else rb_define_const(mCurl, "CURLAUTH_ANYSAFE", LONG2NUM(0)); #endif /* When passed to Curl::Easy#http_auth_types or Curl::Easy#proxy_auth_types, allows libcurl to select any suitable authentication method. */ #ifdef HAVE_CURLAUTH_ANY rb_define_const(mCurl, "CURLAUTH_ANY", LONG2NUM(CURLAUTH_ANY)); #else rb_define_const(mCurl, "CURLAUTH_ANY", LONG2NUM(0)); #endif CURB_DEFINE(CURLOPT_VERBOSE); CURB_DEFINE(CURLOPT_HEADER); CURB_DEFINE(CURLOPT_NOPROGRESS); CURB_DEFINE(CURLOPT_NOSIGNAL); #if HAVE_CURLOPT_PATH_AS_IS CURB_DEFINE(CURLOPT_PATH_AS_IS); #endif CURB_DEFINE(CURLOPT_WRITEFUNCTION); CURB_DEFINE(CURLOPT_WRITEDATA); CURB_DEFINE(CURLOPT_READFUNCTION); CURB_DEFINE(CURLOPT_READDATA); CURB_DEFINE(CURLOPT_IOCTLFUNCTION); CURB_DEFINE(CURLOPT_IOCTLDATA); #if HAVE_CURLOPT_SEEKFUNCTION CURB_DEFINE(CURLOPT_SEEKFUNCTION); #endif #if HAVE_CURLOPT_SEEKDATA CURB_DEFINE(CURLOPT_SEEKDATA); #endif #if HAVE_CURLOPT_SOCKOPTFUNCTION CURB_DEFINE(CURLOPT_SOCKOPTFUNCTION); #endif #if HAVE_CURLOPT_SOCKOPTDATA CURB_DEFINE(CURLOPT_SOCKOPTDATA); #endif #if HAVE_CURLOPT_OPENSOCKETFUNCTION CURB_DEFINE(CURLOPT_OPENSOCKETFUNCTION); #endif #if HAVE_CURLOPT_OPENSOCKETDATA CURB_DEFINE(CURLOPT_OPENSOCKETDATA); #endif CURB_DEFINE(CURLOPT_PROGRESSFUNCTION); CURB_DEFINE(CURLOPT_PROGRESSDATA); CURB_DEFINE(CURLOPT_HEADERFUNCTION); CURB_DEFINE(CURLOPT_WRITEHEADER); CURB_DEFINE(CURLOPT_DEBUGFUNCTION); CURB_DEFINE(CURLOPT_DEBUGDATA); CURB_DEFINE(CURLOPT_SSL_CTX_FUNCTION); CURB_DEFINE(CURLOPT_SSL_CTX_DATA); CURB_DEFINE(CURLOPT_CONV_TO_NETWORK_FUNCTION); CURB_DEFINE(CURLOPT_CONV_FROM_NETWORK_FUNCTION); CURB_DEFINE(CURLOPT_CONV_FROM_UTF8_FUNCTION); #if HAVE_CURLOPT_INTERLEAVEFUNCTION CURB_DEFINE(CURLOPT_INTERLEAVEFUNCTION); #endif #if HAVE_CURLOPT_INTERLEAVEDATA CURB_DEFINE(CURLOPT_INTERLEAVEDATA); #endif #if HAVE_CURLOPT_CHUNK_BGN_FUNCTION CURB_DEFINE(CURLOPT_CHUNK_BGN_FUNCTION); #endif #if HAVE_CURLOPT_CHUNK_END_FUNCTION CURB_DEFINE(CURLOPT_CHUNK_END_FUNCTION); #endif #if HAVE_CURLOPT_CHUNK_DATA CURB_DEFINE(CURLOPT_CHUNK_DATA); #endif #if HAVE_CURLOPT_FNMATCH_FUNCTION CURB_DEFINE(CURLOPT_FNMATCH_FUNCTION); #endif #if HAVE_CURLOPT_FNMATCH_DATA CURB_DEFINE(CURLOPT_FNMATCH_DATA); #endif #if HAVE_CURLOPT_ERRORBUFFER CURB_DEFINE(CURLOPT_ERRORBUFFER); #endif #if HAVE_CURLOPT_STDERR CURB_DEFINE(CURLOPT_STDERR); #endif #if HAVE_CURLOPT_FAILONERROR CURB_DEFINE(CURLOPT_FAILONERROR); #endif CURB_DEFINE(CURLOPT_URL); #if HAVE_CURLOPT_PROTOCOLS CURB_DEFINE(CURLOPT_PROTOCOLS); #endif #if HAVE_CURLOPT_REDIR_PROTOCOLS CURB_DEFINE(CURLOPT_REDIR_PROTOCOLS); #endif CURB_DEFINE(CURLOPT_PROXY); CURB_DEFINE(CURLOPT_PROXYPORT); #if HAVE_CURLOPT_PROXYTYPE CURB_DEFINE(CURLOPT_PROXYTYPE); #endif #if HAVE_CURLOPT_NOPROXY CURB_DEFINE(CURLOPT_NOPROXY); #endif CURB_DEFINE(CURLOPT_HTTPPROXYTUNNEL); #if HAVE_CURLOPT_SOCKS5_GSSAPI_SERVICE CURB_DEFINE(CURLOPT_SOCKS5_GSSAPI_SERVICE); #endif #if HAVE_CURLOPT_SOCKS5_GSSAPI_NEC CURB_DEFINE(CURLOPT_SOCKS5_GSSAPI_NEC); #endif CURB_DEFINE(CURLOPT_INTERFACE); #if HAVE_CURLOPT_LOCALPORT CURB_DEFINE(CURLOPT_LOCALPORT); #endif CURB_DEFINE(CURLOPT_DNS_CACHE_TIMEOUT); CURB_DEFINE(CURLOPT_DNS_USE_GLOBAL_CACHE); CURB_DEFINE(CURLOPT_BUFFERSIZE); CURB_DEFINE(CURLOPT_PORT); CURB_DEFINE(CURLOPT_TCP_NODELAY); #if HAVE_CURLOPT_ADDRESS_SCOPE CURB_DEFINE(CURLOPT_ADDRESS_SCOPE); #endif CURB_DEFINE(CURLOPT_NETRC); CURB_DEFINE(CURL_NETRC_OPTIONAL); CURB_DEFINE(CURL_NETRC_IGNORED); CURB_DEFINE(CURL_NETRC_REQUIRED); #if HAVE_CURLOPT_NETRC_FILE CURB_DEFINE(CURLOPT_NETRC_FILE); #endif CURB_DEFINE(CURLOPT_USERPWD); CURB_DEFINE(CURLOPT_PROXYUSERPWD); #if HAVE_CURLOPT_USERNAME CURB_DEFINE(CURLOPT_USERNAME); #endif #if HAVE_CURLOPT_PASSWORD CURB_DEFINE(CURLOPT_PASSWORD); #endif #if HAVE_CURLOPT_PROXYUSERNAME CURB_DEFINE(CURLOPT_PASSWORD); #endif #if HAVE_CURLOPT_PROXYPASSWORD CURB_DEFINE(CURLOPT_PASSWORD); #endif #if HAVE_CURLOPT_HTTPAUTH CURB_DEFINE(CURLOPT_HTTPAUTH); #endif #if HAVE_CURLAUTH_DIGEST_IE CURB_DEFINE(CURLAUTH_DIGEST_IE); #endif #if HAVE_CURLAUTH_ONLY CURB_DEFINE(CURLAUTH_ONLY); #endif #if HAVE_CURLOPT_TLSAUTH_TYPE CURB_DEFINE(CURLOPT_TLSAUTH_TYPE); #endif #if HAVE_CURLOPT_TLSAUTH_SRP CURB_DEFINE(CURLOPT_TLSAUTH_SRP); #endif #if HAVE_CURLOPT_TLSAUTH_USERNAME CURB_DEFINE(CURLOPT_TLSAUTH_USERNAME); #endif #if HAVE_CURLOPT_TLSAUTH_PASSWORD CURB_DEFINE(CURLOPT_TLSAUTH_PASSWORD); #endif #if HAVE_CURLOPT_PROXYAUTH CURB_DEFINE(CURLOPT_PROXYAUTH); #endif #if HAVE_CURLOPT_AUTOREFERER CURB_DEFINE(CURLOPT_AUTOREFERER); #endif #if HAVE_CURLOPT_ENCODING CURB_DEFINE(CURLOPT_ENCODING); #endif #if HAVE_CURLOPT_FOLLOWLOCATION CURB_DEFINE(CURLOPT_FOLLOWLOCATION); #endif #if HAVE_CURLOPT_UNRESTRICTED_AUTH CURB_DEFINE(CURLOPT_UNRESTRICTED_AUTH); #endif #if HAVE_CURLOPT_MAXREDIRS CURB_DEFINE(CURLOPT_MAXREDIRS); #endif #if HAVE_CURLOPT_POSTREDIR CURB_DEFINE(CURLOPT_POSTREDIR); #endif #if HAVE_CURLOPT_PUT CURB_DEFINE(CURLOPT_PUT); #endif #if HAVE_CURLOPT_POST CURB_DEFINE(CURLOPT_POST); #endif CURB_DEFINE(CURLOPT_POSTFIELDS); CURB_DEFINE(CURLOPT_POSTFIELDSIZE); #if HAVE_CURLOPT_POSTFIELDSIZE_LARGE CURB_DEFINE(CURLOPT_POSTFIELDSIZE_LARGE); #endif #if HAVE_CURLOPT_COPYPOSTFIELDS CURB_DEFINE(CURLOPT_COPYPOSTFIELDS); #endif #if HAVE_CURLOPT_HTTPPOST CURB_DEFINE(CURLOPT_HTTPPOST); #endif CURB_DEFINE(CURLOPT_REFERER); CURB_DEFINE(CURLOPT_USERAGENT); CURB_DEFINE(CURLOPT_HTTPHEADER); #if HAVE_CURLOPT_PROXYHEADER CURB_DEFINE(CURLOPT_PROXYHEADER); #endif #if HAVE_CURLOPT_HTTP200ALIASES CURB_DEFINE(CURLOPT_HTTP200ALIASES); #endif CURB_DEFINE(CURLOPT_COOKIE); CURB_DEFINE(CURLOPT_COOKIEFILE); CURB_DEFINE(CURLOPT_COOKIEJAR); #if HAVE_CURLOPT_COOKIESESSION CURB_DEFINE(CURLOPT_COOKIESESSION); #endif #if HAVE_CURLOPT_COOKIELIST CURB_DEFINE(CURLOPT_COOKIELIST); #endif #if HAVE_CURLOPT_HTTPGET CURB_DEFINE(CURLOPT_HTTPGET); #endif CURB_DEFINE(CURLOPT_HTTP_VERSION); CURB_DEFINE(CURL_HTTP_VERSION_NONE); CURB_DEFINE(CURL_HTTP_VERSION_1_0); CURB_DEFINE(CURL_HTTP_VERSION_1_1); #if LIBCURL_VERSION_NUM >= 0x072100 /* 7.33.0 */ CURB_DEFINE(CURL_HTTP_VERSION_2_0); #endif #if LIBCURL_VERSION_NUM >= 0x072f00 /* 7.47.0 */ CURB_DEFINE(CURL_HTTP_VERSION_2TLS); #endif #if HAVE_CURLOPT_IGNORE_CONTENT_LENGTH CURB_DEFINE(CURLOPT_IGNORE_CONTENT_LENGTH); #endif #if HAVE_CURLOPT_HTTP_CONTENT_DECODING CURB_DEFINE(CURLOPT_HTTP_CONTENT_DECODING); #endif #if HAVE_CURLOPT_HTTP_TRANSFER_DECODING CURB_DEFINE(CURLOPT_HTTP_TRANSFER_DECODING); #endif #if HAVE_CURLOPT_MAIL_FROM CURB_DEFINE(CURLOPT_MAIL_FROM); #endif #if HAVE_CURLOPT_MAIL_RCPT CURB_DEFINE(CURLOPT_MAIL_RCPT); #endif #if HAVE_CURLOPT_TFTP_BLKSIZE CURB_DEFINE(CURLOPT_TFTP_BLKSIZE); #endif #if HAVE_CURLOPT_FTPPORT CURB_DEFINE(CURLOPT_FTPPORT); #endif #if HAVE_CURLOPT_QUOTE CURB_DEFINE(CURLOPT_QUOTE); #endif #if HAVE_CURLOPT_POSTQUOTE CURB_DEFINE(CURLOPT_POSTQUOTE); #endif #if HAVE_CURLOPT_PREQUOTE CURB_DEFINE(CURLOPT_PREQUOTE); #endif #if HAVE_CURLOPT_DIRLISTONLY CURB_DEFINE(CURLOPT_DIRLISTONLY); #endif #if HAVE_CURLOPT_APPEND CURB_DEFINE(CURLOPT_APPEND); #endif #if HAVE_CURLOPT_FTP_USE_EPRT CURB_DEFINE(CURLOPT_FTP_USE_EPRT); #endif #if HAVE_CURLOPT_FTP_USE_EPSV CURB_DEFINE(CURLOPT_FTP_USE_EPSV); #endif #if HAVE_CURLOPT_FTP_USE_PRET CURB_DEFINE(CURLOPT_FTP_USE_PRET); #endif #if HAVE_CURLOPT_FTP_CREATE_MISSING_DIRS CURB_DEFINE(CURLOPT_FTP_CREATE_MISSING_DIRS); #endif #if HAVE_CURLOPT_FTP_RESPONSE_TIMEOUT CURB_DEFINE(CURLOPT_FTP_RESPONSE_TIMEOUT); #endif #if HAVE_CURLOPT_FTP_ALTERNATIVE_TO_USER CURB_DEFINE(CURLOPT_FTP_ALTERNATIVE_TO_USER); #endif #if HAVE_CURLOPT_FTP_SKIP_PASV_IP CURB_DEFINE(CURLOPT_FTP_SKIP_PASV_IP); #endif #if HAVE_CURLOPT_FTPSSLAUTH CURB_DEFINE(CURLOPT_FTPSSLAUTH); #endif #if HAVE_CURLFTPAUTH_DEFAULT CURB_DEFINE(CURLFTPAUTH_DEFAULT); #endif #if HAVE_CURLFTPAUTH_SSL CURB_DEFINE(CURLFTPAUTH_SSL); #endif #if HAVE_CURLFTPAUTH_TLS CURB_DEFINE(CURLFTPAUTH_TLS); #endif #if HAVE_CURLOPT_FTP_SSL_CCC CURB_DEFINE(CURLOPT_FTP_SSL_CCC); #endif #if HAVE_CURLFTPSSL_CCC_NONE CURB_DEFINE(CURLFTPSSL_CCC_NONE); #endif #if HAVE_CURLFTPSSL_CCC_PASSIVE CURB_DEFINE(CURLFTPSSL_CCC_PASSIVE); #endif #if HAVE_CURLFTPSSL_CCC_ACTIVE CURB_DEFINE(CURLFTPSSL_CCC_ACTIVE); #endif #if HAVE_CURLOPT_FTP_ACCOUNT CURB_DEFINE(CURLOPT_FTP_ACCOUNT); #endif #if HAVE_CURLOPT_FTP_FILEMETHOD CURB_DEFINE(CURLOPT_FTP_FILEMETHOD); #endif #if HAVE_CURLFTPMETHOD_MULTICWD CURB_DEFINE(CURLFTPMETHOD_MULTICWD); #endif #if HAVE_CURLFTPMETHOD_NOCWD CURB_DEFINE(CURLFTPMETHOD_NOCWD); #endif #if HAVE_CURLFTPMETHOD_SINGLECWD CURB_DEFINE(CURLFTPMETHOD_SINGLECWD); #endif #if HAVE_CURLOPT_RTSP_REQUEST CURB_DEFINE(CURLOPT_RTSP_REQUEST); #endif #if HAVE_CURL_RTSPREQ_OPTIONS CURB_DEFINE(CURL_RTSPREQ_OPTIONS); #endif #if HAVE_CURL_RTSPREQ_DESCRIBE CURB_DEFINE(CURL_RTSPREQ_DESCRIBE); #endif #if HAVE_CURL_RTSPREQ_ANNOUNCE CURB_DEFINE(CURL_RTSPREQ_ANNOUNCE); #endif #if HAVE_CURL_RTSPREQ_SETUP CURB_DEFINE(CURL_RTSPREQ_SETUP); #endif #if HAVE_CURL_RTSPREQ_PLAY CURB_DEFINE(CURL_RTSPREQ_PLAY); #endif #if HAVE_CURL_RTSPREQ_PAUSE CURB_DEFINE(CURL_RTSPREQ_PAUSE); #endif #if HAVE_CURL_RTSPREQ_TEARDOWN CURB_DEFINE(CURL_RTSPREQ_TEARDOWN); #endif #if HAVE_CURL_RTSPREQ_GET_PARAMETER CURB_DEFINE(CURL_RTSPREQ_GET_PARAMETER); #endif #if HAVE_CURL_RTSPREQ_SET_PARAMETER CURB_DEFINE(CURL_RTSPREQ_SET_PARAMETER); #endif #if HAVE_CURL_RTSPREQ_RECORD CURB_DEFINE(CURL_RTSPREQ_RECORD); #endif #if HAVE_CURL_RTSPREQ_RECEIVE CURB_DEFINE(CURL_RTSPREQ_RECEIVE); #endif #if HAVE_CURLOPT_RTSP_SESSION_ID CURB_DEFINE(CURLOPT_RTSP_SESSION_ID); #endif #if HAVE_CURLOPT_RTSP_STREAM_URI CURB_DEFINE(CURLOPT_RTSP_STREAM_URI); #endif #if HAVE_CURLOPT_RTSP_TRANSPORT CURB_DEFINE(CURLOPT_RTSP_TRANSPORT); #endif #if HAVE_CURLOPT_RTSP_HEADER CURB_DEFINE(CURLOPT_RTSP_HEADER); #endif #if HAVE_CURLOPT_RTSP_CLIENT_CSEQ CURB_DEFINE(CURLOPT_RTSP_CLIENT_CSEQ); #endif #if HAVE_CURLOPT_RTSP_SERVER_CSEQ CURB_DEFINE(CURLOPT_RTSP_SERVER_CSEQ); #endif CURB_DEFINE(CURLOPT_TRANSFERTEXT); #if HAVE_CURLOPT_PROXY_TRANSFER_MODE CURB_DEFINE(CURLOPT_PROXY_TRANSFER_MODE); #endif #if HAVE_CURLOPT_CRLF CURB_DEFINE(CURLOPT_CRLF); #endif #if HAVE_CURLOPT_RANGE CURB_DEFINE(CURLOPT_RANGE); #endif #if HAVE_CURLOPT_RESUME_FROM CURB_DEFINE(CURLOPT_RESUME_FROM); #endif #if HAVE_CURLOPT_RESUME_FROM_LARGE CURB_DEFINE(CURLOPT_RESUME_FROM_LARGE); #endif #if HAVE_CURLOPT_CUSTOMREQUEST CURB_DEFINE(CURLOPT_CUSTOMREQUEST); #endif #if HAVE_CURLOPT_FILETIME CURB_DEFINE(CURLOPT_FILETIME); #endif #if HAVE_CURLOPT_NOBODY CURB_DEFINE(CURLOPT_NOBODY); #endif #if HAVE_CURLOPT_INFILESIZE CURB_DEFINE(CURLOPT_INFILESIZE); #endif #if HAVE_CURLOPT_INFILESIZE_LARGE CURB_DEFINE(CURLOPT_INFILESIZE_LARGE); #endif #if HAVE_CURLOPT_UPLOAD CURB_DEFINE(CURLOPT_UPLOAD); #endif #if HAVE_CURLOPT_MAXFILESIZE CURB_DEFINE(CURLOPT_MAXFILESIZE); #endif #if HAVE_CURLOPT_MAXFILESIZE_LARGE CURB_DEFINE(CURLOPT_MAXFILESIZE_LARGE); #endif #if HAVE_CURLOPT_TIMECONDITION CURB_DEFINE(CURLOPT_TIMECONDITION); #endif #if HAVE_CURLOPT_TIMEVALUE CURB_DEFINE(CURLOPT_TIMEVALUE); #endif #if HAVE_CURLOPT_TIMEOUT CURB_DEFINE(CURLOPT_TIMEOUT); #endif #if HAVE_CURLOPT_TIMEOUT_MS CURB_DEFINE(CURLOPT_TIMEOUT_MS); #endif #if HAVE_CURLOPT_LOW_SPEED_LIMIT CURB_DEFINE(CURLOPT_LOW_SPEED_LIMIT); #endif #if HAVE_CURLOPT_LOW_SPEED_TIME CURB_DEFINE(CURLOPT_LOW_SPEED_TIME); #endif #if HAVE_CURLOPT_MAX_SEND_SPEED_LARGE CURB_DEFINE(CURLOPT_MAX_SEND_SPEED_LARGE); #endif #if HAVE_CURLOPT_MAX_RECV_SPEED_LARGE CURB_DEFINE(CURLOPT_MAX_RECV_SPEED_LARGE); #endif #if HAVE_CURLOPT_MAXCONNECTS CURB_DEFINE(CURLOPT_MAXCONNECTS); #endif #if HAVE_CURLOPT_CLOSEPOLICY CURB_DEFINE(CURLOPT_CLOSEPOLICY); #endif #if HAVE_CURLOPT_FRESH_CONNECT CURB_DEFINE(CURLOPT_FRESH_CONNECT); #endif #if HAVE_CURLOPT_FORBID_REUSE CURB_DEFINE(CURLOPT_FORBID_REUSE); #endif #if HAVE_CURLOPT_CONNECTTIMEOUT CURB_DEFINE(CURLOPT_CONNECTTIMEOUT); #endif #if HAVE_CURLOPT_CONNECTTIMEOUT_MS CURB_DEFINE(CURLOPT_CONNECTTIMEOUT_MS); #endif #if HAVE_CURLOPT_IPRESOLVE CURB_DEFINE(CURLOPT_IPRESOLVE); #endif #if HAVE_CURL_IPRESOLVE_WHATEVER CURB_DEFINE(CURL_IPRESOLVE_WHATEVER); #endif #if HAVE_CURL_IPRESOLVE_V4 CURB_DEFINE(CURL_IPRESOLVE_V4); #endif #if HAVE_CURL_IPRESOLVE_V6 CURB_DEFINE(CURL_IPRESOLVE_V6); #endif #if HAVE_CURLOPT_CONNECT_ONLY CURB_DEFINE(CURLOPT_CONNECT_ONLY); #endif #if HAVE_CURLOPT_USE_SSL CURB_DEFINE(CURLOPT_USE_SSL); #endif #if HAVE_CURLUSESSL_NONE CURB_DEFINE(CURLUSESSL_NONE); #endif #if HAVE_CURLUSESSL_TRY CURB_DEFINE(CURLUSESSL_TRY); #endif #if HAVE_CURLUSESSL_CONTROL CURB_DEFINE(CURLUSESSL_CONTROL); #endif #if HAVE_CURLUSESSL_ALL CURB_DEFINE(CURLUSESSL_ALL); #endif #if HAVE_CURLOPT_RESOLVE CURB_DEFINE(CURLOPT_RESOLVE); #endif #if HAVE_CURLOPT_SSLCERT CURB_DEFINE(CURLOPT_SSLCERT); #endif #if HAVE_CURLOPT_SSLCERTTYPE CURB_DEFINE(CURLOPT_SSLCERTTYPE); #endif #if HAVE_CURLOPT_SSLKEY CURB_DEFINE(CURLOPT_SSLKEY); #endif #if HAVE_CURLOPT_SSLKEYTYPE CURB_DEFINE(CURLOPT_SSLKEYTYPE); #endif #if HAVE_CURLOPT_KEYPASSWD CURB_DEFINE(CURLOPT_KEYPASSWD); #endif #if HAVE_CURLOPT_SSLENGINE CURB_DEFINE(CURLOPT_SSLENGINE); #endif #if HAVE_CURLOPT_SSLENGINE_DEFAULT CURB_DEFINE(CURLOPT_SSLENGINE_DEFAULT); #endif #if HAVE_CURLOPT_SSLVERSION CURB_DEFINE(CURLOPT_SSLVERSION); #endif #if HAVE_CURL_SSLVERSION_TLSv1 CURB_DEFINE(CURL_SSLVERSION_TLSv1); #endif #if HAVE_CURL_SSLVERSION_SSLv2 CURB_DEFINE(CURL_SSLVERSION_SSLv2); #endif #if HAVE_CURL_SSLVERSION_SSLv3 CURB_DEFINE(CURL_SSLVERSION_SSLv3); #endif #if HAVE_CURL_SSLVERSION_TLSv1_0 CURB_DEFINE(CURL_SSLVERSION_TLSv1_0); CURB_DEFINE(CURL_SSLVERSION_MAX_TLSv1_0); #endif #if HAVE_CURL_SSLVERSION_TLSv1_1 CURB_DEFINE(CURL_SSLVERSION_TLSv1_1); CURB_DEFINE(CURL_SSLVERSION_MAX_TLSv1_1); #endif #if HAVE_CURL_SSLVERSION_TLSv1_2 CURB_DEFINE(CURL_SSLVERSION_TLSv1_2); CURB_DEFINE(CURL_SSLVERSION_MAX_TLSv1_2); #endif #if HAVE_CURL_SSLVERSION_TLSv1_3 CURB_DEFINE(CURL_SSLVERSION_TLSv1_3); CURB_DEFINE(CURL_SSLVERSION_MAX_TLSv1_3); #endif #if HAVE_CURLOPT_SSL_VERIFYPEER CURB_DEFINE(CURLOPT_SSL_VERIFYPEER); #endif #if HAVE_CURLOPT_CAINFO CURB_DEFINE(CURLOPT_CAINFO); #endif #if HAVE_CURLOPT_ISSUERCERT CURB_DEFINE(CURLOPT_ISSUERCERT); #endif #if HAVE_CURLOPT_CAPATH CURB_DEFINE(CURLOPT_CAPATH); #endif #if HAVE_CURLOPT_CRLFILE CURB_DEFINE(CURLOPT_CRLFILE); #endif #if HAVE_CURLOPT_SSL_VERIFYHOST CURB_DEFINE(CURLOPT_SSL_VERIFYHOST); #endif #if HAVE_CURLOPT_CERTINFO CURB_DEFINE(CURLOPT_CERTINFO); #endif #if HAVE_CURLOPT_RANDOM_FILE CURB_DEFINE(CURLOPT_RANDOM_FILE); #endif #if HAVE_CURLOPT_EGDSOCKET CURB_DEFINE(CURLOPT_EGDSOCKET); #endif #if HAVE_CURLOPT_SSL_CIPHER_LIST CURB_DEFINE(CURLOPT_SSL_CIPHER_LIST); #endif #if HAVE_CURLOPT_SSL_SESSIONID_CACHE CURB_DEFINE(CURLOPT_SSL_SESSIONID_CACHE); #endif #if HAVE_CURLOPT_KRBLEVEL CURB_DEFINE(CURLOPT_KRBLEVEL); #endif #if HAVE_CURLOPT_SSH_AUTH_TYPES CURB_DEFINE(CURLOPT_SSH_AUTH_TYPES); #endif #if HAVE_CURLOPT_SSH_HOST_PUBLIC_KEY_MD5 CURB_DEFINE(CURLOPT_SSH_HOST_PUBLIC_KEY_MD5); #endif #if HAVE_CURLOPT_SSH_PUBLIC_KEYFILE CURB_DEFINE(CURLOPT_SSH_PUBLIC_KEYFILE); #endif #if HAVE_CURLOPT_SSH_PRIVATE_KEYFILE CURB_DEFINE(CURLOPT_SSH_PRIVATE_KEYFILE); #endif #if HAVE_CURLOPT_SSH_KNOWNHOSTS CURB_DEFINE(CURLOPT_SSH_KNOWNHOSTS); #endif #if HAVE_CURLOPT_SSH_KEYFUNCTION CURB_DEFINE(CURLOPT_SSH_KEYFUNCTION); #endif #if HAVE_CURLKHSTAT_FINE_ADD_TO_FILE CURB_DEFINE(CURLKHSTAT_FINE_ADD_TO_FILE); #endif #if HAVE_CURLKHSTAT_FINE CURB_DEFINE(CURLKHSTAT_FINE); #endif #if HAVE_CURLKHSTAT_REJECT CURB_DEFINE(CURLKHSTAT_REJECT); #endif #if HAVE_CURLKHSTAT_DEFER CURB_DEFINE(CURLKHSTAT_DEFER); #endif #if HAVE_CURLOPT_SSH_KEYDATA CURB_DEFINE(CURLOPT_SSH_KEYDATA); #endif #if HAVE_CURLOPT_PRIVATE CURB_DEFINE(CURLOPT_PRIVATE); #endif #if HAVE_CURLOPT_SHARE CURB_DEFINE(CURLOPT_SHARE); #endif #if HAVE_CURLOPT_NEW_FILE_PERMS CURB_DEFINE(CURLOPT_NEW_FILE_PERMS); #endif #if HAVE_CURLOPT_NEW_DIRECTORY_PERMS CURB_DEFINE(CURLOPT_NEW_DIRECTORY_PERMS); #endif #if HAVE_CURLOPT_TELNETOPTIONS CURB_DEFINE(CURLOPT_TELNETOPTIONS); #endif #if HAVE_CURLOPT_GSSAPI_DELEGATION CURB_DEFINE(CURLOPT_GSSAPI_DELEGATION); #endif #if HAVE_CURLGSSAPI_DELEGATION_FLAG CURB_DEFINE(CURLGSSAPI_DELEGATION_FLAG); #endif #if HAVE_CURLGSSAPI_DELEGATION_POLICY_FLAG CURB_DEFINE(CURLGSSAPI_DELEGATION_POLICY_FLAG); #endif #if HAVE_CURLOPT_UNIX_SOCKET_PATH CURB_DEFINE(CURLOPT_UNIX_SOCKET_PATH); #endif #if HAVE_CURLOPT_PIPEWAIT CURB_DEFINE(CURLOPT_PIPEWAIT); #endif #if HAVE_CURLOPT_TCP_KEEPALIVE CURB_DEFINE(CURLOPT_TCP_KEEPALIVE); CURB_DEFINE(CURLOPT_TCP_KEEPIDLE); CURB_DEFINE(CURLOPT_TCP_KEEPINTVL); #endif #if HAVE_CURLOPT_HAPROXYPROTOCOL CURB_DEFINE(CURLOPT_HAPROXYPROTOCOL); #endif #if HAVE_CURLOPT_PROXY_SSL_VERIFYHOST CURB_DEFINE(CURLOPT_PROXY_SSL_VERIFYHOST); #endif #if HAVE_CURLPROTO_RTMPTE CURB_DEFINE(CURLPROTO_RTMPTE); #endif #if HAVE_CURLPROTO_RTMPTS CURB_DEFINE(CURLPROTO_RTMPTS); #endif #if HAVE_CURLPROTO_SMBS CURB_DEFINE(CURLPROTO_SMBS); #endif #if HAVE_CURLPROTO_LDAP CURB_DEFINE(CURLPROTO_LDAP); #endif #if HAVE_CURLPROTO_FTP CURB_DEFINE(CURLPROTO_FTP); #endif #if HAVE_CURLPROTO_SMTPS CURB_DEFINE(CURLPROTO_SMTPS); #endif #if HAVE_CURLPROTO_HTTP CURB_DEFINE(CURLPROTO_HTTP); #endif #if HAVE_CURLPROTO_SMTP CURB_DEFINE(CURLPROTO_SMTP); #endif #if HAVE_CURLPROTO_TFTP CURB_DEFINE(CURLPROTO_TFTP); #endif #if HAVE_CURLPROTO_LDAPS CURB_DEFINE(CURLPROTO_LDAPS); #endif #if HAVE_CURLPROTO_IMAPS CURB_DEFINE(CURLPROTO_IMAPS); #endif #if HAVE_CURLPROTO_SCP CURB_DEFINE(CURLPROTO_SCP); #endif #if HAVE_CURLPROTO_SFTP CURB_DEFINE(CURLPROTO_SFTP); #endif #if HAVE_CURLPROTO_TELNET CURB_DEFINE(CURLPROTO_TELNET); #endif #if HAVE_CURLPROTO_FILE CURB_DEFINE(CURLPROTO_FILE); #endif #if HAVE_CURLPROTO_FTPS CURB_DEFINE(CURLPROTO_FTPS); #endif #if HAVE_CURLPROTO_HTTPS CURB_DEFINE(CURLPROTO_HTTPS); #endif #if HAVE_CURLPROTO_IMAP CURB_DEFINE(CURLPROTO_IMAP); #endif #if HAVE_CURLPROTO_POP3 CURB_DEFINE(CURLPROTO_POP3); #endif #if HAVE_CURLPROTO_GOPHER CURB_DEFINE(CURLPROTO_GOPHER); #endif #if HAVE_CURLPROTO_DICT CURB_DEFINE(CURLPROTO_DICT); #endif #if HAVE_CURLPROTO_SMB CURB_DEFINE(CURLPROTO_SMB); #endif #if HAVE_CURLPROTO_RTMP CURB_DEFINE(CURLPROTO_RTMP); #endif #if HAVE_CURLPROTO_ALL CURB_DEFINE(CURLPROTO_ALL); #endif #if HAVE_CURLPROTO_RTMPE CURB_DEFINE(CURLPROTO_RTMPE); #endif #if HAVE_CURLPROTO_RTMPS CURB_DEFINE(CURLPROTO_RTMPS); #endif #if HAVE_CURLPROTO_RTMPT CURB_DEFINE(CURLPROTO_RTMPT); #endif #if HAVE_CURLPROTO_POP3S CURB_DEFINE(CURLPROTO_POP3S); #endif #if HAVE_CURLPROTO_RTSP CURB_DEFINE(CURLPROTO_RTSP); #endif #if LIBCURL_VERSION_NUM >= 0x072B00 /* 7.43.0 */ CURB_DEFINE(CURLPIPE_NOTHING); CURB_DEFINE(CURLPIPE_HTTP1); CURB_DEFINE(CURLPIPE_MULTIPLEX); rb_define_const(mCurl, "PIPE_NOTHING", LONG2NUM(CURLPIPE_NOTHING)); rb_define_const(mCurl, "PIPE_HTTP1", LONG2NUM(CURLPIPE_HTTP1)); rb_define_const(mCurl, "PIPE_MULTIPLEX", LONG2NUM(CURLPIPE_MULTIPLEX)); #endif #if LIBCURL_VERSION_NUM >= 0x072100 /* 7.33.0 */ rb_define_const(mCurl, "HTTP_2_0", LONG2NUM(CURL_HTTP_VERSION_2_0)); #endif rb_define_const(mCurl, "HTTP_1_1", LONG2NUM(CURL_HTTP_VERSION_1_1)); rb_define_const(mCurl, "HTTP_1_0", LONG2NUM(CURL_HTTP_VERSION_1_0)); rb_define_const(mCurl, "HTTP_NONE", LONG2NUM(CURL_HTTP_VERSION_NONE)); rb_define_singleton_method(mCurl, "ipv6?", ruby_curl_ipv6_q, 0); rb_define_singleton_method(mCurl, "kerberos4?", ruby_curl_kerberos4_q, 0); rb_define_singleton_method(mCurl, "ssl?", ruby_curl_ssl_q, 0); rb_define_singleton_method(mCurl, "libz?", ruby_curl_libz_q, 0); rb_define_singleton_method(mCurl, "ntlm?", ruby_curl_ntlm_q, 0); rb_define_singleton_method(mCurl, "gssnegotiate?", ruby_curl_gssnegotiate_q, 0); rb_define_singleton_method(mCurl, "debug?", ruby_curl_debug_q, 0); rb_define_singleton_method(mCurl, "asyncdns?", ruby_curl_asyncdns_q, 0); rb_define_singleton_method(mCurl, "spnego?", ruby_curl_spnego_q, 0); rb_define_singleton_method(mCurl, "largefile?", ruby_curl_largefile_q, 0); rb_define_singleton_method(mCurl, "idn?", ruby_curl_idn_q, 0); rb_define_singleton_method(mCurl, "sspi?", ruby_curl_sspi_q, 0); rb_define_singleton_method(mCurl, "conv?", ruby_curl_conv_q, 0); rb_define_singleton_method(mCurl, "http2?", ruby_curl_http2_q, 0); init_curb_errors(); init_curb_easy(); init_curb_postfield(); init_curb_multi(); init_curb_upload(); } curb-1.0.5/ext/curb_postfield.c0000644000004100000410000004772014362470347016474 0ustar www-datawww-data/* curb_postfield.c - Field class for POST method * Copyright (c)2006 Ross Bamford. * Licensed under the Ruby License. See LICENSE for details. * * $Id: curb_postfield.c 30 2006-12-09 12:30:24Z roscopeco $ */ #include "curb_postfield.h" #include "curb_errors.h" extern VALUE mCurl; static VALUE idCall; #ifdef RDOC_NEVER_DEFINED mCurl = rb_define_module("Curl"); #endif VALUE cCurlPostField; /* ================= APPEND FORM FUNC ================ */ /* This gets called by the post method on Curl::Easy for each postfield * supplied in the arguments. It's job is to add the supplied field to * the list that's being built for a perform. * * THIS FUNC MODIFIES ITS ARGUMENTS. See curl_formadd(3) for details. */ void append_to_form(VALUE self, struct curl_httppost **first, struct curl_httppost **last) { ruby_curl_postfield *rbcpf; CURLFORMcode result = -1; Data_Get_Struct(self, ruby_curl_postfield, rbcpf); if (rbcpf->name == Qnil) { rb_raise(eCurlErrInvalidPostField, "Cannot post unnamed field"); } else { if ((rbcpf->local_file != Qnil) || (rbcpf->remote_file != Qnil)) { // is a file upload field if (rbcpf->content_proc != Qnil) { // with content proc rbcpf->buffer_str = rb_funcall(rbcpf->content_proc, idCall, 1, self); if (rbcpf->remote_file == Qnil) { rb_raise(eCurlErrInvalidPostField, "Cannot post file upload field with no filename"); } else { if (rbcpf->content_type == Qnil) { result = curl_formadd(first, last, CURLFORM_PTRNAME, StringValuePtr(rbcpf->name), CURLFORM_BUFFER, StringValuePtr(rbcpf->remote_file), CURLFORM_BUFFERPTR, StringValuePtr(rbcpf->buffer_str), CURLFORM_BUFFERLENGTH, RSTRING_LEN(rbcpf->buffer_str), CURLFORM_END); } else { result = curl_formadd(first, last, CURLFORM_PTRNAME, StringValuePtr(rbcpf->name), CURLFORM_BUFFER, StringValuePtr(rbcpf->remote_file), CURLFORM_BUFFERPTR, StringValuePtr(rbcpf->buffer_str), CURLFORM_BUFFERLENGTH, RSTRING_LEN(rbcpf->buffer_str), CURLFORM_CONTENTTYPE, StringValuePtr(rbcpf->content_type), CURLFORM_END); } } } else if (rbcpf->content != Qnil) { // with content if (rbcpf->remote_file == Qnil) { rb_raise(eCurlErrInvalidPostField, "Cannot post file upload field with no filename"); } else { if (rbcpf->content_type == Qnil) { result = curl_formadd(first, last, CURLFORM_PTRNAME, StringValuePtr(rbcpf->name), CURLFORM_BUFFER, StringValuePtr(rbcpf->remote_file), CURLFORM_BUFFERPTR, StringValuePtr(rbcpf->content), CURLFORM_BUFFERLENGTH, RSTRING_LEN(rbcpf->content), CURLFORM_END); } else { result = curl_formadd(first, last, CURLFORM_PTRNAME, StringValuePtr(rbcpf->name), CURLFORM_BUFFER, StringValuePtr(rbcpf->remote_file), CURLFORM_BUFFERPTR, StringValuePtr(rbcpf->content), CURLFORM_BUFFERLENGTH, RSTRING_LEN(rbcpf->content), CURLFORM_CONTENTTYPE, StringValuePtr(rbcpf->content_type), CURLFORM_END); } } } else if (rbcpf->local_file != Qnil) { // with local filename if (rbcpf->local_file == Qnil) { rb_raise(eCurlErrInvalidPostField, "Cannot post file upload field no filename"); } else { if (rbcpf->remote_file == Qnil) { rbcpf->remote_file = rbcpf->local_file; } if (rbcpf->content_type == Qnil) { result = curl_formadd(first, last, CURLFORM_PTRNAME, StringValuePtr(rbcpf->name), CURLFORM_FILE, StringValuePtr(rbcpf->local_file), CURLFORM_FILENAME, StringValuePtr(rbcpf->remote_file), CURLFORM_END); } else { result = curl_formadd(first, last, CURLFORM_PTRNAME, StringValuePtr(rbcpf->name), CURLFORM_FILE, StringValuePtr(rbcpf->local_file), CURLFORM_FILENAME, StringValuePtr(rbcpf->remote_file), CURLFORM_CONTENTTYPE, StringValuePtr(rbcpf->content_type), CURLFORM_END); } } } else { rb_raise(eCurlErrInvalidPostField, "Cannot post file upload field with no data"); } } else { // is a content field if (rbcpf->content_proc != Qnil) { rbcpf->buffer_str = rb_funcall(rbcpf->content_proc, idCall, 1, self); if (rbcpf->content_type == Qnil) { result = curl_formadd(first, last, CURLFORM_PTRNAME, StringValuePtr(rbcpf->name), CURLFORM_PTRCONTENTS, StringValuePtr(rbcpf->buffer_str), CURLFORM_CONTENTSLENGTH, RSTRING_LEN(rbcpf->buffer_str), CURLFORM_END); } else { result = curl_formadd(first, last, CURLFORM_PTRNAME, StringValuePtr(rbcpf->name), CURLFORM_PTRCONTENTS, StringValuePtr(rbcpf->buffer_str), CURLFORM_CONTENTSLENGTH, RSTRING_LEN(rbcpf->buffer_str), CURLFORM_CONTENTTYPE, StringValuePtr(rbcpf->content_type), CURLFORM_END); } } else if (rbcpf->content != Qnil) { if (rbcpf->content_type == Qnil) { result = curl_formadd(first, last, CURLFORM_PTRNAME, StringValuePtr(rbcpf->name), CURLFORM_PTRCONTENTS, StringValuePtr(rbcpf->content), CURLFORM_CONTENTSLENGTH, RSTRING_LEN(rbcpf->content), CURLFORM_END); } else { result = curl_formadd(first, last, CURLFORM_PTRNAME, StringValuePtr(rbcpf->name), CURLFORM_PTRCONTENTS, StringValuePtr(rbcpf->content), CURLFORM_CONTENTSLENGTH, RSTRING_LEN(rbcpf->content), CURLFORM_CONTENTTYPE, StringValuePtr(rbcpf->content_type), CURLFORM_END); } } else { rb_raise(eCurlErrInvalidPostField, "Cannot post content field with no data"); } } } if (result != 0) { const char *reason = NULL; switch (result) { case CURL_FORMADD_MEMORY: reason = "Memory allocation failed"; break; case CURL_FORMADD_OPTION_TWICE: reason = "Duplicate option"; break; case CURL_FORMADD_NULL: reason = "Unexpected NULL string"; break; case CURL_FORMADD_UNKNOWN_OPTION: reason = "Unknown option"; break; case CURL_FORMADD_INCOMPLETE: reason = "Incomplete form data"; break; case CURL_FORMADD_ILLEGAL_ARRAY: reason = "Illegal array [BINDING BUG]"; break; case CURL_FORMADD_DISABLED: reason = "Installed libcurl cannot support requested feature(s)"; break; default: reason = "Unknown error"; } rb_raise(eCurlErrInvalidPostField, "Failed to add field (%s)", reason); } } /* ================== MARK/FREE FUNC ==================*/ void curl_postfield_mark(ruby_curl_postfield *rbcpf) { rb_gc_mark(rbcpf->name); rb_gc_mark(rbcpf->content); rb_gc_mark(rbcpf->content_type); rb_gc_mark(rbcpf->local_file); rb_gc_mark(rbcpf->remote_file); rb_gc_mark(rbcpf->buffer_str); } void curl_postfield_free(ruby_curl_postfield *rbcpf) { free(rbcpf); } /* ================= ALLOC METHODS ====================*/ /* * call-seq: * Curl::PostField.content(name, content) => # * Curl::PostField.content(name, content, content_type = nil) => # * Curl::PostField.content(name, content_type = nil) { |field| ... } => # * * Create a new Curl::PostField, supplying the field name, content, * and, optionally, Content-type (curl will attempt to determine this if * not specified). * * The block form allows a block to supply the content for this field, called * during the perform. The block should return a ruby string with the field * data. */ static VALUE ruby_curl_postfield_new_content(int argc, VALUE *argv, VALUE klass) { ruby_curl_postfield *rbcpf = ALLOC(ruby_curl_postfield); // wierdness - we actually require two args, unless a block is provided, but // we have to work that out below. rb_scan_args(argc, argv, "12&", &rbcpf->name, &rbcpf->content, &rbcpf->content_type, &rbcpf->content_proc); // special handling if theres a block, second arg is actually content_type if (rbcpf->content_proc != Qnil) { if (rbcpf->content != Qnil) { // we were given a content-type rbcpf->content_type = rbcpf->content; rbcpf->content = Qnil; } else { // default content type rbcpf->content_type = Qnil; } } else { // no block, so make sure content was provided if (rbcpf->content == Qnil) { rb_raise(rb_eArgError, "Incorrect number of arguments (expected 2 or 3)"); } } /* assoc objects */ rbcpf->local_file = Qnil; rbcpf->remote_file = Qnil; rbcpf->buffer_str = Qnil; return Data_Wrap_Struct(cCurlPostField, curl_postfield_mark, curl_postfield_free, rbcpf); } /* * call-seq: * Curl::PostField.file(name, local_file_name) => # * Curl::PostField.file(name, local_file_name, remote_file_name = local_file_name) => # * Curl::PostField.file(name, remote_file_name) { |field| ... } => # * * Create a new Curl::PostField for a file upload field, supplying the local filename * to read from, and optionally the remote filename (defaults to the local name). * * The block form allows a block to supply the content for this field, called * during the perform. The block should return a ruby string with the field * data. */ static VALUE ruby_curl_postfield_new_file(int argc, VALUE *argv, VALUE klass) { // TODO needs to handle content-type too ruby_curl_postfield *rbcpf = ALLOC(ruby_curl_postfield); rb_scan_args(argc, argv, "21&", &rbcpf->name, &rbcpf->local_file, &rbcpf->remote_file, &rbcpf->content_proc); // special handling if theres a block, second arg is actually remote name. if (rbcpf->content_proc != Qnil) { if (rbcpf->local_file != Qnil) { // we were given a local file if (rbcpf->remote_file == Qnil) { // we weren't given a remote, so local is actually remote // (correct block call form) rbcpf->remote_file = rbcpf->local_file; } // Shouldn't get a local file, so can ignore it. rbcpf->local_file = Qnil; } } else { if (rbcpf->remote_file == Qnil) { rbcpf->remote_file = rbcpf->local_file; } } /* assoc objects */ rbcpf->content = Qnil; rbcpf->content_type = Qnil; rbcpf->buffer_str = Qnil; return Data_Wrap_Struct(cCurlPostField, curl_postfield_mark, curl_postfield_free, rbcpf); } /* ================= ATTRIBUTES ====================*/ /* * call-seq: * field.name = "name" => "name" * * Set the POST field name for this PostField. */ static VALUE ruby_curl_postfield_name_set(VALUE self, VALUE name) { CURB_OBJECT_SETTER(ruby_curl_postfield, name); } /* * call-seq: * field.name => "name" * * Obtain the POST field name for this PostField. */ static VALUE ruby_curl_postfield_name_get(VALUE self) { CURB_OBJECT_GETTER(ruby_curl_postfield, name); } /* * call-seq: * field.content = "content" => "content" * * Set the POST field content for this PostField. Ignored when a * content_proc is supplied via either +Curl::PostField.file+ or * +set_content_proc+. */ static VALUE ruby_curl_postfield_content_set(VALUE self, VALUE content) { CURB_OBJECT_SETTER(ruby_curl_postfield, content); } /* * call-seq: * field.content => "content" * * Obtain the POST field content for this PostField. */ static VALUE ruby_curl_postfield_content_get(VALUE self) { CURB_OBJECT_GETTER(ruby_curl_postfield, content); } /* * call-seq: * field.content_type = "content_type" => "content_type" * * Set the POST field Content-type for this PostField. */ static VALUE ruby_curl_postfield_content_type_set(VALUE self, VALUE content_type) { CURB_OBJECT_SETTER(ruby_curl_postfield, content_type); } /* * call-seq: * field.content_type => "content_type" * * Get the POST field Content-type for this PostField. */ static VALUE ruby_curl_postfield_content_type_get(VALUE self) { CURB_OBJECT_GETTER(ruby_curl_postfield, content_type); } /* * call-seq: * field.local_file = "filename" => "filename" * * Set the POST field local filename for this PostField (when performing * a file upload). Ignored when a content_proc is supplied via either * +Curl::PostField.file+ or +set_content_proc+. */ static VALUE ruby_curl_postfield_local_file_set(VALUE self, VALUE local_file) { CURB_OBJECT_SETTER(ruby_curl_postfield, local_file); } /* * call-seq: * field.local_file => "filename" * * Get the POST field local filename for this PostField (when performing * a file upload). */ static VALUE ruby_curl_postfield_local_file_get(VALUE self) { CURB_OBJECT_GETTER(ruby_curl_postfield, local_file); } /* * call-seq: * field.remote_file = "filename" => "filename" * * Set the POST field remote filename for this PostField (when performing * a file upload). If no remote filename is provided, and no content_proc * is supplied, the local filename is used. If no remote filename is * specified when a content_proc is used, an exception will be raised * during the perform. */ static VALUE ruby_curl_postfield_remote_file_set(VALUE self, VALUE remote_file) { CURB_OBJECT_SETTER(ruby_curl_postfield, remote_file); } /* * call-seq: * field.local_file => "filename" * * Get the POST field remote filename for this PostField (when performing * a file upload). */ static VALUE ruby_curl_postfield_remote_file_get(VALUE self) { CURB_OBJECT_GETTER(ruby_curl_postfield, remote_file); } /* * call-seq: * field.set_content_proc { |field| ... } => * * Set a content proc for this field. This proc will be called during the * perform to supply the content for this field, overriding any setting * of +content+ or +local_file+. */ static VALUE ruby_curl_postfield_content_proc_set(int argc, VALUE *argv, VALUE self) { CURB_HANDLER_PROC_SETTER(ruby_curl_postfield, content_proc); } /* * call-seq: * field.to_str => "name=value" * field.to_s => "name=value" * * Obtain a String representation of this PostField in url-encoded * format. This is used to construct the post data for non-multipart * POSTs. * * Only content fields may be converted to strings. */ static VALUE ruby_curl_postfield_to_str(VALUE self) { // FIXME This is using the deprecated curl_escape func ruby_curl_postfield *rbcpf; VALUE result = Qnil; VALUE name = Qnil; char *tmpchrs; Data_Get_Struct(self, ruby_curl_postfield, rbcpf); if (rbcpf->name != Qnil) { name = rbcpf->name; if (rb_type(name) == T_STRING) { name = rbcpf->name; } else if (rb_respond_to(name,rb_intern("to_s"))) { name = rb_funcall(name, rb_intern("to_s"), 0); } else { name = Qnil; // we can't handle this object } } if (name == Qnil) { rb_raise(eCurlErrInvalidPostField, "Cannot convert unnamed field to string %s:%d, make sure your field name responds_to :to_s", __FILE__, __LINE__); } tmpchrs = curl_escape(StringValuePtr(name), (int)RSTRING_LEN(name)); if (!tmpchrs) { rb_raise(eCurlErrInvalidPostField, "Failed to url-encode name `%s'", tmpchrs); } else { VALUE tmpcontent = Qnil; VALUE escd_name = rb_str_new2(tmpchrs); curl_free(tmpchrs); if (rbcpf->content_proc != Qnil) { tmpcontent = rb_funcall(rbcpf->content_proc, idCall, 1, self); } else if (rbcpf->content != Qnil) { tmpcontent = rbcpf->content; } else if (rbcpf->local_file != Qnil) { tmpcontent = rbcpf->local_file; } else if (rbcpf->remote_file != Qnil) { tmpcontent = rbcpf->remote_file; } else { tmpcontent = rb_str_new2(""); } if (TYPE(tmpcontent) != T_STRING) { if (rb_respond_to(tmpcontent, rb_intern("to_s"))) { tmpcontent = rb_funcall(tmpcontent, rb_intern("to_s"), 0); } else { rb_raise(rb_eRuntimeError, "postfield(%s) is not a string and does not respond_to to_s", RSTRING_PTR(escd_name) ); } } //fprintf(stderr, "encoding content: %ld - %s\n", RSTRING_LEN(tmpcontent), RSTRING_PTR(tmpcontent) ); tmpchrs = curl_escape(RSTRING_PTR(tmpcontent), (int)RSTRING_LEN(tmpcontent)); if (!tmpchrs) { rb_raise(eCurlErrInvalidPostField, "Failed to url-encode content `%s'", tmpchrs); } else { VALUE escd_content = rb_str_new2(tmpchrs); curl_free(tmpchrs); result = escd_name; rb_str_cat(result, "=", 1); rb_str_concat(result, escd_content); } } return result; } /* =================== INIT LIB =====================*/ void init_curb_postfield() { VALUE sc; idCall = rb_intern("call"); cCurlPostField = rb_define_class_under(mCurl, "PostField", rb_cObject); rb_undef_alloc_func(cCurlPostField); /* Class methods */ rb_define_singleton_method(cCurlPostField, "content", ruby_curl_postfield_new_content, -1); rb_define_singleton_method(cCurlPostField, "file", ruby_curl_postfield_new_file, -1); sc = rb_singleton_class(cCurlPostField); rb_undef(sc, rb_intern("new")); rb_define_method(cCurlPostField, "name=", ruby_curl_postfield_name_set, 1); rb_define_method(cCurlPostField, "name", ruby_curl_postfield_name_get, 0); rb_define_method(cCurlPostField, "content=", ruby_curl_postfield_content_set, 1); rb_define_method(cCurlPostField, "content", ruby_curl_postfield_content_get, 0); rb_define_method(cCurlPostField, "content_type=", ruby_curl_postfield_content_type_set, 1); rb_define_method(cCurlPostField, "content_type", ruby_curl_postfield_content_type_get, 0); rb_define_method(cCurlPostField, "local_file=", ruby_curl_postfield_local_file_set, 1); rb_define_method(cCurlPostField, "local_file", ruby_curl_postfield_local_file_get, 0); rb_define_method(cCurlPostField, "remote_file=", ruby_curl_postfield_remote_file_set, 1); rb_define_method(cCurlPostField, "remote_file", ruby_curl_postfield_remote_file_get, 0); rb_define_method(cCurlPostField, "set_content_proc", ruby_curl_postfield_content_proc_set, -1); rb_define_method(cCurlPostField, "to_str", ruby_curl_postfield_to_str, 0); rb_define_alias(cCurlPostField, "to_s", "to_str"); } curb-1.0.5/ext/curb_macros.h0000644000004100000410000002265314362470347015772 0ustar www-datawww-data/* Curb - helper macros for ruby integration * Copyright (c)2006 Ross Bamford. * Licensed under the Ruby License. See LICENSE for details. * * $Id: curb_macros.h 13 2006-11-23 23:54:25Z roscopeco $ */ #ifndef __CURB_MACROS_H #define __CURB_MACROS_H #define rb_easy_sym(sym) ID2SYM(rb_intern(sym)) #define rb_easy_hkey(key) ID2SYM(rb_intern(key)) #define rb_easy_set(key,val) rb_hash_aset(rbce->opts, rb_easy_hkey(key) , val) #define rb_easy_get(key) rb_hash_aref(rbce->opts, rb_easy_hkey(key)) #define rb_easy_del(key) rb_hash_delete(rbce->opts, rb_easy_hkey(key)) #define rb_easy_nil(key) (rb_hash_aref(rbce->opts, rb_easy_hkey(key)) == Qnil) #define rb_easy_type_check(key,type) (rb_type(rb_hash_aref(rbce->opts, rb_easy_hkey(key))) == type) // TODO: rb_sym_to_s may not be defined? #define rb_easy_get_str(key) \ RSTRING_PTR((rb_easy_type_check(key,T_STRING) ? rb_easy_get(key) : rb_str_to_str(rb_easy_get(key)))) /* getter/setter macros for various things */ /* setter for anything that stores a ruby VALUE in the struct */ #define CURB_OBJECT_SETTER(type, attr) \ type *ptr; \ \ Data_Get_Struct(self, type, ptr); \ ptr->attr = attr; \ \ return attr; /* getter for anything that stores a ruby VALUE */ #define CURB_OBJECT_GETTER(type, attr) \ type *ptr; \ \ Data_Get_Struct(self, type, ptr); \ return ptr->attr; /* setter for anything that stores a ruby VALUE in the struct opts hash */ #define CURB_OBJECT_HSETTER(type, attr) \ type *ptr; \ \ Data_Get_Struct(self, type, ptr); \ rb_hash_aset(ptr->opts, rb_easy_hkey(#attr), attr); \ \ return attr; /* getter for anything that stores a ruby VALUE in the struct opts hash */ #define CURB_OBJECT_HGETTER(type, attr) \ type *ptr; \ \ Data_Get_Struct(self, type, ptr); \ return rb_hash_aref(ptr->opts, rb_easy_hkey(#attr)); /* setter for bool flags */ #define CURB_BOOLEAN_SETTER(type, attr) \ type *ptr; \ Data_Get_Struct(self, type, ptr); \ \ if (attr == Qnil || attr == Qfalse) { \ ptr->attr = 0; \ } else { \ ptr->attr = 1; \ } \ \ return attr; /* getter for bool flags */ #define CURB_BOOLEAN_GETTER(type, attr) \ type *ptr; \ Data_Get_Struct(self, type, ptr); \ \ return((ptr->attr) ? Qtrue : Qfalse); /* special setter for on_event handlers that take a block */ #define CURB_HANDLER_PROC_SETTER(type, handler) \ type *ptr; \ VALUE oldproc; \ \ Data_Get_Struct(self, type, ptr); \ \ oldproc = ptr->handler; \ rb_scan_args(argc, argv, "0&", &ptr->handler); \ \ return oldproc; \ /* special setter for on_event handlers that take a block, same as above but stores int he opts hash */ #define CURB_HANDLER_PROC_HSETTER(type, handler) \ type *ptr; \ VALUE oldproc, newproc; \ \ Data_Get_Struct(self, type, ptr); \ \ oldproc = rb_hash_aref(ptr->opts, rb_easy_hkey(#handler)); \ rb_scan_args(argc, argv, "0&", &newproc); \ \ rb_hash_aset(ptr->opts, rb_easy_hkey(#handler), newproc); \ \ return oldproc; /* setter for numerics that are kept in c longs */ #define CURB_IMMED_SETTER(type, attr, nilval) \ type *ptr; \ \ Data_Get_Struct(self, type, ptr); \ if (attr == Qnil) { \ ptr->attr = nilval; \ } else { \ ptr->attr = NUM2LONG(attr); \ } \ \ return attr; \ /* setter for numerics that are kept in c longs */ #define CURB_IMMED_GETTER(type, attr, nilval) \ type *ptr; \ \ Data_Get_Struct(self, type, ptr); \ if (ptr->attr == nilval) { \ return Qnil; \ } else { \ return LONG2NUM(ptr->attr); \ } /* special setter for port / port ranges */ #define CURB_IMMED_PORT_SETTER(type, attr, msg) \ type *ptr; \ \ Data_Get_Struct(self, type, ptr); \ if (attr == Qnil) { \ ptr->attr = 0; \ } else { \ int port = NUM2INT(attr); \ \ if ((port) && ((port & 0xFFFF) == port)) { \ ptr->attr = port; \ } else { \ rb_raise(rb_eArgError, "Invalid " msg " %d (expected between 1 and 65535)", port); \ } \ } \ \ return attr; \ /* special getter for port / port ranges */ #define CURB_IMMED_PORT_GETTER(type, attr) \ type *ptr; \ \ Data_Get_Struct(self, type, ptr); \ if (ptr->attr == 0) { \ return Qnil; \ } else { \ return INT2NUM(ptr->attr); \ } #define CURB_DEFINE(name) \ rb_define_const(mCurl, #name, LONG2NUM(name)) /* copy and raise exception */ #define CURB_CHECK_RB_CALLBACK_RAISE(did_raise) \ VALUE exception = rb_hash_aref(did_raise, rb_easy_hkey("error")); \ if (FIX2INT(rb_hash_size(did_raise)) > 0 && exception != Qnil) { \ rb_hash_clear(did_raise); \ VALUE message = rb_funcall(exception, rb_intern("message"), 0); \ VALUE aborted_exception = rb_exc_new_str(eCurlErrAbortedByCallback, message); \ VALUE backtrace = rb_funcall(exception, rb_intern("backtrace"), 0); \ rb_funcall(aborted_exception, rb_intern("set_backtrace"), 1, backtrace); \ rb_exc_raise(aborted_exception); \ } #endif curb-1.0.5/ext/curb_postfield.h0000644000004100000410000000200214362470347016461 0ustar www-datawww-data/* curb_postfield.h - Field class for POST method * Copyright (c)2006 Ross Bamford. * Licensed under the Ruby License. See LICENSE for details. * * $Id: curb_postfield.h 4 2006-11-17 18:35:31Z roscopeco $ */ #ifndef __CURB_POSTFIELD_H #define __CURB_POSTFIELD_H #include "curb.h" /* * postfield doesn't actually wrap a curl_httppost - instead, * it just holds together some ruby objects and has a C-side * method to add it to a given form list during the perform. */ typedef struct { /* Objects we associate */ VALUE name; VALUE content; VALUE content_type; VALUE content_proc; VALUE local_file; VALUE remote_file; /* this will sometimes hold a string, which is the result * of the content_proc invocation. We need it to hang around. */ VALUE buffer_str; } ruby_curl_postfield; extern VALUE cCurlPostField; void append_to_form(VALUE self, struct curl_httppost **first, struct curl_httppost **last); void init_curb_postfield(); #endif curb-1.0.5/ext/extconf.rb0000644000004100000410000003540714362470347015316 0ustar www-datawww-datarequire 'mkmf' dir_config('curl') if find_executable('curl-config') $CFLAGS << " #{`curl-config --cflags`.strip} -g" if ENV['STATIC_BUILD'] $LIBS << " #{`curl-config --static-libs`.strip}" else $LIBS << " #{`curl-config --libs`.strip}" end ca_bundle_path=`curl-config --ca`.strip if !ca_bundle_path.nil? and ca_bundle_path != '' $defs.push( %{-D HAVE_CURL_CONFIG_CA} ) $defs.push( %{-D CURL_CONFIG_CA='#{ca_bundle_path.inspect}'} ) end elsif !have_library('curl') or !have_header('curl/curl.h') fail <<-EOM Can't find libcurl or curl/curl.h Make sure development libs (ie libcurl4-openssl-dev) are installed on the system. Try passing --with-curl-dir or --with-curl-lib and --with-curl-include options to extconf. EOM end # Check arch flags # TODO: detect mismatched arch types when libcurl mac ports is mixed with native mac ruby or vice versa #archs = $CFLAGS.scan(/-arch\s(.*?)\s/).first # get the first arch flag #if archs and archs.size >= 1 # # need to reduce the number of archs... # # guess the first one is correct... at least the first one is probably the ruby installed arch... # # this could lead to compiled binaries that crash at runtime... # $CFLAGS.gsub!(/-arch\s(.*?)\s/,' ') # $CFLAGS << " -arch #{archs.first}" # puts "Selected arch: #{archs.first}" #end def define(s, v=1) $defs.push( format("-D HAVE_%s=%d", s.to_s.upcase, v) ) end def have_constant(name) sname = name.is_a?(Symbol) ? name.to_s : name.upcase checking_for name do src = %{ #include int main() { int test = (int)#{sname}; return 0; } } if try_compile(src,"#{$CFLAGS} #{$LIBS}") define name true else #define name, 0 false end end end have_constant "curlopt_tcp_keepalive" have_constant "curlopt_tcp_keepidle" have_constant "curlopt_tcp_keepintvl" have_constant "curlinfo_appconnect_time" have_constant "curlinfo_redirect_time" have_constant "curlinfo_response_code" have_constant "curlinfo_filetime" have_constant "curlinfo_redirect_count" have_constant "curlinfo_os_errno" have_constant "curlinfo_num_connects" have_constant "curlinfo_cookielist" have_constant "curlinfo_ftp_entry_path" have_constant "curl_version_ssl" have_constant "curl_version_libz" have_constant "curl_version_ntlm" have_constant "curl_version_gssnegotiate" have_constant "curl_version_debug" have_constant "curl_version_asynchdns" have_constant "curl_version_spnego" have_constant "curl_version_largefile" have_constant "curl_version_idn" have_constant "curl_version_sspi" have_constant "curl_version_conv" have_constant "curl_version_http2" have_constant "curlproxy_http" have_constant "curlproxy_socks4" have_constant "curlproxy_socks4a" have_constant "curlproxy_socks5" have_constant "curlproxy_socks5_hostname" have_constant "curlauth_basic" have_constant "curlauth_digest" have_constant "curlauth_gssnegotiate" have_constant "curlauth_ntlm" have_constant "curlauth_anysafe" have_constant "curlauth_any" have_constant "curle_tftp_notfound" have_constant "curle_tftp_perm" have_constant "curle_tftp_diskfull" have_constant "curle_tftp_illegal" have_constant "curle_tftp_unknownid" have_constant "curle_tftp_exists" have_constant "curle_tftp_nosuchuser" # older versions of libcurl 7.12 have_constant "curle_send_fail_rewind" have_constant "curle_ssl_engine_initfailed" have_constant "curle_login_denied" # older than 7.10.0 have_constant "curlopt_nosignal" # older than 7.16.0 have_constant "curlmopt_pipelining" # older than 7.16.3 have_constant "curlmopt_maxconnects" have_constant "curlopt_seekfunction" have_constant "curlopt_seekdata" have_constant "curlopt_sockoptfunction" have_constant "curlopt_sockoptdata" have_constant "curlopt_opensocketfunction" have_constant "curlopt_opensocketdata" # additional consts have_constant "curle_conv_failed" have_constant "curle_conv_reqd" have_constant "curle_ssl_cacert_badfile" have_constant "curle_remote_file_not_found" have_constant "curle_ssh" have_constant "curle_ssl_shutdown_failed" have_constant "curle_again" have_constant "curle_ssl_crl_badfile" have_constant "curle_ssl_issuer_error" # added in 7.18.2 have_constant "curlinfo_redirect_url" # username/password added in 7.19.1 have_constant "curlopt_username" have_constant "curlopt_password" have_constant "curlinfo_primary_ip" # ie quirk added in 7.19.3 have_constant "curlauth_digest_ie" # added in 7.15.1 have_constant "curlftpmethod_multicwd" have_constant "curlftpmethod_nocwd" have_constant "curlftpmethod_singlecwd" # centos 4.5 build of libcurl have_constant "curlm_bad_socket" have_constant "curlm_unknown_option" have_func("curl_multi_timeout") have_func("curl_multi_fdset") have_func("curl_multi_perform") have_constant "curlopt_haproxyprotocol" # constants have_constant "curlopt_interleavefunction" have_constant "curlopt_interleavedata" have_constant "curlopt_chunk_bgn_function" have_constant "curlopt_chunk_end_function" have_constant "curlopt_chunk_data" have_constant "curlopt_fnmatch_function" have_constant "curlopt_fnmatch_data" have_constant "curlopt_errorbuffer" have_constant "curlopt_stderr" have_constant "curlopt_failonerror" have_constant "curlopt_url" have_constant "curlopt_protocols" have_constant "curlopt_redir_protocols" have_constant "curlopt_proxy" have_constant "curlopt_proxyport" have_constant "curlopt_proxytype" have_constant "curlopt_noproxy" have_constant "curlopt_httpproxytunnel" have_constant "curlopt_socks5_gssapi_service" have_constant "curlopt_socks5_gssapi_nec" have_constant "curlopt_interface" have_constant "curlopt_localport" have_constant "curlopt_dns_cache_timeout" have_constant "curlopt_dns_use_global_cache" have_constant "curlopt_buffersize" have_constant "curlopt_port" have_constant "curlopt_tcp_nodelay" have_constant "curlopt_address_scope" have_constant "curlopt_netrc" have_constant "curl_netrc_optional" have_constant "curl_netrc_ignored" have_constant "curl_netrc_required" have_constant "curlopt_netrc_file" have_constant "curlopt_userpwd" have_constant "curlopt_proxyuserpwd" have_constant "curlopt_username" have_constant "curlopt_password" have_constant "curlopt_password" have_constant "curlopt_password" have_constant "curlopt_httpauth" have_constant "curlauth_digest_ie" have_constant "curlauth_only" have_constant "curlopt_tlsauth_type" have_constant "curlopt_tlsauth_srp" have_constant "curlopt_tlsauth_username" have_constant "curlopt_tlsauth_password" have_constant "curlopt_proxyauth" have_constant "curlopt_autoreferer" have_constant "curlopt_encoding" have_constant "curlopt_followlocation" have_constant "curlopt_unrestricted_auth" have_constant "curlopt_maxredirs" have_constant "curlopt_postredir" have_constant "curlopt_put" have_constant "curlopt_post" have_constant "curlopt_postfields" have_constant "curlopt_postfieldsize" have_constant "curlopt_postfieldsize_large" have_constant "curlopt_copypostfields" have_constant "curlopt_httppost" have_constant "curlopt_referer" have_constant "curlopt_useragent" have_constant "curlopt_httpheader" have_constant "curlopt_proxyheader" have_constant "curlopt_http200aliases" have_constant "curlopt_cookie" have_constant "curlopt_cookiefile" have_constant "curlopt_cookiejar" have_constant "curlopt_cookiesession" have_constant "curlopt_cookielist" have_constant "curlopt_httpget" have_constant "curlopt_http_version" have_constant "curl_http_version_none" have_constant "curl_http_version_1_0" have_constant "curl_http_version_1_1" have_constant "curlopt_ignore_content_length" have_constant "curlopt_http_content_decoding" have_constant "curlopt_http_transfer_decoding" have_constant "curlopt_mail_from" have_constant "curlopt_mail_rcpt" have_constant "curlopt_tftp_blksize" have_constant "curlopt_ftpport" have_constant "curlopt_quote" have_constant "curlopt_postquote" have_constant "curlopt_prequote" have_constant "curlopt_dirlistonly" have_constant "curlopt_append" have_constant "curlopt_ftp_use_eprt" have_constant "curlopt_ftp_use_epsv" have_constant "curlopt_ftp_use_pret" have_constant "curlopt_ftp_create_missing_dirs" have_constant "curlopt_ftp_response_timeout" have_constant "curlopt_ftp_alternative_to_user" have_constant "curlopt_ftp_skip_pasv_ip" have_constant "curlopt_ftpsslauth" have_constant "curlftpauth_default" have_constant "curlftpauth_ssl" have_constant "curlftpauth_tls" have_constant "curlopt_ftp_ssl_ccc" have_constant "curlftpssl_ccc_none" have_constant "curlftpssl_ccc_passive" have_constant "curlftpssl_ccc_active" have_constant "curlopt_ftp_account" have_constant "curlopt_ftp_filemethod" have_constant "curlftpmethod_multicwd" have_constant "curlftpmethod_nocwd" have_constant "curlftpmethod_singlecwd" have_constant "curlopt_rtsp_request" have_constant "curl_rtspreq_options" have_constant "curl_rtspreq_describe" have_constant "curl_rtspreq_announce" have_constant "curl_rtspreq_setup" have_constant "curl_rtspreq_play" have_constant "curl_rtspreq_pause" have_constant "curl_rtspreq_teardown" have_constant "curl_rtspreq_get_parameter" have_constant "curl_rtspreq_set_parameter" have_constant "curl_rtspreq_record" have_constant "curl_rtspreq_receive" have_constant "curlopt_rtsp_session_id" have_constant "curlopt_rtsp_stream_uri" have_constant "curlopt_rtsp_transport" have_constant "curlopt_rtsp_header" have_constant "curlopt_rtsp_client_cseq" have_constant "curlopt_rtsp_server_cseq" have_constant "curlopt_transfertext" have_constant "curlopt_proxy_transfer_mode" have_constant "curlopt_crlf" have_constant "curlopt_range" have_constant "curlopt_resume_from" have_constant "curlopt_resume_from_large" have_constant "curlopt_customrequest" have_constant "curlopt_filetime" have_constant "curlopt_nobody" have_constant "curlopt_infilesize" have_constant "curlopt_infilesize_large" have_constant "curlopt_upload" have_constant "curlopt_maxfilesize" have_constant "curlopt_maxfilesize_large" have_constant "curlopt_timecondition" have_constant "curlopt_timevalue" have_constant "curlopt_timeout" have_constant "curlopt_timeout_ms" have_constant "curlopt_low_speed_limit" have_constant "curlopt_low_speed_time" have_constant "curlopt_max_send_speed_large" have_constant "curlopt_max_recv_speed_large" have_constant "curlopt_maxconnects" have_constant "curlopt_closepolicy" have_constant "curlopt_fresh_connect" have_constant "curlopt_forbid_reuse" have_constant "curlopt_connecttimeout" have_constant "curlopt_connecttimeout_ms" have_constant "curlopt_ipresolve" have_constant "curl_ipresolve_whatever" have_constant "curl_ipresolve_v4" have_constant "curl_ipresolve_v6" have_constant "curlopt_connect_only" have_constant "curlopt_use_ssl" have_constant "curlusessl_none" have_constant "curlusessl_try" have_constant "curlusessl_control" have_constant "curlusessl_all" have_constant "curlopt_resolve" have_constant "curlopt_sslcert" have_constant "curlopt_sslcerttype" have_constant "curlopt_sslkey" have_constant "curlopt_sslkeytype" have_constant "curlopt_keypasswd" have_constant "curlopt_sslengine" have_constant "curlopt_sslengine_default" have_constant "curlopt_sslversion" have_constant "curl_sslversion_default" have_constant :CURL_SSLVERSION_TLSv1 have_constant :CURL_SSLVERSION_SSLv2 have_constant :CURL_SSLVERSION_SSLv3 # Added in 7.34.0 have_constant :CURL_SSLVERSION_TLSv1_0 have_constant :CURL_SSLVERSION_TLSv1_1 have_constant :CURL_SSLVERSION_TLSv1_2 # Added in 7.52.0 have_constant :CURL_SSLVERSION_TLSv1_3 have_constant "curlopt_ssl_verifypeer" have_constant "curlopt_cainfo" have_constant "curlopt_issuercert" have_constant "curlopt_capath" have_constant "curlopt_crlfile" have_constant "curlopt_ssl_verifyhost" have_constant "curlopt_certinfo" have_constant "curlopt_random_file" have_constant "curlopt_egdsocket" have_constant "curlopt_ssl_cipher_list" have_constant "curlopt_ssl_sessionid_cache" have_constant "curlopt_krblevel" have_constant "curlopt_ssh_auth_types" have_constant "curlopt_ssh_host_public_key_md5" have_constant "curlopt_ssh_public_keyfile" have_constant "curlopt_ssh_private_keyfile" have_constant "curlopt_ssh_knownhosts" have_constant "curlopt_ssh_keyfunction" have_constant "curlkhstat_fine_add_to_file" have_constant "curlkhstat_fine" have_constant "curlkhstat_reject" have_constant "curlkhstat_defer" have_constant "curlopt_ssh_keydata" have_constant "curlopt_private" have_constant "curlopt_share" have_constant "curlopt_new_file_perms" have_constant "curlopt_new_directory_perms" have_constant "curlopt_telnetoptions" # was obsoleted in August 2007 for 7.17.0, reused in April 2011 for 7.21.5 have_constant "curle_not_built_in" have_constant "curle_obsolete" # removed in 7.24 ? have_constant "curle_ftp_pret_failed" have_constant "curle_rtsp_cseq_error" have_constant "curle_rtsp_session_error" have_constant "curle_ftp_bad_file_list" have_constant "curle_chunk_failed" have_constant "curle_no_connection_available" have_constant "curle_ssl_pinnedpubkeynotmatch" have_constant "curle_ssl_invalidcertstatus" have_constant "curle_http2_stream" # gssapi/spnego delegation related constants have_constant "curlopt_gssapi_delegation" have_constant "curlgssapi_delegation_policy_flag" have_constant "curlgssapi_delegation_flag" have_constant "CURLM_ADDED_ALREADY" # added in 7.40.0 have_constant "curlopt_unix_socket_path" # added in 7.42.0 have_constant "curlopt_path_as_is" # added in 7.43.0 have_constant "curlopt_pipewait" have_constant "curlopt_proxy_ssl_verifyhost" # protocol constants have_constant "curlproto_all" have_constant "curlproto_dict" have_constant "curlproto_file" have_constant "curlproto_ftp" have_constant "curlproto_ftps" have_constant "curlproto_gopher" have_constant "curlproto_http" have_constant "curlproto_https" have_constant "curlproto_imap" have_constant "curlproto_imaps" have_constant "curlproto_ldap" have_constant "curlproto_ldaps" have_constant "curlproto_pop3" have_constant "curlproto_pop3s" have_constant "curlproto_rtmp" have_constant "curlproto_rtmpe" have_constant "curlproto_rtmps" have_constant "curlproto_rtmpt" have_constant "curlproto_rtmpte" have_constant "curlproto_rtmpts" have_constant "curlproto_rtsp" have_constant "curlproto_scp" have_constant "curlproto_sftp" have_constant "curlproto_smb" have_constant "curlproto_smbs" have_constant "curlproto_smtp" have_constant "curlproto_smtps" have_constant "curlproto_telnet" have_constant "curlproto_tftp" if try_compile('int main() { return 0; }','-Wall') $CFLAGS << ' -Wall' end # do some checking to detect ruby 1.8 hash.c vs ruby 1.9 hash.c def test_for(name, const, src) checking_for name do if try_compile(src,"#{$CFLAGS} #{$LIBS}") define const true else false end end end test_for("curl_easy_escape", "CURL_EASY_ESCAPE", %{ #include int main() { CURL *easy = curl_easy_init(); curl_easy_escape(easy,"hello",5); return 0; } }) have_func('rb_thread_blocking_region') have_header('ruby/thread.h') && have_func('rb_thread_call_without_gvl', 'ruby/thread.h') have_header('ruby/io.h') have_func('rb_io_stdio_file') create_header('curb_config.h') create_makefile('curb_core') curb-1.0.5/ext/curb_multi.c0000644000004100000410000005121314362470347015625 0ustar www-datawww-data/* curb_multi.c - Curl multi mode * Copyright (c)2008 Todd A. Fisher. * Licensed under the Ruby License. See LICENSE for details. * */ #include "curb_config.h" #include #ifdef HAVE_RUBY_ST_H #include #else #include #endif #ifdef HAVE_RB_THREAD_CALL_WITHOUT_GVL #include #endif #include "curb_easy.h" #include "curb_errors.h" #include "curb_postfield.h" #include "curb_multi.h" #include #ifdef _WIN32 // for O_RDWR and O_BINARY #include #endif extern VALUE mCurl; static VALUE idCall; #ifdef RDOC_NEVER_DEFINED mCurl = rb_define_module("Curl"); #endif VALUE cCurlMulti; static long cCurlMutiDefaulttimeout = 100; /* milliseconds */ static char cCurlMutiAutoClose = 0; static void rb_curl_multi_remove(ruby_curl_multi *rbcm, VALUE easy); static void rb_curl_multi_read_info(VALUE self, CURLM *mptr); static void rb_curl_multi_run(VALUE self, CURLM *multi_handle, int *still_running); static VALUE callback_exception(VALUE did_raise, VALUE exception) { // TODO: we could have an option to enable exception reporting /* VALUE ret = rb_funcall(exception, rb_intern("message"), 0); VALUE trace = rb_funcall(exception, rb_intern("backtrace"), 0); if (RB_TYPE_P(trace, T_ARRAY) && RARRAY_LEN(trace) > 0) { printf("we got an exception: %s:%d\n", StringValueCStr(ret), RARRAY_LEN(trace)); VALUE sep = rb_str_new_cstr("\n"); VALUE trace_lines = rb_ary_join(trace, sep); if (RB_TYPE_P(trace_lines, T_STRING)) { printf("%s\n", StringValueCStr(trace_lines)); } else { printf("trace is not a string??\n"); } } else { printf("we got an exception: %s\nno stack available\n", StringValueCStr(ret)); } */ rb_hash_aset(did_raise, rb_easy_hkey("error"), exception); return exception; } void curl_multi_free(ruby_curl_multi *rbcm) { curl_multi_cleanup(rbcm->handle); free(rbcm); } static void ruby_curl_multi_init(ruby_curl_multi *rbcm) { rbcm->handle = curl_multi_init(); if (!rbcm->handle) { rb_raise(mCurlErrFailedInit, "Failed to initialize multi handle"); } rbcm->active = 0; rbcm->running = 0; } /* * call-seq: * Curl::Multi.new => # * * Create a new Curl::Multi instance */ VALUE ruby_curl_multi_new(VALUE klass) { ruby_curl_multi *rbcm = ALLOC(ruby_curl_multi); ruby_curl_multi_init(rbcm); /* * The mark routine will be called by the garbage collector during its ``mark'' phase. * If your structure references other Ruby objects, then your mark function needs to * identify these objects using rb_gc_mark(value). If the structure doesn't reference * other Ruby objects, you can simply pass 0 as a function pointer. */ return Data_Wrap_Struct(klass, 0, curl_multi_free, rbcm); } /* * call-seq: * Curl::Multi.default_timeout = 4 => 4 * * Set the global default time out for all Curl::Multi Handles. This value is used * when libcurl cannot determine a timeout value when calling curl_multi_timeout. * */ VALUE ruby_curl_multi_set_default_timeout(VALUE klass, VALUE timeout) { cCurlMutiDefaulttimeout = NUM2LONG(timeout); return timeout; } /* * call-seq: * Curl::Multi.default_timeout = 4 => 4 * * Get the global default time out for all Curl::Multi Handles. * */ VALUE ruby_curl_multi_get_default_timeout(VALUE klass) { return LONG2NUM(cCurlMutiDefaulttimeout); } /* * call-seq: * Curl::Multi.autoclose = true => true * * Automatically close open connections after each request. Otherwise, the connection will remain open * for reuse until the next GC * */ VALUE ruby_curl_multi_set_autoclose(VALUE klass, VALUE onoff) { cCurlMutiAutoClose = ((onoff == Qtrue) ? 1 : 0); return onoff; } /* * call-seq: * Curl::Multi.autoclose => true|false * * Get the global default autoclose setting for all Curl::Multi Handles. * */ VALUE ruby_curl_multi_get_autoclose(VALUE klass) { return cCurlMutiAutoClose == 1 ? Qtrue : Qfalse; } /* * call-seq: * multi.requests => [#, ...] * * Returns an array containing all the active requests on this Curl::Multi object. */ /* * call-seq: * multi = Curl::Multi.new * multi.max_connects = 800 * * Set the max connections in the cache for a multi handle */ static VALUE ruby_curl_multi_max_connects(VALUE self, VALUE count) { #ifdef HAVE_CURLMOPT_MAXCONNECTS ruby_curl_multi *rbcm; Data_Get_Struct(self, ruby_curl_multi, rbcm); curl_multi_setopt(rbcm->handle, CURLMOPT_MAXCONNECTS, NUM2LONG(count)); #endif return count; } /* * call-seq: * multi = Curl::Multi.new * multi.pipeline = true * * Pass a long set to 1 for HTTP/1.1 pipelining, 2 for HTTP/2 multiplexing, or 0 to disable. * Enabling pipelining on a multi handle will make it attempt to perform HTTP Pipelining as * far as possible for transfers using this handle. This means that if you add a second request * that can use an already existing connection, the second request will be "piped" on the same * connection rather than being executed in parallel. (Added in 7.16.0, multiplex added in 7.43.0) * */ static VALUE ruby_curl_multi_pipeline(VALUE self, VALUE method) { #ifdef HAVE_CURLMOPT_PIPELINING ruby_curl_multi *rbcm; long value; if (method == Qtrue) { value = 1; } else if (method == Qfalse) { value = 0; } else { value = NUM2LONG(method); } Data_Get_Struct(self, ruby_curl_multi, rbcm); curl_multi_setopt(rbcm->handle, CURLMOPT_PIPELINING, value); #endif return method == Qtrue ? 1 : 0; } /* * call-seq: * multi = Curl::Multi.new * easy = Curl::Easy.new('url') * * multi.add(easy) * * Add an easy handle to the multi stack */ VALUE ruby_curl_multi_add(VALUE self, VALUE easy) { CURLMcode mcode; ruby_curl_easy *rbce; ruby_curl_multi *rbcm; Data_Get_Struct(self, ruby_curl_multi, rbcm); Data_Get_Struct(easy, ruby_curl_easy, rbce); /* setup the easy handle */ ruby_curl_easy_setup( rbce ); mcode = curl_multi_add_handle(rbcm->handle, rbce->curl); if (mcode != CURLM_CALL_MULTI_PERFORM && mcode != CURLM_OK) { raise_curl_multi_error_exception(mcode); } rbcm->active++; /* Increase the running count, so that the perform loop keeps running. * If this number is not correct, the next call to curl_multi_perform will correct it. */ rbcm->running++; /* track a reference to associated multi handle */ rbce->multi = self; return self; } /* * call-seq: * multi = Curl::Multi.new * easy = Curl::Easy.new('url') * * multi.add(easy) * * # sometime later * multi.remove(easy) * * Remove an easy handle from a multi stack. * * Will raise an exception if the easy handle is not found */ VALUE ruby_curl_multi_remove(VALUE self, VALUE rb_easy_handle) { ruby_curl_multi *rbcm; Data_Get_Struct(self, ruby_curl_multi, rbcm); rb_curl_multi_remove(rbcm, rb_easy_handle); return self; } static void rb_curl_multi_remove(ruby_curl_multi *rbcm, VALUE easy) { CURLMcode result; ruby_curl_easy *rbce; Data_Get_Struct(easy, ruby_curl_easy, rbce); result = curl_multi_remove_handle(rbcm->handle, rbce->curl); if (result != 0) { raise_curl_multi_error_exception(result); } rbcm->active--; ruby_curl_easy_cleanup( easy, rbce ); } // on_success, on_failure, on_complete static VALUE call_status_handler1(VALUE ary) { return rb_funcall(rb_ary_entry(ary, 0), idCall, 1, rb_ary_entry(ary, 1)); } static VALUE call_status_handler2(VALUE ary) { return rb_funcall(rb_ary_entry(ary, 0), idCall, 2, rb_ary_entry(ary, 1), rb_ary_entry(ary, 2)); } static void rb_curl_mutli_handle_complete(VALUE self, CURL *easy_handle, int result) { long response_code = -1; VALUE easy; ruby_curl_easy *rbce = NULL; VALUE callargs; CURLcode ecode = curl_easy_getinfo(easy_handle, CURLINFO_PRIVATE, (char**)&easy); Data_Get_Struct(easy, ruby_curl_easy, rbce); rbce->last_result = result; /* save the last easy result code */ // remove the easy handle from multi on completion so it can be reused again rb_funcall(self, rb_intern("remove"), 1, easy); /* after running a request cleanup the headers, these are set before each request */ if (rbce->curl_headers) { curl_slist_free_all(rbce->curl_headers); rbce->curl_headers = NULL; } if (ecode != 0) { raise_curl_easy_error_exception(ecode); } VALUE did_raise = rb_hash_new(); if (!rb_easy_nil("complete_proc")) { callargs = rb_ary_new3(2, rb_easy_get("complete_proc"), easy); rbce->callback_active = 1; rb_rescue(call_status_handler1, callargs, callback_exception, did_raise); rbce->callback_active = 0; CURB_CHECK_RB_CALLBACK_RAISE(did_raise); } #ifdef HAVE_CURLINFO_RESPONSE_CODE curl_easy_getinfo(rbce->curl, CURLINFO_RESPONSE_CODE, &response_code); #else // old libcurl curl_easy_getinfo(rbce->curl, CURLINFO_HTTP_CODE, &response_code); #endif long redirect_count; curl_easy_getinfo(rbce->curl, CURLINFO_REDIRECT_COUNT, &redirect_count); if (result != 0) { if (!rb_easy_nil("failure_proc")) { callargs = rb_ary_new3(3, rb_easy_get("failure_proc"), easy, rb_curl_easy_error(result)); rbce->callback_active = 1; rb_rescue(call_status_handler2, callargs, callback_exception, did_raise); rbce->callback_active = 0; CURB_CHECK_RB_CALLBACK_RAISE(did_raise); } } else if (!rb_easy_nil("success_proc") && ((response_code >= 200 && response_code < 300) || response_code == 0)) { /* NOTE: we allow response_code == 0, in the case of non http requests e.g. reading from disk */ callargs = rb_ary_new3(2, rb_easy_get("success_proc"), easy); rbce->callback_active = 1; rb_rescue(call_status_handler1, callargs, callback_exception, did_raise); rbce->callback_active = 0; CURB_CHECK_RB_CALLBACK_RAISE(did_raise); } else if (!rb_easy_nil("redirect_proc") && ((response_code >= 300 && response_code < 400) || redirect_count > 0) ) { rbce->callback_active = 1; callargs = rb_ary_new3(3, rb_easy_get("redirect_proc"), easy, rb_curl_easy_error(result)); rbce->callback_active = 0; rb_rescue(call_status_handler2, callargs, callback_exception, did_raise); CURB_CHECK_RB_CALLBACK_RAISE(did_raise); } else if (!rb_easy_nil("missing_proc") && (response_code >= 400 && response_code < 500)) { rbce->callback_active = 1; callargs = rb_ary_new3(3, rb_easy_get("missing_proc"), easy, rb_curl_easy_error(result)); rbce->callback_active = 0; rb_rescue(call_status_handler2, callargs, callback_exception, did_raise); CURB_CHECK_RB_CALLBACK_RAISE(did_raise); } else if (!rb_easy_nil("failure_proc") && (response_code >= 500 && response_code <= 999)) { callargs = rb_ary_new3(3, rb_easy_get("failure_proc"), easy, rb_curl_easy_error(result)); rbce->callback_active = 1; rb_rescue(call_status_handler2, callargs, callback_exception, did_raise); rbce->callback_active = 0; CURB_CHECK_RB_CALLBACK_RAISE(did_raise); } } static void rb_curl_multi_read_info(VALUE self, CURLM *multi_handle) { int msgs_left; CURLcode c_easy_result; CURLMsg *c_multi_result; // for picking up messages with the transfer status CURL *c_easy_handle; /* Check for finished easy handles and remove from the multi handle. * curl_multi_info_read will query for messages from individual handles. * * The messages fetched with this function are removed from the curl internal * queue and when there are no messages left it will return NULL (and break * the loop effectively). */ while ((c_multi_result = curl_multi_info_read(multi_handle, &msgs_left))) { // A message is there, but we really care only about transfer completetion. if (c_multi_result->msg != CURLMSG_DONE) continue; c_easy_handle = c_multi_result->easy_handle; c_easy_result = c_multi_result->data.result; /* return code for transfer */ rb_curl_mutli_handle_complete(self, c_easy_handle, c_easy_result); } } /* called within ruby_curl_multi_perform */ static void rb_curl_multi_run(VALUE self, CURLM *multi_handle, int *still_running) { CURLMcode mcode; /* * curl_multi_perform will return CURLM_CALL_MULTI_PERFORM only when it wants to be called again immediately. * When things are fine and there is nothing immediate it wants done, it'll return CURLM_OK. * * It will perform all pending actions on all added easy handles attached to this multi handle. We will loop * here as long as mcode is CURLM_CALL_MULTIPERFORM. */ do { mcode = curl_multi_perform(multi_handle, still_running); } while (mcode == CURLM_CALL_MULTI_PERFORM); /* * Nothing more to do, check if an error occured in the loop above and raise an exception if necessary. */ if (mcode != CURLM_OK) { raise_curl_multi_error_exception(mcode); } /* * Everything is ok, but this does not mean all the transfers are completed. * There is no data to read or write available for Curl at the moment. * * At this point we can return control to the caller to do something else while * curl is waiting for more actions to queue. */ } #ifdef _WIN32 void create_crt_fd(fd_set *os_set, fd_set *crt_set) { int i; crt_set->fd_count = os_set->fd_count; for (i = 0; i < os_set->fd_count; i++) { WSAPROTOCOL_INFO wsa_pi; // dupicate the SOCKET int r = WSADuplicateSocket(os_set->fd_array[i], GetCurrentProcessId(), &wsa_pi); SOCKET s = WSASocket(wsa_pi.iAddressFamily, wsa_pi.iSocketType, wsa_pi.iProtocol, &wsa_pi, 0, 0); // create the CRT fd so ruby can get back to the SOCKET int fd = _open_osfhandle(s, O_RDWR|O_BINARY); os_set->fd_array[i] = s; crt_set->fd_array[i] = fd; } } void cleanup_crt_fd(fd_set *os_set, fd_set *crt_set) { int i; for (i = 0; i < os_set->fd_count; i++) { // cleanup the CRT fd _close(crt_set->fd_array[i]); // cleanup the duplicated SOCKET closesocket(os_set->fd_array[i]); } } #endif #if defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL) struct _select_set { int maxfd; fd_set *fdread, *fdwrite, *fdexcep; struct timeval *tv; }; static VALUE curb_select(void *args) { struct _select_set* set = args; int rc = select(set->maxfd, set->fdread, set->fdwrite, set->fdexcep, set->tv); return INT2FIX(rc); } #endif /* * call-seq: * multi = Curl::Multi.new * easy1 = Curl::Easy.new('url') * easy2 = Curl::Easy.new('url') * * multi.add(easy1) * multi.add(easy2) * * multi.perform do * # while idle other code my execute here * end * * Run multi handles, looping selecting when data can be transfered */ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) { CURLMcode mcode; ruby_curl_multi *rbcm; int maxfd, rc = -1; fd_set fdread, fdwrite, fdexcep; #ifdef _WIN32 fd_set crt_fdread, crt_fdwrite, crt_fdexcep; #endif long timeout_milliseconds; struct timeval tv = {0, 0}; struct timeval tv_100ms = {0, 100000}; VALUE block = Qnil; #if defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL) struct _select_set fdset_args; #endif rb_scan_args(argc, argv, "0&", &block); Data_Get_Struct(self, ruby_curl_multi, rbcm); timeout_milliseconds = cCurlMutiDefaulttimeout; // Run curl_multi_perform for the first time to get the ball rolling rb_curl_multi_run( self, rbcm->handle, &(rbcm->running) ); // Check the easy handles for new messages one more time before yielding // control to passed ruby block. // // This call will block until all queued messages are processed and if any // handle completed the transfer we will run the on_complete callback here too. rb_curl_multi_read_info( self, rbcm->handle ); // There are no more messages to handle by curl and we can run the ruby block // passed to perform method. // When the block completes curl will resume. if (block != Qnil) { rb_funcall(block, rb_intern("call"), 1, self); } do { while (rbcm->running) { #ifdef HAVE_CURL_MULTI_TIMEOUT /* get the curl suggested time out */ mcode = curl_multi_timeout(rbcm->handle, &timeout_milliseconds); if (mcode != CURLM_OK) { raise_curl_multi_error_exception(mcode); } #else /* libcurl doesn't have a timeout method defined, initialize to -1 we'll pick up the default later */ timeout_milliseconds = -1; #endif if (timeout_milliseconds == 0) { /* no delay */ rb_curl_multi_run( self, rbcm->handle, &(rbcm->running) ); rb_curl_multi_read_info( self, rbcm->handle ); if (block != Qnil) { rb_funcall(block, rb_intern("call"), 1, self); } continue; } if (timeout_milliseconds < 0 || timeout_milliseconds > cCurlMutiDefaulttimeout) { timeout_milliseconds = cCurlMutiDefaulttimeout; /* libcurl doesn't know how long to wait, use a default timeout */ /* or buggy versions libcurl sometimes reports huge timeouts... let's cap it */ } tv.tv_sec = 0; /* never wait longer than 1 second */ tv.tv_usec = (int)(timeout_milliseconds * 1000); /* XXX: int is the right type for OSX, what about linux? */ FD_ZERO(&fdread); FD_ZERO(&fdwrite); FD_ZERO(&fdexcep); /* load the fd sets from the multi handle */ mcode = curl_multi_fdset(rbcm->handle, &fdread, &fdwrite, &fdexcep, &maxfd); if (mcode != CURLM_OK) { raise_curl_multi_error_exception(mcode); } if (maxfd == -1) { /* libcurl recommends sleeping for 100ms */ rb_thread_wait_for(tv_100ms); rb_curl_multi_run( self, rbcm->handle, &(rbcm->running) ); rb_curl_multi_read_info( self, rbcm->handle ); if (block != Qnil) { rb_funcall(block, rb_intern("call"), 1, self); } continue; } #ifdef _WIN32 create_crt_fd(&fdread, &crt_fdread); create_crt_fd(&fdwrite, &crt_fdwrite); create_crt_fd(&fdexcep, &crt_fdexcep); #endif #if (defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL)) fdset_args.maxfd = maxfd+1; fdset_args.fdread = &fdread; fdset_args.fdwrite = &fdwrite; fdset_args.fdexcep = &fdexcep; fdset_args.tv = &tv; #endif #ifdef HAVE_RB_THREAD_CALL_WITHOUT_GVL rc = (int)(VALUE) rb_thread_call_without_gvl((void *(*)(void *))curb_select, &fdset_args, RUBY_UBF_IO, 0); #elif HAVE_RB_THREAD_BLOCKING_REGION rc = rb_thread_blocking_region(curb_select, &fdset_args, RUBY_UBF_IO, 0); #elif HAVE_RB_THREAD_FD_SELECT rc = rb_thread_fd_select(maxfd+1, &fdread, &fdwrite, &fdexcep, &tv); #else rc = rb_thread_select(maxfd+1, &fdread, &fdwrite, &fdexcep, &tv); #endif #ifdef _WIN32 cleanup_crt_fd(&fdread, &crt_fdread); cleanup_crt_fd(&fdwrite, &crt_fdwrite); cleanup_crt_fd(&fdexcep, &crt_fdexcep); #endif switch(rc) { case -1: if(errno != EINTR) { rb_raise(rb_eRuntimeError, "select(): %s", strerror(errno)); break; } case 0: /* timeout */ default: /* action */ rb_curl_multi_run( self, rbcm->handle, &(rbcm->running) ); rb_curl_multi_read_info( self, rbcm->handle ); if (block != Qnil) { rb_funcall(block, rb_intern("call"), 1, self); } break; } } } while( rbcm->running ); rb_curl_multi_read_info( self, rbcm->handle ); if (block != Qnil) { rb_funcall(block, rb_intern("call"), 1, self); } if (cCurlMutiAutoClose == 1) { rb_funcall(self, rb_intern("close"), 0); } return Qtrue; } /* * call-seq: * * multi.close * after closing the multi handle all connections will be closed and the handle will no longer be usable * */ VALUE ruby_curl_multi_close(VALUE self) { ruby_curl_multi *rbcm; Data_Get_Struct(self, ruby_curl_multi, rbcm); curl_multi_cleanup(rbcm->handle); ruby_curl_multi_init(rbcm); return self; } /* =================== INIT LIB =====================*/ void init_curb_multi() { idCall = rb_intern("call"); cCurlMulti = rb_define_class_under(mCurl, "Multi", rb_cObject); rb_undef_alloc_func(cCurlMulti); /* Class methods */ rb_define_singleton_method(cCurlMulti, "new", ruby_curl_multi_new, 0); rb_define_singleton_method(cCurlMulti, "default_timeout=", ruby_curl_multi_set_default_timeout, 1); rb_define_singleton_method(cCurlMulti, "default_timeout", ruby_curl_multi_get_default_timeout, 0); rb_define_singleton_method(cCurlMulti, "autoclose=", ruby_curl_multi_set_autoclose, 1); rb_define_singleton_method(cCurlMulti, "autoclose", ruby_curl_multi_get_autoclose, 0); /* Instance methods */ rb_define_method(cCurlMulti, "max_connects=", ruby_curl_multi_max_connects, 1); rb_define_method(cCurlMulti, "pipeline=", ruby_curl_multi_pipeline, 1); rb_define_method(cCurlMulti, "_add", ruby_curl_multi_add, 1); rb_define_method(cCurlMulti, "_remove", ruby_curl_multi_remove, 1); rb_define_method(cCurlMulti, "perform", ruby_curl_multi_perform, -1); rb_define_method(cCurlMulti, "_close", ruby_curl_multi_close, 0); } curb-1.0.5/ext/curb.h0000644000004100000410000000222214362470347014414 0ustar www-datawww-data/* Curb - Libcurl(3) bindings for Ruby. * Copyright (c)2006 Ross Bamford. * Licensed under the Ruby License. See LICENSE for details. * * $Id: curb.h 39 2006-12-23 15:28:45Z roscopeco $ */ #ifndef __CURB_H #define __CURB_H #include #ifdef HAVE_RUBY_IO_H #include "ruby/io.h" #else #include "rubyio.h" // ruby 1.8 #endif #include #include "banned.h" #include "curb_config.h" #include "curb_easy.h" #include "curb_errors.h" #include "curb_postfield.h" #include "curb_multi.h" #include "curb_macros.h" // These should be managed from the Rake 'release' task. #define CURB_VERSION "1.0.5" #define CURB_VER_NUM 1005 #define CURB_VER_MAJ 1 #define CURB_VER_MIN 0 #define CURB_VER_MIC 5 #define CURB_VER_PATCH 0 // Maybe not yet defined in Ruby #ifndef RSTRING_LEN #define RSTRING_LEN(x) RSTRING(x)->len #endif #ifndef RSTRING_PTR #define RSTRING_PTR(x) RSTRING(x)->ptr #endif #ifndef RHASH_SIZE #define RHASH_SIZE(hash) RHASH(hash)->tbl->num_entries #endif // ruby 1.8 does not provide the macro #ifndef DBL2NUM #define DBL2NUM(dbl) rb_float_new(dbl) #endif extern VALUE mCurl; extern void Init_curb_core(); #endif curb-1.0.5/ext/banned.h0000644000004100000410000000143314362470347014713 0ustar www-datawww-data#ifndef BANNED_H #define BANNED_H /* * This header lists functions that have been banned from our code base, * because they're too easy to misuse (and even if used correctly, * complicate audits). Including this header turns them into compile-time * errors. */ #define BANNED(func) sorry_##func##_is_a_banned_function #undef strcpy #define strcpy(x,y) BANNED(strcpy) #undef strcat #define strcat(x,y) BANNED(strcat) #undef strncpy #define strncpy(x,y,n) BANNED(strncpy) #undef strncat #define strncat(x,y,n) BANNED(strncat) #undef sprintf #undef vsprintf #ifdef HAVE_VARIADIC_MACROS #define sprintf(...) BANNED(sprintf) #define vsprintf(...) BANNED(vsprintf) #else #define sprintf(buf,fmt,arg) BANNED(sprintf) #define vsprintf(buf,fmt,arg) BANNED(sprintf) #endif #endif /* BANNED_H */ curb-1.0.5/ext/curb_upload.h0000644000004100000410000000136214362470347015764 0ustar www-datawww-data/* curb_upload.h - Curl upload handle * Copyright (c)2009 Todd A Fisher. * Licensed under the Ruby License. See LICENSE for details. */ #ifndef __CURB_UPLOAD_H #define __CURB_UPLOAD_H #include "curb.h" #include /* * Maintain the state of an upload e.g. for putting large streams with very little memory * out to a server. via PUT requests */ typedef struct { VALUE stream; size_t offset; } ruby_curl_upload; extern VALUE cCurlUpload; void init_curb_upload(); VALUE ruby_curl_upload_new(VALUE klass); VALUE ruby_curl_upload_stream_set(VALUE self, VALUE stream); VALUE ruby_curl_upload_stream_get(VALUE self); VALUE ruby_curl_upload_offset_set(VALUE self, VALUE offset); VALUE ruby_curl_upload_offset_get(VALUE self); #endif curb-1.0.5/ext/curb_easy.c0000644000004100000410000037667614362470347015463 0ustar www-datawww-data/* curb_easy.c - Curl easy mode * Copyright (c)2006 Ross Bamford. * Licensed under the Ruby License. See LICENSE for details. * * $Id: curb_easy.c 30 2006-12-09 12:30:24Z roscopeco $ */ #include "curb_easy.h" #include "curb_errors.h" #include "curb_postfield.h" #include "curb_upload.h" #include "curb_multi.h" #include #include extern VALUE mCurl; static VALUE idCall; static VALUE idJoin; static VALUE rbstrAmp; #ifdef RDOC_NEVER_DEFINED mCurl = rb_define_module("Curl"); #endif VALUE cCurlEasy; // for Ruby 1.8 #ifndef HAVE_RB_IO_STDIO_FILE static FILE * rb_io_stdio_file(rb_io_t *fptr) { return fptr->f; } #endif /* ================== CURL HANDLER FUNCS ==============*/ static VALUE callback_exception(VALUE unused, VALUE exception) { return Qfalse; } /* These handle both body and header data */ static size_t default_data_handler(char *stream, size_t size, size_t nmemb, VALUE out) { rb_str_buf_cat(out, stream, size * nmemb); return size * nmemb; } // size_t function( void *ptr, size_t size, size_t nmemb, void *stream); static size_t read_data_handler(void *ptr, size_t size, size_t nmemb, ruby_curl_easy *rbce) { VALUE upload = rb_easy_get("upload"); size_t read_bytes = (size*nmemb); VALUE stream = ruby_curl_upload_stream_get(upload); if (rb_respond_to(stream, rb_intern("read"))) {//if (rb_respond_to(stream, rb_intern("to_s"))) { /* copy read_bytes from stream into ptr */ VALUE str = rb_funcall(stream, rb_intern("read"), 1, rb_int_new(read_bytes) ); if( str != Qnil ) { memcpy(ptr, RSTRING_PTR(str), RSTRING_LEN(str)); return RSTRING_LEN(str); } else { return 0; } } else if (rb_respond_to(stream, rb_intern("to_s"))) { ruby_curl_upload *rbcu; VALUE str; size_t len; size_t remaining; char *str_ptr; Data_Get_Struct(upload, ruby_curl_upload, rbcu); str = rb_funcall(stream, rb_intern("to_s"), 0); len = RSTRING_LEN(str); remaining = len - rbcu->offset; str_ptr = RSTRING_PTR(str); if( remaining < read_bytes ) { if( remaining > 0 ) { memcpy(ptr, str_ptr+rbcu->offset, remaining); read_bytes = remaining; rbcu->offset += remaining; } return remaining; } else if( remaining > read_bytes ) { // read_bytes <= remaining - send what we can fit in the buffer(ptr) memcpy(ptr, str_ptr+rbcu->offset, read_bytes); rbcu->offset += read_bytes; } else { // they're equal memcpy(ptr, str_ptr+rbcu->offset, --read_bytes); rbcu->offset += read_bytes; } return read_bytes; } else { return 0; } } int seek_data_handler(ruby_curl_easy *rbce, curl_off_t offset, int origin) { VALUE upload = rb_easy_get("upload"); VALUE stream = ruby_curl_upload_stream_get(upload); if (rb_respond_to(stream, rb_intern("seek"))) { rb_funcall(stream, rb_intern("seek"), 2, SEEK_SET, offset); } else { ruby_curl_upload *rbcu; Data_Get_Struct(upload, ruby_curl_upload, rbcu); // This OK because curl only uses SEEK_SET as per the documentation rbcu->offset = offset; } return 0; } static size_t proc_data_handler(char *stream, size_t size, size_t nmemb, VALUE proc) { VALUE procret; procret = rb_funcall(proc, idCall, 1, rb_str_new(stream, size * nmemb)); switch (rb_type(procret)) { case T_FIXNUM: return FIX2LONG(procret); case T_BIGNUM: return NUM2LONG(procret); default: rb_warn("Curl data handlers should return the number of bytes read as an Integer"); return size * nmemb; } } static size_t proc_data_handler_body(char *stream, size_t size, size_t nmemb, ruby_curl_easy *rbce) { size_t ret; rbce->callback_active = 1; ret = proc_data_handler(stream, size, nmemb, rb_easy_get("body_proc")); rbce->callback_active = 0; return ret; } static size_t proc_data_handler_header(char *stream, size_t size, size_t nmemb, ruby_curl_easy *rbce) { size_t ret; rbce->callback_active = 1; ret = proc_data_handler(stream, size, nmemb, rb_easy_get("header_proc")); rbce->callback_active = 0; return ret; } static VALUE call_progress_handler(VALUE ary) { return rb_funcall(rb_ary_entry(ary, 0), idCall, 4, rb_ary_entry(ary, 1), // rb_float_new(dltotal), rb_ary_entry(ary, 2), // rb_float_new(dlnow), rb_ary_entry(ary, 3), // rb_float_new(ultotal), rb_ary_entry(ary, 4)); // rb_float_new(ulnow)); } static int proc_progress_handler(VALUE proc, double dltotal, double dlnow, double ultotal, double ulnow) { VALUE procret; VALUE callargs = rb_ary_new2(5); rb_ary_store(callargs, 0, proc); rb_ary_store(callargs, 1, rb_float_new(dltotal)); rb_ary_store(callargs, 2, rb_float_new(dlnow)); rb_ary_store(callargs, 3, rb_float_new(ultotal)); rb_ary_store(callargs, 4, rb_float_new(ulnow)); //v = rb_rescue(range_check, (VALUE)args, range_failed, 0); //procret = rb_funcall(proc, idCall, 4, rb_float_new(dltotal), // rb_float_new(dlnow), // rb_float_new(ultotal), // rb_float_new(ulnow)); procret = rb_rescue(call_progress_handler, callargs, callback_exception, Qnil); return(((procret == Qfalse) || (procret == Qnil)) ? -1 : 0); } static VALUE call_debug_handler(VALUE ary) { return rb_funcall(rb_ary_entry(ary, 0), idCall, 2, rb_ary_entry(ary, 1), // INT2NUM(type), rb_ary_entry(ary, 2)); // rb_str_new(data, data_len) } static int proc_debug_handler(CURL *curl, curl_infotype type, char *data, size_t data_len, VALUE proc) { VALUE callargs = rb_ary_new2(3); rb_ary_store(callargs, 0, proc); rb_ary_store(callargs, 1, INT2NUM(type)); rb_ary_store(callargs, 2, rb_str_new(data, data_len)); rb_rescue(call_debug_handler, callargs, callback_exception, Qnil); /* no way to indicate to libcurl that we should break out given an exception in the on_debug handler... * this means exceptions will be swallowed */ //rb_funcall(proc, idCall, 2, INT2NUM(type), rb_str_new(data, data_len)); return 0; } /* ================== MARK/FREE FUNC ==================*/ void curl_easy_mark(ruby_curl_easy *rbce) { if (!NIL_P(rbce->opts)) { rb_gc_mark(rbce->opts); } if (!NIL_P(rbce->multi)) { rb_gc_mark(rbce->multi); } } static void ruby_curl_easy_free(ruby_curl_easy *rbce) { if (rbce->curl_headers) { curl_slist_free_all(rbce->curl_headers); } if (rbce->curl_proxy_headers) { curl_slist_free_all(rbce->curl_proxy_headers); } if (rbce->curl_ftp_commands) { curl_slist_free_all(rbce->curl_ftp_commands); } if (rbce->curl_resolve) { curl_slist_free_all(rbce->curl_resolve); } if (rbce->curl) { /* disable any progress or debug events */ curl_easy_setopt(rbce->curl, CURLOPT_WRITEFUNCTION, NULL); curl_easy_setopt(rbce->curl, CURLOPT_WRITEDATA, NULL); curl_easy_setopt(rbce->curl, CURLOPT_HEADERFUNCTION, NULL); curl_easy_setopt(rbce->curl, CURLOPT_HEADERDATA, NULL); curl_easy_setopt(rbce->curl, CURLOPT_DEBUGFUNCTION, NULL); curl_easy_setopt(rbce->curl, CURLOPT_DEBUGDATA, NULL); curl_easy_setopt(rbce->curl, CURLOPT_VERBOSE, 0); curl_easy_setopt(rbce->curl, CURLOPT_PROGRESSFUNCTION, NULL); curl_easy_setopt(rbce->curl, CURLOPT_NOPROGRESS, 1); curl_easy_cleanup(rbce->curl); rbce->curl = NULL; } } void curl_easy_free(ruby_curl_easy *rbce) { ruby_curl_easy_free(rbce); free(rbce); } /* ================= ALLOC METHODS ====================*/ static void ruby_curl_easy_zero(ruby_curl_easy *rbce) { rbce->opts = rb_hash_new(); memset(rbce->err_buf, 0, sizeof(rbce->err_buf)); rbce->curl_headers = NULL; rbce->curl_proxy_headers = NULL; rbce->curl_ftp_commands = NULL; rbce->curl_resolve = NULL; /* various-typed opts */ rbce->local_port = 0; rbce->local_port_range = 0; rbce->proxy_port = 0; rbce->proxy_type = -1; rbce->http_auth_types = 0; rbce->proxy_auth_types = 0; rbce->max_redirs = -1; rbce->timeout = 0; rbce->timeout_ms = 0; rbce->connect_timeout = 0; rbce->connect_timeout_ms = 0; rbce->dns_cache_timeout = 60; rbce->ftp_response_timeout = 0; rbce->low_speed_limit = 0; rbce->low_speed_time = 0; rbce->max_send_speed_large = 0; rbce->max_recv_speed_large = 0; rbce->ssl_version = -1; rbce->use_ssl = -1; rbce->ftp_filemethod = -1; rbce->resolve_mode = CURL_IPRESOLVE_WHATEVER; /* bool opts */ rbce->proxy_tunnel = 0; rbce->fetch_file_time = 0; rbce->ssl_verify_peer = 1; rbce->ssl_verify_host = 2; rbce->header_in_body = 0; rbce->use_netrc = 0; rbce->follow_location = 0; rbce->unrestricted_auth = 0; rbce->verbose = 0; rbce->multipart_form_post = 0; rbce->enable_cookies = 0; rbce->ignore_content_length = 0; rbce->callback_active = 0; } /* * Allocate space for a Curl::Easy instance. */ static VALUE ruby_curl_easy_allocate(VALUE klass) { ruby_curl_easy *rbce; rbce = ALLOC(ruby_curl_easy); rbce->curl = NULL; rbce->opts = Qnil; rbce->multi = Qnil; ruby_curl_easy_zero(rbce); return Data_Wrap_Struct(klass, curl_easy_mark, curl_easy_free, rbce); } /* * call-seq: * Curl::Easy.new => # * Curl::Easy.new(url = nil) => # * Curl::Easy.new(url = nil) { |self| ... } => # * * Initialize a new Curl::Easy instance, optionally supplying the URL. * The block form allows further configuration to be supplied before * the instance is returned. */ static VALUE ruby_curl_easy_initialize(int argc, VALUE *argv, VALUE self) { CURLcode ecode; VALUE url, blk; ruby_curl_easy *rbce; rb_scan_args(argc, argv, "01&", &url, &blk); Data_Get_Struct(self, ruby_curl_easy, rbce); /* handler */ rbce->curl = curl_easy_init(); if (!rbce->curl) { rb_raise(eCurlErrFailedInit, "Failed to initialize easy handle"); } rbce->multi = Qnil; rbce->opts = Qnil; ruby_curl_easy_zero(rbce); curl_easy_setopt(rbce->curl, CURLOPT_ERRORBUFFER, &rbce->err_buf); rb_easy_set("url", url); /* set the pointer to the curl handle */ ecode = curl_easy_setopt(rbce->curl, CURLOPT_PRIVATE, (void*)self); if (ecode != CURLE_OK) { raise_curl_easy_error_exception(ecode); } if (blk != Qnil) { rb_funcall(blk, idCall, 1, self); } return self; } /* * call-seq: * easy.clone => * easy.dup => * * Clone this Curl::Easy instance, creating a new instance. * This method duplicates the underlying CURL* handle. */ static VALUE ruby_curl_easy_clone(VALUE self) { ruby_curl_easy *rbce, *newrbce; Data_Get_Struct(self, ruby_curl_easy, rbce); newrbce = ALLOC(ruby_curl_easy); memcpy(newrbce, rbce, sizeof(ruby_curl_easy)); newrbce->curl = curl_easy_duphandle(rbce->curl); newrbce->curl_headers = NULL; newrbce->curl_proxy_headers = NULL; newrbce->curl_ftp_commands = NULL; newrbce->curl_resolve = NULL; curl_easy_setopt(rbce->curl, CURLOPT_ERRORBUFFER, &rbce->err_buf); return Data_Wrap_Struct(cCurlEasy, curl_easy_mark, curl_easy_free, newrbce); } /* * call-seq: * easy.close => nil * * Close the Curl::Easy instance. Any open connections are closed * The easy handle is reinitialized. If a previous multi handle was * open it is set to nil and will be cleared after a GC. */ static VALUE ruby_curl_easy_close(VALUE self) { CURLcode ecode; ruby_curl_easy *rbce; Data_Get_Struct(self, ruby_curl_easy, rbce); if (rbce->callback_active) { rb_raise(rb_eRuntimeError, "Cannot close an active curl handle within a callback"); } ruby_curl_easy_free(rbce); /* reinit the handle */ rbce->curl = curl_easy_init(); if (!rbce->curl) { rb_raise(eCurlErrFailedInit, "Failed to initialize easy handle"); } rbce->multi = Qnil; ruby_curl_easy_zero(rbce); /* give the new curl handle a reference back to the ruby object */ ecode = curl_easy_setopt(rbce->curl, CURLOPT_PRIVATE, (void*)self); if (ecode != CURLE_OK) { raise_curl_easy_error_exception(ecode); } return Qnil; } /* * call-seq: * easy.reset => Hash * * Reset the Curl::Easy instance, clears out all settings. * * from http://curl.haxx.se/libcurl/c/curl_easy_reset.html * Re-initializes all options previously set on a specified CURL handle to the default values. This puts back the handle to the same state as it was in when it was just created with curl_easy_init(3). * It does not change the following information kept in the handle: live connections, the Session ID cache, the DNS cache, the cookies and shares. * * The return value contains all settings stored. */ static VALUE ruby_curl_easy_reset(VALUE self) { CURLcode ecode; ruby_curl_easy *rbce; VALUE opts_dup; Data_Get_Struct(self, ruby_curl_easy, rbce); if (rbce->callback_active) { rb_raise(rb_eRuntimeError, "Cannot close an active curl handle within a callback"); } opts_dup = rb_funcall(rbce->opts, rb_intern("dup"), 0); curl_easy_reset(rbce->curl); ruby_curl_easy_zero(rbce); curl_easy_setopt(rbce->curl, CURLOPT_ERRORBUFFER, &rbce->err_buf); /* reset clobbers the private setting, so reset it to self */ ecode = curl_easy_setopt(rbce->curl, CURLOPT_PRIVATE, (void*)self); if (ecode != CURLE_OK) { raise_curl_easy_error_exception(ecode); } /* Free everything up */ if (rbce->curl_headers) { curl_slist_free_all(rbce->curl_headers); rbce->curl_headers = NULL; } /* Free everything up */ if (rbce->curl_proxy_headers) { curl_slist_free_all(rbce->curl_proxy_headers); rbce->curl_proxy_headers = NULL; } return opts_dup; } /* ================ OBJ ATTRIBUTES ==================*/ /* * call-seq: * easy.url => string * * Obtain the URL that will be used by subsequent calls to +perform+. */ static VALUE ruby_curl_easy_url_get(VALUE self) { CURB_OBJECT_HGETTER(ruby_curl_easy, url); } /* * call-seq: * easy.proxy_url => string * * Obtain the HTTP Proxy URL that will be used by subsequent calls to +perform+. */ static VALUE ruby_curl_easy_proxy_url_get(VALUE self) { CURB_OBJECT_HGETTER(ruby_curl_easy, proxy_url); } /* * call-seq: * easy.headers = "Header: val" => "Header: val" * easy.headers = {"Header" => "val" ..., "Header" => "val"} => {"Header: val", ...} * easy.headers = ["Header: val" ..., "Header: val"] => ["Header: val", ...] * * Set custom HTTP headers for following requests. This can be used to add * custom headers, or override standard headers used by libcurl. It defaults to a * Hash. * * For example to set a standard or custom header: * * easy.headers["MyHeader"] = "myval" * * To remove a standard header (this is useful when removing libcurls default * 'Expect: 100-Continue' header when using HTTP form posts): * * easy.headers["Expect"] = '' * * Anything passed to libcurl as a header will be converted to a string during * the perform step. */ static VALUE ruby_curl_easy_headers_set(VALUE self, VALUE headers) { CURB_OBJECT_HSETTER(ruby_curl_easy, headers); } static VALUE ruby_curl_easy_proxy_headers_set(VALUE self, VALUE proxy_headers) { CURB_OBJECT_HSETTER(ruby_curl_easy, proxy_headers); } /* * call-seq: * easy.headers => Hash, Array or Str * * Obtain the custom HTTP headers for following requests. */ static VALUE ruby_curl_easy_headers_get(VALUE self) { ruby_curl_easy *rbce; VALUE headers; Data_Get_Struct(self, ruby_curl_easy, rbce); headers = rb_easy_get("headers");//rb_hash_aref(rbce->opts, rb_intern("headers")); if (headers == Qnil) { headers = rb_easy_set("headers", rb_hash_new()); } return headers; } /* * call-seq: * easy.proxy_headers = "Header: val" => "Header: val" * easy.proxy_headers = {"Header" => "val" ..., "Header" => "val"} => {"Header: val", ...} * easy.proxy_headers = ["Header: val" ..., "Header: val"] => ["Header: val", ...] * * * For example to set a standard or custom header: * * easy.proxy_headers["MyHeader"] = "myval" * * To remove a standard header (this is useful when removing libcurls default * 'Expect: 100-Continue' header when using HTTP form posts): * * easy.proxy_headers["Expect"] = '' * * Anything passed to libcurl as a header will be converted to a string during * the perform step. */ /* * call-seq: * easy.proxy_headers => Hash, Array or Str * * Obtain the custom HTTP proxy_headers for following requests. */ static VALUE ruby_curl_easy_proxy_headers_get(VALUE self) { ruby_curl_easy *rbce; VALUE proxy_headers; Data_Get_Struct(self, ruby_curl_easy, rbce); proxy_headers = rb_easy_get("proxy_headers");//rb_hash_aref(rbce->opts, rb_intern("proxy_headers")); if (proxy_headers == Qnil) { proxy_headers = rb_easy_set("proxy_headers", rb_hash_new()); } return proxy_headers; } /* * call-seq: * easy.interface => string * * Obtain the interface name that is used as the outgoing network interface. * The name can be an interface name, an IP address or a host name. */ static VALUE ruby_curl_easy_interface_get(VALUE self) { CURB_OBJECT_HGETTER(ruby_curl_easy, interface_hm); } /* * call-seq: * easy.userpwd => string * * Obtain the username/password string that will be used for subsequent * calls to +perform+. */ static VALUE ruby_curl_easy_userpwd_get(VALUE self) { CURB_OBJECT_HGETTER(ruby_curl_easy, userpwd); } /* * call-seq: * easy.proxypwd => string * * Obtain the username/password string that will be used for proxy * connection during subsequent calls to +perform+. The supplied string * should have the form "username:password" */ static VALUE ruby_curl_easy_proxypwd_get(VALUE self) { CURB_OBJECT_HGETTER(ruby_curl_easy, proxypwd); } /* * call-seq: * easy.cookies => "name1=content1; name2=content2;" * * Obtain the cookies for this Curl::Easy instance. */ static VALUE ruby_curl_easy_cookies_get(VALUE self) { CURB_OBJECT_HGETTER(ruby_curl_easy, cookies); } /* * call-seq: * easy.cookiefile => string * * Obtain the cookiefile file for this Curl::Easy instance. */ static VALUE ruby_curl_easy_cookiefile_get(VALUE self) { CURB_OBJECT_HGETTER(ruby_curl_easy, cookiefile); } /* * call-seq: * easy.cookiejar => string * * Obtain the cookiejar file to use for this Curl::Easy instance. */ static VALUE ruby_curl_easy_cookiejar_get(VALUE self) { CURB_OBJECT_HGETTER(ruby_curl_easy, cookiejar); } /* * call-seq: * easy.cert = string => "" * * Set a cert file to use for this Curl::Easy instance. This file * will be used to validate SSL connections. * */ static VALUE ruby_curl_easy_cert_set(VALUE self, VALUE cert) { CURB_OBJECT_HSETTER(ruby_curl_easy, cert); } /* * call-seq: * easy.cert => string * * Obtain the cert file to use for this Curl::Easy instance. */ static VALUE ruby_curl_easy_cert_get(VALUE self) { CURB_OBJECT_HGETTER(ruby_curl_easy, cert); } /* * call-seq: * easy.cert_key = "cert_key.file" => "" * * Set a cert key to use for this Curl::Easy instance. This file * will be used to validate SSL certificates. * */ static VALUE ruby_curl_easy_cert_key_set(VALUE self, VALUE cert_key) { CURB_OBJECT_HSETTER(ruby_curl_easy, cert_key); } /* * call-seq: * easy.cert_key => "cert_key.file" * * Obtain the cert key file to use for this Curl::Easy instance. */ static VALUE ruby_curl_easy_cert_key_get(VALUE self) { CURB_OBJECT_HGETTER(ruby_curl_easy, cert_key); } /* * call-seq: * easy.cacert = string => "" * * Set a cacert bundle to use for this Curl::Easy instance. This file * will be used to validate SSL certificates. * */ static VALUE ruby_curl_easy_cacert_set(VALUE self, VALUE cacert) { CURB_OBJECT_HSETTER(ruby_curl_easy, cacert); } /* * call-seq: * easy.cacert => string * * Obtain the cacert file to use for this Curl::Easy instance. */ static VALUE ruby_curl_easy_cacert_get(VALUE self) { CURB_OBJECT_HGETTER(ruby_curl_easy, cacert); } /* * call-seq: * easy.certpassword = string => "" * * Set a password used to open the specified cert */ static VALUE ruby_curl_easy_certpassword_set(VALUE self, VALUE certpassword) { CURB_OBJECT_HSETTER(ruby_curl_easy, certpassword); } /* * call-seq: * easy.certtype = "PEM|DER" => "" * * Set a cert type to use for this Curl::Easy instance. * Default is PEM * */ static VALUE ruby_curl_easy_certtype_set(VALUE self, VALUE certtype) { CURB_OBJECT_HSETTER(ruby_curl_easy, certtype); } /* * call-seq: * easy.certtype => string * * Obtain the cert type used for this Curl::Easy instance */ static VALUE ruby_curl_easy_certtype_get(VALUE self) { CURB_OBJECT_HGETTER(ruby_curl_easy, certtype); } /* * call-seq: * easy.encoding = string => string * * Set the accepted encoding types, curl will handle all of the decompression * */ static VALUE ruby_curl_easy_encoding_set(VALUE self, VALUE encoding) { CURB_OBJECT_HSETTER(ruby_curl_easy, encoding); } /* * call-seq: * easy.encoding => string * * Get the set encoding types * */ static VALUE ruby_curl_easy_encoding_get(VALUE self) { CURB_OBJECT_HGETTER(ruby_curl_easy, encoding); } /* * call-seq: * easy.useragent = "Ruby/Curb" => "" * * Set the user agent string for this Curl::Easy instance * */ static VALUE ruby_curl_easy_useragent_set(VALUE self, VALUE useragent) { CURB_OBJECT_HSETTER(ruby_curl_easy, useragent); } /* * call-seq: * easy.useragent => "Ruby/Curb" * * Obtain the user agent string used for this Curl::Easy instance */ static VALUE ruby_curl_easy_useragent_get(VALUE self) { CURB_OBJECT_HGETTER(ruby_curl_easy, useragent); } /* * call-seq: * easy.post_body = "some=form%20data&to=send" => string or nil * * Sets the POST body of this Curl::Easy instance. This is expected to be * URL encoded; no additional processing or encoding is done on the string. * The content-type header will be set to application/x-www-form-urlencoded. * * This is handy if you want to perform a POST against a Curl::Multi instance. */ static VALUE ruby_curl_easy_post_body_set(VALUE self, VALUE post_body) { ruby_curl_easy *rbce; CURL *curl; char *data; long len; Data_Get_Struct(self, ruby_curl_easy, rbce); curl = rbce->curl; if ( post_body == Qnil ) { rb_easy_del("postdata_buffer"); curl_easy_setopt(curl, CURLOPT_HTTPGET, 1); } else { if (rb_type(post_body) == T_STRING) { data = StringValuePtr(post_body); len = RSTRING_LEN(post_body); } else if (rb_respond_to(post_body, rb_intern("to_s"))) { VALUE str_body = rb_funcall(post_body, rb_intern("to_s"), 0); data = StringValuePtr(str_body); len = RSTRING_LEN(post_body); } else { rb_raise(rb_eRuntimeError, "post data must respond_to .to_s"); } // Store the string, since it has to hang around for the duration of the // request. See CURLOPT_POSTFIELDS in the libcurl docs. //rbce->postdata_buffer = post_body; rb_easy_set("postdata_buffer", post_body); curl_easy_setopt(curl, CURLOPT_POST, 1); curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data); curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, len); return post_body; } return Qnil; } /* * call-seq: * easy.post_body => string or nil * * Obtain the POST body used in this Curl::Easy instance. */ static VALUE ruby_curl_easy_post_body_get(VALUE self) { CURB_OBJECT_HGETTER(ruby_curl_easy, postdata_buffer); } /* * call-seq: * easy.put_data = data => "" * * Points this Curl::Easy instance to data to be uploaded via PUT. This * sets the request to a PUT type request - useful if you want to PUT via * a multi handle. */ static VALUE ruby_curl_easy_put_data_set(VALUE self, VALUE data) { ruby_curl_easy *rbce; CURL *curl; VALUE upload; VALUE headers; Data_Get_Struct(self, ruby_curl_easy, rbce); upload = ruby_curl_upload_new(cCurlUpload); ruby_curl_upload_stream_set(upload,data); curl = rbce->curl; rb_easy_set("upload", upload); /* keep the upload object alive as long as the easy handle is active or until the upload is complete or terminated... */ curl_easy_setopt(curl, CURLOPT_NOBODY, 0); curl_easy_setopt(curl, CURLOPT_UPLOAD, 1); curl_easy_setopt(curl, CURLOPT_READFUNCTION, (curl_read_callback)read_data_handler); #if HAVE_CURLOPT_SEEKFUNCTION curl_easy_setopt(curl, CURLOPT_SEEKFUNCTION, (curl_seek_callback)seek_data_handler); #endif curl_easy_setopt(curl, CURLOPT_READDATA, rbce); #if HAVE_CURLOPT_SEEKDATA curl_easy_setopt(curl, CURLOPT_SEEKDATA, rbce); #endif /* * we need to set specific headers for the PUT to work... so * convert the internal headers structure to a HASH if one is set */ if (!rb_easy_nil("headers")) { if (rb_easy_type_check("headers", T_ARRAY) || rb_easy_type_check("headers", T_STRING)) { rb_raise(rb_eRuntimeError, "Must set headers as a HASH to modify the headers in an PUT request"); } } // exit fast if the payload is empty if (NIL_P(data)) { return data; } headers = rb_easy_get("headers"); if( headers == Qnil ) { headers = rb_hash_new(); } if (rb_respond_to(data, rb_intern("read"))) { VALUE stat = rb_funcall(data, rb_intern("stat"), 0); if( stat && rb_hash_aref(headers, rb_str_new2("Content-Length")) == Qnil) { VALUE size; if( rb_hash_aref(headers, rb_str_new2("Expect")) == Qnil ) { rb_hash_aset(headers, rb_str_new2("Expect"), rb_str_new2("")); } size = rb_funcall(stat, rb_intern("size"), 0); curl_easy_setopt(curl, CURLOPT_INFILESIZE, NUM2LONG(size)); } else if( rb_hash_aref(headers, rb_str_new2("Content-Length")) == Qnil && rb_hash_aref(headers, rb_str_new2("Transfer-Encoding")) == Qnil ) { rb_hash_aset(headers, rb_str_new2("Transfer-Encoding"), rb_str_new2("chunked")); } else if( rb_hash_aref(headers, rb_str_new2("Content-Length")) ) { VALUE size = rb_funcall(rb_hash_aref(headers, rb_str_new2("Content-Length")), rb_intern("to_i"), 0); curl_easy_setopt(curl, CURLOPT_INFILESIZE, NUM2LONG(size)); } } else if (rb_respond_to(data, rb_intern("to_s"))) { curl_easy_setopt(curl, CURLOPT_INFILESIZE, RSTRING_LEN(data)); if( rb_hash_aref(headers, rb_str_new2("Expect")) == Qnil ) { rb_hash_aset(headers, rb_str_new2("Expect"), rb_str_new2("")); } } else { rb_raise(rb_eRuntimeError, "PUT data must respond to read or to_s"); } rb_easy_set("headers",headers); // if we made it this far, all should be well. return data; } /* * call-seq: * easy.ftp_commands = ["CWD /", "MKD directory"] => ["CWD /", ...] * * Explicitly sets the list of commands to execute on the FTP server when calling perform */ static VALUE ruby_curl_easy_ftp_commands_set(VALUE self, VALUE ftp_commands) { CURB_OBJECT_HSETTER(ruby_curl_easy, ftp_commands); } /* * call-seq: * easy.ftp_commands => array or nil */ static VALUE ruby_curl_easy_ftp_commands_get(VALUE self) { CURB_OBJECT_HGETTER(ruby_curl_easy, ftp_commands); } /* * call-seq: * easy.resolve = [ "example.com:80:127.0.0.1" ] => [ "example.com:80:127.0.0.1" ] * * Set the resolve list to statically resolve hostnames to IP addresses, * bypassing DNS for matching hostname/port combinations. */ static VALUE ruby_curl_easy_resolve_set(VALUE self, VALUE resolve) { CURB_OBJECT_HSETTER(ruby_curl_easy, resolve); } /* * call-seq: * easy.resolve => array or nil */ static VALUE ruby_curl_easy_resolve_get(VALUE self) { CURB_OBJECT_HGETTER(ruby_curl_easy, resolve); } /* ================== IMMED ATTRS ==================*/ /* * call-seq: * easy.local_port = fixnum or nil => fixnum or nil * * Set the local port that will be used for the following +perform+ calls. * * Passing +nil+ will return to the default behaviour (no local port * preference). * * This option is ignored if compiled against libcurl < 7.15.2. */ static VALUE ruby_curl_easy_local_port_set(VALUE self, VALUE local_port) { CURB_IMMED_PORT_SETTER(ruby_curl_easy, local_port, "port"); } /* * call-seq: * easy.local_port => fixnum or nil * * Obtain the local port that will be used for the following +perform+ calls. * * This option is ignored if compiled against libcurl < 7.15.2. */ static VALUE ruby_curl_easy_local_port_get(VALUE self) { CURB_IMMED_PORT_GETTER(ruby_curl_easy, local_port); } /* * call-seq: * easy.local_port_range = fixnum or nil => fixnum or nil * * Set the local port range that will be used for the following +perform+ * calls. This is a number (between 0 and 65535) that determines how far * libcurl may deviate from the supplied +local_port+ in order to find * an available port. * * If you set +local_port+ it's also recommended that you set this, since * it is fairly likely that your specified port will be unavailable. * * This option is ignored if compiled against libcurl < 7.15.2. */ static VALUE ruby_curl_easy_local_port_range_set(VALUE self, VALUE local_port_range) { CURB_IMMED_PORT_SETTER(ruby_curl_easy, local_port_range, "port range"); } /* * call-seq: * easy.local_port_range => fixnum or nil * * Obtain the local port range that will be used for the following +perform+ * calls. * * This option is ignored if compiled against libcurl < 7.15.2. */ static VALUE ruby_curl_easy_local_port_range_get(VALUE self) { CURB_IMMED_PORT_GETTER(ruby_curl_easy, local_port_range); } /* * call-seq: * easy.proxy_port = fixnum or nil => fixnum or nil * * Set the proxy port that will be used for the following +perform+ calls. */ static VALUE ruby_curl_easy_proxy_port_set(VALUE self, VALUE proxy_port) { CURB_IMMED_PORT_SETTER(ruby_curl_easy, proxy_port, "port"); } /* * call-seq: * easy.proxy_port => fixnum or nil * * Obtain the proxy port that will be used for the following +perform+ calls. */ static VALUE ruby_curl_easy_proxy_port_get(VALUE self) { CURB_IMMED_PORT_GETTER(ruby_curl_easy, proxy_port); } /* * call-seq: * easy.proxy_type = fixnum or nil => fixnum or nil * * Set the proxy type that will be used for the following +perform+ calls. * This should be one of the Curl::CURLPROXY constants. */ static VALUE ruby_curl_easy_proxy_type_set(VALUE self, VALUE proxy_type) { CURB_IMMED_SETTER(ruby_curl_easy, proxy_type, -1); } /* * call-seq: * easy.proxy_type => fixnum or nil * * Obtain the proxy type that will be used for the following +perform+ calls. */ static VALUE ruby_curl_easy_proxy_type_get(VALUE self) { CURB_IMMED_GETTER(ruby_curl_easy, proxy_type, -1); } #if defined(HAVE_CURLAUTH_DIGEST_IE) #define CURL_HTTPAUTH_STR_TO_NUM(node) \ (!strncmp("basic",node,5)) ? CURLAUTH_BASIC : \ (!strncmp("digest_ie",node,9)) ? CURLAUTH_DIGEST_IE : \ (!strncmp("digest",node,6)) ? CURLAUTH_DIGEST : \ (!strncmp("gssnegotiate",node,12)) ? CURLAUTH_GSSNEGOTIATE : \ (!strncmp("ntlm",node,4)) ? CURLAUTH_NTLM : \ (!strncmp("anysafe",node,7)) ? CURLAUTH_ANYSAFE : \ (!strncmp("any",node,3)) ? CURLAUTH_ANY : 0 #else #define CURL_HTTPAUTH_STR_TO_NUM(node) \ (!strncmp("basic",node,5)) ? CURLAUTH_BASIC : \ (!strncmp("digest",node,6)) ? CURLAUTH_DIGEST : \ (!strncmp("gssnegotiate",node,12)) ? CURLAUTH_GSSNEGOTIATE : \ (!strncmp("ntlm",node,4)) ? CURLAUTH_NTLM : \ (!strncmp("anysafe",node,7)) ? CURLAUTH_ANYSAFE : \ (!strncmp("any",node,3)) ? CURLAUTH_ANY : 0 #endif /* * call-seq: * easy.http_auth_types = fixnum or nil => fixnum or nil * easy.http_auth_types = [:basic,:digest,:digest_ie,:gssnegotiate, :ntlm, :any, :anysafe] * * Set the HTTP authentication types that may be used for the following * +perform+ calls. This is a bitmap made by ORing together the * Curl::CURLAUTH constants. */ static VALUE ruby_curl_easy_http_auth_types_set(int argc, VALUE *argv, VALUE self) {//VALUE self, VALUE http_auth_types) { ruby_curl_easy *rbce; VALUE args_ary; long i, len; char* node = NULL; long mask = 0; rb_scan_args(argc, argv, "*", &args_ary); Data_Get_Struct(self, ruby_curl_easy, rbce); len = RARRAY_LEN(args_ary); if (len == 1 && (rb_ary_entry(args_ary,0) == Qnil || TYPE(rb_ary_entry(args_ary,0)) == T_FIXNUM || TYPE(rb_ary_entry(args_ary,0)) == T_BIGNUM)) { if (rb_ary_entry(args_ary,0) == Qnil) { rbce->http_auth_types = 0; } else { rbce->http_auth_types = NUM2LONG(rb_ary_entry(args_ary,0)); } } else { // we could have multiple values, but they should be symbols node = RSTRING_PTR(rb_funcall(rb_ary_entry(args_ary,0),rb_intern("to_s"),0)); mask = CURL_HTTPAUTH_STR_TO_NUM(node); for( i = 1; i < len; ++i ) { node = RSTRING_PTR(rb_funcall(rb_ary_entry(args_ary,i),rb_intern("to_s"),0)); mask |= CURL_HTTPAUTH_STR_TO_NUM(node); } rbce->http_auth_types = mask; } return LONG2NUM(rbce->http_auth_types); } /* * call-seq: * easy.http_auth_types => fixnum or nil * * Obtain the HTTP authentication types that may be used for the following * +perform+ calls. */ static VALUE ruby_curl_easy_http_auth_types_get(VALUE self) { CURB_IMMED_GETTER(ruby_curl_easy, http_auth_types, 0); } /* * call-seq: * easy.proxy_auth_types = fixnum or nil => fixnum or nil * * Set the proxy authentication types that may be used for the following * +perform+ calls. This is a bitmap made by ORing together the * Curl::CURLAUTH constants. */ static VALUE ruby_curl_easy_proxy_auth_types_set(VALUE self, VALUE proxy_auth_types) { CURB_IMMED_SETTER(ruby_curl_easy, proxy_auth_types, 0); } /* * call-seq: * easy.proxy_auth_types => fixnum or nil * * Obtain the proxy authentication types that may be used for the following * +perform+ calls. */ static VALUE ruby_curl_easy_proxy_auth_types_get(VALUE self) { CURB_IMMED_GETTER(ruby_curl_easy, proxy_auth_types, 0); } /* * call-seq: * easy.max_redirects = fixnum or nil => fixnum or nil * * Set the maximum number of redirections to follow in the following +perform+ * calls. Set to nil or -1 allow an infinite number (the default). Setting this * option only makes sense if +follow_location+ is also set true. * * With libcurl >= 7.15.1, setting this to 0 will cause libcurl to refuse any * redirect. */ static VALUE ruby_curl_easy_max_redirects_set(VALUE self, VALUE max_redirs) { CURB_IMMED_SETTER(ruby_curl_easy, max_redirs, -1); } /* * call-seq: * easy.max_redirects => fixnum or nil * * Obtain the maximum number of redirections to follow in the following * +perform+ calls. */ static VALUE ruby_curl_easy_max_redirects_get(VALUE self) { CURB_IMMED_GETTER(ruby_curl_easy, max_redirs, -1); } /* * call-seq: * easy.timeout = float, fixnum or nil => numeric * * Set the maximum time in seconds that you allow the libcurl transfer * operation to take. Normally, name lookups can take a considerable time * and limiting operations to less than a few minutes risk aborting * perfectly normal operations. * * Set to nil (or zero) to disable timeout (it will then only timeout * on the system's internal timeouts). * * Uses timeout_ms internally instead of timeout because it allows for * better precision and libcurl will use the last set value when both * timeout and timeout_ms are set. * */ static VALUE ruby_curl_easy_timeout_set(VALUE self, VALUE timeout_s) { ruby_curl_easy *rbce; Data_Get_Struct(self, ruby_curl_easy, rbce); if (Qnil == timeout_s || NUM2DBL(timeout_s) <= 0.0) { rbce->timeout_ms = 0; } else { rbce->timeout_ms = (unsigned long)(NUM2DBL(timeout_s) * 1000); } return DBL2NUM(rbce->timeout_ms / 1000.0); } /* * call-seq: * easy.timeout => numeric * * Obtain the maximum time in seconds that you allow the libcurl transfer * operation to take. * * Uses timeout_ms internally instead of timeout. * */ static VALUE ruby_curl_easy_timeout_get(VALUE self) { ruby_curl_easy *rbce; Data_Get_Struct(self, ruby_curl_easy, rbce); return DBL2NUM(rbce->timeout_ms / 1000.0); } /* * call-seq: * easy.timeout_ms = fixnum or nil => fixnum or nil * * Set the maximum time in milliseconds that you allow the libcurl transfer * operation to take. Normally, name lookups can take a considerable time * and limiting operations to less than a few minutes risk aborting * perfectly normal operations. * * Set to nil (or zero) to disable timeout (it will then only timeout * on the system's internal timeouts). */ static VALUE ruby_curl_easy_timeout_ms_set(VALUE self, VALUE timeout_ms) { ruby_curl_easy *rbce; Data_Get_Struct(self, ruby_curl_easy, rbce); if (Qnil == timeout_ms || NUM2DBL(timeout_ms) <= 0.0) { rbce->timeout_ms = 0; } else { rbce->timeout_ms = NUM2ULONG(timeout_ms); } return ULONG2NUM(rbce->timeout_ms); } /* * call-seq: * easy.timeout_ms => fixnum or nil * * Obtain the maximum time in milliseconds that you allow the libcurl transfer * operation to take. */ static VALUE ruby_curl_easy_timeout_ms_get(VALUE self) { ruby_curl_easy *rbce; Data_Get_Struct(self, ruby_curl_easy, rbce); return LONG2NUM(rbce->timeout_ms); } /* * call-seq: * easy.connect_timeout = fixnum or nil => fixnum or nil * * Set the maximum time in seconds that you allow the connection to the * server to take. This only limits the connection phase, once it has * connected, this option is of no more use. * * Set to nil (or zero) to disable connection timeout (it will then only * timeout on the system's internal timeouts). */ static VALUE ruby_curl_easy_connect_timeout_set(VALUE self, VALUE connect_timeout) { CURB_IMMED_SETTER(ruby_curl_easy, connect_timeout, 0); } /* * call-seq: * easy.connect_timeout => fixnum or nil * * Obtain the maximum time in seconds that you allow the connection to the * server to take. */ static VALUE ruby_curl_easy_connect_timeout_get(VALUE self) { CURB_IMMED_GETTER(ruby_curl_easy, connect_timeout, 0); } /* * call-seq: * easy.connect_timeout_ms = fixnum or nil => fixnum or nil * * Set the maximum time in milliseconds that you allow the connection to the * server to take. This only limits the connection phase, once it has * connected, this option is of no more use. * * Set to nil (or zero) to disable connection timeout (it will then only * timeout on the system's internal timeouts). */ static VALUE ruby_curl_easy_connect_timeout_ms_set(VALUE self, VALUE connect_timeout_ms) { CURB_IMMED_SETTER(ruby_curl_easy, connect_timeout_ms, 0); } /* * call-seq: * easy.connect_timeout_ms => fixnum or nil * * Obtain the maximum time in milliseconds that you allow the connection to the * server to take. */ static VALUE ruby_curl_easy_connect_timeout_ms_get(VALUE self) { CURB_IMMED_GETTER(ruby_curl_easy, connect_timeout_ms, 0); } /* * call-seq: * easy.dns_cache_timeout = fixnum or nil => fixnum or nil * * Set the dns cache timeout in seconds. Name resolves will be kept in * memory for this number of seconds. Set to zero (0) to completely disable * caching, or set to nil (or -1) to make the cached entries remain forever. * By default, libcurl caches this info for 60 seconds. */ static VALUE ruby_curl_easy_dns_cache_timeout_set(VALUE self, VALUE dns_cache_timeout) { CURB_IMMED_SETTER(ruby_curl_easy, dns_cache_timeout, -1); } /* * call-seq: * easy.dns_cache_timeout => fixnum or nil * * Obtain the dns cache timeout in seconds. */ static VALUE ruby_curl_easy_dns_cache_timeout_get(VALUE self) { CURB_IMMED_GETTER(ruby_curl_easy, dns_cache_timeout, -1); } /* * call-seq: * easy.ftp_response_timeout = fixnum or nil => fixnum or nil * * Set a timeout period (in seconds) on the amount of time that the server * is allowed to take in order to generate a response message for a command * before the session is considered hung. While curl is waiting for a * response, this value overrides +timeout+. It is recommended that if used * in conjunction with +timeout+, you set +ftp_response_timeout+ to a value * smaller than +timeout+. * * Ignored if libcurl version is < 7.10.8. */ static VALUE ruby_curl_easy_ftp_response_timeout_set(VALUE self, VALUE ftp_response_timeout) { CURB_IMMED_SETTER(ruby_curl_easy, ftp_response_timeout, 0); } /* * call-seq: * easy.ftp_response_timeout => fixnum or nil * * Obtain the maximum time that libcurl will wait for FTP command responses. */ static VALUE ruby_curl_easy_ftp_response_timeout_get(VALUE self) { CURB_IMMED_GETTER(ruby_curl_easy, ftp_response_timeout, 0); } /* * call-seq: * easy.low_speed_limit = fixnum or nil => fixnum or nil * * Set the transfer speed (in bytes per second) that the transfer should be * below during +low_speed_time+ seconds for the library to consider it too * slow and abort. */ static VALUE ruby_curl_easy_low_speed_limit_set(VALUE self, VALUE low_speed_limit) { CURB_IMMED_SETTER(ruby_curl_easy, low_speed_limit, 0); } /* * call-seq: * easy.low_speed_limit => fixnum or nil * * Obtain the minimum transfer speed over +low_speed+time+ below which the * transfer will be aborted. */ static VALUE ruby_curl_easy_low_speed_limit_get(VALUE self) { CURB_IMMED_GETTER(ruby_curl_easy, low_speed_limit, 0); } /* * call-seq: * easy.low_speed_time = fixnum or nil => fixnum or nil * * Set the time (in seconds) that the transfer should be below the * +low_speed_limit+ for the library to consider it too slow and abort. */ static VALUE ruby_curl_easy_low_speed_time_set(VALUE self, VALUE low_speed_time) { CURB_IMMED_SETTER(ruby_curl_easy, low_speed_time, 0); } /* * call-seq: * easy.low_speed_time => fixnum or nil * * Obtain the time that the transfer should be below +low_speed_limit+ for * the library to abort it. */ static VALUE ruby_curl_easy_low_speed_time_get(VALUE self) { CURB_IMMED_GETTER(ruby_curl_easy, low_speed_time, 0); } /* * call-seq: * easy.max_send_speed_large = fixnum or nil => fixnum or nil * * Set the maximal sending transfer speed (in bytes per second) */ static VALUE ruby_curl_easy_max_send_speed_large_set(VALUE self, VALUE max_send_speed_large) { CURB_IMMED_SETTER(ruby_curl_easy, max_send_speed_large, 0); } /* * call-seq: * easy.max_send_speed_large = fixnum or nil => fixnum or nil * * Get the maximal sending transfer speed (in bytes per second) */ static VALUE ruby_curl_easy_max_send_speed_large_get(VALUE self) { CURB_IMMED_GETTER(ruby_curl_easy, max_send_speed_large, 0); } /* * call-seq: * easy.max_recv_speed_large = fixnum or nil => fixnum or nil * * Set the maximal receiving transfer speed (in bytes per second) */ static VALUE ruby_curl_easy_max_recv_speed_large_set(VALUE self, VALUE max_recv_speed_large) { CURB_IMMED_SETTER(ruby_curl_easy, max_recv_speed_large, 0); } /* * call-seq: * easy.max_recv_speed_large = fixnum or nil => fixnum or nil * * Get the maximal receiving transfer speed (in bytes per second) */ static VALUE ruby_curl_easy_max_recv_speed_large_get(VALUE self) { CURB_IMMED_GETTER(ruby_curl_easy, max_recv_speed_large, 0); } /* * call-seq: * easy.username = string => string * * Set the HTTP Authentication username. */ static VALUE ruby_curl_easy_username_set(VALUE self, VALUE username) { #if HAVE_CURLOPT_USERNAME CURB_OBJECT_HSETTER(ruby_curl_easy, username); #else return Qnil; #endif } /* * call-seq: * easy.username => string * * Get the current username */ static VALUE ruby_curl_easy_username_get(VALUE self) { #if HAVE_CURLOPT_USERNAME CURB_OBJECT_HGETTER(ruby_curl_easy, username); #else return Qnil; #endif } /* * call-seq: * easy.password = string => string * * Set the HTTP Authentication password. */ static VALUE ruby_curl_easy_password_set(VALUE self, VALUE password) { #if HAVE_CURLOPT_PASSWORD CURB_OBJECT_HSETTER(ruby_curl_easy, password); #else return Qnil; #endif } /* * call-seq: * easy.password => string * * Get the current password */ static VALUE ruby_curl_easy_password_get(VALUE self) { #if HAVE_CURLOPT_PASSWORD CURB_OBJECT_HGETTER(ruby_curl_easy, password); #else return Qnil; #endif } /* * call-seq: * easy.ssl_version = value => fixnum or nil * * Sets the version of SSL/TLS that libcurl will attempt to use. Valid * options are: * * Curl::CURL_SSLVERSION_DEFAULT * Curl::CURL_SSLVERSION_TLSv1 (TLS 1.x) * Curl::CURL_SSLVERSION_SSLv2 * Curl::CURL_SSLVERSION_SSLv3 * Curl::CURL_SSLVERSION_TLSv1_0 * Curl::CURL_SSLVERSION_TLSv1_1 * Curl::CURL_SSLVERSION_TLSv1_2 * Curl::CURL_SSLVERSION_TLSv1_3 */ static VALUE ruby_curl_easy_ssl_version_set(VALUE self, VALUE ssl_version) { CURB_IMMED_SETTER(ruby_curl_easy, ssl_version, -1); } /* * call-seq: * easy.ssl_version => fixnum * * Get the version of SSL/TLS that libcurl will attempt to use. */ static VALUE ruby_curl_easy_ssl_version_get(VALUE self) { CURB_IMMED_GETTER(ruby_curl_easy, ssl_version, -1); } /* * call-seq: * easy.use_ssl = value => fixnum or nil * * Ensure libcurl uses SSL for FTP connections. Valid options are Curl::CURL_USESSL_NONE, * Curl::CURL_USESSL_TRY, Curl::CURL_USESSL_CONTROL, and Curl::CURL_USESSL_ALL. */ static VALUE ruby_curl_easy_use_ssl_set(VALUE self, VALUE use_ssl) { CURB_IMMED_SETTER(ruby_curl_easy, use_ssl, -1); } /* * call-seq: * easy.use_ssl => fixnum * * Get the desired level for using SSL on FTP connections. */ static VALUE ruby_curl_easy_use_ssl_get(VALUE self) { CURB_IMMED_GETTER(ruby_curl_easy, use_ssl, -1); } /* * call-seq: * easy.ftp_filemethod = value => fixnum or nil * * Controls how libcurl reaches files on the server. Valid options are Curl::CURL_MULTICWD, * Curl::CURL_NOCWD, and Curl::CURL_SINGLECWD (see libcurl docs for CURLOPT_FTP_METHOD). */ static VALUE ruby_curl_easy_ftp_filemethod_set(VALUE self, VALUE ftp_filemethod) { CURB_IMMED_SETTER(ruby_curl_easy, ftp_filemethod, -1); } /* * call-seq: * easy.ftp_filemethod => fixnum * * Get the configuration for how libcurl will reach files on the server. */ static VALUE ruby_curl_easy_ftp_filemethod_get(VALUE self) { CURB_IMMED_GETTER(ruby_curl_easy, ftp_filemethod, -1); } /* ================== BOOL ATTRS ===================*/ /* * call-seq: * easy.proxy_tunnel = boolean => boolean * * Configure whether this Curl instance will use proxy tunneling. */ static VALUE ruby_curl_easy_proxy_tunnel_set(VALUE self, VALUE proxy_tunnel) { CURB_BOOLEAN_SETTER(ruby_curl_easy, proxy_tunnel); } /* * call-seq: * easy.proxy_tunnel? => boolean * * Determine whether this Curl instance will use proxy tunneling. */ static VALUE ruby_curl_easy_proxy_tunnel_q(VALUE self) { CURB_BOOLEAN_GETTER(ruby_curl_easy, proxy_tunnel); } /* * call-seq: * easy.fetch_file_time = boolean => boolean * * Configure whether this Curl instance will fetch remote file * times, if available. */ static VALUE ruby_curl_easy_fetch_file_time_set(VALUE self, VALUE fetch_file_time) { CURB_BOOLEAN_SETTER(ruby_curl_easy, fetch_file_time); } /* * call-seq: * easy.fetch_file_time? => boolean * * Determine whether this Curl instance will fetch remote file * times, if available. */ static VALUE ruby_curl_easy_fetch_file_time_q(VALUE self) { CURB_BOOLEAN_GETTER(ruby_curl_easy, fetch_file_time); } /* * call-seq: * easy.ssl_verify_peer = boolean => boolean * * Configure whether this Curl instance will verify the SSL peer * certificate. When true (the default), and the verification fails to * prove that the certificate is authentic, the connection fails. When * false, the connection succeeds regardless. * * Authenticating the certificate is not by itself very useful. You * typically want to ensure that the server, as authentically identified * by its certificate, is the server you mean to be talking to. * The ssl_verify_host? options controls that. */ static VALUE ruby_curl_easy_ssl_verify_peer_set(VALUE self, VALUE ssl_verify_peer) { CURB_BOOLEAN_SETTER(ruby_curl_easy, ssl_verify_peer); } /* * call-seq: * easy.ssl_verify_peer? => boolean * * Determine whether this Curl instance will verify the SSL peer * certificate. */ static VALUE ruby_curl_easy_ssl_verify_peer_q(VALUE self) { CURB_BOOLEAN_GETTER(ruby_curl_easy, ssl_verify_peer); } /* * call-seq: * easy.ssl_verify_host = [0, 1, 2] => [0, 1, 2] * * Configure whether this Curl instance will verify that the server cert * is for the server it is known as. When true (the default) the server * certificate must indicate that the server is the server to which you * meant to connect, or the connection fails. When false, the connection * will succeed regardless of the names in the certificate. * * this option controls is of the identity that the server claims. * The server could be lying. To control lying, see ssl_verify_peer? . */ static VALUE ruby_curl_easy_ssl_verify_host_set(VALUE self, VALUE ssl_verify_host) { CURB_IMMED_SETTER(ruby_curl_easy, ssl_verify_host, 0); } /* * call-seq: * easy.ssl_verify_host => number * * Determine whether this Curl instance will verify that the server cert * is for the server it is known as. */ static VALUE ruby_curl_easy_ssl_verify_host_get(VALUE self) { CURB_IMMED_GETTER(ruby_curl_easy, ssl_verify_host, 0); } /* * call-seq: * easy.header_in_body = boolean => boolean * * Configure whether this Curl instance will return HTTP headers * combined with body data. If this option is set true, both header * and body data will go to +body_str+ (or the configured +on_body+ handler). */ static VALUE ruby_curl_easy_header_in_body_set(VALUE self, VALUE header_in_body) { CURB_BOOLEAN_SETTER(ruby_curl_easy, header_in_body); } /* * call-seq: * easy.header_in_body? => boolean * * Determine whether this Curl instance will return HTTP headers * combined with body data. */ static VALUE ruby_curl_easy_header_in_body_q(VALUE self) { CURB_BOOLEAN_GETTER(ruby_curl_easy, header_in_body); } /* * call-seq: * easy.use_netrc = boolean => boolean * * Configure whether this Curl instance will use data from the user's * .netrc file for FTP connections. */ static VALUE ruby_curl_easy_use_netrc_set(VALUE self, VALUE use_netrc) { CURB_BOOLEAN_SETTER(ruby_curl_easy, use_netrc); } /* * call-seq: * easy.use_netrc? => boolean * * Determine whether this Curl instance will use data from the user's * .netrc file for FTP connections. */ static VALUE ruby_curl_easy_use_netrc_q(VALUE self) { CURB_BOOLEAN_GETTER(ruby_curl_easy, use_netrc); } /* * call-seq: * * easy = Curl::Easy.new * easy.autoreferer=true */ static VALUE ruby_curl_easy_autoreferer_set(VALUE self, VALUE autoreferer) { ruby_curl_easy *rbce; Data_Get_Struct(self, ruby_curl_easy, rbce); if (Qtrue == autoreferer) { curl_easy_setopt(rbce->curl, CURLOPT_AUTOREFERER, 1); } else { curl_easy_setopt(rbce->curl, CURLOPT_AUTOREFERER, 0); } return autoreferer; } /* * call-seq: * easy.follow_location? => boolean * * Determine whether this Curl instance will follow Location: headers * in HTTP responses. */ static VALUE ruby_curl_easy_follow_location_q(VALUE self) { CURB_BOOLEAN_GETTER(ruby_curl_easy, follow_location); } /* * call-seq: * easy.unrestricted_auth = boolean => boolean * * Configure whether this Curl instance may use any HTTP authentication * method available when necessary. */ static VALUE ruby_curl_easy_unrestricted_auth_set(VALUE self, VALUE unrestricted_auth) { CURB_BOOLEAN_SETTER(ruby_curl_easy, unrestricted_auth); } /* * call-seq: * easy.unrestricted_auth? => boolean * * Determine whether this Curl instance may use any HTTP authentication * method available when necessary. */ static VALUE ruby_curl_easy_unrestricted_auth_q(VALUE self) { CURB_BOOLEAN_GETTER(ruby_curl_easy, unrestricted_auth); } /* * call-seq: * easy.verbose = boolean => boolean * * Configure whether this Curl instance gives verbose output to STDERR * during transfers. Ignored if this instance has an on_debug handler. */ static VALUE ruby_curl_easy_verbose_set(VALUE self, VALUE verbose) { CURB_BOOLEAN_SETTER(ruby_curl_easy, verbose); } /* * call-seq: * easy.verbose? => boolean * * Determine whether this Curl instance gives verbose output to STDERR * during transfers. */ static VALUE ruby_curl_easy_verbose_q(VALUE self) { CURB_BOOLEAN_GETTER(ruby_curl_easy, verbose); } /* * call-seq: * easy.multipart_form_post = boolean => boolean * * Configure whether this Curl instance uses multipart/formdata content * type for HTTP POST requests. If this is false (the default), then the * application/x-www-form-urlencoded content type is used for the form * data. * * If this is set true, you must pass one or more PostField instances * to the http_post method - no support for posting multipart forms from * a string is provided. */ static VALUE ruby_curl_easy_multipart_form_post_set(VALUE self, VALUE multipart_form_post) { CURB_BOOLEAN_SETTER(ruby_curl_easy, multipart_form_post); } /* * call-seq: * easy.multipart_form_post? => boolean * * Determine whether this Curl instance uses multipart/formdata content * type for HTTP POST requests. */ static VALUE ruby_curl_easy_multipart_form_post_q(VALUE self) { CURB_BOOLEAN_GETTER(ruby_curl_easy, multipart_form_post); } /* * call-seq: * easy.enable_cookies = boolean => boolean * * Configure whether the libcurl cookie engine is enabled for this Curl::Easy * instance. */ static VALUE ruby_curl_easy_enable_cookies_set(VALUE self, VALUE enable_cookies) { CURB_BOOLEAN_SETTER(ruby_curl_easy, enable_cookies); } /* * call-seq: * easy.enable_cookies? => boolean * * Determine whether the libcurl cookie engine is enabled for this * Curl::Easy instance. */ static VALUE ruby_curl_easy_enable_cookies_q(VALUE self) { CURB_BOOLEAN_GETTER(ruby_curl_easy, enable_cookies); } /* * call-seq: * easy.ignore_content_length = boolean * * Configure whether this Curl::Easy instance should ignore the content * length header. */ static VALUE ruby_curl_easy_ignore_content_length_set(VALUE self, VALUE ignore_content_length) { CURB_BOOLEAN_SETTER(ruby_curl_easy, ignore_content_length); } /* * call-seq: * easy.ignore_content_length? => boolean * * Determine whether this Curl::Easy instance ignores the content * length header. */ static VALUE ruby_curl_easy_ignore_content_length_q(VALUE self) { CURB_BOOLEAN_GETTER(ruby_curl_easy, ignore_content_length); } /* * call-seq: * easy.resolve_mode => symbol * * Determines what type of IP address this Curl::Easy instance * resolves DNS names to. */ static VALUE ruby_curl_easy_resolve_mode(VALUE self) { ruby_curl_easy *rbce; unsigned short rm; Data_Get_Struct(self, ruby_curl_easy, rbce); rm = rbce->resolve_mode; switch(rm) { case CURL_IPRESOLVE_V4: return rb_easy_sym("ipv4"); case CURL_IPRESOLVE_V6: return rb_easy_sym("ipv6"); default: return rb_easy_sym("auto"); } } /* * call-seq: * easy.resolve_mode = symbol => symbol * * Configures what type of IP address this Curl::Easy instance * resolves DNS names to. Valid options are: * * [:auto] resolves DNS names to all IP versions your system allows * [:ipv4] resolves DNS names to IPv4 only * [:ipv6] resolves DNS names to IPv6 only */ static VALUE ruby_curl_easy_resolve_mode_set(VALUE self, VALUE resolve_mode) { if (TYPE(resolve_mode) != T_SYMBOL) { rb_raise(rb_eTypeError, "Must pass a symbol"); return Qnil; } else { ruby_curl_easy *rbce; ID resolve_mode_id; Data_Get_Struct(self, ruby_curl_easy, rbce); resolve_mode_id = rb_to_id(resolve_mode); if (resolve_mode_id == rb_intern("auto")) { rbce->resolve_mode = CURL_IPRESOLVE_WHATEVER; return resolve_mode; } else if (resolve_mode_id == rb_intern("ipv4")) { rbce->resolve_mode = CURL_IPRESOLVE_V4; return resolve_mode; } else if (resolve_mode_id == rb_intern("ipv6")) { rbce->resolve_mode = CURL_IPRESOLVE_V6; return resolve_mode; } else { rb_raise(rb_eArgError, "Must set to one of :auto, :ipv4, :ipv6"); return Qnil; } } } /* ================= EVENT PROCS ================== */ /* * call-seq: * easy.on_body { |body_data| ... } => * * Assign or remove the +on_body+ handler for this Curl::Easy instance. * To remove a previously-supplied handler, call this method with no * attached block. * * The +on_body+ handler is called for each chunk of response body passed back * by libcurl during +perform+. It should perform any processing necessary, * and return the actual number of bytes handled. Normally, this will * equal the length of the data string, and CURL will continue processing. * If the returned length does not equal the input length, CURL will abort * the processing with a Curl::Err::AbortedByCallbackError. */ static VALUE ruby_curl_easy_on_body_set(int argc, VALUE *argv, VALUE self) { CURB_HANDLER_PROC_HSETTER(ruby_curl_easy, body_proc); } /* * call-seq: * easy.on_success { |easy| ... } => * * Assign or remove the +on_success+ handler for this Curl::Easy instance. * To remove a previously-supplied handler, call this method with no * attached block. * * The +on_success+ handler is called when the request is finished with a * status of 20x */ static VALUE ruby_curl_easy_on_success_set(int argc, VALUE *argv, VALUE self) { CURB_HANDLER_PROC_HSETTER(ruby_curl_easy, success_proc); } /* * call-seq: * easy.on_failure {|easy,code| ... } => * * Assign or remove the +on_failure+ handler for this Curl::Easy instance. * To remove a previously-supplied handler, call this method with no * attached block. * * The +on_failure+ handler is called when the request is finished with a * status of 50x */ static VALUE ruby_curl_easy_on_failure_set(int argc, VALUE *argv, VALUE self) { CURB_HANDLER_PROC_HSETTER(ruby_curl_easy, failure_proc); } /* * call-seq: * easy.on_missing {|easy,code| ... } => * * Assign or remove the on_missing handler for this Curl::Easy instance. * To remove a previously-supplied handler, call this method with no attached * block. * * The +on_missing+ handler is called when request is finished with a * status of 40x */ static VALUE ruby_curl_easy_on_missing_set(int argc, VALUE *argv, VALUE self) { CURB_HANDLER_PROC_HSETTER(ruby_curl_easy, missing_proc); } /* * call-seq: * easy.on_redirect {|easy,code| ... } => * * Assign or remove the on_redirect handler for this Curl::Easy instance. * To remove a previously-supplied handler, call this method with no attached * block. * * The +on_redirect+ handler is called when request is finished with a * status of 30x */ static VALUE ruby_curl_easy_on_redirect_set(int argc, VALUE *argv, VALUE self) { CURB_HANDLER_PROC_HSETTER(ruby_curl_easy, redirect_proc); } /* * call-seq: * easy.on_complete {|easy| ... } => * * Assign or remove the +on_complete+ handler for this Curl::Easy instance. * To remove a previously-supplied handler, call this method with no * attached block. * * The +on_complete+ handler is called when the request is finished. */ static VALUE ruby_curl_easy_on_complete_set(int argc, VALUE *argv, VALUE self) { CURB_HANDLER_PROC_HSETTER(ruby_curl_easy, complete_proc); } /* * call-seq: * easy.on_header { |header_data| ... } => * * Assign or remove the +on_header+ handler for this Curl::Easy instance. * To remove a previously-supplied handler, call this method with no * attached block. * * The +on_header+ handler is called for each chunk of response header passed * back by libcurl during +perform+. The semantics are the same as for the * block supplied to +on_body+. */ static VALUE ruby_curl_easy_on_header_set(int argc, VALUE *argv, VALUE self) { CURB_HANDLER_PROC_HSETTER(ruby_curl_easy, header_proc); } /* * call-seq: * easy.on_progress { |dl_total, dl_now, ul_total, ul_now| ... } => * * Assign or remove the +on_progress+ handler for this Curl::Easy instance. * To remove a previously-supplied handler, call this method with no * attached block. * * The +on_progress+ handler is called regularly by libcurl (approximately once * per second) during transfers to allow the application to receive progress * information. There is no guarantee that the reported progress will change * between calls. * * The result of the block call determines whether libcurl continues the transfer. * Returning a non-true value (i.e. nil or false) will cause the transfer to abort, * throwing a Curl::Err::AbortedByCallbackError. */ static VALUE ruby_curl_easy_on_progress_set(int argc, VALUE *argv, VALUE self) { CURB_HANDLER_PROC_HSETTER(ruby_curl_easy, progress_proc); } /* * call-seq: * easy.on_debug { |type, data| ... } => * * Assign or remove the +on_debug+ handler for this Curl::Easy instance. * To remove a previously-supplied handler, call this method with no * attached block. * * The +on_debug+ handler, if configured, will receive detailed information * from libcurl during the perform call. This can be useful for debugging. * Setting a debug handler overrides libcurl's internal handler, disabling * any output from +verbose+, if set. * * The type argument will match one of the Curl::Easy::CURLINFO_XXXX * constants, and specifies the kind of information contained in the * data. The data is passed as a String. */ static VALUE ruby_curl_easy_on_debug_set(int argc, VALUE *argv, VALUE self) { CURB_HANDLER_PROC_HSETTER(ruby_curl_easy, debug_proc); } /* =================== PERFORM =====================*/ /*********************************************** * This is an rb_iterate callback used to set up http headers. */ static VALUE cb_each_http_header(VALUE header, VALUE wrap, int _c, const VALUE *_ptr, VALUE unused) { struct curl_slist **list; VALUE header_str = Qnil; Data_Get_Struct(wrap, struct curl_slist *, list); //rb_p(header); if (rb_type(header) == T_ARRAY) { // we're processing a hash, header is [name, val] VALUE name, value; name = rb_obj_as_string(rb_ary_entry(header, 0)); value = rb_obj_as_string(rb_ary_entry(header, 1)); if (rb_str_strlen(value) == 0) { // removing the header e.g. Accept: with nothing trailing should remove it see: https://curl.se/libcurl/c/CURLOPT_HTTPHEADER.html header_str = rb_str_plus(name, rb_str_new2(":")); } else { // This is a bit inefficient, but we don't want to be modifying // the actual values in the original hash. header_str = rb_str_plus(name, rb_str_new2(": ")); header_str = rb_str_plus(header_str, value); } } else { header_str = rb_obj_as_string(header); } //rb_p(header_str); *list = curl_slist_append(*list, StringValuePtr(header_str)); return header_str; } /*********************************************** * This is an rb_iterate callback used to set up http proxy headers. */ static VALUE cb_each_http_proxy_header(VALUE proxy_header, VALUE wrap, int _c, const VALUE *_ptr, VALUE unused) { struct curl_slist **list; VALUE proxy_header_str = Qnil; Data_Get_Struct(wrap, struct curl_slist *, list); //rb_p(proxy_header); if (rb_type(proxy_header) == T_ARRAY) { // we're processing a hash, proxy header is [name, val] VALUE name, value; name = rb_obj_as_string(rb_ary_entry(proxy_header, 0)); value = rb_obj_as_string(rb_ary_entry(proxy_header, 1)); // This is a bit inefficient, but we don't want to be modifying // the actual values in the original hash. proxy_header_str = rb_str_plus(name, rb_str_new2(": ")); proxy_header_str = rb_str_plus(proxy_header_str, value); } else { proxy_header_str = rb_obj_as_string(proxy_header); } //rb_p(header_str); *list = curl_slist_append(*list, StringValuePtr(proxy_header_str)); return proxy_header_str; } /*********************************************** * This is an rb_iterate callback used to set up ftp commands. */ static VALUE cb_each_ftp_command(VALUE ftp_command, VALUE wrap, int _c, const VALUE *_ptr, VALUE unused) { struct curl_slist **list; VALUE ftp_command_string; Data_Get_Struct(wrap, struct curl_slist *, list); ftp_command_string = rb_obj_as_string(ftp_command); *list = curl_slist_append(*list, StringValuePtr(ftp_command)); return ftp_command_string; } /*********************************************** * This is an rb_iterate callback used to set up the resolve list. */ static VALUE cb_each_resolve(VALUE resolve, VALUE wrap, int _c, const VALUE *_ptr, VALUE unused) { struct curl_slist **list; VALUE resolve_string; Data_Get_Struct(wrap, struct curl_slist *, list); resolve_string = rb_obj_as_string(resolve); *list = curl_slist_append(*list, StringValuePtr(resolve)); return resolve_string; } /*********************************************** * * Setup a connection * * Always returns Qtrue, rb_raise on error. */ VALUE ruby_curl_easy_setup(ruby_curl_easy *rbce) { // TODO this could do with a bit of refactoring... CURL *curl; VALUE url, _url = rb_easy_get("url"); struct curl_slist **hdrs = &(rbce->curl_headers); struct curl_slist **phdrs = &(rbce->curl_proxy_headers); struct curl_slist **cmds = &(rbce->curl_ftp_commands); struct curl_slist **rslv = &(rbce->curl_resolve); curl = rbce->curl; if (_url == Qnil) { rb_raise(eCurlErrError, "No URL supplied"); } url = rb_check_string_type(_url); curl_easy_setopt(curl, CURLOPT_URL, StringValuePtr(url)); // network stuff and auth if (!rb_easy_nil("interface_hm")) { curl_easy_setopt(curl, CURLOPT_INTERFACE, rb_easy_get_str("interface_hm")); } else { curl_easy_setopt(curl, CURLOPT_INTERFACE, NULL); } #if HAVE_CURLOPT_USERNAME == 1 && HAVE_CURLOPT_PASSWORD == 1 if (!rb_easy_nil("username")) { curl_easy_setopt(curl, CURLOPT_USERNAME, rb_easy_get_str("username")); } else { curl_easy_setopt(curl, CURLOPT_USERNAME, NULL); } if (!rb_easy_nil("password")) { curl_easy_setopt(curl, CURLOPT_PASSWORD, rb_easy_get_str("password")); } else { curl_easy_setopt(curl, CURLOPT_PASSWORD, NULL); } #endif if (!rb_easy_nil("userpwd")) { curl_easy_setopt(curl, CURLOPT_USERPWD, rb_easy_get_str("userpwd")); #if HAVE_CURLOPT_USERNAME == 1 } else if (rb_easy_nil("username") && rb_easy_nil("password")) { /* don't set this even to NULL if we have set username and password */ #else } else { #endif curl_easy_setopt(curl, CURLOPT_USERPWD, NULL); } if (rb_easy_nil("proxy_url")) { curl_easy_setopt(curl, CURLOPT_PROXY, NULL); } else { curl_easy_setopt(curl, CURLOPT_PROXY, rb_easy_get_str("proxy_url")); } if (rb_easy_nil("proxypwd")) { curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD, NULL); } else { curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD, rb_easy_get_str("proxypwd")); } // body/header procs if (!rb_easy_nil("body_proc")) { curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, (curl_write_callback)&proc_data_handler_body); curl_easy_setopt(curl, CURLOPT_WRITEDATA, rbce); /* clear out the body_data if it was set */ rb_easy_del("body_data"); } else { VALUE body_buffer = rb_easy_set("body_data", rb_str_buf_new(32768)); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, (curl_write_callback)&default_data_handler); curl_easy_setopt(curl, CURLOPT_WRITEDATA, body_buffer); } if (!rb_easy_nil("header_proc")) { curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, (curl_write_callback)&proc_data_handler_header); curl_easy_setopt(curl, CURLOPT_HEADERDATA, rbce); /* clear out the header_data if it was set */ rb_easy_del("header_data"); } else { VALUE header_buffer = rb_easy_set("header_data", rb_str_buf_new(16384)); curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, (curl_write_callback)&default_data_handler); curl_easy_setopt(curl, CURLOPT_HEADERDATA, header_buffer); } /* encoding */ if (!rb_easy_nil("encoding")) { curl_easy_setopt(curl, CURLOPT_ENCODING, rb_easy_get_str("encoding")); } // progress and debug procs if (!rb_easy_nil("progress_proc")) { curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, (curl_progress_callback)&proc_progress_handler); curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, rb_easy_get("progress_proc")); curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0); } else { curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1); } if (!rb_easy_nil("debug_proc")) { curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, (curl_debug_callback)&proc_debug_handler); curl_easy_setopt(curl, CURLOPT_DEBUGDATA, rb_easy_get("debug_proc")); curl_easy_setopt(curl, CURLOPT_VERBOSE, 1); } else { // have to remove handler to re-enable standard verbosity curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, NULL); curl_easy_setopt(curl, CURLOPT_DEBUGDATA, NULL); curl_easy_setopt(curl, CURLOPT_VERBOSE, rbce->verbose); } /* general opts */ curl_easy_setopt(curl, CURLOPT_HEADER, rbce->header_in_body); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, rbce->follow_location); curl_easy_setopt(curl, CURLOPT_MAXREDIRS, rbce->max_redirs); curl_easy_setopt(curl, CURLOPT_HTTPPROXYTUNNEL, rbce->proxy_tunnel); curl_easy_setopt(curl, CURLOPT_FILETIME, rbce->fetch_file_time); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, rbce->ssl_verify_peer); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, rbce->ssl_verify_host); if ((rbce->use_netrc != Qnil) && (rbce->use_netrc != Qfalse)) { curl_easy_setopt(curl, CURLOPT_NETRC, CURL_NETRC_OPTIONAL); } else { curl_easy_setopt(curl, CURLOPT_NETRC, CURL_NETRC_IGNORED); } curl_easy_setopt(curl, CURLOPT_UNRESTRICTED_AUTH, rbce->unrestricted_auth); #if HAVE_CURLOPT_TIMEOUT_MS curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, rbce->timeout_ms); #endif curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, rbce->connect_timeout); #if HAVE_CURLOPT_CONNECTTIMEOUT_MS curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT_MS, rbce->connect_timeout_ms); #endif curl_easy_setopt(curl, CURLOPT_DNS_CACHE_TIMEOUT, rbce->dns_cache_timeout); curl_easy_setopt(curl, CURLOPT_IGNORE_CONTENT_LENGTH, rbce->ignore_content_length); curl_easy_setopt(curl, CURLOPT_IPRESOLVE, rbce->resolve_mode); #if LIBCURL_VERSION_NUM >= 0x070a08 curl_easy_setopt(curl, CURLOPT_FTP_RESPONSE_TIMEOUT, rbce->ftp_response_timeout); #else if (rbce->ftp_response_timeout > 0) { rb_warn("Installed libcurl is too old to support ftp_response_timeout"); } #endif curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, rbce->low_speed_limit); curl_easy_setopt(curl, CURLOPT_LOW_SPEED_TIME, rbce->low_speed_time); curl_easy_setopt(curl, CURLOPT_MAX_RECV_SPEED_LARGE, rbce->max_recv_speed_large); curl_easy_setopt(curl, CURLOPT_MAX_SEND_SPEED_LARGE, rbce->max_send_speed_large); // Set up localport / proxy port // FIXME these won't get returned to default if they're unset Ruby if (rbce->proxy_port > 0) { curl_easy_setopt(curl, CURLOPT_PROXYPORT, rbce->proxy_port); } if (rbce->local_port > 0) { #if LIBCURL_VERSION_NUM >= 0x070f02 curl_easy_setopt(curl, CURLOPT_LOCALPORT, rbce->local_port); if (rbce->local_port_range > 0) { curl_easy_setopt(curl, CURLOPT_LOCALPORTRANGE, rbce->local_port_range); } #else rb_warn("Installed libcurl is too old to support local_port"); #endif } if (rbce->proxy_type != -1) { #if LIBCURL_VERSION_NUM >= 0x070a00 if (rbce->proxy_type == -2) { rb_warn("Installed libcurl is too old to support the selected proxy type"); } else { curl_easy_setopt(curl, CURLOPT_PROXYTYPE, rbce->proxy_type); } } else { curl_easy_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_HTTP); #else rb_warn("Installed libcurl is too old to support proxy_type"); #endif } /* * NOTE: we used to set CURLAUTH_ANY but see: http://curl.haxx.se/mail/lib-2015-06/0033.html */ if (rbce->http_auth_types != 0) { #if LIBCURL_VERSION_NUM >= 0x070a06 curl_easy_setopt(curl, CURLOPT_HTTPAUTH, rbce->http_auth_types); #else rb_warn("Installed libcurl is too old to support http_auth_types"); #endif } if (rbce->proxy_auth_types != 0) { #if LIBCURL_VERSION_NUM >= 0x070a07 curl_easy_setopt(curl, CURLOPT_PROXYAUTH, rbce->proxy_auth_types); #else rb_warn("Installed libcurl is too old to support proxy_auth_types"); #endif } /* Set up HTTP cookie handling if necessary FIXME this may not get disabled if it's enabled, the disabled again from ruby. */ if (rbce->enable_cookies) { if (!rb_easy_nil("cookiejar")) { curl_easy_setopt(curl, CURLOPT_COOKIEJAR, rb_easy_get_str("cookiejar")); } if (!rb_easy_nil("cookiefile")) { curl_easy_setopt(curl, CURLOPT_COOKIEFILE, rb_easy_get_str("cookiefile")); } else { curl_easy_setopt(curl, CURLOPT_COOKIEFILE, ""); /* "" = magic to just enable */ } } if (!rb_easy_nil("cookies")) { curl_easy_setopt(curl, CURLOPT_COOKIE, rb_easy_get_str("cookies")); } /* Set up HTTPS cert handling if necessary */ if (!rb_easy_nil("cert")) { if (!rb_easy_nil("certtype")) { curl_easy_setopt(curl, CURLOPT_SSLCERTTYPE, rb_easy_get_str("certtype")); } curl_easy_setopt(curl, CURLOPT_SSLCERT, rb_easy_get_str("cert")); if (!rb_easy_nil("certpassword")) { curl_easy_setopt(curl, CURLOPT_SSLCERTPASSWD, rb_easy_get_str("certpassword")); } if (!rb_easy_nil("cert_key")) { curl_easy_setopt(curl, CURLOPT_SSLKEY, rb_easy_get_str("cert_key")); } } if (!rb_easy_nil("cacert")) { curl_easy_setopt(curl, CURLOPT_CAINFO, rb_easy_get_str("cacert")); } #ifdef HAVE_CURL_CONFIG_CA else { curl_easy_setopt(curl, CURLOPT_CAINFO, CURL_CONFIG_CA); } #endif #ifdef CURL_VERSION_SSL if (rbce->ssl_version > 0) { curl_easy_setopt(curl, CURLOPT_SSLVERSION, rbce->ssl_version); } if (rbce->use_ssl > 0) { curl_easy_setopt(curl, CURB_FTPSSL, rbce->use_ssl); } #else if (rbce->ssl_version > 0 || rbce->use_ssl > 0) { rb_warn("libcurl is not configured with SSL support"); } #endif if (rbce->ftp_filemethod > 0) { curl_easy_setopt(curl, CURLOPT_FTP_FILEMETHOD, rbce->ftp_filemethod); } /* Set the user-agent string if specified */ if (!rb_easy_nil("useragent")) { curl_easy_setopt(curl, CURLOPT_USERAGENT, rb_easy_get_str("useragent")); } /* Setup HTTP headers if necessary */ curl_easy_setopt(curl, CURLOPT_HTTPHEADER, NULL); // XXX: maybe we shouldn't be clearing this? if (!rb_easy_nil("headers")) { if (rb_easy_type_check("headers", T_ARRAY) || rb_easy_type_check("headers", T_HASH)) { VALUE wrap = Data_Wrap_Struct(rb_cObject, 0, 0, hdrs); rb_iterate(rb_each, rb_easy_get("headers"), cb_each_http_header, wrap); } else { VALUE headers_str = rb_obj_as_string(rb_easy_get("headers")); *hdrs = curl_slist_append(*hdrs, StringValuePtr(headers_str)); } if (*hdrs) { curl_easy_setopt(curl, CURLOPT_HTTPHEADER, *hdrs); } } #if HAVE_CURLOPT_PROXYHEADER /* Setup HTTP proxy headers if necessary */ curl_easy_setopt(curl, CURLOPT_PROXYHEADER, NULL); // XXX: maybe we shouldn't be clearing this? if (!rb_easy_nil("proxy_headers")) { if (rb_easy_type_check("proxy_headers", T_ARRAY) || rb_easy_type_check("proxy_headers", T_HASH)) { VALUE wrap = Data_Wrap_Struct(rb_cObject, 0, 0, phdrs); rb_iterate(rb_each, rb_easy_get("proxy_headers"), cb_each_http_proxy_header, wrap); } else { VALUE proxy_headers_str = rb_obj_as_string(rb_easy_get("proxy_headers")); *phdrs = curl_slist_append(*hdrs, StringValuePtr(proxy_headers_str)); } if (*phdrs) { curl_easy_setopt(curl, CURLOPT_PROXYHEADER, *phdrs); } } #endif /* Setup FTP commands if necessary */ if (!rb_easy_nil("ftp_commands")) { if (rb_easy_type_check("ftp_commands", T_ARRAY)) { VALUE wrap = Data_Wrap_Struct(rb_cObject, 0, 0, cmds); rb_iterate(rb_each, rb_easy_get("ftp_commands"), cb_each_ftp_command, wrap); } if (*cmds) { curl_easy_setopt(curl, CURLOPT_QUOTE, *cmds); } } #if HAVE_CURLOPT_RESOLVE /* Setup resolve list if necessary */ if (!rb_easy_nil("resolve")) { if (rb_easy_type_check("resolve", T_ARRAY)) { VALUE wrap = Data_Wrap_Struct(rb_cObject, 0, 0, rslv); rb_iterate(rb_each, rb_easy_get("resolve"), cb_each_resolve, wrap); } if (*rslv) { curl_easy_setopt(curl, CURLOPT_RESOLVE, *rslv); } } #endif return Qnil; } /*********************************************** * * Clean up a connection * * Always returns Qnil. */ VALUE ruby_curl_easy_cleanup( VALUE self, ruby_curl_easy *rbce ) { CURL *curl = rbce->curl; struct curl_slist *ftp_commands; struct curl_slist *resolve; /* Free everything up */ if (rbce->curl_headers) { curl_slist_free_all(rbce->curl_headers); rbce->curl_headers = NULL; } if (rbce->curl_proxy_headers) { curl_slist_free_all(rbce->curl_proxy_headers); rbce->curl_proxy_headers = NULL; } ftp_commands = rbce->curl_ftp_commands; if (ftp_commands) { curl_slist_free_all(ftp_commands); rbce->curl_ftp_commands = NULL; } resolve = rbce->curl_resolve; if (resolve) { curl_slist_free_all(resolve); rbce->curl_resolve = NULL; } /* clean up a PUT request's curl options. */ if (!rb_easy_nil("upload")) { rb_easy_del("upload"); // set the upload object to Qnil to let the GC clean up curl_easy_setopt(curl, CURLOPT_UPLOAD, 0); curl_easy_setopt(curl, CURLOPT_READFUNCTION, NULL); curl_easy_setopt(curl, CURLOPT_READDATA, NULL); curl_easy_setopt(curl, CURLOPT_INFILESIZE, 0); } // set values on cleanup to nil //rb_easy_del("multi"); return Qnil; } /* * Common implementation of easy.http(verb) and easy.http_delete */ static VALUE ruby_curl_easy_perform_verb_str(VALUE self, const char *verb) { ruby_curl_easy *rbce; CURL *curl; VALUE retval; Data_Get_Struct(self, ruby_curl_easy, rbce); curl = rbce->curl; memset(rbce->err_buf, 0, sizeof(rbce->err_buf)); curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, verb); retval = rb_funcall(self, rb_intern("perform"), 0); curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, NULL); return retval; } /* * call-seq: * easy.http(verb) * * Send an HTTP request with method set to verb, using the current options set for this Curl::Easy instance. * This method always returns true or raises an exception (defined under Curl::Err) on error. */ static VALUE ruby_curl_easy_perform_verb(VALUE self, VALUE verb) { VALUE str_verb; if (rb_type(verb) == T_STRING) { return ruby_curl_easy_perform_verb_str(self, StringValueCStr(verb)); } else if (rb_respond_to(verb,rb_intern("to_s"))) { str_verb = rb_funcall(verb, rb_intern("to_s"), 0); return ruby_curl_easy_perform_verb_str(self, StringValueCStr(str_verb)); } else { rb_raise(rb_eRuntimeError, "Invalid HTTP VERB, must response to 'to_s'"); } } /* * call-seq: * easy.http_post("url=encoded%20form%20data;and=so%20on") => true * easy.http_post("url=encoded%20form%20data", "and=so%20on", ...) => true * easy.http_post("url=encoded%20form%20data", Curl::PostField, "and=so%20on", ...) => true * easy.http_post(Curl::PostField, Curl::PostField ..., Curl::PostField) => true * * POST the specified formdata to the currently configured URL using * the current options set for this Curl::Easy instance. This method * always returns true, or raises an exception (defined under * Curl::Err) on error. * * The Content-type of the POST is determined by the current setting * of multipart_form_post? , according to the following rules: * * When false (the default): the form will be POSTed with a * content-type of 'application/x-www-form-urlencoded', and any of the * four calling forms may be used. * * When true: the form will be POSTed with a content-type of * 'multipart/formdata'. Only the last calling form may be used, * i.e. only PostField instances may be POSTed. In this mode, * individual fields' content-types are recognised, and file upload * fields are supported. * */ static VALUE ruby_curl_easy_perform_post(int argc, VALUE *argv, VALUE self) { ruby_curl_easy *rbce; CURL *curl; int i; VALUE args_ary; rb_scan_args(argc, argv, "*", &args_ary); Data_Get_Struct(self, ruby_curl_easy, rbce); curl = rbce->curl; memset(rbce->err_buf, 0, sizeof(rbce->err_buf)); curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, NULL); if (rbce->multipart_form_post) { VALUE ret; struct curl_httppost *first = NULL, *last = NULL; // Make the multipart form for (i = 0; i < argc; i++) { if (rb_obj_is_instance_of(argv[i], cCurlPostField)) { append_to_form(argv[i], &first, &last); } else if (rb_type(argv[i]) == T_ARRAY) { // see: https://github.com/rvanlieshout/curb/commit/8bcdefddc0162484681ebd1a92d52a642666a445 long c = 0, argv_len = RARRAY_LEN(argv[i]); for (; c < argv_len; ++c) { if (rb_obj_is_instance_of(rb_ary_entry(argv[i],c), cCurlPostField)) { append_to_form(rb_ary_entry(argv[i],c), &first, &last); } else { rb_raise(eCurlErrInvalidPostField, "You must use PostFields only with multipart form posts"); return Qnil; } } } else { rb_raise(eCurlErrInvalidPostField, "You must use PostFields only with multipart form posts"); return Qnil; } } curl_easy_setopt(curl, CURLOPT_POST, 0); curl_easy_setopt(curl, CURLOPT_HTTPPOST, first); ret = rb_funcall(self, rb_intern("perform"), 0); curl_formfree(first); return ret; } else { VALUE post_body = Qnil; /* TODO: check for PostField.file and raise error before to_s fails */ if ((post_body = rb_funcall(args_ary, idJoin, 1, rbstrAmp)) == Qnil) { rb_raise(eCurlErrError, "Failed to join arguments"); return Qnil; } else { /* if the function call above returns an empty string because no additional arguments were passed this makes sure a previously set easy.post_body = "arg=foo&bar=bin" will be honored */ if( post_body != Qnil && rb_type(post_body) == T_STRING && RSTRING_LEN(post_body) > 0 ) { ruby_curl_easy_post_body_set(self, post_body); } /* if post body is not defined, set it so we enable POST header, even though the request body is empty */ if( rb_easy_nil("postdata_buffer") ) { ruby_curl_easy_post_body_set(self, post_body); } return rb_funcall(self, rb_intern("perform"), 0); } } } /* * call-seq: * easy.http_put(data) => true * * PUT the supplied data to the currently configured URL using the * current options set for this Curl::Easy instance. This method always * returns true, or raises an exception (defined under Curl::Err) on error. */ static VALUE ruby_curl_easy_perform_put(VALUE self, VALUE data) { ruby_curl_easy *rbce; CURL *curl; Data_Get_Struct(self, ruby_curl_easy, rbce); curl = rbce->curl; memset(rbce->err_buf, 0, sizeof(rbce->err_buf)); curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, NULL); ruby_curl_easy_put_data_set(self, data); return rb_funcall(self, rb_intern("perform"), 0); } /* =================== DATA FUNCS =============== */ /* * call-seq: * easy.body_str => "response body" * * Return the response body from the previous call to +perform+. This * is populated by the default +on_body+ handler - if you supply * your own body handler, this string will be empty. */ static VALUE ruby_curl_easy_body_str_get(VALUE self) { /* TODO: can we force_encoding on the return here if we see charset=utf-8 in the content-type header? Content-Type: application/json; charset=utf-8 */ CURB_OBJECT_HGETTER(ruby_curl_easy, body_data); } /* * call-seq: * easy.header_str => "response header" * * Return the response header from the previous call to +perform+. This * is populated by the default +on_header+ handler - if you supply * your own header handler, this string will be empty. */ static VALUE ruby_curl_easy_header_str_get(VALUE self) { CURB_OBJECT_HGETTER(ruby_curl_easy, header_data); } /* ============== LASTCONN INFO FUNCS ============ */ /* * call-seq: * easy.last_effective_url => "http://some.url" or nil * * Retrieve the last effective URL used by this instance. * This is the URL used in the last +perform+ call, * and may differ from the value of easy.url. */ static VALUE ruby_curl_easy_last_effective_url_get(VALUE self) { ruby_curl_easy *rbce; char* url; Data_Get_Struct(self, ruby_curl_easy, rbce); curl_easy_getinfo(rbce->curl, CURLINFO_EFFECTIVE_URL, &url); if (url && url[0]) { // curl returns empty string if none return rb_str_new2(url); } else { return Qnil; } } /* * call-seq: * easy.response_code => fixnum * * Retrieve the last received HTTP or FTP code. This will be zero * if no server response code has been received. Note that a proxy's * CONNECT response should be read with +http_connect_code+ * and not this method. */ static VALUE ruby_curl_easy_response_code_get(VALUE self) { ruby_curl_easy *rbce; long code; Data_Get_Struct(self, ruby_curl_easy, rbce); #ifdef HAVE_CURLINFO_RESPONSE_CODE curl_easy_getinfo(rbce->curl, CURLINFO_RESPONSE_CODE, &code); #else // old libcurl curl_easy_getinfo(rbce->curl, CURLINFO_HTTP_CODE, &code); #endif return LONG2NUM(code); } #if defined(HAVE_CURLINFO_PRIMARY_IP) /* * call-seq: * easy.primary_ip => "xx.xx.xx.xx" or nil * * Retrieve the resolved IP of the most recent connection * done with this curl handle. This string may be IPv6 if * that's enabled. This feature requires curl 7.19.x and above */ static VALUE ruby_curl_easy_primary_ip_get(VALUE self) { ruby_curl_easy *rbce; char* ip; Data_Get_Struct(self, ruby_curl_easy, rbce); curl_easy_getinfo(rbce->curl, CURLINFO_PRIMARY_IP, &ip); if (ip && ip[0]) { // curl returns empty string if none return rb_str_new2(ip); } else { return Qnil; } } #endif /* * call-seq: * easy.http_connect_code => fixnum * * Retrieve the last received proxy response code to a CONNECT request. */ static VALUE ruby_curl_easy_http_connect_code_get(VALUE self) { ruby_curl_easy *rbce; long code; Data_Get_Struct(self, ruby_curl_easy, rbce); curl_easy_getinfo(rbce->curl, CURLINFO_HTTP_CONNECTCODE, &code); return LONG2NUM(code); } /* * call-seq: * easy.file_time => fixnum * * Retrieve the remote time of the retrieved document (in number of * seconds since 1 jan 1970 in the GMT/UTC time zone). If you get -1, * it can be because of many reasons (unknown, the server hides it * or the server doesn't support the command that tells document time * etc) and the time of the document is unknown. * * Note that you must tell the server to collect this information * before the transfer is made, by setting +fetch_file_time?+ to true, * or you will unconditionally get a -1 back. * * This requires libcurl 7.5 or higher - otherwise -1 is unconditionally * returned. */ static VALUE ruby_curl_easy_file_time_get(VALUE self) { #ifdef HAVE_CURLINFO_FILETIME ruby_curl_easy *rbce; long time; Data_Get_Struct(self, ruby_curl_easy, rbce); curl_easy_getinfo(rbce->curl, CURLINFO_FILETIME, &time); return LONG2NUM(time); #else rb_warn("Installed libcurl is too old to support file_time"); return LONG2NUM(0); #endif } /* * call-seq: * easy.total_time => float * * Retrieve the total time in seconds for the previous transfer, * including name resolving, TCP connect etc. */ static VALUE ruby_curl_easy_total_time_get(VALUE self) { ruby_curl_easy *rbce; double time; Data_Get_Struct(self, ruby_curl_easy, rbce); curl_easy_getinfo(rbce->curl, CURLINFO_TOTAL_TIME, &time); return rb_float_new(time); } /* * call-seq: * easy.name_lookup_time => float * * Retrieve the time, in seconds, it took from the start until the * name resolving was completed. */ static VALUE ruby_curl_easy_name_lookup_time_get(VALUE self) { ruby_curl_easy *rbce; double time; Data_Get_Struct(self, ruby_curl_easy, rbce); curl_easy_getinfo(rbce->curl, CURLINFO_NAMELOOKUP_TIME, &time); return rb_float_new(time); } /* * call-seq: * easy.connect_time => float * * Retrieve the time, in seconds, it took from the start until the * connect to the remote host (or proxy) was completed. */ static VALUE ruby_curl_easy_connect_time_get(VALUE self) { ruby_curl_easy *rbce; double time; Data_Get_Struct(self, ruby_curl_easy, rbce); curl_easy_getinfo(rbce->curl, CURLINFO_CONNECT_TIME, &time); return rb_float_new(time); } /* * call-seq: * easy.app_connect_time => float * * Retrieve the time, in seconds, it took from the start until the SSL/SSH * connect/handshake to the remote host was completed. This time is most often * very near to the pre transfer time, except for cases such as HTTP * pipelining where the pretransfer time can be delayed due to waits in line * for the pipeline and more. */ #if defined(HAVE_CURLINFO_APPCONNECT_TIME) static VALUE ruby_curl_easy_app_connect_time_get(VALUE self) { ruby_curl_easy *rbce; double time; Data_Get_Struct(self, ruby_curl_easy, rbce); curl_easy_getinfo(rbce->curl, CURLINFO_APPCONNECT_TIME, &time); return rb_float_new(time); } #endif /* * call-seq: * easy.pre_transfer_time => float * * Retrieve the time, in seconds, it took from the start until the * file transfer is just about to begin. This includes all pre-transfer * commands and negotiations that are specific to the particular protocol(s) * involved. */ static VALUE ruby_curl_easy_pre_transfer_time_get(VALUE self) { ruby_curl_easy *rbce; double time; Data_Get_Struct(self, ruby_curl_easy, rbce); curl_easy_getinfo(rbce->curl, CURLINFO_PRETRANSFER_TIME, &time); return rb_float_new(time); } /* * call-seq: * easy.start_transfer_time => float * * Retrieve the time, in seconds, it took from the start until the first byte * is just about to be transferred. This includes the +pre_transfer_time+ and * also the time the server needs to calculate the result. */ static VALUE ruby_curl_easy_start_transfer_time_get(VALUE self) { ruby_curl_easy *rbce; double time; Data_Get_Struct(self, ruby_curl_easy, rbce); curl_easy_getinfo(rbce->curl, CURLINFO_STARTTRANSFER_TIME, &time); return rb_float_new(time); } /* * call-seq: * easy.redirect_time => float * * Retrieve the total time, in seconds, it took for all redirection steps * include name lookup, connect, pretransfer and transfer before final * transaction was started. +redirect_time+ contains the complete * execution time for multiple redirections. * * Requires libcurl 7.9.7 or higher, otherwise -1 is always returned. */ static VALUE ruby_curl_easy_redirect_time_get(VALUE self) { #ifdef HAVE_CURLINFO_REDIRECT_TIME ruby_curl_easy *rbce; double time; Data_Get_Struct(self, ruby_curl_easy, rbce); curl_easy_getinfo(rbce->curl, CURLINFO_REDIRECT_TIME, &time); return rb_float_new(time); #else rb_warn("Installed libcurl is too old to support redirect_time"); return rb_float_new(-1); #endif } /* * call-seq: * easy.redirect_count => integer * * Retrieve the total number of redirections that were actually followed. * * Requires libcurl 7.9.7 or higher, otherwise -1 is always returned. */ static VALUE ruby_curl_easy_redirect_count_get(VALUE self) { #ifdef HAVE_CURLINFO_REDIRECT_COUNT ruby_curl_easy *rbce; long count; Data_Get_Struct(self, ruby_curl_easy, rbce); curl_easy_getinfo(rbce->curl, CURLINFO_REDIRECT_COUNT, &count); return LONG2NUM(count); #else rb_warn("Installed libcurl is too old to support redirect_count"); return LONG2NUM(-1); #endif } /* * call-seq: * easy.redirect_url => "http://some.url" or nil * * Retrieve the URL a redirect would take you to if you * would enable CURLOPT_FOLLOWLOCATION. * * Requires libcurl 7.18.2 or higher, otherwise -1 is always returned. */ static VALUE ruby_curl_easy_redirect_url_get(VALUE self) { #ifdef HAVE_CURLINFO_REDIRECT_URL ruby_curl_easy *rbce; char* url; Data_Get_Struct(self, ruby_curl_easy, rbce); curl_easy_getinfo(rbce->curl, CURLINFO_REDIRECT_URL, &url); if (url && url[0]) { // curl returns empty string if none return rb_str_new2(url); } else { return Qnil; } #else rb_warn("Installed libcurl is too old to support redirect_url"); return LONG2NUM(-1); #endif } /* * call-seq: * easy.uploaded_bytes => float * * Retrieve the total amount of bytes that were uploaded in the * preceeding transfer. */ static VALUE ruby_curl_easy_uploaded_bytes_get(VALUE self) { ruby_curl_easy *rbce; double bytes; Data_Get_Struct(self, ruby_curl_easy, rbce); curl_easy_getinfo(rbce->curl, CURLINFO_SIZE_UPLOAD, &bytes); return rb_float_new(bytes); } /* * call-seq: * easy.downloaded_bytes => float * * Retrieve the total amount of bytes that were downloaded in the * preceeding transfer. */ static VALUE ruby_curl_easy_downloaded_bytes_get(VALUE self) { ruby_curl_easy *rbce; double bytes; Data_Get_Struct(self, ruby_curl_easy, rbce); curl_easy_getinfo(rbce->curl, CURLINFO_SIZE_DOWNLOAD, &bytes); return rb_float_new(bytes); } /* * call-seq: * easy.upload_speed => float * * Retrieve the average upload speed that curl measured for the * preceeding complete upload. */ static VALUE ruby_curl_easy_upload_speed_get(VALUE self) { ruby_curl_easy *rbce; double bytes; Data_Get_Struct(self, ruby_curl_easy, rbce); curl_easy_getinfo(rbce->curl, CURLINFO_SPEED_UPLOAD, &bytes); return rb_float_new(bytes); } /* * call-seq: * easy.download_speed => float * * Retrieve the average download speed that curl measured for * the preceeding complete download. */ static VALUE ruby_curl_easy_download_speed_get(VALUE self) { ruby_curl_easy *rbce; double bytes; Data_Get_Struct(self, ruby_curl_easy, rbce); curl_easy_getinfo(rbce->curl, CURLINFO_SPEED_DOWNLOAD, &bytes); return rb_float_new(bytes); } /* * call-seq: * easy.header_size => fixnum * * Retrieve the total size of all the headers received in the * preceeding transfer. */ static VALUE ruby_curl_easy_header_size_get(VALUE self) { ruby_curl_easy *rbce; long size; Data_Get_Struct(self, ruby_curl_easy, rbce); curl_easy_getinfo(rbce->curl, CURLINFO_HEADER_SIZE, &size); return LONG2NUM(size); } /* * call-seq: * easy.request_size => fixnum * * Retrieve the total size of the issued requests. This is so far * only for HTTP requests. Note that this may be more than one request * if +follow_location?+ is true. */ static VALUE ruby_curl_easy_request_size_get(VALUE self) { ruby_curl_easy *rbce; long size; Data_Get_Struct(self, ruby_curl_easy, rbce); curl_easy_getinfo(rbce->curl, CURLINFO_REQUEST_SIZE, &size); return LONG2NUM(size); } /* * call-seq: * easy.ssl_verify_result => integer * * Retrieve the result of the certification verification that was requested * (by setting +ssl_verify_peer?+ to +true+). */ static VALUE ruby_curl_easy_ssl_verify_result_get(VALUE self) { ruby_curl_easy *rbce; long result; Data_Get_Struct(self, ruby_curl_easy, rbce); curl_easy_getinfo(rbce->curl, CURLINFO_SSL_VERIFYRESULT, &result); return LONG2NUM(result); } /* TODO CURLINFO_SSL_ENGINES Pass the address of a 'struct curl_slist *' to receive a linked-list of OpenSSL crypto-engines supported. Note that engines are normally implemented in separate dynamic libraries. Hence not all the returned engines may be available at run-time. NOTE: you must call curl_slist_free_all(3) on the list pointer once you're done with it, as libcurl will not free the data for you. (Added in 7.12.3) */ /* * call-seq: * easy.downloaded_content_length => float * * Retrieve the content-length of the download. This is the value read * from the Content-Length: field. */ static VALUE ruby_curl_easy_downloaded_content_length_get(VALUE self) { ruby_curl_easy *rbce; double bytes; Data_Get_Struct(self, ruby_curl_easy, rbce); curl_easy_getinfo(rbce->curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &bytes); return rb_float_new(bytes); } /* * call-seq: * easy.uploaded_content_length => float * * Retrieve the content-length of the upload. */ static VALUE ruby_curl_easy_uploaded_content_length_get(VALUE self) { ruby_curl_easy *rbce; double bytes; Data_Get_Struct(self, ruby_curl_easy, rbce); curl_easy_getinfo(rbce->curl, CURLINFO_CONTENT_LENGTH_UPLOAD, &bytes); return rb_float_new(bytes); } /* * call-seq: * easy.content_type => "content/type" or nil * * Retrieve the content-type of the downloaded object. This is the value read * from the Content-Type: field. If you get +nil+, it means that the server * didn't send a valid Content-Type header or that the protocol used doesn't * support this. */ static VALUE ruby_curl_easy_content_type_get(VALUE self) { ruby_curl_easy *rbce; char* type; Data_Get_Struct(self, ruby_curl_easy, rbce); curl_easy_getinfo(rbce->curl, CURLINFO_CONTENT_TYPE, &type); if (type && type[0]) { // curl returns empty string if none return rb_str_new2(type); } else { return Qnil; } } /* NOT REQUIRED? CURLINFO_PRIVATE Pass a pointer to a 'char *' to receive the pointer to the private data associated with the curl handle (set with the CURLOPT_PRIVATE option to curl_easy_setopt(3)). (Added in 7.10.3) */ /* TODO these will need constants setting up too for checking the bits. * * Alternatively, could return an object that wraps the long, and has * question methods to query the auth types. Could return long from to_i(nt) * CURLINFO_HTTPAUTH_AVAIL Pass a pointer to a long to receive a bitmask indicating the authentication method(s) available. The meaning of the bits is explained in the CURLOPT_HTTPAUTH option for curl_easy_setopt(3). (Added in 7.10.8) CURLINFO_PROXYAUTH_AVAIL Pass a pointer to a long to receive a bitmask indicating the authentication method(s) available for your proxy authentication. (Added in 7.10.8) */ /* * call-seq: * easy.os_errno => integer * * Retrieve the errno variable from a connect failure (requires * libcurl 7.12.2 or higher, otherwise 0 is always returned). */ static VALUE ruby_curl_easy_os_errno_get(VALUE self) { #ifdef HAVE_CURLINFO_OS_ERRNO ruby_curl_easy *rbce; long result; Data_Get_Struct(self, ruby_curl_easy, rbce); curl_easy_getinfo(rbce->curl, CURLINFO_OS_ERRNO, &result); return LONG2NUM(result); #else rb_warn("Installed libcurl is too old to support os_errno"); return LONG2NUM(0); #endif } /* * call-seq: * easy.num_connects => integer * * Retrieve the number of new connections libcurl had to create to achieve * the previous transfer (only the successful connects are counted). * Combined with +redirect_count+ you are able to know how many times libcurl * successfully reused existing connection(s) or not. * * See the Connection Options of curl_easy_setopt(3) to see how libcurl tries * to make persistent connections to save time. * * (requires libcurl 7.12.3 or higher, otherwise -1 is always returned). */ static VALUE ruby_curl_easy_num_connects_get(VALUE self) { #ifdef HAVE_CURLINFO_NUM_CONNECTS ruby_curl_easy *rbce; long result; Data_Get_Struct(self, ruby_curl_easy, rbce); curl_easy_getinfo(rbce->curl, CURLINFO_NUM_CONNECTS, &result); return LONG2NUM(result); #else rb_warn("Installed libcurl is too old to support num_connects"); return LONG2NUM(-1); #endif } /* * call-seq: * easy.cookielist => array * * Retrieves the cookies curl knows in an array of strings. * Returned strings are in Netscape cookiejar format or in Set-Cookie format. * * See also option CURLINFO_COOKIELIST of curl_easy_getopt(3) to see how libcurl behaves. * * (requires libcurl 7.14.1 or higher, otherwise -1 is always returned). */ static VALUE ruby_curl_easy_cookielist_get(VALUE self) { #ifdef HAVE_CURLINFO_COOKIELIST ruby_curl_easy *rbce; struct curl_slist *cookies; struct curl_slist *cookie; VALUE rb_cookies; Data_Get_Struct(self, ruby_curl_easy, rbce); curl_easy_getinfo(rbce->curl, CURLINFO_COOKIELIST, &cookies); if (!cookies) return Qnil; rb_cookies = rb_ary_new(); for (cookie = cookies; cookie; cookie = cookie->next) rb_ary_push(rb_cookies, rb_str_new2(cookie->data)); curl_slist_free_all(cookies); return rb_cookies; #else rb_warn("Installed libcurl is too old to support cookielist"); return INT2FIX(-1); #endif } /* TODO this needs to be implemented. Could probably support CONNECT_ONLY by having this * return an open Socket or something. * CURLINFO_LASTSOCKET Pass a pointer to a long to receive the last socket used by this curl session. If the socket is no longer valid, -1 is returned. When you finish working with the socket, you must call curl_easy_cleanup() as usual and let libcurl close the socket and cleanup other resources associated with the handle. This is typically used in combination with CURLOPT_CONNECT_ONLY. (Added in 7.15.2) */ /* * call-seq: * easy.ftp_entry_path => "C:\ftp\root\" or nil * * Retrieve the path of the entry path. That is the initial path libcurl ended * up in when logging on to the remote FTP server. This returns +nil+ if * something is wrong. * * (requires libcurl 7.15.4 or higher, otherwise +nil+ is always returned). */ static VALUE ruby_curl_easy_ftp_entry_path_get(VALUE self) { #ifdef HAVE_CURLINFO_FTP_ENTRY_PATH ruby_curl_easy *rbce; char* path = NULL; Data_Get_Struct(self, ruby_curl_easy, rbce); curl_easy_getinfo(rbce->curl, CURLINFO_FTP_ENTRY_PATH, &path); if (path && path[0]) { // curl returns NULL or empty string if none return rb_str_new2(path); } else { return Qnil; } #else rb_warn("Installed libcurl is too old to support num_connects"); return Qnil; #endif } /* * call-seq: * easy.multi => "#" */ static VALUE ruby_curl_easy_multi_get(VALUE self) { ruby_curl_easy *rbce; Data_Get_Struct(self, ruby_curl_easy, rbce); return rbce->multi; } /* * call-seq: * easy.multi=multi => "#" */ static VALUE ruby_curl_easy_multi_set(VALUE self, VALUE multi) { ruby_curl_easy *rbce; Data_Get_Struct(self, ruby_curl_easy, rbce); rbce->multi = multi; return rbce->multi; } /* * call-seq: * easy.last_result => 0 */ static VALUE ruby_curl_easy_last_result(VALUE self) { ruby_curl_easy *rbce; Data_Get_Struct(self, ruby_curl_easy, rbce); return LONG2NUM(rbce->last_result); } /* * call-seq: * easy.last_error => "Error details" or nil */ static VALUE ruby_curl_easy_last_error(VALUE self) { ruby_curl_easy *rbce; Data_Get_Struct(self, ruby_curl_easy, rbce); if (rbce->err_buf[0]) { // curl returns NULL or empty string if none return rb_str_new2(rbce->err_buf); } else { return Qnil; } } /* * call-seq: * easy.setopt Fixnum, value => value * * Initial access to libcurl curl_easy_setopt */ static VALUE ruby_curl_easy_set_opt(VALUE self, VALUE opt, VALUE val) { ruby_curl_easy *rbce; long option = NUM2LONG(opt); rb_io_t *open_f_ptr; Data_Get_Struct(self, ruby_curl_easy, rbce); switch (option) { /* BEHAVIOR OPTIONS */ case CURLOPT_VERBOSE: { VALUE verbose = val; CURB_BOOLEAN_SETTER(ruby_curl_easy, verbose); } break; case CURLOPT_FOLLOWLOCATION: { VALUE follow_location = val; CURB_BOOLEAN_SETTER(ruby_curl_easy, follow_location); } break; /* TODO: CALLBACK OPTIONS */ /* TODO: ERROR OPTIONS */ /* NETWORK OPTIONS */ case CURLOPT_URL: { VALUE url = val; CURB_OBJECT_HSETTER(ruby_curl_easy, url); } break; case CURLOPT_CUSTOMREQUEST: curl_easy_setopt(rbce->curl, CURLOPT_CUSTOMREQUEST, NIL_P(val) ? NULL : StringValueCStr(val)); break; case CURLOPT_HTTP_VERSION: curl_easy_setopt(rbce->curl, CURLOPT_HTTP_VERSION, NUM2LONG(val)); break; case CURLOPT_PROXY: { VALUE proxy_url = val; CURB_OBJECT_HSETTER(ruby_curl_easy, proxy_url); } break; case CURLOPT_INTERFACE: { VALUE interface_hm = val; CURB_OBJECT_HSETTER(ruby_curl_easy, interface_hm); } break; case CURLOPT_HEADER: case CURLOPT_NOPROGRESS: case CURLOPT_NOSIGNAL: #if HAVE_CURLOPT_PATH_AS_IS case CURLOPT_PATH_AS_IS: #endif #if HAVE_CURLOPT_PIPEWAIT case CURLOPT_PIPEWAIT: #endif case CURLOPT_HTTPGET: case CURLOPT_NOBODY: { int type = rb_type(val); VALUE value; if (type == T_TRUE) { value = rb_int_new(1); } else if (type == T_FALSE) { value = rb_int_new(0); } else { value = rb_funcall(val, rb_intern("to_i"), 0); } curl_easy_setopt(rbce->curl, option, NUM2LONG(value)); } break; case CURLOPT_POST: { curl_easy_setopt(rbce->curl, CURLOPT_POST, rb_type(val) == T_TRUE); } break; case CURLOPT_MAXCONNECTS: { curl_easy_setopt(rbce->curl, CURLOPT_MAXCONNECTS, NUM2LONG(val)); } break; case CURLOPT_POSTFIELDS: { curl_easy_setopt(rbce->curl, CURLOPT_POSTFIELDS, NIL_P(val) ? NULL : StringValueCStr(val)); } break; case CURLOPT_USERPWD: { VALUE userpwd = val; CURB_OBJECT_HSETTER(ruby_curl_easy, userpwd); } break; case CURLOPT_PROXYUSERPWD: { VALUE proxypwd = val; CURB_OBJECT_HSETTER(ruby_curl_easy, proxypwd); } break; case CURLOPT_COOKIE: { VALUE cookies = val; CURB_OBJECT_HSETTER(ruby_curl_easy, cookies); } break; case CURLOPT_COOKIEFILE: { VALUE cookiefile = val; CURB_OBJECT_HSETTER(ruby_curl_easy, cookiefile); } break; case CURLOPT_COOKIEJAR: { VALUE cookiejar = val; CURB_OBJECT_HSETTER(ruby_curl_easy, cookiejar); } break; case CURLOPT_TCP_NODELAY: { curl_easy_setopt(rbce->curl, CURLOPT_TCP_NODELAY, NUM2LONG(val)); } break; case CURLOPT_RANGE: { curl_easy_setopt(rbce->curl, CURLOPT_RANGE, StringValueCStr(val)); } break; case CURLOPT_RESUME_FROM: { curl_easy_setopt(rbce->curl, CURLOPT_RESUME_FROM, NUM2LONG(val)); } break; case CURLOPT_FAILONERROR: { curl_easy_setopt(rbce->curl, CURLOPT_FAILONERROR, NUM2LONG(val)); } break; case CURLOPT_SSL_CIPHER_LIST: { curl_easy_setopt(rbce->curl, CURLOPT_SSL_CIPHER_LIST, StringValueCStr(val)); } break; case CURLOPT_FORBID_REUSE: { curl_easy_setopt(rbce->curl, CURLOPT_FORBID_REUSE, NUM2LONG(val)); } break; #if HAVE_CURLOPT_GSSAPI_DELEGATION case CURLOPT_GSSAPI_DELEGATION: { curl_easy_setopt(rbce->curl, CURLOPT_GSSAPI_DELEGATION, NUM2LONG(val)); } break; #endif #if HAVE_CURLOPT_UNIX_SOCKET_PATH case CURLOPT_UNIX_SOCKET_PATH: { curl_easy_setopt(rbce->curl, CURLOPT_UNIX_SOCKET_PATH, StringValueCStr(val)); } break; #endif #if HAVE_CURLOPT_MAX_SEND_SPEED_LARGE case CURLOPT_MAX_SEND_SPEED_LARGE: { curl_easy_setopt(rbce->curl, CURLOPT_MAX_SEND_SPEED_LARGE, (curl_off_t) NUM2LL(val)); } break; #endif #if HAVE_CURLOPT_MAX_RECV_SPEED_LARGE case CURLOPT_MAX_RECV_SPEED_LARGE: { curl_easy_setopt(rbce->curl, CURLOPT_MAX_RECV_SPEED_LARGE, (curl_off_t) NUM2LL(val)); } break; #endif #if HAVE_CURLOPT_MAXFILESIZE case CURLOPT_MAXFILESIZE: curl_easy_setopt(rbce->curl, CURLOPT_MAXFILESIZE, NUM2LONG(val)); break; #endif #if HAVE_CURLOPT_TCP_KEEPALIVE case CURLOPT_TCP_KEEPALIVE: curl_easy_setopt(rbce->curl, CURLOPT_TCP_KEEPALIVE, NUM2LONG(val)); break; case CURLOPT_TCP_KEEPIDLE: curl_easy_setopt(rbce->curl, CURLOPT_TCP_KEEPIDLE, NUM2LONG(val)); break; case CURLOPT_TCP_KEEPINTVL: curl_easy_setopt(rbce->curl, CURLOPT_TCP_KEEPINTVL, NUM2LONG(val)); break; #endif #if HAVE_CURLOPT_HAPROXYPROTOCOL case CURLOPT_HAPROXYPROTOCOL: curl_easy_setopt(rbce->curl, CURLOPT_HAPROXYPROTOCOL, NUM2LONG(val)); break; #endif case CURLOPT_STDERR: // libcurl requires raw FILE pointer and this should be IO object in Ruby. // Tempfile or StringIO won't work. Check_Type(val, T_FILE); GetOpenFile(val, open_f_ptr); curl_easy_setopt(rbce->curl, CURLOPT_STDERR, rb_io_stdio_file(open_f_ptr)); break; case CURLOPT_PROTOCOLS: case CURLOPT_REDIR_PROTOCOLS: curl_easy_setopt(rbce->curl, option, NUM2LONG(val)); break; #if HAVE_CURLOPT_SSL_SESSIONID_CACHE case CURLOPT_SSL_SESSIONID_CACHE: curl_easy_setopt(rbce->curl, CURLOPT_SSL_SESSIONID_CACHE, NUM2LONG(val)); break; #endif #if HAVE_CURLOPT_PROXY_SSL_VERIFYHOST case CURLOPT_PROXY_SSL_VERIFYHOST: curl_easy_setopt(rbce->curl, CURLOPT_PROXY_SSL_VERIFYHOST, NUM2LONG(val)); break; #endif default: rb_raise(rb_eTypeError, "Curb unsupported option"); } return val; } /* * call-seq: * easy.getinfo Fixnum => value * * Iniital access to libcurl curl_easy_getinfo, remember getinfo doesn't return the same values as setopt */ static VALUE ruby_curl_easy_get_opt(VALUE self, VALUE opt) { return Qnil; } /* * call-seq: * easy.inspect => "#" */ static VALUE ruby_curl_easy_inspect(VALUE self) { char buf[64]; ruby_curl_easy *rbce; Data_Get_Struct(self, ruby_curl_easy, rbce); /* if we don't have a url set... we'll crash... */ if( !rb_easy_nil("url") && rb_easy_type_check("url", T_STRING)) { VALUE url = rb_easy_get("url"); size_t len = 13+((RSTRING_LEN(url) > 50) ? 50 : RSTRING_LEN(url)); /* "#" */ memcpy(buf,"#"); } /* ================== ESCAPING FUNCS ==============*/ /* * call-seq: * easy.escape("some text") => "some%20text" * * Convert the given input string to a URL encoded string and return * the result. All input characters that are not a-z, A-Z or 0-9 are * converted to their "URL escaped" version (%NN where NN is a * two-digit hexadecimal number). */ static VALUE ruby_curl_easy_escape(VALUE self, VALUE svalue) { ruby_curl_easy *rbce; char *result; VALUE rresult; VALUE str = svalue; Data_Get_Struct(self, ruby_curl_easy, rbce); /* NOTE: make sure the value is a string, if not call to_s */ if( rb_type(str) != T_STRING ) { str = rb_funcall(str,rb_intern("to_s"),0); } #if (LIBCURL_VERSION_NUM >= 0x070f04) result = (char*)curl_easy_escape(rbce->curl, StringValuePtr(str), (int)RSTRING_LEN(str)); #else result = (char*)curl_escape(StringValuePtr(str), (int)RSTRING_LEN(str)); #endif rresult = rb_str_new2(result); curl_free(result); return rresult; } /* * call-seq: * easy.unescape("some%20text") => "some text" * * Convert the given URL encoded input string to a "plain string" and return * the result. All input characters that are URL encoded (%XX where XX is a * two-digit hexadecimal number) are converted to their binary versions. */ static VALUE ruby_curl_easy_unescape(VALUE self, VALUE str) { ruby_curl_easy *rbce; int rlen; char *result; VALUE rresult; Data_Get_Struct(self, ruby_curl_easy, rbce); #if (LIBCURL_VERSION_NUM >= 0x070f04) result = (char*)curl_easy_unescape(rbce->curl, StringValuePtr(str), (int)RSTRING_LEN(str), &rlen); #else result = (char*)curl_unescape(StringValuePtr(str), (int)RSTRING_LEN(str)); rlen = strlen(result); #endif rresult = rb_str_new(result, rlen); curl_free(result); return rresult; } /* ================= CLASS METHODS ==================*/ /* * call-seq: * Curl::Easy.error(code) => [ErrCode, String] * * translate an internal libcurl error to ruby error class */ static VALUE ruby_curl_easy_error_message(VALUE klass, VALUE code) { return rb_curl_easy_error(NUM2INT(code)); } /* =================== INIT LIB =====================*/ // TODO: https://bugs.ruby-lang.org/issues/18007 void init_curb_easy() { idCall = rb_intern("call"); idJoin = rb_intern("join"); rbstrAmp = rb_str_new2("&"); rb_global_variable(&rbstrAmp); cCurlEasy = rb_define_class_under(mCurl, "Easy", rb_cObject); /* Class methods */ rb_define_alloc_func(cCurlEasy, ruby_curl_easy_allocate); rb_define_singleton_method(cCurlEasy, "error", ruby_curl_easy_error_message, 1); /* Initialize method */ rb_define_method(cCurlEasy, "initialize", ruby_curl_easy_initialize, -1); /* Attributes for config next perform */ rb_define_method(cCurlEasy, "url", ruby_curl_easy_url_get, 0); rb_define_method(cCurlEasy, "proxy_url", ruby_curl_easy_proxy_url_get, 0); rb_define_method(cCurlEasy, "proxy_headers=", ruby_curl_easy_proxy_headers_set, 1); rb_define_method(cCurlEasy, "proxy_headers", ruby_curl_easy_proxy_headers_get, 0); rb_define_method(cCurlEasy, "headers=", ruby_curl_easy_headers_set, 1); rb_define_method(cCurlEasy, "headers", ruby_curl_easy_headers_get, 0); rb_define_method(cCurlEasy, "interface", ruby_curl_easy_interface_get, 0); rb_define_method(cCurlEasy, "userpwd", ruby_curl_easy_userpwd_get, 0); rb_define_method(cCurlEasy, "proxypwd", ruby_curl_easy_proxypwd_get, 0); rb_define_method(cCurlEasy, "cookies", ruby_curl_easy_cookies_get, 0); rb_define_method(cCurlEasy, "cookiefile", ruby_curl_easy_cookiefile_get, 0); rb_define_method(cCurlEasy, "cookiejar", ruby_curl_easy_cookiejar_get, 0); rb_define_method(cCurlEasy, "cert=", ruby_curl_easy_cert_set, 1); rb_define_method(cCurlEasy, "cert", ruby_curl_easy_cert_get, 0); rb_define_method(cCurlEasy, "cert_key=", ruby_curl_easy_cert_key_set, 1); rb_define_method(cCurlEasy, "cert_key", ruby_curl_easy_cert_key_get, 0); rb_define_method(cCurlEasy, "cacert=", ruby_curl_easy_cacert_set, 1); rb_define_method(cCurlEasy, "cacert", ruby_curl_easy_cacert_get, 0); rb_define_method(cCurlEasy, "certpassword=", ruby_curl_easy_certpassword_set, 1); rb_define_method(cCurlEasy, "certtype=", ruby_curl_easy_certtype_set, 1); rb_define_method(cCurlEasy, "certtype", ruby_curl_easy_certtype_get, 0); rb_define_method(cCurlEasy, "encoding=", ruby_curl_easy_encoding_set, 1); rb_define_method(cCurlEasy, "encoding", ruby_curl_easy_encoding_get, 0); rb_define_method(cCurlEasy, "useragent=", ruby_curl_easy_useragent_set, 1); rb_define_method(cCurlEasy, "useragent", ruby_curl_easy_useragent_get, 0); rb_define_method(cCurlEasy, "post_body=", ruby_curl_easy_post_body_set, 1); rb_define_method(cCurlEasy, "post_body", ruby_curl_easy_post_body_get, 0); rb_define_method(cCurlEasy, "put_data=", ruby_curl_easy_put_data_set, 1); rb_define_method(cCurlEasy, "ftp_commands=", ruby_curl_easy_ftp_commands_set, 1); rb_define_method(cCurlEasy, "ftp_commands", ruby_curl_easy_ftp_commands_get, 0); rb_define_method(cCurlEasy, "resolve=", ruby_curl_easy_resolve_set, 1); rb_define_method(cCurlEasy, "resolve", ruby_curl_easy_resolve_get, 0); rb_define_method(cCurlEasy, "local_port=", ruby_curl_easy_local_port_set, 1); rb_define_method(cCurlEasy, "local_port", ruby_curl_easy_local_port_get, 0); rb_define_method(cCurlEasy, "local_port_range=", ruby_curl_easy_local_port_range_set, 1); rb_define_method(cCurlEasy, "local_port_range", ruby_curl_easy_local_port_range_get, 0); rb_define_method(cCurlEasy, "proxy_port=", ruby_curl_easy_proxy_port_set, 1); rb_define_method(cCurlEasy, "proxy_port", ruby_curl_easy_proxy_port_get, 0); rb_define_method(cCurlEasy, "proxy_type=", ruby_curl_easy_proxy_type_set, 1); rb_define_method(cCurlEasy, "proxy_type", ruby_curl_easy_proxy_type_get, 0); rb_define_method(cCurlEasy, "http_auth_types=", ruby_curl_easy_http_auth_types_set, -1); rb_define_method(cCurlEasy, "http_auth_types", ruby_curl_easy_http_auth_types_get, 0); rb_define_method(cCurlEasy, "proxy_auth_types=", ruby_curl_easy_proxy_auth_types_set, 1); rb_define_method(cCurlEasy, "proxy_auth_types", ruby_curl_easy_proxy_auth_types_get, 0); rb_define_method(cCurlEasy, "max_redirects=", ruby_curl_easy_max_redirects_set, 1); rb_define_method(cCurlEasy, "max_redirects", ruby_curl_easy_max_redirects_get, 0); rb_define_method(cCurlEasy, "timeout=", ruby_curl_easy_timeout_set, 1); rb_define_method(cCurlEasy, "timeout", ruby_curl_easy_timeout_get, 0); rb_define_method(cCurlEasy, "timeout_ms=", ruby_curl_easy_timeout_ms_set, 1); rb_define_method(cCurlEasy, "timeout_ms", ruby_curl_easy_timeout_ms_get, 0); rb_define_method(cCurlEasy, "connect_timeout=", ruby_curl_easy_connect_timeout_set, 1); rb_define_method(cCurlEasy, "connect_timeout", ruby_curl_easy_connect_timeout_get, 0); rb_define_method(cCurlEasy, "connect_timeout_ms=", ruby_curl_easy_connect_timeout_ms_set, 1); rb_define_method(cCurlEasy, "connect_timeout_ms", ruby_curl_easy_connect_timeout_ms_get, 0); rb_define_method(cCurlEasy, "dns_cache_timeout=", ruby_curl_easy_dns_cache_timeout_set, 1); rb_define_method(cCurlEasy, "dns_cache_timeout", ruby_curl_easy_dns_cache_timeout_get, 0); rb_define_method(cCurlEasy, "ftp_response_timeout=", ruby_curl_easy_ftp_response_timeout_set, 1); rb_define_method(cCurlEasy, "ftp_response_timeout", ruby_curl_easy_ftp_response_timeout_get, 0); rb_define_method(cCurlEasy, "low_speed_limit=", ruby_curl_easy_low_speed_limit_set, 1); rb_define_method(cCurlEasy, "low_speed_limit", ruby_curl_easy_low_speed_limit_get, 0); rb_define_method(cCurlEasy, "low_speed_time=", ruby_curl_easy_low_speed_time_set, 1); rb_define_method(cCurlEasy, "low_speed_time", ruby_curl_easy_low_speed_time_get, 0); rb_define_method(cCurlEasy, "max_send_speed_large=", ruby_curl_easy_max_send_speed_large_set, 1); rb_define_method(cCurlEasy, "max_send_speed_large", ruby_curl_easy_max_send_speed_large_get, 0); rb_define_method(cCurlEasy, "max_recv_speed_large=", ruby_curl_easy_max_recv_speed_large_set, 1); rb_define_method(cCurlEasy, "max_recv_speed_large", ruby_curl_easy_max_recv_speed_large_get, 0); rb_define_method(cCurlEasy, "ssl_version=", ruby_curl_easy_ssl_version_set, 1); rb_define_method(cCurlEasy, "ssl_version", ruby_curl_easy_ssl_version_get, 0); rb_define_method(cCurlEasy, "use_ssl=", ruby_curl_easy_use_ssl_set, 1); rb_define_method(cCurlEasy, "use_ssl", ruby_curl_easy_use_ssl_get, 0); rb_define_method(cCurlEasy, "ftp_filemethod=", ruby_curl_easy_ftp_filemethod_set, 1); rb_define_method(cCurlEasy, "ftp_filemethod", ruby_curl_easy_ftp_filemethod_get, 0); rb_define_method(cCurlEasy, "username=", ruby_curl_easy_username_set, 1); rb_define_method(cCurlEasy, "username", ruby_curl_easy_username_get, 0); rb_define_method(cCurlEasy, "password=", ruby_curl_easy_password_set, 1); rb_define_method(cCurlEasy, "password", ruby_curl_easy_password_get, 0); rb_define_method(cCurlEasy, "proxy_tunnel=", ruby_curl_easy_proxy_tunnel_set, 1); rb_define_method(cCurlEasy, "proxy_tunnel?", ruby_curl_easy_proxy_tunnel_q, 0); rb_define_method(cCurlEasy, "fetch_file_time=", ruby_curl_easy_fetch_file_time_set, 1); rb_define_method(cCurlEasy, "fetch_file_time?", ruby_curl_easy_fetch_file_time_q, 0); rb_define_method(cCurlEasy, "ssl_verify_peer=", ruby_curl_easy_ssl_verify_peer_set, 1); rb_define_method(cCurlEasy, "ssl_verify_peer?", ruby_curl_easy_ssl_verify_peer_q, 0); rb_define_method(cCurlEasy, "ssl_verify_host_integer=", ruby_curl_easy_ssl_verify_host_set, 1); rb_define_method(cCurlEasy, "ssl_verify_host", ruby_curl_easy_ssl_verify_host_get, 0); rb_define_method(cCurlEasy, "header_in_body=", ruby_curl_easy_header_in_body_set, 1); rb_define_method(cCurlEasy, "header_in_body?", ruby_curl_easy_header_in_body_q, 0); rb_define_method(cCurlEasy, "use_netrc=", ruby_curl_easy_use_netrc_set, 1); rb_define_method(cCurlEasy, "use_netrc?", ruby_curl_easy_use_netrc_q, 0); rb_define_method(cCurlEasy, "follow_location?", ruby_curl_easy_follow_location_q, 0); rb_define_method(cCurlEasy, "autoreferer=", ruby_curl_easy_autoreferer_set, 1); rb_define_method(cCurlEasy, "unrestricted_auth=", ruby_curl_easy_unrestricted_auth_set, 1); rb_define_method(cCurlEasy, "unrestricted_auth?", ruby_curl_easy_unrestricted_auth_q, 0); rb_define_method(cCurlEasy, "verbose=", ruby_curl_easy_verbose_set, 1); rb_define_method(cCurlEasy, "verbose?", ruby_curl_easy_verbose_q, 0); rb_define_method(cCurlEasy, "multipart_form_post=", ruby_curl_easy_multipart_form_post_set, 1); rb_define_method(cCurlEasy, "multipart_form_post?", ruby_curl_easy_multipart_form_post_q, 0); rb_define_method(cCurlEasy, "enable_cookies=", ruby_curl_easy_enable_cookies_set, 1); rb_define_method(cCurlEasy, "enable_cookies?", ruby_curl_easy_enable_cookies_q, 0); rb_define_method(cCurlEasy, "ignore_content_length=", ruby_curl_easy_ignore_content_length_set, 1); rb_define_method(cCurlEasy, "ignore_content_length?", ruby_curl_easy_ignore_content_length_q, 0); rb_define_method(cCurlEasy, "resolve_mode", ruby_curl_easy_resolve_mode, 0); rb_define_method(cCurlEasy, "resolve_mode=", ruby_curl_easy_resolve_mode_set, 1); rb_define_method(cCurlEasy, "on_body", ruby_curl_easy_on_body_set, -1); rb_define_method(cCurlEasy, "on_header", ruby_curl_easy_on_header_set, -1); rb_define_method(cCurlEasy, "on_progress", ruby_curl_easy_on_progress_set, -1); rb_define_method(cCurlEasy, "on_debug", ruby_curl_easy_on_debug_set, -1); rb_define_method(cCurlEasy, "on_success", ruby_curl_easy_on_success_set, -1); rb_define_method(cCurlEasy, "on_failure", ruby_curl_easy_on_failure_set, -1); rb_define_method(cCurlEasy, "on_missing", ruby_curl_easy_on_missing_set, -1); rb_define_method(cCurlEasy, "on_redirect", ruby_curl_easy_on_redirect_set, -1); rb_define_method(cCurlEasy, "on_complete", ruby_curl_easy_on_complete_set, -1); rb_define_method(cCurlEasy, "http", ruby_curl_easy_perform_verb, 1); rb_define_method(cCurlEasy, "http_post", ruby_curl_easy_perform_post, -1); rb_define_method(cCurlEasy, "http_put", ruby_curl_easy_perform_put, 1); /* Post-perform info methods */ rb_define_method(cCurlEasy, "body_str", ruby_curl_easy_body_str_get, 0); rb_define_method(cCurlEasy, "header_str", ruby_curl_easy_header_str_get, 0); rb_define_method(cCurlEasy, "last_effective_url", ruby_curl_easy_last_effective_url_get, 0); rb_define_method(cCurlEasy, "response_code", ruby_curl_easy_response_code_get, 0); rb_define_method(cCurlEasy, "code", ruby_curl_easy_response_code_get, 0); #if defined(HAVE_CURLINFO_PRIMARY_IP) rb_define_method(cCurlEasy, "primary_ip", ruby_curl_easy_primary_ip_get, 0); #endif rb_define_method(cCurlEasy, "http_connect_code", ruby_curl_easy_http_connect_code_get, 0); rb_define_method(cCurlEasy, "file_time", ruby_curl_easy_file_time_get, 0); rb_define_method(cCurlEasy, "total_time", ruby_curl_easy_total_time_get, 0); rb_define_method(cCurlEasy, "name_lookup_time", ruby_curl_easy_name_lookup_time_get, 0); rb_define_method(cCurlEasy, "connect_time", ruby_curl_easy_connect_time_get, 0); #if defined(HAVE_CURLINFO_APPCONNECT_TIME) rb_define_method(cCurlEasy, "app_connect_time", ruby_curl_easy_app_connect_time_get, 0); #endif rb_define_method(cCurlEasy, "pre_transfer_time", ruby_curl_easy_pre_transfer_time_get, 0); rb_define_method(cCurlEasy, "start_transfer_time", ruby_curl_easy_start_transfer_time_get, 0); rb_define_method(cCurlEasy, "redirect_time", ruby_curl_easy_redirect_time_get, 0); rb_define_method(cCurlEasy, "redirect_count", ruby_curl_easy_redirect_count_get, 0); rb_define_method(cCurlEasy, "redirect_url", ruby_curl_easy_redirect_url_get, 0); rb_define_method(cCurlEasy, "downloaded_bytes", ruby_curl_easy_downloaded_bytes_get, 0); rb_define_method(cCurlEasy, "uploaded_bytes", ruby_curl_easy_uploaded_bytes_get, 0); rb_define_method(cCurlEasy, "download_speed", ruby_curl_easy_download_speed_get, 0); rb_define_method(cCurlEasy, "upload_speed", ruby_curl_easy_upload_speed_get, 0); rb_define_method(cCurlEasy, "header_size", ruby_curl_easy_header_size_get, 0); rb_define_method(cCurlEasy, "request_size", ruby_curl_easy_request_size_get, 0); rb_define_method(cCurlEasy, "ssl_verify_result", ruby_curl_easy_ssl_verify_result_get, 0); rb_define_method(cCurlEasy, "downloaded_content_length", ruby_curl_easy_downloaded_content_length_get, 0); rb_define_method(cCurlEasy, "uploaded_content_length", ruby_curl_easy_uploaded_content_length_get, 0); rb_define_method(cCurlEasy, "content_type", ruby_curl_easy_content_type_get, 0); rb_define_method(cCurlEasy, "os_errno", ruby_curl_easy_os_errno_get, 0); rb_define_method(cCurlEasy, "num_connects", ruby_curl_easy_num_connects_get, 0); rb_define_method(cCurlEasy, "cookielist", ruby_curl_easy_cookielist_get, 0); rb_define_method(cCurlEasy, "ftp_entry_path", ruby_curl_easy_ftp_entry_path_get, 0); rb_define_method(cCurlEasy, "close", ruby_curl_easy_close, 0); rb_define_method(cCurlEasy, "reset", ruby_curl_easy_reset, 0); /* Curl utils */ rb_define_method(cCurlEasy, "escape", ruby_curl_easy_escape, 1); rb_define_method(cCurlEasy, "unescape", ruby_curl_easy_unescape, 1); /* Runtime support */ rb_define_method(cCurlEasy, "clone", ruby_curl_easy_clone, 0); rb_define_alias(cCurlEasy, "dup", "clone"); rb_define_method(cCurlEasy, "inspect", ruby_curl_easy_inspect, 0); rb_define_method(cCurlEasy, "multi", ruby_curl_easy_multi_get, 0); rb_define_method(cCurlEasy, "multi=", ruby_curl_easy_multi_set, 1); rb_define_method(cCurlEasy, "last_result", ruby_curl_easy_last_result, 0); rb_define_method(cCurlEasy, "last_error", ruby_curl_easy_last_error, 0); rb_define_method(cCurlEasy, "setopt", ruby_curl_easy_set_opt, 2); rb_define_method(cCurlEasy, "getinfo", ruby_curl_easy_get_opt, 1); }